(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('cross-fetch'), require('@sdk/utils/math')) : typeof define === 'function' && define.amd ? define(['exports', 'cross-fetch', '@sdk/utils/math'], factory) : (global = global || self, factory(global.Potree = {}, global.fetch$1, global.math$1)); }(this, (function (exports, fetch$1, math$1) { 'use strict'; fetch$1 = fetch$1 && Object.prototype.hasOwnProperty.call(fetch$1, 'default') ? fetch$1['default'] : fetch$1; math$1 = math$1 && Object.prototype.hasOwnProperty.call(math$1, 'default') ? math$1['default'] : math$1; // threejs.org/license var REVISION='124';var MOUSE={LEFT:0,MIDDLE:1,RIGHT:2,ROTATE:0,DOLLY:1,PAN:2};var TOUCH={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3};var CullFaceNone=0;var CullFaceBack=1;var CullFaceFront=2;var CullFaceFrontBack=3;var BasicShadowMap=0;var PCFShadowMap=1;var PCFSoftShadowMap=2;var VSMShadowMap=3;var FrontSide=0;var BackSide=1;var DoubleSide=2;var FlatShading$1=1;var SmoothShading=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 CineonToneMapping=3;var ACESFilmicToneMapping=4;var CustomToneMapping=5;var UVMapping=300;var CubeReflectionMapping=301;var CubeRefractionMapping=302;var EquirectangularReflectionMapping=303;var EquirectangularRefractionMapping=304;var CubeUVReflectionMapping=306;var CubeUVRefractionMapping=307;var RepeatWrapping=1000;var ClampToEdgeWrapping=1001;var MirroredRepeatWrapping=1002;var NearestFilter=1003;var NearestMipmapNearestFilter=1004;var NearestMipMapNearestFilter=1004;var NearestMipmapLinearFilter=1005;var NearestMipMapLinearFilter=1005;var LinearFilter=1006;var LinearMipmapNearestFilter=1007;var LinearMipMapNearestFilter=1007;var LinearMipmapLinearFilter=1008;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$1=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 RedFormat=1028;var RedIntegerFormat=1029;var RGFormat=1030;var RGIntegerFormat=1031;var RGBIntegerFormat=1032;var RGBAIntegerFormat=1033;var RGB_S3TC_DXT1_Format=33776;var RGBA_S3TC_DXT1_Format$1=33777;var RGBA_S3TC_DXT3_Format=33778;var RGBA_S3TC_DXT5_Format$1=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 RGB_ETC2_Format=37492;var RGBA_ETC2_EAC_Format=37496;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 RGBA_BPTC_Format=36492;var SRGB8_ALPHA8_ASTC_4x4_Format=37840;var SRGB8_ALPHA8_ASTC_5x4_Format=37841;var SRGB8_ALPHA8_ASTC_5x5_Format=37842;var SRGB8_ALPHA8_ASTC_6x5_Format=37843;var SRGB8_ALPHA8_ASTC_6x6_Format=37844;var SRGB8_ALPHA8_ASTC_8x5_Format=37845;var SRGB8_ALPHA8_ASTC_8x6_Format=37846;var SRGB8_ALPHA8_ASTC_8x8_Format=37847;var SRGB8_ALPHA8_ASTC_10x5_Format=37848;var SRGB8_ALPHA8_ASTC_10x6_Format=37849;var SRGB8_ALPHA8_ASTC_10x8_Format=37850;var SRGB8_ALPHA8_ASTC_10x10_Format=37851;var SRGB8_ALPHA8_ASTC_12x10_Format=37852;var SRGB8_ALPHA8_ASTC_12x12_Format=37853;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 NormalAnimationBlendMode=2500;var AdditiveAnimationBlendMode=2501;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;var TangentSpaceNormalMap=0;var ObjectSpaceNormalMap=1;var ZeroStencilOp=0;var KeepStencilOp=7680;var ReplaceStencilOp=7681;var IncrementStencilOp=7682;var DecrementStencilOp=7683;var IncrementWrapStencilOp=34055;var DecrementWrapStencilOp=34056;var InvertStencilOp=5386;var NeverStencilFunc=512;var LessStencilFunc=513;var EqualStencilFunc=514;var LessEqualStencilFunc=515;var GreaterStencilFunc=516;var NotEqualStencilFunc=517;var GreaterEqualStencilFunc=518;var AlwaysStencilFunc=519;var StaticDrawUsage=35044;var DynamicDrawUsage=35048;var StreamDrawUsage=35040;var StaticReadUsage=35045;var DynamicReadUsage=35049;var StreamReadUsage=35041;var StaticCopyUsage=35046;var DynamicCopyUsage=35050;var StreamCopyUsage=35042;var GLSL1='100';var GLSL3='300 es';/** * https://github.com/mrdoob/eventdispatcher.js/ */function EventDispatcher(){}Object.assign(EventDispatcher.prototype,{addEventListener:function addEventListener(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 hasEventListener(type,listener){if(this._listeners===undefined)return false;var listeners=this._listeners;return listeners[type]!==undefined&&listeners[type].indexOf(listener)!==-1;},dispatchEvent:function dispatchEvent(event){if(this._listeners===undefined)return;var listeners=this._listeners;var listenerArray=listeners[event.type];if(listenerArray!==undefined){event.target=this;// Make a copy, in case listeners are removed while iterating. var array=listenerArray.slice(0);for(var i=0,l=array.length;i>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 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 euclideanModulo:function euclideanModulo(n,m){return (n%m+m)%m;},// Linear mapping from range to range mapLinear:function mapLinear(x,a1,a2,b1,b2){return b1+(x-a1)*(b2-b1)/(a2-a1);},// https://en.wikipedia.org/wiki/Linear_interpolation lerp:function lerp(x,y,t){return (1-t)*x+t*y;},// http://en.wikipedia.org/wiki/Smoothstep 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);},smootherstep: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 randInt:function randInt(low,high){return low+Math.floor(Math.random()*(high-low+1));},// Random float from interval randFloat:function randFloat(low,high){return low+Math.random()*(high-low);},// Random float from <-range/2, range/2> interval randFloatSpread:function randFloatSpread(range){return range*(0.5-Math.random());},// Deterministic pseudo-random float in the interval [ 0, 1 ] seededRandom:function seededRandom(s){if(s!==undefined)_seed=s%2147483647;// Park-Miller algorithm _seed=_seed*16807%2147483647;return (_seed-1)/2147483646;},degToRad:function degToRad(degrees){return degrees*MathUtils.DEG2RAD;},radToDeg:function radToDeg(radians){return radians*MathUtils.RAD2DEG;},isPowerOfTwo:function isPowerOfTwo(value){return (value&value-1)===0&&value!==0;},ceilPowerOfTwo:function ceilPowerOfTwo(value){return Math.pow(2,Math.ceil(Math.log(value)/Math.LN2));},floorPowerOfTwo:function floorPowerOfTwo(value){return Math.pow(2,Math.floor(Math.log(value)/Math.LN2));},setQuaternionFromProperEuler: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 var cos=Math.cos;var sin=Math.sin;var c2=cos(b/2);var s2=sin(b/2);var c13=cos((a+c)/2);var s13=sin((a+c)/2);var c1_3=cos((a-c)/2);var s1_3=sin((a-c)/2);var c3_1=cos((c-a)/2);var 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);}}};class Vector2{constructor(){var x=arguments.length>0&&arguments[0]!==undefined?arguments[0]:0;var y=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;Object.defineProperty(this,'isVector2',{value: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){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(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){var 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 var angle=Math.atan2(-this.y,-this.x)+Math.PI;return angle;}distanceTo(v){return Math.sqrt(this.distanceToSquared(v));}distanceToSquared(v){var 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){var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;this.x=array[offset];this.y=array[offset+1];return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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){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;}random(){this.x=Math.random();this.y=Math.random();return this;}}class Matrix3{constructor(){Object.defineProperty(this,'isMatrix3',{value: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){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(){this.set(1,0,0,0,1,0,0,0,1);return this;}clone(){return new this.constructor().fromArray(this.elements);}copy(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;}extractBasis(xAxis,yAxis,zAxis){xAxis.setFromMatrix3Column(this,0);yAxis.setFromMatrix3Column(this,1);zAxis.setFromMatrix3Column(this,2);return this;}setFromMatrix4(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;}multiply(m){return this.multiplyMatrices(this,m);}premultiply(m){return this.multiplyMatrices(m,this);}multiplyMatrices(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(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(){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;}invert(){var 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);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(){var tmp;var 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).copy(this).invert().transpose();}transposeIntoArray(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(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);return this;}scale(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(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(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(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(array){var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;for(var _i2=0;_i2<9;_i2++){this.elements[_i2]=array[_i2+offset];}return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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;}}var _canvas;var ImageUtils={getDataURL:function getDataURL(image){if(/^data:/i.test(image.src)){return image.src;}if(typeof HTMLCanvasElement=='undefined'){return image.src;}var canvas;if(image instanceof HTMLCanvasElement){canvas=image;}else {if(_canvas===undefined)_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);}canvas=_canvas;}if(canvas.width>2048||canvas.height>2048){return canvas.toDataURL('image/jpeg',0.6);}else {return canvas.toDataURL('image/png');}}};var textureId=0;function Texture(){var image=arguments.length>0&&arguments[0]!==undefined?arguments[0]:Texture.DEFAULT_IMAGE;var mapping=arguments.length>1&&arguments[1]!==undefined?arguments[1]:Texture.DEFAULT_MAPPING;var wrapS=arguments.length>2&&arguments[2]!==undefined?arguments[2]:ClampToEdgeWrapping;var wrapT=arguments.length>3&&arguments[3]!==undefined?arguments[3]:ClampToEdgeWrapping;var magFilter=arguments.length>4&&arguments[4]!==undefined?arguments[4]:LinearFilter;var minFilter=arguments.length>5&&arguments[5]!==undefined?arguments[5]:LinearMipmapLinearFilter;var format=arguments.length>6&&arguments[6]!==undefined?arguments[6]:RGBAFormat;var type=arguments.length>7&&arguments[7]!==undefined?arguments[7]:UnsignedByteType;var anisotropy=arguments.length>8&&arguments[8]!==undefined?arguments[8]:1;var encoding=arguments.length>9&&arguments[9]!==undefined?arguments[9]:LinearEncoding;Object.defineProperty(this,'id',{value:textureId++});this.uuid=MathUtils.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.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 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:function clone(){return new this.constructor().copy(this);},copy:function 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;return this;},toJSON:function toJSON(meta){var isRootObject=meta===undefined||typeof meta==='string';if(!isRootObject&&meta.textures[this.uuid]!==undefined){return meta.textures[this.uuid];}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,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 var image=this.image;if(image.uuid===undefined){image.uuid=MathUtils.generateUUID();// UGH }if(!isRootObject&&meta.images[image.uuid]===undefined){var url;if(Array.isArray(image)){// process array of images e.g. CubeTexture url=[];for(var _i3=0,l=image.length;_i31){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;}});Object.defineProperty(Texture.prototype,'needsUpdate',{set:function set(value){if(value===true)this.version++;}});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(){var x=arguments.length>0&&arguments[0]!==undefined?arguments[0]:0;var y=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var z=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;var w=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;Object.defineProperty(this,'isVector4',{value: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;}multiplyScalar(scalar){this.x*=scalar;this.y*=scalar;this.z*=scalar;this.w*=scalar;return this;}applyMatrix4(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(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);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(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 var 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)yy&&xx>zz){// m11 is the largest diagonal term if(xxzz){// m22 is the largest diagonal term if(yy1&&arguments[1]!==undefined?arguments[1]:0;this.x=array[offset];this.y=array[offset+1];this.z=array[offset+2];this.w=array[offset+3];return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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;}}/* 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||{};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.image={};this.texture.image.width=width;this.texture.image.height=height;this.texture.generateMipmaps=options.generateMipmaps!==undefined?options.generateMipmaps:false;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;}WebGLRenderTarget.prototype=Object.assign(Object.create(EventDispatcher.prototype),{constructor:WebGLRenderTarget,isWebGLRenderTarget:true,setSize:function setSize(width,height){if(this.width!==width||this.height!==height){this.width=width;this.height=height;this.texture.image.width=width;this.texture.image.height=height;this.dispose();}this.viewport.set(0,0,width,height);this.scissor.set(0,0,width,height);},clone:function clone(){return new this.constructor().copy(this);},copy:function copy(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 dispose(){this.dispatchEvent({type:'dispose'});}});function WebGLMultisampleRenderTarget(width,height,options){WebGLRenderTarget.call(this,width,height,options);this.samples=4;}WebGLMultisampleRenderTarget.prototype=Object.assign(Object.create(WebGLRenderTarget.prototype),{constructor:WebGLMultisampleRenderTarget,isWebGLMultisampleRenderTarget:true,copy:function copy(source){WebGLRenderTarget.prototype.copy.call(this,source);this.samples=source.samples;return this;}});class Quaternion{constructor(){var x=arguments.length>0&&arguments[0]!==undefined?arguments[0]:0;var y=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var z=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;var w=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;Object.defineProperty(this,'isQuaternion',{value:true});this._x=x;this._y=y;this._z=z;this._w=w;}static slerp(qa,qb,qm,t){return qm.copy(qa).slerp(qb,t);}static slerpFlat(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];var 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;var 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;}static multiplyQuaternionsFlat(dst,dstOffset,src0,srcOffset0,src1,srcOffset1){var x0=src0[srcOffset0];var y0=src0[srcOffset0+1];var z0=src0[srcOffset0+2];var w0=src0[srcOffset0+3];var x1=src1[srcOffset1];var y1=src1[srcOffset1+1];var z1=src1[srcOffset1+2];var 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.');}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);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 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(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;if(trace>0){var 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){var _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){var _s2=2.0*Math.sqrt(1.0+m22-m11-m33);this._w=(m13-m31)/_s2;this._x=(m12+m21)/_s2;this._y=0.25*_s2;this._z=(m23+m32)/_s2;}else {var _s3=2.0*Math.sqrt(1.0+m33-m11-m22);this._w=(m21-m12)/_s3;this._x=(m13+m31)/_s3;this._y=(m23+m32)/_s3;this._z=0.25*_s3;}this._onChangeCallback();return this;}setFromUnitVectors(vFrom,vTo){// assumes direction vectors vFrom and vTo are normalized var EPS=0.000001;var r=vFrom.dot(vTo)+1;if(rMath.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(MathUtils.clamp(this.dot(q),-1,1)));}rotateTowards(q,step){var angle=this.angleTo(q);if(angle===0)return this;var 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(){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(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 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(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 sqrSinHalfTheta=1.0-cosHalfTheta*cosHalfTheta;if(sqrSinHalfTheta<=Number.EPSILON){var 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;}var sinHalfTheta=Math.sqrt(sqrSinHalfTheta);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(quaternion){return quaternion._x===this._x&&quaternion._y===this._y&&quaternion._z===this._z&&quaternion._w===this._w;}fromArray(array){var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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(){}}class Vector3{constructor(){var x=arguments.length>0&&arguments[0]!==undefined?arguments[0]:0;var y=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var z=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;Object.defineProperty(this,'isVector3',{value: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.setFromEuler(euler));}applyAxisAngle(axis,angle){return this.applyQuaternion(_quaternion.setFromAxisAngle(axis,angle));}applyMatrix3(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;}applyNormalMatrix(m){return this.applyMatrix3(m).normalize();}applyMatrix4(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(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(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 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(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){var 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){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(v){var denominator=v.lengthSq();if(denominator===0)return this.set(0,0,0);var scalar=v.dot(this)/denominator;return this.copy(v).multiplyScalar(scalar);}projectOnPlane(planeNormal){_vector.copy(this).projectOnVector(planeNormal);return this.sub(_vector);}reflect(normal){// reflect incident vector off plane orthogonal to normal // normal is assumed to have unit length return this.sub(_vector.copy(normal).multiplyScalar(2*this.dot(normal)));}angleTo(v){var denominator=Math.sqrt(this.lengthSq()*v.lengthSq());if(denominator===0)return Math.PI/2;var theta=this.dot(v)/denominator;// clamp, to handle numerical problems return Math.acos(MathUtils.clamp(theta,-1,1));}distanceTo(v){return Math.sqrt(this.distanceToSquared(v));}distanceToSquared(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(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){var 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){var e=m.elements;this.x=e[12];this.y=e[13];this.z=e[14];return this;}setFromMatrixScale(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(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){var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;this.x=array[offset];this.y=array[offset+1];this.z=array[offset+2];return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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;}}var _vector=/*@__PURE__*/new Vector3();var _quaternion=/*@__PURE__*/new Quaternion();class Box3{constructor(min,max){Object.defineProperty(this,'isBox3',{value:true});this.min=min!==undefined?min:new Vector3(+Infinity,+Infinity,+Infinity);this.max=max!==undefined?max:new Vector3(-Infinity,-Infinity,-Infinity);}set(min,max){this.min.copy(min);this.max.copy(max);return this;}setFromArray(array){var minX=+Infinity;var minY=+Infinity;var minZ=+Infinity;var maxX=-Infinity;var maxY=-Infinity;var maxZ=-Infinity;for(var _i4=0,l=array.length;_i4maxX)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){var minX=+Infinity;var minY=+Infinity;var minZ=+Infinity;var maxX=-Infinity;var maxY=-Infinity;var maxZ=-Infinity;for(var _i5=0,l=attribute.count;_i5maxX)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(var _i6=0,il=points.length;_i6this.max.x||point.ythis.max.y||point.zthis.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. 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(box){// using 6 splitting planes to rule out intersections. return box.max.xthis.max.x||box.max.ythis.max.y||box.max.zthis.max.z?false:true;}intersectsSphere(sphere){// Find the point on the AABB closest to the sphere center. this.clampPoint(sphere.center,_vector$1);// If that point is inside the sphere, the AABB and sphere intersect. return _vector$1.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. 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(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,_v0,_v1,_v2,_extents)){return false;}// test 3 face normals from the aabb axes=[1,0,0,0,1,0,0,0,1];if(!satForAxes(axes,_v0,_v1,_v2,_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,_v1,_v2,_extents);}clampPoint(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(point){var clampedPoint=_vector$1.copy(point).clamp(this.min,this.max);return clampedPoint.sub(point).length();}getBoundingSphere(target){if(target===undefined){console.error('THREE.Box3: .getBoundingSphere() target is now required');//target = new Sphere(); // removed to avoid cyclic dependency }this.getCenter(target.center);target.radius=this.getSize(_vector$1).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);}}function satForAxes(axes,v0,v1,v2,extents){for(var _i8=0,j=axes.length-3;_i8<=j;_i8+=3){_testAxis.fromArray(axes,_i8);// 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;}var _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()];var _vector$1=/*@__PURE__*/new Vector3();var _box=/*@__PURE__*/new Box3();// triangle centered vertices var _v0=/*@__PURE__*/new Vector3();var _v1=/*@__PURE__*/new Vector3();var _v2=/*@__PURE__*/new Vector3();// triangle edge vectors var _f0=/*@__PURE__*/new Vector3();var _f1=/*@__PURE__*/new Vector3();var _f2=/*@__PURE__*/new Vector3();var _center=/*@__PURE__*/new Vector3();var _extents=/*@__PURE__*/new Vector3();var _triangleNormal=/*@__PURE__*/new Vector3();var _testAxis=/*@__PURE__*/new Vector3();var _box$1=/*@__PURE__*/new Box3();class Sphere{constructor(center,radius){this.center=center!==undefined?center:new Vector3();this.radius=radius!==undefined?radius:-1;}set(center,radius){this.center.copy(center);this.radius=radius;return this;}setFromPoints(points,optionalCenter){var center=this.center;if(optionalCenter!==undefined){center.copy(optionalCenter);}else {_box$1.setFromPoints(points).getCenter(center);}var maxRadiusSq=0;for(var _i9=0,il=points.length;_i9this.radius*this.radius){target.sub(this.center).normalize();target.multiplyScalar(this.radius).add(this.center);}return target;}getBoundingBox(target){if(target===undefined){console.warn('THREE.Sphere: .getBoundingBox() target is now required');target=new Box3();}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;}equals(sphere){return sphere.center.equals(this.center)&&sphere.radius===this.radius;}}var _vector$2=/*@__PURE__*/new Vector3();var _segCenter=/*@__PURE__*/new Vector3();var _segDir=/*@__PURE__*/new Vector3();var _diff=/*@__PURE__*/new Vector3();var _edge1=/*@__PURE__*/new Vector3();var _edge2=/*@__PURE__*/new Vector3();var _normal=/*@__PURE__*/new Vector3();class Ray{constructor(origin,direction){this.origin=origin!==undefined?origin:new Vector3();this.direction=direction!==undefined?direction:new Vector3(0,0,-1);}set(origin,direction){this.origin.copy(origin);this.direction.copy(direction);return this;}clone(){return new this.constructor().copy(this);}copy(ray){this.origin.copy(ray.origin);this.direction.copy(ray.direction);return this;}at(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(v){this.direction.copy(v).sub(this.origin).normalize();return this;}recast(t){this.origin.copy(this.at(t,_vector$2));return this;}closestPointToPoint(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(point){return Math.sqrt(this.distanceSqToPoint(point));}distanceSqToPoint(point){var directionDistance=_vector$2.subVectors(point,this.origin).dot(this.direction);// point behind the ray if(directionDistance<0){return this.origin.distanceToSquared(point);}_vector$2.copy(this.direction).multiplyScalar(directionDistance).add(this.origin);return _vector$2.distanceToSquared(point);}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(sphere,target){_vector$2.subVectors(sphere.center,this.origin);var tca=_vector$2.dot(this.direction);var d2=_vector$2.dot(_vector$2)-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(sphere){return this.distanceSqToPoint(sphere.center)<=sphere.radius*sphere.radius;}distanceToPlane(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(plane,target){var 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 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(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=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=0?tmin:tmax,target);}intersectsBox(box){return this.intersectBox(box,_vector$2)!==null;}intersectTriangle(a,b,c,backfaceCulling,target){// Compute the offset origin, edges, and normal. // 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(matrix4){this.origin.applyMatrix4(matrix4);this.direction.transformDirection(matrix4);return this;}equals(ray){return ray.origin.equals(this.origin)&&ray.direction.equals(this.direction);}}class Matrix4{constructor(){Object.defineProperty(this,'isMatrix4',{value: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){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(){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){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(m){var te=this.elements,me=m.elements;te[12]=me[12];te[13]=me[13];te[14]=me[14];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 var te=this.elements;var me=m.elements;var scaleX=1/_v1$1.setFromMatrixColumn(m,0).length();var scaleY=1/_v1$1.setFromMatrixColumn(m,1).length();var scaleZ=1/_v1$1.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.');}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(q){return this.compose(_zero,q,_one);}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(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){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(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;}determinant(){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(){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(x,y,z){var 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 var 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;var 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);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(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(){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(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){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(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(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(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(x,y,z){this.set(x,0,0,0,0,y,0,0,0,0,z,0,0,0,0,1);return this;}makeShear(x,y,z){this.set(1,y,z,0,x,1,z,0,x,y,1,0,0,0,0,1);return this;}compose(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(position,quaternion,scale){var te=this.elements;var sx=_v1$1.set(te[0],te[1],te[2]).length();var sy=_v1$1.set(te[4],te[5],te[6]).length();var sz=_v1$1.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 _m1.copy(this);var invSX=1/sx;var invSY=1/sy;var invSZ=1/sz;_m1.elements[0]*=invSX;_m1.elements[1]*=invSX;_m1.elements[2]*=invSX;_m1.elements[4]*=invSY;_m1.elements[5]*=invSY;_m1.elements[6]*=invSY;_m1.elements[8]*=invSZ;_m1.elements[9]*=invSZ;_m1.elements[10]*=invSZ;quaternion.setFromRotationMatrix(_m1);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.');}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(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(matrix){var te=this.elements;var me=matrix.elements;for(var _i10=0;_i10<16;_i10++){if(te[_i10]!==me[_i10])return false;}return true;}fromArray(array){var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;for(var _i11=0;_i11<16;_i11++){this.elements[_i11]=array[_i11+offset];}return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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;}}var _v1$1=/*@__PURE__*/new Vector3();var _m1=/*@__PURE__*/new Matrix4();var _zero=/*@__PURE__*/new Vector3(0,0,0);var _one=/*@__PURE__*/new Vector3(1,1,1);var _x=/*@__PURE__*/new Vector3();var _y=/*@__PURE__*/new Vector3();var _z=/*@__PURE__*/new Vector3();class Euler{constructor(){var x=arguments.length>0&&arguments[0]!==undefined?arguments[0]:0;var y=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var z=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;var order=arguments.length>3&&arguments[3]!==undefined?arguments[3]:Euler.DefaultOrder;Object.defineProperty(this,'isEuler',{value: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._x=x;this._y=y;this._z=z;this._order=order||this._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,update){var clamp=MathUtils.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;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!==false)this._onChangeCallback();return this;}setFromQuaternion(q,order,update){_matrix.makeRotationFromQuaternion(q);return this.setFromRotationMatrix(_matrix,order,update);}setFromVector3(v,order){return this.set(v.x,v.y,v.z,order||this._order);}reorder(newOrder){// WARNING: this discards revolution information -bhouston _quaternion$1.setFromEuler(this);return this.setFromQuaternion(_quaternion$1,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(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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.DefaultOrder='XYZ';Euler.RotationOrders=['XYZ','YZX','ZXY','XZY','YXZ','ZYX'];var _matrix=/*@__PURE__*/new Matrix4();var _quaternion$1=/*@__PURE__*/new Quaternion();class Layers{constructor(){this.mask=1|0;}set(channel){this.mask=1<1){for(var _i12=0;_i121){for(var _i13=0;_i130){object.children=[];for(var _i22=0;_i220){object.animations=[];for(var _i23=0;_i230)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){var values=[];for(var key in cache){var data=cache[key];delete data.metadata;values.push(data);}return values;}},clone:function clone(recursive){return new this.constructor().copy(this,recursive);},copy:function copy(source){var recursive=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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(var _i24=0;_i241){return undefined;}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. var startSign=this.distanceToPoint(line.start);var 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){if(target===undefined){console.warn('THREE.Plane: .coplanarPoint() target is now required');target=new Vector3();}return target.copy(this.normal).multiplyScalar(-this.constant);}applyMatrix4(matrix,optionalNormalMatrix){var normalMatrix=optionalNormalMatrix||_normalMatrix.getNormalMatrix(matrix);var referencePoint=this.coplanarPoint(_vector1).applyMatrix4(matrix);var 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;}}var _v0$1=/*@__PURE__*/new Vector3();var _v1$3=/*@__PURE__*/new Vector3();var _v2$1=/*@__PURE__*/new Vector3();var _v3=/*@__PURE__*/new Vector3();var _vab=/*@__PURE__*/new Vector3();var _vac=/*@__PURE__*/new Vector3();var _vbc=/*@__PURE__*/new Vector3();var _vap=/*@__PURE__*/new Vector3();var _vbp=/*@__PURE__*/new Vector3();var _vcp=/*@__PURE__*/new Vector3();class Triangle{constructor(a,b,c){this.a=a!==undefined?a:new Vector3();this.b=b!==undefined?b:new Vector3();this.c=c!==undefined?c:new Vector3();}static 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$1.subVectors(a,b);target.cross(_v0$1);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 static getBarycoord(point,a,b,c,target){_v0$1.subVectors(c,a);_v1$3.subVectors(b,a);_v2$1.subVectors(point,a);var dot00=_v0$1.dot(_v0$1);var dot01=_v0$1.dot(_v1$3);var dot02=_v0$1.dot(_v2$1);var dot11=_v1$3.dot(_v1$3);var dot12=_v1$3.dot(_v2$1);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);}static containsPoint(point,a,b,c){this.getBarycoord(point,a,b,c,_v3);return _v3.x>=0&&_v3.y>=0&&_v3.x+_v3.y<=1;}static getUV(point,p1,p2,p3,uv1,uv2,uv3,target){this.getBarycoord(point,p1,p2,p3,_v3);target.set(0,0);target.addScaledVector(uv1,_v3.x);target.addScaledVector(uv2,_v3.y);target.addScaledVector(uv3,_v3.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;}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){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(target){return Triangle.getNormal(this.a,this.b,this.c,target);}getPlane(target){if(target===undefined){console.warn('THREE.Triangle: .getPlane() target is now required');target=new Plane();}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){if(target===undefined){console.warn('THREE.Triangle: .closestPointToPoint() target is now required');target=new Vector3();}var a=this.a,b=this.b,c=this.c;var 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);var d1=_vab.dot(_vap);var 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);var d3=_vab.dot(_vbp);var d4=_vac.dot(_vbp);if(d3>=0&&d4<=d3){// vertex region of B; barycentric coords (0, 1, 0) return target.copy(b);}var 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);var d5=_vab.dot(_vcp);var d6=_vac.dot(_vcp);if(d6>=0&&d5<=d6){// vertex region of C; barycentric coords (0, 0, 1) return target.copy(c);}var 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);}var 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 var 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);}}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};var _hslA={h:0,s:0,l:0};var _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){Object.defineProperty(this,'isColor',{value:true});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=MathUtils.euclideanModulo(h,1);s=MathUtils.clamp(s,0,1);l=MathUtils.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(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*(\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=/^(\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=/^(\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) var h=parseFloat(color[1])/360;var s=parseInt(color[2],10)/100;var l=parseInt(color[3],10)/100;handleAlpha(color[4]);return this.setHSL(h,s,l);}break;}}else if(m=/^\#([A-Fa-f\d]+)$/.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){return this.setColorName(style);}return this;}setColorName(style){// 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(){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;}copyGammaToLinear(color){var gammaFactor=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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(color){var gammaFactor=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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(gammaFactor){this.copyGammaToLinear(this,gammaFactor);return this;}convertLinearToGamma(gammaFactor){this.copyLinearToGamma(this,gammaFactor);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 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+(g1&&arguments[1]!==undefined?arguments[1]:0;this.r=array[offset];this.g=array[offset+1];this.b=array[offset+2];return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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.r=1;Color.prototype.g=1;Color.prototype.b=1;class Face3{constructor(a,b,c,normal,color){var materialIndex=arguments.length>5&&arguments[5]!==undefined?arguments[5]:0;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;}clone(){return new this.constructor().copy(this);}copy(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 _i25=0,il=source.vertexNormals.length;_i250)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.morphNormals===true)data.morphNormals=true;if(this.skinning===true)data.skinning=true;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){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 clone(){return new this.constructor().copy(this);},copy:function copy(source){this.name=source.name;this.fog=source.fog;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.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;var srcPlanes=source.clippingPlanes;var dstPlanes=null;if(srcPlanes!==null){var n=srcPlanes.length;dstPlanes=new Array(n);for(var _i27=0;_i27!==n;++_i27){dstPlanes[_i27]=srcPlanes[_i27].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.premultipliedAlpha=source.premultipliedAlpha;this.visible=source.visible;this.toneMapped=source.toneMapped;this.userData=JSON.parse(JSON.stringify(source.userData));return this;},dispose:function dispose(){this.dispatchEvent({type:'dispose'});}});Object.defineProperty(Material.prototype,'needsUpdate',{set:function set(value){if(value===true)this.version++;}});/** * 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.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;};var _vector$3=new Vector3();var _vector2$1=new Vector2();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.usage=StaticDrawUsage;this.updateRange={offset:0,count:-1};this.version=0;}Object.defineProperty(BufferAttribute.prototype,'needsUpdate',{set:function set(value){if(value===true)this.version++;}});Object.assign(BufferAttribute.prototype,{isBufferAttribute:true,onUploadCallback:function onUploadCallback(){},setUsage:function setUsage(value){this.usage=value;return this;},copy:function 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:function copyAt(index1,attribute,index2){index1*=this.itemSize;index2*=attribute.itemSize;for(var _i28=0,l=this.itemSize;_i281&&arguments[1]!==undefined?arguments[1]:0;this.array.set(value,offset);return this;},getX:function getX(index){return this.array[index*this.itemSize];},setX:function setX(index,x){this.array[index*this.itemSize]=x;return this;},getY:function getY(index){return this.array[index*this.itemSize+1];},setY:function setY(index,y){this.array[index*this.itemSize+1]=y;return this;},getZ:function getZ(index){return this.array[index*this.itemSize+2];},setZ:function setZ(index,z){this.array[index*this.itemSize+2]=z;return this;},getW:function getW(index){return this.array[index*this.itemSize+3];},setW:function setW(index,w){this.array[index*this.itemSize+3]=w;return this;},setXY:function setXY(index,x,y){index*=this.itemSize;this.array[index+0]=x;this.array[index+1]=y;return this;},setXYZ:function 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:function 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:function onUpload(callback){this.onUploadCallback=callback;return this;},clone:function clone(){return new this.constructor(this.array,this.itemSize).copy(this);},toJSON:function toJSON(){return {itemSize:this.itemSize,type:this.array.constructor.name,array:Array.prototype.slice.call(this.array),normalized:this.normalized};}});// 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 Float16BufferAttribute(array,itemSize,normalized){BufferAttribute.call(this,new Uint16Array(array),itemSize,normalized);}Float16BufferAttribute.prototype=Object.create(BufferAttribute.prototype);Float16BufferAttribute.prototype.constructor=Float16BufferAttribute;Float16BufferAttribute.prototype.isFloat16BufferAttribute=true;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;class DirectGeometry{constructor(){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;}computeGroups(geometry){var groups=[];var group,i;var materialIndex=undefined;var faces=geometry.faces;for(i=0;i0;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 _i38=0;_i380){morphTargetsNormal=[];for(var _i39=0;_i390&&faces.length===0){console.error('THREE.DirectGeometry: Faceless geometries are not supported.');}for(var _i40=0;_i40max)max=array[_i41];}return max;}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};function getTypedArray(type,buffer){return new TYPED_ARRAYS[type](buffer);}var _bufferGeometryId=1;// BufferGeometry uses odd numbers as Id var _m1$2=new Matrix4();var _obj=new Object3D();var _offset=new Vector3();var _box$2=new Box3();var _boxMorphTargets=new Box3();var _vector$4=new Vector3();function BufferGeometry(){Object.defineProperty(this,'id',{value:_bufferGeometryId+=2});this.uuid=MathUtils.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={};}BufferGeometry.prototype=Object.assign(Object.create(EventDispatcher.prototype),{constructor:BufferGeometry,isBufferGeometry:true,getIndex:function getIndex(){return this.index;},setIndex:function setIndex(index){if(Array.isArray(index)){this.index=new(arrayMax(index)>65535?Uint32BufferAttribute:Uint16BufferAttribute)(index,1);}else {this.index=index;}return this;},getAttribute:function getAttribute(name){return this.attributes[name];},setAttribute:function setAttribute(name,attribute){this.attributes[name]=attribute;return this;},deleteAttribute:function deleteAttribute(name){delete this.attributes[name];return this;},hasAttribute:function hasAttribute(name){return this.attributes[name]!==undefined;},addGroup:function addGroup(start,count){var materialIndex=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;this.groups.push({start:start,count:count,materialIndex:materialIndex});},clearGroups:function clearGroups(){this.groups=[];},setDrawRange:function setDrawRange(start,count){this.drawRange.start=start;this.drawRange.count=count;},applyMatrix4:function applyMatrix4(matrix){var position=this.attributes.position;if(position!==undefined){position.applyMatrix4(matrix);position.needsUpdate=true;}var normal=this.attributes.normal;if(normal!==undefined){var normalMatrix=new Matrix3().getNormalMatrix(matrix);normal.applyNormalMatrix(normalMatrix);normal.needsUpdate=true;}var 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;},rotateX:function rotateX(angle){// rotate geometry around world x-axis _m1$2.makeRotationX(angle);this.applyMatrix4(_m1$2);return this;},rotateY:function rotateY(angle){// rotate geometry around world y-axis _m1$2.makeRotationY(angle);this.applyMatrix4(_m1$2);return this;},rotateZ:function rotateZ(angle){// rotate geometry around world z-axis _m1$2.makeRotationZ(angle);this.applyMatrix4(_m1$2);return this;},translate:function translate(x,y,z){// translate geometry _m1$2.makeTranslation(x,y,z);this.applyMatrix4(_m1$2);return this;},scale:function scale(x,y,z){// scale geometry _m1$2.makeScale(x,y,z);this.applyMatrix4(_m1$2);return this;},lookAt:function lookAt(vector){_obj.lookAt(vector);_obj.updateMatrix();this.applyMatrix4(_obj.matrix);return this;},center:function center(){this.computeBoundingBox();this.boundingBox.getCenter(_offset).negate();this.translate(_offset.x,_offset.y,_offset.z);return this;},setFromObject:function setFromObject(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.setAttribute('position',positions.copyVector3sArray(geometry.vertices));this.setAttribute('color',colors.copyColorsArray(geometry.colors));if(geometry.lineDistances&&geometry.lineDistances.length===geometry.vertices.length){var lineDistances=new Float32BufferAttribute(geometry.lineDistances.length,1);this.setAttribute('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 setFromPoints(points){var position=[];for(var _i42=0,l=points.length;_i420){var normals=new Float32Array(geometry.normals.length*3);this.setAttribute('normal',new BufferAttribute(normals,3).copyVector3sArray(geometry.normals));}if(geometry.colors.length>0){var colors=new Float32Array(geometry.colors.length*3);this.setAttribute('color',new BufferAttribute(colors,3).copyColorsArray(geometry.colors));}if(geometry.uvs.length>0){var uvs=new Float32Array(geometry.uvs.length*2);this.setAttribute('uv',new BufferAttribute(uvs,2).copyVector2sArray(geometry.uvs));}if(geometry.uvs2.length>0){var uvs2=new Float32Array(geometry.uvs2.length*2);this.setAttribute('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 _i43=0,l=morphTargets.length;_i430){var skinIndices=new Float32BufferAttribute(geometry.skinIndices.length*4,4);this.setAttribute('skinIndex',skinIndices.copyVector4sArray(geometry.skinIndices));}if(geometry.skinWeights.length>0){var skinWeights=new Float32BufferAttribute(geometry.skinWeights.length*4,4);this.setAttribute('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 computeBoundingBox(){if(this.boundingBox===null){this.boundingBox=new Box3();}var position=this.attributes.position;var 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(var _i44=0,il=morphAttributesPosition.length;_i440)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){data.data.index={type:index.array.constructor.name,array:Array.prototype.slice.call(index.array)};}var attributes=this.attributes;for(var _key in attributes){var attribute=attributes[_key];var attributeData=attribute.toJSON(data.data);if(attribute.name!=='')attributeData.name=attribute.name;data.data.attributes[_key]=attributeData;}var morphAttributes={};var hasMorphAttributes=false;for(var _key2 in this.morphAttributes){var attributeArray=this.morphAttributes[_key2];var array=[];for(var _i56=0,il=attributeArray.length;_i560){morphAttributes[_key2]=array;hasMorphAttributes=true;}}if(hasMorphAttributes){data.data.morphAttributes=morphAttributes;data.data.morphTargetsRelative=this.morphTargetsRelative;}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 clone(){/* // Handle primitives const parameters = this.parameters; if ( parameters !== undefined ) { const values = []; for ( const key in parameters ) { values.push( parameters[ key ] ); } const 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 copy(source){// reset this.index=null;this.attributes={};this.morphAttributes={};this.groups=[];this.boundingBox=null;this.boundingSphere=null;// used for storing cloned, shared data var data={};// name this.name=source.name;// index var index=source.index;if(index!==null){this.setIndex(index.clone(data));}// attributes var attributes=source.attributes;for(var name in attributes){var attribute=attributes[name];this.setAttribute(name,attribute.clone(data));}// morph attributes var morphAttributes=source.morphAttributes;for(var _name2 in morphAttributes){var array=[];var morphAttribute=morphAttributes[_name2];// morphAttribute: array of Float32BufferAttributes for(var _i57=0,l=morphAttribute.length;_i570&&arguments[0]!==undefined?arguments[0]:new BufferGeometry();var material=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new MeshBasicMaterial();Object3D.call(this);this.type='Mesh';this.geometry=geometry;this.material=material;this.updateMorphTargets();}Mesh.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Mesh,isMesh:true,copy:function copy(source){Object3D.prototype.copy.call(this,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:function updateMorphTargets(){var geometry=this.geometry;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(var m=0,ml=morphAttribute.length;m0){console.error('THREE.Mesh.updateMorphTargets() no longer supports THREE.Geometry. Use THREE.BufferGeometry instead.');}}},raycast: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.copy(matrixWorld).invert();_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 index=geometry.index;var position=geometry.attributes.position;var morphPosition=geometry.morphAttributes.position;var morphTargetsRelative=geometry.morphTargetsRelative;var uv=geometry.attributes.uv;var uv2=geometry.attributes.uv2;var groups=geometry.groups;var drawRange=geometry.drawRange;if(index!==null){// indexed buffer geometry if(Array.isArray(material)){for(var _i59=0,il=groups.length;_i590)uvs=faceVertexUvs;for(var f=0,fl=faces.length;fraycaster.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.fromBufferAttribute(position,a);_vB.fromBufferAttribute(position,b);_vC.fromBufferAttribute(position,c);var morphInfluences=object.morphTargetInfluences;if(material.morphTargets&&morphPosition&&morphInfluences){_morphA.set(0,0,0);_morphB.set(0,0,0);_morphC.set(0,0,0);for(var _i63=0,il=morphPosition.length;_i630&&arguments[0]!==undefined?arguments[0]:1;var height=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var depth=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var widthSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;var heightSegments=arguments.length>4&&arguments[4]!==undefined?arguments[4]:1;var depthSegments=arguments.length>5&&arguments[5]!==undefined?arguments[5]:1;super();this.type='BoxBufferGeometry';this.parameters={width:width,height:height,depth:depth,widthSegments:widthSegments,heightSegments:heightSegments,depthSegments:depthSegments};var scope=this;// segments widthSegments=Math.floor(widthSegments);heightSegments=Math.floor(heightSegments);depthSegments=Math.floor(depthSegments);// 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.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){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 vector=new Vector3();// generate vertices, normals and uvs for(var iy=0;iy0?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(var _iy=0;_iy, * vertexShader: , * * wireframe: , * wireframeLinewidth: , * * lights: , * * skinning: , * morphTargets: , * morphNormals: * } */function ShaderMaterial(parameters){Material.call(this);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.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;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);}}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=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.skinning=source.skinning;this.morphTargets=source.morphTargets;this.morphNormals=source.morphNormals;this.extensions=Object.assign({},source.extensions);this.glslVersion=source.glslVersion;return this;};ShaderMaterial.prototype.toJSON=function(meta){var data=Material.prototype.toJSON.call(this,meta);data.glslVersion=this.glslVersion;data.uniforms={};for(var name in this.uniforms){var uniform=this.uniforms[name];var 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;var extensions={};for(var key in this.extensions){if(this.extensions[key]===true)extensions[key]=true;}if(Object.keys(extensions).length>0)data.extensions=extensions;return data;};function Camera(){Object3D.call(this);this.type='Camera';this.matrixWorldInverse=new Matrix4();this.projectionMatrix=new Matrix4();this.projectionMatrixInverse=new Matrix4();}Camera.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Camera,isCamera:true,copy:function copy(source,recursive){Object3D.prototype.copy.call(this,source,recursive);this.matrixWorldInverse.copy(source.matrixWorldInverse);this.projectionMatrix.copy(source.projectionMatrix);this.projectionMatrixInverse.copy(source.projectionMatrixInverse);return this;},getWorldDirection:function getWorldDirection(target){if(target===undefined){console.warn('THREE.Camera: .getWorldDirection() target is now required');target=new Vector3();}this.updateWorldMatrix(true,false);var e=this.matrixWorld.elements;return target.set(-e[8],-e[9],-e[10]).normalize();},updateMatrixWorld:function updateMatrixWorld(force){Object3D.prototype.updateMatrixWorld.call(this,force);this.matrixWorldInverse.copy(this.matrixWorld).invert();},updateWorldMatrix:function updateWorldMatrix(updateParents,updateChildren){Object3D.prototype.updateWorldMatrix.call(this,updateParents,updateChildren);this.matrixWorldInverse.copy(this.matrixWorld).invert();},clone:function clone(){return new this.constructor().copy(this);}});function PerspectiveCamera(){var fov=arguments.length>0&&arguments[0]!==undefined?arguments[0]:50;var aspect=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var near=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0.1;var far=arguments.length>3&&arguments[3]!==undefined?arguments[3]:2000;Camera.call(this);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();}PerspectiveCamera.prototype=Object.assign(Object.create(Camera.prototype),{constructor:PerspectiveCamera,isPerspectiveCamera:true,copy:function copy(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 setFocalLength(focalLength){// see http://www.bobatkins.com/photography/technical/field_of_view.html var vExtentSlope=0.5*this.getFilmHeight()/focalLength;this.fov=MathUtils.RAD2DEG*2*Math.atan(vExtentSlope);this.updateProjectionMatrix();},/** * Calculates the focal length from the current .fov and .filmGauge. */getFocalLength:function getFocalLength(){var vExtentSlope=Math.tan(MathUtils.DEG2RAD*0.5*this.fov);return 0.5*this.getFilmHeight()/vExtentSlope;},getEffectiveFOV:function getEffectiveFOV(){return MathUtils.RAD2DEG*2*Math.atan(Math.tan(MathUtils.DEG2RAD*0.5*this.fov)/this.zoom);},getFilmWidth:function getFilmWidth(){// film not completely covered in portrait format (aspect < 1) return this.filmGauge*Math.min(this.aspect,1);},getFilmHeight:function 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:function 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:function clearViewOffset(){if(this.view!==null){this.view.enabled=false;}this.updateProjectionMatrix();},updateProjectionMatrix:function updateProjectionMatrix(){var near=this.near;var top=near*Math.tan(MathUtils.DEG2RAD*0.5*this.fov)/this.zoom;var height=2*top;var width=this.aspect*height;var left=-0.5*width;var 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);this.projectionMatrixInverse.copy(this.projectionMatrix).invert();},toJSON:function toJSON(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;}});var fov=90,aspect=1;function CubeCamera(near,far,renderTarget){Object3D.call(this);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;var 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);var 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);var 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);var 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);var 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);var 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);this.update=function(renderer,scene){if(this.parent===null)this.updateMatrixWorld();var currentXrEnabled=renderer.xr.enabled;var currentRenderTarget=renderer.getRenderTarget();renderer.xr.enabled=false;var 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;};}CubeCamera.prototype=Object.create(Object3D.prototype);CubeCamera.prototype.constructor=CubeCamera;function CubeTexture(images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding){images=images!==undefined?images:[];mapping=mapping!==undefined?mapping:CubeReflectionMapping;format=format!==undefined?format:RGBFormat;Texture.call(this,images,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy,encoding);this.flipY=false;// Why CubeTexture._needsFlipEnvMap is necessary: // // 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 _needsFlipEnvMap controls this conversion. The flip is not required (and thus _needsFlipEnvMap is set to false) // when using WebGLCubeRenderTarget.texture as a cube texture. this._needsFlipEnvMap=true;}CubeTexture.prototype=Object.create(Texture.prototype);CubeTexture.prototype.constructor=CubeTexture;CubeTexture.prototype.isCubeTexture=true;Object.defineProperty(CubeTexture.prototype,'images',{get:function get(){return this.image;},set:function set(value){this.image=value;}});function WebGLCubeRenderTarget(size,options,dummy){if(Number.isInteger(options)){console.warn('THREE.WebGLCubeRenderTarget: constructor signature is now WebGLCubeRenderTarget( size, options )');options=dummy;}WebGLRenderTarget.call(this,size,size,options);options=options||{};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._needsFlipEnvMap=false;}WebGLCubeRenderTarget.prototype=Object.create(WebGLRenderTarget.prototype);WebGLCubeRenderTarget.prototype.constructor=WebGLCubeRenderTarget;WebGLCubeRenderTarget.prototype.isWebGLCubeRenderTarget=true;WebGLCubeRenderTarget.prototype.fromEquirectangularTexture=function(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;var shader={uniforms:{tEquirect:{value:null}},vertexShader:/* glsl */"\n\n\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\tvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n\n\t\t\t\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvWorldDirection = transformDirection( position, modelMatrix );\n\n\t\t\t\t#include \n\t\t\t\t#include \n\n\t\t\t}\n\t\t",fragmentShader:/* glsl */"\n\n\t\t\tuniform sampler2D tEquirect;\n\n\t\t\tvarying vec3 vWorldDirection;\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 direction = normalize( vWorldDirection );\n\n\t\t\t\tvec2 sampleUV = equirectUv( direction );\n\n\t\t\t\tgl_FragColor = texture2D( tEquirect, sampleUV );\n\n\t\t\t}\n\t\t"};var geometry=new BoxBufferGeometry(5,5,5);var material=new ShaderMaterial({name:'CubemapFromEquirect',uniforms:cloneUniforms(shader.uniforms),vertexShader:shader.vertexShader,fragmentShader:shader.fragmentShader,side:BackSide,blending:NoBlending});material.uniforms.tEquirect.value=texture;var mesh=new Mesh(geometry,material);var currentMinFilter=texture.minFilter;// Avoid blurred poles if(texture.minFilter===LinearMipmapLinearFilter)texture.minFilter=LinearFilter;var camera=new CubeCamera(1,10,this);camera.update(renderer,mesh);texture.minFilter=currentMinFilter;mesh.geometry.dispose();mesh.material.dispose();return this;};WebGLCubeRenderTarget.prototype.clear=function(renderer,color,depth,stencil){var currentRenderTarget=renderer.getRenderTarget();for(var _i64=0;_i64<6;_i64++){renderer.setRenderTarget(this,_i64);renderer.clear(color,depth,stencil);}renderer.setRenderTarget(currentRenderTarget);};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||null,width:width||1,height:height||1};this.magFilter=magFilter!==undefined?magFilter:NearestFilter;this.minFilter=minFilter!==undefined?minFilter:NearestFilter;this.generateMipmaps=false;this.flipY=false;this.unpackAlignment=1;this.needsUpdate=true;}DataTexture.prototype=Object.create(Texture.prototype);DataTexture.prototype.constructor=DataTexture;DataTexture.prototype.isDataTexture=true;var _sphere$1=/*@__PURE__*/new Sphere();var _vector$5=/*@__PURE__*/new Vector3();class Frustum{constructor(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()];}set(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(){return new this.constructor().copy(this);}copy(frustum){var planes=this.planes;for(var _i65=0;_i65<6;_i65++){planes[_i65].copy(frustum.planes[_i65]);}return this;}setFromProjectionMatrix(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(object){var geometry=object.geometry;if(geometry.boundingSphere===null)geometry.computeBoundingSphere();_sphere$1.copy(geometry.boundingSphere).applyMatrix4(object.matrixWorld);return this.intersectsSphere(_sphere$1);}intersectsSprite(sprite){_sphere$1.center.set(0,0,0);_sphere$1.radius=0.7071067811865476;_sphere$1.applyMatrix4(sprite.matrixWorld);return this.intersectsSphere(_sphere$1);}intersectsSphere(sphere){var planes=this.planes;var center=sphere.center;var negRadius=-sphere.radius;for(var _i66=0;_i66<6;_i66++){var distance=planes[_i66].distanceToPoint(center);if(distance0?box.max.x:box.min.x;_vector$5.y=plane.normal.y>0?box.max.y:box.min.y;_vector$5.z=plane.normal.z>0?box.max.z:box.min.z;if(plane.distanceToPoint(_vector$5)<0){return false;}}return true;}containsPoint(point){var planes=this.planes;for(var _i68=0;_i68<6;_i68++){if(planes[_i68].distanceToPoint(point)<0){return false;}}return true;}}function WebGLAnimation(){var context=null;var isAnimating=false;var animationLoop=null;var requestId=null;function onAnimationFrame(time,frame){animationLoop(time,frame);requestId=context.requestAnimationFrame(onAnimationFrame);}return {start:function start(){if(isAnimating===true)return;if(animationLoop===null)return;requestId=context.requestAnimationFrame(onAnimationFrame);isAnimating=true;},stop:function stop(){context.cancelAnimationFrame(requestId);isAnimating=false;},setAnimationLoop:function setAnimationLoop(callback){animationLoop=callback;},setContext:function setContext(value){context=value;}};}function WebGLAttributes(gl,capabilities){var isWebGL2=capabilities.isWebGL2;var buffers=new WeakMap();function createBuffer(attribute,bufferType){var array=attribute.array;var usage=attribute.usage;var buffer=gl.createBuffer();gl.bindBuffer(bufferType,buffer);gl.bufferData(bufferType,array,usage);attribute.onUploadCallback();var type=5126;if(array instanceof Float32Array){type=5126;}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=5131;}else {console.warn('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;}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(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;var data=buffers.get(attribute);if(data){gl.deleteBuffer(data.buffer);buffers.delete(attribute);}}function update(attribute,bufferType){if(attribute.isGLBufferAttribute){var cached=buffers.get(attribute);if(!cached||cached.version0&&arguments[0]!==undefined?arguments[0]:1;var height=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var widthSegments=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var heightSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;super();this.type='PlaneBufferGeometry';this.parameters={width:width,height:height,widthSegments:widthSegments,heightSegments:heightSegments};var width_half=width/2;var height_half=height/2;var gridX=Math.floor(widthSegments);var gridY=Math.floor(heightSegments);var gridX1=gridX+1;var gridY1=gridY+1;var segment_width=width/gridX;var segment_height=height/gridY;// var indices=[];var vertices=[];var normals=[];var uvs=[];for(var iy=0;iy1&&arguments[1]!==undefined?arguments[1]:1;clearColor.set(color);clearAlpha=alpha;setClear(clearColor,clearAlpha);},getClearAlpha:function getClearAlpha(){return clearAlpha;},setClearAlpha:function setClearAlpha(alpha){clearAlpha=alpha;setClear(clearColor,clearAlpha);},render:render};}function WebGLBindingStates(gl,extensions,attributes,capabilities){var maxVertexAttributes=gl.getParameter(34921);var extension=capabilities.isWebGL2?null:extensions.get('OES_vertex_array_object');var vaoAvailable=capabilities.isWebGL2||extension!==null;var bindingStates={};var defaultState=createBindingState(null);var currentState=defaultState;function setup(object,material,program,geometry,index){var updateBuffers=false;if(vaoAvailable){var state=getBindingState(geometry,program,material);if(currentState!==state){currentState=state;bindVertexArrayObject(currentState.object);}updateBuffers=needsUpdate(geometry,index);if(updateBuffers)saveCache(geometry,index);}else {var 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,34963);}if(updateBuffers){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){var wireframe=material.wireframe===true;var programMap=bindingStates[geometry.id];if(programMap===undefined){programMap={};bindingStates[geometry.id]=programMap;}var stateMap=programMap[program.id];if(stateMap===undefined){stateMap={};programMap[program.id]=stateMap;}var state=stateMap[wireframe];if(state===undefined){state=createBindingState(createVertexArrayObject());stateMap[wireframe]=state;}return state;}function createBindingState(vao){var newAttributes=[];var enabledAttributes=[];var attributeDivisors=[];for(var _i69=0;_i69=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){enableAttributeAndDivisor(programAttribute,data.meshPerAttribute);if(geometry._maxInstanceCount===undefined){geometry._maxInstanceCount=data.meshPerAttribute*data.count;}}else {enableAttribute(programAttribute);}gl.bindBuffer(34962,buffer);vertexAttribPointer(programAttribute,size,type,normalized,stride*bytesPerElement,offset*bytesPerElement);}else {if(geometryAttribute.isInstancedBufferAttribute){enableAttributeAndDivisor(programAttribute,geometryAttribute.meshPerAttribute);if(geometry._maxInstanceCount===undefined){geometry._maxInstanceCount=geometryAttribute.meshPerAttribute*geometryAttribute.count;}}else {enableAttribute(programAttribute);}gl.bindBuffer(34962,buffer);vertexAttribPointer(programAttribute,size,type,normalized,0,0);}}else if(name==='instanceMatrix'){var _attribute7=attributes.get(object.instanceMatrix);// TODO Attribute may not be available on context restore if(_attribute7===undefined)continue;var _buffer=_attribute7.buffer;var _type=_attribute7.type;enableAttributeAndDivisor(programAttribute+0,1);enableAttributeAndDivisor(programAttribute+1,1);enableAttributeAndDivisor(programAttribute+2,1);enableAttributeAndDivisor(programAttribute+3,1);gl.bindBuffer(34962,_buffer);gl.vertexAttribPointer(programAttribute+0,4,_type,false,64,0);gl.vertexAttribPointer(programAttribute+1,4,_type,false,64,16);gl.vertexAttribPointer(programAttribute+2,4,_type,false,64,32);gl.vertexAttribPointer(programAttribute+3,4,_type,false,64,48);}else if(name==='instanceColor'){var _attribute8=attributes.get(object.instanceColor);// TODO Attribute may not be available on context restore if(_attribute8===undefined)continue;var _buffer2=_attribute8.buffer;var _type2=_attribute8.type;enableAttributeAndDivisor(programAttribute,1);gl.bindBuffer(34962,_buffer2);gl.vertexAttribPointer(programAttribute,3,_type2,false,12,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);}}}}}disableUnusedAttributes();}function dispose(){reset();for(var geometryId in bindingStates){var programMap=bindingStates[geometryId];for(var programId in programMap){var stateMap=programMap[programId];for(var 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;var programMap=bindingStates[geometry.id];for(var programId in programMap){var stateMap=programMap[programId];for(var wireframe in stateMap){deleteVertexArrayObject(stateMap[wireframe].object);delete stateMap[wireframe];}delete programMap[programId];}delete bindingStates[geometry.id];}function releaseStatesOfProgram(program){for(var geometryId in bindingStates){var programMap=bindingStates[geometryId];if(programMap[program.id]===undefined)continue;var stateMap=programMap[program.id];for(var 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){var isWebGL2=capabilities.isWebGL2;var 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;var 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){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(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';}/* eslint-disable no-undef */var isWebGL2=typeof WebGL2RenderingContext!=='undefined'&&gl instanceof WebGL2RenderingContext||typeof WebGL2ComputeRenderingContext!=='undefined'&&gl instanceof WebGL2ComputeRenderingContext;/* eslint-enable no-undef */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(34930);var maxVertexTextures=gl.getParameter(35660);var maxTextureSize=gl.getParameter(3379);var maxCubemapSize=gl.getParameter(34076);var maxAttributes=gl.getParameter(34921);var maxVertexUniforms=gl.getParameter(36347);var maxVaryings=gl.getParameter(36348);var maxFragmentUniforms=gl.getParameter(36349);var vertexTextures=maxVertexTextures>0;var floatFragmentTextures=isWebGL2||!!extensions.get('OES_texture_float');var floatVertexTextures=vertexTextures&&floatFragmentTextures;var maxSamples=isWebGL2?gl.getParameter(36183):0;return {isWebGL2:isWebGL2,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){var scope=this;var globalState=null,numGlobalPlanes=0,localClippingEnabled=false,renderingShadows=false;var 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(material,camera,useCache){var planes=material.clippingPlanes,clipIntersection=material.clipIntersection,clipShadows=material.clipShadows;var 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 {var nGlobal=renderingShadows?0:numGlobalPlanes,lGlobal=nGlobal*4;var dstArray=materialProperties.clippingState||null;uniform.value=dstArray;// ensure unique state dstArray=projectPlanes(planes,camera,lGlobal,useCache);for(var _i72=0;_i72!==lGlobal;++_i72){dstArray[_i72]=globalState[_i72];}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){var nPlanes=planes!==null?planes.length:0;var 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.length0){var currentRenderList=renderer.getRenderList();var currentRenderTarget=renderer.getRenderTarget();var renderTarget=new WebGLCubeRenderTarget(image.height/2);renderTarget.fromEquirectangularTexture(renderer,texture);cubemaps.set(texture,renderTarget);renderer.setRenderTarget(currentRenderTarget);renderer.setRenderList(currentRenderList);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){var texture=event.target;texture.removeEventListener('dispose',onTextureDispose);var cubemap=cubemaps.get(texture);if(cubemap!==undefined){cubemaps.delete(texture);cubemap.dispose();}}function dispose(){cubemaps=new WeakMap();}return {get:get,dispose:dispose};}function WebGLExtensions(gl){var extensions={};return {has:function has(name){if(extensions[name]!==undefined){return extensions[name]!==null;}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);}extensions[name]=extension;return extension!==null;},get:function get(name){if(!this.has(name)){console.warn('THREE.WebGLRenderer: '+name+' extension not supported.');}return extensions[name];}};}function WebGLGeometries(gl,attributes,info,bindingStates){var geometries=new WeakMap();var wireframeAttributes=new WeakMap();function onGeometryDispose(event){var geometry=event.target;var buffergeometry=geometries.get(geometry);if(buffergeometry.index!==null){attributes.remove(buffergeometry.index);}for(var name in buffergeometry.attributes){attributes.remove(buffergeometry.attributes[name]);}geometry.removeEventListener('dispose',onGeometryDispose);geometries.delete(geometry);var attribute=wireframeAttributes.get(buffergeometry);if(attribute){attributes.remove(attribute);wireframeAttributes.delete(buffergeometry);}bindingStates.releaseStatesOfGeometry(buffergeometry);if(geometry.isInstancedBufferGeometry===true){delete geometry._maxInstanceCount;}// info.memory.geometries--;}function get(object,geometry){var buffergeometry=geometries.get(geometry);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.set(geometry,buffergeometry);info.memory.geometries++;return buffergeometry;}function update(geometry){var geometryAttributes=geometry.attributes;// Updating index buffer in VAO now. See WebGLBindingStates. for(var name in geometryAttributes){attributes.update(geometryAttributes[name],34962);}// morph targets var morphAttributes=geometry.morphAttributes;for(var _name3 in morphAttributes){var array=morphAttributes[_name3];for(var _i74=0,l=array.length;_i7465535?Uint32BufferAttribute:Uint16BufferAttribute)(indices,1);attribute.version=version;// Updating index buffer in VAO now. See WebGLBindingStates // var previousAttribute=wireframeAttributes.get(geometry);if(previousAttribute)attributes.remove(previousAttribute);// wireframeAttributes.set(geometry,attribute);}function getWireframeAttribute(geometry){var currentAttribute=wireframeAttributes.get(geometry);if(currentAttribute){var geometryIndex=geometry.index;if(geometryIndex!==null){// if the attribute is obsolete, create a new one if(currentAttribute.version0&&arguments[0]!==undefined?arguments[0]:null;var width=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var height=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var depth=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;Texture.call(this,null);this.image={data,width,height,depth};this.magFilter=NearestFilter;this.minFilter=NearestFilter;this.wrapR=ClampToEdgeWrapping;this.generateMipmaps=false;this.flipY=false;this.needsUpdate=true;}DataTexture2DArray.prototype=Object.create(Texture.prototype);DataTexture2DArray.prototype.constructor=DataTexture2DArray;DataTexture2DArray.prototype.isDataTexture2DArray=true;function DataTexture3D(){var data=arguments.length>0&&arguments[0]!==undefined?arguments[0]:null;var width=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var height=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var depth=arguments.length>3&&arguments[3]!==undefined?arguments[3]: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 Texture.call(this,null);this.image={data,width,height,depth};this.magFilter=NearestFilter;this.minFilter=NearestFilter;this.wrapR=ClampToEdgeWrapping;this.generateMipmaps=false;this.flipY=false;this.needsUpdate=true;}DataTexture3D.prototype=Object.create(Texture.prototype);DataTexture3D.prototype.constructor=DataTexture3D;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 * */var emptyTexture=new Texture();var emptyTexture2dArray=new DataTexture2DArray();var emptyTexture3d=new DataTexture3D();var emptyCubeTexture=new CubeTexture();// --- 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;var r=arrayCacheF32[n];if(r===undefined){r=new Float32Array(n);arrayCacheF32[n]=r;}if(nBlocks!==0){firstElem.toArray(r,0);for(var _i82=1,offset=0;_i82!==nBlocks;++_i82){offset+=blockSize;array[_i82].toArray(r,offset);}}return r;}function arraysEqual(a,b){if(a.length!==b.length)return false;for(var _i83=0,l=a.length;_i83/gm;function resolveIncludes(string){return string.replace(includePattern,includeReplacer);}function includeReplacer(match,include){var string=ShaderChunk[include];if(string===undefined){throw new Error('Can not resolve #include <'+include+'>');}return resolveIncludes(string);}// Unroll Loops var deprecatedUnrollLoopPattern=/#pragma unroll_loop[\s]+?for \( int i \= (\d+)\; i < (\d+)\; i \+\+ \) \{([\s\S]+?)(?=\})\}/g;var unrollLoopPattern=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function unrollLoops(string){return string.replace(unrollLoopPattern,loopReplacer).replace(deprecatedUnrollLoopPattern,deprecatedLoopReplacer);}function deprecatedLoopReplacer(match,start,end,snippet){console.warn('WebGLProgram: #pragma unroll_loop shader syntax is deprecated. Please use #pragma unroll_loop_start syntax instead.');return loopReplacer(match,start,end,snippet);}function loopReplacer(match,start,end,snippet){var string='';for(var _i94=parseInt(start);_i940?renderer.gammaFactor:1.0;var customExtensions=parameters.isWebGL2?'':generateExtensions(parameters);var customDefines=generateDefines(defines);var program=gl.createProgram();var prefixVertex,prefixFragment;var versionString=parameters.glslVersion?'#version '+parameters.glslVersion+'\n':'';if(parameters.isRawShaderMaterial){prefixVertex=[customDefines].filter(filterEmptyLine).join('\n');if(prefixVertex.length>0){prefixVertex+='\n';}prefixFragment=[customExtensions,customDefines].filter(filterEmptyLine).join('\n');if(prefixFragment.length>0){prefixFragment+='\n';}}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 GAMMA_FACTOR '+gammaFactorDefine,'#define MAX_BONES '+parameters.maxBones,parameters.useFog&¶meters.fog?'#define USE_FOG':'',parameters.useFog&¶meters.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&¶meters.objectSpaceNormalMap?'#define OBJECTSPACE_NORMALMAP':'',parameters.normalMap&¶meters.tangentSpaceNormalMap?'#define TANGENTSPACE_NORMALMAP':'',parameters.clearcoatMap?'#define USE_CLEARCOATMAP':'',parameters.clearcoatRoughnessMap?'#define USE_CLEARCOAT_ROUGHNESSMAP':'',parameters.clearcoatNormalMap?'#define USE_CLEARCOAT_NORMALMAP':'',parameters.displacementMap&¶meters.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.transmissionMap?'#define USE_TRANSMISSIONMAP':'',parameters.vertexTangents?'#define USE_TANGENT':'',parameters.vertexColors?'#define USE_COLOR':'',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&¶meters.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&¶meters.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','#ifdef USE_COLOR',' attribute vec3 color;','#endif','#ifdef USE_MORPHTARGETS',' attribute vec3 morphTarget0;',' attribute vec3 morphTarget1;',' attribute vec3 morphTarget2;',' attribute vec3 morphTarget3;',' #ifdef USE_MORPHNORMALS',' attribute vec3 morphNormal0;',' attribute vec3 morphNormal1;',' attribute vec3 morphNormal2;',' attribute vec3 morphNormal3;',' #else',' attribute vec3 morphTarget4;',' attribute vec3 morphTarget5;',' attribute vec3 morphTarget6;',' attribute vec3 morphTarget7;',' #endif','#endif','#ifdef USE_SKINNING',' attribute vec4 skinIndex;',' attribute vec4 skinWeight;','#endif','\n'].filter(filterEmptyLine).join('\n');prefixFragment=[customExtensions,generatePrecision(parameters),'#define SHADER_NAME '+parameters.shaderName,customDefines,parameters.alphaTest?'#define ALPHATEST '+parameters.alphaTest+(parameters.alphaTest%1?'':'.0'):'',// add '.0' if integer '#define GAMMA_FACTOR '+gammaFactorDefine,parameters.useFog&¶meters.fog?'#define USE_FOG':'',parameters.useFog&¶meters.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&¶meters.objectSpaceNormalMap?'#define OBJECTSPACE_NORMALMAP':'',parameters.normalMap&¶meters.tangentSpaceNormalMap?'#define TANGENTSPACE_NORMALMAP':'',parameters.clearcoatMap?'#define USE_CLEARCOATMAP':'',parameters.clearcoatRoughnessMap?'#define USE_CLEARCOAT_ROUGHNESSMAP':'',parameters.clearcoatNormalMap?'#define USE_CLEARCOAT_NORMALMAP':'',parameters.specularMap?'#define USE_SPECULARMAP':'',parameters.roughnessMap?'#define USE_ROUGHNESSMAP':'',parameters.metalnessMap?'#define USE_METALNESSMAP':'',parameters.alphaMap?'#define USE_ALPHAMAP':'',parameters.sheen?'#define USE_SHEEN':'',parameters.transmissionMap?'#define USE_TRANSMISSIONMAP':'',parameters.vertexTangents?'#define USE_TANGENT':'',parameters.vertexColors||parameters.instancingColor?'#define USE_COLOR':'',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&¶meters.rendererExtensionFragDepth?'#define USE_LOGDEPTHBUF_EXT':'',(parameters.extensionShaderTextureLOD||parameters.envMap)&¶meters.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':'',ShaderChunk['encodings_pars_fragment'],// this code is required here because it is used by the various encoding/decoding function defined below parameters.map?getTexelDecodingFunction('mapTexelToLinear',parameters.mapEncoding):'',parameters.matcap?getTexelDecodingFunction('matcapTexelToLinear',parameters.matcapEncoding):'',parameters.envMap?getTexelDecodingFunction('envMapTexelToLinear',parameters.envMapEncoding):'',parameters.emissiveMap?getTexelDecodingFunction('emissiveMapTexelToLinear',parameters.emissiveMapEncoding):'',parameters.lightMap?getTexelDecodingFunction('lightMapTexelToLinear',parameters.lightMapEncoding):'',getTexelEncodingFunction('linearToOutputTexel',parameters.outputEncoding),parameters.depthPacking?'#define DEPTH_PACKING '+parameters.depthPacking:'','\n'].filter(filterEmptyLine).join('\n');}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&¶meters.isRawShaderMaterial!==true){// GLSL 3.0 conversion for built-in materials and ShaderMaterial versionString='#version 300 es\n';prefixVertex=['#define attribute in','#define varying out','#define texture2D texture'].join('\n')+'\n'+prefixVertex;prefixFragment=['#define varying in',parameters.glslVersion===GLSL3?'':'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('\n')+'\n'+prefixFragment;}var vertexGlsl=versionString+prefixVertex+vertexShader;var fragmentGlsl=versionString+prefixFragment+fragmentShader;// console.log( '*VERTEX*', vertexGlsl ); // console.log( '*FRAGMENT*', fragmentGlsl ); var glVertexShader=WebGLShader(gl,35633,vertexGlsl);var 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){var programLog=gl.getProgramInfoLog(program).trim();var vertexLog=gl.getShaderInfoLog(glVertexShader).trim();var fragmentLog=gl.getShaderInfoLog(glFragmentShader).trim();var runnable=true;var haveDiagnostics=true;if(gl.getProgramParameter(program,35714)===false){runnable=false;var vertexErrors=getShaderErrors(gl,glVertexShader,'vertex');var fragmentErrors=getShaderErrors(gl,glFragmentShader,'fragment');console.error('THREE.WebGLProgram: shader error: ',gl.getError(),'35715',gl.getProgramParameter(program,35715),'gl.getProgramInfoLog',programLog,vertexErrors,fragmentErrors);//xzw add: if(fragmentErrors){console.log(fragmentGlsl.split("\n").map((a,i)=>"".concat(i+1).padEnd(5)+a).join("\n"));}else {console.log(vertexGlsl.split("\n").map((a,i)=>"".concat(i+1).padEnd(5)+a).join("\n"));}}else if(programLog!==''){console.warn('THREE.WebGLProgram: gl.getProgramInfoLog()',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 var cachedUniforms;this.getUniforms=function(){if(cachedUniforms===undefined){cachedUniforms=new WebGLUniforms(gl,program);}return cachedUniforms;};// set up caching for attribute locations var cachedAttributes;this.getAttributes=function(){if(cachedAttributes===undefined){cachedAttributes=fetchAttributeLocations(gl,program);}return cachedAttributes;};// free resource this.destroy=function(){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;}function WebGLPrograms(renderer,cubemaps,extensions,capabilities,bindingStates,clipping){var programs=[];var isWebGL2=capabilities.isWebGL2;var logarithmicDepthBuffer=capabilities.logarithmicDepthBuffer;var floatVertexTextures=capabilities.floatVertexTextures;var maxVertexUniforms=capabilities.maxVertexUniforms;var vertexTextures=capabilities.vertexTextures;var precision=capabilities.precision;var 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'};var parameterNames=['precision','isWebGL2','supportsVertexTextures','outputEncoding','instancing','instancingColor','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','transmissionMap'];function getMaxBones(object){var skeleton=object.skeleton;var 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) var nVertexUniforms=maxVertexUniforms;var nVertexMatrices=Math.floor((nVertexUniforms-20)/4);var maxBones=Math.min(nVertexMatrices,bones.length);if(maxBones0,maxBones:maxBones,useVertexTexture: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,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,alphaTest:material.alphaTest,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){var array=[];if(parameters.shaderID){array.push(parameters.shaderID);}else {array.push(parameters.fragmentShader);array.push(parameters.vertexShader);}if(parameters.defines!==undefined){for(var name in parameters.defines){array.push(name);array.push(parameters.defines[name]);}}if(parameters.isRawShaderMaterial===false){for(var _i95=0;_i951)opaque.sort(customOpaqueSort||painterSortStable);if(transparent.length>1)transparent.sort(customTransparentSort||reversePainterSortStable);}function finish(){// Clear references from inactive renderItems in the list for(var _i97=renderItemsIndex,il=renderItems.length;_i970){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;var 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){var directionalLength=0;var pointLength=0;var spotLength=0;var rectAreaLength=0;var hemiLength=0;var viewMatrix=camera.matrixWorldInverse;for(var _i101=0,l=lights.length;_i1011&&arguments[1]!==undefined?arguments[1]:0;var renderState;if(renderStates.has(scene)===false){renderState=new WebGLRenderState(extensions,capabilities);renderStates.set(scene,[]);renderStates.get(scene).push(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: * } */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.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;};/** * 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.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;};var vsm_frag="uniform sampler2D shadow_pass;\nuniform vec2 resolution;\nuniform float radius;\n#include \nvoid main() {\n\tfloat mean = 0.0;\n\tfloat squared_mean = 0.0;\n\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy ) / resolution ) );\n\tfor ( float i = -1.0; i < 1.0 ; i += SAMPLE_RATE) {\n\t\t#ifdef HORIZONTAL_PASS\n\t\t\tvec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( i, 0.0 ) * radius ) / resolution ) );\n\t\t\tmean += distribution.x;\n\t\t\tsquared_mean += distribution.y * distribution.y + distribution.x * distribution.x;\n\t\t#else\n\t\t\tfloat depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, i ) * radius ) / resolution ) );\n\t\t\tmean += depth;\n\t\t\tsquared_mean += depth * depth;\n\t\t#endif\n\t}\n\tmean = mean * HALF_SAMPLE_RATE;\n\tsquared_mean = squared_mean * HALF_SAMPLE_RATE;\n\tfloat std_dev = sqrt( squared_mean - mean * mean );\n\tgl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) );\n}";var vsm_vert="void main() {\n\tgl_Position = vec4( position, 1.0 );\n}";function WebGLShadowMap(_renderer,_objects,maxTextureSize){var _frustum=new Frustum();var _shadowMapSize=new Vector2(),_viewportSize=new Vector2(),_viewport=new Vector4(),_depthMaterials=[],_distanceMaterials=[],_materialCache={};var shadowSide={0:BackSide,1:FrontSide,2:DoubleSide};var shadowMaterialVertical=new ShaderMaterial({defines:{SAMPLE_RATE:2.0/8.0,HALF_SAMPLE_RATE:1.0/8.0},uniforms:{shadow_pass:{value:null},resolution:{value:new Vector2()},radius:{value:4.0}},vertexShader:vsm_vert,fragmentShader:vsm_frag});var shadowMaterialHorizontal=shadowMaterialVertical.clone();shadowMaterialHorizontal.defines.HORIZONTAL_PASS=1;var fullScreenTri=new BufferGeometry();fullScreenTri.setAttribute('position',new BufferAttribute(new Float32Array([-1,-1,0.5,3,-1,0.5,-1,3,0.5]),3));var fullScreenMesh=new Mesh(fullScreenTri,shadowMaterialVertical);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;var currentRenderTarget=_renderer.getRenderTarget();var activeCubeFace=_renderer.getActiveCubeFace();var activeMipmapLevel=_renderer.getActiveMipmapLevel();var _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(var _i102=0,il=lights.length;_i102maxTextureSize||_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){var 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){var _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();var viewportCount=shadow.getViewportCount();for(var vp=0;vp0;}var useSkinning=false;if(object.isSkinnedMesh===true){if(material.skinning===true){useSkinning=true;}else {console.warn('THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:',object);}}var useInstancing=object.isInstancedMesh===true;result=getMaterialVariant(useMorphing,useSkinning,useInstancing);}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;if(type===VSMShadowMap){result.side=material.shadowSide!==null?material.shadowSide:material.side;}else {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(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;var 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);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=1.0;}else if(glVersion.indexOf('OpenGL ES')!==-1){version=parseFloat(/^OpenGL ES (\d)/.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,10241,9728);gl.texParameteri(type,10240,9728);for(var _i104=0;_i104maxSize||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){var floor=needsPowerOfTwo?MathUtils.floorPowerOfTwo:Math.floor;var width=floor(scale*image.width);var height=floor(scale*image.height);if(_canvas===undefined)_canvas=createCanvas(width,height);// cube textures can't reuse the same canvas var canvas=needsNewCanvas?createCanvas(width,height):_canvas;canvas.width=width;canvas.height=height;var 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(image){return MathUtils.isPowerOfTwo(image.width)&&MathUtils.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,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;}function getInternalFormat(internalFormatName,glFormat,glType){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+'\'');}var internalFormat=glFormat;if(glFormat===6403){if(glType===5126)internalFormat=33326;if(glType===5131)internalFormat=33325;if(glType===5121)internalFormat=33321;}if(glFormat===6407){if(glType===5126)internalFormat=34837;if(glType===5131)internalFormat=34843;if(glType===5121)internalFormat=32849;}if(glFormat===6408){if(glType===5126)internalFormat=34836;if(glType===5131)internalFormat=34842;if(glType===5121)internalFormat=32856;}if(internalFormat===33325||internalFormat===33326||internalFormat===34842||internalFormat===34836){extensions.get('EXT_color_buffer_float');}return internalFormat;}// 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){var texture=event.target;texture.removeEventListener('dispose',onTextureDispose);deallocateTexture(texture);if(texture.isVideoTexture){_videoTextures.delete(texture);}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(textureProperties.__webglInit===undefined)return;_gl.deleteTexture(textureProperties.__webglTexture);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.isWebGLCubeRenderTarget){for(var _i105=0;_i105<6;_i105++){_gl.deleteFramebuffer(renderTargetProperties.__webglFramebuffer[_i105]);if(renderTargetProperties.__webglDepthbuffer)_gl.deleteRenderbuffer(renderTargetProperties.__webglDepthbuffer[_i105]);}}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);}properties.remove(renderTarget.texture);properties.remove(renderTarget);}// var textureUnits=0;function resetTextureUnits(){textureUnits=0;}function allocateTextureUnit(){var 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){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');}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){var 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){var 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){var 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);}var wrappingToGL={[RepeatWrapping]:10497,[ClampToEdgeWrapping]:33071,[MirroredRepeatWrapping]:33648};var 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.');}}var 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&&(isWebGL2||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 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){var textureType=3553;if(texture.isDataTexture2DArray)textureType=35866;if(texture.isDataTexture3D)textureType=32879;initTexture(textureProperties,texture);state.activeTexture(33984+slot);state.bindTexture(textureType,textureProperties.__webglTexture);_gl.pixelStorei(37440,texture.flipY);_gl.pixelStorei(37441,texture.premultiplyAlpha);_gl.pixelStorei(3317,texture.unpackAlignment);var needsPowerOfTwo=textureNeedsPowerOfTwo(texture)&&isPowerOfTwo(texture.image)===false;var image=resizeImage(texture.image,needsPowerOfTwo,false,maxTextureSize);var supportsMips=isPowerOfTwo(image)||isWebGL2,glFormat=utils.convert(texture.format);var glType=utils.convert(texture.type),glInternalFormat=getInternalFormat(texture.internalFormat,glFormat,glType);setTextureParameters(textureType,texture,supportsMips);var mipmap;var mipmaps=texture.mipmaps;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$1){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=UnsignedShortType;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$1){console.warn('THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.');texture.type=UnsignedInt248Type$1;glType=utils.convert(texture.type);}}// 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){for(var _i106=0,il=mipmaps.length;_i1060&&supportsMips){for(var _i108=0,_il10=mipmaps.length;_i108<_il10;_i108++){mipmap=mipmaps[_i108];state.texImage2D(3553,_i108,glInternalFormat,glFormat,glType,mipmap);}texture.generateMipmaps=false;textureProperties.__maxMipLevel=mipmaps.length-1;}else {state.texImage2D(3553,0,glInternalFormat,glFormat,glType,image);textureProperties.__maxMipLevel=0;}}if(textureNeedsGenerateMipmaps(texture,supportsMips)){generateMipmap(textureType,texture,image.width,image.height);}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(33984+slot);state.bindTexture(34067,textureProperties.__webglTexture);_gl.pixelStorei(37440,texture.flipY);var isCompressed=texture&&(texture.isCompressedTexture||texture.image[0].isCompressedTexture);var isDataTexture=texture.image[0]&&texture.image[0].isDataTexture;var cubeImage=[];for(var _i109=0;_i109<6;_i109++){if(!isCompressed&&!isDataTexture){cubeImage[_i109]=resizeImage(texture.image[_i109],false,true,maxCubemapSize);}else {cubeImage[_i109]=isDataTexture?texture.image[_i109].image:texture.image[_i109];}}var image=cubeImage[0],supportsMips=isPowerOfTwo(image)||isWebGL2,glFormat=utils.convert(texture.format),glType=utils.convert(texture.type),glInternalFormat=getInternalFormat(texture.internalFormat,glFormat,glType);setTextureParameters(34067,texture,supportsMips);var mipmaps;if(isCompressed){for(var _i110=0;_i110<6;_i110++){mipmaps=cubeImage[_i110].mipmaps;for(var j=0;j0&&arguments[0]!==undefined?arguments[0]:[];PerspectiveCamera.call(this);this.cameras=array;}ArrayCamera.prototype=Object.assign(Object.create(PerspectiveCamera.prototype),{constructor:ArrayCamera,isArrayCamera:true});function Group(){Object3D.call(this);this.type='Group';}Group.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Group,isGroup:true});function WebXRController(){this._targetRay=null;this._grip=null;this._hand=null;}Object.assign(WebXRController.prototype,{constructor:WebXRController,getHandSpace:function 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};if(window.XRHand){for(var _i115=0;_i115<=window.XRHand.LITTLE_PHALANX_TIP;_i115++){// The transform of this joint will be updated with the joint pose on each frame var joint=new Group();joint.matrixAutoUpdate=false;joint.visible=false;this._hand.joints.push(joint);// ?? this._hand.add(joint);}}}return this._hand;},getTargetRaySpace:function getTargetRaySpace(){if(this._targetRay===null){this._targetRay=new Group();this._targetRay.matrixAutoUpdate=false;this._targetRay.visible=false;}return this._targetRay;},getGripSpace:function getGripSpace(){if(this._grip===null){this._grip=new Group();this._grip.matrixAutoUpdate=false;this._grip.visible=false;}return this._grip;},dispatchEvent:function 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:function 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:function update(inputSource,frame,referenceSpace){var inputPose=null;var gripPose=null;var handPose=null;var targetRay=this._targetRay;var grip=this._grip;var hand=this._hand;if(inputSource&&frame.session.visibilityState!=='visible-blurred'){if(hand&&inputSource.hand){handPose=true;for(var _i116=0;_i116<=window.XRHand.LITTLE_PHALANX_TIP;_i116++){if(inputSource.hand[_i116]){// Update the joints groups with the XRJoint poses var jointPose=frame.getJointPose(inputSource.hand[_i116],referenceSpace);var joint=hand.joints[_i116];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 pinch var indexTip=hand.joints[window.XRHand.INDEX_PHALANX_TIP];var thumbTip=hand.joints[window.XRHand.THUMB_PHALANX_TIP];var distance=indexTip.position.distanceTo(thumbTip.position);var distanceToPinch=0.02;var 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(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(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(targetRay!==null){targetRay.visible=inputPose!==null;}if(grip!==null){grip.visible=gripPose!==null;}if(hand!==null){hand.visible=handPose!==null;}return this;}});function WebXRManager(renderer,gl){var scope=this;var session=null;var framebufferScaleFactor=1.0;var referenceSpace=null;var referenceSpaceType='local-floor';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 cameras=[cameraL,cameraR];var cameraVR=new ArrayCamera();cameraVR.layers.enable(1);cameraVR.layers.enable(2);var _currentDepthNear=null;var _currentDepthFar=null;// this.enabled=false;this.isPresenting=false;this.getController=function(index){var controller=controllers[index];if(controller===undefined){controller=new WebXRController();controllers[index]=controller;}return controller.getTargetRaySpace();};this.getControllerGrip=function(index){var controller=controllers[index];if(controller===undefined){controller=new WebXRController();controllers[index]=controller;}return controller.getGripSpace();};this.getHand=function(index){var controller=controllers[index];if(controller===undefined){controller=new WebXRController();controllers[index]=controller;}return controller.getHandSpace();};// function onSessionEvent(event){var 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();// 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;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.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();if(attributes.xrCompatible!==true){gl.makeXRCompatible();}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 _i117=0;_i1170)renderObjects(opaqueObjects,scene,camera);if(transparentObjects.length>0)renderObjects(transparentObjects,scene,camera);// if(scene.isScene===true)scene.onAfterRender(_this,scene,camera);// if(_currentRenderTarget!==null){// Generate mipmap if we're using any kind of mipmap filtering textures.updateRenderTargetMipmap(_currentRenderTarget);// resolve multisample renderbuffers to a single-sample texture if necessary textures.updateMultisampleRenderTarget(_currentRenderTarget);}// 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(); renderStateStack.pop();if(renderStateStack.length>0){currentRenderState=renderStateStack[renderStateStack.length-1];}else {currentRenderState=null;}currentRenderList=null;};function projectObject(object,camera,groupOrder,sortObjects){if(object.visible===false)return;var 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);}var geometry=objects.update(object);var material=object.material;if(material.visible){currentRenderList.push(object,geometry,material,groupOrder,_vector3.z,null);}}}else if(object.isImmediateRenderObject){if(sortObjects){_vector3.setFromMatrixPosition(object.matrixWorld).applyMatrix4(_projScreenMatrix);}currentRenderList.push(object,null,object.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);}var _geometry2=objects.update(object);var _material=object.material;if(Array.isArray(_material)){var groups=_geometry2.groups;for(var _i126=0,l=groups.length;_i126 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=MathUtils.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,textures);p_uniforms.setValue(_gl,'boneTextureSize',skeleton.boneTextureSize);}else {p_uniforms.setOptional(_gl,skeleton,'boneMatrices');}}}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);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.setFramebuffer=function(value){if(_framebuffer!==value&&_currentRenderTarget===null)_gl.bindFramebuffer(36160,value);_framebuffer=value;};this.getActiveCubeFace=function(){return _currentActiveCubeFace;};this.getActiveMipmapLevel=function(){return _currentActiveMipmapLevel;};this.getRenderList=function(){return currentRenderList;};this.setRenderList=function(renderList){currentRenderList=renderList;};this.getRenderTarget=function(){return _currentRenderTarget;};this.setRenderTarget=function(renderTarget){var activeCubeFace=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var activeMipmapLevel=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;_currentRenderTarget=renderTarget;_currentActiveCubeFace=activeCubeFace;_currentActiveMipmapLevel=activeMipmapLevel;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.isWebGLCubeRenderTarget){framebuffer=__webglFramebuffer[activeCubeFace];isCube=true;}else if(renderTarget.isWebGLMultisampleRenderTarget){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;}if(_currentFramebuffer!==framebuffer){_gl.bindFramebuffer(36160,framebuffer);_currentFramebuffer=framebuffer;}state.viewport(_currentViewport);state.scissor(_currentScissor);state.setScissorTest(_currentScissorTest);if(isCube){var textureProperties=properties.get(renderTarget.texture);_gl.framebufferTexture2D(36160,36064,34069+activeCubeFace,textureProperties.__webglTexture,activeMipmapLevel);}};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;}var framebuffer=properties.get(renderTarget).__webglFramebuffer;if(renderTarget.isWebGLCubeRenderTarget&&activeCubeFaceIndex!==undefined){framebuffer=framebuffer[activeCubeFaceIndex];}if(framebuffer){var restore=false;if(framebuffer!==_currentFramebuffer){_gl.bindFramebuffer(36160,framebuffer);restore=true;}try{var texture=renderTarget.texture;var textureFormat=texture.format;var 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;}if(textureType!==UnsignedByteType&&utils.convert(textureType)!==_gl.getParameter(35738)&&// IE11, Edge and Chrome Mac < 52 (#9513) !(textureType===FloatType&&(capabilities.isWebGL2||extensions.get('OES_texture_float')||extensions.get('WEBGL_color_buffer_float')))&&// Chrome Mac >= 52 and Firefox !(textureType===HalfFloatType&&(capabilities.isWebGL2?extensions.get('EXT_color_buffer_float'):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(36160)===36053){// 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(36160,_currentFramebuffer);}}}};this.copyFramebufferToTexture=function(position,texture){var level=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;var levelScale=Math.pow(2,-level);var width=Math.floor(texture.image.width*levelScale);var height=Math.floor(texture.image.height*levelScale);var glFormat=utils.convert(texture.format);textures.setTexture2D(texture,0);_gl.copyTexImage2D(3553,level,glFormat,position.x,position.y,width,height,0);state.unbindTexture();};this.copyTextureToTexture=function(position,srcTexture,dstTexture){var level=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0;var width=srcTexture.image.width;var height=srcTexture.image.height;var glFormat=utils.convert(dstTexture.format);var 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.initTexture=function(texture){textures.setTexture2D(texture,0);state.unbindTexture();};this.resetState=function(){state.reset();bindingStates.reset();};if(typeof __THREE_DEVTOOLS__!=='undefined'){__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe',{detail:this}));// eslint-disable-line no-undef }}function WebGL1Renderer(parameters){WebGLRenderer.call(this,parameters);}WebGL1Renderer.prototype=Object.assign(Object.create(WebGLRenderer.prototype),{constructor:WebGL1Renderer,isWebGL1Renderer:true});class FogExp2{constructor(color,density){Object.defineProperty(this,'isFogExp2',{value:true});this.name='';this.color=new Color(color);this.density=density!==undefined?density:0.00025;}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,far){Object.defineProperty(this,'isFog',{value:true});this.name='';this.color=new Color(color);this.near=near!==undefined?near:1;this.far=far!==undefined?far:1000;}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();Object.defineProperty(this,'isScene',{value: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}));// eslint-disable-line no-undef }}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){var data=super.toJSON(meta);if(this.background!==null)data.object.background=this.background.toJSON(meta);if(this.environment!==null)data.object.environment=this.environment.toJSON(meta);if(this.fog!==null)data.object.fog=this.fog.toJSON();return data;}}function InterleavedBuffer(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=MathUtils.generateUUID();}Object.defineProperty(InterleavedBuffer.prototype,'needsUpdate',{set:function set(value){if(value===true)this.version++;}});Object.assign(InterleavedBuffer.prototype,{isInterleavedBuffer:true,onUploadCallback:function onUploadCallback(){},setUsage:function setUsage(value){this.usage=value;return this;},copy:function 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:function copyAt(index1,attribute,index2){index1*=this.stride;index2*=attribute.stride;for(var _i129=0,l=this.stride;_i1291&&arguments[1]!==undefined?arguments[1]:0;this.array.set(value,offset);return this;},clone:function clone(data){if(data.arrayBuffers===undefined){data.arrayBuffers={};}if(this.array.buffer._uuid===undefined){this.array.buffer._uuid=MathUtils.generateUUID();}if(data.arrayBuffers[this.array.buffer._uuid]===undefined){data.arrayBuffers[this.array.buffer._uuid]=this.array.slice(0).buffer;}var array=new this.array.constructor(data.arrayBuffers[this.array.buffer._uuid]);var ib=new InterleavedBuffer(array,this.stride);ib.setUsage(this.usage);return ib;},onUpload:function onUpload(callback){this.onUploadCallback=callback;return this;},toJSON:function 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=MathUtils.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};}});var _vector$6=new Vector3();function InterleavedBufferAttribute(interleavedBuffer,itemSize,offset,normalized){this.name='';this.data=interleavedBuffer;this.itemSize=itemSize;this.offset=offset;this.normalized=normalized===true;}Object.defineProperties(InterleavedBufferAttribute.prototype,{count:{get:function get(){return this.data.count;}},array:{get:function get(){return this.data.array;}},needsUpdate:{set:function set(value){this.data.needsUpdate=value;}}});Object.assign(InterleavedBufferAttribute.prototype,{isInterleavedBufferAttribute:true,applyMatrix4:function applyMatrix4(m){for(var _i130=0,l=this.data.count;_i130, * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * rotation: , * sizeAttenuation: * } */function SpriteMaterial(parameters){Material.call(this);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);}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.alphaMap=source.alphaMap;this.rotation=source.rotation;this.sizeAttenuation=source.sizeAttenuation;return this;};var _geometry;var _intersectPoint=new Vector3();var _worldScale=new Vector3();var _mvPosition=new Vector3();var _alignedPosition=new Vector2();var _rotatedPosition=new Vector2();var _viewWorldMatrix=new Matrix4();var _vA$1=new Vector3();var _vB$1=new Vector3();var _vC$1=new Vector3();var _uvA$1=new Vector2();var _uvB$1=new Vector2();var _uvC$1=new Vector2();function Sprite$1(material){Object3D.call(this);this.type='Sprite';if(_geometry===undefined){_geometry=new BufferGeometry();var 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]);var 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);}Sprite$1.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Sprite$1,isSprite:true,raycast:function 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);}var rotation=this.material.rotation;var sin,cos;if(rotation!==0){cos=Math.cos(rotation);sin=Math.sin(rotation);}var center=this.center;transformVertex(_vA$1.set(-0.5,-0.5,0),_mvPosition,center,_worldScale,sin,cos);transformVertex(_vB$1.set(0.5,-0.5,0),_mvPosition,center,_worldScale,sin,cos);transformVertex(_vC$1.set(0.5,0.5,0),_mvPosition,center,_worldScale,sin,cos);_uvA$1.set(0,0);_uvB$1.set(1,0);_uvC$1.set(1,1);// check first triangle var intersect=raycaster.ray.intersectTriangle(_vA$1,_vB$1,_vC$1,false,_intersectPoint);if(intersect===null){// check second triangle transformVertex(_vB$1.set(-0.5,0.5,0),_mvPosition,center,_worldScale,sin,cos);_uvB$1.set(0,1);intersect=raycaster.ray.intersectTriangle(_vA$1,_vC$1,_vB$1,false,_intersectPoint);if(intersect===null){return;}}var distance=raycaster.ray.origin.distanceTo(_intersectPoint);if(distanceraycaster.far)return;intersects.push({distance:distance,point:_intersectPoint.clone(),uv:Triangle.getUV(_intersectPoint,_vA$1,_vB$1,_vC$1,_uvA$1,_uvB$1,_uvC$1,new Vector2()),face:null,object:this});},copy:function copy(source){Object3D.prototype.copy.call(this,source);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);}var _v1$4=new Vector3();var _v2$2=new Vector3();function LOD(){Object3D.call(this);this._currentLevel=0;this.type='LOD';Object.defineProperties(this,{levels:{enumerable:true,value:[]}});this.autoUpdate=true;}LOD.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:LOD,isLOD:true,copy:function copy(source){Object3D.prototype.copy.call(this,source,false);var levels=source.levels;for(var _i133=0,l=levels.length;_i1331&&arguments[1]!==undefined?arguments[1]:0;distance=Math.abs(distance);var levels=this.levels;var l;for(l=0;l0){var _i134,l;for(_i134=1,l=levels.length;_i1340){_v1$4.setFromMatrixPosition(this.matrixWorld);var distance=raycaster.ray.origin.distanceTo(_v1$4);this.getObjectForDistance(distance).raycast(raycaster,intersects);}},update:function update(camera){var levels=this.levels;if(levels.length>1){_v1$4.setFromMatrixPosition(camera.matrixWorld);_v2$2.setFromMatrixPosition(this.matrixWorld);var distance=_v1$4.distanceTo(_v2$2)/camera.zoom;levels[0].object.visible=true;var _i135,l;for(_i135=1,l=levels.length;_i135=levels[_i135].distance){levels[_i135-1].object.visible=false;levels[_i135].object.visible=true;}else {break;}}this._currentLevel=_i135-1;for(;_i1350&&arguments[0]!==undefined?arguments[0]:[];var boneInverses=arguments.length>1&&arguments[1]!==undefined?arguments[1]:[];this.uuid=MathUtils.generateUUID();this.bones=bones.slice(0);this.boneInverses=boneInverses;this.boneMatrices=null;this.boneTexture=null;this.boneTextureSize=0;this.frame=-1;this.init();}Object.assign(Skeleton.prototype,{init:function init(){var bones=this.bones;var 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(var _i139=0,il=this.bones.length;_i139, * 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.morphTargets=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;this.morphTargets=source.morphTargets;return this;};var _start=new Vector3();var _end=new Vector3();var _inverseMatrix$1=new Matrix4();var _ray$1=new Ray();var _sphere$2=new Sphere();function Line(){var geometry=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new BufferGeometry();var material=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new LineBasicMaterial();Object3D.call(this);this.type='Line';this.geometry=geometry;this.material=material;this.updateMorphTargets();}Line.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Line,isLine:true,copy:function copy(source){Object3D.prototype.copy.call(this,source);this.material=source.material;this.geometry=source.geometry;return this;},computeLineDistances: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 _i148=1,l=positionAttribute.count;_i148localThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);//Move back to world space for distance calculation var distance=raycaster.ray.origin.distanceTo(interRay);if(distanceraycaster.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:_i150,face:null,faceIndex:null,object:this});}}else {for(var _i151=0,_l7=positionAttribute.count-1;_i151<_l7;_i151+=step){vStart.fromBufferAttribute(positionAttribute,_i151);vEnd.fromBufferAttribute(positionAttribute,_i151+1);var _distSq=_ray$1.distanceSqToSegment(vStart,vEnd,interRay,interSegment);if(_distSq>localThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);//Move back to world space for distance calculation var _distance=raycaster.ray.origin.distanceTo(interRay);if(_distanceraycaster.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:_i151,face:null,faceIndex:null,object:this});}}}else if(geometry.isGeometry){var vertices=geometry.vertices;var nbVertices=vertices.length;for(var _i152=0;_i152localThresholdSq)continue;interRay.applyMatrix4(this.matrixWorld);//Move back to world space for distance calculation var _distance2=raycaster.ray.origin.distanceTo(interRay);if(_distance2raycaster.far)continue;intersects.push({distance:_distance2,// 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:_i152,face:null,faceIndex:null,object:this});}}},updateMorphTargets:function updateMorphTargets(){var geometry=this.geometry;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(var m=0,ml=morphAttribute.length;m0){console.error('THREE.Line.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.');}}}});var _start$1=new Vector3();var _end$1=new Vector3();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 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 _i153=0,l=positionAttribute.count;_i153, * opacity: , * map: new THREE.Texture( ), * alphaMap: new THREE.Texture( ), * * size: , * sizeAttenuation: * * morphTargets: * } */function PointsMaterial(parameters){Material.call(this);this.type='PointsMaterial';this.color=new Color(0xffffff);this.map=null;this.alphaMap=null;this.size=1;this.sizeAttenuation=true;this.morphTargets=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.alphaMap=source.alphaMap;this.size=source.size;this.sizeAttenuation=source.sizeAttenuation;this.morphTargets=source.morphTargets;return this;};var _inverseMatrix$2=new Matrix4();var _ray$2=new Ray();var _sphere$3=new Sphere();var _position$1=new Vector3();function Points(){var geometry=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new BufferGeometry();var material=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new PointsMaterial();Object3D.call(this);this.type='Points';this.geometry=geometry;this.material=material;this.updateMorphTargets();}Points.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Points,isPoints:true,copy:function copy(source){Object3D.prototype.copy.call(this,source);this.material=source.material;this.geometry=source.geometry;return this;},raycast:function raycast(raycaster,intersects){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$3.copy(geometry.boundingSphere);_sphere$3.applyMatrix4(matrixWorld);_sphere$3.radius+=threshold;if(raycaster.ray.intersectsSphere(_sphere$3)===false)return;// _inverseMatrix$2.copy(matrixWorld).invert();_ray$2.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2);var localThreshold=threshold/((this.scale.x+this.scale.y+this.scale.z)/3);var localThresholdSq=localThreshold*localThreshold;if(geometry.isBufferGeometry){var index=geometry.index;var attributes=geometry.attributes;var positionAttribute=attributes.position;if(index!==null){var indices=index.array;for(var _i155=0,il=indices.length;_i1550){var morphAttribute=morphAttributes[keys[0]];if(morphAttribute!==undefined){this.morphTargetInfluences=[];this.morphTargetDictionary={};for(var m=0,ml=morphAttribute.length;m0){console.error('THREE.Points.updateMorphTargets() does not support THREE.Geometry. Use THREE.BufferGeometry instead.');}}}});function testPoint(point,index,localThresholdSq,matrixWorld,raycaster,intersects,object){var rayPointDistanceSq=_ray$2.distanceSqToPoint(point);if(rayPointDistanceSqraycaster.far)return;intersects.push({distance:distance,distanceToRay:Math.sqrt(rayPointDistanceSq),point:intersectPoint,index:index,face:null,object:object});}}function VideoTexture(video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy){Texture.call(this,video,mapping,wrapS,wrapT,magFilter,minFilter,format,type,anisotropy);this.format=format!==undefined?format:RGBFormat;this.minFilter=minFilter!==undefined?minFilter:LinearFilter;this.magFilter=magFilter!==undefined?magFilter:LinearFilter;this.generateMipmaps=false;var scope=this;function updateVideo(){scope.needsUpdate=true;video.requestVideoFrameCallback(updateVideo);}if('requestVideoFrameCallback'in video){video.requestVideoFrameCallback(updateVideo);}}VideoTexture.prototype=Object.assign(Object.create(Texture.prototype),{constructor:VideoTexture,clone:function clone(){return new this.constructor(this.image).copy(this);},isVideoTexture:true,update:function update(){var video=this.image;var hasVideoFrameCallback='requestVideoFrameCallback'in video;if(hasVideoFrameCallback===false&&video.readyState>=video.HAVE_CURRENT_DATA){this.needsUpdate=true;}}});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;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;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$1;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;var _geometryId=0;// Geometry uses even numbers as Id var _m1$3=new Matrix4();var _obj$1=new Object3D();var _offset$1=new Vector3();function Geometry(){Object.defineProperty(this,'id',{value:_geometryId+=2});this.uuid=MathUtils.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,applyMatrix4:function applyMatrix4(matrix){var normalMatrix=new Matrix3().getNormalMatrix(matrix);for(var _i158=0,il=this.vertices.length;_i1580){for(var _i161=0;_i1610&&arguments[0]!==undefined?arguments[0]:true;var vertices=new Array(this.vertices.length);for(var v=0,vl=this.vertices.length;v0){this.normalsNeedUpdate=true;}},computeFlatVertexNormals:function computeFlatVertexNormals(){this.computeFaceNormals();for(var f=0,fl=this.faces.length;f0){this.normalsNeedUpdate=true;}},computeMorphNormals:function computeMorphNormals(){// save original normals // - create temp variables on first access // otherwise just copy (for faster repeated calls) for(var f=0,fl=this.faces.length;f2&&arguments[2]!==undefined?arguments[2]:0;if(!(geometry&&geometry.isGeometry)){console.error('THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.',geometry);return;}var normalMatrix;var vertexOffset=this.vertices.length,vertices1=this.vertices,vertices2=geometry.vertices,faces1=this.faces,faces2=geometry.faces,colors1=this.colors,colors2=geometry.colors;if(matrix!==undefined){normalMatrix=new Matrix3().getNormalMatrix(matrix);}// vertices for(var _i166=0,il=vertices2.length;_i1660&&arguments[0]!==undefined?arguments[0]:4;var verticesMap={};// Hashmap for looking up vertices by position coordinates (and making sure they are unique) var unique=[],changes=[];var precision=Math.pow(10,precisionPoints);for(var _i170=0,il=this.vertices.length;_i170=0;_i172--){var idx=faceIndicesToRemove[_i172];this.faces.splice(idx,1);for(var j=0,jl=this.faceVertexUvs.length;j0;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][_i177];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<0)data.data.colors=colors;if(uvs.length>0)data.data.uvs=[uvs];// temporal backward compatibility data.data.faces=faces;return data;},clone:function clone(){/* // Handle primitives const parameters = this.parameters; if ( parameters !== undefined ) { const values = []; for ( const key in parameters ) { values.push( parameters[ key ] ); } const 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 copy(source){// 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(var _i178=0,il=vertices.length;_i1780&&arguments[0]!==undefined?arguments[0]:1;var segments=arguments.length>1&&arguments[1]!==undefined?arguments[1]:8;var thetaStart=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;var thetaLength=arguments.length>3&&arguments[3]!==undefined?arguments[3]:Math.PI*2;super();this.type='CircleBufferGeometry';this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength};segments=Math.max(3,segments);// buffers var indices=[];var vertices=[];var normals=[];var uvs=[];// helper variables 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(var s=0,_i187=3;s<=segments;s++,_i187+=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[_i187]/radius+1)/2;uv.y=(vertices[_i187+1]/radius+1)/2;uvs.push(uv.x,uv.y);}// indices for(var _i188=1;_i188<=segments;_i188++){indices.push(_i188,_i188+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));}}class CircleGeometry extends Geometry{constructor(radius,segments,thetaStart,thetaLength){super();this.type='CircleGeometry';this.parameters={radius:radius,segments:segments,thetaStart:thetaStart,thetaLength:thetaLength};this.fromBufferGeometry(new CircleBufferGeometry(radius,segments,thetaStart,thetaLength));this.mergeVertices();}}class CylinderBufferGeometry extends BufferGeometry{constructor(){var radiusTop=arguments.length>0&&arguments[0]!==undefined?arguments[0]:1;var radiusBottom=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var height=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var radialSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:8;var heightSegments=arguments.length>4&&arguments[4]!==undefined?arguments[4]:1;var openEnded=arguments.length>5&&arguments[5]!==undefined?arguments[5]:false;var thetaStart=arguments.length>6&&arguments[6]!==undefined?arguments[6]:0;var thetaLength=arguments.length>7&&arguments[7]!==undefined?arguments[7]:Math.PI*2;super();this.type='CylinderBufferGeometry';this.parameters={radiusTop:radiusTop,radiusBottom:radiusBottom,height:height,radialSegments:radialSegments,heightSegments:heightSegments,openEnded:openEnded,thetaStart:thetaStart,thetaLength:thetaLength};var scope=this;radialSegments=Math.floor(radialSegments);heightSegments=Math.floor(heightSegments);// 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.setAttribute('position',new Float32BufferAttribute(vertices,3));this.setAttribute('normal',new Float32BufferAttribute(normals,3));this.setAttribute('uv',new Float32BufferAttribute(uvs,2));function generateTorso(){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(var 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(var 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(var _x2=0;_x20&&arguments[0]!==undefined?arguments[0]:1;var height=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var radialSegments=arguments.length>2&&arguments[2]!==undefined?arguments[2]:8;var heightSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;var openEnded=arguments.length>4&&arguments[4]!==undefined?arguments[4]:false;var thetaStart=arguments.length>5&&arguments[5]!==undefined?arguments[5]:0;var thetaLength=arguments.length>6&&arguments[6]!==undefined?arguments[6]:Math.PI*2;super(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};}}class PolyhedronBufferGeometry extends BufferGeometry{constructor(vertices,indices){var radius=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var detail=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0;super();this.type='PolyhedronBufferGeometry';this.parameters={vertices:vertices,indices:indices,radius:radius,detail:detail};// 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 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){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 _i190=0;_i1900.9&&min<0.1){if(x0<0.2)uvBuffer[_i195+0]+=1;if(x1<0.2)uvBuffer[_i195+2]+=1;if(x2<0.2)uvBuffer[_i195+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 _i196=0,j=0;_i1960&&arguments[0]!==undefined?arguments[0]:1;var detail=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;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];super(vertices,indices,radius,detail);this.type='DodecahedronBufferGeometry';this.parameters={radius:radius,detail:detail};}}class DodecahedronGeometry extends Geometry{constructor(radius,detail){super();this.type='DodecahedronGeometry';this.parameters={radius:radius,detail:detail};this.fromBufferGeometry(new DodecahedronBufferGeometry(radius,detail));this.mergeVertices();}}var _v0$2=new Vector3();var _v1$5=new Vector3();var _normal$1=new Vector3();var _triangle=new Triangle();class EdgesGeometry extends BufferGeometry{constructor(geometry,thresholdAngle){super();this.type='EdgesGeometry';this.parameters={thresholdAngle:thresholdAngle};thresholdAngle=thresholdAngle!==undefined?thresholdAngle:1;if(geometry.isGeometry){geometry=new BufferGeometry().fromGeometry(geometry);}var precisionPoints=4;var precision=Math.pow(10,precisionPoints);var thresholdDot=Math.cos(MathUtils.DEG2RAD*thresholdAngle);var indexAttr=geometry.getIndex();var positionAttr=geometry.getAttribute('position');var indexCount=indexAttr?indexAttr.count:positionAttr.count;var indexArr=[0,0,0];var vertKeys=['a','b','c'];var hashes=new Array(3);var edgeData={};var vertices=[];for(var _i197=0;_i19780*dim){minX=maxX=data[0];minY=maxY=data[1];for(var _i198=dim;_i198maxX)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=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 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 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.xb.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);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!==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){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 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);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=[];var i,len,start,end,list;for(i=0,len=holeIndices.length;i=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.x&&p.x>=mx&&hx!==p.x&&pointInTriangle(hym.x||p.x===m.x&§orContainsSector(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===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;i0||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=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){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;var 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=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 _i199=start,j=end-dim;_i1992&&points[l-1].equals(points[0])){points.pop();}}function addContour(vertices,contour){for(var _i202=0;_i202, // 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 ExtrudeBufferGeometry extends BufferGeometry{constructor(shapes,options){super();this.type='ExtrudeBufferGeometry';this.parameters={shapes:shapes,options:options};shapes=Array.isArray(shapes)?shapes:[shapes];var scope=this;var verticesArray=[];var uvArray=[];for(var _i203=0,l=shapes.length;_i203Number.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 _i204=0,il=contour.length,j=il-1,k=_i204+1;_i204 0; b -- ) { var t=b/bevelSegments;var z=bevelThickness*Math.cos(t*Math.PI/2);var _bs=bevelSize*Math.sin(t*Math.PI/2)+bevelOffset;// contract shape for(var _i206=0,_il27=contour.length;_i206<_il27;_i206++){var vert=scalePt2(contour[_i206],contourMovements[_i206],_bs);v(vert.x,vert.y,-z);}// expand holes for(var _h3=0,_hl3=holes.length;_h3<_hl3;_h3++){var _ahole3=holes[_h3];oneHoleMovements=holesMovements[_h3];for(var _i207=0,_il28=_ahole3.length;_i207<_il28;_i207++){var _vert=scalePt2(_ahole3[_i207],oneHoleMovements[_i207],_bs);v(_vert.x,_vert.y,-z);}}}var bs=bevelSize+bevelOffset;// Back facing vertices for(var _i208=0;_i208=0;_b5--){var _t=_b5/bevelSegments;var _z2=bevelThickness*Math.cos(_t*Math.PI/2);var _bs2=bevelSize*Math.sin(_t*Math.PI/2)+bevelOffset;// contract shape for(var _i210=0,_il29=contour.length;_i210<_il29;_i210++){var _vert4=scalePt2(contour[_i210],contourMovements[_i210],_bs2);v(_vert4.x,_vert4.y,depth+_z2);}// expand holes for(var _h4=0,_hl4=holes.length;_h4<_hl4;_h4++){var _ahole4=holes[_h4];oneHoleMovements=holesMovements[_h4];for(var _i211=0,_il30=_ahole4.length;_i211<_il30;_i211++){var _vert5=scalePt2(_ahole4[_i211],oneHoleMovements[_i211],_bs2);if(!extrudeByPath){v(_vert5.x,_vert5.y,depth+_z2);}else {v(_vert5.x,_vert5.y+extrudePts[steps-1].y,extrudePts[steps-1].x+_z2);}}}}/* Faces */ // Top and bottom faces if(!options.openEnded){//xzw add 可以选择开口,不创建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(var _i212=0;_i212=0){if(shapeDontClose&&i==0)break;//xzw add var _j13=i;var _k2=i-1;if(_k2<0)_k2=contour.length-1;//console.log('b', i,j, i-1, k,vertices.length); for(var _s4=0,sl=steps+bevelSegments*2;_s4, // 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 Geometry{constructor(shapes,options){super();this.type='ExtrudeGeometry';this.parameters={shapes:shapes,options:options};this.fromBufferGeometry(new ExtrudeBufferGeometry(shapes,options));this.mergeVertices();}toJSON(){var data=super.toJSON();var shapes=this.parameters.shapes;var options=this.parameters.options;return toJSON$1(shapes,options,data);}}function toJSON$1(shapes,options,data){data.shapes=[];if(Array.isArray(shapes)){for(var _i217=0,l=shapes.length;_i2170&&arguments[0]!==undefined?arguments[0]:1;var detail=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;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];super(vertices,indices,radius,detail);this.type='IcosahedronBufferGeometry';this.parameters={radius:radius,detail:detail};}}class IcosahedronGeometry extends Geometry{constructor(radius,detail){super();this.type='IcosahedronGeometry';this.parameters={radius:radius,detail:detail};this.fromBufferGeometry(new IcosahedronBufferGeometry(radius,detail));this.mergeVertices();}}class LatheBufferGeometry extends BufferGeometry{constructor(points){var segments=arguments.length>1&&arguments[1]!==undefined?arguments[1]:12;var phiStart=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0;var phiLength=arguments.length>3&&arguments[3]!==undefined?arguments[3]:Math.PI*2;super();this.type='LatheBufferGeometry';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=MathUtils.clamp(phiLength,0,Math.PI*2);// buffers var indices=[];var vertices=[];var uvs=[];// helper variables var inverseSegments=1.0/segments;var vertex=new Vector3();var uv=new Vector2();// generate vertices and uvs for(var _i218=0;_i218<=segments;_i218++){var phi=phiStart+_i218*inverseSegments*phiLength;var sin=Math.sin(phi);var cos=Math.cos(phi);for(var 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=_i218/segments;uv.y=j/(points.length-1);uvs.push(uv.x,uv.y);}}// indices for(var _i219=0;_i2190&&arguments[0]!==undefined?arguments[0]:1;var detail=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;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];super(vertices,indices,radius,detail);this.type='OctahedronBufferGeometry';this.parameters={radius:radius,detail:detail};}}class OctahedronGeometry extends Geometry{constructor(radius,detail){super();this.type='OctahedronGeometry';this.parameters={radius:radius,detail:detail};this.fromBufferGeometry(new OctahedronBufferGeometry(radius,detail));this.mergeVertices();}}/** * Parametric Surfaces Geometry * based on the brilliant article by @prideout https://prideout.net/blog/old/blog/index.html@p=44.html */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();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(var _i221=0;_i221<=stacks;_i221++){var v=_i221/stacks;for(var 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(var _i222=0;_i2220&&arguments[0]!==undefined?arguments[0]:0.5;var outerRadius=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var thetaSegments=arguments.length>2&&arguments[2]!==undefined?arguments[2]:8;var phiSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:1;var thetaStart=arguments.length>4&&arguments[4]!==undefined?arguments[4]:0;var thetaLength=arguments.length>5&&arguments[5]!==undefined?arguments[5]:Math.PI*2;super();this.type='RingBufferGeometry';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 var indices=[];var vertices=[];var normals=[];var uvs=[];// some helper variables var radius=innerRadius;var radiusStep=(outerRadius-innerRadius)/phiSegments;var vertex=new Vector3();var uv=new Vector2();// generate vertices, normals and uvs for(var j=0;j<=phiSegments;j++){for(var _i223=0;_i223<=thetaSegments;_i223++){// values are generate from the inside of the ring to the outside var segment=thetaStart+_i223/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(var _j17=0;_j171&&arguments[1]!==undefined?arguments[1]:12;super();this.type='ShapeBufferGeometry';this.parameters={shapes:shapes,curveSegments:curveSegments};// 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 _i225=0;_i2250&&arguments[0]!==undefined?arguments[0]:1;var widthSegments=arguments.length>1&&arguments[1]!==undefined?arguments[1]:8;var heightSegments=arguments.length>2&&arguments[2]!==undefined?arguments[2]:6;var phiStart=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0;var phiLength=arguments.length>4&&arguments[4]!==undefined?arguments[4]:Math.PI*2;var thetaStart=arguments.length>5&&arguments[5]!==undefined?arguments[5]:0;var thetaLength=arguments.length>6&&arguments[6]!==undefined?arguments[6]:Math.PI;super();this.type='SphereBufferGeometry';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));var thetaEnd=Math.min(thetaStart+thetaLength,Math.PI);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(var iy=0;iy<=heightSegments;iy++){var verticesRow=[];var v=iy/heightSegments;// special case for the poles var uOffset=0;if(iy==0&&thetaStart==0){uOffset=0.5/widthSegments;}else if(iy==heightSegments&&thetaEnd==Math.PI){uOffset=-0.5/widthSegments;}for(var 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.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(var _iy3=0;_iy30)indices.push(a,b,d);if(_iy3!==heightSegments-1||thetaEnd0&&arguments[0]!==undefined?arguments[0]:1;var detail=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;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];super(vertices,indices,radius,detail);this.type='TetrahedronBufferGeometry';this.parameters={radius:radius,detail:detail};}}class TetrahedronGeometry extends Geometry{constructor(radius,detail){super();this.type='TetrahedronGeometry';this.parameters={radius:radius,detail:detail};this.fromBufferGeometry(new TetrahedronBufferGeometry(radius,detail));this.mergeVertices();}}/** * 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 (including bevelOffset) is bevel * bevelOffset: // how far from text outline does bevel start * } */class TextBufferGeometry extends ExtrudeBufferGeometry{constructor(text){var parameters=arguments.length>1&&arguments[1]!==undefined?arguments[1]:{};var font=parameters.font;if(!(font&&font.isFont)){console.error('THREE.TextGeometry: font parameter is not an instance of THREE.Font.');return new BufferGeometry();}var shapes=font.generateShapes(text,parameters.size);// 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;super(shapes,parameters);this.type='TextBufferGeometry';}}/** * 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 (including bevelOffset) is bevel * bevelOffset: // how far from text outline does bevel start * } */class TextGeometry extends Geometry{constructor(text,parameters){super();this.type='TextGeometry';this.parameters={text:text,parameters:parameters};this.fromBufferGeometry(new TextBufferGeometry(text,parameters));this.mergeVertices();}}class TorusBufferGeometry extends BufferGeometry{constructor(){var radius=arguments.length>0&&arguments[0]!==undefined?arguments[0]:1;var tube=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0.4;var radialSegments=arguments.length>2&&arguments[2]!==undefined?arguments[2]:8;var tubularSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:6;var arc=arguments.length>4&&arguments[4]!==undefined?arguments[4]:Math.PI*2;super();this.type='TorusBufferGeometry';this.parameters={radius:radius,tube:tube,radialSegments:radialSegments,tubularSegments:tubularSegments,arc:arc};radialSegments=Math.floor(radialSegments);tubularSegments=Math.floor(tubularSegments);// buffers var indices=[];var vertices=[];var normals=[];var uvs=[];// helper variables var center=new Vector3();var vertex=new Vector3();var normal=new Vector3();// generate vertices, normals and uvs for(var j=0;j<=radialSegments;j++){for(var _i232=0;_i232<=tubularSegments;_i232++){var u=_i232/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(_i232/tubularSegments);uvs.push(j/radialSegments);}}// generate indices for(var _j18=1;_j18<=radialSegments;_j18++){for(var _i233=1;_i233<=tubularSegments;_i233++){// indices var a=(tubularSegments+1)*_j18+_i233-1;var b=(tubularSegments+1)*(_j18-1)+_i233-1;var c=(tubularSegments+1)*(_j18-1)+_i233;var d=(tubularSegments+1)*_j18+_i233;// 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));}}class TorusGeometry extends Geometry{constructor(radius,tube,radialSegments,tubularSegments,arc){super();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();}}class TorusKnotBufferGeometry extends BufferGeometry{constructor(){var radius=arguments.length>0&&arguments[0]!==undefined?arguments[0]:1;var tube=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0.4;var tubularSegments=arguments.length>2&&arguments[2]!==undefined?arguments[2]:64;var radialSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:8;var p=arguments.length>4&&arguments[4]!==undefined?arguments[4]:2;var q=arguments.length>5&&arguments[5]!==undefined?arguments[5]:3;super();this.type='TorusKnotBufferGeometry';this.parameters={radius:radius,tube:tube,tubularSegments:tubularSegments,radialSegments:radialSegments,p:p,q:q};tubularSegments=Math.floor(tubularSegments);radialSegments=Math.floor(radialSegments);// buffers var indices=[];var vertices=[];var normals=[];var uvs=[];// helper variables 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(var _i234=0;_i234<=tubularSegments;++_i234){// the radian "u" is used to calculate the position on the torus curve of the current tubular segement var u=_i234/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(var 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(_i234/tubularSegments);uvs.push(j/radialSegments);}}// generate indices for(var _j19=1;_j19<=tubularSegments;_j19++){for(var _i235=1;_i235<=radialSegments;_i235++){// indices var a=(radialSegments+1)*(_j19-1)+(_i235-1);var b=(radialSegments+1)*_j19+(_i235-1);var c=(radialSegments+1)*_j19+_i235;var d=(radialSegments+1)*(_j19-1)+_i235;// 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){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;}}}class TorusKnotGeometry extends Geometry{constructor(radius,tube,tubularSegments,radialSegments,p,q,heightScale){super();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();}}class TubeBufferGeometry extends BufferGeometry{constructor(path){var tubularSegments=arguments.length>1&&arguments[1]!==undefined?arguments[1]:64;var radius=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var radialSegments=arguments.length>3&&arguments[3]!==undefined?arguments[3]:8;var closed=arguments.length>4&&arguments[4]!==undefined?arguments[4]:false;super();this.type='TubeBufferGeometry';this.parameters={path:path,tubularSegments:tubularSegments,radius:radius,radialSegments:radialSegments,closed:closed};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();// buffer var vertices=[];var normals=[];var uvs=[];var 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(var _i236=0;_i236 * } */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;};function RawShaderMaterial(parameters){ShaderMaterial.call(this,parameters);this.type='RawShaderMaterial';}RawShaderMaterial.prototype=Object.create(ShaderMaterial.prototype);RawShaderMaterial.prototype.constructor=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: , * * skinning: , * morphTargets: , * morphNormals: * } */function MeshStandardMaterial(parameters){Material.call(this);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.skinning=false;this.morphTargets=false;this.morphNormals=false;this.vertexTangents=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.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.skinning=source.skinning;this.morphTargets=source.morphTargets;this.morphNormals=source.morphNormals;this.vertexTangents=source.vertexTangents;return this;};/** * parameters = { * clearcoat: , * clearcoatMap: new THREE.Texture( ), * clearcoatRoughness: , * clearcoatRoughnessMap: new THREE.Texture( ), * clearcoatNormalScale: , * clearcoatNormalMap: new THREE.Texture( ), * * reflectivity: , * ior: , * * sheen: , * * transmission: , * transmissionMap: new THREE.Texture( ) * } */function MeshPhysicalMaterial(parameters){MeshStandardMaterial.call(this);this.defines={'STANDARD':'','PHYSICAL':''};this.type='MeshPhysicalMaterial';this.clearcoat=0.0;this.clearcoatMap=null;this.clearcoatRoughness=0.0;this.clearcoatRoughnessMap=null;this.clearcoatNormalScale=new Vector2(1,1);this.clearcoatNormalMap=null;this.reflectivity=0.5;// maps to F0 = 0.04 Object.defineProperty(this,'ior',{get:function get(){return (1+0.4*this.reflectivity)/(1-0.4*this.reflectivity);},set:function set(ior){this.reflectivity=MathUtils.clamp(2.5*(ior-1)/(ior+1),0,1);}});this.sheen=null;// null will disable sheen bsdf this.transmission=0.0;this.transmissionMap=null;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={'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.reflectivity=source.reflectivity;if(source.sheen){this.sheen=(this.sheen||new Color()).copy(source.sheen);}else {this.sheen=null;}this.transmission=source.transmission;this.transmissionMap=source.transmissionMap;return this;};/** * 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: , * * 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.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.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.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.skinning=source.skinning;this.morphTargets=source.morphTargets;this.morphNormals=source.morphNormals;return this;};/** * 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: , * * skinning: , * morphTargets: , * morphNormals: * } */function MeshToonMaterial(parameters){Material.call(this);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.skinning=false;this.morphTargets=false;this.morphNormals=false;this.setValues(parameters);}MeshToonMaterial.prototype=Object.create(Material.prototype);MeshToonMaterial.prototype.constructor=MeshToonMaterial;MeshToonMaterial.prototype.isMeshToonMaterial=true;MeshToonMaterial.prototype.copy=function(source){Material.prototype.copy.call(this,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.skinning=source.skinning;this.morphTargets=source.morphTargets;this.morphNormals=source.morphNormals;return this;};/** * parameters = { * opacity: , * * bumpMap: new THREE.Texture( ), * bumpScale: , * * normalMap: new THREE.Texture( ), * normalMapType: THREE.TangentSpaceNormalMap, * 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.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.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.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.skinning=source.skinning;this.morphTargets=source.morphTargets;this.morphNormals=source.morphNormals;return this;};/** * 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;};/** * 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( ), * * skinning: , * morphTargets: , * morphNormals: * } */function MeshMatcapMaterial(parameters){Material.call(this);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.skinning=false;this.morphTargets=false;this.morphNormals=false;this.setValues(parameters);}MeshMatcapMaterial.prototype=Object.create(Material.prototype);MeshMatcapMaterial.prototype.constructor=MeshMatcapMaterial;MeshMatcapMaterial.prototype.isMeshMatcapMaterial=true;MeshMatcapMaterial.prototype.copy=function(source){Material.prototype.copy.call(this,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.skinning=source.skinning;this.morphTargets=source.morphTargets;this.morphNormals=source.morphNormals;return this;};/** * 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({__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});var AnimationUtils={// same as Array.prototype.slice, but also works on typed arrays arraySlice:function 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: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 },isTypedArray:function isTypedArray(object){return ArrayBuffer.isView(object)&&!(object instanceof DataView);},// returns an array by which times and values can be sorted getKeyframeOrder:function getKeyframeOrder(times){function compareTime(i,j){return times[i]-times[j];}var n=times.length;var result=new Array(n);for(var _i242=0;_i242!==n;++_i242)result[_i242]=_i242;result.sort(compareTime);return result;},// uses the array previously returned by 'getKeyframeOrder' to sort data sortedArray:function sortedArray(values,stride,order){var nValues=values.length;var result=new values.constructor(nValues);for(var _i243=0,dstOffset=0;dstOffset!==nValues;++_i243){var srcOffset=order[_i243]*stride;for(var j=0;j!==stride;++j){result[dstOffset++]=values[srcOffset+j];}}return result;},// function for parsing AOS keyframe formats flattenJSON:function flattenJSON(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);}},subclip:function subclip(sourceClip,name,startFrame,endFrame){var fps=arguments.length>4&&arguments[4]!==undefined?arguments[4]:30;var clip=sourceClip.clone();clip.name=name;var tracks=[];for(var _i244=0;_i244=endFrame)continue;times.push(track.times[j]);for(var k=0;kclip.tracks[_i245].times[0]){minStartTime=clip.tracks[_i245].times[0];}}// shift all tracks such that clip begins at t=0 for(var _i246=0;_i2461&&arguments[1]!==undefined?arguments[1]:0;var referenceClip=arguments.length>2&&arguments[2]!==undefined?arguments[2]:targetClip;var fps=arguments.length>3&&arguments[3]!==undefined?arguments[3]:30;if(fps<=0)fps=30;var numTracks=referenceClip.tracks.length;var referenceTime=referenceFrame/fps;// Make each track's values relative to the values at the reference frame var _loop=function _loop(){var referenceTrack=referenceClip.tracks[_i247];var referenceTrackType=referenceTrack.ValueTypeName;// Skip this track if it's non-numeric if(referenceTrackType==='bool'||referenceTrackType==='string')return 0;// continue // Find the track in the target clip whose name and type matches the reference track var targetTrack=targetClip.tracks.find(function(track){return track.name===referenceTrack.name&&track.ValueTypeName===referenceTrackType;});if(targetTrack===undefined)return 0;// continue var referenceOffset=0;var referenceValueSize=referenceTrack.getValueSize();if(referenceTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline){referenceOffset=referenceValueSize/3;}var targetOffset=0;var targetValueSize=targetTrack.getValueSize();if(targetTrack.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline){targetOffset=targetValueSize/3;}var lastIndex=referenceTrack.times.length-1;var 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 var startIndex=referenceOffset;var 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 var _startIndex=lastIndex*referenceValueSize+referenceOffset;var _endIndex=_startIndex+referenceValueSize-referenceOffset;referenceValue=AnimationUtils.arraySlice(referenceTrack.values,_startIndex,_endIndex);}else {// Interpolate to the reference value var interpolant=referenceTrack.createInterpolant();var _startIndex2=referenceOffset;var _endIndex2=referenceValueSize-referenceOffset;interpolant.evaluate(referenceTime);referenceValue=AnimationUtils.arraySlice(interpolant.resultBuffer,_startIndex2,_endIndex2);}// Conjugate the quaternion if(referenceTrackType==='quaternion'){var referenceQuat=new Quaternion().fromArray(referenceValue).normalize().conjugate();referenceQuat.toArray(referenceValue);}// Subtract the reference value from all of the track values var numTimes=targetTrack.times.length;for(var j=0;j= t1 || t1 === undefined ) { forward_scan:if(!(t=t0)){// looping? var t1global=pp[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>>1;if(t seconds conversions) scale:function scale(timeScale){if(timeScale!==1.0){var times=this.times;for(var _i252=0,n=times.length;_i252!==n;++_i252){times[_i252]*=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 trim(startTime,endTime){var times=this.times,nKeys=times.length;var from=0,to=nKeys-1;while(from!==nKeys&×[from]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 validate(){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 _i253=0;_i253!==nKeys;_i253++){var currTime=times[_i253];if(typeof currTime==='number'&&isNaN(currTime)){console.error('THREE.KeyframeTrack: Time is not a valid number.',this,_i253,currTime);valid=false;break;}if(prevTime!==null&&prevTime>currTime){console.error('THREE.KeyframeTrack: Out of order keys.',this,_i253,currTime,prevTime);valid=false;break;}prevTime=currTime;}if(values!==undefined){if(AnimationUtils.isTypedArray(values)){for(var _i254=0,n=values.length;_i254!==n;++_i254){var value=values[_i254];if(isNaN(value)){console.error('THREE.KeyframeTrack: Value is not a valid number.',this,_i254,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 optimize(){// times or values may be shared with other tracks, so overwriting is unsafe var times=AnimationUtils.arraySlice(this.times),values=AnimationUtils.arraySlice(this.values),stride=this.getValueSize(),smoothInterpolation=this.getInterpolation()===InterpolateSmooth,lastIndex=times.length-1;var writeIndex=1;for(var _i255=1;_i2550){times[writeIndex]=times[lastIndex];for(var _readOffset=lastIndex*stride,_writeOffset=writeIndex*stride,_j23=0;_j23!==stride;++_j23){values[_writeOffset+_j23]=values[_readOffset+_j23];}++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:function clone(){var times=AnimationUtils.arraySlice(this.times,0);var values=AnimationUtils.arraySlice(this.values,0);var TypedKeyframeTrack=this.constructor;var 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;}});/** * A Track of Boolean keyframe values. */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 )". });/** * A Track of keyframe values that represent color. */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. */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 });/** * Spherical linear unit quaternion interpolant. */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 interpolate_(i1,t0,t,t1){var result=this.resultBuffer,values=this.sampleValues,stride=this.valueSize,alpha=(t-t0)/(t1-t0);var offset=i1*stride;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. */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 InterpolantFactoryMethodLinear(result){return new QuaternionLinearInterpolant(this.times,this.values,this.getValueSize(),result);},InterpolantFactoryMethodSmooth:undefined// not yet implemented });/** * A Track that interpolates Strings */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 vectored keyframe values. */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 });function AnimationClip(name){var duration=arguments.length>1&&arguments[1]!==undefined?arguments[1]:-1;var tracks=arguments.length>2?arguments[2]:undefined;var blendMode=arguments.length>3&&arguments[3]!==undefined?arguments[3]:NormalAnimationBlendMode;this.name=name;this.tracks=tracks;this.duration=duration;this.blendMode=blendMode;this.uuid=MathUtils.generateUUID();// this means it should figure out its duration by scanning the tracks if(this.duration<0){this.resetDuration();}}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');}var trackType=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);}}Object.assign(AnimationClip,{parse:function parse(json){var tracks=[],jsonTracks=json.tracks,frameTime=1.0/(json.fps||1.0);for(var _i256=0,n=jsonTracks.length;_i256!==n;++_i256){tracks.push(parseKeyframeTrack(jsonTracks[_i256]).scale(frameTime));}var clip=new AnimationClip(json.name,json.duration,tracks,json.blendMode);clip.uuid=json.uuid;return clip;},toJSON:function toJSON(clip){var tracks=[],clipTracks=clip.tracks;var json={'name':clip.name,'duration':clip.duration,'tracks':tracks,'uuid':clip.uuid,'blendMode':clip.blendMode};for(var _i257=0,n=clipTracks.length;_i257!==n;++_i257){tracks.push(KeyframeTrack.toJSON(clipTracks[_i257]));}return json;},CreateFromMorphTargetSequence:function CreateFromMorphTargetSequence(name,morphTargetSequence,fps,noLoop){var numMorphTargets=morphTargetSequence.length;var tracks=[];for(var _i258=0;_i2581){var name=parts[1];var animationMorphTargets=animationToMorphTargets[name];if(!animationMorphTargets){animationToMorphTargets[name]=animationMorphTargets=[];}animationMorphTargets.push(morphTarget);}}var clips=[];for(var _name4 in animationToMorphTargets){clips.push(AnimationClip.CreateFromMorphTargetSequence(_name4,animationToMorphTargets[_name4],fps,noLoop));}return clips;},// parse the animation.hierarchy format parseAnimation:function parseAnimation(animation,bones){if(!animation){console.error('THREE.AnimationClip: No animation in JSONLoader data.');return null;}var addNonemptyTrack=function addNonemptyTrack(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';var fps=animation.fps||30;var blendMode=animation.blendMode;// automatic length determination in AnimationClip. var duration=animation.length||-1;var hierarchyTracks=animation.hierarchy||[];for(var h=0;h0||url.search(/^data\:image\/jpeg/)===0;texture.format=isJPEG?RGBFormat:RGBAFormat;texture.needsUpdate=true;if(onLoad!==undefined){onLoad(texture);}},onProgress,onError);return texture;}});/** * 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. * **/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 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:function getPointAt(u,optionalTarget){var t=this.getUtoTmapping(u);return this.getPoint(t,optionalTarget);},// Get sequence of points using getPoint( t ) getPoints:function getPoints(){var divisions=arguments.length>0&&arguments[0]!==undefined?arguments[0]: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 getSpacedPoints(){var divisions=arguments.length>0&&arguments[0]!==undefined?arguments[0]: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 getLength(){var lengths=this.getLengths();return lengths[lengths.length-1];},// Get list of cumulative segment lengths getLengths:function getLengths(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 sum=0;cache.push(0);for(var 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 updateArcLengths(){this.needsUpdate=true;this.getLengths();},// Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant getUtoTmapping:function getUtoTmapping(u,distance){var arcLengths=this.getLengths();var i=0;var 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 getTangent(t,optionalTarget){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 tangent=optionalTarget||(pt1.isVector2?new Vector2():new Vector3());tangent.copy(pt2).sub(pt1).normalize();return tangent;},getTangentAt:function getTangentAt(u,optionalTarget){var t=this.getUtoTmapping(u);return this.getTangent(t,optionalTarget);},computeFrenetFrames:function computeFrenetFrames(segments,closed,getCtrlPointsDirect){//xzw add getCtrlPointsDirect // 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();// compute the tangent vectors for each segment on the curve if(getCtrlPointsDirect){//xzw 直接返回控制点所在的 this.getCurveLengths();segments=this.cacheLengths.length;var lengthSum=this.cacheLengths[this.cacheLengths.length-1];//xzw add 计算控制点处所在处的比例 for(var _i277=0;_i277<=segments;_i277++){var u=_i277==0?0:this.cacheLengths[_i277-1]/lengthSum;tangents[_i277]=this.getTangentAt(u,new Vector3());tangents[_i277].normalize();}}else {for(var _i278=0;_i278<=segments;_i278++){//这是平均分线长度 var _u=_i278/segments;tangents[_i278]=this.getTangentAt(_u,new Vector3());tangents[_i278].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(var _i279=1;_i279<=segments;_i279++){normals[_i279]=normals[_i279-1].clone();binormals[_i279]=binormals[_i279-1].clone();vec.crossVectors(tangents[_i279-1],tangents[_i279]);if(vec.length()>Number.EPSILON){vec.normalize();var theta=Math.acos(MathUtils.clamp(tangents[_i279-1].dot(tangents[_i279]),-1,1));// clamp for floating pt errors normals[_i279].applyMatrix4(mat.makeRotationAxis(vec,theta));}binormals[_i279].crossVectors(tangents[_i279],normals[_i279]);}// if the curve is closed, postprocess the vectors so the first and last normal vectors are the same if(closed===true){var _theta=Math.acos(MathUtils.clamp(normals[0].dot(normals[segments]),-1,1));_theta/=segments;if(tangents[0].dot(vec.crossVectors(normals[0],normals[segments]))>0){_theta=-_theta;}for(var _i280=1;_i280<=segments;_i280++){// twist a little... normals[_i280].applyMatrix4(mat.makeRotationAxis(tangents[_i280],_theta*_i280));binormals[_i280].crossVectors(tangents[_i280],normals[_i280]);}}return {tangents:tangents,normals:normals,binormals:binormals};},clone:function clone(){return new this.constructor().copy(this);},copy:function copy(source){this.arcLengthDivisions=source.arcLengthDivisions;return this;},toJSON:function toJSON(){var data={metadata:{version:4.5,type:'Curve',generator:'Curve.toJSON'}};data.arcLengthDivisions=this.arcLengthDivisions;data.type=this.type;return data;},fromJSON:function fromJSON(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)twoPi)deltaAngle-=twoPi;if(deltaAngle0&&arguments[0]!==undefined?arguments[0]:[];var closed=arguments.length>1&&arguments[1]!==undefined?arguments[1]:false;var curveType=arguments.length>2&&arguments[2]!==undefined?arguments[2]:'centripetal';var tension=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0.5;Curve.call(this);this.type='CatmullRomCurve3';this.points=points;this.closed=closed;this.curveType=curveType;this.tension=tension;}CatmullRomCurve3.prototype=Object.create(Curve.prototype);CatmullRomCurve3.prototype.constructor=CatmullRomCurve3;CatmullRomCurve3.prototype.isCatmullRomCurve3=true;CatmullRomCurve3.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();var point=optionalTarget;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,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;}var p1=points[intPoint%l];var p2=points[(intPoint+1)%l];if(this.closed||intPoint+20&&arguments[0]!==undefined?arguments[0]:new Vector2();var v1=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();var v2=arguments.length>2&&arguments[2]!==undefined?arguments[2]:new Vector2();var v3=arguments.length>3&&arguments[3]!==undefined?arguments[3]:new Vector2();Curve.call(this);this.type='CubicBezierCurve';this.v0=v0;this.v1=v1;this.v2=v2;this.v3=v3;}CubicBezierCurve.prototype=Object.create(Curve.prototype);CubicBezierCurve.prototype.constructor=CubicBezierCurve;CubicBezierCurve.prototype.isCubicBezierCurve=true;CubicBezierCurve.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();var point=optionalTarget;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(){var v0=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new Vector3();var v1=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();var v2=arguments.length>2&&arguments[2]!==undefined?arguments[2]:new Vector3();var v3=arguments.length>3&&arguments[3]!==undefined?arguments[3]:new Vector3();Curve.call(this);this.type='CubicBezierCurve3';this.v0=v0;this.v1=v1;this.v2=v2;this.v3=v3;}CubicBezierCurve3.prototype=Object.create(Curve.prototype);CubicBezierCurve3.prototype.constructor=CubicBezierCurve3;CubicBezierCurve3.prototype.isCubicBezierCurve3=true;CubicBezierCurve3.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();var point=optionalTarget;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(){var v1=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new Vector2();var v2=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();Curve.call(this);this.type='LineCurve';this.v1=v1;this.v2=v2;}LineCurve.prototype=Object.create(Curve.prototype);LineCurve.prototype.constructor=LineCurve;LineCurve.prototype.isLineCurve=true;LineCurve.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();var 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 LineCurve.prototype.getPointAt=function(u,optionalTarget){return this.getPoint(u,optionalTarget);};LineCurve.prototype.getTangent=function(t,optionalTarget){var tangent=optionalTarget||new Vector2();tangent.copy(this.v2).sub(this.v1).normalize();return tangent;};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(){var v1=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new Vector3();var v2=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();Curve.call(this);this.type='LineCurve3';this.v1=v1;this.v2=v2;}LineCurve3.prototype=Object.create(Curve.prototype);LineCurve3.prototype.constructor=LineCurve3;LineCurve3.prototype.isLineCurve3=true;LineCurve3.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();var 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 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(){var v0=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new Vector2();var v1=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();var v2=arguments.length>2&&arguments[2]!==undefined?arguments[2]:new Vector2();Curve.call(this);this.type='QuadraticBezierCurve';this.v0=v0;this.v1=v1;this.v2=v2;}QuadraticBezierCurve.prototype=Object.create(Curve.prototype);QuadraticBezierCurve.prototype.constructor=QuadraticBezierCurve;QuadraticBezierCurve.prototype.isQuadraticBezierCurve=true;QuadraticBezierCurve.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();var point=optionalTarget;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(){var v0=arguments.length>0&&arguments[0]!==undefined?arguments[0]:new Vector3();var v1=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();var v2=arguments.length>2&&arguments[2]!==undefined?arguments[2]:new Vector3();Curve.call(this);this.type='QuadraticBezierCurve3';this.v0=v0;this.v1=v1;this.v2=v2;}QuadraticBezierCurve3.prototype=Object.create(Curve.prototype);QuadraticBezierCurve3.prototype.constructor=QuadraticBezierCurve3;QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3=true;QuadraticBezierCurve3.prototype.getPoint=function(t){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector3();var point=optionalTarget;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(){var points=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];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){var optionalTarget=arguments.length>1&&arguments[1]!==undefined?arguments[1]:new Vector2();var point=optionalTarget;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 _i284=0,l=source.points.length;_i284=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 0&&arguments[0]!==undefined?arguments[0]:40;var points=[];for(var _i288=0;_i288<=divisions;_i288++){points.push(this.getPoint(_i288/divisions));}if(this.autoClose){points.push(points[0]);}return points;},getPoints:function getPoints(){var divisions=arguments.length>0&&arguments[0]!==undefined?arguments[0]:12;var points=[];var last;for(var _i289=0,curves=this.curves;_i2891&&!points[points.length-1].equals(points[0])){points.push(points[0]);}return points;},copy:function copy(source){Curve.prototype.copy.call(this,source);this.curves=[];for(var _i290=0,l=source.curves.length;_i2900){// 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);return this;},copy:function copy(source){CurvePath.prototype.copy.call(this,source);this.currentPoint.copy(source.currentPoint);return this;},toJSON:function toJSON(){var data=CurvePath.prototype.toJSON.call(this);data.currentPoint=this.currentPoint.toArray();return data;},fromJSON:function fromJSON(json){CurvePath.prototype.fromJSON.call(this,json);this.currentPoint.fromArray(json.currentPoint);return this;}});function Shape(points){Path.call(this,points);this.uuid=MathUtils.generateUUID();this.type='Shape';this.holes=[];}Shape.prototype=Object.assign(Object.create(Path.prototype),{constructor:Shape,getPointsHoles:function getPointsHoles(divisions){var holesPts=[];for(var _i294=0,l=this.holes.length;_i2941&&arguments[1]!==undefined?arguments[1]:1;Object3D.call(this);this.type='Light';this.color=new Color(color);this.intensity=intensity;}Light.prototype=Object.assign(Object.create(Object3D.prototype),{constructor:Light,isLight:true,copy:function copy(source){Object3D.prototype.copy.call(this,source);this.color.copy(source.color);this.intensity=source.intensity;return this;},toJSON:function toJSON(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;}});function HemisphereLight(skyColor,groundColor,intensity){Light.call(this,skyColor,intensity);this.type='HemisphereLight';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 copy(source){Light.prototype.copy.call(this,source);this.groundColor.copy(source.groundColor);return this;}});function LightShadow(camera){this.camera=camera;this.bias=0;this.normalBias=0;this.radius=1;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)];}Object.assign(LightShadow.prototype,{_projScreenMatrix:new Matrix4(),_lightPositionWorld:new Vector3(),_lookTarget:new Vector3(),getViewportCount:function getViewportCount(){return this._viewportCount;},getFrustum:function getFrustum(){return this._frustum;},updateMatrices:function updateMatrices(light){var shadowCamera=this.camera,shadowMatrix=this.matrix,projScreenMatrix=this._projScreenMatrix,lookTarget=this._lookTarget,lightPositionWorld=this._lightPositionWorld;lightPositionWorld.setFromMatrixPosition(light.matrixWorld);shadowCamera.position.copy(lightPositionWorld);lookTarget.setFromMatrixPosition(light.target.matrixWorld);shadowCamera.lookAt(lookTarget);shadowCamera.updateMatrixWorld();projScreenMatrix.multiplyMatrices(shadowCamera.projectionMatrix,shadowCamera.matrixWorldInverse);this._frustum.setFromProjectionMatrix(projScreenMatrix);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:function getViewport(viewportIndex){return this._viewports[viewportIndex];},getFrameExtents:function getFrameExtents(){return this._frameExtents;},copy:function copy(source){this.camera=source.camera.clone();this.bias=source.bias;this.radius=source.radius;this.mapSize.copy(source.mapSize);return this;},clone:function clone(){return new this.constructor().copy(this);},toJSON:function toJSON(){var 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;}});function SpotLightShadow(){LightShadow.call(this,new PerspectiveCamera(50,1,0.5,500));this.focus=1;}SpotLightShadow.prototype=Object.assign(Object.create(LightShadow.prototype),{constructor:SpotLightShadow,isSpotLightShadow:true,updateMatrices:function updateMatrices(light){var camera=this.camera;var fov=MathUtils.RAD2DEG*2*light.angle*this.focus;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();}LightShadow.prototype.updateMatrices.call(this,light);}});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 get(){// 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 set(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 copy(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;}});function PointLightShadow(){LightShadow.call(this,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)];}PointLightShadow.prototype=Object.assign(Object.create(LightShadow.prototype),{constructor:PointLightShadow,isPointLightShadow:true,updateMatrices:function updateMatrices(light){var viewportIndex=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var camera=this.camera,shadowMatrix=this.matrix,lightPositionWorld=this._lightPositionWorld,lookTarget=this._lookTarget,projScreenMatrix=this._projScreenMatrix;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);}});function PointLight(color,intensity,distance,decay){Light.call(this,color,intensity);this.type='PointLight';Object.defineProperty(this,'power',{get:function get(){// 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 set(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 PointLightShadow();}PointLight.prototype=Object.assign(Object.create(Light.prototype),{constructor:PointLight,isPointLight:true,copy:function copy(source){Light.prototype.copy.call(this,source);this.distance=source.distance;this.decay=source.decay;this.shadow=source.shadow.clone();return this;}});function OrthographicCamera(){var left=arguments.length>0&&arguments[0]!==undefined?arguments[0]:-1;var right=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var top=arguments.length>2&&arguments[2]!==undefined?arguments[2]:1;var bottom=arguments.length>3&&arguments[3]!==undefined?arguments[3]:-1;var near=arguments.length>4&&arguments[4]!==undefined?arguments[4]:0.1;var far=arguments.length>5&&arguments[5]!==undefined?arguments[5]:2000;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;this.far=far;this.updateProjectionMatrix();}OrthographicCamera.prototype=Object.assign(Object.create(Camera.prototype),{constructor:OrthographicCamera,isOrthographicCamera:true,copy:function copy(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 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:function clearViewOffset(){if(this.view!==null){this.view.enabled=false;}this.updateProjectionMatrix();},updateProjectionMatrix:function updateProjectionMatrix(){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 scaleW=(this.right-this.left)/this.view.fullWidth/this.zoom;var 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:function toJSON(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;}});function DirectionalLightShadow(){LightShadow.call(this,new OrthographicCamera(-5,5,5,-5,0.5,500));}DirectionalLightShadow.prototype=Object.assign(Object.create(LightShadow.prototype),{constructor:DirectionalLightShadow,isDirectionalLightShadow:true,updateMatrices:function updateMatrices(light){LightShadow.prototype.updateMatrices.call(this,light);}});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 copy(source){Light.prototype.copy.call(this,source);this.target=source.target.clone();this.shadow=source.shadow.clone();return this;}});function AmbientLight(color,intensity){Light.call(this,color,intensity);this.type='AmbientLight';}AmbientLight.prototype=Object.assign(Object.create(Light.prototype),{constructor:AmbientLight,isAmbientLight:true});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 copy(source){Light.prototype.copy.call(this,source);this.width=source.width;this.height=source.height;return this;},toJSON:function toJSON(meta){var data=Light.prototype.toJSON.call(this,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(){Object.defineProperty(this,'isSphericalHarmonics3',{value:true});this.coefficients=[];for(var _i298=0;_i298<9;_i298++){this.coefficients.push(new Vector3());}}set(coefficients){for(var _i299=0;_i299<9;_i299++){this.coefficients[_i299].copy(coefficients[_i299]);}return this;}zero(){for(var _i300=0;_i300<9;_i300++){this.coefficients[_i300].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 var x=normal.x,y=normal.y,z=normal.z;var 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 var x=normal.x,y=normal.y,z=normal.z;var 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(var _i301=0;_i301<9;_i301++){this.coefficients[_i301].add(sh.coefficients[_i301]);}return this;}addScaledSH(sh,s){for(var _i302=0;_i302<9;_i302++){this.coefficients[_i302].addScaledVector(sh.coefficients[_i302],s);}return this;}scale(s){for(var _i303=0;_i303<9;_i303++){this.coefficients[_i303].multiplyScalar(s);}return this;}lerp(sh,alpha){for(var _i304=0;_i304<9;_i304++){this.coefficients[_i304].lerp(sh.coefficients[_i304],alpha);}return this;}equals(sh){for(var _i305=0;_i305<9;_i305++){if(!this.coefficients[_i305].equals(sh.coefficients[_i305])){return false;}}return true;}copy(sh){return this.set(sh.coefficients);}clone(){return new this.constructor().copy(this);}fromArray(array){var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var coefficients=this.coefficients;for(var _i306=0;_i306<9;_i306++){coefficients[_i306].fromArray(array,offset+_i306*3);}return this;}toArray(){var array=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];var offset=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var coefficients=this.coefficients;for(var _i307=0;_i307<9;_i307++){coefficients[_i307].toArray(array,offset+_i307*3);}return array;}// evaluate the basis functions // shBasis is an Array[ 9 ] static getBasisAt(normal,shBasis){// normal is assumed to be unit length var 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);}}function LightProbe(sh,intensity){Light.call(this,undefined,intensity);this.type='LightProbe';this.sh=sh!==undefined?sh:new SphericalHarmonics3();}LightProbe.prototype=Object.assign(Object.create(Light.prototype),{constructor:LightProbe,isLightProbe:true,copy:function copy(source){Light.prototype.copy.call(this,source);this.sh.copy(source.sh);return this;},fromJSON:function fromJSON(json){this.intensity=json.intensity;// TODO: Move this bit to Light.fromJSON(); this.sh.fromArray(json.sh);return this;},toJSON:function toJSON(meta){var data=Light.prototype.toJSON.call(this,meta);data.object.sh=this.sh.toArray();return data;}});function MaterialLoader(manager){Loader.call(this,manager);this.textures={};}MaterialLoader.prototype=Object.assign(Object.create(Loader.prototype),{constructor:MaterialLoader,load:function load(url,onLoad,onProgress,onError){var scope=this;var 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:function parse(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!==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=new Color().setHex(json.sheen);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.shininess!==undefined)material.shininess=json.shininess;if(json.clearcoat!==undefined)material.clearcoat=json.clearcoat;if(json.clearcoatRoughness!==undefined)material.clearcoatRoughness=json.clearcoatRoughness;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.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.skinning!==undefined)material.skinning=json.skinning;if(json.morphTargets!==undefined)material.morphTargets=json.morphTargets;if(json.morphNormals!==undefined)material.morphNormals=json.morphNormals;if(json.dithering!==undefined)material.dithering=json.dithering;if(json.vertexTangents!==undefined)material.vertexTangents=json.vertexTangents;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(var name in json.uniforms){var 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(var 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){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.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.transmission!==undefined)material.transmission=json.transmission;if(json.transmissionMap!==undefined)material.transmissionMap=getTexture(json.transmissionMap);return material;},setTextures:function setTextures(value){this.textures=value;return this;}});var LoaderUtils={decodeText:function 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. var s='';for(var _i308=0,il=array.length;_i3080){var manager=new LoadingManager(onLoad);loader=new ImageLoader(manager);loader.setCrossOrigin(this.crossOrigin);for(var _i316=0,il=json.length;_i316 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;qNumber.EPSILON){// not parallel if(edgeDy<0){edgeLowPt=inPolygon[q];edgeDx=-edgeDx;edgeHighPt=inPolygon[p];edgeDy=-edgeDy;}if(inPt.yedgeHighPt.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;var 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 _i322=0,l=subPaths.length;_i322 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;sIdx0){// console.log("to change: ", toChange); if(!ambiguous)newShapeHoles=betterShapeHoles;}}var tmpHoles;for(var _i323=0,il=newShapes.length;_i3231&&arguments[1]!==undefined?arguments[1]:100;var shapes=[];var paths=createPaths(text,size,this.data);for(var p=0,pl=paths.length;p0&&arguments[0]!==undefined?arguments[0]: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;var 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(var _i326=1,l=this.filters.length;_i3260){this.source.disconnect(this.filters[0]);for(var _i327=1,l=this.filters.length;_i3271&&arguments[1]!==undefined?arguments[1]: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(){var value=0;var data=this.getFrequencyData();for(var _i328=0;_i328' accumulate:function 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 var buffer=this.buffer,stride=this.valueSize,offset=accuIndex*stride+stride;var currentWeight=this.cumulativeWeight;if(currentWeight===0){// accuN := incoming * weight for(var _i329=0;_i329!==stride;++_i329){buffer[offset+_i329]=buffer[_i329];}currentWeight=weight;}else {// accuN := accuN + incoming * weight currentWeight+=weight;var mix=weight/currentWeight;this._mixBufferRegion(buffer,offset,0,mix,stride);}this.cumulativeWeight=currentWeight;},// accumulate data in the 'incoming' region into 'add' accumulateAdditive:function accumulateAdditive(weight){var 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:function apply(accuIndex){var 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 ) var 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(var _i330=stride,e=stride+stride;_i330!==e;++_i330){if(buffer[_i330]!==buffer[_i330+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 saveOriginalState(){var binding=this.binding;var 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(var _i331=stride,e=originalValueOffset;_i331!==e;++_i331){buffer[_i331]=buffer[originalValueOffset+_i331%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:function restoreOriginalState(){var originalValueOffset=this.valueSize*3;this.binding.setValue(this.buffer,originalValueOffset);},_setAdditiveIdentityNumeric:function _setAdditiveIdentityNumeric(){var startIndex=this._addIndex*this.valueSize;var endIndex=startIndex+this.valueSize;for(var _i332=startIndex;_i332=0.5){for(var _i334=0;_i334!==stride;++_i334){buffer[dstOffset+_i334]=buffer[srcOffset+_i334];}}},_slerp:function _slerp(buffer,dstOffset,srcOffset,t){Quaternion.slerpFlat(buffer,dstOffset,buffer,dstOffset,buffer,srcOffset,t);},_slerpAdditive:function _slerpAdditive(buffer,dstOffset,srcOffset,t,stride){var 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:function _lerp(buffer,dstOffset,srcOffset,t,stride){var s=1-t;for(var _i335=0;_i335!==stride;++_i335){var j=dstOffset+_i335;buffer[j]=buffer[j]*s+buffer[srcOffset+_i335]*t;}},_lerpAdditive:function _lerpAdditive(buffer,dstOffset,srcOffset,t,stride){for(var _i336=0;_i336!==stride;++_i336){var j=dstOffset+_i336;buffer[j]=buffer[j]+buffer[srcOffset+_i336]*t;}}});// Characters [].:/ are reserved for track binding syntax. var _RESERVED_CHARS_RE='\\[\\]\\.:\\/';var _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. 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'];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 getValue(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 setValue(array,offset){var bindings=this._bindings;for(var _i337=this._targetGroup.nCachedObjects_,n=bindings.length;_i337!==n;++_i337){bindings[_i337].setValue(array,offset);}},bind:function bind(){var bindings=this._bindings;for(var _i338=this._targetGroup.nCachedObjects_,n=bindings.length;_i338!==n;++_i338){bindings[_i338].bind();}},unbind:function unbind(){var bindings=this._bindings;for(var _i339=this._targetGroup.nCachedObjects_,n=bindings.length;_i339!==n;++_i339){bindings[_i339].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 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} */sanitizeNodeName:function sanitizeNodeName(name){return name.replace(/\s/g,'_').replace(_reservedRe,'');},parseTrackName: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 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;},findNode:function findNode(root,nodeName){if(!nodeName||nodeName===''||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 searchNodeSubtree(children){for(var _i340=0;_i340 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 add(){var objects=this._objects,indicesByUUID=this._indicesByUUID,paths=this._paths,parsedPaths=this._parsedPaths,bindings=this._bindings,nBindings=bindings.length;var knownObject=undefined,nObjects=objects.length,nCachedObjects=this.nCachedObjects_;for(var _i347=0,n=arguments.length;_i347!==n;++_i347){var object=arguments[_i347],uuid=object.uuid;var 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){// 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 uncache(){var objects=this._objects,indicesByUUID=this._indicesByUUID,bindings=this._bindings,nBindings=bindings.length;var nCachedObjects=this.nCachedObjects_,nObjects=objects.length;for(var _i349=0,n=arguments.length;_i349!==n;++_i349){var object=arguments[_i349],uuid=object.uuid,index=indicesByUUID[uuid];if(index!==undefined){delete indicesByUUID[uuid];if(index0){indicesByUUID[_lastObject.uuid]=index;}objects[index]=_lastObject;objects.pop();// accounting is done, now do the same for all bindings for(var _j26=0,_m3=nBindings;_j26!==_m3;++_j26){var _bindingsForPath=bindings[_j26];_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 subscribe_(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;var index=indicesByPath[path];var 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 _i350=nCachedObjects,n=objects.length;_i350!==n;++_i350){var object=objects[_i350];bindingsForPath[_i350]=new PropertyBinding(object,path,parsedPath);}return bindingsForPath;},unsubscribe_:function unsubscribe_(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();}}});class AnimationAction{constructor(mixer,clip){var localRoot=arguments.length>2&&arguments[2]!==undefined?arguments[2]:null;var blendMode=arguments.length>3&&arguments[3]!==undefined?arguments[3]:clip.blendMode;this._mixer=mixer;this._clip=clip;this._localRoot=localRoot;this.blendMode=blendMode;var tracks=clip.tracks,nTracks=tracks.length,interpolants=new Array(nTracks);var interpolantSettings={endingStart:ZeroCurvatureEnding,endingEnd:ZeroCurvatureEnding};for(var _i351=0;_i351!==nTracks;++_i351){var interpolant=tracks[_i351].createInterpolant(null);interpolants[_i351]=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){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(fadeInAction,duration,warp){return fadeInAction.crossFadeFrom(this,duration,warp);}stopFading(){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(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){var mixer=this._mixer,now=mixer.time,timeScale=this.timeScale;var interpolant=this._timeScaleInterpolant;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(){var 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;}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;switch(this.blendMode){case AdditiveAnimationBlendMode:for(var j=0,m=interpolants.length;j!==m;++j){interpolants[j].evaluate(clipTime);propertyMixers[j].accumulateAdditive(weight);}break;case NormalAnimationBlendMode:default:for(var _j27=0,_m4=interpolants.length;_j27!==_m4;++_j27){interpolants[_j27].evaluate(clipTime);propertyMixers[_j27].accumulate(accuIndex,weight);}}}}_updateWeight(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(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(deltaTime){var duration=this._clip.duration;var loop=this.loop;var time=this.time+deltaTime;var loopCount=this._loopCount;var 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 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.time=time;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.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){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(duration,weightNow,weightThen){var mixer=this._mixer,now=mixer.time;var 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;}}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 _bindAction(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;var bindingsByName=bindingsByRoot[rootUuid];if(bindingsByName===undefined){bindingsByName={};bindingsByRoot[rootUuid]=bindingsByName;}for(var _i352=0;_i352!==nTracks;++_i352){var track=tracks[_i352],trackName=track.name;var binding=bindingsByName[trackName];if(binding!==undefined){bindings[_i352]=binding;}else {binding=bindings[_i352];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[_i352].binding.parsedPath;binding=new PropertyMixer(PropertyBinding.create(root,trackName,path),track.ValueTypeName,track.getValueSize());++binding.referenceCount;this._addInactiveBinding(binding,rootUuid,trackName);bindings[_i352]=binding;}interpolants[_i352].resultBuffer=binding.buffer;}},_activateAction:function _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 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 _i353=0,n=bindings.length;_i353!==n;++_i353){var binding=bindings[_i353];if(binding.useCount++===0){this._lendBinding(binding);binding.saveOriginalState();}}this._lendAction(action);}},_deactivateAction:function _deactivateAction(action){if(this._isActiveAction(action)){var bindings=action._propertyBindings;// decrement reference counts / sort out state for(var _i354=0,n=bindings.length;_i354!==n;++_i354){var binding=bindings[_i354];if(--binding.useCount===0){binding.restoreOriginalState();this._takeBackBinding(binding);}}this._takeBackAction(action);}},// Memory manager _initMemoryManager:function _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;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 _isActiveAction(action){var index=action._cacheIndex;return index!==null&&index| 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 _takeBackAction(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 _addInactiveBinding(binding,rootUuid,trackName){var bindingsByRoot=this._bindingsByRootAndName,bindings=this._bindings;var bindingByName=bindingsByRoot[rootUuid];if(bindingByName===undefined){bindingByName={};bindingsByRoot[rootUuid]=bindingByName;}bindingByName[trackName]=binding;binding._cacheIndex=bindings.length;bindings.push(binding);},_removeInactiveBinding:function _removeInactiveBinding(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];if(Object.keys(bindingByName).length===0){delete bindingsByRoot[rootUuid];}},_lendBinding:function _lendBinding(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 _takeBackBinding(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 _lendControlInterpolant(){var interpolants=this._controlInterpolants,lastActiveIndex=this._nActiveControlInterpolants++;var 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 _takeBackControlInterpolant(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 clipAction(clip,optionalRoot,blendMode){var root=optionalRoot||this._root,rootUuid=root.uuid;var clipObject=typeof clip==='string'?AnimationClip.findByName(root,clip):clip;var clipUuid=clipObject!==null?clipObject.uuid:clip;var actionsForClip=this._actionsByClip[clipUuid];var prototypeAction=null;if(blendMode===undefined){if(clipObject!==null){blendMode=clipObject.blendMode;}else {blendMode=NormalAnimationBlendMode;}}if(actionsForClip!==undefined){var 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 var 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:function existingAction(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 stopAllAction(){var actions=this._actions,nActions=this._nActiveActions;for(var _i356=nActions-1;_i356>=0;--_i356){actions[_i356].stop();}return this;},// advance the time and update apply the animation update:function update(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 _i357=0;_i357!==nActions;++_i357){var action=actions[_i357];action._update(time,deltaTime,timeDirection,accuIndex);}// update scene graph var bindings=this._bindings,nBindings=this._nActiveBindings;for(var _i358=0;_i358!==nBindings;++_i358){bindings[_i358].apply(accuIndex);}return this;},// Allows you to seek to a specific time in an animation. setTime:function setTime(timeInSeconds){this.time=0;// Zero out time attribute for AnimationMixer object; for(var _i359=0;_i3590&&arguments[0]!==undefined?arguments[0]:1;var phi=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var theta=arguments.length>2&&arguments[2]!==undefined?arguments[2]: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;}clone(){return new this.constructor().copy(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(){var 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(MathUtils.clamp(y/this.radius,-1,1));}return this;}}/** * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system */class Cylindrical{constructor(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;}set(radius,theta,y){this.radius=radius;this.theta=theta;this.y=y;return this;}clone(){return new this.constructor().copy(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;}}var _vector$8=/*@__PURE__*/new Vector2();class Box2{constructor(min,max){Object.defineProperty(this,'isBox2',{value:true});this.min=min!==undefined?min:new Vector2(+Infinity,+Infinity);this.max=max!==undefined?max:new Vector2(-Infinity,-Infinity);}set(min,max){this.min.copy(min);this.max.copy(max);return this;}setFromPoints(points){this.makeEmpty();for(var _i363=0,il=points.length;_i363this.max.x||point.ythis.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. 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(box){// using 4 splitting planes to rule out intersections return box.max.xthis.max.x||box.max.ythis.max.y?false:true;}clampPoint(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(point){var clampedPoint=_vector$8.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);}}var _startP=/*@__PURE__*/new Vector3();var _startEnd=/*@__PURE__*/new Vector3();class Line3{constructor(start,end){this.start=start!==undefined?start:new Vector3();this.end=end!==undefined?end:new Vector3();}set(start,end){this.start.copy(start);this.end.copy(end);return this;}clone(){return new this.constructor().copy(this);}copy(line){this.start.copy(line.start);this.end.copy(line.end);return this;}getCenter(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(target){if(target===undefined){console.warn('THREE.Line3: .delta() target is now required');target=new Vector3();}return target.subVectors(this.end,this.start);}distanceSq(){return this.start.distanceToSquared(this.end);}distance(){return this.start.distanceTo(this.end);}at(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(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=MathUtils.clamp(t,0,1);}return t;}closestPointToPoint(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(matrix){this.start.applyMatrix4(matrix);this.end.applyMatrix4(matrix);return this;}equals(line){return line.start.equals(this.start)&&line.end.equals(this.end);}}function ImmediateRenderObject(material){Object3D.call(this);this.material=material;this.render=function/* renderCallback */(){};this.hasPositions=false;this.hasNormals=false;this.hasColors=false;this.hasUvs=false;this.positionArray=null;this.normalArray=null;this.colorArray=null;this.uvArray=null;this.count=0;}ImmediateRenderObject.prototype=Object.create(Object3D.prototype);ImmediateRenderObject.prototype.constructor=ImmediateRenderObject;ImmediateRenderObject.prototype.isImmediateRenderObject=true;var _vector$9=/*@__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;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 _i364=0,j=1,l=32;_i3640&&arguments[0]!==undefined?arguments[0]:10;var divisions=arguments.length>1&&arguments[1]!==undefined?arguments[1]:10;var color1=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0x444444;var color2=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0x888888;color1=new Color(color1);color2=new Color(color2);var center=divisions/2;var step=size/divisions;var halfSize=size/2;var vertices=[],colors=[];for(var _i369=0,j=0,k=-halfSize;_i369<=divisions;_i369++,k+=step){vertices.push(-halfSize,0,k,halfSize,0,k);vertices.push(k,0,-halfSize,k,0,halfSize);var color=_i369===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.setAttribute('position',new Float32BufferAttribute(vertices,3));geometry.setAttribute('color',new Float32BufferAttribute(colors,3));var material=new LineBasicMaterial({vertexColors:true,toneMapped:false});super(geometry,material);this.type='GridHelper';}}class PolarGridHelper extends LineSegments{constructor(){var radius=arguments.length>0&&arguments[0]!==undefined?arguments[0]:10;var radials=arguments.length>1&&arguments[1]!==undefined?arguments[1]:16;var circles=arguments.length>2&&arguments[2]!==undefined?arguments[2]:8;var divisions=arguments.length>3&&arguments[3]!==undefined?arguments[3]:64;var color1=arguments.length>4&&arguments[4]!==undefined?arguments[4]:0x444444;var color2=arguments.length>5&&arguments[5]!==undefined?arguments[5]:0x888888;color1=new Color(color1);color2=new Color(color2);var vertices=[];var colors=[];// create the radials for(var _i370=0;_i370<=radials;_i370++){var v=_i370/radials*(Math.PI*2);var x=Math.sin(v)*radius;var z=Math.cos(v)*radius;vertices.push(0,0,0);vertices.push(x,0,z);var color=_i370&1?color1:color2;colors.push(color.r,color.g,color.b);colors.push(color.r,color.g,color.b);}// create the circles for(var _i371=0;_i371<=circles;_i371++){var _color=_i371&1?color1:color2;var r=radius-radius/circles*_i371;for(var j=0;j1&&arguments[1]!==undefined?arguments[1]: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.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$3.setFromObject(this.object);}if(_box$3.isEmpty())return;var min=_box$3.min;var max=_box$3.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();}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){var color=arguments.length>1&&arguments[1]!==undefined?arguments[1]: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.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){var 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){var size=arguments.length>1&&arguments[1]!==undefined?arguments[1]:1;var hex=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0xffff00;var color=hex;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.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;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.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){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);super.updateMatrixWorld(force);}}var _axis$1=/*@__PURE__*/new Vector3();var _lineGeometry,_coneGeometry;class ArrowHelper extends Object3D{constructor(dir,origin,length,color,headLength,headWidth){super();// dir is assumed to be normalized this.type='ArrowHelper';if(dir===undefined)dir=new Vector3(0,0,1);if(origin===undefined)origin=new Vector3(0,0,0);if(length===undefined)length=1;if(color===undefined)color=0xffff00;if(headLength===undefined)headLength=0.2*length;if(headWidth===undefined)headWidth=0.2*headLength;if(_lineGeometry===undefined){_lineGeometry=new BufferGeometry();_lineGeometry.setAttribute('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,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$1.set(dir.z,0,-dir.x).normalize();var radians=Math.acos(dir.y);this.quaternion.setFromAxisAngle(_axis$1,radians);}}setLength(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.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(){var size=arguments.length>0&&arguments[0]!==undefined?arguments[0]: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.setAttribute('position',new Float32BufferAttribute(vertices,3));geometry.setAttribute('color',new Float32BufferAttribute(colors,3));var material=new LineBasicMaterial({vertexColors:true,toneMapped:false});super(geometry,material);this.type='AxesHelper';}}var _floatView=new Float32Array(1);var _int32View=new Int32Array(_floatView.buffer);var DataUtils={// Converts float32 to float16 (stored as uint16 value). toHalfFloat:function toHalfFloat(val){// 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;var x=_int32View[0];var bits=x>>16&0x8000;/* Get the sign */var m=x>>12&0x07ff;/* Keep one extra bit for rounding */var 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;}};var LOD_MIN=4;var LOD_MAX=8;var 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. var EXTRA_LOD_SIGMA=[0.125,0.215,0.35,0.446,0.526,0.582];var 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. var MAX_SAMPLES=20;var ENCODINGS={[LinearEncoding]:0,[sRGBEncoding]:1,[RGBEEncoding]:2,[RGBM7Encoding]:3,[RGBM16Encoding]:4,[RGBDEncoding]:5,[GammaEncoding]:6};var _flatCamera=/*@__PURE__*/new OrthographicCamera();var{_lodPlanes,_sizeLods,_sigmas}=/*@__PURE__*/_createPlanes();var _clearColor=/*@__PURE__*/new Color();var _oldTarget=null;// Golden Ratio var PHI=(1+Math.sqrt(5))/2;var 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. var _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. */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){var sigma=arguments.length>1&&arguments[1]!==undefined?arguments[1]:0;var near=arguments.length>2&&arguments[2]!==undefined?arguments[2]:0.1;var far=arguments.length>3&&arguments[3]!==undefined?arguments[3]:100;_oldTarget=this._renderer.getRenderTarget();var 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 * (RGBFormat) or HDR (RGBEFormat). The ideal input image size is 1k (1024 x 512), * as this matches best with the 256 x 256 cubemap output. */fromEquirectangular(equirectangular){return this._fromTexture(equirectangular);}/** * Generates a PMREM from an cubemap texture, which can be either LDR * (RGBFormat) or HDR (RGBEFormat). The ideal input cube size is 256 x 256, * as this matches best with the 256 x 256 cubemap output. */fromCubemap(cubemap){return this._fromTexture(cubemap);}/** * 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._cubemapShader!==null)this._cubemapShader.dispose();if(this._equirectShader!==null)this._equirectShader.dispose();for(var _i373=0;_i373<_lodPlanes.length;_i373++){_lodPlanes[_i373].dispose();}}// private interface _cleanup(outputTarget){this._pingPongRenderTarget.dispose();this._renderer.setRenderTarget(_oldTarget);outputTarget.scissorTest=false;_setViewport(outputTarget,0,0,outputTarget.width,outputTarget.height);}_fromTexture(texture){_oldTarget=this._renderer.getRenderTarget();var cubeUVRenderTarget=this._allocateTargets(texture);this._textureToCubeUV(texture,cubeUVRenderTarget);this._applyPMREM(cubeUVRenderTarget);this._cleanup(cubeUVRenderTarget);return cubeUVRenderTarget;}_allocateTargets(texture){// warning: null texture is valid var params={magFilter:NearestFilter,minFilter:NearestFilter,generateMipmaps:false,type:UnsignedByteType,format:RGBEFormat,encoding:_isLDR(texture)?texture.encoding:RGBEEncoding,depthBuffer:false};var cubeUVRenderTarget=_createRenderTarget(params);cubeUVRenderTarget.depthBuffer=texture?false:true;this._pingPongRenderTarget=_createRenderTarget(params);return cubeUVRenderTarget;}_compileMaterial(material){var tmpMesh=new Mesh(_lodPlanes[0],material);this._renderer.compile(tmpMesh,_flatCamera);}_sceneToCubeUV(scene,near,far,cubeUVRenderTarget){var fov=90;var aspect=1;var cubeCamera=new PerspectiveCamera(fov,aspect,near,far);var upSign=[1,-1,1,1,1,1];var forwardSign=[1,1,1,-1,-1,-1];var renderer=this._renderer;var outputEncoding=renderer.outputEncoding;var toneMapping=renderer.toneMapping;renderer.getClearColor(_clearColor);var clearAlpha=renderer.getClearAlpha();renderer.toneMapping=NoToneMapping;renderer.outputEncoding=LinearEncoding;var background=scene.background;if(background&&background.isColor){background.convertSRGBToLinear();// Convert linear to RGBE var maxComponent=Math.max(background.r,background.g,background.b);var fExp=Math.min(Math.max(Math.ceil(Math.log2(maxComponent)),-128.0),127.0);background=background.multiplyScalar(Math.pow(2.0,-fExp));var alpha=(fExp+128.0)/255.0;renderer.setClearColor(background,alpha);scene.background=null;}for(var _i374=0;_i374<6;_i374++){var col=_i374%3;if(col==0){cubeCamera.up.set(0,upSign[_i374],0);cubeCamera.lookAt(forwardSign[_i374],0,0);}else if(col==1){cubeCamera.up.set(0,0,upSign[_i374]);cubeCamera.lookAt(0,forwardSign[_i374],0);}else {cubeCamera.up.set(0,upSign[_i374],0);cubeCamera.lookAt(0,0,forwardSign[_i374]);}_setViewport(cubeUVRenderTarget,col*SIZE_MAX,_i374>2?SIZE_MAX:0,SIZE_MAX,SIZE_MAX);renderer.setRenderTarget(cubeUVRenderTarget);renderer.render(scene,cubeCamera);}renderer.toneMapping=toneMapping;renderer.outputEncoding=outputEncoding;renderer.setClearColor(_clearColor,clearAlpha);}_textureToCubeUV(texture,cubeUVRenderTarget){var renderer=this._renderer;if(texture.isCubeTexture){if(this._cubemapShader==null){this._cubemapShader=_getCubemapShader();}}else {if(this._equirectShader==null){this._equirectShader=_getEquirectShader();}}var material=texture.isCubeTexture?this._cubemapShader:this._equirectShader;var mesh=new Mesh(_lodPlanes[0],material);var uniforms=material.uniforms;uniforms['envMap'].value=texture;if(!texture.isCubeTexture){uniforms['texelSize'].value.set(1.0/texture.image.width,1.0/texture.image.height);}uniforms['inputEncoding'].value=ENCODINGS[texture.encoding];uniforms['outputEncoding'].value=ENCODINGS[cubeUVRenderTarget.texture.encoding];_setViewport(cubeUVRenderTarget,0,0,3*SIZE_MAX,2*SIZE_MAX);renderer.setRenderTarget(cubeUVRenderTarget);renderer.render(mesh,_flatCamera);}_applyPMREM(cubeUVRenderTarget){var renderer=this._renderer;var autoClear=renderer.autoClear;renderer.autoClear=false;for(var _i375=1;_i375MAX_SAMPLES){console.warn("sigmaRadians, ".concat(sigmaRadians,", is too large and will clip, as it requested ").concat(samples," samples when the maximum is set to ").concat(MAX_SAMPLES));}var weights=[];var sum=0;for(var _i376=0;_i376LOD_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 _isLDR(texture){if(texture===undefined||texture.type!==UnsignedByteType)return false;return texture.encoding===LinearEncoding||texture.encoding===sRGBEncoding||texture.encoding===GammaEncoding;}function _createPlanes(){var _lodPlanes=[];var _sizeLods=[];var _sigmas=[];var lod=LOD_MAX;for(var _i378=0;_i378LOD_MAX-LOD_MIN){sigma=EXTRA_LOD_SIGMA[_i378-LOD_MAX+LOD_MIN-1];}else if(_i378==0){sigma=0;}_sigmas.push(sigma);var texelSize=1.0/(sizeLod-1);var min=-texelSize/2;var max=1+texelSize/2;var uv1=[min,min,max,min,max,max,min,min,max,max,min,max];var cubeFaces=6;var vertices=6;var positionSize=3;var uvSize=2;var faceIndexSize=1;var position=new Float32Array(positionSize*vertices*cubeFaces);var uv=new Float32Array(uvSize*vertices*cubeFaces);var faceIndex=new Float32Array(faceIndexSize*vertices*cubeFaces);for(var face=0;face2?0:-1;var 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);var fill=[face,face,face,face,face,face];faceIndex.set(fill,faceIndexSize*vertices*face);}var 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){var 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){var weights=new Float32Array(maxSamples);var poleAxis=new Vector3(0,1,0);var 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},'inputEncoding':{value:ENCODINGS[LinearEncoding]},'outputEncoding':{value:ENCODINGS[LinearEncoding]}},vertexShader:_getCommonVertexShader(),fragmentShader:/* glsl */"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform int samples;\n\t\t\tuniform float weights[ n ];\n\t\t\tuniform bool latitudinal;\n\t\t\tuniform float dTheta;\n\t\t\tuniform float mipInt;\n\t\t\tuniform vec3 poleAxis;\n\n\t\t\t".concat(_getEncodings(),"\n\n\t\t\t#define ENVMAP_TYPE_CUBE_UV\n\t\t\t#include \n\n\t\t\tvec3 getSample( float theta, vec3 axis ) {\n\n\t\t\t\tfloat cosTheta = cos( theta );\n\t\t\t\t// Rodrigues' axis-angle rotation\n\t\t\t\tvec3 sampleDirection = vOutputDirection * cosTheta\n\t\t\t\t\t+ cross( axis, vOutputDirection ) * sin( theta )\n\t\t\t\t\t+ axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta );\n\n\t\t\t\treturn bilinearCubeUV( envMap, sampleDirection, mipInt );\n\n\t\t\t}\n\n\t\t\tvoid main() {\n\n\t\t\t\tvec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection );\n\n\t\t\t\tif ( all( equal( axis, vec3( 0.0 ) ) ) ) {\n\n\t\t\t\t\taxis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x );\n\n\t\t\t\t}\n\n\t\t\t\taxis = normalize( axis );\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis );\n\n\t\t\t\tfor ( int i = 1; i < n; i++ ) {\n\n\t\t\t\t\tif ( i >= samples ) {\n\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\t}\n\n\t\t\t\t\tfloat theta = dTheta * float( i );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis );\n\t\t\t\t\tgl_FragColor.rgb += weights[ i ] * getSample( theta, axis );\n\n\t\t\t\t}\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t"),blending:NoBlending,depthTest:false,depthWrite:false});return shaderMaterial;}function _getEquirectShader(){var texelSize=new Vector2(1,1);var shaderMaterial=new RawShaderMaterial({name:'EquirectangularToCubeUV',uniforms:{'envMap':{value:null},'texelSize':{value:texelSize},'inputEncoding':{value:ENCODINGS[LinearEncoding]},'outputEncoding':{value:ENCODINGS[LinearEncoding]}},vertexShader:_getCommonVertexShader(),fragmentShader:/* glsl */"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform sampler2D envMap;\n\t\t\tuniform vec2 texelSize;\n\n\t\t\t".concat(_getEncodings(),"\n\n\t\t\t#include \n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\n\t\t\t\tvec3 outputDirection = normalize( vOutputDirection );\n\t\t\t\tvec2 uv = equirectUv( outputDirection );\n\n\t\t\t\tvec2 f = fract( uv / texelSize - 0.5 );\n\t\t\t\tuv -= f * texelSize;\n\t\t\t\tvec3 tl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x += texelSize.x;\n\t\t\t\tvec3 tr = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.y += texelSize.y;\n\t\t\t\tvec3 br = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\t\t\t\tuv.x -= texelSize.x;\n\t\t\t\tvec3 bl = envMapTexelToLinear( texture2D ( envMap, uv ) ).rgb;\n\n\t\t\t\tvec3 tm = mix( tl, tr, f.x );\n\t\t\t\tvec3 bm = mix( bl, br, f.x );\n\t\t\t\tgl_FragColor.rgb = mix( tm, bm, f.y );\n\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t"),blending:NoBlending,depthTest:false,depthWrite:false});return shaderMaterial;}function _getCubemapShader(){var shaderMaterial=new RawShaderMaterial({name:'CubemapToCubeUV',uniforms:{'envMap':{value:null},'inputEncoding':{value:ENCODINGS[LinearEncoding]},'outputEncoding':{value:ENCODINGS[LinearEncoding]}},vertexShader:_getCommonVertexShader(),fragmentShader:/* glsl */"\n\n\t\t\tprecision mediump float;\n\t\t\tprecision mediump int;\n\n\t\t\tvarying vec3 vOutputDirection;\n\n\t\t\tuniform samplerCube envMap;\n\n\t\t\t".concat(_getEncodings(),"\n\n\t\t\tvoid main() {\n\n\t\t\t\tgl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 );\n\t\t\t\tgl_FragColor.rgb = envMapTexelToLinear( textureCube( envMap, vec3( - vOutputDirection.x, vOutputDirection.yz ) ) ).rgb;\n\t\t\t\tgl_FragColor = linearToOutputTexel( gl_FragColor );\n\n\t\t\t}\n\t\t"),blending:NoBlending,depthTest:false,depthWrite:false});return shaderMaterial;}function _getCommonVertexShader(){return/* glsl */"\n\n\t\tprecision mediump float;\n\t\tprecision mediump int;\n\n\t\tattribute vec3 position;\n\t\tattribute vec2 uv;\n\t\tattribute float faceIndex;\n\n\t\tvarying vec3 vOutputDirection;\n\n\t\t// RH coordinate system; PMREM face-indexing convention\n\t\tvec3 getDirection( vec2 uv, float face ) {\n\n\t\t\tuv = 2.0 * uv - 1.0;\n\n\t\t\tvec3 direction = vec3( uv, 1.0 );\n\n\t\t\tif ( face == 0.0 ) {\n\n\t\t\t\tdirection = direction.zyx; // ( 1, v, u ) pos x\n\n\t\t\t} else if ( face == 1.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xz *= -1.0; // ( -u, 1, -v ) pos y\n\n\t\t\t} else if ( face == 2.0 ) {\n\n\t\t\t\tdirection.x *= -1.0; // ( -u, v, 1 ) pos z\n\n\t\t\t} else if ( face == 3.0 ) {\n\n\t\t\t\tdirection = direction.zyx;\n\t\t\t\tdirection.xz *= -1.0; // ( -1, v, -u ) neg x\n\n\t\t\t} else if ( face == 4.0 ) {\n\n\t\t\t\tdirection = direction.xzy;\n\t\t\t\tdirection.xy *= -1.0; // ( -u, -1, v ) neg y\n\n\t\t\t} else if ( face == 5.0 ) {\n\n\t\t\t\tdirection.z *= -1.0; // ( u, v, -1 ) neg z\n\n\t\t\t}\n\n\t\t\treturn direction;\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\tvOutputDirection = getDirection( uv, faceIndex );\n\t\t\tgl_Position = vec4( position, 1.0 );\n\n\t\t}\n\t";}function _getEncodings(){return/* glsl */"\n\n\t\tuniform int inputEncoding;\n\t\tuniform int outputEncoding;\n\n\t\t#include \n\n\t\tvec4 inputTexelToLinear( vec4 value ) {\n\n\t\t\tif ( inputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( inputEncoding == 1 ) {\n\n\t\t\t\treturn sRGBToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 2 ) {\n\n\t\t\t\treturn RGBEToLinear( value );\n\n\t\t\t} else if ( inputEncoding == 3 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 7.0 );\n\n\t\t\t} else if ( inputEncoding == 4 ) {\n\n\t\t\t\treturn RGBMToLinear( value, 16.0 );\n\n\t\t\t} else if ( inputEncoding == 5 ) {\n\n\t\t\t\treturn RGBDToLinear( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn GammaToLinear( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 linearToOutputTexel( vec4 value ) {\n\n\t\t\tif ( outputEncoding == 0 ) {\n\n\t\t\t\treturn value;\n\n\t\t\t} else if ( outputEncoding == 1 ) {\n\n\t\t\t\treturn LinearTosRGB( value );\n\n\t\t\t} else if ( outputEncoding == 2 ) {\n\n\t\t\t\treturn LinearToRGBE( value );\n\n\t\t\t} else if ( outputEncoding == 3 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 7.0 );\n\n\t\t\t} else if ( outputEncoding == 4 ) {\n\n\t\t\t\treturn LinearToRGBM( value, 16.0 );\n\n\t\t\t} else if ( outputEncoding == 5 ) {\n\n\t\t\t\treturn LinearToRGBD( value, 256.0 );\n\n\t\t\t} else {\n\n\t\t\t\treturn LinearToGamma( value, 2.2 );\n\n\t\t\t}\n\n\t\t}\n\n\t\tvec4 envMapTexelToLinear( vec4 color ) {\n\n\t\t\treturn inputTexelToLinear( color );\n\n\t\t}\n\t";}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;var NoColors=0;var FaceColors=1;var VertexColors=2;function MeshFaceMaterial(materials){console.warn('THREE.MeshFaceMaterial has been removed. Use an Array instead.');return materials;}function MultiMaterial(){var materials=arguments.length>0&&arguments[0]!==undefined?arguments[0]:[];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$1(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;};// Object.assign(CurvePath.prototype,{createPointsGeometry:function createPointsGeometry(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 createSpacedPointsGeometry(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 createGeometry(points){console.warn('THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.');var geometry=new Geometry();for(var _i379=0,l=points.length;_i379 p.ids.includes(o));else { point = points.find(p => math.closeTo(p.x, o.x, precision) && math.closeTo(p.y, o.y, precision)); if (!point) point = new Point(o.x, o.y, { record: true, id: o.id }, type);else { //console.log('addPoint', point, o) point.addPoint(o.id); } } if (!point) { console.log("no point!"); } return point; }; var getLine = function getLine(id) { return lines.find(line => line.ids.includes(id)); }; var getAngleInfo = function getAngleInfo(points) { var info = {}; info.angle = points[1].clone().sub(points[0]).angle(); if (math.closeTo(info.angle, Math.PI * 2)) { //如360-0.01 info.angle -= Math.PI * 2; //有可能得到负数-0.001 } else if (info.angle > Math.PI || math.closeTo(info.angle, Math.PI)) { //如180+-0.01 info.angle -= Math.PI; info.reverse = true; } return info; //结果大约是 0 - 3.14 }; class Point extends Vector2 { constructor(x, y) { var o = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; super(x, y); if (o.record) { this.id = o.id; if (this.id == void 0) this.id = "add_" + points.length; this.ids = [this.id]; //存储拥有该坐标的点原始数据的id points.push(this); } this.type = o.type || ""; this.lines = []; } addPoint(id) { this.ids.push(id); } searchLineByFactor(dir, type, comeLine) { var lines = this.lines.filter(line => line.searchTime < 2); if (lines.length == 0) return;else if (lines.length == 1) return lines[0];else lines = lines.filter(line => line != comeLine); if (lines.length == 1) return lines[0]; var result; lines.forEach(line => { var vec = line.getVector(); if (line.points[1] == this) vec.negate(); var factor = math.getVec2Angle(dir, vec); if (new Vector3(dir.x, dir.y, 0).cross(new Vector3(vec.x, vec.y, 0)).z < 0) factor *= -1; ///// if (!result) { result = { line, factor }; } else { if (type == "min" && factor < result.factor || type == "max" && factor > result.factor) result = { line, factor }; } }); return result.line; } } var lineLen = 0; class Line$1 { constructor(o) { if (o.points[0] == o.points[1]) return; this.points = o.points; this.type = o.type || 'line'; if (this.type == 'line') { var oldLine = lines.find(line => line.points.includes(o.points[0]) && line.points.includes(o.points[1])); if (oldLine) { o.id != void 0 && oldLine.ids.push(o.id); return oldLine; } this.id = o.id == void 0 ? "line" + lineLen++ : o.id; this.ids = [this.id]; o.dontWriteToPoint || this.points.forEach(point => { point.lines.push(this); }); o.isChild || lines.push(this); this.searchTime = 0; // 最多两次 } this.children = []; //分割 this.parents = []; //分割 this.match = []; } getAngleInfo() { var angleInfo = getAngleInfo(this.points); this.angle = angleInfo.angle; this.reverse = angleInfo.reverse; } getIntersectWithLine(line, precision) { var joint = line.points.find(point => this.points.includes(point)); if (joint) return { point: joint, type: "joint" }; var intersect = math.isLineIntersect(line.points, this.points, false, precision); if (intersect) return { point: intersect, type: "intersect" }; } writeToPoint() { this.points.forEach(point => { point.lines.includes(this) || point.lines.push(this); }); } checkIfParent(line) { if (this == line) { return true; //原因就是slice的点和端点很近 误差导致 } else return this.parents.find(e => e.checkIfParent(line)); } splitByPoint(point) { var line1 = new Line$1({ points: [point, this.points[0]], dontWriteToPoint: true, hasntsure: true }); var line2 = new Line$1({ points: [point, this.points[1]], dontWriteToPoint: true, hasntsure: true }); if (!line1.points || !line2.points) { //有至少一个是点相同的,没写到group.lines里 console.warn('splitByPoint 线有点相同'); return; } if (this.checkIfParent(line1) || this.checkIfParent(line2) || line1.checkIfParent(this) || line2.checkIfParent(this)) { console.warn("splitByPoint 发现parent和children一样"); //,请检查getSliceWalls,尤其 if(math.closeTo(line1.angle,line2.angle)){ 处 return; } var deal = line => { this.children.push(line); line.parents.push(this); if (!lines.includes(line)) lines.push(line); line.writeToPoint(); }; deal(line1); deal(line2); var index = this.points[0].lines.indexOf(this); index > -1 && this.points[0].lines.splice(index, 1); var index = this.points[1].lines.indexOf(this); index > -1 && this.points[1].lines.splice(index, 1); return [line1, line2]; } splitByPoints(points) { points = points.map(point => { return { dis: point.distanceTo(this.points[0]), point: point }; }); points.sort((point1, point2) => { return point1.dis - point2.dis; }); var children = []; points.forEach((point, index) => { var line1 = new Line$1({ points: [point.point, index == 0 ? this.points[0] : points[index - 1].point], group: this.group, dontWriteToPoint: true, hasntsure: true }); children.push(line1); }); var line2 = new Line$1({ points: [points[points.length - 1].point, this.points[1]], group: this.group, dontWriteToPoint: true, hasntsure: true }); children.push(line2); var a = children.find(line => !line.points || this.checkIfParent(line) || line.checkIfParent(this)); if (a) { console.error("splitByPoints return"); return; } children.forEach(line => { this.children.push(line); line.parents.push(this); if (!lines.includes(line)) lines.push(line); line.writeToPoint(); line.writeToPoint(); }); var index = this.points[0].lines.indexOf(this); index > -1 && this.points[0].lines.splice(index, 1); var index = this.points[1].lines.indexOf(this); index > -1 && this.points[1].lines.splice(index, 1); } getAllSlices() { //如果有被分割的片段 就返回片段,否则返回自身 var children = []; var _traverse = function traverse(elem) { if (elem.children.length == 0) children.push(elem);else elem.children.forEach(_traverse); }; _traverse(this); return children; } getVector() { return this.points[1].clone().sub(this.points[0]); } getLength() { return this.points[0].distanceTo(this.points[1]); } getCenter() { return this.points[1].clone().add(this.points[0]).multiplyScalar(.5); } } var getMixedSet = function getMixedSet(arr1, arr2) { //交集 return arr1.filter(item => arr2.includes(item)); }; var getUnionSet = function getUnionSet(arr1, arr2) { //并集 return arr1.concat(arr2.filter(item => !arr1.includes(item))); }; var getDifferenceSet = function getDifferenceSet(arr1, arr2) { //差集 var arr11 = arr1.filter(item => !arr2.includes(item)); var arr22 = arr2.filter(item => !arr1.includes(item)); return arr11.concat(arr22); }; var getDifferenceSetMuti = function getDifferenceSetMuti(arr) { //收集绝对没有重复的元素,也就是判断出现次数=1的 var set = []; arr.forEach(arr1 => { arr1.forEach(item => { var index = set.indexOf(item); if (index > -1) { set.splice(index, 1); } else { set.push(item); } }); }); return set; }; function DoorAtWhichLine(points, lines) { var mid = points[0].clone().add(points[1]).multiplyScalar(0.5); lines = lines.filter(line => math.ifPointAtLineBound(mid, line.points, precision)); if (lines.length == 0) return; var result = { line: null, dis: Infinity }; lines.forEach(line => { var foot = math.getFootPoint(mid, line.points[0], line.points[1]); var dis = foot.distanceTo(mid); if (dis < result.dis) { result.line = line; result.dis = dis; } }); return result; } var ringLen = 0; class Ring { constructor(o) { this.id = ringLen++; this.type = o.type || 'normal'; this.points = o.points; this.lines = o.lines; rings.push(this); this.child = []; //包含的环 this.parent = []; //被包含的环 this.smallNeibours = []; //相邻最小环(存在和它有一个以上的相同边的最小环) var area = math.getArea(this.points); this.area = Math.abs(area); this.isClockwise = area < 0; //是否逆时针。一般都是逆时针得到的,如果是顺时针,可能是贪吃蛇的情况,可能不是最小环,需要去掉。 } } var findLine = function findLine(p1, p2) { return lines.find(line => line.points.includes(p1) && line.points.includes(p2)); }; var ifSamePart = function ifSamePart(checkPart, part) { //checkPart中所包含的part片段是否和基准part的顺序一样(逆序也可以, 中间有其他数也可以,起始不同也行。比如 01234和204一样的) var axis, startIndex, newCheckPart = []; for (var j = 0, len1 = checkPart.length; j < len1; j++) { //将checkPart中比part多的数除去,使两个数组中包含的数完全相同。 if (part.indexOf(checkPart[j]) > -1) newCheckPart.push(checkPart[j]); } for (var i = 0, len = part.length; i < len; i++) { var index = newCheckPart.indexOf(part[i]); if (index == -1) return false; if (i == 0) startIndex = index; //标记第一个查找点对应的index else if (i == 1) { //标记查找顺序是正还是逆 axis = index - startIndex; if (axis == len - 1) axis = -1; //刚好是首和尾 else if (axis == 1 - len) axis = 1; if (axis != -1 && axis != 1) { return false; } } else { //判断是否是按顺序的 if (index != (startIndex + axis * i + len) % len) return false; } } return { sameAxis: axis > 0 }; //如果一样的话返回正逆是否相同 }; //或者判断是否有相同边(但是相同点是可以组成不同环) var ifSameRing = function ifSameRing(ring1, ring2) { //判断两个环是否相等。 除了可以逆向外顺序要对 if (ring1 instanceof Ring) ring1 = ring1.points; if (ring2 instanceof Ring) ring2 = ring2.points; if (ring1.length != ring2.length) return false; if (ring1.lines && ring2.lines) { if (getDifferenceSet(ring1.lines, ring2.lines).length == 0) return true; //差集个数为0 } else { if (ifSamePart(ring1, ring2)) return true; } }; var _atWhichChildLine = function atWhichChildLine(point, line, precision) { if (line.children.length == 0) { //这里可能要放低精度 保证能找到 if (math.ifPointAtLineBound(point, line.points, precision)) return line; } else { for (var i = 0; i < line.children.length; i++) { var at = _atWhichChildLine(point, line.children[i], precision); if (at) return at; } } }; function getSliceLines() { var len = lines.length; var _deal = function deal(line1, line2) { if (line1 == line2) return; if (line1.angle == void 0) line1.getAngleInfo(); if (line2.angle == void 0) line2.getAngleInfo(); var intersect = line1.getIntersectWithLine(line2, precision); if (intersect) { var point; //得到交点 if (intersect.type == "intersect") { point = getPoint(intersect.point, "whenGetSliceLines"); var line1_ = _atWhichChildLine(point, line1); var line2_ = _atWhichChildLine(point, line2); //重合的情况还没考虑(平行) if (!line1_) line1_ = _atWhichChildLine(point, line1, precision); //降低精度 if (!line1_) line1_ = _atWhichChildLine(point, line1, precision * 2); //降低精度 if (!line2_) line2_ = _atWhichChildLine(point, line2, precision); if (!line2_) line2_ = _atWhichChildLine(point, line2, precision * 2); //降低精度 //拆分线条: //如果还报错,找不到ChildLine,就直接返回吧 或者搞个循环 逐渐降低精度 if (!line1_ || !line2_) { console.warn("atWhichChildLine仍旧找不到 :" + line1.id + ',' + line2.id + ", pointId: " + point.id); line1_ || console.warn("找不到line1"); line2_ || console.warn("找不到line2"); return; } if (line1_.points.find(p => p == point) && line2_.points.find(p => p == point)) {//这个点是line1_、 line2_端点,不做处理 //console.log("joint型 "+point.id) } else if (line1_.points.find(p => p == point)) { //T型交叉 line2_.splitByPoint(point); //加入到母线中,之后还先用母线判断交点 //console.log("T型交叉1 "+point.id) } else if (line2_.points.find(p => p == point)) { //T型交叉 line1_.splitByPoint(point); //console.log("T型交叉2 "+point.id) } else { //十字交叉 line1_.splitByPoint(point); line2_.splitByPoint(point); } } else { point = intersect.point; //交点是端点 if (math.closeTo(line1.angle, line2.angle)) { //重合一部分 var children1 = line1.getAllSlices(); var children2 = line2.getAllSlices(); if (children1.length > 1 || children2.length > 1) { //使用最小分割片段来比较 children1.forEach(child1 => { children2.forEach(child2 => { _deal(child1, child2); }); }); return; } var anotherPoint1 = line1.points.find(point_ => point_ != point); var anotherPoint2 = line2.points.find(point_ => point_ != point); if (math.ifPointAtLineBound(anotherPoint1, line2.points)) { line2.splitByPoint(anotherPoint1); } else if (math.ifPointAtLineBound(anotherPoint2, line1.points)) { line1.splitByPoint(anotherPoint2); } } } } else if (math.closeTo(line1.angle, line2.angle)) { var vec1 = line1.getVector(); var vec = line1.points[0].clone().sub(line2.points[0]); var cos = math.getVec2Cos(vec1, vec); if (math.closeTo(cos, -1, 1e-4) || math.closeTo(cos, 1, 1e-4)) { //共线 var children1 = line1.getAllSlices(); var children2 = line2.getAllSlices(); if (children1.length > 1 || children2.length > 1) { //使用最小分割片段来比较 children1.forEach(child1 => { children2.forEach(child2 => { _deal(child1, child2); }); }); return; } //判断是否重叠 var A = line1.points[0]; var C = line1.reverse == line2.reverse ? line2.points[0] : line2.points[1]; var B = line1.points[1]; var D = line1.reverse == line2.reverse ? line2.points[1] : line2.points[0]; var BC = C.clone().sub(B); var AD = D.clone().sub(A); if (BC.length() < AD.length()) { var BA = A.clone().sub(B); if (math.getVec2Angle(BC, BA) >= 1.57) return; //没有重叠部分 } else { var AB = B.clone().sub(A); if (math.getVec2Angle(AD, AB) >= 1.57) return; } var f = function f(line1, line2) { var one = math.ifPointAtLineBound(line1.points[0], line2.points); var two = math.ifPointAtLineBound(line1.points[1], line2.points); if (one && two) { //line1在line2上 line2.splitByPoints(line1.points); return true; } else if (one || two) { //错开 var point1 = one ? line1.points[0] : line1.points[1]; var anotherPoint1 = one ? line1.points[1] : line1.points[0]; var dis1 = line2.points[0].distanceTo(anotherPoint1); var dis2 = line2.points[1].distanceTo(anotherPoint1); var point2 = dis1 < dis2 ? line2.points[0] : line2.points[1]; line1.splitByPoint(point2); line2.splitByPoint(point1); return true; } }; f(line1, line2) || f(line2, line1); } } }; for (var i = 0; i < len; i++) { var line1 = lines[i]; for (var j = i + 1; j < len; j++) { var line2 = lines[j]; _deal(line1, line2); } } //console.log("原有线条个数:"+len) //lines = lines.filter((line)=>{return line.children.length == 0}) //console.log("现有线条个数:"+lines.length) } var bound = new Box2(); var build = function build(o) { //融合了相近点 //根据bound 处理precision o.points.forEach(p => { bound.expandByPoint(new Vector2(p.x, p.y)); }); if (o.precision != void 0) { precision = o.precision; } else { var boundSize = bound.getSize(new Vector2()); precision = MathUtils.clamp(Math.max(boundSize.x, boundSize.y) / 70, 0.2, 2); } o.points.forEach(point => getPoint(point)); //{x:..,y:..} o.lines.forEach(line => { //{p1:id1. p2:id2} new Line$1({ points: [getPoint(line.p1), getPoint(line.p2)], id: line.id }); }); //注意:不能出现一条线的两个点坐标一致,否则寻路时方向出错。 所以手动融合下相近点。 }; var searchRings = function searchRings() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; points = []; lines = []; rings = []; lineLen = ringLen = 0; o.points = o.points || []; o.lines = o.lines || []; build(o); if (!o.dontSliceLines) { getSliceLines(); } //查找最小回路: //参考: 引入方向因子的最小回路、最大回路搜索算法.pdf //方法: 逆时针寻找(标记)最外层大环 -->从走过的点开始逆时针寻找最小环(直到所有可走的路被走过两次)-->逆时针寻找最外层大环(直到所有可走的路被走过两次)-->.. //其中找大环时选择方向因子最小的路, 而小环则相反(但只有开始第一条路是一样的, 都是选择最左边的点的因子最小的路)。 //标记方法: 每条线需要被搜索两次才算完毕。搜索完毕的线退出搜索。(依据:搜索完全部最小回路后 , 在无向图中删除搜索过 2 次的边及孤立节点得到退化图 , 恰好构成最大回路。) var searchTime = 0; var addRingJudgeCount = 0; var addRingJudge = function addRingJudge(ring, lines, connectedLines, type) { // 处理拣出的片段 addRingJudgeCount++; //console.log("addRingJudge points("+ type+"):"+ ring.map(point=>point.id) ) if (o.onlyGetOutRing && type == "small") return; if (type == "small" || o.onlyGetOutRing) { //挑出回路: var newRings = []; while (ring.length) { var road = []; var turnBack = false; for (var i = 0; i < ring.length; i++) { if (road.includes(ring[i])) { //如果走到方才的点,可能形成回路。 无论是不是回路都要摘去这段。 var index = road.indexOf(ring[i]); var pointArr = ring.slice(index, i); var linesArr = lines.slice(index, i); ring.splice(index, i - index); lines.splice(index, i - index); if (pointArr.length > 2) { // 如果只有两个数,代表原路返回, 如 1->2(->1) if (!rings.find(ring_ => ifSameRing(pointArr, ring_))) newRings.push(new Ring({ points: pointArr, lines: linesArr })); } turnBack = true; break; } else { road.push(ring[i]); turnBack = false; } } if (!turnBack) { //没有重复的点,那么就直接处理整条。 if (ring.length > 2) { // 如果只有两个数,代表原路返回, 如 1->2(->1) if (!rings.find(ring_ => ifSameRing(ring, ring_))) newRings.push(new Ring({ points: ring, lines })); } break; } } if (type != 'small') { newRings.forEach(e => e.isOutRing = true); } //console.log(newRings) } else { return ring; } }; var _search = function search(point2d, comeRoad, type, connectedLines) { searchTime++; var goLine; var direction; if (type.includes("big")) { if (!comeRoad) { if (type.includes("Left")) { //逆时针 direction = new Vector2(1, 0); } else { direction = new Vector2(-1, 0); } goLine = point2d.searchLineByFactor(direction, "min"); } else { var lastPoint = comeRoad.points[comeRoad.points.length - 1]; direction = point2d.clone().sub(lastPoint); goLine = point2d.searchLineByFactor(direction, "min", findLine(point2d, lastPoint)); } } else { if (!comeRoad) { //似乎找最小环时,第一条线也是找最小的因子,这样才能保证逆时针(除非只有顺时针一条路) direction = new Vector2(1, 0); goLine = point2d.searchLineByFactor(direction, "min"); } else { var lastPoint = comeRoad.points[comeRoad.points.length - 1]; direction = point2d.clone().sub(lastPoint); goLine = point2d.searchLineByFactor(direction, "max", findLine(point2d, lastPoint)); } } if (!goLine) return; goLine.searchTime++; connectedLines.includes(goLine) || connectedLines.push(goLine); var nextPoint = goLine.points.find(point => point2d != point); //if( comeRoad && comeRoad.points[comeRoad.points.length - 1] == nextPoint ) return;//不能查找来时的方向(反方向) //走不通就原路返回 var roadPoints = comeRoad ? comeRoad.points.concat([point2d]) : [point2d]; //每个分叉都能构成一条新的road var roadLines = comeRoad ? comeRoad.lines.concat([goLine]) : [goLine]; //走到第一个点就算停止,这时候可能得到一个环、或者一段走了两遍的线、或者一条线上带了些环。 if (nextPoint == roadPoints[0]) return addRingJudge(roadPoints, roadLines, connectedLines, type); //形成环 else { /* var len = roadPoints.indexOf(nextPoint); if( len > -1){ //走到走过的路的某一点 构成这段路的回路 var points = roadPoints.slice(len, roadPoints.length); var lines = roadLines.slice(len, roadPoints.length); addRingJudge(points, lines) }else{ */ return _search(nextPoint, { lines: roadLines, points: roadPoints }, type, connectedLines); //继续寻路 //} } }; while (1) { //搜寻一次大环 var connectedLines = []; //被搜寻过的且searchTime<2的线。一旦全部搜完就说明该连通区域搜寻完毕,继续查下一个连通区域。 var startPoint = null; points.forEach(point => { //找出x最小的点 if (!point.lines.find(line => line.searchTime < 2)) return; if (!startPoint) startPoint = point;else if (point.x < startPoint.x) startPoint = point; }); if (!startPoint) break; //说明全部找完 var ring = _search(startPoint, null, "bigLeft", connectedLines); //逆时针 //search(startPoint, null, "bigRight", connectedLines);//顺时针(为了防止最外层不是回路之前写了顺时针,但如果是回路就会走重复。后来发现只要逆时针即可,因为走完后剩下的可以再次找大环) connectedLines = connectedLines.filter(line => line.searchTime < 2); var _loop = function _loop() { //目标是顺着connectedLines把所有连通的小环都找到 var points_ = []; //connectedLines中所有的点 connectedLines.forEach(line => line.points.forEach(point => { if (!points_.includes(point)) points_.push(point); })); startPoint = null; points_.forEach(point => { //找出x最小的点 if (!point.lines.find(line => line.searchTime < 2)) return; if (!startPoint) startPoint = point;else if (point.x < startPoint.x) startPoint = point; }); if (!startPoint) return 1; // break _search(startPoint, null, "small", connectedLines); connectedLines = connectedLines.filter(line => line.searchTime < 2); }, startPoint; while (connectedLines.length > 0) { if (_loop()) break; } } /* if(o.onlyGetOutRing){ rings = rings.filter(e=>e.isOutRing) } */ //console.log("searchTime "+searchTime + ", addRingJudgeCount " +addRingJudgeCount) //找出所有的相邻关系,包括公共边 var len = rings.length; for (var i = 0; i < len; i++) { var ring1 = rings[i]; for (var j = i + 1; j < len; j++) { var ring2 = rings[j]; var bothHasLines = getMixedSet(ring1.lines, ring2.lines); if (bothHasLines.length) { //ring1oíring2?àáú ring1.smallNeibours.push(ring2); ring2.smallNeibours.push(ring1); } else {} } } rings.forEach(ring1 => { for (var _i = 0; _i < len; _i++) { var ring2 = rings[_i]; if (ring1 == ring2 || ring1.smallNeibours.includes(ring2)) continue; var inside = void 0; for (var u = 0; u < ring1.points.length; u++) { inside = math.isPointInArea(ring2.points, null, ring1.points[u]); if (!inside) break;else if (inside && !inside.atLine) { break; } } if (inside) { //只要其中一个点在ring2内,就说明ring1是内环 if (inside.atLine) { //(还是会存在点全在线上的情况,这时候判断中心点) var center = math.getCenterOfGravityPoint(ring1.points); var inside1 = math.isPointInArea(ring2.points, null, center); if (!inside1) { continue; } } ring2.child.push(ring1); ring1.parent.push(ring2); } } }); //去除非最小的ring 是否应该检测parent child? /* like this: |———————————————————————| |———|———————|———————| | | | | | | | |———————|———————| | |———————————————————————| */ var wiseRings = rings.filter(r => !r.isClockwise); //一般都是逆时针得到的,如果是顺时针,可能是贪吃蛇的情况,可能不是最小环,需要去掉。 if (wiseRings.length > 0) { //console.log('%c存在非最小的ring! 进行处理:',"color:#00f"); wiseRings.forEach(ring => { //(此案例验证出smallNeibours就是它的最小构成,可以再看看别的案例) if (ring.smallNeibours.length > 0) { //另:如果内部只有一个,说明它是最小环,不需要处理 var is = false; var difference = getDifferenceSet(ring.lines, getDifferenceSetMuti(ring.smallNeibours.concat(ring.child).map(ring => ring.lines))); //获取所有smallNeibours和child的边中没有重复过的边(就是outline) 和该ring的线比较 is = difference.every(line => ring.child.find(r => r.lines.includes(line))); //多出的线只能是child中的线 if (is) { console.log('%c删除非最小环 ring' + ring.id, "color:#00f"); console.log(ring); rings.splice(rings.indexOf(ring), 1); ring.child.forEach(c => { var index = c.parent.indexOf(ring); index > -1 && c.parent.splice(index, 1); }); ring.parent.forEach(c => { var index = c.child.indexOf(ring); index > -1 && c.child.splice(index, 1); }); ring.smallNeibours.forEach(c => { var index = c.smallNeibours.indexOf(ring); index > -1 && c.smallNeibours.splice(index, 1); }); } } }); } /* rings = rings.filter(ring=>{ rings = rings.filter(ring=>{ var enoughSize = ring.area > 0.5 if(!enoughSize){console.log('因面积过小去除ring '+ring.id + " , area: "+ring.area)} return enoughSize }) rings.forEach(ring=>{ if(ring.closetChilds){ ring.closetChilds = ring.closetChilds.filter(e=>rings.includes(e)) } }) return rings }) */ //在dealRings前不能随意删除rings,因为判断是否是最小环时需要全部的环 rings.forEach(ring => { //这里和cad中的不太一样, cad中双数个parent算外环,单数内环; 这里不分内外, 只看有无parent child if (ring.parent.length) { ring.closetParent = ring.parent.find(ring_ => ring_.parent.length == ring.parent.length - 1); //最近一层的大环就是比它的parent个数少一的 ring.closetParent.closetChilds || (ring.closetParent.closetChilds = []); //内环可能多个 ring.closetParent.closetChilds.push(ring); } }); //console.log(rings) var _ring = rings.map(ring => { var data = { id: ring.id, points: ring.points.map(point => { return { id: point.ids[0], x: point.x, y: point.y }; }), /* doors : o.doors.filter(door=>{ if(ring.closetChilds){ var childOutLines = getDifferenceSetMuti(ring.closetChilds.map(ring=>ring.lines)) //最近子环的外边 return ring.lines.concat(childOutLines).includes(door.atLine) }else{ return ring.lines.includes(door.atLine) } }), */ area: ring.area, closetParent: ring.closetParent && ring.closetParent.id, closetChilds: ring.closetChilds && ring.closetChilds.map(e => e.id) }; return data; }); //console.log(JSON.stringify(_ring)) return _ring; }; var easing = {}; //渐变曲线函数,反应加速度的变化 //currentTime:x轴当前时间(从0-到duration), startY:起始点, duration:总时长, wholeY:路程 (即endY-startY) //参数基本是 x, 0, 1, 1 /* easeOut 基本是y= m * (x-dur)^k + n, 若k为偶数,m<0, 若k为奇数,m>0; (因为偶数的话必须开口向下才能获得斜率递减的递增的那段,而奇数是对称的,单调递增. ) 根据x=0时y=0, x=dur时y=S , 得 n = S,m = -S/(-dur)^k */ easing.getEaseOut = function (k) { // k 是>=2的整数. 越大变化率越大, 相同初始速度所需要时间越久 var easeFun; k = Math.round(k); if (k < 2) { k = Math.PI / 2; easeFun = easing.easeOutSine; } else { easeFun = function easeFun(currentTime, startY, wholeY, duration) { if (k > 2) { console.log(k); } return -wholeY / Math.pow(-duration, k) * Math.pow(currentTime - duration, k) + wholeY; }; } return { k, easeFun }; }; easing.linearTween = function (currentTime, startY, wholeY, duration) { return wholeY * currentTime / duration + startY; }, easing.easeInQuad = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, wholeY * currentTime * currentTime + startY; }, easing.easeOutQuad = function (currentTime, startY, wholeY, duration) { // 如套上实际的距离S和时长dur, y = - S / dur *(x^2-2x) 当s为1,dur为1时,是 y = -(x-1)^2 + 1 , 在0-1中是斜率递减的递增函数. 导数- S / dur *(2x-2 ) 可求出实时速度 故在0这一时刻,速度为 2S/dur return currentTime /= duration, -wholeY * currentTime * (currentTime - 2) + startY; }, easing.easeInOutQuad = function (currentTime, startY, wholeY, duration) { return currentTime /= duration / 2, currentTime < 1 ? wholeY / 2 * currentTime * currentTime + startY : (currentTime--, -wholeY / 2 * (currentTime * (currentTime - 2) - 1) + startY); }, easing.easeInCubic = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, wholeY * currentTime * currentTime * currentTime + startY; }, easing.easeOutCubic = function (currentTime, startY, wholeY, duration) { // y = S / dur^3 *(x-dur)^3 + S,对称中心是(dur,S),从0-dur是 斜率递减的递增函数,导数为3S/dur^3 * (x-dur)^2, 0时速度为3S/dur return currentTime /= duration, currentTime--, wholeY * (currentTime * currentTime * currentTime + 1) + startY; }, easing.easeInOutCubic = function (currentTime, startY, wholeY, duration) { return currentTime /= duration / 2, currentTime < 1 ? wholeY / 2 * currentTime * currentTime * currentTime + startY : (currentTime -= 2, wholeY / 2 * (currentTime * currentTime * currentTime + 2) + startY); }, easing.easeInQuart = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, wholeY * currentTime * currentTime * currentTime * currentTime + startY; }, easing.easeOutQuart = function (currentTime, startY, wholeY, duration) { //根据上面的计算,估计0时速度应该是4S/dur吧…… return currentTime /= duration, currentTime--, -wholeY * (currentTime * currentTime * currentTime * currentTime - 1) + startY; }, easing.easeInOutQuart = function (currentTime, startY, wholeY, duration) { return currentTime /= duration / 2, currentTime < 1 ? wholeY / 2 * currentTime * currentTime * currentTime * currentTime + startY : (currentTime -= 2, -wholeY / 2 * (currentTime * currentTime * currentTime * currentTime - 2) + startY); }, easing.easeInQuint = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, wholeY * currentTime * currentTime * currentTime * currentTime * currentTime + startY; }, easing.easeOutQuint = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, currentTime--, wholeY * (currentTime * currentTime * currentTime * currentTime * currentTime + 1) + startY; }, easing.easeInOutQuint = function (currentTime, startY, wholeY, duration) { return currentTime /= duration / 2, currentTime < 1 ? wholeY / 2 * currentTime * currentTime * currentTime * currentTime * currentTime + startY : (currentTime -= 2, wholeY / 2 * (currentTime * currentTime * currentTime * currentTime * currentTime + 2) + startY); }, easing.easeInSine = function (currentTime, startY, wholeY, duration) { return -wholeY * Math.cos(currentTime / duration * (Math.PI / 2)) + wholeY + startY; }, easing.easeOutSine = function (currentTime, startY, wholeY, duration) { // y' = S * PI / 2 / dur * cos(PI/2/dur * x) return wholeY * Math.sin(currentTime / duration * (Math.PI / 2)) + startY; }, easing.easeInOutSine = function (currentTime, startY, wholeY, duration) { return -wholeY / 2 * (Math.cos(Math.PI * currentTime / duration) - 1) + startY; }, easing.easeInExpo = function (currentTime, startY, wholeY, duration) { return wholeY * Math.pow(2, 10 * (currentTime / duration - 1)) + startY; }, easing.easeOutExpo = function (currentTime, startY, wholeY, duration) { return wholeY * (-Math.pow(2, -10 * currentTime / duration) + 1) + startY; }, easing.easeInOutExpo = function (currentTime, startY, wholeY, duration) { return currentTime /= duration / 2, currentTime < 1 ? wholeY / 2 * Math.pow(2, 10 * (currentTime - 1)) + startY : (currentTime--, wholeY / 2 * (-Math.pow(2, -10 * currentTime) + 2) + startY); }, easing.easeInCirc = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, -wholeY * (Math.sqrt(1 - currentTime * currentTime) - 1) + startY; }, easing.easeOutCirc = function (currentTime, startY, wholeY, duration) { return currentTime /= duration, currentTime--, wholeY * Math.sqrt(1 - currentTime * currentTime) + startY; }, easing.easeInOutCirc = function (currentTime, startY, wholeY, duration) { return currentTime /= duration / 2, currentTime < 1 ? -wholeY / 2 * (Math.sqrt(1 - currentTime * currentTime) - 1) + startY : (currentTime -= 2, wholeY / 2 * (Math.sqrt(1 - currentTime * currentTime) + 1) + startY); }, easing.easeInElastic = function (currentTime, startY, wholeY, duration) { var r = 1.70158, o = 0, a = wholeY; return 0 === currentTime ? startY : 1 === (currentTime /= duration) ? startY + wholeY : (o || (o = .3 * duration), a < Math.abs(wholeY) ? (a = wholeY, r = o / 4) : r = o / (2 * Math.PI) * Math.asin(wholeY / a), -(a * Math.pow(2, 10 * (currentTime -= 1)) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o)) + startY); }, easing.easeOutElastic = function (currentTime, startY, wholeY, duration) { var r = 1.70158, o = 0, a = wholeY; return 0 === currentTime ? startY : 1 === (currentTime /= duration) ? startY + wholeY : (o || (o = .3 * duration), a < Math.abs(wholeY) ? (a = wholeY, r = o / 4) : r = o / (2 * Math.PI) * Math.asin(wholeY / a), a * Math.pow(2, -10 * currentTime) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o) + wholeY + startY); }, easing.easeInOutElastic = function (currentTime, startY, wholeY, duration) { var r = 1.70158, o = 0, a = wholeY; return 0 === currentTime ? startY : 2 === (currentTime /= duration / 2) ? startY + wholeY : (o || (o = duration * (.3 * 1.5)), a < Math.abs(wholeY) ? (a = wholeY, r = o / 4) : r = o / (2 * Math.PI) * Math.asin(wholeY / a), currentTime < 1 ? -.5 * (a * Math.pow(2, 10 * (currentTime -= 1)) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o)) + startY : a * Math.pow(2, -10 * (currentTime -= 1)) * Math.sin((currentTime * duration - r) * (2 * Math.PI) / o) * .5 + wholeY + startY); }, easing.easeInBack = function (currentTime, startY, wholeY, duration, r) { return void 0 === r && (r = 1.70158), wholeY * (currentTime /= duration) * currentTime * ((r + 1) * currentTime - r) + startY; }, easing.easeOutBack = function (currentTime, startY, wholeY, duration, r) { return void 0 === r && (r = 1.70158), wholeY * ((currentTime = currentTime / duration - 1) * currentTime * ((r + 1) * currentTime + r) + 1) + startY; }, easing.easeInOutBack = function (currentTime, startY, wholeY, duration, r) { return void 0 === r && (r = 1.70158), (currentTime /= duration / 2) < 1 ? wholeY / 2 * (currentTime * currentTime * (((r *= 1.525) + 1) * currentTime - r)) + startY : wholeY / 2 * ((currentTime -= 2) * currentTime * (((r *= 1.525) + 1) * currentTime + r) + 2) + startY; }, easing.easeOutBounce = function (currentTime, startY, wholeY, duration) { return (currentTime /= duration) < 1 / 2.75 ? wholeY * (7.5625 * currentTime * currentTime) + startY : currentTime < 2 / 2.75 ? wholeY * (7.5625 * (currentTime -= 1.5 / 2.75) * currentTime + .75) + startY : currentTime < 2.5 / 2.75 ? wholeY * (7.5625 * (currentTime -= 2.25 / 2.75) * currentTime + .9375) + startY : wholeY * (7.5625 * (currentTime -= 2.625 / 2.75) * currentTime + .984375) + startY; }, easing.easeInBounce = function (currentTime, startY, wholeY, r) { return wholeY - easing.easeOutBounce(r - currentTime, 0, wholeY, r) + startY; }, easing.easeInOutBounce = function (currentTime, startY, wholeY, r) { return currentTime < r / 2 ? .5 * easing.easeInBounce(2 * currentTime, 0, wholeY, r) + startY : .5 * easing.easeOutBounce(x, 2 * currentTime - r, 0, wholeY, r) + .5 * wholeY + startY; }; var lerp = { /* vector: function(currentTime, startY, f) {//xzw change, add f var wholeY = currentTime.clone(); return startY = startY.clone(), function(duration) { currentTime.set(wholeY.x * (1 - duration) + startY.x * duration, wholeY.y * (1 - duration) + startY.y * duration, wholeY.z * (1 - duration) + startY.z * duration) f && f(currentTime,duration); } }, quaternion: function(currentTime, startY, f) {//xzw change, add f var wholeY = currentTime.clone(); return function(duration) { currentTime.copy(wholeY).slerp(startY, duration); f && f(currentTime,duration); } }, property: function(currentTime, startY, wholeY, duration) { var r = currentTime[startY]; return function(o) { currentTime[startY] = r * (1 - o) + wholeY * o, duration && duration(currentTime[startY]) } }, uniform: function(currentTime, startY, wholeY) { var duration = currentTime.material.uniforms[startY].value; return function(r) { try{ currentTime.material.uniforms[startY] && (currentTime.material.uniforms[startY].value = duration * (1 - r) + wholeY * r) }catch(currentTime){ console.log(1) } } }, matrix4: function(currentTime, startY) { var wholeY = currentTime.clone(); return function(duration) { for (var r = currentTime.elements, o = wholeY.elements, a = startY.elements, s = 0; s < 16; s++) r[s] = o[s] * (1 - duration) + a[s] * duration } }, allUniforms: function(currentTime, startY, wholeY) { var duration = currentTime.map(function(currentTime) { return this.uniform(currentTime, startY, wholeY) } .bind(this)); return function(currentTime) { duration.forEach(function(startY) { startY(currentTime) }) } } */ vector: function vector(t, i, f) { //xzw change, add f var n = t.clone(); return i = i.clone(), function (e, delta) { t.set(n.x * (1 - e) + i.x * e, n.y * (1 - e) + i.y * e, n.z * (1 - e) + i.z * e); f && f(t, e, delta); }; }, quaternion: function quaternion(t, i, f) { //xzw change, add f var n = t.clone(); return function (e) { t.copy(n).slerp(i, e); f && f(t, e); }; }, property: function property(t, i, n, r) { var o = t[i]; return function (e) { t[i] = o * (1 - e) + n * e, r && r(t[i]); }; }, uniform: function uniform(t, i, n) { var r = t.material.uniforms[i].value; return function (e) { t.material.uniforms[i] && (t.material.uniforms[i].value = r * (1 - e) + n * e); }; }, matrix4: function matrix4(o, a) { var s = o.clone(); return function (e) { for (var t = o.elements, i = s.elements, n = a.elements, r = 0; r < 16; r++) t[r] = i[r] * (1 - e) + n[r] * e; }; }, allUniforms: function allUniforms(e, t, i) { var n = e.map(function (e) { return this.uniform(e, t, i); }.bind(this)); return function (t) { n.forEach(function (e) { e(t); }); }; } }; /* 渐变 */ var transitions = { globalDone: null, funcs: [], counter: 0, uniqueID: 0, start: function start(func, duration, done, delay, ease, name, id, cancelFun) { var ignoreFirstFrame = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : true; return delay = delay || 0, this.funcs.push({ func: func, current: -delay * Math.abs(duration), //当前时间 duration: (1 - Math.max(delay, 0)) * Math.abs(duration), //总时长 done: done, easing: ease || easing.linearTween, //渐变曲线 cycling: duration < 0, running: !0, debug: delay < 0, name: name || "T" + this.counter, id: void 0 === id ? this.counter : id, paused: !1, cancelFun: cancelFun, //取消时执行的函数 updateCount: 0, ignoreFirstFrame }), func(0, 16), this.counter += 1, func; }, trigger: function trigger(e) { var t = void 0 === e.delayRatio ? 0 : e.delayRatio, u = e.func || function () {}, r = void 0 === e.duration ? 0 : e.duration; void 0 !== e.cycling && e.cycling && (r = -Math.abs(r)); var o = e.done || null, a = e.easing || easing.linearTween, s = e.name || "R" + this.counter, l = void 0 === e.id ? this.counter : e.id; return this.start(u, r, o, t, a, s, l); }, setTimeout: function setTimeout(e, t, u) { var duration = void 0 === u ? this.counter : u; return this.trigger({ done: e, duration: void 0 === t ? 0 : t, name: "O" + this.counter, id: duration }); }, pause: function pause() { this.paused = !0; }, resume: function resume() { this.paused = !1; }, update: function update(e) { this.funcs.forEach(function (t) { if (t.updateCount++ == 0 && t.ignoreFirstFrame) return; //add start可能发生在一帧中任意时刻,而每次update的是在一帧中的固定时刻,所以从start到第一次update的时间并不是所传入的delta,该delta 是上一帧的update到这一帧的update的耗时。 故去掉了第一次的update,相当于延迟一帧再update. if (!(t.paused || (t.current += 1e3 * e, t.current < 0))) { if (t.current >= t.duration && !t.cycling) { var u = t.easing(1, 0, 1, 1); t.func(u, 1e3 * e), t.done && t.done(), t.running = !1; } else { var duration = t.easing(t.current % t.duration / t.duration, 0, 1, 1), r = t.func(duration, 1e3 * e) || !1; r && (t.done && t.done(), t.running = !1); } } }); var t = this.funcs.length; this.funcs = this.funcs.filter(function (e) { return e.running; }); var u = this.funcs.length; if (t > 0 && 0 === u && this.globalDone) { var duration = this.globalDone; this.globalDone = null, duration(); } }, adjustSpeed: function adjustSpeed(e, t) { for (var u = this.getById(e), n = 0; n < u.length; n++) { var r = u[n]; r.duration /= t, r.current /= t; } }, getById: function getById(e) { return this.funcs.filter(function (t) { return e === t.id; }); }, get: function get(e) { for (var t = 0; t < this.funcs.length; t += 1) if (this.funcs[t].func === e) return this.funcs[t]; return null; }, isRunning: function isRunning(e) { var t = this.get(e); return null !== t && t.running; }, countActive: function countActive() { for (var e = 0, t = 0; t < this.funcs.length; t += 1) e += this.funcs[t].running; return e; }, listActive: function listActive() { for (var e = [], t = 0; t < this.funcs.length; t += 1) this.funcs[t].running && e.push(this.funcs[t].name); return e; }, done: function done(e) { this.globalDone = e; }, cancelById: function cancelById(e, dealCancelFun) { //xzw add dealDone var t = void 0 === e ? 0 : e; var cancels = []; this.funcs = this.funcs.filter(function (e) { var is = e.id == t; if (is && dealCancelFun) { e.cancelFun && cancels.push(e.cancelFun); } /* else if(is && e.cancelFun){ console.warn('cancelById', e.id,e.name) } */ return !is; }); cancels.forEach(e => { e(); }); //先从funcs中去除后再执行 }, cancel: function cancel(e) { //console.warn('cancel', e ) this.funcs = this.funcs.filter(function (t) { return t.func !== e; }); }, getUniqueId: function getUniqueId() { return this.uniqueID -= 1, this.uniqueID; } }; var UPNG$1 = function () { var _bin = { nextZero: function nextZero(data, p) { while (data[p] != 0) p++; return p; }, readUshort: function readUshort(buff, p) { return buff[p] << 8 | buff[p + 1]; }, writeUshort: function writeUshort(buff, p, n) { buff[p] = n >> 8 & 255; buff[p + 1] = n & 255; }, readUint: function readUint(buff, p) { return buff[p] * (256 * 256 * 256) + (buff[p + 1] << 16 | buff[p + 2] << 8 | buff[p + 3]); }, writeUint: function writeUint(buff, p, n) { buff[p] = n >> 24 & 255; buff[p + 1] = n >> 16 & 255; buff[p + 2] = n >> 8 & 255; buff[p + 3] = n & 255; }, readASCII: function readASCII(buff, p, l) { var s = ""; for (var i = 0; i < l; i++) s += String.fromCharCode(buff[p + i]); return s; }, writeASCII: function writeASCII(data, p, s) { for (var i = 0; i < s.length; i++) data[p + i] = s.charCodeAt(i); }, readBytes: function readBytes(buff, p, l) { var arr = []; for (var i = 0; i < l; i++) arr.push(buff[p + i]); return arr; }, pad: function pad(n) { return n.length < 2 ? "0" + n : n; }, readUTF8: function readUTF8(buff, p, l) { var s = "", ns; for (var i = 0; i < l; i++) s += "%" + _bin.pad(buff[p + i].toString(16)); try { ns = decodeURIComponent(s); } catch (e) { return _bin.readASCII(buff, p, l); } return ns; } }; function toRGBA8(out) { var w = out.width, h = out.height; if (out.tabs.acTL == null) return [decodeImage(out.data, w, h, out).buffer]; var frms = []; if (out.frames[0].data == null) out.frames[0].data = out.data; var len = w * h * 4, img = new Uint8Array(len), empty = new Uint8Array(len), prev = new Uint8Array(len); for (var i = 0; i < out.frames.length; i++) { var frm = out.frames[i]; var fx = frm.rect.x, fy = frm.rect.y, fw = frm.rect.width, fh = frm.rect.height; var fdata = decodeImage(frm.data, fw, fh, out); if (i != 0) for (var j = 0; j < len; j++) prev[j] = img[j]; if (frm.blend == 0) _copyTile(fdata, fw, fh, img, w, h, fx, fy, 0);else if (frm.blend == 1) _copyTile(fdata, fw, fh, img, w, h, fx, fy, 1); frms.push(img.buffer.slice(0)); if (frm.dispose == 0) {} else if (frm.dispose == 1) _copyTile(empty, fw, fh, img, w, h, fx, fy, 0);else if (frm.dispose == 2) for (var j = 0; j < len; j++) img[j] = prev[j]; } return frms; } function decodeImage(data, w, h, out) { var area = w * h, bpp = _getBPP(out); var bpl = Math.ceil(w * bpp / 8); // bytes per line var bf = new Uint8Array(area * 4), bf32 = new Uint32Array(bf.buffer); var ctype = out.ctype, depth = out.depth; var rs = _bin.readUshort; //console.log(ctype, depth); var time = Date.now(); if (ctype == 6) { // RGB + alpha var qarea = area << 2; if (depth == 8) for (var i = 0; i < qarea; i += 4) { bf[i] = data[i]; bf[i + 1] = data[i + 1]; bf[i + 2] = data[i + 2]; bf[i + 3] = data[i + 3]; } if (depth == 16) for (var i = 0; i < qarea; i++) { bf[i] = data[i << 1]; } } else if (ctype == 2) { // RGB var ts = out.tabs["tRNS"]; if (ts == null) { if (depth == 8) for (var i = 0; i < area; i++) { var ti = i * 3; bf32[i] = 255 << 24 | data[ti + 2] << 16 | data[ti + 1] << 8 | data[ti]; } if (depth == 16) for (var i = 0; i < area; i++) { var ti = i * 6; bf32[i] = 255 << 24 | data[ti + 4] << 16 | data[ti + 2] << 8 | data[ti]; } } else { var tr = ts[0], tg = ts[1], tb = ts[2]; if (depth == 8) for (var i = 0; i < area; i++) { var qi = i << 2, ti = i * 3; bf32[i] = 255 << 24 | data[ti + 2] << 16 | data[ti + 1] << 8 | data[ti]; if (data[ti] == tr && data[ti + 1] == tg && data[ti + 2] == tb) bf[qi + 3] = 0; } if (depth == 16) for (var i = 0; i < area; i++) { var qi = i << 2, ti = i * 6; bf32[i] = 255 << 24 | data[ti + 4] << 16 | data[ti + 2] << 8 | data[ti]; if (rs(data, ti) == tr && rs(data, ti + 2) == tg && rs(data, ti + 4) == tb) bf[qi + 3] = 0; } } } else if (ctype == 3) { // palette var p = out.tabs["PLTE"], ap = out.tabs["tRNS"], tl = ap ? ap.length : 0; //console.log(p, ap); if (depth == 1) for (var y = 0; y < h; y++) { var s0 = y * bpl, t0 = y * w; for (var i = 0; i < w; i++) { var qi = t0 + i << 2, j = data[s0 + (i >> 3)] >> 7 - ((i & 7) << 0) & 1, cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } if (depth == 2) for (var y = 0; y < h; y++) { var s0 = y * bpl, t0 = y * w; for (var i = 0; i < w; i++) { var qi = t0 + i << 2, j = data[s0 + (i >> 2)] >> 6 - ((i & 3) << 1) & 3, cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } if (depth == 4) for (var y = 0; y < h; y++) { var s0 = y * bpl, t0 = y * w; for (var i = 0; i < w; i++) { var qi = t0 + i << 2, j = data[s0 + (i >> 1)] >> 4 - ((i & 1) << 2) & 15, cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } if (depth == 8) for (var i = 0; i < area; i++) { var qi = i << 2, j = data[i], cj = 3 * j; bf[qi] = p[cj]; bf[qi + 1] = p[cj + 1]; bf[qi + 2] = p[cj + 2]; bf[qi + 3] = j < tl ? ap[j] : 255; } } else if (ctype == 4) { // gray + alpha if (depth == 8) for (var i = 0; i < area; i++) { var qi = i << 2, di = i << 1, gr = data[di]; bf[qi] = gr; bf[qi + 1] = gr; bf[qi + 2] = gr; bf[qi + 3] = data[di + 1]; } if (depth == 16) for (var i = 0; i < area; i++) { var qi = i << 2, di = i << 2, gr = data[di]; bf[qi] = gr; bf[qi + 1] = gr; bf[qi + 2] = gr; bf[qi + 3] = data[di + 2]; } } else if (ctype == 0) { // gray var tr = out.tabs["tRNS"] ? out.tabs["tRNS"] : -1; for (var y = 0; y < h; y++) { var off = y * bpl, to = y * w; if (depth == 1) for (var x = 0; x < w; x++) { var gr = 255 * (data[off + (x >>> 3)] >>> 7 - (x & 7) & 1), al = gr == tr * 255 ? 0 : 255; bf32[to + x] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 2) for (var x = 0; x < w; x++) { var gr = 85 * (data[off + (x >>> 2)] >>> 6 - ((x & 3) << 1) & 3), al = gr == tr * 85 ? 0 : 255; bf32[to + x] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 4) for (var x = 0; x < w; x++) { var gr = 17 * (data[off + (x >>> 1)] >>> 4 - ((x & 1) << 2) & 15), al = gr == tr * 17 ? 0 : 255; bf32[to + x] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 8) for (var x = 0; x < w; x++) { var gr = data[off + x], al = gr == tr ? 0 : 255; bf32[to + x] = al << 24 | gr << 16 | gr << 8 | gr; } else if (depth == 16) for (var x = 0; x < w; x++) { var gr = data[off + (x << 1)], al = rs(data, off + (x << 1)) == tr ? 0 : 255; bf32[to + x] = al << 24 | gr << 16 | gr << 8 | gr; } } } //console.log(Date.now()-time); return bf; } function decode(buff) { var data = new Uint8Array(buff), offset = 8, bin = _bin, rUs = bin.readUshort, rUi = bin.readUint; var out = { tabs: {}, frames: [] }; var dd = new Uint8Array(data.length), doff = 0; // put all IDAT data into it var fd, foff = 0; // frames var mgck = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]; for (var i = 0; i < 8; i++) if (data[i] != mgck[i]) throw "The input is not a PNG file!"; while (offset < data.length) { var len = bin.readUint(data, offset); offset += 4; var type = bin.readASCII(data, offset, 4); offset += 4; //console.log(type,len); if (type == "IHDR") { _IHDR(data, offset, out); } else if (type == "iCCP") { var off = offset; while (data[off] != 0) off++; var nam = bin.readASCII(data, offset, off - offset); var cpr = data[off + 1]; var fil = data.slice(off + 2, offset + len); var res = null; try { res = _inflate(fil); } catch (e) { res = inflateRaw(fil); } out.tabs[type] = res; } else if (type == "CgBI") { out.tabs[type] = data.slice(offset, offset + 4); } else if (type == "IDAT") { for (var i = 0; i < len; i++) dd[doff + i] = data[offset + i]; doff += len; } else if (type == "acTL") { out.tabs[type] = { num_frames: rUi(data, offset), num_plays: rUi(data, offset + 4) }; fd = new Uint8Array(data.length); } else if (type == "fcTL") { if (foff != 0) { var fr = out.frames[out.frames.length - 1]; fr.data = _decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect.height); foff = 0; } var rct = { x: rUi(data, offset + 12), y: rUi(data, offset + 16), width: rUi(data, offset + 4), height: rUi(data, offset + 8) }; var del = rUs(data, offset + 22); del = rUs(data, offset + 20) / (del == 0 ? 100 : del); var frm = { rect: rct, delay: Math.round(del * 1000), dispose: data[offset + 24], blend: data[offset + 25] }; //console.log(frm); out.frames.push(frm); } else if (type == "fdAT") { for (var i = 0; i < len - 4; i++) fd[foff + i] = data[offset + i + 4]; foff += len - 4; } else if (type == "pHYs") { out.tabs[type] = [bin.readUint(data, offset), bin.readUint(data, offset + 4), data[offset + 8]]; } else if (type == "cHRM") { out.tabs[type] = []; for (var i = 0; i < 8; i++) out.tabs[type].push(bin.readUint(data, offset + i * 4)); } else if (type == "tEXt" || type == "zTXt") { if (out.tabs[type] == null) out.tabs[type] = {}; var nz = bin.nextZero(data, offset); var keyw = bin.readASCII(data, offset, nz - offset); var text, tl = offset + len - nz - 1; if (type == "tEXt") text = bin.readASCII(data, nz + 1, tl);else { var bfr = _inflate(data.slice(nz + 2, nz + 2 + tl)); text = bin.readUTF8(bfr, 0, bfr.length); } out.tabs[type][keyw] = text; } else if (type == "iTXt") { if (out.tabs[type] == null) out.tabs[type] = {}; var nz = 0, off = offset; nz = bin.nextZero(data, off); var keyw = bin.readASCII(data, off, nz - off); off = nz + 1; var cflag = data[off], cmeth = data[off + 1]; off += 2; nz = bin.nextZero(data, off); var ltag = bin.readASCII(data, off, nz - off); off = nz + 1; nz = bin.nextZero(data, off); var tkeyw = bin.readUTF8(data, off, nz - off); off = nz + 1; var text, tl = len - (off - offset); if (cflag == 0) text = bin.readUTF8(data, off, tl);else { var bfr = _inflate(data.slice(off, off + tl)); text = bin.readUTF8(bfr, 0, bfr.length); } out.tabs[type][keyw] = text; } else if (type == "PLTE") { out.tabs[type] = bin.readBytes(data, offset, len); } else if (type == "hIST") { var pl = out.tabs["PLTE"].length / 3; out.tabs[type] = []; for (var i = 0; i < pl; i++) out.tabs[type].push(rUs(data, offset + i * 2)); } else if (type == "tRNS") { if (out.ctype == 3) out.tabs[type] = bin.readBytes(data, offset, len);else if (out.ctype == 0) out.tabs[type] = rUs(data, offset);else if (out.ctype == 2) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)]; //else console.log("tRNS for unsupported color type",out.ctype, len); } else if (type == "gAMA") out.tabs[type] = bin.readUint(data, offset) / 100000;else if (type == "sRGB") out.tabs[type] = data[offset];else if (type == "bKGD") { if (out.ctype == 0 || out.ctype == 4) out.tabs[type] = [rUs(data, offset)];else if (out.ctype == 2 || out.ctype == 6) out.tabs[type] = [rUs(data, offset), rUs(data, offset + 2), rUs(data, offset + 4)];else if (out.ctype == 3) out.tabs[type] = data[offset]; } else if (type == "IEND") { break; } //else { console.log("unknown chunk type", type, len); out.tabs[type]=data.slice(offset,offset+len); } offset += len; var crc = bin.readUint(data, offset); offset += 4; } if (foff != 0) { var fr = out.frames[out.frames.length - 1]; fr.data = _decompress(out, fd.slice(0, foff), fr.rect.width, fr.rect.height); } out.data = _decompress(out, dd, out.width, out.height); delete out.compress; delete out.interlace; delete out.filter; return out; } function _decompress(out, dd, w, h) { var time = Date.now(); var bpp = _getBPP(out), bpl = Math.ceil(w * bpp / 8), buff = new Uint8Array((bpl + 1 + out.interlace) * h); if (out.tabs["CgBI"]) dd = inflateRaw(dd, buff);else dd = _inflate(dd, buff); //console.log(dd.length, buff.length); //console.log(Date.now()-time); var time = Date.now(); if (out.interlace == 0) dd = _filterZero(dd, out, 0, w, h);else if (out.interlace == 1) dd = _readInterlace(dd, out); //console.log(Date.now()-time); return dd; } function _inflate(data, buff) { var out = inflateRaw(new Uint8Array(data.buffer, 2, data.length - 6), buff); return out; } var inflateRaw = function () { var D = function () { var o = Uint16Array, j = Uint32Array; return { m: new o(16), v: new o(16), d: [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15], o: [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, 999, 999, 999], z: [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, 0, 0, 0], B: new o(32), p: [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, 65535, 65535], w: [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, 0, 0], h: new j(32), g: new o(512), s: [], A: new o(32), t: [], k: new o(32768), c: [], a: [], n: new o(32768), e: [], C: new o(512), b: [], i: new o(1 << 15), r: new j(286), f: new j(30), l: new j(19), u: new j(15e3), q: new o(1 << 16), j: new o(1 << 15) }; }(); function C(o, j) { var I = o.length, A, r, i, y, G, f = D.v; for (var y = 0; y <= j; y++) f[y] = 0; for (y = 1; y < I; y += 2) f[o[y]]++; var a = D.m; A = 0; f[0] = 0; for (r = 1; r <= j; r++) { A = A + f[r - 1] << 1; a[r] = A; } for (i = 0; i < I; i += 2) { G = o[i + 1]; if (G != 0) { o[i] = a[G]; a[G]++; } } } function t(o, j, I) { var A = o.length, r = D.i; for (var i = 0; i < A; i += 2) if (o[i + 1] != 0) { var y = i >> 1, G = o[i + 1], f = y << 4 | G, a = j - G, k = o[i] << a, N = k + (1 << a); while (k != N) { var x = r[k] >>> 15 - j; I[x] = f; k++; } } } function g(o, j) { var I = D.i, A = 15 - j; for (var r = 0; r < o.length; r += 2) { var i = o[r] << j - o[r + 1]; o[r] = I[i] >>> A; } } (function () { var o = 1 << 15; for (var j = 0; j < o; j++) { var I = j; I = (I & 2863311530) >>> 1 | (I & 1431655765) << 1; I = (I & 3435973836) >>> 2 | (I & 858993459) << 2; I = (I & 4042322160) >>> 4 | (I & 252645135) << 4; I = (I & 4278255360) >>> 8 | (I & 16711935) << 8; D.i[j] = (I >>> 16 | I << 16) >>> 17; } function A(r, i, y) { while (i-- != 0) r.push(0, y); } for (var j = 0; j < 32; j++) { D.B[j] = D.o[j] << 3 | D.z[j]; D.h[j] = D.p[j] << 4 | D.w[j]; } A(D.s, 144, 8); A(D.s, 255 - 143, 9); A(D.s, 279 - 255, 7); A(D.s, 287 - 279, 8); C(D.s, 9); t(D.s, 9, D.g); g(D.s, 9); A(D.t, 32, 5); C(D.t, 5); t(D.t, 5, D.A); g(D.t, 5); A(D.b, 19, 0); A(D.c, 286, 0); A(D.e, 30, 0); A(D.a, 320, 0); })(); function F(o, j, I) { return (o[j >>> 3] | o[(j >>> 3) + 1] << 8) >>> (j & 7) & (1 << I) - 1; } function s(o, j, I) { return (o[j >>> 3] | o[(j >>> 3) + 1] << 8 | o[(j >>> 3) + 2] << 16) >>> (j & 7) & (1 << I) - 1; } function w(o, j) { return (o[j >>> 3] | o[(j >>> 3) + 1] << 8 | o[(j >>> 3) + 2] << 16) >>> (j & 7); } function b(o, j) { return (o[j >>> 3] | o[(j >>> 3) + 1] << 8 | o[(j >>> 3) + 2] << 16 | o[(j >>> 3) + 3] << 24) >>> (j & 7); } function v(o, j) { var I = Uint8Array, r = 0, i = 0, y = 0, G = 0, f = 0, a = 0, k = 0, N = 0, x = 0, P, J; if (o[0] == 3 && o[1] == 0) return j ? j : new I(0); var A = j == null; if (A) j = new I(o.length >>> 2 << 3); while (r == 0) { r = s(o, x, 1); i = s(o, x + 1, 2); x += 3; if (i == 0) { if ((x & 7) != 0) x += 8 - (x & 7); var K = (x >>> 3) + 4, m = o[K - 4] | o[K - 3] << 8; if (A) j = H(j, N + m); j.set(new I(o.buffer, o.byteOffset + K, m), N); x = K + m << 3; N += m; continue; } if (A) j = H(j, N + (1 << 17)); if (i == 1) { P = D.g; J = D.A; a = (1 << 9) - 1; k = (1 << 5) - 1; } if (i == 2) { y = F(o, x, 5) + 257; G = F(o, x + 5, 5) + 1; f = F(o, x + 10, 4) + 4; x += 14; var O = x, Q = 1; for (var p = 0; p < 38; p += 2) { D.b[p] = 0; D.b[p + 1] = 0; } for (var p = 0; p < f; p++) { var l = F(o, x + p * 3, 3); D.b[(D.d[p] << 1) + 1] = l; if (l > Q) Q = l; } x += 3 * f; C(D.b, Q); t(D.b, Q, D.C); P = D.k; J = D.n; x = B(D.C, (1 << Q) - 1, y + G, o, x, D.a); var u = d(D.a, 0, y, D.c); a = (1 << u) - 1; var n = d(D.a, y, G, D.e); k = (1 << n) - 1; C(D.c, u); t(D.c, u, P); C(D.e, n); t(D.e, n, J); } while (!0) { var h = P[w(o, x) & a]; x += h & 15; var L = h >>> 4; if (L >>> 8 == 0) { j[N++] = L; } else if (L == 256) { break; } else { var M = N + L - 254; if (L > 264) { var z = D.B[L - 257]; M = N + (z >>> 3) + F(o, x, z & 7); x += z & 7; } var e = J[w(o, x) & k]; x += e & 15; var E = e >>> 4, c = D.h[E], q = (c >>> 4) + s(o, x, c & 15); x += c & 15; if (A) j = H(j, N + (1 << 17)); while (N < M) { j[N] = j[N++ - q]; j[N] = j[N++ - q]; j[N] = j[N++ - q]; j[N] = j[N++ - q]; } N = M; } } } return j.length == N ? j : j.slice(0, N); } function H(o, j) { var I = o.length; if (j <= I) return o; var A = new Uint8Array(Math.max(I << 1, j)); A.set(o, 0); return A; } function B(o, j, I, A, r, i) { var y = 0; while (y < I) { var G = o[w(A, r) & j]; r += G & 15; var f = G >>> 4; if (f <= 15) { i[y] = f; y++; } else { var a = 0, k = 0; if (f == 16) { k = 3 + F(A, r, 2); r += 2; a = i[y - 1]; } else if (f == 17) { k = 3 + F(A, r, 3); r += 3; } else if (f == 18) { k = 11 + F(A, r, 7); r += 7; } var N = y + k; while (y < N) { i[y] = a; y++; } } } return r; } function d(o, j, I, A) { var r = 0, i = 0, y = A.length >>> 1; while (i < I) { var G = o[i + j]; A[i << 1] = 0; A[(i << 1) + 1] = G; if (G > r) r = G; i++; } while (i < y) { A[i << 1] = 0; A[(i << 1) + 1] = 0; i++; } return r; } return v; }(); function _readInterlace(data, out) { var w = out.width, h = out.height; var bpp = _getBPP(out), cbpp = bpp >> 3, bpl = Math.ceil(w * bpp / 8); var img = new Uint8Array(h * bpl); var di = 0; var starting_row = [0, 0, 4, 0, 2, 0, 1]; var starting_col = [0, 4, 0, 2, 0, 1, 0]; var row_increment = [8, 8, 8, 4, 4, 2, 2]; var col_increment = [8, 8, 4, 4, 2, 2, 1]; var pass = 0; while (pass < 7) { var ri = row_increment[pass], ci = col_increment[pass]; var sw = 0, sh = 0; var cr = starting_row[pass]; while (cr < h) { cr += ri; sh++; } var cc = starting_col[pass]; while (cc < w) { cc += ci; sw++; } var bpll = Math.ceil(sw * bpp / 8); _filterZero(data, out, di, sw, sh); var y = 0, row = starting_row[pass]; while (row < h) { var col = starting_col[pass]; var cdi = di + y * bpll << 3; while (col < w) { if (bpp == 1) { var val = data[cdi >> 3]; val = val >> 7 - (cdi & 7) & 1; img[row * bpl + (col >> 3)] |= val << 7 - ((col & 7) << 0); } if (bpp == 2) { var val = data[cdi >> 3]; val = val >> 6 - (cdi & 7) & 3; img[row * bpl + (col >> 2)] |= val << 6 - ((col & 3) << 1); } if (bpp == 4) { var val = data[cdi >> 3]; val = val >> 4 - (cdi & 7) & 15; img[row * bpl + (col >> 1)] |= val << 4 - ((col & 1) << 2); } if (bpp >= 8) { var ii = row * bpl + col * cbpp; for (var j = 0; j < cbpp; j++) img[ii + j] = data[(cdi >> 3) + j]; } cdi += bpp; col += ci; } y++; row += ri; } if (sw * sh != 0) di += sh * (1 + bpll); pass = pass + 1; } return img; } function _getBPP(out) { var noc = [1, null, 3, 1, 2, null, 4][out.ctype]; return noc * out.depth; } function _filterZero(data, out, off, w, h) { var bpp = _getBPP(out), bpl = Math.ceil(w * bpp / 8); bpp = Math.ceil(bpp / 8); var i, di, type = data[off], x = 0; if (type > 1) data[off] = [0, 0, 1][type - 2]; if (type == 3) for (x = bpp; x < bpl; x++) data[x + 1] = data[x + 1] + (data[x + 1 - bpp] >>> 1) & 255; for (var y = 0; y < h; y++) { i = off + y * bpl; di = i + y + 1; type = data[di - 1]; x = 0; if (type == 0) for (; x < bpl; x++) data[i + x] = data[di + x];else if (type == 1) { for (; x < bpp; x++) data[i + x] = data[di + x]; for (; x < bpl; x++) data[i + x] = data[di + x] + data[i + x - bpp]; } else if (type == 2) { for (; x < bpl; x++) data[i + x] = data[di + x] + data[i + x - bpl]; } else if (type == 3) { for (; x < bpp; x++) data[i + x] = data[di + x] + (data[i + x - bpl] >>> 1); for (; x < bpl; x++) data[i + x] = data[di + x] + (data[i + x - bpl] + data[i + x - bpp] >>> 1); } else { for (; x < bpp; x++) data[i + x] = data[di + x] + _paeth(0, data[i + x - bpl], 0); for (; x < bpl; x++) data[i + x] = data[di + x] + _paeth(data[i + x - bpp], data[i + x - bpl], data[i + x - bpp - bpl]); } } return data; } function _paeth(a, b, c) { var p = a + b - c, pa = p - a, pb = p - b, pc = p - c; if (pa * pa <= pb * pb && pa * pa <= pc * pc) return a;else if (pb * pb <= pc * pc) return b; return c; } function _IHDR(data, offset, out) { out.width = _bin.readUint(data, offset); offset += 4; out.height = _bin.readUint(data, offset); offset += 4; out.depth = data[offset]; offset++; out.ctype = data[offset]; offset++; out.compress = data[offset]; offset++; out.filter = data[offset]; offset++; out.interlace = data[offset]; offset++; } function _copyTile(sb, sw, sh, tb, tw, th, xoff, yoff, mode) { var w = Math.min(sw, tw), h = Math.min(sh, th); var si = 0, ti = 0; for (var y = 0; y < h; y++) for (var x = 0; x < w; x++) { if (xoff >= 0 && yoff >= 0) { si = y * sw + x << 2; ti = (yoff + y) * tw + xoff + x << 2; } else { si = (-yoff + y) * sw - xoff + x << 2; ti = y * tw + x << 2; } if (mode == 0) { tb[ti] = sb[si]; tb[ti + 1] = sb[si + 1]; tb[ti + 2] = sb[si + 2]; tb[ti + 3] = sb[si + 3]; } else if (mode == 1) { var fa = sb[si + 3] * (1 / 255), fr = sb[si] * fa, fg = sb[si + 1] * fa, fb = sb[si + 2] * fa; var ba = tb[ti + 3] * (1 / 255), br = tb[ti] * ba, bg = tb[ti + 1] * ba, bb = tb[ti + 2] * ba; var ifa = 1 - fa, oa = fa + ba * ifa, ioa = oa == 0 ? 0 : 1 / oa; tb[ti + 3] = 255 * oa; tb[ti + 0] = (fr + br * ifa) * ioa; tb[ti + 1] = (fg + bg * ifa) * ioa; tb[ti + 2] = (fb + bb * ifa) * ioa; } else if (mode == 2) { // copy only differences, otherwise zero var fa = sb[si + 3], fr = sb[si], fg = sb[si + 1], fb = sb[si + 2]; var ba = tb[ti + 3], br = tb[ti], bg = tb[ti + 1], bb = tb[ti + 2]; if (fa == ba && fr == br && fg == bg && fb == bb) { tb[ti] = 0; tb[ti + 1] = 0; tb[ti + 2] = 0; tb[ti + 3] = 0; } else { tb[ti] = fr; tb[ti + 1] = fg; tb[ti + 2] = fb; tb[ti + 3] = fa; } } else if (mode == 3) { // check if can be blended var fa = sb[si + 3], fr = sb[si], fg = sb[si + 1], fb = sb[si + 2]; var ba = tb[ti + 3], br = tb[ti], bg = tb[ti + 1], bb = tb[ti + 2]; if (fa == ba && fr == br && fg == bg && fb == bb) continue; //if(fa!=255 && ba!=0) return false; if (fa < 220 && ba > 20) return false; } } return true; } return { decode: decode, toRGBA8: toRGBA8, _paeth: _paeth, _copyTile: _copyTile, _bin: _bin }; }(); (function () { var _copyTile = UPNG$1._copyTile, _bin = UPNG$1._bin, paeth = UPNG$1._paeth; var crcLib = { table: function () { var tab = new Uint32Array(256); for (var n = 0; n < 256; n++) { var c = n; for (var k = 0; k < 8; k++) { if (c & 1) c = 0xedb88320 ^ c >>> 1;else c = c >>> 1; } tab[n] = c; } return tab; }(), update: function update(c, buf, off, len) { for (var i = 0; i < len; i++) c = crcLib.table[(c ^ buf[off + i]) & 0xff] ^ c >>> 8; return c; }, crc: function crc(b, o, l) { return crcLib.update(0xffffffff, b, o, l) ^ 0xffffffff; } }; function addErr(er, tg, ti, f) { tg[ti] += er[0] * f >> 4; tg[ti + 1] += er[1] * f >> 4; tg[ti + 2] += er[2] * f >> 4; tg[ti + 3] += er[3] * f >> 4; } function N(x) { return Math.max(0, Math.min(255, x)); } function D(a, b) { var dr = a[0] - b[0], dg = a[1] - b[1], db = a[2] - b[2], da = a[3] - b[3]; return dr * dr + dg * dg + db * db + da * da; } // MTD: 0: None, 1: floyd-steinberg, 2: Bayer function dither(sb, w, h, plte, tb, oind, MTD) { if (MTD == null) MTD = 1; var pc = plte.length, nplt = [], rads = []; for (var i = 0; i < pc; i++) { var c = plte[i]; nplt.push([c >>> 0 & 255, c >>> 8 & 255, c >>> 16 & 255, c >>> 24 & 255]); } for (var i = 0; i < pc; i++) { var ne = 0xffffffff, ni = 0; for (var j = 0; j < pc; j++) { var ce = D(nplt[i], nplt[j]); if (j != i && ce < ne) { ne = ce; ni = j; } } var hd = Math.sqrt(ne) / 2; rads[i] = ~~(hd * hd); } var tb32 = new Uint32Array(tb.buffer); var err = new Int16Array(w * h * 4); /* var S=2, M = [ 0,2, 3,1]; //*/ //* var S = 4, M = [0, 8, 2, 10, 12, 4, 14, 6, 3, 11, 1, 9, 15, 7, 13, 5]; //*/ for (var i = 0; i < M.length; i++) M[i] = 255 * (-0.5 + (M[i] + 0.5) / (S * S)); for (var y = 0; y < h; y++) { for (var x = 0; x < w; x++) { var i = (y * w + x) * 4; var cc; if (MTD != 2) cc = [N(sb[i] + err[i]), N(sb[i + 1] + err[i + 1]), N(sb[i + 2] + err[i + 2]), N(sb[i + 3] + err[i + 3])];else { var ce = M[(y & S - 1) * S + (x & S - 1)]; cc = [N(sb[i] + ce), N(sb[i + 1] + ce), N(sb[i + 2] + ce), N(sb[i + 3] + ce)]; } var ni = 0, nd = 0xffffff; for (var j = 0; j < pc; j++) { var cd = D(cc, nplt[j]); if (cd < nd) { nd = cd; ni = j; } } var nc = nplt[ni]; var er = [cc[0] - nc[0], cc[1] - nc[1], cc[2] - nc[2], cc[3] - nc[3]]; if (MTD == 1) { //addErr(er, err, i+4, 16); if (x != w - 1) addErr(er, err, i + 4, 7); if (y != h - 1) { if (x != 0) addErr(er, err, i + 4 * w - 4, 3); addErr(er, err, i + 4 * w, 5); if (x != w - 1) addErr(er, err, i + 4 * w + 4, 1); } //*/ } oind[i >> 2] = ni; tb32[i >> 2] = plte[ni]; } } } function encode(bufs, w, h, ps, dels, tabs, forbidPlte) { if (ps == null) ps = 0; if (forbidPlte == null) forbidPlte = false; var nimg = compress(bufs, w, h, ps, [false, false, false, 0, forbidPlte, false]); compressPNG(nimg, -1); return _main(nimg, w, h, dels, tabs); } function encodeLL(bufs, w, h, cc, ac, depth, dels, tabs) { var nimg = { ctype: 0 + (cc == 1 ? 0 : 2) + (ac == 0 ? 0 : 4), depth: depth, frames: [] }; var time = Date.now(); var bipp = (cc + ac) * depth, bipl = bipp * w; for (var i = 0; i < bufs.length; i++) nimg.frames.push({ rect: { x: 0, y: 0, width: w, height: h }, img: new Uint8Array(bufs[i]), blend: 0, dispose: 1, bpp: Math.ceil(bipp / 8), bpl: Math.ceil(bipl / 8) }); compressPNG(nimg, 0, true); var out = _main(nimg, w, h, dels, tabs); return out; } function _main(nimg, w, h, dels, tabs) { if (tabs == null) tabs = {}; var crc = crcLib.crc, wUi = _bin.writeUint, wUs = _bin.writeUshort, wAs = _bin.writeASCII; var offset = 8, anim = nimg.frames.length > 1, pltAlpha = false; var cicc; var leng = 8 + (16 + 5 + 4) /*+ (9+4)*/ + (anim ? 20 : 0); if (tabs["sRGB"] != null) leng += 8 + 1 + 4; if (tabs["pHYs"] != null) leng += 8 + 9 + 4; if (tabs["iCCP"] != null) { cicc = pako.deflate(tabs["iCCP"]); leng += 8 + 11 + 2 + cicc.length + 4; } if (nimg.ctype == 3) { var dl = nimg.plte.length; for (var i = 0; i < dl; i++) if (nimg.plte[i] >>> 24 != 255) pltAlpha = true; leng += 8 + dl * 3 + 4 + (pltAlpha ? 8 + dl * 1 + 4 : 0); } for (var j = 0; j < nimg.frames.length; j++) { var fr = nimg.frames[j]; if (anim) leng += 38; leng += fr.cimg.length + 12; if (j != 0) leng += 4; } leng += 12; var data = new Uint8Array(leng); var wr = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]; for (var i = 0; i < 8; i++) data[i] = wr[i]; wUi(data, offset, 13); offset += 4; wAs(data, offset, "IHDR"); offset += 4; wUi(data, offset, w); offset += 4; wUi(data, offset, h); offset += 4; data[offset] = nimg.depth; offset++; // depth data[offset] = nimg.ctype; offset++; // ctype data[offset] = 0; offset++; // compress data[offset] = 0; offset++; // filter data[offset] = 0; offset++; // interlace wUi(data, offset, crc(data, offset - 17, 17)); offset += 4; // crc // 13 bytes to say, that it is sRGB if (tabs["sRGB"] != null) { wUi(data, offset, 1); offset += 4; wAs(data, offset, "sRGB"); offset += 4; data[offset] = tabs["sRGB"]; offset++; wUi(data, offset, crc(data, offset - 5, 5)); offset += 4; // crc } if (tabs["iCCP"] != null) { var sl = 11 + 2 + cicc.length; wUi(data, offset, sl); offset += 4; wAs(data, offset, "iCCP"); offset += 4; wAs(data, offset, "ICC profile"); offset += 11; offset += 2; data.set(cicc, offset); offset += cicc.length; wUi(data, offset, crc(data, offset - (sl + 4), sl + 4)); offset += 4; // crc } if (tabs["pHYs"] != null) { wUi(data, offset, 9); offset += 4; wAs(data, offset, "pHYs"); offset += 4; wUi(data, offset, tabs["pHYs"][0]); offset += 4; wUi(data, offset, tabs["pHYs"][1]); offset += 4; data[offset] = tabs["pHYs"][2]; offset++; wUi(data, offset, crc(data, offset - 13, 13)); offset += 4; // crc } if (anim) { wUi(data, offset, 8); offset += 4; wAs(data, offset, "acTL"); offset += 4; wUi(data, offset, nimg.frames.length); offset += 4; wUi(data, offset, tabs["loop"] != null ? tabs["loop"] : 0); offset += 4; wUi(data, offset, crc(data, offset - 12, 12)); offset += 4; // crc } if (nimg.ctype == 3) { var dl = nimg.plte.length; wUi(data, offset, dl * 3); offset += 4; wAs(data, offset, "PLTE"); offset += 4; for (var i = 0; i < dl; i++) { var ti = i * 3, c = nimg.plte[i], r = c & 255, g = c >>> 8 & 255, b = c >>> 16 & 255; data[offset + ti + 0] = r; data[offset + ti + 1] = g; data[offset + ti + 2] = b; } offset += dl * 3; wUi(data, offset, crc(data, offset - dl * 3 - 4, dl * 3 + 4)); offset += 4; // crc if (pltAlpha) { wUi(data, offset, dl); offset += 4; wAs(data, offset, "tRNS"); offset += 4; for (var i = 0; i < dl; i++) data[offset + i] = nimg.plte[i] >>> 24 & 255; offset += dl; wUi(data, offset, crc(data, offset - dl - 4, dl + 4)); offset += 4; // crc } } var fi = 0; for (var j = 0; j < nimg.frames.length; j++) { var fr = nimg.frames[j]; if (anim) { wUi(data, offset, 26); offset += 4; wAs(data, offset, "fcTL"); offset += 4; wUi(data, offset, fi++); offset += 4; wUi(data, offset, fr.rect.width); offset += 4; wUi(data, offset, fr.rect.height); offset += 4; wUi(data, offset, fr.rect.x); offset += 4; wUi(data, offset, fr.rect.y); offset += 4; wUs(data, offset, dels[j]); offset += 2; wUs(data, offset, 1000); offset += 2; data[offset] = fr.dispose; offset++; // dispose data[offset] = fr.blend; offset++; // blend wUi(data, offset, crc(data, offset - 30, 30)); offset += 4; // crc } var imgd = fr.cimg, dl = imgd.length; wUi(data, offset, dl + (j == 0 ? 0 : 4)); offset += 4; var ioff = offset; wAs(data, offset, j == 0 ? "IDAT" : "fdAT"); offset += 4; if (j != 0) { wUi(data, offset, fi++); offset += 4; } data.set(imgd, offset); offset += dl; wUi(data, offset, crc(data, ioff, offset - ioff)); offset += 4; // crc } wUi(data, offset, 0); offset += 4; wAs(data, offset, "IEND"); offset += 4; wUi(data, offset, crc(data, offset - 4, 4)); offset += 4; // crc return data.buffer; } function compressPNG(out, filter, levelZero) { for (var i = 0; i < out.frames.length; i++) { var frm = out.frames[i], nw = frm.rect.width, nh = frm.rect.height; var fdata = new Uint8Array(nh * frm.bpl + nh); frm.cimg = _filterZero(frm.img, nh, frm.bpp, frm.bpl, fdata, filter, levelZero); } } function compress(bufs, w, h, ps, prms) // prms: onlyBlend, minBits, forbidPlte { //var time = Date.now(); var onlyBlend = prms[0], evenCrd = prms[1], forbidPrev = prms[2], minBits = prms[3], forbidPlte = prms[4], dith = prms[5]; var ctype = 6, depth = 8, alphaAnd = 255; for (var j = 0; j < bufs.length; j++) { // when not quantized, other frames can contain colors, that are not in an initial frame var img = new Uint8Array(bufs[j]), ilen = img.length; for (var i = 0; i < ilen; i += 4) alphaAnd &= img[i + 3]; } var gotAlpha = alphaAnd != 255; //console.log("alpha check", Date.now()-time); time = Date.now(); //var brute = gotAlpha && forGIF; // brute : frames can only be copied, not "blended" var frms = framize(bufs, w, h, onlyBlend, evenCrd, forbidPrev); //console.log("framize", Date.now()-time); time = Date.now(); var cmap = {}, plte = [], inds = []; if (ps != 0) { var nbufs = []; for (var i = 0; i < frms.length; i++) nbufs.push(frms[i].img.buffer); var abuf = concatRGBA(nbufs), qres = quantize(abuf, ps); for (var i = 0; i < qres.plte.length; i++) plte.push(qres.plte[i].est.rgba); var cof = 0; for (var i = 0; i < frms.length; i++) { var frm = frms[i], bln = frm.img.length, ind = new Uint8Array(qres.inds.buffer, cof >> 2, bln >> 2); inds.push(ind); var bb = new Uint8Array(qres.abuf, cof, bln); //console.log(frm.img, frm.width, frm.height); //var time = Date.now(); if (dith) dither(frm.img, frm.rect.width, frm.rect.height, plte, bb, ind); //console.log(Date.now()-time); frm.img.set(bb); cof += bln; } //console.log("quantize", Date.now()-time); time = Date.now(); } else { // what if ps==0, but there are <=256 colors? we still need to detect, if the palette could be used for (var j = 0; j < frms.length; j++) { // when not quantized, other frames can contain colors, that are not in an initial frame var frm = frms[j], img32 = new Uint32Array(frm.img.buffer), nw = frm.rect.width, ilen = img32.length; var ind = new Uint8Array(ilen); inds.push(ind); for (var i = 0; i < ilen; i++) { var c = img32[i]; if (i != 0 && c == img32[i - 1]) ind[i] = ind[i - 1];else if (i > nw && c == img32[i - nw]) ind[i] = ind[i - nw];else { var cmc = cmap[c]; if (cmc == null) { cmap[c] = cmc = plte.length; plte.push(c); if (plte.length >= 300) break; } ind[i] = cmc; } } } //console.log("make palette", Date.now()-time); time = Date.now(); } var cc = plte.length; //console.log("colors:",cc); if (cc <= 256 && forbidPlte == false) { if (cc <= 2) depth = 1;else if (cc <= 4) depth = 2;else if (cc <= 16) depth = 4;else depth = 8; depth = Math.max(depth, minBits); } for (var j = 0; j < frms.length; j++) { var frm = frms[j], nx = frm.rect.x, ny = frm.rect.y, nw = frm.rect.width, nh = frm.rect.height; var cimg = frm.img, cimg32 = new Uint32Array(cimg.buffer); var bpl = 4 * nw, bpp = 4; if (cc <= 256 && forbidPlte == false) { bpl = Math.ceil(depth * nw / 8); var nimg = new Uint8Array(bpl * nh); var inj = inds[j]; for (var y = 0; y < nh; y++) { var i = y * bpl, ii = y * nw; if (depth == 8) for (var x = 0; x < nw; x++) nimg[i + x] = inj[ii + x];else if (depth == 4) for (var x = 0; x < nw; x++) nimg[i + (x >> 1)] |= inj[ii + x] << 4 - (x & 1) * 4;else if (depth == 2) for (var x = 0; x < nw; x++) nimg[i + (x >> 2)] |= inj[ii + x] << 6 - (x & 3) * 2;else if (depth == 1) for (var x = 0; x < nw; x++) nimg[i + (x >> 3)] |= inj[ii + x] << 7 - (x & 7) * 1; } cimg = nimg; ctype = 3; bpp = 1; } else if (gotAlpha == false && frms.length == 1) { // some next "reduced" frames may contain alpha for blending var nimg = new Uint8Array(nw * nh * 3), area = nw * nh; for (var i = 0; i < area; i++) { var ti = i * 3, qi = i * 4; nimg[ti] = cimg[qi]; nimg[ti + 1] = cimg[qi + 1]; nimg[ti + 2] = cimg[qi + 2]; } cimg = nimg; ctype = 2; bpp = 3; bpl = 3 * nw; } frm.img = cimg; frm.bpl = bpl; frm.bpp = bpp; } //console.log("colors => palette indices", Date.now()-time); time = Date.now(); return { ctype: ctype, depth: depth, plte: plte, frames: frms }; } function framize(bufs, w, h, alwaysBlend, evenCrd, forbidPrev) { /* DISPOSE - 0 : no change - 1 : clear to transparent - 2 : retstore to content before rendering (previous frame disposed) BLEND - 0 : replace - 1 : blend */ var frms = []; for (var j = 0; j < bufs.length; j++) { var cimg = new Uint8Array(bufs[j]), cimg32 = new Uint32Array(cimg.buffer); var nimg; var nx = 0, ny = 0, nw = w, nh = h, blend = alwaysBlend ? 1 : 0; if (j != 0) { var tlim = forbidPrev || alwaysBlend || j == 1 || frms[j - 2].dispose != 0 ? 1 : 2, tstp = 0, tarea = 1e9; for (var it = 0; it < tlim; it++) { var pimg = new Uint8Array(bufs[j - 1 - it]), p32 = new Uint32Array(bufs[j - 1 - it]); var mix = w, miy = h, max = -1, may = -1; for (var y = 0; y < h; y++) for (var x = 0; x < w; x++) { var i = y * w + x; if (cimg32[i] != p32[i]) { if (x < mix) mix = x; if (x > max) max = x; if (y < miy) miy = y; if (y > may) may = y; } } if (max == -1) mix = miy = max = may = 0; if (evenCrd) { if ((mix & 1) == 1) mix--; if ((miy & 1) == 1) miy--; } var sarea = (max - mix + 1) * (may - miy + 1); if (sarea < tarea) { tarea = sarea; tstp = it; nx = mix; ny = miy; nw = max - mix + 1; nh = may - miy + 1; } } // alwaysBlend: pokud zjistím, že blendit nelze, nastavím předchozímu snímku dispose=1. Zajistím, aby obsahoval můj obdélník. var pimg = new Uint8Array(bufs[j - 1 - tstp]); if (tstp == 1) frms[j - 1].dispose = 2; nimg = new Uint8Array(nw * nh * 4); _copyTile(pimg, w, h, nimg, nw, nh, -nx, -ny, 0); blend = _copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 3) ? 1 : 0; if (blend == 1) _prepareDiff(cimg, w, h, nimg, { x: nx, y: ny, width: nw, height: nh });else _copyTile(cimg, w, h, nimg, nw, nh, -nx, -ny, 0); } else nimg = cimg.slice(0); // img may be rewritten further ... don't rewrite input frms.push({ rect: { x: nx, y: ny, width: nw, height: nh }, img: nimg, blend: blend, dispose: 0 }); } if (alwaysBlend) for (var j = 0; j < frms.length; j++) { var frm = frms[j]; if (frm.blend == 1) continue; var r0 = frm.rect, r1 = frms[j - 1].rect; var miX = Math.min(r0.x, r1.x), miY = Math.min(r0.y, r1.y); var maX = Math.max(r0.x + r0.width, r1.x + r1.width), maY = Math.max(r0.y + r0.height, r1.y + r1.height); var r = { x: miX, y: miY, width: maX - miX, height: maY - miY }; frms[j - 1].dispose = 1; if (j - 1 != 0) _updateFrame(bufs, w, h, frms, j - 1, r, evenCrd); _updateFrame(bufs, w, h, frms, j, r, evenCrd); } var area = 0; if (bufs.length != 1) for (var i = 0; i < frms.length; i++) { var frm = frms[i]; area += frm.rect.width * frm.rect.height; //if(i==0 || frm.blend!=1) continue; //var ob = new Uint8Array( //console.log(frm.blend, frm.dispose, frm.rect); } //if(area!=0) console.log(area); return frms; } function _updateFrame(bufs, w, h, frms, i, r, evenCrd) { var U8 = Uint8Array, U32 = Uint32Array; var pimg = new U8(bufs[i - 1]), pimg32 = new U32(bufs[i - 1]), nimg = i + 1 < bufs.length ? new U8(bufs[i + 1]) : null; var cimg = new U8(bufs[i]), cimg32 = new U32(cimg.buffer); var mix = w, miy = h, max = -1, may = -1; for (var y = 0; y < r.height; y++) for (var x = 0; x < r.width; x++) { var cx = r.x + x, cy = r.y + y; var j = cy * w + cx, cc = cimg32[j]; // no need to draw transparency, or to dispose it. Or, if writing the same color and the next one does not need transparency. if (cc == 0 || frms[i - 1].dispose == 0 && pimg32[j] == cc && (nimg == null || nimg[j * 4 + 3] != 0) /**/) {} else { if (cx < mix) mix = cx; if (cx > max) max = cx; if (cy < miy) miy = cy; if (cy > may) may = cy; } } if (max == -1) mix = miy = max = may = 0; if (evenCrd) { if ((mix & 1) == 1) mix--; if ((miy & 1) == 1) miy--; } r = { x: mix, y: miy, width: max - mix + 1, height: may - miy + 1 }; var fr = frms[i]; fr.rect = r; fr.blend = 1; fr.img = new Uint8Array(r.width * r.height * 4); if (frms[i - 1].dispose == 0) { _copyTile(pimg, w, h, fr.img, r.width, r.height, -r.x, -r.y, 0); _prepareDiff(cimg, w, h, fr.img, r); } else _copyTile(cimg, w, h, fr.img, r.width, r.height, -r.x, -r.y, 0); } function _prepareDiff(cimg, w, h, nimg, rec) { _copyTile(cimg, w, h, nimg, rec.width, rec.height, -rec.x, -rec.y, 2); } function _filterZero(img, h, bpp, bpl, data, filter, levelZero) { var fls = [], ftry = [0, 1, 2, 3, 4]; if (filter != -1) ftry = [filter];else if (h * bpl > 500000 || bpp == 1) ftry = [0]; var opts; if (levelZero) opts = { level: 0 }; var CMPR = data.length > 10e6 && window.UZIP != null ? window.UZIP : pako; var time = Date.now(); for (var i = 0; i < ftry.length; i++) { for (var y = 0; y < h; y++) _filterLine(data, img, y, bpl, bpp, ftry[i]); //var nimg = new Uint8Array(data.length); //var sz = UZIP.F.deflate(data, nimg); fls.push(nimg.slice(0,sz)); //var dfl = pako["deflate"](data), dl=dfl.length-4; //var crc = (dfl[dl+3]<<24)|(dfl[dl+2]<<16)|(dfl[dl+1]<<8)|(dfl[dl+0]<<0); //console.log(crc, UZIP.adler(data,2,data.length-6)); fls.push(CMPR["deflate"](data, opts)); } var ti, tsize = 1e9; for (var i = 0; i < fls.length; i++) if (fls[i].length < tsize) { ti = i; tsize = fls[i].length; } return fls[ti]; } function _filterLine(data, img, y, bpl, bpp, type) { var i = y * bpl, di = i + y; data[di] = type; di++; if (type == 0) { if (bpl < 500) for (var x = 0; x < bpl; x++) data[di + x] = img[i + x];else data.set(new Uint8Array(img.buffer, i, bpl), di); } else if (type == 1) { for (var x = 0; x < bpp; x++) data[di + x] = img[i + x]; for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x] - img[i + x - bpp] + 256 & 255; } else if (y == 0) { for (var x = 0; x < bpp; x++) data[di + x] = img[i + x]; if (type == 2) for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x]; if (type == 3) for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x] - (img[i + x - bpp] >> 1) + 256 & 255; if (type == 4) for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x] - paeth(img[i + x - bpp], 0, 0) + 256 & 255; } else { if (type == 2) { for (var x = 0; x < bpl; x++) data[di + x] = img[i + x] + 256 - img[i + x - bpl] & 255; } if (type == 3) { for (var x = 0; x < bpp; x++) data[di + x] = img[i + x] + 256 - (img[i + x - bpl] >> 1) & 255; for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x] + 256 - (img[i + x - bpl] + img[i + x - bpp] >> 1) & 255; } if (type == 4) { for (var x = 0; x < bpp; x++) data[di + x] = img[i + x] + 256 - paeth(0, img[i + x - bpl], 0) & 255; for (var x = bpp; x < bpl; x++) data[di + x] = img[i + x] + 256 - paeth(img[i + x - bpp], img[i + x - bpl], img[i + x - bpp - bpl]) & 255; } } } function quantize(abuf, ps, doKmeans) { var time = Date.now(); var sb = new Uint8Array(abuf), tb = sb.slice(0), tb32 = new Uint32Array(tb.buffer); var KD = getKDtree(tb, ps); var root = KD[0], leafs = KD[1], K = leafs.length; //console.log(Date.now()-time, "tree made"); time = Date.now(); var cl32 = new Uint32Array(K), clr8 = new Uint8Array(cl32.buffer); for (var i = 0; i < K; i++) cl32[i] = leafs[i].est.rgba; var len = sb.length; var inds = new Uint8Array(len >> 2), nd; if (K <= 60) { findNearest(sb, inds, clr8); remap(inds, tb32, cl32); } else if (sb.length < 32e6) // precise, but slow :( //for(var j=0; j<4; j++) for (var i = 0; i < len; i += 4) { var r = sb[i] * (1 / 255), g = sb[i + 1] * (1 / 255), b = sb[i + 2] * (1 / 255), a = sb[i + 3] * (1 / 255); nd = getNearest(root, r, g, b, a); inds[i >> 2] = nd.ind; tb32[i >> 2] = nd.est.rgba; } else for (var i = 0; i < len; i += 4) { var r = sb[i] * (1 / 255), g = sb[i + 1] * (1 / 255), b = sb[i + 2] * (1 / 255), a = sb[i + 3] * (1 / 255); nd = root; while (nd.left) nd = planeDst(nd.est, r, g, b, a) <= 0 ? nd.left : nd.right; inds[i >> 2] = nd.ind; tb32[i >> 2] = nd.est.rgba; } //console.log(Date.now()-time, "nearest found"); time = Date.now(); if (doKmeans || sb.length * K < 10 * 4e6) { var le = 1e9; for (var i = 0; i < 10; i++) { var ce = kmeans(sb, inds, clr8); //console.log(i,ce); if (ce / le > 0.997) break; le = ce; } for (var i = 0; i < K; i++) leafs[i].est.rgba = cl32[i]; remap(inds, tb32, cl32); //console.log(Date.now()-time, "k-means"); } return { abuf: tb.buffer, inds: inds, plte: leafs }; } function remap(inds, tb32, pl32) { for (var i = 0; i < inds.length; i++) tb32[i] = pl32[inds[i]]; } function kmeans(sb, inds, plte) { updatePalette(sb, inds, plte); var err = findNearest(sb, inds, plte); return err; } function updatePalette(sb, inds, plte) { var K = plte.length >>> 2; var sums = new Uint32Array(K * 4), cnts = new Uint32Array(K); for (var i = 0; i < sb.length; i += 4) { var ind = inds[i >>> 2], qi = ind * 4; cnts[ind]++; sums[qi] += sb[i]; sums[qi + 1] += sb[i + 1]; sums[qi + 2] += sb[i + 2]; sums[qi + 3] += sb[i + 3]; } for (var i = 0; i < plte.length; i++) plte[i] = Math.round(sums[i] / cnts[i >>> 2]); } function findNearest(sb, inds, plte) { var terr = 0, K = plte.length >>> 2; var nd = []; // squared half-distance to the nearest color for (var i = 0; i < K; i++) { var qi = i * 4; var r = plte[qi], g = plte[qi + 1], b = plte[qi + 2], a = plte[qi + 3], ti = 0, te = 1e9; for (var j = 0; j < K; j++) { if (i == j) continue; var qj = j * 4, dr = r - plte[qj], dg = g - plte[qj + 1], db = b - plte[qj + 2], da = a - plte[qj + 3]; var err = dr * dr + dg * dg + db * db + da * da; if (err < te) { te = err; ti = j; } } nd[i] = Math.sqrt(te) * 0.5; nd[i] = nd[i] * nd[i]; } for (var i = 0; i < sb.length; i += 4) { var r = sb[i], g = sb[i + 1], b = sb[i + 2], a = sb[i + 3]; var ti = inds[i >>> 2], qi = ti * 4, dr = r - plte[qi], dg = g - plte[qi + 1], db = b - plte[qi + 2], da = a - plte[qi + 3], te = dr * dr + dg * dg + db * db + da * da; if (te > nd[ti]) for (var j = 0; j < K; j++) { qi = j * 4; dr = r - plte[qi]; dg = g - plte[qi + 1]; db = b - plte[qi + 2]; da = a - plte[qi + 3]; var err = dr * dr + dg * dg + db * db + da * da; if (err < te) { te = err; ti = j; if (te < nd[j]) break; } } inds[i >>> 2] = ti; terr += te; } return terr / (sb.length >>> 2); } function getKDtree(nimg, ps, err) { if (err == null) err = 0.0001; var nimg32 = new Uint32Array(nimg.buffer); var root = { i0: 0, i1: nimg.length, bst: null, est: null, tdst: 0, left: null, right: null }; // basic statistic, extra statistic root.bst = stats(nimg, root.i0, root.i1); root.est = estats(root.bst); var leafs = [root]; while (leafs.length < ps) { var maxL = 0, mi = 0; for (var i = 0; i < leafs.length; i++) if (leafs[i].est.L > maxL) { maxL = leafs[i].est.L; mi = i; } if (maxL < err) break; var node = leafs[mi]; var s0 = splitPixels(nimg, nimg32, node.i0, node.i1, node.est.e, node.est.eMq255); var s0wrong = node.i0 >= s0 || node.i1 <= s0; //console.log(maxL, leafs.length, mi); if (s0wrong) { node.est.L = 0; continue; } var ln = { i0: node.i0, i1: s0, bst: null, est: null, tdst: 0, left: null, right: null }; ln.bst = stats(nimg, ln.i0, ln.i1); ln.est = estats(ln.bst); var rn = { i0: s0, i1: node.i1, bst: null, est: null, tdst: 0, left: null, right: null }; rn.bst = { R: [], m: [], N: node.bst.N - ln.bst.N }; for (var i = 0; i < 16; i++) rn.bst.R[i] = node.bst.R[i] - ln.bst.R[i]; for (var i = 0; i < 4; i++) rn.bst.m[i] = node.bst.m[i] - ln.bst.m[i]; rn.est = estats(rn.bst); node.left = ln; node.right = rn; leafs[mi] = ln; leafs.push(rn); } leafs.sort(function (a, b) { return b.bst.N - a.bst.N; }); for (var i = 0; i < leafs.length; i++) leafs[i].ind = i; return [root, leafs]; } function getNearest(nd, r, g, b, a) { if (nd.left == null) { nd.tdst = dist(nd.est.q, r, g, b, a); return nd; } var pd = planeDst(nd.est, r, g, b, a); var node0 = nd.left, node1 = nd.right; if (pd > 0) { node0 = nd.right; node1 = nd.left; } var ln = getNearest(node0, r, g, b, a); if (ln.tdst <= pd * pd) return ln; var rn = getNearest(node1, r, g, b, a); return rn.tdst < ln.tdst ? rn : ln; } function planeDst(est, r, g, b, a) { var e = est.e; return e[0] * r + e[1] * g + e[2] * b + e[3] * a - est.eMq; } function dist(q, r, g, b, a) { var d0 = r - q[0], d1 = g - q[1], d2 = b - q[2], d3 = a - q[3]; return d0 * d0 + d1 * d1 + d2 * d2 + d3 * d3; } function splitPixels(nimg, nimg32, i0, i1, e, eMq) { i1 -= 4; var shfs = 0; while (i0 < i1) { while (vecDot(nimg, i0, e) <= eMq) i0 += 4; while (vecDot(nimg, i1, e) > eMq) i1 -= 4; if (i0 >= i1) break; var t = nimg32[i0 >> 2]; nimg32[i0 >> 2] = nimg32[i1 >> 2]; nimg32[i1 >> 2] = t; i0 += 4; i1 -= 4; } while (vecDot(nimg, i0, e) > eMq) i0 -= 4; return i0 + 4; } function vecDot(nimg, i, e) { return nimg[i] * e[0] + nimg[i + 1] * e[1] + nimg[i + 2] * e[2] + nimg[i + 3] * e[3]; } function stats(nimg, i0, i1) { var R = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; var m = [0, 0, 0, 0]; var N = i1 - i0 >> 2; for (var i = i0; i < i1; i += 4) { var r = nimg[i] * (1 / 255), g = nimg[i + 1] * (1 / 255), b = nimg[i + 2] * (1 / 255), a = nimg[i + 3] * (1 / 255); //var r = nimg[i], g = nimg[i+1], b = nimg[i+2], a = nimg[i+3]; m[0] += r; m[1] += g; m[2] += b; m[3] += a; R[0] += r * r; R[1] += r * g; R[2] += r * b; R[3] += r * a; R[5] += g * g; R[6] += g * b; R[7] += g * a; R[10] += b * b; R[11] += b * a; R[15] += a * a; } R[4] = R[1]; R[8] = R[2]; R[9] = R[6]; R[12] = R[3]; R[13] = R[7]; R[14] = R[11]; return { R: R, m: m, N: N }; } function estats(stats) { var R = stats.R, m = stats.m, N = stats.N; // when all samples are equal, but N is large (millions), the Rj can be non-zero ( 0.0003.... - precission error) var m0 = m[0], m1 = m[1], m2 = m[2], m3 = m[3], iN = N == 0 ? 0 : 1 / N; var Rj = [R[0] - m0 * m0 * iN, R[1] - m0 * m1 * iN, R[2] - m0 * m2 * iN, R[3] - m0 * m3 * iN, R[4] - m1 * m0 * iN, R[5] - m1 * m1 * iN, R[6] - m1 * m2 * iN, R[7] - m1 * m3 * iN, R[8] - m2 * m0 * iN, R[9] - m2 * m1 * iN, R[10] - m2 * m2 * iN, R[11] - m2 * m3 * iN, R[12] - m3 * m0 * iN, R[13] - m3 * m1 * iN, R[14] - m3 * m2 * iN, R[15] - m3 * m3 * iN]; var A = Rj, M = M4; var b = [Math.random(), Math.random(), Math.random(), Math.random()], mi = 0, tmi = 0; if (N != 0) for (var i = 0; i < 16; i++) { b = M.multVec(A, b); tmi = Math.sqrt(M.dot(b, b)); b = M.sml(1 / tmi, b); if (i != 0 && Math.abs(tmi - mi) < 1e-9) break; mi = tmi; } //b = [0,0,1,0]; mi=N; var q = [m0 * iN, m1 * iN, m2 * iN, m3 * iN]; var eMq255 = M.dot(M.sml(255, q), b); return { Cov: Rj, q: q, e: b, L: mi, eMq255: eMq255, eMq: M.dot(b, q), rgba: (Math.round(255 * q[3]) << 24 | Math.round(255 * q[2]) << 16 | Math.round(255 * q[1]) << 8 | Math.round(255 * q[0]) << 0) >>> 0 }; } var M4 = { multVec: function multVec(m, v) { return [m[0] * v[0] + m[1] * v[1] + m[2] * v[2] + m[3] * v[3], m[4] * v[0] + m[5] * v[1] + m[6] * v[2] + m[7] * v[3], m[8] * v[0] + m[9] * v[1] + m[10] * v[2] + m[11] * v[3], m[12] * v[0] + m[13] * v[1] + m[14] * v[2] + m[15] * v[3]]; }, dot: function dot(x, y) { return x[0] * y[0] + x[1] * y[1] + x[2] * y[2] + x[3] * y[3]; }, sml: function sml(a, y) { return [a * y[0], a * y[1], a * y[2], a * y[3]]; } }; function concatRGBA(bufs) { var tlen = 0; for (var i = 0; i < bufs.length; i++) tlen += bufs[i].byteLength; var nimg = new Uint8Array(tlen), noff = 0; for (var i = 0; i < bufs.length; i++) { var img = new Uint8Array(bufs[i]), il = img.length; for (var j = 0; j < il; j += 4) { var r = img[j], g = img[j + 1], b = img[j + 2], a = img[j + 3]; if (a == 0) r = g = b = 0; nimg[noff + j] = r; nimg[noff + j + 1] = g; nimg[noff + j + 2] = b; nimg[noff + j + 3] = a; } noff += il; } return nimg.buffer; } UPNG$1.encode = encode; UPNG$1.encodeLL = encodeLL; UPNG$1.encode.compress = compress; UPNG$1.encode.dither = dither; UPNG$1.quantize = quantize; UPNG$1.quantize.findNearest = findNearest; UPNG$1.quantize.getKDtree = getKDtree; UPNG$1.quantize.getNearest = getNearest; })(); window.UPNG = UPNG$1; var Common = { sortByScore: function sortByScore(list, request, rank) { var i = request ? Common.filterAll(list, request) : list; return 0 === i.length ? [] : i = i.map(function (e) { var results = rank.map(function (f) { return f(e); }); var scores = results.map(e => e.score != void 0 ? e.score : e); var logs = results.map(e => e.log); return { item: e, scores, logs, score: scores.reduce(function (t, i) { //总分 return t + i; }, 0) }; }).sort(function (e, t) { return t.score - e.score; }); }, filterAll: function filterAll(e, t) { return e.filter(function (e) { return t.every(function (t) { return t(e); }); }); }, //--------------- find: function find(list, request, rank, sortByScore) { if (sortByScore) { var r = this.sortByScore(list, request, rank); return r[0] && r[0].item; } else { var i = request ? Common.filterAll(list, request) : list; return 0 === i.length ? null : (rank && rank.forEach(function (e) { i = Common.stableSort(i, e); }), i[0]); } }, stableSort: function stableSort(e, f) { //用到排序函数,涉及到两个item相减 return e.map(function (e, i) { return { value: e, index: i }; }).sort(function (e, u) { var n = f(e.value, u.value); return 0 !== n ? n : e.index - u.index; //似乎就是加多了这一步:若差距为0,按照原顺序 }).map(function (e) { return e.value; }); }, average: function average(e, t) { if (0 === e.length) return null; for (var i = 0, n = 0, r = 0; r < e.length; r++) { var o = t ? e[r][t] : e[r]; i += o, n++; } return i / n; }, //--------------------------- getMixedSet: function getMixedSet(arr1, arr2) { //交集 return arr1.filter(item => arr2.includes(item)); }, getUnionSet: function getUnionSet(arr1, arr2) { //并集 return arr1.concat(arr2.filter(item => !arr1.includes(item))); }, getDifferenceSet: function getDifferenceSet(arr1, arr2) { //差集 不能识别重复的,如getDifferenceSet([1,2,2],[1,1,2]) 为空 var arr11 = arr1.filter(item => !arr2.includes(item)); var arr22 = arr2.filter(item => !arr1.includes(item)); return arr11.concat(arr22); }, getDifferenceSetMuti: function getDifferenceSetMuti(arr) { //收集绝对没有重复的元素,也就是判断出现次数=1的 var set = []; arr.forEach(arr1 => { arr1.forEach(item => { var index = set.indexOf(item); if (index > -1) { set.splice(index, 1); } else { set.push(item); } }); }); return set; }, CloneJson: function CloneJson(data) { var str = JSON.stringify(data); return JSON.parse(str); }, CloneObject: function CloneObject(copyObj, isSimpleCopy) { var simpleCopyList = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; var judgeSimpleCopyFun = arguments.length > 3 ? arguments[3] : undefined; //isSimpleCopy 只复制最外层 //复制json result的可能:普通数字或字符串、普通数组、复杂对象 judgeSimpleCopyFun || (judgeSimpleCopyFun = () => {}); if (!copyObj || typeof copyObj == 'number' || typeof copyObj == 'string' || copyObj.isObject3D || copyObj instanceof Function || simpleCopyList.some(className => copyObj instanceof className) || judgeSimpleCopyFun(copyObj)) { return copyObj; } if (copyObj instanceof Array) { return copyObj.map(e => { return this.CloneObject(e, isSimpleCopy, simpleCopyList, judgeSimpleCopyFun); }); } else { if (copyObj.clone instanceof Function) { //解决一部分 return copyObj.clone(); } } var result = {}; for (var key in copyObj) { if (copyObj[key] instanceof Object && !isSimpleCopy) result[key] = this.CloneObject(copyObj[key], isSimpleCopy, simpleCopyList, judgeSimpleCopyFun);else result[key] = copyObj[key]; //如果是函数类同基本数据,即复制引用 } return result; }, CloneClassObject: function CloneClassObject(copyObj) { var { ignoreList = [], simpleCopyList = [] } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; //复杂类对象 var newobj = new copyObj.constructor(); this.CopyClassObject(newobj, copyObj, { ignoreList, simpleCopyList }); return newobj; }, CopyClassObject: function CopyClassObject(targetObj, copyObj) { var { ignoreList = [], simpleCopyList = [] } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; //复杂类对象 for (var i in copyObj) { if (i in copyObj.__proto__) break; //到函数了跳出 if (ignoreList.includes(i)) { continue; } else if (simpleCopyList.includes(i)) { targetObj[i] = copyObj[i]; } else { targetObj[i] = this.CloneObject(copyObj[i], false, simpleCopyList); } /* else if(copyObj[i].clone instanceof Function ){ targetObj[i] = copyObj[i].clone() }else{ targetObj[i] = copyObj[i]; } */ } }, ifSame: function ifSame(object1, object2) { var simpleEqualClass = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; //对于复杂的类对象,若能简单判断就直接写进simpleEqualClass if (object1 == object2) return true; // 0 != undefined , 0 == '' else if (!object1 || !object2) return false;else if (object1.constructor != object2.constructor) { return false; } else if (simpleEqualClass.some(className => object1 instanceof className)) { return object1 == object2; } else if (object1 instanceof Array) { if (object1.length != object2.length) return false; var _object2 = object2.slice(0); var _loop = function _loop(i) { u = _object2.find(e => Common.ifSame(object1[i], e, simpleEqualClass)); if (u == void 0 && !_object2.includes(u) && !object1.includes(u)) return { v: false };else { var index = _object2.indexOf(u); _object2.splice(index, 1); } }, u, _ret; for (var i = 0; i < object1.length; i++) { _ret = _loop(i); if (_ret) return _ret.v; } return true; } else if (object1.equals instanceof Function) { //复杂数据仅支持这种,其他的可能卡住? return object1.equals(object2); } else if (typeof object1 == 'number' || typeof object1 == 'string') { if (isNaN(object1) && isNaN(object2)) return true;else return object1 == object2; } else if (typeof object1 == "object") { var keys1 = Object.keys(object1); var keys2 = Object.keys(object2); if (!Common.ifSame(keys1, keys2, simpleEqualClass)) return false; for (var _i in object1) { var same = Common.ifSame(object1[_i], object2[_i], simpleEqualClass); if (!same) return false; } return true; } else { console.log('isSame出现例外'); } }, downloadFile: function downloadFile(data, filename, cb) { var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a'); save_link.href = data; save_link.download = filename; var event = document.createEvent('MouseEvents'); event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); save_link.dispatchEvent(event); cb && cb(); }, replaceAll: function replaceAll(str, f, e) { //f全部替换成e if (str.replaceAll) return str.replaceAll(f, e);else { var escapeRegExp = string => { return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string }; return str.replace(new RegExp(escapeRegExp(f), 'g'), e); /* var reg = new RegExp(f, "g"); //创建正则RegExp对象 这个没法转换'(' return str.replace(reg, e); //str.split(f).join(e); */ } }, dealURL() { var url = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var urlNew = this.replaceAll(url, "+", "%2B"); //this.replaceAll(url, "\\+", "%2B");// 浏览器似乎不支持访问带+的地址 urlNew = this.replaceAll(urlNew, "/.//", "/"); //去除双斜杠(/.//) //urlNew = encodeURIComponent(url) return urlNew; }, getNameFromURL(url, removePostfix) { if (!url) return ''; var get = e => { var a = e.split('/').pop(); if (removePostfix) a = a.split('.')[0]; return a; }; if (url instanceof Array) { return url.map(e => get(e)); } return get(url); }, //--------------------------- intervalTool: { //延时update,防止卡顿 list: [], /* isWaiting:function(name, func, delayTime){ if(!this.list.includes(name)){ //如果没有该项, 则开始判断 var needWait = func(); //触发了改变,则等待一段时间后再自动判断 if(needWait){ this.list.push(name); setTimeout(()=>{ var a = this.list.indexOf(name); this.list.splice(a,1); this.isWaiting(name, func, delayTime) //循环 },delayTime) } } }, */ isWaiting: function isWaiting(name, func, delayTime /* , autoCycle */) { var item = this.list.find(e => e.name == name); if (!item) { //如果没有该项, 则加入循环 var ifContinue = func(); item = { name, func, delayTime }; /* if(name == 'processPriorityQueue'){ console.log('isWaiting', delayTime) } */ this.list.push(item); setTimeout(() => { var a = this.list.indexOf(item); this.list.splice(a, 1); var { func, delayTime } = item; if (item.requestUpdate || ifContinue) this.isWaiting(name, func, delayTime); //循环 }, delayTime); } else { //如果有该项,说明现在请求下一次继续更新 //if(delayTime == 0){//想立刻更新一次 // func() //}else{ //更新属性 item.func = func; item.delayTime = delayTime; item.requestUpdate = true; //} } } }, waitTool: { //定时器,在等待的这段时间内如果又触发则重新计时 list: [], wait(name, func, time) { var item = this.list.find(e => e.name == name); var timer = setTimeout(() => { func(); var index = this.list.indexOf(item); this.list.splice(index, 1); }, time); if (item) { clearTimeout(item.timer); } else { item = { name }; this.list.push(item); } item.timer = timer; }, cancel(name) { var index = this.list.findIndex(e => e.name == name); index > -1 && this.list.splice(index, 1); } }, pushToGroupAuto: function pushToGroupAuto(items, groups, recognizeFunction, recognizeGroup) { //自动分组。 items是将分到一起的组合。items.length = 1 or 2. recognizeFunction = recognizeFunction || function () {}; if (recognizeGroup) { //有更复杂的识别处理,直接传递整个组 var atGroups = groups.filter(group => recognizeGroup(group, items)); } else { var atGroups = groups.filter(group => group.find(item => items[0] == item || recognizeFunction(item, items[0]) || items[1] == item || items[1] && recognizeFunction(item, items[1]))); } if (atGroups.length) { //在不同组 //因为items是一组的,所以先都放入组1 items.forEach(item => { if (!atGroups[0].includes(item)) atGroups[0].push(item); }); if (atGroups.length > 1) { //如果在不同组,说明这两个组需要合并 var combineGroup = []; atGroups.forEach(group => { combineGroup = Common.getUnionSet(combineGroup, group); groups.splice(groups.indexOf(group), 1); }); groups.push(combineGroup); } } else { //直接加入为一组 groups.push(items); } }, getBestCount: function () { var lastCount = {}; return function (name) { var minCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var maxCount = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 6; var durBound1 = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1.2; var durBound2 = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 10; var ifLog = arguments.length > 5 ? arguments[5] : undefined; var maxHistory = arguments.length > 6 ? arguments[6] : undefined; var timeStamp = performance.getEntriesByName("loop-start"); var count; if (timeStamp.length) { var dur = performance.now() - timeStamp[timeStamp.length - 1].startTime; //dur在iphoneX中静止有7,pc是2 count = Math.round(math.linearClamp(dur, [durBound1, durBound2], [maxCount, minCount])); if (maxHistory) { if (!lastCount[name]) lastCount[name] = []; if (count == 0 && lastCount[name].length > maxHistory - 1 && !lastCount[name].some(e => e > 0)) { count = 1; } lastCount[name].push(count); if (lastCount[name].length > maxHistory) lastCount[name].splice(0, 1); } if (ifLog) { //注意,console.log本身用时挺高, 降4倍时可能占用0.5毫秒 name && count && console.log(name, count, ' ,dur:', dur.toFixed(3)); } } else { count = maxCount; // ? } //主要在手机端有效果。 return count; }; }(), getBestCountFPS(name, ifLog) { var minCount = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var maxCount = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 6; var minFps = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 10; var maxFps = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 60; var minPanoCount = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 200; var maxPanoCount = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : 1000; var fps = Potree.fps; var count = math.linearClamp(fps, [minFps, maxFps], [minCount, maxCount]); var count2 = math.linearClamp(viewer.images360.panos.length, [minPanoCount, maxPanoCount], [minCount, maxCount]); count = (count + count2) / 2; count = Math.round(count); if (ifLog) { name && count && console.log(name, count, ' ,fps:', fps.toFixed(3)); } return count; }, batchHandling: { //分批处理 lists: [], getSlice: function getSlice(name, items, _ref) { var { stopWhenAllUsed, min = 5, max = 100, durBound1, durBound2, useEquals, maxUseCount } = _ref; if (items.length == 0 || (maxUseCount = maxUseCount == void 0 ? Common.getBestCount(name, min, max, durBound1, durBound2 /* , true */) : maxUseCount, !maxUseCount) //本次最多可以使用的个数 ) { return { list: [] }; } if (!this.lists[name]) this.lists[name] = { list: [] }; //更新列表项目,但不变原来的顺序 var list = this.lists[name].list.filter(a => items.some(item => useEquals ? a.item.equals(item) : a.item == item)); //去掉已经不在items里的项目 this.lists[name].list = list; items.forEach(item => { //增加新的项目。 if (!list.some(a => useEquals ? a.item.equals(item) : a.item == item)) { list.push({ item, count: 0 }); } }); //至此,在后排的都是未使用的 var unUsed = list.filter(e => e.count == 0); //未使用的项目(count为0)优先 var result = []; unUsed.slice(0, maxUseCount).forEach(e => { result.push(e.item); e.count++; }); if (unUsed.length > maxUseCount) { //还是剩有未使用的项目,等待下一次 } else { //所有项目都能使用一次 if (!stopWhenAllUsed) { //若不是全部使用就停止 var wholeCount = Math.min(items.length, maxUseCount); var restCount = wholeCount - result.length; //补齐 list.slice(0, restCount).forEach(e => { result.push(e.item); e.count++; }); } list.forEach(e => e.count--); //复原,等待新的循环 } /* result.forEach((e,i)=>{//有重复的 if( result.slice(0,i).some(a=>a.equals(e)) || result.slice(i+1).some(a=>a.equals(e)) ) { console.log(e) } }) */ return { list: result }; } }, getRootWindow() { //获取包含Potree的根window var win = window; try { while (win.parent != win && win.parent.Potree) { win = win.parent; } if (window != win) return win; } catch (e) { //console.log(e) //可能跨域,从而win.parent.Potree报错 console.log('getRootWindow 跨域'); return; } }, watch: function watch(object, propName, initialValue) { //监听某个属性的变化 var v = initialValue; Object.defineProperty(object, propName, { get: function get() { return v; }, set: function set(e) { console.warn('watch:', propName, e); v = e; } }); }, imgAddLabel: function imgAddLabel(img, labelImg) { var labelInfo = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; //图上加另一张小图,用于添加水印 var canvas; if (img instanceof Image) { canvas = document.createElement('canvas'); } else { canvas = img; } var context = canvas.getContext('2d'); var marginLeft = labelInfo.bgMargin && labelInfo.bgMargin.left || 0; var marginRight = labelInfo.bgMargin && labelInfo.bgMargin.right || 0; var marginTop = labelInfo.bgMargin && labelInfo.bgMargin.top || 0; var marginBottom = labelInfo.bgMargin && labelInfo.bgMargin.bottom || 0; if (img instanceof Image) { //如果img是canvas,说明已绘制在canvas上了就不用绘制了 var width = img.width + marginLeft + marginRight; var height = img.height + marginTop + marginBottom; canvas.width = width; canvas.height = height; if (labelInfo.bgColor) { context.fillStyle = 'rgba(' + labelInfo.bgColor.r + ',' + labelInfo.bgColor.g + ',' + labelInfo.bgColor.b + ',' + labelInfo.bgColor.a + ')'; context.fillRect(0, 0, width, height); } context.drawImage(img, marginLeft, marginTop, img.width, img.height); } var labelWidth = labelInfo.widthRatioToImg ? img.width * labelInfo.widthRatioToImg : labelImg.width; //widthRatioToImg:label的width占img的width的比例 var labelHeight = labelWidth * labelImg.height / labelImg.width; if (labelInfo.leftRatioToImg == void 0 && labelInfo.rightRatioToImg != void 0) { labelInfo.leftRatioToImg = 1 - labelInfo.rightRatioToImg - (labelInfo.widthRatioToImg || labelImg.width / img.width); } if (labelInfo.topRatioToImg == void 0 && labelInfo.bottomRatioToImg != void 0) { labelInfo.topRatioToImg = 1 - labelInfo.bottomRatioToImg - labelHeight / img.height; } var labelLeft = img.width * labelInfo.leftRatioToImg + marginLeft; //leftRatioToImg:label的left占img的width的比例 var labelTop = img.height * labelInfo.topRatioToImg + marginTop; //topRatioToImg:label的top占img的height的比例 context.globalAlpha = labelInfo.opacity != void 0 ? labelInfo.opacity : 1; context.drawImage(labelImg, labelLeft, labelTop, labelWidth, labelHeight); if (labelInfo.outputCanvas) { return canvas; } var dataUrl = canvas.toDataURL('image/png', labelInfo.compressRatio); //Common.downloadFile(dataUrl, 'screenshot.png') context.clearRect(0, 0, canvas.width, canvas.height); return dataUrl; }, /* changeShaderToWebgl2(vs, fs, matType, otherReplaces=[]){//部分shader要根据webgl版本作更改 if(!Potree.settings.isWebgl2)return {vs, fs} let turnTo300 = matType != 'ShaderMaterial' && (vs.includes('gl_FragDepthEXT') || fs.includes('gl_FragDepthEXT') ) let addV300 = turnTo300 && matType != 'RawShaderMaterial' // RawShaderMaterial直接material.glslVersion = '300 es' 以加在define之前 let change = (shader, shaderType)=>{ let newShader = shader if(turnTo300){ //非shaderMaterial需要手动改为300 es的写法 addV300 && (newShader = '#version 300 es \n' + newShader) //需要加 #version 300 es。 three.js自带的渲染会自动加所以不用 newShader = newShader.replaceAll('varying ', shaderType == 'vs' ? 'out ' : 'in ') newShader = newShader.replaceAll('attribute ', 'in ') if(shaderType == 'fs'){ newShader = newShader.replaceAll('gl_FragColor', 'fragColor') newShader = newShader.replace('void main', 'out vec4 fragColor;\n void main' )//在void main前加入这个声明 } newShader = newShader.replaceAll('gl_FragDepthEXT','gl_FragDepth') newShader = newShader.replaceAll('texture2D','texture') newShader = newShader.replaceAll('textureCube','texture') } newShader = newShader.replace('#extension GL_EXT_frag_depth : enable','') newShader = newShader.replaceAll('defined(GL_EXT_frag_depth) &&','') otherReplaces.forEach(({oldStr,newStr})=>{ newShader = newShader.replaceAll(oldStr,newStr) }) return newShader } vs = change(vs,'vs') fs = change(fs,'fs') //console.log('成功替换为webgl2' ) return {vs,fs} }//three.js的shaderMaterial也有替换功能,搜 '#define gl_FragDepthEXT gl_FragDepth', */ changeShaderToWebgl2(vs, fs, matType) { var otherReplaces = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; //部分shader要根据webgl版本作更改 if (!Potree.settings.isWebgl2) return { vs, fs }; var turnTo300 = matType != 'ShaderMaterial' && (vs.includes('gl_FragDepthEXT') || fs.includes('gl_FragDepthEXT')); var addV300 = turnTo300 && matType != 'RawShaderMaterial'; // RawShaderMaterial直接material.glslVersion = '300 es' 以加在define之前 var change = (shader, shaderType) => { var newShader = shader; if (turnTo300) { //非shaderMaterial需要手动改为300 es的写法 addV300 && (newShader = '#version 300 es \n' + newShader); //需要加 #version 300 es。 three.js自带的渲染会自动加所以不用 newShader = this.replaceAll(newShader, 'varying ', shaderType == 'vs' ? 'out ' : 'in '); newShader = this.replaceAll(newShader, 'attribute ', 'in '); if (shaderType == 'fs') { newShader = this.replaceAll(newShader, 'gl_FragColor', 'fragColor'); newShader = newShader.replace('void main', 'out vec4 fragColor;\n void main'); //在void main前加入这个声明 } newShader = this.replaceAll(newShader, 'gl_FragDepthEXT', 'gl_FragDepth'); newShader = this.replaceAll(newShader, 'texture2D', 'texture'); newShader = this.replaceAll(newShader, 'textureCube', 'texture'); } newShader = newShader.replace('#extension GL_EXT_frag_depth : enable', ''); newShader = this.replaceAll(newShader, 'defined(GL_EXT_frag_depth) &&', ''); otherReplaces.forEach(_ref2 => { var { oldStr, newStr } = _ref2; newShader = this.replaceAll(newShader, oldStr, newStr); }); return newShader; }; vs = change(vs, 'vs'); fs = change(fs, 'fs'); //console.log('成功替换为webgl2' ) return { vs, fs }; }, //three.js的shaderMaterial也有替换功能,搜 '#define gl_FragDepthEXT gl_FragDepth', load16bitPngTex(src, onLoad, onError, pixelFun) { //单通道无符号16位的png (正常图片是32位一个像素) var texture = new DataTexture(); fetch(src).then(response => { if (!response.ok) { console.log('loadFile失败', src); return onError && onError(); } return response.arrayBuffer(); }).then(arrayBuffer => { //文件数据 var png = UPNG.decode(arrayBuffer); //解析出像素数据 var pixelCount = png.width * png.height; var data = new Uint8Array(pixelCount * 4); var view = new DataView(data.buffer); for (var i = 0; i < pixelCount; i++) { //将uint16的只有两个byte的扩充到四个byte,因为贴图只支持4个byte的 var r = png.data[i * 2]; var g = png.data[i * 2 + 1]; data[i * 4 + 0] = g; //反了 data[i * 4 + 1] = r; data[i * 4 + 2] = data[i * 4 + 3] = 255; //无用值填充 if (pixelFun) { pixelFun(view.getUint16(i * 4, true), i, pixelCount); //原值 } } texture.image.data = data; texture.image.width = png.width; texture.image.height = png.height; texture.image.src = src; texture.needsUpdate = true; onLoad && onLoad(); }).catch(error => { onError && onError(error); }); return texture; } }; Potree.Common = Common; class View { //base constructor() { this.position = new Vector3(0, 0, 0); this.yaw = Math.PI / 4; this._pitch = -Math.PI / 4; this.radius = 1; this.maxPitch = Math.PI / 2; this.minPitch = -Math.PI / 2; } clone() { var c = new View(); c.yaw = this.yaw; c._pitch = this.pitch; c.radius = this.radius; c.maxPitch = this.maxPitch; c.minPitch = this.minPitch; return c; } get pitch() { return this._pitch; } set pitch(angle) { this._pitch = Math.max(Math.min(angle, this.maxPitch), this.minPitch); } get direction() { var dir = new Vector3(0, 1, 0); dir.applyAxisAngle(new Vector3(1, 0, 0), this.pitch); dir.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); return dir; } set direction(dir) { dir = dir.clone().normalize(); //add if (dir.x === 0 && dir.y === 0) { this.pitch = Math.PI / 2 * Math.sign(dir.z); //this.yaw = 0 //add:还是要指定一下, 否则不统一 } else { var yaw = Math.atan2(dir.y, dir.x) - Math.PI / 2; var pitch = Math.atan2(dir.z, Math.sqrt(dir.x * dir.x + dir.y * dir.y)); this.yaw = yaw; this.pitch = pitch; } } lookAt(t) { //setPivot var V; if (arguments.length === 1) { V = new Vector3().subVectors(t, this.position); } else if (arguments.length === 3) { V = new Vector3().subVectors(new Vector3(...arguments), this.position); } var radius = V.length(); var dir = V.normalize(); this.radius = radius; this.direction = dir; } getPivot() { return new Vector3().addVectors(this.position, this.direction.multiplyScalar(this.radius)); } getSide() { var side = new Vector3(1, 0, 0); side.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); return side; } pan(x, y) { var dir = new Vector3(0, 1, 0); dir.applyAxisAngle(new Vector3(1, 0, 0), this.pitch); dir.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); // let side = new THREE.Vector3(1, 0, 0); // side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); var side = this.getSide(); var up = side.clone().cross(dir); var pan = side.multiplyScalar(x).add(up.multiplyScalar(y)); this.position = this.position.add(pan); // this.target = this.target.add(pan); } translate(x, y, z) { var dir = new Vector3(0, 1, 0); dir.applyAxisAngle(new Vector3(1, 0, 0), this.pitch); dir.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); var side = new Vector3(1, 0, 0); side.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); var up = side.clone().cross(dir); var t = side.multiplyScalar(x).add(dir.multiplyScalar(y)).add(up.multiplyScalar(z)); this.position = this.position.add(t); } translateWorld(x, y, z) { this.position.x += x; this.position.y += y; this.position.z += z; } setView(position, target) { var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var callback = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; var endPosition = null; if (position instanceof Array) { endPosition = new Vector3(...position); } else if (position.x != null) { endPosition = position.clone(); } var endTarget = null; if (target instanceof Array) { endTarget = new Vector3(...target); } else if (target.x != null) { endTarget = target.clone(); } var startPosition = this.position.clone(); var startTarget = this.getPivot(); //const endPosition = position.clone(); //const endTarget = target.clone(); var easing = TWEEN.Easing.Quartic.Out; if (duration === 0) { this.position.copy(endPosition); this.lookAt(endTarget); } else { var value = { x: 0 }; var tween = new TWEEN.Tween(value).to({ x: 1 }, duration); tween.easing(easing); //this.tweens.push(tween); tween.onUpdate(() => { var t = value.x; //console.log(t); var pos = new Vector3((1 - t) * startPosition.x + t * endPosition.x, (1 - t) * startPosition.y + t * endPosition.y, (1 - t) * startPosition.z + t * endPosition.z); var target = new Vector3((1 - t) * startTarget.x + t * endTarget.x, (1 - t) * startTarget.y + t * endTarget.y, (1 - t) * startTarget.z + t * endTarget.z); this.position.copy(pos); this.lookAt(target); }); tween.start(); tween.onComplete(() => { if (callback) { callback(); } }); } } } ; var sid = 0; class ExtendView extends View { constructor() { super(); this.yaw = 0; //Math.PI / 4; // = 4dkk lon + 90 this._pitch = 0; //-Math.PI / 4; //上下旋转 = 4dkk lat this.sid = sid++; this.LookTransition = 'LookTransition' + this.sid; this.FlyTransition = 'FlyTransition' + this.sid; } //add------ applyToCamera(camera) { camera.position.copy(this.position); camera.rotation.copy(this.rotation); camera.updateMatrix(); camera.updateMatrixWorld(); //camera.matrixWorldInverse.copy(camera.matrixWorld).invert(); } get rotation() { var rotation = new Euler(); rotation.order = "ZXY"; rotation.x = Math.PI / 2 + this.pitch; rotation.z = this.yaw; return rotation; } set rotation(rotation) { if (rotation.y != 0) { //因为 rotation的y不一定是0 , 所以不能直接逆着get rotation写。 //console.error('set rotation y不为0!!!!?', rotation ) //过渡时因为quaternion lerp所以不为0。没办法了orz this.direction = new Vector3(0, 0, -1).applyEuler(rotation); //转回direction有损耗,在俯视时的(dir.x==dir.y==0), 丢失yaw信息从而 yaw无法获取(希望不要遇到这种情况,如果有的话,考虑先计算yaw,似乎好像可以算) } else { //尽量不用direction this.pitch = rotation.x - Math.PI / 2; this.yaw = rotation.z; } } get quaternion() { return new Quaternion().setFromEuler(this.rotation); } set quaternion(q) { this.rotation = new Euler().setFromQuaternion(q); } copy(a) { Common.CopyClassObject(this, a, { ignoreList: ['_listeners'] }); } clone() { return Common.CloneClassObject(this, { ignoreList: ['_listeners'] }); } //---------- setCubeView(dir) { switch (dir) { case "front": this.yaw = 0; this.pitch = 0; break; case "back": this.yaw = Math.PI; this.pitch = 0; break; case "left": this.yaw = -Math.PI / 2; this.pitch = 0; break; case "right": this.yaw = Math.PI / 2; this.pitch = 0; break; case "top": this.yaw = 0; this.pitch = -Math.PI / 2; break; case "bottom": this.yaw = -Math.PI; this.pitch = Math.PI / 2; break; } } /* pan (x, y) { let dir = new THREE.Vector3(0, 1, 0); dir.applyAxisAngle(new THREE.Vector3(1, 0, 0), this.pitch); dir.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); // let side = new THREE.Vector3(1, 0, 0); // side.applyAxisAngle(new THREE.Vector3(0, 0, 1), this.yaw); let side = this.getSide(); let up = side.clone().cross(dir); let pan = side.multiplyScalar(x).add(up.multiplyScalar(y)); this.position = this.position.add(pan); // this.target = this.target.add(pan); } */ pan(x, y) { //发现pan其实就是translate this.translate(x, 0, y); } translate(x, y, z, forceHorizon) { //相机方向 var dir = new Vector3(0, 1, 0); dir.applyAxisAngle(new Vector3(1, 0, 0), forceHorizon ? 0 : this.pitch); //上下角度 dir.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); //水平角度 var side = new Vector3(1, 0, 0); side.applyAxisAngle(new Vector3(0, 0, 1), this.yaw); //垂直于相机当前水平朝向的 左右方向 var up = side.clone().cross(dir); //垂直于相机当前水平朝向的 向上方向 var t = side.multiplyScalar(x) //x影响 左右分量 .add(dir.multiplyScalar(y)) //y影响 前后分量 .add(up.multiplyScalar(z)); //z影响 上下分量 this.position = this.position.add(t); if ((!math.closeTo(x, 0, 1e-2) || !math.closeTo(y, 0, 1e-2) || !math.closeTo(z, 0, 1e-2)) && Potree.settings.displayMode != 'showPanos') { this.cancelFlying('pos'); } this.restrictPos(); } translateWorld(x, y, z) { super.translateWorld(x, y, z); if ((!math.closeTo(x, 0, 1e-2) || !math.closeTo(y, 0, 1e-2) || !math.closeTo(z, 0, 1e-2)) && Potree.settings.displayMode != 'showPanos') { this.cancelFlying('pos'); } this.restrictPos(); } restrictPos(position) { //add if (this.limitBound) { (position || this.position).clamp(this.limitBound.min, this.limitBound.max); } } isFlying() { var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'all'; var a = transitions.getById(this.FlyTransition).length > 0; var b = transitions.getById(this.LookTransition).length > 0; return type == 'pos' ? a : type == 'rotate' ? b : a || b; } cancelFlying() { var type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'all'; var dealCancel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; //外界只能通过这个来cancel type == 'pos' ? transitions.cancelById(this.FlyTransition, dealCancel) : type == 'rotate' ? transitions.cancelById(this.LookTransition, dealCancel) : (transitions.cancelById(this.FlyTransition, dealCancel), transitions.cancelById(this.LookTransition, dealCancel)); //console.log('cancelFlying ' , this.sid, type) } setView() { var info = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // position, target, duration = 0, callback = null, onUpdate = null, Easing='', cancelFun this.cancelFlying(); var posWaitDone, rotWaitDone, dir; var posDone = () => { rotWaitDone || done(); posWaitDone = false; }; var rotDone = () => { if (endTarget) { this.lookAt(endTarget); //compute radius for orbitcontrol } else if (endQuaternion) { this.rotation = new Euler().setFromQuaternion(endQuaternion); } if (endYaw != void 0) { //前面两种在正俯视仰视时不准,故额外加一个这个 this.yaw = endYaw, this.pitch = endPitch; } //if(dir.x == 0 && dir.y == 0)this.yaw = 0 //统一一下 朝上的话是正的。朝下的一般不是0,会保留一个接近0的小数所以不用管 posWaitDone || done(); rotWaitDone = false; }; var done = () => { //一定要旋转和位移都结束了才能执行 var f = () => { this.position.copy(endPosition); //因为延时 后control的update会导致位置改变 info.callback && info.callback(); this.dispatchEvent('flyingDone'); }; if (info.duration) { setTimeout(f, 10); //延迟是为了使isFlying先为false 1有概率不够 } else { f(); //有的需要迅速执行回调 } }; var endPosition = new Vector3().copy(info.position); var startPosition = this.position.clone(); var startQuaternion, endQuaternion, endTarget = info.target && new Vector3().copy(info.target), endYaw, startYaw, endPitch, startPitch; this.restrictPos(endPosition); if (info.endYaw == void 0) { if (info.target) { endQuaternion = math.getQuaFromPosAim(endPosition, endTarget); //若为垂直,会自动偏向x负的方向 } else if (info.quaternion) { endQuaternion = info.quaternion.clone(); } if (endQuaternion && math.closeTo(Math.abs(this.direction.z), 1, 1e-4)) { //在垂直的视角下的quaternion刚开始突变的厉害,这时候可能渐变yaw比较好(如俯视时点击测量线) var a = this.clone(); a.quaternion = endQuaternion; info.endYaw = a.yaw; info.endPitch = a.pitch; //console.log('turn to yaw') } } if (info.endYaw != void 0) { startPitch = this.pitch; endPitch = info.endPitch; startYaw = this.yaw; endYaw = info.endYaw; if (Math.abs(startYaw - endYaw) > Math.PI) { //如果差距大于半个圆,就要反个方向转(把大的那个数字减去360度) startYaw > endYaw ? startYaw -= Math.PI * 2 : endYaw -= Math.PI * 2; } //console.log('startYaw', startYaw, 'endYaw', endYaw) } if (endQuaternion) { startQuaternion = this.quaternion; } if (!info.duration) { this.position.copy(endPosition); this.restrictPos(); posWaitDone = true, rotWaitDone = true; info.onUpdate && info.onUpdate(1); posDone(); rotDone(); } else { info.onUpdate && info.onUpdate(0); //初始化progress var posChange = !this.position.equals(endPosition); if (posChange) { posWaitDone = true; transitions.start(lerp.vector(this.position, endPosition, (pos, progress, delta) => { info.onUpdate && info.onUpdate(progress, delta); }), info.duration, posDone, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine, null, this.FlyTransition, () => { //中途取消 if (rotWaitDone) { /* endPosition = new THREE.Vector3().copy(this.position)//更改旋转的endQuaternion endQuaternion = math.getQuaFromPosAim(endPosition,endTarget) */ //直接改变endQuaternion会突变,所以还是cancel吧 this.cancelFlying('rotate'); } else { this.dispatchEvent('flyCancel'); } posWaitDone = false; info.cancelFun && info.cancelFun(); }, info.ignoreFirstFrame); } if (endQuaternion || endYaw != void 0) { rotWaitDone = true; transitions.start((progress, delta) => { if (endYaw != void 0) { this.yaw = startYaw * (1 - progress) + endYaw * progress; this.pitch = startPitch * (1 - progress) + endPitch * progress; } else { var quaternion = new Quaternion().copy(startQuaternion); lerp.quaternion(quaternion, endQuaternion)(progress); //在垂直的视角下的角度突变的厉害,这时候可能渐变yaw比较好 this.quaternion = quaternion; //console.log(quaternion,this.yaw) } posChange || info.onUpdate && info.onUpdate(progress, delta); }, info.duration, rotDone, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine, null, this.LookTransition, () => { //中途取消 rotWaitDone = false; info.cancelFun && info.cancelFun(); this.dispatchEvent('flyCancel'); }, info.ignoreFirstFrame); } if (!posWaitDone && !rotWaitDone) { //已经到达目标 info.onUpdate && info.onUpdate(1); done(); } /* transitions.start(lerp.vector(this.position, endPosition, (pos, progress)=>{ let t = progress if(endQuaternion){ let quaternion = (new THREE.Quaternion()).copy(startQuaternion) lerp.quaternion(quaternion, endQuaternion)(progress), this.rotation = new THREE.Euler().setFromQuaternion(quaternion) } this.restrictPos() //console.log('setView flying') info.onUpdate && info.onUpdate(t)//add }), info.duration, done, 0, info.Easing ? easing[info.Easing] : easing.easeInOutSine ,null, this.LookTransition, info.cancelFun); //easeInOutQuad */ } } //平移Ortho相机 moveOrthoCamera(viewport, info, duration, easeName) { //boundSize优先于endZoom。 var camera = info.camera || viewport.camera; var startZoom = camera.zoom; var endPosition = info.endPosition; var boundSize = info.boundSize; var endZoom = info.endZoom; var margin = info.margin || { x: 0, y: 0 }; /* 200 */ //像素 var _onUpdate = info.onUpdate; if (info.bound) { //需要修改boundSize以适应相机的旋转,当相机不在xy水平面上朝向z时 endPosition = endPosition || info.bound.getCenter(new Vector3()); var matrixRot = new Matrix4().makeRotationFromEuler(this.rotation).invert(); var boundingBox = info.bound.clone().applyMatrix4(matrixRot); boundSize = boundingBox.getSize(new Vector3()); } if (boundSize && boundSize.x == 0 && boundSize.y == 0) { boundSize.set(1, 1); //避免infinity } this.setView(Object.assign(info, { position: endPosition, duration, onUpdate: (progress, delta) => { if (boundSize || endZoom) { if (boundSize) { var aspect = boundSize.x / boundSize.y; var w, h; if (camera.aspect > aspect) { //视野更宽则用bound的纵向来决定 h = boundSize.y; endZoom = (viewport.resolution.y - margin.y) / h; //注意,要在resolution不为0时执行 } else { w = boundSize.x; endZoom = (viewport.resolution.x - margin.x) / w; } //onUpdate时更新endzoom是因为画布大小可能更改 } this.zoom = camera.zoom = endZoom * progress + startZoom * (1 - progress); //view里也加一下,有些地方需要记录,如截图 camera.updateProjectionMatrix(); _onUpdate && _onUpdate(progress, delta); } }, Easing: easeName })); } zoomOrthoCamera(camera, endZoom, pointer, duration, onProgress) { //定点缩放 var startZoom = camera.zoom; var pointerPos = new Vector3(pointer.x, pointer.y, 0.5); transitions.start(progress => { var oldPos = pointerPos.clone().unproject(camera); this.zoom = camera.zoom = endZoom * progress + startZoom * (1 - progress); camera.updateProjectionMatrix(); var newPos = pointerPos.clone().unproject(camera); //定点缩放, 恢复一下鼠标所在位置的位置改变量 var moveVec = new Vector3().subVectors(newPos, oldPos); camera.position.sub(moveVec); this.position.copy(camera.position); onProgress && onProgress(); }, duration, null /* done */, 0, easing.easeInOutSine, null, "zoomInView" /* , info.cancelFun */); } tranCamera(viewport, info, duration, easeName) { viewport.camera = info.midCamera; //viewport.camera.matrixWorld = info.endCamera.matrixWorld //viewer.setCameraMode(CameraMode.ORTHOGRAPHIC) info.midCamera.projectionMatrix.copy(info.startCamera.projectionMatrix); var onUpdate = info.onUpdate; info.onUpdate = (progress, delta) => { lerp.matrix4(info.midCamera.projectionMatrix, info.endCamera.projectionMatrix)(progress); onUpdate && onUpdate(progress, delta); }; var callback = info.callback; info.callback = () => { viewport.camera = info.endCamera; viewer.scene.measurements.forEach(e => { Potree.Utils.updateVisible(e, 'tranCamera', true); }); this.applyToCamera(viewport.camera); viewer.dispatchEvent({ type: 'camera_changed', viewport: viewer.mainViewport, changeInfo: {} }); //update sprite callback && callback(); }; //info.forbitCancel = true info.camera = info.endCamera; if (info.camera.type == "OrthographicCamera") { this.moveOrthoCamera(viewport, info, duration, easeName); } else { this.setView(Object.assign(info, { duration })); } } } ; var view = new ExtendView(); var math = { getBaseLog(x, y) { //返回以 x 为底 y 的对数(即 logx y) . Math.log 返回一个数的自然对数 return Math.log(y) / Math.log(x); }, convertVector: { ZupToYup: function ZupToYup(e) { //navvis -> 4dkk return new Vector3(e.x, e.z, -e.y); }, YupToZup: function YupToZup(e) { //4dkk -> navvis return new Vector3(e.x, -e.z, e.y); } }, convertQuaternion: { ZupToYup: function ZupToYup(e) { //navvis -> 4dkk //不同于convertVisionQuaternion var rotation = new Euler(-Math.PI / 2, 0, 0); var quaternion = new Quaternion().setFromEuler(rotation); return e.clone().premultiply(quaternion); //return new THREE.Quaternion(e.x,e.z,-e.y,e.w).multiply((new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(1,0,0), THREE.Math.degToRad(90))) }, YupToZup: function YupToZup(e) { //4dkk -> navvis var rotation = new Euler(Math.PI / 2, 0, 0); var quaternion = new Quaternion().setFromEuler(rotation); return e.clone().premultiply(quaternion); } }, convertVisionQuaternion: function convertVisionQuaternion(e) { return new Quaternion(e.x, e.z, -e.y, e.w).multiply(new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), MathUtils.degToRad(90))); }, invertVisionQuaternion: function invertVisionQuaternion(e) { //反转给算法部 var a = e.clone().multiply(new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), MathUtils.degToRad(-90))); return new Quaternion(a.x, -a.z, a.y, a.w); }, //------------ getVec2Angle: function getVec2Angle(dir1, dir2) { return Math.acos(MathUtils.clamp(this.getVec2Cos(dir1, dir2), -1, 1)); }, getVec2Cos: function getVec2Cos(dir1, dir2) { return dir1.dot(dir2) / dir1.length() / dir2.length(); }, getAngle: function getAngle(vec1, vec2) { var axis = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'z'; //带方向的角度 vector3 if (!vec1.isVector3) { vec1 = new Vector3(vec1.x, vec1.y, 0); vec2 = new Vector3(vec2.x, vec2.y, 0); } var angle = vec1.angleTo(vec2); var axis_ = vec1.clone().cross(vec2); if (typeof axis == 'string') { if (axis_[axis] < 0) { angle *= -1; } } else { //vector3 if (axis_.dot(axis) < 0) { angle *= -1; } } return angle; }, closeTo: function closeTo(a, b) { var precision = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1e-6; var f = (a, b) => { return Math.abs(a - b) < precision; }; if (typeof a == 'number') { return f(a, b); } else { var judge = name => { if (a[name] == void 0) return true; //有值就判断,没值就不判断 else return f(a[name], b[name]); }; return judge('x') && judge('y') && judge('z') && judge('w'); } }, toPrecision: function toPrecision(e, t) { //xzw change 保留小数 var f = function f(e, t) { var i = Math.pow(10, t); return Math.round(e * i) / i; }; if (e instanceof Array) { for (var s = 0; s < e.length; s++) { e[s] = f(e[s], t); } return e; } else if (e instanceof Object) { for (var s in e) { e[s] = f(e[s], t); } return e; } else if (typeof e == 'number') { return f(e, t); } else { return e; } }, isEmptyQuaternion: function isEmptyQuaternion(e) { return 0 === Math.abs(e.x) && 0 === Math.abs(e.y) && 0 === Math.abs(e.z) && 0 === Math.abs(e.w); }, projectPositionToCanvas: function projectPositionToCanvas(e, t, i) { i = i || new Vector3(), i.copy(e); var r = .5 * $('#player').width(), o = .5 * $('#player').height(); return i.project(t), i.x = i.x * r + r, i.y = -(i.y * o) + o, i; }, handelPadResize: false, /* handelPadding : function () { //去除player左边和上面的宽高,因为pc的player左上有其他element 许钟文 var pads = [];//记录下来避免反复计算 var index = []; var resetPad = function(){ pads = []; index = []; math.handelPadResize = false; //switchview时resized为true } if(config.isEdit && !config.isMobile){ window.addEventListener('resize',resetPad); } return function(x, y, domE){ if(!config.isEdit || config.isMobile) { return { x: x, y: y } } if(this.handelPadResize)resetPad(); domE = domE || $('#player')[0]; var pad; var i = index.indexOf(domE); if (i == -1){ index.push(domE); pad = { x: this.getOffset("left", domE), y: this.getOffset("top", domE) } pads.push(pad) } else pad = pads[i]; return { x: x - pad.x, y: y - pad.y } } }(), */ getOffset: function getOffset(type, element, parent) { //获取元素的边距 许钟文 var offset = type == "left" ? element.offsetLeft : element.offsetTop; if (!parent) parent = $("body")[0]; while (element = element.offsetParent) { if (element == parent) break; offset += type == "left" ? element.offsetLeft : element.offsetTop; } return offset; }, constrainedTurn: function constrainedTurn(e) { var t = e % (2 * Math.PI); return t = t > Math.PI ? t -= 2 * Math.PI : t < -Math.PI ? t += 2 * Math.PI : t; }, getFOVDotThreshold: function getFOVDotThreshold(e) { return Math.cos(MathUtils.degToRad(e / 2)); }, transform2DForwardVectorByCubeFace: function transform2DForwardVectorByCubeFace(e, t, i, n) { switch (e) { case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_X: i.set(1, t.y, t.x); break; case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_X: i.set(-1, t.y, -t.x); break; case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Y: i.set(-t.x, 1, -t.y); break; case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: i.set(-t.x, -1, t.y); break; case GLCubeFaces.GL_TEXTURE_CUBE_MAP_POSITIVE_Z: i.set(-t.x, t.y, 1); break; case GLCubeFaces.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: i.set(t.x, t.y, -1); } n && i.normalize(); }, getFootPoint: function getFootPoint(oldPos, p1, p2, restricInline) { //找oldPos在线段p1, p2上的垂足 /* if(isWorld){//输出全局坐标 需要考虑meshGroup.position p1 = p1.clone(); p2 = p2.clone(); p1.y += mainDesign.meshGroup.position.y; p2.y += mainDesign.meshGroup.position.y; } */ if (p1.equals(p2)) return p1.clone(); var op1 = oldPos.clone().sub(p1); var p1p2 = p1.clone().sub(p2); var p1p2Len = p1p2.length(); var leftLen = op1.dot(p1p2) / p1p2Len; var pos = p1.clone().add(p1p2.multiplyScalar(leftLen / p1p2Len)); if (restricInline && pos.clone().sub(p1).dot(pos.clone().sub(p2)) > 0) { //foot不在线段上 if (pos.distanceTo(p1) < pos.distanceTo(p2)) pos = p1.clone();else pos = p2.clone(); } return pos; }, /** * 计算多边形的重心 * @param {*} points */ getCenterOfGravityPoint: function getCenterOfGravityPoint(mPoints) { var area = 0.0; //多边形面积 var Gx = 0.0, Gy = 0.0; // 重心的x、y for (var i = 1; i <= mPoints.length; i++) { var ix = mPoints[i % mPoints.length].x; var iy = mPoints[i % mPoints.length].y; var nx = mPoints[i - 1].x; var ny = mPoints[i - 1].y; var temp = (ix * ny - iy * nx) / 2.0; area += temp; Gx += temp * (ix + nx) / 3.0; Gy += temp * (iy + ny) / 3.0; } Gx = Gx / area; Gy = Gy / area; return { x: Gx, y: Gy }; }, getBound: function getBound(ring) { var bound = new Box2(); for (var j = 0, len = ring.length; j < len; j++) { bound.expandByPoint(ring[j]); } return bound; }, isPointInArea: function isPointInArea(ring, holes, point, ifAtLine) { //判断点是否在某个环内, 若传递了holes代表还要不能在内环内 var bound = this.getBound(ring); if (point.x < bound.min.x || point.x > bound.max.x || point.y < bound.min.y || point.y > bound.max.y) return false; var inside = false; var x = point.x, y = point.y; for (var i = 0, j = ring.length - 1; i < ring.length; j = i++) { var xi = ring[i].x, yi = ring[i].y; var xj = ring[j].x, yj = ring[j].y; if ((xi - x) * (yj - y) == (xi - x) * (yi - y) && x >= Math.min(xi, xj) && x <= Math.max(xi, xj) //xzw add && y >= Math.min(yi, yj) && y <= Math.max(yi, yj)) { //return !!ifAtLine;//在线段上,则判断为…… (默认在外) return { atLine: true }; } if (yi > y != yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi) { inside = !inside; } } if (inside && holes) { return !holes.some(ring => this.isPointInArea(ring, null, point, ifAtLine)); //不能存在于任何一个二级内环内 } else { return inside; } }, getArea: function getArea(ring) { //求面积 顺时针为正 来自three shape for (var t = ring.length, i = 0, n = t - 1, r = 0; r < t; n = r++) i += ring[n].x * ring[r].y - ring[r].x * ring[n].y; return -.5 * i; }, getVolume: function getVolume(faceArr) {//求三角多面体的体积。和求面积同理都用鞋带计算法 https://blog.csdn.net/weixin_43414513/article/details/123758897 //问题:怎么确定方向 //每个三角形的顺序必须是右手螺旋法则指向物体外(外向里看为逆时针),分别为ABC,则每个三角形和原点O构成的三棱锥体积为 (OA cross OB) dot(OC) / 6 . 结果一般朝向原点的为负,反之为正 }, isInBetween: function isInBetween(a, b, c, precision) { // 如果b几乎等于a或c,返回false.为了避免浮点运行时两值几乎相等,但存在相差0.00000...0001的这种情况出现使用下面方式进行避免 /* if (Math.abs(a - b) < 0.000001 || Math.abs(b - c) < 0.000001) { return false; } return (a <= b && b <= c) || (c <= b && b <= a);*/ //更改:如果b和a或c中一个接近 就算在a和c之间 return a <= b && b <= c || c <= b && b <= a || this.closeTo(a, b, precision) || this.closeTo(b, c, precision); }, ifPointAtLineBound: function ifPointAtLineBound(point, linePoints, precision) { //待验证 横线和竖线比较特殊 return math.isInBetween(linePoints[0].x, point.x, linePoints[1].x, precision) && math.isInBetween(linePoints[0].y, point.y, linePoints[1].y, precision); }, isLineIntersect: function isLineIntersect(line1, line2, notSegment, precision) { //线段和线段是否有交点. notSegment代表是直线而不是线段 var a1 = line1[1].y - line1[0].y; var b1 = line1[0].x - line1[1].x; var c1 = a1 * line1[0].x + b1 * line1[0].y; //转换成一般式: Ax+By = C var a2 = line2[1].y - line2[0].y; var b2 = line2[0].x - line2[1].x; var c2 = a2 * line2[0].x + b2 * line2[0].y; // 计算交点 var d = a1 * b2 - a2 * b1; // 当d==0时,两线平行 if (d == 0) { return false; } else { var x = (b2 * c1 - b1 * c2) / d; var y = (a1 * c2 - a2 * c1) / d; // 检测交点是否在两条线段上 /* if (notSegment || (isInBetween(line1[0].x, x, line1[1].x) || isInBetween(line1[0].y, y, line1[1].y)) && (isInBetween(line2[0].x, x, line2[1].x) || isInBetween(line2[0].y, y, line2[1].y))) { return {x,y}; } */ if (notSegment || math.ifPointAtLineBound({ x, y }, line1, precision) && math.ifPointAtLineBound({ x, y }, line2, precision)) { return { x, y }; } } }, getNormal2d: function getNormal2d() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //获取二维法向量 方向向内 var x, y, x1, y1; //line2d的向量 if (o.vec) { x1 = o.vec.x; y1 = o.vec.y; } else { x1 = o.p1.x - o.p2.x; y1 = o.p1.y - o.p2.y; } //假设法向量的x或y固定为1或-1 if (y1 != 0) { x = 1; y = -(x1 * x) / y1; } else if (x1 != 0) { //y如果为0,正常情况x不会是0 y = 1; x = -(y1 * y) / x1; } else { console.log("两个点一样"); return null; } //判断方向里或者外: var vNormal = new Vector3(x, 0, y); var vLine = new Vector3(x1, 0, y1); var vDir = vNormal.cross(vLine); if (vDir.y > 0) { x *= -1; y *= -1; } return new Vector2(x, y).normalize(); }, getQuaBetween2Vector: function getQuaBetween2Vector(oriVec, newVec, upVec) { //获取从oriVec旋转到newVec可以应用的quaternion var angle = oriVec.angleTo(newVec); var axis = oriVec.clone().cross(newVec).normalize(); //两个up之间 if (axis.length() == 0) { //当夹角为180 或 0 度时,得到的axis为(0,0,0),故使用备用的指定upVec return new Quaternion().setFromAxisAngle(upVec, angle); } return new Quaternion().setFromAxisAngle(axis, angle); }, /* getQuaBetween2Vector2 : function(oriVec, newVec ){//not camera var _ = (new THREE.Matrix4).lookAt( oriVec, new THREE.Vector3, new THREE.Vector3(0,1,0)) var aimQua = (new THREE.Quaternion).setFromRotationMatrix(_) var _2 = (new THREE.Matrix4).lookAt( newVec, new THREE.Vector3, new THREE.Vector3(0,1,0)) var aimQua2 = (new THREE.Quaternion).setFromRotationMatrix(_2) return aimQua2.multiply(aimQua.clone().inverse()) } */ getQuaByAim: function getQuaByAim(aim) { var center = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Vector3(); var forward = new Vector3(0, 1, 0); var qua1 = new Quaternion().setFromUnitVectors(forward, aim.clone().sub(center).normalize()); /* var _ = (new THREE.Matrix4).lookAt(center,aim, new THREE.Vector3(0,1,0)); let qua2 = (new THREE.Quaternion).setFromRotationMatrix(_); let rot1 = new THREE.Euler().setFromQuaternion(qua1) let rot2 = new THREE.Euler().setFromQuaternion(qua2) //奇怪,qua2怎么都不对 console.log(rot1,rot2) */ return qua1; }, getAimByQua: function getAimByQua(quaternion) { var center = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Vector3(); return new Vector3(0, 0, -1).applyQuaternion(quaternion).add(center); }, getScaleForConstantSize: function () { //获得规定二维大小的mesh的scale值。可以避免因camera的projection造成的mesh视觉大小改变。 来源:tag.updateDisc var w; var i = new Vector3(), o = new Vector3(), l = new Vector3(), c = new Vector3(), h = new Vector3(); return function () { var op = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (op.width2d) w = op.width2d; //如果恒定二维宽度 else { //否则考虑上距离,加一丢丢近大远小的效果 var currentDis, nearBound, farBound; if (op.camera.type == "OrthographicCamera") { currentDis = 200 / op.camera.zoom; //(op.camera.right - op.camera.left) / op.camera.zoom } else { currentDis = op.position.distanceTo(op.camera.position); } w = op.maxSize - (op.maxSize - op.minSize) * MathUtils.smoothstep(currentDis, op.nearBound, op.farBound); //maxSize : mesh要表现的最大像素宽度; nearBound: 最近距离,若比nearBound近,则使用maxSize } i.copy(op.position).project(op.camera); //tag中心在屏幕上的二维坐标 o.set(op.resolution.x / 2, op.resolution.y / 2, 1).multiply(i); //转化成px -w/2 到 w/2的范围 l.set(w / 2, 0, 0).add(o); //加上tag宽度的一半 c.set(2 / op.resolution.x, 2 / op.resolution.y, 1).multiply(l); //再转回 -1 到 1的范围 h.copy(c).unproject(op.camera); //再转成三维坐标,求得tag边缘的位置 var g = h.distanceTo(op.position); //就能得到tag的三维半径 //这里使用的都是resolution2, 好处是手机端不会太小, 坏处是pc更改网页显示百分比时显示的大小会变(或许可以自己算出设备真实的deviceRatio, 因window.screen是不会改变的),但考虑到用户可以自行调节字大小也许是好的 return g; //可能NAN 当相机和position重叠时 }; }(), //W , H, left, top分别是rect的宽、高、左、上 getCrossPointAtRect: function getCrossPointAtRect(p1, aim, W, H, left, top) { //求射线p1-aim在rect边界上的交点,其中aim在rect范围内,p1则不一定(交点在aim这边的延长线上) var x, y, borderX; var r = (aim.x - p1.x) / (aim.y - p1.y); //根据相似三角形原理先求出这个比值 var getX = function getX(y) { return r * (y - p1.y) + p1.x; }; var getY = function getY(x) { return 1 / r * (x - p1.x) + p1.y; }; if (aim.x >= p1.x) { borderX = W + left; } else { borderX = left; } x = borderX; y = getY(x); if (y < top || y > top + H) { if (y < top) { y = top; } else { y = top + H; } x = getX(y); } return new Vector2(x, y); }, getDirFromUV: function getDirFromUV(uv) { //获取dir 反向计算 - - 二维转三维比较麻烦 var dirB; //所求 单位向量 var y = Math.cos(uv.y * Math.PI); //uv中纵向可以直接确定y, 根据上面getUVfromDir的反向计算 // 故 uv.y * Math.PI 就是到垂直线(向上)的夹角 var angle = 2 * Math.PI * uv.x - Math.PI; //x/z代表的是角度 var axisX, axisZ; //axis为1代表是正,-1是负数 if (-Math.PI <= angle && angle < 0) { axisX = -1; //下半圆 } else { axisX = 1; //上半圆 } if (-Math.PI / 2 <= angle && angle < Math.PI / 2) { axisZ = 1; //右半圆 } else { axisZ = -1; //左半圆 } var XDivideZ = Math.tan(angle); var z = Math.sqrt((1 - y * y) / (1 + XDivideZ * XDivideZ)); var x = XDivideZ * z; if (z * axisZ < 0) { //异号 z *= -1; x *= -1; if (x * axisX < 0) { // console.log("wrong!!!!!??????????") } } x *= -1; //计算完成后这里不能漏掉 *= -1 dirB = this.convertVector.YupToZup(new Vector3(x, y, z)); //理想状态下x和z和anotherDir相同 return dirB; }, getUVfromDir: function getUVfromDir(dir) { //获取UV 同shader里的计算 var dir = this.convertVector.ZupToYup(dir); dir.x *= -1; //计算前这里不能漏掉 *= -1 见shader var tx = Math.atan2(dir.x, dir.z) / (Math.PI * 2.0) + 0.5; //atan2(y,x) 返回从 X 轴正向逆时针旋转到点 (x,y) 时经过的角度。区间是-PI 到 PI 之间的值 var ty = Math.acos(dir.y) / Math.PI; return new Vector2(tx, ty); //理想状态下tx相同 }, getDirByLonLat: function getDirByLonLat(lon, lat) { var dir = new Vector3(); var phi = MathUtils.degToRad(90 - lat); var theta = MathUtils.degToRad(lon); dir.x = Math.sin(phi) * Math.cos(theta); dir.y = Math.cos(phi); dir.z = Math.sin(phi) * Math.sin(theta); return dir; } //0,0 => (1,0,0) 270=>(0,0,-1) , projectPointAtPlane: function projectPointAtPlane() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //获取一个点在一个面上的投影 {facePoints:[a,b,c], point:} var plane = new Plane().setFromCoplanarPoints(...o.facePoints); return plane.projectPoint(o.point, new Vector3()); }, getPolygonsMixedRings: function getPolygonsMixedRings(polygons, onlyGetOutRing) { //{points:[vector2,...],holes:[[],[]]} var points = []; var lines = []; var i = 0; polygons.forEach(e => points.push(...e.map(a => new Vector2().copy(a)))); polygons.forEach((ps, j) => { var length = ps.length; var index = 0; while (index < length) { lines.push({ p1: index + i, p2: (index + 1) % length + i }); index++; } i += length; }); points.forEach((p, j) => { p.id = j; }); var rings = searchRings({ points, lines, onlyGetOutRing }); //console.log(rings) rings = rings.filter(e => e.closetParent == void 0); // 子环不加,被外环包含了 return rings; }, getQuaFromPosAim(position, target) { /* let matrix = (new THREE.Matrix4).lookAt(position, target, new THREE.Vector3(0,0,1)) //这里垂直的话会默认给一个右向所以不这么写 return (new THREE.Quaternion).setFromRotationMatrix(matrix) */ view.direction = new Vector3().subVectors(target, position); return view.quaternion; }, getYawPitchByQua(qua) { view.quaternion = qua; return { yaw: view.yaw, pitch: view.pitch }; }, getBoundByPoints(points, minSize) { var bound = new Box3(); points.forEach(point => { bound.expandByPoint(point); }); var center = bound.getCenter(new Vector3()); if (minSize) { var minBound = new Box3().setFromCenterAndSize(center, minSize); bound.union(minBound); } return { bounding: bound, size: bound.getSize(new Vector3()), center }; }, /* linearClamp(value, x1,x2, y1, y2){//x为bound.min, bound.max value = THREE.Math.clamp(value, x1,x2) return y1 + ( y2 - y1) * (value - x1) / (x2 - x1) } */ linearClamp(value, xArr, yArr) { //xArr需要按顺序从小到大,yArr对应xArr中的值 var len = xArr.length; if (value <= xArr[0]) return yArr[0]; if (value >= xArr[len - 1]) return yArr[len - 1]; var i = 0; while (++i < len) { if (value < xArr[i]) { var x1 = xArr[i - 1], x2 = xArr[i], y1 = yArr[i - 1], y2 = yArr[i]; value = y1 + (y2 - y1) * (value - x1) / (x2 - x1); break; } } return value; }, getKelvinFromCelsius(c) { //摄氏度->开尔文 return c + 273.1; //273.15 算法的值是整数然后除以10所以只保留一位小数 }, getCelsiusFromKelvin(c) { return c - 273.1; } }; /* 如何将若干个点拟合出线段 // 假设你有一个点数组,每个点表示为一个包含x和y坐标的对象 const points = [ { x: 1, y: 2 }, { x: 2, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 5 }, // ... 更多点 ]; // 定义用于计算最小二乘法参数的函数 function leastSquares( ) { let sumX = 0; let sumY = 0; let sumXY = 0; let sumX2 = 0; let n = points.length; for (const point of points) { sumX += point.x; sumY += point.y; sumXY += point.x * point.y; sumX2 += point.x * point.x; } const k = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); //不知道为何这样算,自己解不出来(使点到直线距离之和平方最小),但测了下似乎是准的 const b = (sumY - k * sumX) / n; return { k, b }; } // 使用上述函数计算拟合直线的参数 const { k, b } = leastSquares(points); // 现在你可以使用这些参数来表示拟合出的线段 console.log(`拟合线段的方程为: y = ${k}x + ${b}`); */ Potree.math = math; function mobileVersion(e, t) { //ios的版本 var i = window.navigator.userAgent, n = i.match(e); return n = n ? n[1].split(t) : [], { major: parseInt(n[0]) || 0, minor: parseInt(n[1]) || 0, patch: parseInt(n[2]) || 0 }; } var browser = { isFullscreen: function isFullscreen() { return document.fullscreenElement || document.mozFullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement; }, supportsFullscreen: function supportsFullscreen() { return document.fullscreenEnabled || document.mozFullscreenEnabled || document.mozFullScreenEnabled || document.webkitFullscreenEnabled || document.msFullscreenEnabled; }, isPointerLocked: function isPointerLocked() { return document.pointerLockElement || document.mozPointerLockElement || document.webkitPointerLockElement; }, requestFullscreen: function requestFullscreen(dom, t) { dom.requestFullscreen ? dom.requestFullscreen() : dom.mozRequestFullScreen ? dom.mozRequestFullScreen() : dom.webkitRequestFullscreen ? dom.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT) : dom.msRequestFullscreen && dom.msRequestFullscreen(), t && $(document).on("fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange", browser.requestPointerLock); }, requestPointerLock: function requestPointerLock() { var e; if (document.fullscreenElement) e = document.fullscreenElement();else if (document.mozFullscreenElement) e = document.mozFullscreenElement();else if (document.mozFullScreenElement) e = document.mozFullScreenElement();else { if (!document.webkitFullscreenElement) return; e = document.webkitFullscreenElement(); } ; e.requestPointerLock = e.requestPointerLock || e.mozRequestPointerLock || e.webkitRequestPointerLock, e.requestPointerLock(), $(document).off("fullscreenchange webkitfullscreenchange mozfullscreenchange MSFullscreenChange", this); }, exitPointerLock: function exitPointerLock() { ; document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock || document.webkitExitPointerLock, document.exitPointerLock(); }, exitFullscreen: function exitFullscreen() { document.exitFullscreen ? document.exitFullscreen() : document.msExitFullscreen ? document.msExitFullscreen() : document.mozCancelFullScreen ? document.mozCancelFullScreen() : document.webkitExitFullscreen && document.webkitExitFullscreen(); }, details: function details() { var e = navigator.userAgent.match("(Firefox|Chrome|Safari)/([\\d]+)"); return e ? { name: e[1], version: parseInt(e[2]), platform: navigator.platform } : {}; }, is: function is(e) { return this.details() && this.details().name === e; }, inIframe: function inIframe() { return window.parent !== window; }, aspectRatio: function aspectRatio($elem) { $elem = $elem || $("#player"); var e = $elem.width() / $elem.height(); return isFinite(e) ? e : 0; }, userAgent: function userAgent() { return window.navigator.userAgent; }, isMobile: function isMobile() { var e = navigator.userAgent || navigator.vendor || window.opera; return /(android|bb\d+|meego).+mobile|android|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od|ad)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(e) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(e.substr(0, 4)); }, isLandscape: function isLandscape() { return this.isMobile && this.aspectRatio() > 1; }, isSmallScreen: function isSmallScreen() { var e = screen.width / window.devicePixelRatio; return e < 240; }, detectIE: function detectIE() { var e = window.navigator.userAgent, t = e.indexOf("MSIE "); return t !== -1 || !!navigator.userAgent.match(/Trident.*rv\:11\./); }, detectSafari: function detectSafari() { var e = window.navigator.userAgent, t = e.indexOf("Safari"); return t !== -1 && !this.detectOpera() && !this.detectChrome(); //xzw add detectOpera }, detectFirefox: function detectFirefox() { var e = window.navigator.userAgent; return e.indexOf("Firefox") !== -1; }, detectChrome: function detectChrome() { var e = window.navigator.userAgent; return e.indexOf("Chrome") !== -1 && !this.detectOpera(); }, detectOpera: function detectOpera() { var e = window.navigator.userAgent; return e.indexOf("OPR") !== -1; }, detectIOS: function detectIOS() { return this.detectIPhone() || this.detectIPad() || this.detectIPod(); }, detectIPad: function detectIPad() { var e = window.navigator.userAgent, t = /iPad/; return t.test(e); }, detectIPod: function detectIPod() { var e = window.navigator.userAgent, t = /iPod/; return t.test(e); }, detectIPhone: function detectIPhone() { var e = window.navigator.userAgent, t = /iPhone/; return t.test(e); }, detectAndroid: function detectAndroid() { var e = window.navigator.userAgent; return e.indexOf("Android") !== -1; }, detectAndroidMobile: function detectAndroidMobile() { var e = window.navigator.userAgent; return this.detectAndroid() && e.indexOf("Mobile") !== -1; }, detectSamsungNative: function detectSamsungNative() { var e = window.navigator.userAgent; return e.indexOf("SM-G900H") !== -1 || e.indexOf("GT-I9500") !== -1 || e.indexOf("SM-N900") !== -1; }, detectSamsungS6: function detectSamsungS6() { var e = window.navigator.userAgent; return e.indexOf("SM-G92") !== -1; }, /************************************************************徐世廷*************************************************************/ detectHUAWEI5X: function detectHUAWEI5X() { return -1 !== window.navigator.userAgent.indexOf("KIW-TL00H"); }, /*******************************************************************************************************************************/ detectWebVR: function detectWebVR() { return !(!window.navigator.getVRDisplays || !window.VRDisplay); }, getVRDisplay: function getVRDisplay() { var e = $.Deferred(); return this.detectWebVR() ? (navigator.getVRDisplays().then(function (t) { t.length >= 1 && e.resolve(t[0]), e.reject(null); }), e) : e.reject(null); }, iosVersion: function iosVersion() { if (!this.detectIOS()) throw new DeviceMismatchException("Did not detect an iDevice"); var e = /((?:\d+\_?){1,3}) like Mac OS/, t = "_"; return mobileVersion(e, t); }, androidVersion: function androidVersion() { if (!this.detectAndroid()) throw new DeviceMismatchException("Did not detect an Android based device"); var e = /Android ((?:\d+\.?){1,3})/, t = "."; return mobileVersion(e, t); }, valueFromCookie: function valueFromCookie(e, t) { var i = new RegExp(e + "=([0-9a-f]+)(; ?|$)").exec(document.cookie); if (!i) return t; var n = i[1]; return "boolean" == typeof t ? "true" === n || "1" === n : "number" == typeof t ? parseFloat(n) : n; }, valueFromHash: function valueFromHash(e, t) { var i = new RegExp("[#&?]" + e + "=([^#&?]*)"), n = i.exec(window.location.href); if (!n) return t; var r = n[1]; return "boolean" == typeof t ? "true" === r || "1" === r : "number" == typeof t ? parseFloat(r) : window.decodeURIComponent(r); }, //-------许钟文:------------------------------------------------- getProjectNum: function getProjectNum() { //获取场景projectNum if (window.__ProjectNum && window.__ProjectNum != "__ProjectNum__") { return window.__ProjectNum; } var number = window.location.href.substring(window.location.href.indexOf("=") + 1); if (number.indexOf("&") != -1) { number = number.substring(0, number.indexOf("&")); } if (number.indexOf("#") != -1) { number = number.substring(0, number.indexOf("#")); } return number; }, urlHasValue: function urlHasValue(key, isGetValue) { var querys = window.location.search.substr(1).split("&"); if (isGetValue) { for (var i = 0; i < querys.length; i++) { var keypair = querys[i].split("="); if (keypair.length === 2 && keypair[0] === key) { return keypair[1]; } } return ""; } else { for (var _i = 0; _i < querys.length; _i++) { var _keypair = querys[_i].split("="); if (_keypair[0] == key) { return true; } } return false; } }, /** * 获取查询参数的值 * @param {String} key * @returns String */ urlQueryValue(key) { return this.urlHasValue(key, true) || ""; }, /** * 判断是否存在hash * @param {String} key * @returns Boolean */ urlIsHasHash(key) { var querys = window.location.hash.substr(1).replace('/?', '').split("&"); return querys.includes(key); }, islongPhone: function islongPhone() { //是否是刘海全面屏幕 仅仅根据比例判断 - - //screen.height == 812 && screen.width == 375) var r = screen.height / screen.width; //可能横屏 return this.isMobile() && (r > 1.99 || r < 0.502512); //18/9=2.165 //??? }, detectWeixin: function detectWeixin() { //微信 包括PC的微信 return window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == "micromessenger"; }, detectWeixinMiniProgram: function detectWeixinMiniProgram() { return window.navigator.userAgent.match("miniProgram"); }, detectEdge: function detectEdge() { return window.navigator.userAgent.indexOf("Edge") > -1; }, detectApp: function detectApp() { return this.urlHasValue("app"); }, /** * 判断标签页是否切换状态 */ isTabHidden: function isTabHidden() { var prefixes = ["webkit", "moz", "ms", "o"]; if ("hidden" in document) return document.hidden; for (var i = 0; i < prefixes.length; i++) { if (prefixes[i] + "Hidden" in document) return document[prefixes[i] + "Hidden"]; } return false; }, maybeQilin() { //可能是银河麒麟桌面系统,webgl容易出bug 2024.10 return window.navigator.userAgent.toLowerCase().includes('linux'); } }; WebGLRenderer.prototype.paramThreeToGL = function (e) { var t, i = this.extensions, r = this.getContext(); //context; if (e === RepeatWrapping) return r.REPEAT; if (e === ClampToEdgeWrapping) return r.CLAMP_TO_EDGE; if (e === MirroredRepeatWrapping) return r.MIRRORED_REPEAT; if (e === NearestFilter) return r.NEAREST; if (e === NearestMipMapNearestFilter) return r.NEAREST_MIPMAP_NEAREST; if (e === NearestMipMapLinearFilter) return r.NEAREST_MIPMAP_LINEAR; if (e === LinearFilter) return r.LINEAR; if (e === LinearMipMapNearestFilter) return r.LINEAR_MIPMAP_NEAREST; if (e === LinearMipMapLinearFilter) return r.LINEAR_MIPMAP_LINEAR; if (e === UnsignedByteType) return r.UNSIGNED_BYTE; if (e === UnsignedShort4444Type) return r.UNSIGNED_SHORT_4_4_4_4; if (e === UnsignedShort5551Type) return r.UNSIGNED_SHORT_5_5_5_1; if (e === UnsignedShort565Type) return r.UNSIGNED_SHORT_5_6_5; if (e === ByteType) return r.BYTE; if (e === ShortType) return r.SHORT; if (e === UnsignedShortType) return r.UNSIGNED_SHORT; if (e === IntType) return r.INT; if (e === UnsignedIntType) return r.UNSIGNED_INT; if (e === FloatType) return r.FLOAT; if (t = i.get("OES_texture_half_float"), null !== t && e === HalfFloatType) return t.HALF_FLOAT_OES; if (e === AlphaFormat) return r.ALPHA; if (e === RGBFormat) return r.RGB; if (e === RGBAFormat) return r.RGBA; if (e === LuminanceFormat) return r.LUMINANCE; if (e === LuminanceAlphaFormat) return r.LUMINANCE_ALPHA; if (e === AddEquation) return r.FUNC_ADD; if (e === SubtractEquation) return r.FUNC_SUBTRACT; if (e === ReverseSubtractEquation) return r.FUNC_REVERSE_SUBTRACT; if (e === ZeroFactor) return r.ZERO; if (e === OneFactor) return r.ONE; if (e === SrcColorFactor) return r.SRC_COLOR; if (e === OneMinusSrcColorFactor) return r.ONE_MINUS_SRC_COLOR; if (e === SrcAlphaFactor) return r.SRC_ALPHA; if (e === OneMinusSrcAlphaFactor) return r.ONE_MINUS_SRC_ALPHA; if (e === DstAlphaFactor) return r.DST_ALPHA; if (e === OneMinusDstAlphaFactor) return r.ONE_MINUS_DST_ALPHA; if (e === DstColorFactor) return r.DST_COLOR; if (e === OneMinusDstColorFactor) return r.ONE_MINUS_DST_COLOR; if (e === SrcAlphaSaturateFactor) return r.SRC_ALPHA_SATURATE; if (t = i.get("WEBGL_compressed_texture_s3tc"), null !== t) { if (e === RGB_S3TC_DXT1_Format) return t.COMPRESSED_RGB_S3TC_DXT1_EXT; if (e === RGBA_S3TC_DXT1_Format$1) return t.COMPRESSED_RGBA_S3TC_DXT1_EXT; if (e === RGBA_S3TC_DXT3_Format) return t.COMPRESSED_RGBA_S3TC_DXT3_EXT; if (e === RGBA_S3TC_DXT5_Format$1) return t.COMPRESSED_RGBA_S3TC_DXT5_EXT; } if (t = i.get("WEBGL_compressed_texture_pvrtc"), null !== t) { if (e === RGB_PVRTC_4BPPV1_Format) return t.COMPRESSED_RGB_PVRTC_4BPPV1_IMG; if (e === RGB_PVRTC_2BPPV1_Format) return t.COMPRESSED_RGB_PVRTC_2BPPV1_IMG; if (e === RGBA_PVRTC_4BPPV1_Format) return t.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; if (e === RGBA_PVRTC_2BPPV1_Format) return t.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; } if (t = i.get("WEBGL_compressed_texture_etc1"), null !== t && e === RGB_ETC1_Format) return t.COMPRESSED_RGB_ETC1_WEBGL; if (t = i.get("EXT_blend_minmax"), null !== t) { if (e === MinEquation) return t.MIN_EXT; if (e === MaxEquation) return t.MAX_EXT; } return 0; }; EventDispatcher.prototype.addEventListener = function (type, listener) { var { importance = 0, once } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; //add importance if (this._listeners === undefined) this._listeners = {}; var listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } /* if(type == 'flyingDone'){ console.log('addEventListener flyingDone') } */ if (!listeners[type].some(e => e.listener == listener)) { listeners[type].push({ listener, importance, once }); listeners[type] = listeners[type].sort((e, a) => a.importance - e.importance); //add } }; EventDispatcher.prototype.hasEventListener = function (type, listener) { if (this._listeners === undefined) return false; var listeners = this._listeners; return listeners[type] !== undefined && listeners[type].some(e => e.listener == listener); }; EventDispatcher.prototype.removeEventListener = function (type, listener) { if (this._listeners === undefined) return; var listeners = this._listeners; var listenerArray = listeners[type]; if (listenerArray !== undefined) { /* const index = listenerArray.indexOf( listener ); if ( index !== - 1 ) { listenerArray.splice( index, 1 ); } */ var item = listenerArray.find(e => e.listener == listener); item && listenerArray.splice(listenerArray.indexOf(item), 1); } }; EventDispatcher.prototype.removeEventListeners = function (type) { //add if (this._listeners && this._listeners[type] !== undefined) { delete this._listeners[type]; } }; EventDispatcher.prototype.removeAllListeners = function () { //add this._listeners = {}; }; EventDispatcher.prototype.dispatchEvent = function (event) { if (typeof event == 'string') { //add event = { type: event }; } if (this._listeners === undefined) return; var listeners = this._listeners; var listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; // Make a copy, in case listeners are removed while iterating. for (var { listener, once } of listenerArray.slice(0)) { if (once) { this.removeEventListener(event.type, listener); } var result = listener.call(this, event); //add stopContinue if (result && result.stopContinue) { break; } } } }; EventDispatcher.prototype.traverse = function (callback) { var result = callback(this); if (result && result.stopContinue) { //xzw add return; } var children = this.children; if (children) { for (var i = 0, l = children.length; i < l; i++) { children[i].traverse(callback); } } }; Quaternion.prototype.toObject = function (e) { return { x: this.x, y: this.y, z: this.z, w: this.w }; }; Euler.prototype.toObject = function (addOrder) { var euler = { x: this.x, y: this.y, z: this.z }; addOrder && (euler.order = this.order); return euler; }; Vector3.prototype.toObject = function () { return { x: this.x, y: this.y, z: this.z }; }; Vector2.prototype.toObject = function () { return { x: this.x, y: this.y }; }; Object3D.prototype.traverse = function (callback, lastResult) { var result = callback(this, lastResult); //lastResult一般用于收集祖先的visible if (result && result.stopContinue) { //xzw add return; } var children = this.children; for (var i = 0, l = children.length; i < l; i++) { children[i] && children[i].traverse(callback, result); } }; Shim.FollowRootObject = function (root) { Object3D.call(this); this.root = root; }; Shim.FollowRootObject.prototype = Object.assign(Object.create(Object3D.prototype), { constructor: Shim.FollowRootObject, updateMatrixWorld(force) { //重写,只为了将root当做parent this.scale.set(1 / this.root.scale.x, 1 / this.root.scale.y, 1 / this.root.scale.z); //中和模型缩放。无论模型缩放如何都不能改tag大小 this.updateMatrix(); this.matrixWorld.multiplyMatrices(this.root.matrixWorld, this.matrix); var children = this.children; for (var i = 0, l = children.length; i < l; i++) { children[i].updateMatrixWorld(force); } }, updateWorldMatrix(updateParents, updateChildren) { //重写,只为了将root当做parent if (updateParents === true && this.root !== null) { this.root.updateWorldMatrix(true, false); } if (this.matrixAutoUpdate) this.updateMatrix(); this.matrixWorld.multiplyMatrices(this.root.matrixWorld, this.matrix); if (updateChildren === true) { var children = this.children; for (var i = 0, l = children.length; i < l; i++) { children[i].updateWorldMatrix(false, true); } } } }); Material.prototype.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 ) { //-----主要删了这段,否则很难和 set 一起使用 //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; } } }; function ascSort$1(a, b) { return a.distance - b.distance; } function intersectObject(object, raycaster, intersects, recursive, ignoreUnvisible) { if (ignoreUnvisible && !object.visible) return; //add if (object.layers.test(raycaster.layers)) { 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, ignoreUnvisible); } } } Raycaster.prototype.intersectObject = function (object, recursive, optionalTarget, ignoreUnvisible) { var intersects = optionalTarget || []; intersectObject(object, this, intersects, recursive, ignoreUnvisible); intersects.sort(ascSort$1); return intersects; }; Raycaster.prototype.intersectObjects = function (objects, recursive, optionalTarget, ignoreUnvisible) { //add ignoreUnvisible 跳过不可见 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, ignoreUnvisible); } intersects.sort(ascSort$1); return intersects; }; Object3D.prototype.realVisible = function () { var v = true; var parent = this; var lastParent; while (parent) { if (parent.visible === false) { v = false; break; } lastParent = parent; parent = parent.parent; } if (v && !(lastParent instanceof Scene)) { //已被删除 v = false; } return v; }; Object3D.prototype.isChildOf = function (rootParent) { //是否后代, 包括自身 var parent = this; while (parent) { if (parent == rootParent) { return true; break; } parent = parent.parent; } }; Curve.prototype.getPointAt = function (u, optionalTarget) { var t = this.getUtoTmapping(u); this.UtoTMapArr && this.UtoTMapArr.push(t); //add return this.getPoint(t, optionalTarget); }; var _inverseMatrix$3 = /*@__PURE__*/new Matrix4(); var _ray$3 = /*@__PURE__*/new Ray(); var _sphere$6 = /*@__PURE__*/new Sphere(); var _sphereHitAt = /*@__PURE__*/new Vector3(); var _vA$1$1 = /*@__PURE__*/new Vector3(); var _vB$1$1 = /*@__PURE__*/new Vector3(); var _vC$1$1 = /*@__PURE__*/new Vector3(); var _tempA$1 = /*@__PURE__*/new Vector3(); var _morphA$1 = /*@__PURE__*/new Vector3(); var _intersectionPoint$1 = /*@__PURE__*/new Vector3(); var _intersectionPointWorld$1 = /*@__PURE__*/new Vector3(); var _basePosition$1 = /*@__PURE__*/new Vector3(); var _skinIndex$1 = /*@__PURE__*/new Vector4(); var _skinWeight$1 = /*@__PURE__*/new Vector4(); var _vector3 = /*@__PURE__*/new Vector3(); var _matrix4 = /*@__PURE__*/new Matrix4(); var _vertex = /*@__PURE__*/new Vector3(); var _sphere$5 = /*@__PURE__*/new Sphere(); var _inverseMatrix$2$1 = /*@__PURE__*/new Matrix4(); var _ray$2$1 = /*@__PURE__*/new Ray(); SkinnedMesh.prototype.computeBoundingSphere = function () { var geometry = this.geometry; if (!this.boundingSphere) { this.boundingSphere = new Sphere(); } this.boundingSphere.makeEmpty(); var positionAttribute = geometry.getAttribute('position'); for (var i = 0; i < positionAttribute.count; i++) { this.getVertexPosition(i, _vertex); this.boundingSphere.expandByPoint(_vertex); } }; SkinnedMesh.prototype.computeBoundingBox = function () { var geometry = this.geometry; if (!this.boundingBox) { this.boundingBox = new Box3(); } this.boundingBox.makeEmpty(); var positionAttribute = geometry.getAttribute('position'); for (var i = 0; i < positionAttribute.count; i++) { this.getVertexPosition(i, _vertex); this.boundingBox.expandByPoint(_vertex); } }; SkinnedMesh.prototype.raycast = function (raycaster, intersects) { //根据r173重写,因模型大小跟着骨骼走 var material = this.material; var matrixWorld = this.matrixWorld; if (material === undefined) return; // test with bounding sphere in world space if (!this.boundingSphere) this.computeBoundingSphere(); _sphere$5.copy(this.boundingSphere); _sphere$5.applyMatrix4(matrixWorld); if (raycaster.ray.intersectsSphere(_sphere$5) === false) return; // convert ray to local space of skinned mesh _inverseMatrix$2$1.copy(matrixWorld).invert(); _ray$2$1.copy(raycaster.ray).applyMatrix4(_inverseMatrix$2$1); // test with bounding box in local space if (this.boundingBox) { if (_ray$2$1.intersectsBox(this.boundingBox) === false) return; } // test for intersections with geometry this._computeIntersections(raycaster, intersects, _ray$2$1); }; SkinnedMesh.prototype.getVertexPosition = function (index, target) { //super.getVertexPosition( index, target ); var geometry = this.geometry; var position = geometry.attributes.position; var morphPosition = geometry.morphAttributes.position; var morphTargetsRelative = geometry.morphTargetsRelative; target.fromBufferAttribute(position, index); var morphInfluences = this.morphTargetInfluences; if (morphPosition && morphInfluences) { _morphA$1.set(0, 0, 0); for (var i = 0, il = morphPosition.length; i < il; i++) { var influence = morphInfluences[i]; var morphAttribute = morphPosition[i]; if (influence === 0) continue; _tempA$1.fromBufferAttribute(morphAttribute, index); if (morphTargetsRelative) { _morphA$1.addScaledVector(_tempA$1, influence); } else { _morphA$1.addScaledVector(_tempA$1.sub(target), influence); } } target.add(_morphA$1); } this.applyBoneTransform(index, target); return target; }; SkinnedMesh.prototype.applyBoneTransform = function (index, vector) { var skeleton = this.skeleton; var geometry = this.geometry; _skinIndex$1.fromBufferAttribute(geometry.attributes.skinIndex, index); _skinWeight$1.fromBufferAttribute(geometry.attributes.skinWeight, index); _basePosition$1.copy(vector).applyMatrix4(this.bindMatrix); vector.set(0, 0, 0); for (var i = 0; i < 4; i++) { var weight = _skinWeight$1.getComponent(i); if (weight !== 0) { var boneIndex = _skinIndex$1.getComponent(i); _matrix4.multiplyMatrices(skeleton.bones[boneIndex].matrixWorld, skeleton.boneInverses[boneIndex]); vector.addScaledVector(_vector3.copy(_basePosition$1).applyMatrix4(_matrix4), weight); } } return vector.applyMatrix4(this.bindMatrixInverse); }; SkinnedMesh.prototype._computeIntersections = function (raycaster, intersects, rayLocalSpace) { var intersection; var geometry = this.geometry; var material = this.material; var index = geometry.index; var position = geometry.attributes.position; var uv = geometry.attributes.uv; var uv1 = geometry.attributes.uv1; var normal = geometry.attributes.normal; var groups = geometry.groups; var drawRange = geometry.drawRange; if (index !== null) { // indexed buffer geometry if (Array.isArray(material)) { for (var i = 0, il = groups.length; i < il; i++) { var group = groups[i]; var groupMaterial = material[group.materialIndex]; var start = Math.max(group.start, drawRange.start); var end = Math.min(index.count, Math.min(group.start + group.count, drawRange.start + drawRange.count)); for (var j = start, jl = end; j < jl; j += 3) { var a = index.getX(j); var b = index.getX(j + 1); var 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 { var _start = Math.max(0, drawRange.start); var _end = Math.min(index.count, drawRange.start + drawRange.count); for (var _i = _start, _il = _end; _i < _il; _i += 3) { var _a = index.getX(_i); var _b = index.getX(_i + 1); var _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 (var _i2 = 0, _il2 = groups.length; _i2 < _il2; _i2++) { var _group = groups[_i2]; var _groupMaterial = material[_group.materialIndex]; var _start2 = Math.max(_group.start, drawRange.start); var _end2 = Math.min(position.count, Math.min(_group.start + _group.count, drawRange.start + drawRange.count)); for (var _j = _start2, _jl = _end2; _j < _jl; _j += 3) { var _a2 = _j; var _b2 = _j + 1; var _c2 = _j + 2; intersection = checkGeometryIntersection(this, _groupMaterial, raycaster, rayLocalSpace, uv, uv1, normal, _a2, _b2, _c2); if (intersection) { intersection.faceIndex = Math.floor(_j / 3); // triangle number in non-indexed buffer semantics intersection.face.materialIndex = _group.materialIndex; intersects.push(intersection); } } } } else { var _start3 = Math.max(0, drawRange.start); var _end3 = Math.min(position.count, drawRange.start + drawRange.count); for (var _i3 = _start3, _il3 = _end3; _i3 < _il3; _i3 += 3) { var _a3 = _i3; var _b3 = _i3 + 1; var _c3 = _i3 + 2; intersection = checkGeometryIntersection(this, material, raycaster, rayLocalSpace, uv, uv1, normal, _a3, _b3, _c3); if (intersection) { intersection.faceIndex = Math.floor(_i3 / 3); // triangle number in non-indexed buffer semantics intersects.push(intersection); } } } } }; var _v40 = /*@__PURE__*/new Vector4(); var _v41 = /*@__PURE__*/new Vector4(); var _v42 = /*@__PURE__*/new Vector4(); function checkGeometryIntersection(object, material, raycaster, ray, uv, uv1, normal, a, b, c) { object.getVertexPosition(a, _vA$1$1); object.getVertexPosition(b, _vB$1$1); object.getVertexPosition(c, _vC$1$1); var intersection = checkIntersection$1(object, material, raycaster, ray, _vA$1$1, _vB$1$1, _vC$1$1, _intersectionPoint$1); if (intersection) { var barycoord = new Vector3(); Triangle.getBarycoord(_intersectionPoint$1, _vA$1$1, _vB$1$1, _vC$1$1, barycoord); if (uv) { intersection.uv = Triangle.getInterpolatedAttribute(uv, a, b, c, barycoord, new Vector2()); } if (uv1) { intersection.uv1 = Triangle.getInterpolatedAttribute(uv1, a, b, c, barycoord, new Vector2()); } if (normal) { intersection.normal = Triangle.getInterpolatedAttribute(normal, a, b, c, barycoord, new Vector3()); if (intersection.normal.dot(ray.direction) > 0) { intersection.normal.multiplyScalar(-1); } } var face = { a: a, b: b, c: c, normal: new Vector3(), materialIndex: 0 }; Triangle.getNormal(_vA$1$1, _vB$1$1, _vC$1$1, face.normal); intersection.face = face; intersection.barycoord = barycoord; } return intersection; } function checkIntersection$1(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 === FrontSide, point); } if (intersect === null) return null; _intersectionPointWorld$1.copy(point); _intersectionPointWorld$1.applyMatrix4(object.matrixWorld); var distance = raycaster.ray.origin.distanceTo(_intersectionPointWorld$1); if (distance < raycaster.near || distance > raycaster.far) return null; return { distance: distance, point: _intersectionPointWorld$1.clone(), object: object }; } var _v1$6$1 = /*@__PURE__*/new Vector3(); var _v2$3$1 = /*@__PURE__*/new Vector3(); Sphere.prototype.expandByPoint = function (point) { if (this.isEmpty()) { this.center.copy(point); this.radius = 0; return this; } _v1$6$1.subVectors(point, this.center); var lengthSq = _v1$6$1.lengthSq(); if (lengthSq > this.radius * this.radius) { // calculate the minimal sphere var length = Math.sqrt(lengthSq); var delta = (length - this.radius) * 0.5; this.center.addScaledVector(_v1$6$1, delta / length); this.radius += delta; } return this; }; /* THREE.Triangle.prototype.getBarycoord( point, target ) { return Triangle.getBarycoord( point, this.a, this.b, this.c, target ); } */ Triangle.getInterpolatedAttribute = function (attr, i1, i2, i3, barycoord, target) { _v40.setScalar(0); _v41.setScalar(0); _v42.setScalar(0); _v40.fromBufferAttribute(attr, i1); _v41.fromBufferAttribute(attr, i2); _v42.fromBufferAttribute(attr, i3); target.setScalar(0); target.addScaledVector(_v40, barycoord.x); target.addScaledVector(_v41, barycoord.y); target.addScaledVector(_v42, barycoord.z); return target; }; var MathLight = {}; MathLight.RADIANS_PER_DEGREE = Math.PI / 180; MathLight.DEGREES_PER_RADIAN = 180 / Math.PI; MathLight.Vector3 = function (e, t, i) { this.x = e || 0, this.y = t || 0, this.z = i || 0; }; MathLight.Matrix4 = function () { this.elements = new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]), arguments.length > 0 && console.error("MathLight.Matrix4: the constructor no longer reads arguments. use .set() instead."); }; MathLight.Matrix4.prototype = { identity: function identity() { return this.set(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1), this; }, copy: function copy(e) { return this.elements.set(e.elements), this; }, applyToVector3: function applyToVector3(e) { var t = e.x, i = e.y, n = e.z, r = this.elements; return e.x = r[0] * t + r[4] * i + r[8] * n + r[12], e.y = r[1] * t + r[5] * i + r[9] * n + r[13], e.z = r[2] * t + r[6] * i + r[10] * n + r[14], this; }, getInverse: function getInverse(e, t) { var i = this.elements, n = e.elements, r = n[0], o = n[1], a = n[2], s = n[3], l = n[4], c = n[5], h = n[6], u = n[7], d = n[8], p = n[9], f = n[10], g = n[11], m = n[12], v = n[13], A = n[14], y = n[15], C = p * A * u - v * f * u + v * h * g - c * A * g - p * h * y + c * f * y, I = m * f * u - d * A * u - m * h * g + l * A * g + d * h * y - l * f * y, E = d * v * u - m * p * u + m * c * g - l * v * g - d * c * y + l * p * y, b = m * p * h - d * v * h - m * c * f + l * v * f + d * c * A - l * p * A, w = r * C + o * I + a * E + s * b; if (0 === w) { var _ = "MathLight.Matrix4.getInverse(): can't invert matrix, determinant is 0"; if (t) throw new Error(_); return console.warn(_), this.identity(); } var T = 1 / w; return i[0] = C * T, i[1] = (v * f * s - p * A * s - v * a * g + o * A * g + p * a * y - o * f * y) * T, i[2] = (c * A * s - v * h * s + v * a * u - o * A * u - c * a * y + o * h * y) * T, i[3] = (p * h * s - c * f * s - p * a * u + o * f * u + c * a * g - o * h * g) * T, i[4] = I * T, i[5] = (d * A * s - m * f * s + m * a * g - r * A * g - d * a * y + r * f * y) * T, i[6] = (m * h * s - l * A * s - m * a * u + r * A * u + l * a * y - r * h * y) * T, i[7] = (l * f * s - d * h * s + d * a * u - r * f * u - l * a * g + r * h * g) * T, i[8] = E * T, i[9] = (m * p * s - d * v * s - m * o * g + r * v * g + d * o * y - r * p * y) * T, i[10] = (l * v * s - m * c * s + m * o * u - r * v * u - l * o * y + r * c * y) * T, i[11] = (d * c * s - l * p * s - d * o * u + r * p * u + l * o * g - r * c * g) * T, i[12] = b * T, i[13] = (d * v * a - m * p * a + m * o * f - r * v * f - d * o * A + r * p * A) * T, i[14] = (m * c * a - l * v * a - m * o * h + r * v * h + l * o * A - r * c * A) * T, i[15] = (l * p * a - d * c * a + d * o * h - r * p * h - l * o * f + r * c * f) * T, this; }, makeRotationFromQuaternion: function makeRotationFromQuaternion(e) { var t = this.elements, i = e.x, n = e.y, r = e.z, o = e.w, a = i + i, s = n + n, l = r + r, c = i * a, h = i * s, u = i * l, d = n * s, p = n * l, f = r * l, g = o * a, m = o * s, v = o * l; return t[0] = 1 - (d + f), t[4] = h - v, t[8] = u + m, t[1] = h + v, t[5] = 1 - (c + f), t[9] = p - g, t[2] = u - m, t[6] = p + g, t[10] = 1 - (c + d), t[3] = 0, t[7] = 0, t[11] = 0, t[12] = 0, t[13] = 0, t[14] = 0, t[15] = 1, this; } }; MathLight.Quaternion = function (e, t, i, n) { this._x = e || 0, this._y = t || 0, this._z = i || 0, this._w = void 0 !== n ? n : 1; }; MathLight.Quaternion.prototype = { get x() { return this._x; }, set x(e) { this._x = e; }, get y() { return this._y; }, set y(e) { this._y = e; }, get z() { return this._z; }, set z(e) { this._z = e; }, get w() { return this._w; }, set w(e) { this._w = e; }, copy: function copy(e) { this._x = e.x, this._y = e.y, this._z = e.z, this._w = e.w; }, inverse: function inverse() { return this.conjugate().normalize(); }, conjugate: function conjugate() { return this._x *= -1, this._y *= -1, this._z *= -1, this; }, length: function length() { return Math.sqrt(this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w); }, normalize: function normalize() { var e = this.length(); return 0 === e ? (this._x = 0, this._y = 0, this._z = 0, this._w = 1) : (e = 1 / e, this._x = this._x * e, this._y = this._y * e, this._z = this._z * e, this._w = this._w * e), this; }, setFromAxisAngle: function setFromAxisAngle(e, t) { var i = t / 2, n = Math.sin(i); return this._x = e.x * n, this._y = e.y * n, this._z = e.z * n, this._w = Math.cos(i), this; }, setFromUnitVectors: function () { var e, t, i = 1e-6; return function (n, o) { return void 0 === e && (e = new MathLight.Vector3()), t = MathLight.dot(n, o) + 1, t < i ? (t = 0, Math.abs(n.x) > Math.abs(n.z) ? MathLight.setVector(e, -n.y, n.x, 0) : MathLight.setVector(e, 0, -n.z, n.y)) : MathLight.cross(n, o, e), this._x = e.x, this._y = e.y, this._z = e.z, this._w = t, this.normalize(); }; }(), multiply: function multiply(e) { return this.multiplyQuaternions(this, e); }, premultiply: function premultiply(e) { return this.multiplyQuaternions(e, this); }, multiplyQuaternions: function multiplyQuaternions(e, t) { var i = e._x, n = e._y, r = e._z, o = e._w, a = t._x, s = t._y, l = t._z, c = t._w; return this._x = i * c + o * a + n * l - r * s, this._y = n * c + o * s + r * a - i * l, this._z = r * c + o * l + i * s - n * a, this._w = o * c - i * a - n * s - r * l, this; } }; MathLight.convertWorkshopVector = function (e) { return new MathLight.Vector3(-e.x, e.y, e.z); }; MathLight.convertWorkshopQuaternion = function (e) { return new MathLight.Quaternion(-e.x, e.y, e.z, -e.w).multiply(new MathLight.Quaternion(Math.sqrt(2) / 2, Math.sqrt(2) / 2, 0, 0)); }; MathLight.convertWorkshopOrthoZoom = function (e) { //return e === -1 ? -1 : e / 16 * ($('#player').width() / $('#player').height()) / n.workshopApsect return e === -1 ? -1 : e * ($("#player").width() / $("#player").height()); }; MathLight.convertWorkshopPanoramaQuaternion = function (e) { return new MathLight.Quaternion(e.x, -e.y, -e.z, e.w).normalize().multiply(new MathLight.Quaternion().setFromAxisAngle(new MathLight.Vector3(0, 1, 0), 270 * MathLight.RADIANS_PER_DEGREE)); }; MathLight.normalize = function (e) { var t = e.x * e.x + e.y * e.y + e.z * e.z, i = Math.sqrt(t); e.x /= i, e.y /= i, e.z /= i; }; MathLight.dot = function (e, t) { return e.x * t.x + e.y * t.y + e.z * t.z; }; MathLight.cross = function (e, t, i) { var n = e.x, r = e.y, o = e.z; i.x = r * t.z - o * t.y, i.y = o * t.x - n * t.z, i.z = n * t.y - r * t.x; }; MathLight.setVector = function (e, t, i, n) { e.x = t, e.y = i, e.z = n; }; MathLight.copyVector = function (e, t) { t.x = e.x, t.y = e.y, t.z = e.z; }; MathLight.addVector = function (e, t) { e.x += t.x, e.y += t.y, e.z += t.z; }; MathLight.subVector = function (e, t) { e.x -= t.x, e.y -= t.y, e.z -= t.z; }; MathLight.applyQuaternionToVector = function (e, t) { var i = t.x, n = t.y, r = t.z, o = e.x, a = e.y, s = e.z, l = e.w, c = l * i + a * r - s * n, h = l * n + s * i - o * r, u = l * r + o * n - a * i, d = -o * i - a * n - s * r; t.x = c * l + d * -o + h * -s - u * -a, t.y = h * l + d * -a + u * -o - c * -s, t.z = u * l + d * -s + c * -a - h * -o; }; MathLight.angleBetweenVectors = function (e, t) { return Math.acos(MathLight.dot(e, t)); }; var cameraLight = { clampVFOV: function clampVFOV(currentFov, maxHFov, width, height) { //限制currentFov, 使之造成的横向fov不大于指定值maxHFov var r = cameraLight.getHFOVFromVFOV(currentFov, width, height); return r > maxHFov ? cameraLight.getVFOVFromHFOV(maxHFov, width, height) : currentFov; }, getHFOVForCamera: function getHFOVForCamera(camera, getRad) { return cameraLight.getHFOVByScreenPrecent(camera.fov, camera.aspect, getRad); }, //add getHFOVByScreenPrecent: function getHFOVByScreenPrecent(fov, percent, getRad) { //当fov为占比百分百时,percent代表在屏幕上从中心到边缘的占比 var rad = 2 * Math.atan(percent * Math.tan(fov * MathLight.RADIANS_PER_DEGREE / 2)); if (getRad) return rad;else return rad * MathLight.DEGREES_PER_RADIAN; } }; var XHRFactory = { config: { withCredentials: false, customHeaders: [{ header: null, value: null }] }, createXMLHttpRequest: function createXMLHttpRequest() { var xhr = new XMLHttpRequest(); if (this.config.customHeaders && Array.isArray(this.config.customHeaders) && this.config.customHeaders.length > 0) { var baseOpen = xhr.open; var customHeaders = this.config.customHeaders; xhr.open = function () { baseOpen.apply(this, [].slice.call(arguments)); customHeaders.forEach(function (customHeader) { if (!!customHeader.header && !!customHeader.value) { xhr.setRequestHeader(customHeader.header, customHeader.value); } }); }; } return xhr; } }; // /** class TextSprite$1 extends Object3D { //old constructor(text) { super(); var texture = new Texture(); texture.minFilter = LinearFilter; texture.magFilter = LinearFilter; var spriteMaterial = new SpriteMaterial({ map: texture, depthTest: false, depthWrite: false }); this.texture = texture; this.material = spriteMaterial; //this.material = getRawMaterial(texture); this.sprite = new Sprite$1(this.material); this.add(this.sprite); this.borderThickness = 4; this.fontface = 'Arial'; this.fontsize = 28; this.borderColor = { r: 0, g: 0, b: 0, a: 1.0 }; this.backgroundColor = { r: 255, g: 255, b: 255, a: 1.0 }; this.textColor = { r: 255, g: 255, b: 255, a: 1.0 }; this.text = ''; this.setText(text); } setText(text) { if (this.text !== text) { this.text = text; this.update(); } } setTextColor(color) { this.textColor = color; this.update(); } setBorderColor(color) { this.borderColor = color; this.update(); } setBackgroundColor(color) { this.backgroundColor = color; this.update(); } update() { var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); context.font = 'Bold ' + this.fontsize + 'px ' + this.fontface; // get size data (height depends only on font size) var metrics = context.measureText(this.text); var textWidth = metrics.width; var margin = 5; var spriteWidth = 2 * margin + textWidth + 2 * this.borderThickness; var spriteHeight = this.fontsize * 1.4 + 2 * this.borderThickness; context.canvas.width = spriteWidth; context.canvas.height = spriteHeight; context.font = 'Bold ' + this.fontsize + 'px ' + this.fontface; // background color context.fillStyle = 'rgba(' + this.backgroundColor.r + ',' + this.backgroundColor.g + ',' + this.backgroundColor.b + ',' + this.backgroundColor.a + ')'; // border color context.strokeStyle = 'rgba(' + this.borderColor.r + ',' + this.borderColor.g + ',' + this.borderColor.b + ',' + this.borderColor.a + ')'; context.lineWidth = this.borderThickness; this.roundRect(context, this.borderThickness / 2, this.borderThickness / 2, textWidth + this.borderThickness + 2 * margin, this.fontsize * 1.4 + this.borderThickness, 6); // text color context.strokeStyle = 'rgba(0, 0, 0, 1.0)'; context.strokeText(this.text, this.borderThickness + margin, this.fontsize + this.borderThickness); context.fillStyle = 'rgba(' + this.textColor.r + ',' + this.textColor.g + ',' + this.textColor.b + ',' + this.textColor.a + ')'; context.fillText(this.text, this.borderThickness + margin, this.fontsize + this.borderThickness); var texture = new Texture(canvas); texture.minFilter = LinearFilter; texture.magFilter = LinearFilter; texture.needsUpdate = true; //this.material.needsUpdate = true; // { // screen-space sprite // let [screenWidth, screenHeight] = [1620, 937]; // let uniforms = this.sprite.material.uniforms; // let aspect = spriteHeight / spriteWidth; // let factor = 0.5; // let w = spriteWidth / screenWidth; // let h = spriteHeight / screenHeight; // uniforms.uScale.value = [2 * w, 2 * h]; // //uniforms.uScale.value = [factor * 1, factor * aspect]; // this.sprite.material.uniforms.map.value = texture; // } this.sprite.material.map = texture; this.texture = texture; this.sprite.scale.set(spriteWidth * 0.01, spriteHeight * 0.01, 1.0); } roundRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.quadraticCurveTo(x + w, y, x + w, y + r); ctx.lineTo(x + w, y + h - r); ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); ctx.lineTo(x + r, y + h); ctx.quadraticCurveTo(x, y + h, x, y + h - r); ctx.lineTo(x, y + r); ctx.quadraticCurveTo(x, y, x + r, y); ctx.closePath(); ctx.fill(); ctx.stroke(); } } class Volume extends Object3D { constructor() { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); if (this.constructor.name === "Volume") { console.warn("Can't create object of class Volume directly. Use classes BoxVolume or SphereVolume instead."); } //console.log(this); //console.log(this.constructor); //console.log(this.constructor.name); this._clip = args.clip || false; this._visible = true; this.showVolumeLabel = true; this._modifiable = args.modifiable || true; this.label = new TextSprite$1('0'); this.label.setBorderColor({ r: 0, g: 255, b: 0, a: 0.0 }); this.label.setBackgroundColor({ r: 0, g: 255, b: 0, a: 0.0 }); this.label.material.depthTest = false; this.label.material.depthWrite = false; this.label.material.transparent = true; this.label.position.y -= 0.5; this.add(this.label); this.label.updateMatrixWorld = () => { var volumeWorldPos = new Vector3(); volumeWorldPos.setFromMatrixPosition(this.matrixWorld); this.label.position.copy(volumeWorldPos); this.label.updateMatrix(); this.label.matrixWorld.copy(this.label.matrix); this.label.matrixWorldNeedsUpdate = false; for (var i = 0, l = this.label.children.length; i < l; i++) { this.label.children[i].updateMatrixWorld(true); } }; { // event listeners this.addEventListener('select', e => {}); this.addEventListener('deselect', e => {}); } } get visible() { return this._visible; } set visible(value) { if (this._visible !== value) { this._visible = value; this.dispatchEvent({ type: "visibility_changed", object: this }); } } getVolume() { console.warn("override this in subclass"); } update() {} raycast(raycaster, intersects) {} get clip() { return this._clip; } set clip(value) { if (this._clip !== value) { this._clip = value; this.update(); this.dispatchEvent({ type: "clip_changed", object: this }); } } get modifieable() { return this._modifiable; } set modifieable(value) { this._modifiable = value; this.update(); } } ; class BoxVolume extends Volume { constructor() { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(args); this.constructor.counter = this.constructor.counter === undefined ? 0 : this.constructor.counter + 1; this.name = 'box_' + this.constructor.counter; var boxGeometry = new BoxGeometry(1, 1, 1); boxGeometry.computeBoundingBox(); var boxFrameGeometry = new Geometry(); { var Vector3$1 = Vector3; boxFrameGeometry.vertices.push( // bottom new Vector3$1(-0.5, -0.5, 0.5), new Vector3$1(0.5, -0.5, 0.5), new Vector3$1(0.5, -0.5, 0.5), new Vector3$1(0.5, -0.5, -0.5), new Vector3$1(0.5, -0.5, -0.5), new Vector3$1(-0.5, -0.5, -0.5), new Vector3$1(-0.5, -0.5, -0.5), new Vector3$1(-0.5, -0.5, 0.5), // top new Vector3$1(-0.5, 0.5, 0.5), new Vector3$1(0.5, 0.5, 0.5), new Vector3$1(0.5, 0.5, 0.5), new Vector3$1(0.5, 0.5, -0.5), new Vector3$1(0.5, 0.5, -0.5), new Vector3$1(-0.5, 0.5, -0.5), new Vector3$1(-0.5, 0.5, -0.5), new Vector3$1(-0.5, 0.5, 0.5), // sides new Vector3$1(-0.5, -0.5, 0.5), new Vector3$1(-0.5, 0.5, 0.5), new Vector3$1(0.5, -0.5, 0.5), new Vector3$1(0.5, 0.5, 0.5), new Vector3$1(0.5, -0.5, -0.5), new Vector3$1(0.5, 0.5, -0.5), new Vector3$1(-0.5, -0.5, -0.5), new Vector3$1(-0.5, 0.5, -0.5)); } this.material = new MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.3, depthTest: true, depthWrite: false }); this.box = new Mesh(boxGeometry, this.material); this.box.geometry.computeBoundingBox(); this.boundingBox = this.box.geometry.boundingBox; this.add(this.box); this.frame = new LineSegments(boxFrameGeometry, new LineBasicMaterial({ color: 0x000000 })); // this.frame.mode = THREE.Lines; this.add(this.frame); this.update(); } update() { this.boundingBox = this.box.geometry.boundingBox; this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere()); if (this._clip) { this.box.visible = false; this.label.visible = false; } else { this.box.visible = true; this.label.visible = this.showVolumeLabel; } } raycast(raycaster, intersects) { var is = []; this.box.raycast(raycaster, is); if (is.length > 0) { var I = is[0]; intersects.push({ distance: I.distance, object: this, point: I.point.clone() }); } } getVolume() { return Math.abs(this.scale.x * this.scale.y * this.scale.z); } } ; class SphereVolume$1 extends Volume { constructor() { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(args); this.constructor.counter = this.constructor.counter === undefined ? 0 : this.constructor.counter + 1; this.name = 'sphere_' + this.constructor.counter; var sphereGeometry = new SphereGeometry(1, 32, 32); sphereGeometry.computeBoundingBox(); this.material = new MeshBasicMaterial({ color: 0x00ff00, transparent: true, opacity: 0.3, depthTest: true, depthWrite: false }); this.sphere = new Mesh(sphereGeometry, this.material); this.sphere.visible = false; this.sphere.geometry.computeBoundingBox(); this.boundingBox = this.sphere.geometry.boundingBox; this.add(this.sphere); this.label.visible = false; var frameGeometry = new Geometry(); { var steps = 64; var uSegments = 8; var vSegments = 5; var r = 1; for (var uSegment = 0; uSegment < uSegments; uSegment++) { var alpha = uSegment / uSegments * Math.PI * 2; var dirx = Math.cos(alpha); var diry = Math.sin(alpha); for (var i = 0; i <= steps; i++) { var v = i / steps * Math.PI * 2; var vNext = v + 2 * Math.PI / steps; var height = Math.sin(v); var xyAmount = Math.cos(v); var heightNext = Math.sin(vNext); var xyAmountNext = Math.cos(vNext); var vertex = new Vector3(dirx * xyAmount, diry * xyAmount, height); frameGeometry.vertices.push(vertex); var vertexNext = new Vector3(dirx * xyAmountNext, diry * xyAmountNext, heightNext); frameGeometry.vertices.push(vertexNext); } } // creates rings at poles, just because it's easier to implement for (var vSegment = 0; vSegment <= vSegments + 1; vSegment++) { //let height = (vSegment / (vSegments + 1)) * 2 - 1; // -1 to 1 var uh = vSegment / (vSegments + 1); // -1 to 1 uh = (1 - uh) * (-Math.PI / 2) + uh * (Math.PI / 2); var _height = Math.sin(uh); console.log(uh, _height); for (var _i = 0; _i <= steps; _i++) { var u = _i / steps * Math.PI * 2; var uNext = u + 2 * Math.PI / steps; var _dirx = Math.cos(u); var _diry = Math.sin(u); var dirxNext = Math.cos(uNext); var diryNext = Math.sin(uNext); var _xyAmount = Math.sqrt(1 - _height * _height); var _vertex = new Vector3(_dirx * _xyAmount, _diry * _xyAmount, _height); frameGeometry.vertices.push(_vertex); var _vertexNext = new Vector3(dirxNext * _xyAmount, diryNext * _xyAmount, _height); frameGeometry.vertices.push(_vertexNext); } } } this.frame = new LineSegments(frameGeometry, new LineBasicMaterial({ color: 0x000000 })); this.add(this.frame); var frameMaterial = new MeshBasicMaterial({ wireframe: true, color: 0x000000 }); this.frame = new Mesh(sphereGeometry, frameMaterial); //this.add(this.frame); //this.frame = new THREE.LineSegments(boxFrameGeometry, new THREE.LineBasicMaterial({color: 0x000000})); // this.frame.mode = THREE.Lines; //this.add(this.frame); this.update(); } update() { this.boundingBox = this.sphere.geometry.boundingBox; this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere()); //if (this._clip) { // this.sphere.visible = false; // this.label.visible = false; //} else { // this.sphere.visible = true; // this.label.visible = this.showVolumeLabel; //} } raycast(raycaster, intersects) { var is = []; this.sphere.raycast(raycaster, is); if (is.length > 0) { var I = is[0]; intersects.push({ distance: I.distance, object: this, point: I.point.clone() }); } } // see https://en.wikipedia.org/wiki/Ellipsoid#Volume getVolume() { return 4 / 3 * Math.PI * this.scale.x * this.scale.y * this.scale.z; } } ; class Profile extends Object3D { constructor() { super(); this.constructor.counter = this.constructor.counter === undefined ? 0 : this.constructor.counter + 1; this.name = 'Profile_' + this.constructor.counter; this.points = []; this.spheres = []; this.edges = []; this.boxes = []; this.width = 1; this.height = 20; this._modifiable = true; this.sphereGeometry = new SphereGeometry(0.4, 10, 10); this.color = new Color(0xff0000); this.lineColor = new Color(0xff0000); } createSphereMaterial() { var sphereMaterial = new MeshLambertMaterial({ //shading: THREE.SmoothShading, color: 0xff0000, depthTest: false, depthWrite: false }); return sphereMaterial; } getSegments() { var segments = []; for (var i = 0; i < this.points.length - 1; i++) { var start = this.points[i].clone(); var end = this.points[i + 1].clone(); segments.push({ start: start, end: end }); } return segments; } getSegmentMatrices() { var segments = this.getSegments(); var matrices = []; for (var segment of segments) { var { start, end } = segment; var box = new Object3D(); var length = start.clone().setZ(0).distanceTo(end.clone().setZ(0)); box.scale.set(length, 10000, this.width); box.up.set(0, 0, 1); var center = new Vector3().addVectors(start, end).multiplyScalar(0.5); var diff = new Vector3().subVectors(end, start); var target = new Vector3(diff.y, -diff.x, 0); box.position.set(0, 0, 0); box.lookAt(target); box.position.copy(center); box.updateMatrixWorld(); matrices.push(box.matrixWorld); } return matrices; } addMarker(point) { this.points.push(point); var sphere = new Mesh(this.sphereGeometry, this.createSphereMaterial()); this.add(sphere); this.spheres.push(sphere); // edges & boxes if (this.points.length > 1) { var lineGeometry = new Geometry(); lineGeometry.vertices.push(new Vector3(), new Vector3()); lineGeometry.colors.push(this.lineColor, this.lineColor, this.lineColor); var lineMaterial = new LineBasicMaterial({ vertexColors: VertexColors, linewidth: 2, transparent: true, opacity: 0.4 }); lineMaterial.depthTest = false; var edge = new Line(lineGeometry, lineMaterial); edge.visible = false; this.add(edge); this.edges.push(edge); var boxGeometry = new BoxGeometry(1, 1, 1); var boxMaterial = new MeshBasicMaterial({ color: 0xff0000, transparent: true, opacity: 0.2 }); var box = new Mesh(boxGeometry, boxMaterial); box.visible = false; this.add(box); this.boxes.push(box); } { // event listeners var drag = e => { var I = Utils.getMousePointCloudIntersection(e.drag.end, e.viewer.scene.getActiveCamera(), e.viewer, e.viewer.scene.pointclouds); if (I) { var i = this.spheres.indexOf(e.drag.object); if (i !== -1) { this.setPosition(i, I.location); //this.dispatchEvent({ // 'type': 'marker_moved', // 'profile': this, // 'index': i //}); } } }; var drop = e => { var i = this.spheres.indexOf(e.drag.object); if (i !== -1) { this.dispatchEvent({ 'type': 'marker_dropped', 'profile': this, 'index': i }); } }; var mouseover = e => e.object.material.emissive.setHex(0x888888); var mouseleave = e => e.object.material.emissive.setHex(0x000000); sphere.addEventListener('drag', drag); sphere.addEventListener('drop', drop); sphere.addEventListener('mouseover', mouseover); sphere.addEventListener('mouseleave', mouseleave); } var event = { type: 'marker_added', profile: this, sphere: sphere }; this.dispatchEvent(event); this.setPosition(this.points.length - 1, point); } removeMarker(index) { this.points.splice(index, 1); this.remove(this.spheres[index]); var edgeIndex = index === 0 ? 0 : index - 1; this.remove(this.edges[edgeIndex]); this.edges.splice(edgeIndex, 1); this.remove(this.boxes[edgeIndex]); this.boxes.splice(edgeIndex, 1); this.spheres.splice(index, 1); this.update(); this.dispatchEvent({ 'type': 'marker_removed', 'profile': this }); } setPosition(index, position) { var point = this.points[index]; point.copy(position); var event = { type: 'marker_moved', profile: this, index: index, position: point.clone() }; this.dispatchEvent(event); this.update(); } setWidth(width) { this.width = width; var event = { type: 'width_changed', profile: this, width: width }; this.dispatchEvent(event); this.update(); } getWidth() { return this.width; } update() { if (this.points.length === 0) { return; } else if (this.points.length === 1) { var point = this.points[0]; this.spheres[0].position.copy(point); return; } var min = this.points[0].clone(); var max = this.points[0].clone(); var centroid = new Vector3(); var lastIndex = this.points.length - 1; for (var i = 0; i <= lastIndex; i++) { var _point = this.points[i]; var sphere = this.spheres[i]; var leftIndex = i === 0 ? lastIndex : i - 1; // let rightIndex = (i === lastIndex) ? 0 : i + 1; var leftVertex = this.points[leftIndex]; // let rightVertex = this.points[rightIndex]; var leftEdge = this.edges[leftIndex]; var rightEdge = this.edges[i]; var leftBox = this.boxes[leftIndex]; // rightBox = this.boxes[i]; // let leftEdgeLength = point.distanceTo(leftVertex); // let rightEdgeLength = point.distanceTo(rightVertex); // let leftEdgeCenter = new THREE.Vector3().addVectors(leftVertex, point).multiplyScalar(0.5); // let rightEdgeCenter = new THREE.Vector3().addVectors(point, rightVertex).multiplyScalar(0.5); sphere.position.copy(_point); if (this._modifiable) { sphere.visible = true; } else { sphere.visible = false; } if (leftEdge) { leftEdge.geometry.vertices[1].copy(_point); leftEdge.geometry.verticesNeedUpdate = true; leftEdge.geometry.computeBoundingSphere(); } if (rightEdge) { rightEdge.geometry.vertices[0].copy(_point); rightEdge.geometry.verticesNeedUpdate = true; rightEdge.geometry.computeBoundingSphere(); } if (leftBox) { var start = leftVertex; var end = _point; var length = start.clone().setZ(0).distanceTo(end.clone().setZ(0)); leftBox.scale.set(length, 1000000, this.width); leftBox.up.set(0, 0, 1); var center = new Vector3().addVectors(start, end).multiplyScalar(0.5); var diff = new Vector3().subVectors(end, start); var target = new Vector3(diff.y, -diff.x, 0); leftBox.position.set(0, 0, 0); leftBox.lookAt(target); leftBox.position.copy(center); } centroid.add(_point); min.min(_point); max.max(_point); } centroid.multiplyScalar(1 / this.points.length); for (var _i = 0; _i < this.boxes.length; _i++) { var box = this.boxes[_i]; box.position.z = min.z + (max.z - min.z) / 2; } } raycast(raycaster, intersects) { for (var i = 0; i < this.points.length; i++) { var sphere = this.spheres[i]; sphere.raycast(raycaster, intersects); } // recalculate distances because they are not necessarely correct // for scaled objects. // see https://github.com/mrdoob/three.js/issues/5827 // TODO: remove this once the bug has been fixed for (var _i2 = 0; _i2 < intersects.length; _i2++) { var I = intersects[_i2]; I.distance = raycaster.ray.origin.distanceTo(I.point); } intersects.sort(function (a, b) { return a.distance - b.distance; }); } get modifiable() { return this._modifiable; } set modifiable(value) { this._modifiable = value; this.update(); } } var _box$4 = new Box3(); var _vector$d = new Vector3(); class LineSegmentsGeometry extends InstancedBufferGeometry { constructor() { super(); this.isLineSegmentsGeometry = true; this.type = 'LineSegmentsGeometry'; var positions = [-1, 2, 0, 1, 2, 0, -1, 1, 0, 1, 1, 0, -1, 0, 0, 1, 0, 0, -1, -1, 0, 1, -1, 0]; var uvs = [-1, 2, 1, 2, -1, 1, 1, 1, -1, -1, 1, -1, -1, -2, 1, -2]; var index = [0, 2, 1, 2, 3, 1, 2, 4, 3, 4, 5, 3, 4, 6, 5, 6, 7, 5]; this.setIndex(index); this.setAttribute('position', new Float32BufferAttribute(positions, 3)); this.setAttribute('uv', new Float32BufferAttribute(uvs, 2)); } applyMatrix4(matrix) { var start = this.attributes.instanceStart; var end = this.attributes.instanceEnd; if (start !== undefined) { start.applyMatrix4(matrix); end.applyMatrix4(matrix); start.needsUpdate = true; } if (this.boundingBox !== null) { this.computeBoundingBox(); } if (this.boundingSphere !== null) { this.computeBoundingSphere(); } return this; } setPositions(array) { var lineSegments; if (array instanceof Float32Array) { lineSegments = array; } else if (Array.isArray(array)) { lineSegments = new Float32Array(array); } var instanceBuffer = new InstancedInterleavedBuffer(lineSegments, 6, 1); // xyz, xyz this.setAttribute('instanceStart', new InterleavedBufferAttribute(instanceBuffer, 3, 0)); // xyz this.setAttribute('instanceEnd', new InterleavedBufferAttribute(instanceBuffer, 3, 3)); // xyz // this.computeBoundingBox(); this.computeBoundingSphere(); return this; } setColors(array) { var colors; if (array instanceof Float32Array) { colors = array; } else if (Array.isArray(array)) { colors = new Float32Array(array); } var instanceColorBuffer = new InstancedInterleavedBuffer(colors, 6, 1); // rgb, rgb this.setAttribute('instanceColorStart', new InterleavedBufferAttribute(instanceColorBuffer, 3, 0)); // rgb this.setAttribute('instanceColorEnd', new InterleavedBufferAttribute(instanceColorBuffer, 3, 3)); // rgb return this; } fromWireframeGeometry(geometry) { this.setPositions(geometry.attributes.position.array); return this; } fromEdgesGeometry(geometry) { this.setPositions(geometry.attributes.position.array); return this; } fromMesh(mesh) { this.fromWireframeGeometry(new WireframeGeometry(mesh.geometry)); // set colors, maybe return this; } fromLineSegments(lineSegments) { var geometry = lineSegments.geometry; this.setPositions(geometry.attributes.position.array); // assumes non-indexed // set colors, maybe return this; } computeBoundingBox() { if (this.boundingBox === null) { this.boundingBox = new Box3(); } var start = this.attributes.instanceStart; var end = this.attributes.instanceEnd; if (start !== undefined && end !== undefined) { this.boundingBox.setFromBufferAttribute(start); _box$4.setFromBufferAttribute(end); this.boundingBox.union(_box$4); } } computeBoundingSphere() { if (this.boundingSphere === null) { this.boundingSphere = new Sphere(); } if (this.boundingBox === null) { this.computeBoundingBox(); } var start = this.attributes.instanceStart; var end = this.attributes.instanceEnd; if (start !== undefined && end !== undefined) { var center = this.boundingSphere.center; this.boundingBox.getCenter(center); var maxRadiusSq = 0; for (var i = 0, il = start.count; i < il; i++) { _vector$d.fromBufferAttribute(start, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$d)); _vector$d.fromBufferAttribute(end, i); maxRadiusSq = Math.max(maxRadiusSq, center.distanceToSquared(_vector$d)); } this.boundingSphere.radius = Math.sqrt(maxRadiusSq); if (isNaN(this.boundingSphere.radius)) { console.error('THREE.LineSegmentsGeometry.computeBoundingSphere(): Computed radius is NaN. The instanced position data is likely to have NaN values.', this); } } } toJSON() { // todo } applyMatrix(matrix) { console.warn('THREE.LineSegmentsGeometry: applyMatrix() has been renamed to applyMatrix4().'); return this.applyMatrix4(matrix); } } /** * parameters = { * color: , * lineWidth: , * dashed: , * dashScale: , * dashSize: , * dashOffset: , * gapSize: , * resolution: , // to be set by renderer * } */ UniformsLib.line = { /* worldUnits: { value: 1 }, lineWidth: { value: 1 }, resolution: { value: new Vector2( 1, 1 ) }, dashOffset: { value: 0 }, dashScale: { value: 1 }, dashSize: { value: 1 }, gapSize: { value: 1 } // todo FIX - maybe change to totalSize */ worldUnits: { value: 1 }, lineWidth: { value: 1 }, resolution: { value: new Vector2(1, 1) }, viewportOffset: { value: new Vector2(0, 0) }, //left, top devicePixelRatio: { value: window.devicePixelRatio }, dashScale: { value: 1 }, dashSize: { value: 1 }, dashOffset: { value: 0 }, gapSize: { value: 1 }, opacity: { value: 1 }, backColor: { type: 'v3', value: new Color("#ddd") }, clipDistance: { type: 'f', value: 4 }, //消失距离 occlusionDistance: { type: 'f', value: 1 }, //变为backColor距离 maxClipFactor: { type: 'f', value: 1 }, //0-1 maxOcclusionFactor: { type: 'f', value: 1 }, //0-1 depthTexture: { value: null }, nearPlane: { value: 0.1 }, farPlane: { value: 100000 }, //uUseOrthographicCamera:{ type: "b", value: false }, fadeFar: { value: 10 } //渐变消失 }; ShaderLib['line'] = { uniforms: UniformsUtils.merge([UniformsLib.common, UniformsLib.fog, UniformsLib.line]), vertexShader: /* glsl */"\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\n\t\tuniform float lineWidth;\n\t\tuniform vec2 resolution;\n uniform float devicePixelRatio; //add\n \n \n\t\tattribute vec3 instanceStart;\n\t\tattribute vec3 instanceEnd;\n\n\t\tattribute vec3 instanceColorStart;\n\t\tattribute vec3 instanceColorEnd;\n\n\t\t#ifdef WORLD_UNITS\n\n\t\t\tvarying vec4 worldPos;\n\t\t\tvarying vec3 worldStart;\n\t\t\tvarying vec3 worldEnd;\n\n\t\t\t#ifdef USE_DASH\n\n\t\t\t\tvarying vec2 vUv;\n\n\t\t\t#endif\n\n\t\t#else\n\n\t\t\tvarying vec2 vUv;\n\n\t\t#endif\n\n\t\t#ifdef USE_DASH\n\n\t\t\tuniform float dashScale;\n\t\t\tattribute float instanceDistanceStart;\n\t\t\tattribute float instanceDistanceEnd;\n\t\t\tvarying float vLineDistance;\n\n\t\t#endif\n\n\t\tvoid trimSegment( const in vec4 start, inout vec4 end ) {\n\n\t\t\t// trim end segment so it terminates between the camera plane and the near plane\n\n\t\t\t// conservative estimate of the near plane\n\t\t\tfloat a = projectionMatrix[ 2 ][ 2 ]; // 3nd entry in 3th column\n\t\t\tfloat b = projectionMatrix[ 3 ][ 2 ]; // 3nd entry in 4th column\n\t\t\tfloat nearEstimate = - 0.5 * b / a;\n\n\t\t\tfloat alpha = ( nearEstimate - start.z ) / ( end.z - start.z );\n\n\t\t\tend.xyz = mix( start.xyz, end.xyz, alpha );\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\t#ifdef USE_COLOR\n\n\t\t\t\tvColor.xyz = ( position.y < 0.5 ) ? instanceColorStart : instanceColorEnd;\n\n\t\t\t#endif\n\n\t\t\t#ifdef USE_DASH\n\n\t\t\t\tvLineDistance = ( position.y < 0.5 ) ? dashScale * instanceDistanceStart : dashScale * instanceDistanceEnd;\n\t\t\t\tvUv = uv;\n\n\t\t\t#endif\n\n\t\t\tfloat aspect = resolution.x / resolution.y;\n\n\t\t\t// camera space\n\t\t\tvec4 start = modelViewMatrix * vec4( instanceStart, 1.0 );\n\t\t\tvec4 end = modelViewMatrix * vec4( instanceEnd, 1.0 );\n\n\t\t\t#ifdef WORLD_UNITS\n\n\t\t\t\tworldStart = start.xyz;\n\t\t\t\tworldEnd = end.xyz;\n\n\t\t\t#else\n\n\t\t\t\tvUv = uv;\n\n\t\t\t#endif\n\n\t\t\t// special case for perspective projection, and segments that terminate either in, or behind, the camera plane\n\t\t\t// clearly the gpu firmware has a way of addressing this issue when projecting into ndc space\n\t\t\t// but we need to perform ndc-space calculations in the shader, so we must address this issue directly\n\t\t\t// perhaps there is a more elegant solution -- WestLangley\n\n\t\t\tbool perspective = ( projectionMatrix[ 2 ][ 3 ] == - 1.0 ); // 4th entry in the 3rd column\n\n\t\t\tif ( perspective ) {\n\n\t\t\t\tif ( start.z < 0.0 && end.z >= 0.0 ) {\n\n\t\t\t\t\ttrimSegment( start, end );\n\n\t\t\t\t} else if ( end.z < 0.0 && start.z >= 0.0 ) {\n\n\t\t\t\t\ttrimSegment( end, start );\n\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\t// clip space\n\t\t\tvec4 clipStart = projectionMatrix * start;\n\t\t\tvec4 clipEnd = projectionMatrix * end;\n\n\t\t\t// ndc space\n\t\t\tvec3 ndcStart = clipStart.xyz / clipStart.w;\n\t\t\tvec3 ndcEnd = clipEnd.xyz / clipEnd.w;\n\n\t\t\t// direction\n\t\t\tvec2 dir = ndcEnd.xy - ndcStart.xy;\n\n\t\t\t// account for clip-space aspect ratio\n\t\t\tdir.x *= aspect;\n\t\t\tdir = normalize( dir );\n\n\t\t\t#ifdef WORLD_UNITS\n\n\t\t\t\t// get the offset direction as perpendicular to the view vector\n\t\t\t\tvec3 worldDir = normalize( end.xyz - start.xyz );\n\t\t\t\tvec3 offset;\n\t\t\t\tif ( position.y < 0.5 ) {\n\n\t\t\t\t\toffset = normalize( cross( start.xyz, worldDir ) );\n\n\t\t\t\t} else {\n\n\t\t\t\t\toffset = normalize( cross( end.xyz, worldDir ) );\n\n\t\t\t\t}\n\n\t\t\t\t// sign flip\n\t\t\t\tif ( position.x < 0.0 ) offset *= - 1.0;\n\n\t\t\t\tfloat forwardOffset = dot( worldDir, vec3( 0.0, 0.0, 1.0 ) );\n\n\t\t\t\t// don't extend the line if we're rendering dashes because we\n\t\t\t\t// won't be rendering the endcaps\n\t\t\t\t#ifndef USE_DASH\n\n\t\t\t\t\t// extend the line bounds to encompass endcaps\n\t\t\t\t\tstart.xyz += - worldDir * lineWidth * 0.5;\n\t\t\t\t\tend.xyz += worldDir * lineWidth * 0.5;\n\n\t\t\t\t\t// shift the position of the quad so it hugs the forward edge of the line\n\t\t\t\t\toffset.xy -= dir * forwardOffset;\n\t\t\t\t\toffset.z += 0.5;\n\n\t\t\t\t#endif\n\n\t\t\t\t// endcaps\n\t\t\t\tif ( position.y > 1.0 || position.y < 0.0 ) {\n\n\t\t\t\t\toffset.xy += dir * 2.0 * forwardOffset;\n\n\t\t\t\t}\n\n\t\t\t\t// adjust for lineWidth\n\t\t\t\toffset *= lineWidth * 0.5;\n\n\t\t\t\t// set the world position\n\t\t\t\tworldPos = ( position.y < 0.5 ) ? start : end;\n\t\t\t\tworldPos.xyz += offset;\n\n\t\t\t\t// project the worldpos\n\t\t\t\tvec4 clip = projectionMatrix * worldPos;\n\n\t\t\t\t// shift the depth of the projected points so the line\n\t\t\t\t// segments overlap neatly\n\t\t\t\tvec3 clipPose = ( position.y < 0.5 ) ? ndcStart : ndcEnd;\n\t\t\t\tclip.z = clipPose.z * clip.w;\n\n\t\t\t#else\n\n\t\t\t\tvec2 offset = vec2( dir.y, - dir.x );\n\t\t\t\t// undo aspect ratio adjustment\n\t\t\t\tdir.x /= aspect;\n\t\t\t\toffset.x /= aspect;\n\n\t\t\t\t// sign flip\n\t\t\t\tif ( position.x < 0.0 ) offset *= - 1.0;\n\n\t\t\t\t// endcaps\n\t\t\t\tif ( position.y < 0.0 ) {\n\n\t\t\t\t\toffset += - dir;\n\n\t\t\t\t} else if ( position.y > 1.0 ) {\n\n\t\t\t\t\toffset += dir;\n\n\t\t\t\t}\n\n\t\t\t\t// adjust for lineWidth\n\t\t\t\toffset *= lineWidth;\n\n\t\t\t\t// adjust for clip-space to screen-space conversion // maybe resolution should be based on viewport ...\n\t\t\t\toffset /= resolution.y; //* devicePixelRatio;\n\n\t\t\t\t// select end\n\t\t\t\tvec4 clip = ( position.y < 0.5 ) ? clipStart : clipEnd;\n\n\t\t\t\t// back to clip space\n\t\t\t\toffset *= clip.w;\n\n\t\t\t\tclip.xy += offset;\n\n\t\t\t#endif\n\n\t\t\tgl_Position = clip;\n\n\t\t\tvec4 mvPosition = ( position.y < 0.5 ) ? start : end; // this is an approximation\n\n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\n\t\t}\n\t\t", fragmentShader: /* glsl */"\n\t\tuniform vec3 diffuse;\n\t\tuniform float opacity;\n\t\tuniform float lineWidth;\n uniform bool uUseOrthographicCamera; \n\t\t#ifdef USE_DASH\n\n\t\t\tuniform float dashOffset;\n\t\t\tuniform float dashSize;\n\t\t\tuniform float gapSize; \n\t\t#endif\n \n //\u52A0\n #if defined(GL_EXT_frag_depth) && defined(useDepth) \n uniform sampler2D depthTexture;\n \n uniform vec2 resolution;\n uniform vec2 viewportOffset;\n uniform vec3 backColor;\n uniform float occlusionDistance;\n uniform float clipDistance;\n uniform float maxClipFactor;\n uniform float maxOcclusionFactor; \n #endif\n #if defined(FadeFar)\n uniform float fadeFar;\n #endif\n \n\n\t\tvarying float vLineDistance;\n\n\t\t#ifdef WORLD_UNITS\n\n\t\t\tvarying vec4 worldPos;\n\t\t\tvarying vec3 worldStart;\n\t\t\tvarying vec3 worldEnd;\n\n\t\t\t#ifdef USE_DASH\n\n\t\t\t\tvarying vec2 vUv;\n\n\t\t\t#endif\n\n\t\t#else\n\n\t\t\tvarying vec2 vUv;\n\n\t\t#endif\n\n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\t\t#include \n\n\n #if defined(GL_EXT_frag_depth) && defined(useDepth) || defined(FadeFar) \n uniform float nearPlane;\n uniform float farPlane;\n float convertToLinear(float zValue)\n {\n //if(uUseOrthographicCamera){\n // return zValue*(farPlane-nearPlane)+nearPlane;\n //}else{ \n float z = zValue * 2.0 - 1.0;\n return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));\n //} \n }\n #endif\n\n\n\n\t\tvec2 closestLineToLine(vec3 p1, vec3 p2, vec3 p3, vec3 p4) {\n\n\t\t\tfloat mua;\n\t\t\tfloat mub;\n\n\t\t\tvec3 p13 = p1 - p3;\n\t\t\tvec3 p43 = p4 - p3;\n\n\t\t\tvec3 p21 = p2 - p1;\n\n\t\t\tfloat d1343 = dot( p13, p43 );\n\t\t\tfloat d4321 = dot( p43, p21 );\n\t\t\tfloat d1321 = dot( p13, p21 );\n\t\t\tfloat d4343 = dot( p43, p43 );\n\t\t\tfloat d2121 = dot( p21, p21 );\n\n\t\t\tfloat denom = d2121 * d4343 - d4321 * d4321;\n\n\t\t\tfloat numer = d1343 * d4321 - d1321 * d4343;\n\n\t\t\tmua = numer / denom;\n\t\t\tmua = clamp( mua, 0.0, 1.0 );\n\t\t\tmub = ( d1343 + d4321 * ( mua ) ) / d4343;\n\t\t\tmub = clamp( mub, 0.0, 1.0 );\n\n\t\t\treturn vec2( mua, mub );\n\n\t\t}\n\n\t\tvoid main() {\n\n\t\t\t#include \n\n\t\t\t/*#ifdef USE_DASH\n\n\t\t\t\tif ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps\n\n\t\t\t\tif ( mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize ) discard; // todo - FIX\n\n\t\t\t#endif*/\n \n\t\t\t#ifdef USE_DASH\n\n\t\t\t\tif ( vUv.y < - 1.0 || vUv.y > 1.0 ) discard; // discard endcaps\n \n \n bool unvisible = mod( vLineDistance + dashOffset, dashSize + gapSize ) > dashSize;\n //\u52A0\n #ifdef DASH_with_depth\n \n #else \n if (unvisible) discard; // todo - FIX\n #endif\n\t\t\t#endif\n \n\n\t\t\tfloat alpha = opacity;\n\n\t\t\t#ifdef WORLD_UNITS\n\n\t\t\t\t// Find the closest points on the view ray and the line segment\n\t\t\t\tvec3 rayEnd = normalize( worldPos.xyz ) * 1e5;\n\t\t\t\tvec3 lineDir = worldEnd - worldStart;\n\t\t\t\tvec2 params = closestLineToLine( worldStart, worldEnd, vec3( 0.0, 0.0, 0.0 ), rayEnd );\n\n\t\t\t\tvec3 p1 = worldStart + lineDir * params.x;\n\t\t\t\tvec3 p2 = rayEnd * params.y;\n\t\t\t\tvec3 delta = p1 - p2;\n\t\t\t\tfloat len = length( delta );\n\t\t\t\tfloat norm = len / lineWidth;\n\n\t\t\t\t#ifndef USE_DASH\n\n\t\t\t\t\t#ifdef USE_ALPHA_TO_COVERAGE\n\n\t\t\t\t\t\tfloat dnorm = fwidth( norm );\n\t\t\t\t\t\talpha = 1.0 - smoothstep( 0.5 - dnorm, 0.5 + dnorm, norm );\n\n\t\t\t\t\t#else\n\n\t\t\t\t\t\tif ( norm > 0.5 ) {\n\n\t\t\t\t\t\t\tdiscard;\n\n\t\t\t\t\t\t}\n\n\t\t\t\t\t#endif\n\n\t\t\t\t#endif\n\n\t\t\t#else\n\n\t\t\t\t#ifdef USE_ALPHA_TO_COVERAGE\n\n\t\t\t\t\t// artifacts appear on some hardware if a derivative is taken within a conditional\n\t\t\t\t\tfloat a = vUv.x;\n\t\t\t\t\tfloat b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n\t\t\t\t\tfloat len2 = a * a + b * b;\n\t\t\t\t\tfloat dlen = fwidth( len2 );\n\n\t\t\t\t\tif ( abs( vUv.y ) > 1.0 ) {\n\n\t\t\t\t\t\talpha = 1.0 - smoothstep( 1.0 - dlen, 1.0 + dlen, len2 );\n\n\t\t\t\t\t}\n\n\t\t\t\t#else\n\n\t\t\t\t\tif ( abs( vUv.y ) > 1.0 ) {\n\n\t\t\t\t\t\tfloat a = vUv.x;\n\t\t\t\t\t\tfloat b = ( vUv.y > 0.0 ) ? vUv.y - 1.0 : vUv.y + 1.0;\n\t\t\t\t\t\tfloat len2 = a * a + b * b;\n\n\t\t\t\t\t\tif ( len2 > 1.0 ) discard;\n\n\t\t\t\t\t}\n\n\t\t\t\t#endif\n\n\t\t\t#endif\n\n\t\t\tvec4 diffuseColor = vec4( diffuse, alpha );\n\n\n //\u52A0\n #if defined(GL_EXT_frag_depth) && defined(useDepth) || defined(FadeFar) \n \n float fragDepth = convertToLinear(gl_FragCoord.z);\n #if defined(FadeFar) \n float fadeOutFar = fadeFar * 1.3; //\u5B8C\u5168\u6D88\u5931\u8DDD\u79BB\n if(fragDepth > fadeOutFar){\n discard;\n }else if(fragDepth > fadeFar){\n alpha *= (fadeOutFar - fragDepth) / (fadeOutFar - fadeFar);\n diffuseColor.a = alpha;\n } \n #endif\n \n #if defined(GL_EXT_frag_depth) && defined(useDepth)\n \n float mixFactor = 0.0;\n float clipFactor = 0.0;\n //gl_FragCoord\u5927\u5C0F\u4E3A viewport client\u5927\u5C0F \n vec2 depthTxtCoords = vec2(gl_FragCoord.x - viewportOffset.x, gl_FragCoord.y - viewportOffset.y) / resolution;\n\n float textureDepth = convertToLinear(texture2D(depthTexture, depthTxtCoords).r);\n\n float delta = fragDepth - textureDepth;\n\n if (delta > 0.0)\n {\n \n mixFactor = clamp(delta / occlusionDistance, 0.0, maxOcclusionFactor);\n clipFactor = clamp(delta / clipDistance, 0.0, maxClipFactor);\n }\n \n if (clipFactor == 1.0)\n {\n discard;\n }\n \n vec4 backColor_ = vec4(backColor, alpha /* opacity */); //vec4(0.8,0.8,0.8, 0.8*opacity);\n \n #ifdef DASH_with_depth \n // \u53EA\u5728\u88AB\u906E\u4F4F\u7684\u90E8\u5206\u663E\u793A\u865A\u7EBF, \u6240\u4EE5\u82E5\u540C\u65F6\u662F\u865A\u7EBF\u4E0D\u53EF\u89C1\u90E8\u5206\u548C\u88AB\u906E\u4F4F\u65F6\uFF0C a\u4E3A0\n if(unvisible) backColor_.a = 0.0;\n #endif \n \n //vec4 diffuseColor = vec4(mix(diffuse, backColor_, mixFactor), opacity*(1.0 - clipFactor));\n \n \n \n diffuseColor = mix(diffuseColor, backColor_ , mixFactor); \n \n \n diffuseColor.a *= (1.0 - clipFactor); \n \n #endif\n #endif\n\n\n\t\t\t#include \n\t\t\t#include \n\n\t\t\t//gl_FragColor = vec4( diffuseColor.rgb, alpha );\n\t\t\tgl_FragColor = diffuseColor;\n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\t\t\t#include \n\n\t\t}\n\t\t" }; class LineMaterial extends ShaderMaterial { constructor(parameters) { var { vs, fs } = Potree.Common.changeShaderToWebgl2(ShaderLib['line'].vertexShader, ShaderLib['line'].fragmentShader, 'ShaderMaterial'); super({ type: 'LineMaterial', uniforms: UniformsUtils.clone(ShaderLib['line'].uniforms), vertexShader: vs, fragmentShader: fs, clipping: true // required for clipping support }); this.isLineMaterial = true; this.lineWidth_ = 0; this.supportExtDepth = parameters.supportExtDepth; this.depthTestWhenPick = false; //pick时是否识别点云等 if (parameters.color) { this.color = new Color(parameters.color); } if (parameters.backColor) { this.uniforms.backColor.value = new Color(parameters.backColor); } if (parameters.clipDistance) { this.uniforms.clipDistance.value = parameters.clipDistance; } if (parameters.occlusionDistance) { this.uniforms.occlusionDistance.value = parameters.occlusionDistance; } if (parameters.maxClipFactor) { this.uniforms.maxClipFactor.value = parameters.maxClipFactor; } Object.defineProperties(this, { color: { enumerable: true, get: function get() { return this.uniforms.diffuse.value; }, set: function set(value) { this.uniforms.diffuse.value = value; } }, worldUnits: { enumerable: true, get: function get() { return 'WORLD_UNITS' in this.defines; }, set: function set(value) { if (value === true) { this.defines.WORLD_UNITS = ''; } else { delete this.defines.WORLD_UNITS; } } }, lineWidth: { enumerable: true, get: function get() { return this.lineWidth_; //this.uniforms.lineWidth.value; }, set: function set(value) { this.uniforms.lineWidth.value = value * window.devicePixelRatio; this.lineWidth_ = value; } }, dashed: { enumerable: true, get: function get() { return Boolean('USE_DASH' in this.defines); }, set(value) { if (Boolean(value) !== Boolean('USE_DASH' in this.defines)) { this.needsUpdate = true; } if (value === true) { this.defines.USE_DASH = ''; } else { delete this.defines.USE_DASH; } } }, dashScale: { enumerable: true, get: function get() { return this.uniforms.dashScale.value; }, set: function set(value) { this.uniforms.dashScale.value = value; } }, dashSize: { enumerable: true, get: function get() { return this.uniforms.dashSize.value; }, set: function set(value) { this.uniforms.dashSize.value = value; } }, dashOffset: { enumerable: true, get: function get() { return this.uniforms.dashOffset.value; }, set: function set(value) { this.uniforms.dashOffset.value = value; } }, gapSize: { enumerable: true, get: function get() { return this.uniforms.gapSize.value; }, set: function set(value) { this.uniforms.gapSize.value = value; } }, opacity: { enumerable: true, get: function get() { return this.uniforms.opacity.value; }, set: function set(value) { this.uniforms.opacity.value = value; } }, resolution: { enumerable: true, get: function get() { return this.uniforms.resolution.value; }, set: function set(value) { this.uniforms.resolution.value.copy(value); } }, alphaToCoverage: { enumerable: true, get: function get() { return Boolean('USE_ALPHA_TO_COVERAGE' in this.defines); }, set: function set(value) { if (Boolean(value) !== Boolean('USE_ALPHA_TO_COVERAGE' in this.defines)) { this.needsUpdate = true; } if (value === true) { this.defines.USE_ALPHA_TO_COVERAGE = ''; this.extensions.derivatives = true; } else { delete this.defines.USE_ALPHA_TO_COVERAGE; this.extensions.derivatives = false; } } }, dashWithDepth: { //add enumerable: true, get: function get() { return 'DASH_with_depth' in this.defines; }, set: function set(value) { value = value && !!this.supportExtDepth; if (value != this.dashWithDepth) { if (value) { this.defines.DASH_with_depth = ''; } else { delete this.defines.DASH_with_depth; } this.needsUpdate = true; } } } }); this.events = { setSize: e => { //如果出现横条状的异常,往往是viewportOffset出错 //地图不需要 var viewport = e.viewport; //console.log(viewport.name, viewport.resolution2) this.uniforms.resolution.value.copy(viewport.resolution2); this.uniforms.devicePixelRatio.value = window.devicePixelRatio; this.lineWidth = this.lineWidth_; //update if (!this.realUseDepth || !e.viewport) return; var viewportOffset = viewport.offset || new THREE.Vector2(); this.uniforms.viewportOffset.value.copy(viewportOffset); }, render: e => { //before render 如果有大于两个viewport的话,不同viewport用不同的depthTex this.useDepth && this.updateDepthParams(e); var viewport = e.viewport || viewer.mainViewport; if (viewport != this.lastViewport) { //当mapViewer要渲染测量线后,就需要变viewport this.events.setSize({ viewport }); } this.lastViewport = viewport; } }; this.setValues(parameters); var viewport = viewer.mainViewport; this.events.setSize({ viewport }); viewer.addEventListener('resize', this.events.setSize); viewer.addEventListener("render.begin", this.events.render); } get useDepth() { return this.useDepth_; } set useDepth(value) { value = value && this.supportExtDepth; //如果不支持 EXT_DEPTH 的话会失效 if (this.useDepth_ != value) { this.setRealDepth(value); this.useDepth_ = value; } } setRealDepth(useDepth, viewport) { //确实使用到depthTex if (this.realUseDepth != useDepth) { if (useDepth) { this.defines.useDepth = ''; } else { delete this.defines.useDepth; } this.realUseDepth = useDepth; if (this.autoDepthTest) this.depthWrite = this.depthTest = !useDepth; //如果useDepth = false,使用原始的depthTest this.needsUpdate = true; if (!viewport) viewport = viewer.mainViewport; //暂时这么设置 useDepth && this.events.setSize({ viewport }); } } set fadeFar(far) { var needsUpdate = !this.fadeFar != !far; //null为全部范围可见 //console.log('fadeFar needsUpdate', needsUpdate) if (far) { this.defines.FadeFar = true; this.uniforms.fadeFar.value = far; } else { delete this.defines.FadeFar; } needsUpdate && (this.needsUpdate = true); } get fadeFar() { return 'FadeFar' in this.defines && this.uniforms.fadeFar.value; } updateDepthParams() { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var viewport = e.viewport || viewer.mainViewport; var camera = viewport.camera; var hasDepth = this.useDepth && camera.isPerspectiveCamera && (Potree.settings.pointEnableRT || Potree.settings.displayMode == 'showPanos' || viewer.useEDL); this.setRealDepth(hasDepth, viewport); if (hasDepth) { this.uniforms.depthTexture.value = viewer.getPRenderer().getRtEDL(viewport).depthTexture; //其实只赋值一次就行 } //hasDepth or FadeFar: this.uniforms.nearPlane.value = camera.near; this.uniforms.farPlane.value = camera.far; //this.uniforms.uUseOrthographicCamera.value = !camera.isPerspectiveCamera } } var _start$2 = new Vector3(); var _end$2 = new Vector3(); var _start4 = new Vector4(); var _end4 = new Vector4(); var _ssOrigin = new Vector4(); var _ssOrigin3 = new Vector3(); var _mvMatrix = new Matrix4(); var _line = new Line3(); var _closestPoint = new Vector3(); var _box$5 = new Box3(); var _sphere$4 = new Sphere(); var _clipToWorldVector = new Vector4(); var _ray$4, _instanceStart, _instanceEnd, _lineWidth; // Returns the margin required to expand by in world space given the distance from the camera, // line width, resolution, and camera projection function getWorldSpaceHalfWidth(camera, distance, resolution) { // transform into clip space, adjust the x and y values by the pixel width offset, then // transform back into world space to get world offset. Note clip space is [-1, 1] so full // width does not need to be halved. _clipToWorldVector.set(0, 0, -distance, 1.0).applyMatrix4(camera.projectionMatrix); _clipToWorldVector.multiplyScalar(1.0 / _clipToWorldVector.w); _clipToWorldVector.x = _lineWidth / resolution.width; _clipToWorldVector.y = _lineWidth / resolution.height; _clipToWorldVector.applyMatrix4(camera.projectionMatrixInverse); _clipToWorldVector.multiplyScalar(1.0 / _clipToWorldVector.w); return Math.abs(Math.max(_clipToWorldVector.x, _clipToWorldVector.y)); } function raycastWorldUnits(lineSegments, intersects) { for (var i = 0, l = _instanceStart.count; i < l; i++) { _line.start.fromBufferAttribute(_instanceStart, i); _line.end.fromBufferAttribute(_instanceEnd, i); var pointOnLine = new Vector3(); var point = new Vector3(); _ray$4.distanceSqToSegment(_line.start, _line.end, point, pointOnLine); var isInside = point.distanceTo(pointOnLine) < _lineWidth * 0.5; if (isInside) { intersects.push({ point, pointOnLine, distance: _ray$4.origin.distanceTo(point), object: lineSegments, face: null, faceIndex: i, uv: null, uv2: null }); } } } function raycastScreenSpace(lineSegments, camera, intersects) { var projectionMatrix = camera.projectionMatrix; var material = lineSegments.material; var resolution = material.resolution; var matrixWorld = lineSegments.matrixWorld; var geometry = lineSegments.geometry; var instanceStart = geometry.attributes.instanceStart; var instanceEnd = geometry.attributes.instanceEnd; var near = -camera.near; // // pick a point 1 unit out along the ray to avoid the ray origin // sitting at the camera origin which will cause "w" to be 0 when // applying the projection matrix. _ray$4.at(1, _ssOrigin); // ndc space [ - 1.0, 1.0 ] _ssOrigin.w = 1; _ssOrigin.applyMatrix4(camera.matrixWorldInverse); _ssOrigin.applyMatrix4(projectionMatrix); _ssOrigin.multiplyScalar(1 / _ssOrigin.w); // screen space _ssOrigin.x *= resolution.x / 2; _ssOrigin.y *= resolution.y / 2; _ssOrigin.z = 0; _ssOrigin3.copy(_ssOrigin); _mvMatrix.multiplyMatrices(camera.matrixWorldInverse, matrixWorld); for (var i = 0, l = instanceStart.count; i < l; i++) { _start4.fromBufferAttribute(instanceStart, i); _end4.fromBufferAttribute(instanceEnd, i); _start4.w = 1; _end4.w = 1; // camera space _start4.applyMatrix4(_mvMatrix); _end4.applyMatrix4(_mvMatrix); // skip the segment if it's entirely behind the camera var isBehindCameraNear = _start4.z > near && _end4.z > near; if (isBehindCameraNear) { continue; } // trim the segment if it extends behind camera near if (_start4.z > near) { var deltaDist = _start4.z - _end4.z; var t = (_start4.z - near) / deltaDist; _start4.lerp(_end4, t); } else if (_end4.z > near) { var _deltaDist = _end4.z - _start4.z; var _t = (_end4.z - near) / _deltaDist; _end4.lerp(_start4, _t); } // clip space _start4.applyMatrix4(projectionMatrix); _end4.applyMatrix4(projectionMatrix); // ndc space [ - 1.0, 1.0 ] _start4.multiplyScalar(1 / _start4.w); _end4.multiplyScalar(1 / _end4.w); // screen space _start4.x *= resolution.x / 2; _start4.y *= resolution.y / 2; _end4.x *= resolution.x / 2; _end4.y *= resolution.y / 2; // create 2d segment _line.start.copy(_start4); _line.start.z = 0; _line.end.copy(_end4); _line.end.z = 0; // get closest point on ray to segment var param = _line.closestPointToPointParameter(_ssOrigin3, true); _line.at(param, _closestPoint); // check if the intersection point is within clip space var zPos = MathUtils.lerp(_start4.z, _end4.z, param); var isInClipSpace = zPos >= -1 && zPos <= 1; var isInside = _ssOrigin3.distanceTo(_closestPoint) < _lineWidth * 0.5; if (isInClipSpace && isInside) { _line.start.fromBufferAttribute(instanceStart, i); _line.end.fromBufferAttribute(instanceEnd, i); _line.start.applyMatrix4(matrixWorld); _line.end.applyMatrix4(matrixWorld); var pointOnLine = new Vector3(); var point = new Vector3(); _ray$4.distanceSqToSegment(_line.start, _line.end, point, pointOnLine); intersects.push({ point: point, pointOnLine: pointOnLine, distance: _ray$4.origin.distanceTo(point), object: lineSegments, face: null, faceIndex: i, uv: null, uv2: null }); } } } class LineSegments2 extends Mesh { constructor() { var geometry = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new LineSegmentsGeometry(); var material = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new LineMaterial({ color: Math.random() * 0xffffff }); super(geometry, material); this.isLineSegments2 = true; this.type = 'LineSegments2'; } // for backwards-compatibility, but could be a method of LineSegmentsGeometry... computeLineDistances() { var geometry = this.geometry; var instanceStart = geometry.attributes.instanceStart; var instanceEnd = geometry.attributes.instanceEnd; var lineDistances = new Float32Array(2 * instanceStart.count); for (var i = 0, j = 0, l = instanceStart.count; i < l; i++, j += 2) { _start$2.fromBufferAttribute(instanceStart, i); _end$2.fromBufferAttribute(instanceEnd, i); lineDistances[j] = j === 0 ? 0 : lineDistances[j - 1]; lineDistances[j + 1] = lineDistances[j] + _start$2.distanceTo(_end$2); } var instanceDistanceBuffer = new InstancedInterleavedBuffer(lineDistances, 2, 1); // d0, d1 geometry.setAttribute('instanceDistanceStart', new InterleavedBufferAttribute(instanceDistanceBuffer, 1, 0)); // d0 geometry.setAttribute('instanceDistanceEnd', new InterleavedBufferAttribute(instanceDistanceBuffer, 1, 1)); // d1 return this; } raycast(raycaster, intersects) { var worldUnits = this.material.worldUnits; var camera = raycaster.camera; if (camera === null && !worldUnits) { console.error('LineSegments2: "Raycaster.camera" needs to be set in order to raycast against LineSegments2 while worldUnits is set to false.'); } var threshold = raycaster.params.Line2 !== undefined ? raycaster.params.Line2.threshold || 0 : 0; _ray$4 = raycaster.ray; var matrixWorld = this.matrixWorld; var geometry = this.geometry; var material = this.material; _lineWidth = material.lineWidth + threshold; _instanceStart = geometry.attributes.instanceStart; _instanceEnd = geometry.attributes.instanceEnd; // check if we intersect the sphere bounds if (geometry.boundingSphere === null) { geometry.computeBoundingSphere(); } _sphere$4.copy(geometry.boundingSphere).applyMatrix4(matrixWorld); // increase the sphere bounds by the worst case line screen space width var sphereMargin; if (worldUnits) { sphereMargin = _lineWidth * 0.5; } else { var distanceToSphere = Math.max(camera.near, _sphere$4.distanceToPoint(_ray$4.origin)); sphereMargin = getWorldSpaceHalfWidth(camera, distanceToSphere, material.resolution); } _sphere$4.radius += sphereMargin; if (_ray$4.intersectsSphere(_sphere$4) === false) { return; } // check if we intersect the box bounds if (geometry.boundingBox === null) { geometry.computeBoundingBox(); } _box$5.copy(geometry.boundingBox).applyMatrix4(matrixWorld); // increase the box bounds by the worst case line width var boxMargin; if (worldUnits) { boxMargin = _lineWidth * 0.5; } else { var distanceToBox = Math.max(camera.near, _box$5.distanceToPoint(_ray$4.origin)); boxMargin = getWorldSpaceHalfWidth(camera, distanceToBox, material.resolution); } _box$5.expandByScalar(boxMargin); if (_ray$4.intersectsBox(_box$5) === false) { return; } if (worldUnits) { raycastWorldUnits(this, intersects); } else { raycastScreenSpace(this, camera, intersects); } } } class LineGeometry extends LineSegmentsGeometry { constructor() { super(); this.isLineGeometry = true; this.type = 'LineGeometry'; } setPositions(array) { //见potree.shim.js // converts [ x1, y1, z1, x2, y2, z2, ... ] to pairs format var length = array.length - 3; var points = new Float32Array(2 * length); for (var i = 0; i < length; i += 3) { points[2 * i] = array[i]; points[2 * i + 1] = array[i + 1]; points[2 * i + 2] = array[i + 2]; points[2 * i + 3] = array[i + 3]; points[2 * i + 4] = array[i + 4]; points[2 * i + 5] = array[i + 5]; } super.setPositions(points); return this; } setColors(array) { // converts [ r1, g1, b1, r2, g2, b2, ... ] to pairs format var length = array.length - 3; var colors = new Float32Array(2 * length); for (var i = 0; i < length; i += 3) { colors[2 * i] = array[i]; colors[2 * i + 1] = array[i + 1]; colors[2 * i + 2] = array[i + 2]; colors[2 * i + 3] = array[i + 3]; colors[2 * i + 4] = array[i + 4]; colors[2 * i + 5] = array[i + 5]; } super.setColors(colors); return this; } fromLine(line) { var geometry = line.geometry; this.setPositions(geometry.attributes.position.array); // assumes non-indexed // set colors, maybe return this; } } class Line2 extends LineSegments2 { constructor() { var geometry = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new LineGeometry(); var material = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new LineMaterial({ color: Math.random() * 0xffffff }); super(geometry, material); this.isLine2 = true; this.type = 'Line2'; } } //old function createHeightLine() { var lineGeometry = new LineGeometry(); lineGeometry.setPositions([0, 0, 0, 0, 0, 0]); var lineMaterial = new LineMaterial({ color: 0x00ff00, dashSize: 5, gapSize: 2, linewidth: 2, resolution: new Vector2(1000, 1000) }); lineMaterial.depthTest = false; var heightEdge = new Line2(lineGeometry, lineMaterial); heightEdge.visible = false; //this.add(this.heightEdge); return heightEdge; } function createHeightLabel() { var heightLabel = new TextSprite$1(''); heightLabel.setTextColor({ r: 140, g: 250, b: 140, a: 1.0 }); heightLabel.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); heightLabel.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); heightLabel.fontsize = 16; heightLabel.material.depthTest = false; heightLabel.material.opacity = 1; heightLabel.visible = false; return heightLabel; } function createAreaLabel() { var areaLabel = new TextSprite$1(''); areaLabel.setTextColor({ r: 140, g: 250, b: 140, a: 1.0 }); areaLabel.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); areaLabel.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); areaLabel.fontsize = 16; areaLabel.material.depthTest = false; areaLabel.material.opacity = 1; areaLabel.visible = false; return areaLabel; } function createCircleRadiusLabel() { var circleRadiusLabel = new TextSprite$1(""); circleRadiusLabel.setTextColor({ r: 140, g: 250, b: 140, a: 1.0 }); circleRadiusLabel.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); circleRadiusLabel.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); circleRadiusLabel.fontsize = 16; circleRadiusLabel.material.depthTest = false; circleRadiusLabel.material.opacity = 1; circleRadiusLabel.visible = false; return circleRadiusLabel; } function createCircleRadiusLine() { var lineGeometry = new LineGeometry(); lineGeometry.setPositions([0, 0, 0, 0, 0, 0]); var lineMaterial = new LineMaterial({ color: 0xff0000, linewidth: 2, resolution: new Vector2(1000, 1000), gapSize: 1, dashed: true }); lineMaterial.depthTest = false; var circleRadiusLine = new Line2(lineGeometry, lineMaterial); circleRadiusLine.visible = false; return circleRadiusLine; } function createCircleLine() { var coordinates = []; var n = 128; for (var i = 0; i <= n; i++) { var u0 = 2 * Math.PI * (i / n); var u1 = 2 * Math.PI * (i + 1) / n; var p0 = new Vector3(Math.cos(u0), Math.sin(u0), 0); var p1 = new Vector3(Math.cos(u1), Math.sin(u1), 0); coordinates.push(...p0.toArray(), ...p1.toArray()); } var geometry = new LineGeometry(); geometry.setPositions(coordinates); var material = new LineMaterial({ color: 0xff0000, dashSize: 5, gapSize: 2, linewidth: 2, resolution: new Vector2(1000, 1000) }); material.depthTest = false; var circleLine = new Line2(geometry, material); circleLine.visible = false; circleLine.computeLineDistances(); return circleLine; } function createCircleCenter() { var sg = new SphereGeometry(1, 32, 32); var sm = new MeshNormalMaterial(); var circleCenter = new Mesh(sg, sm); circleCenter.visible = false; return circleCenter; } function createLine() { var geometry = new LineGeometry(); geometry.setPositions([0, 0, 0, 0, 0, 0]); var material = new LineMaterial({ color: 0xff0000, linewidth: 2, resolution: new Vector2(1000, 1000), gapSize: 1, dashed: true }); material.depthTest = false; var line = new Line2(geometry, material); return line; } function createCircle() { var coordinates = []; var n = 128; for (var i = 0; i <= n; i++) { var u0 = 2 * Math.PI * (i / n); var u1 = 2 * Math.PI * (i + 1) / n; var p0 = new Vector3(Math.cos(u0), Math.sin(u0), 0); var p1 = new Vector3(Math.cos(u1), Math.sin(u1), 0); coordinates.push(...p0.toArray(), ...p1.toArray()); } var geometry = new LineGeometry(); geometry.setPositions(coordinates); var material = new LineMaterial({ color: 0xff0000, dashSize: 5, gapSize: 2, linewidth: 2, resolution: new Vector2(1000, 1000) }); material.depthTest = false; var line = new Line2(geometry, material); line.computeLineDistances(); return line; } function createAzimuth() { var azimuth = { label: null, center: null, target: null, north: null, centerToNorth: null, centerToTarget: null, centerToTargetground: null, targetgroundToTarget: null, circle: null, node: null }; var sg = new SphereGeometry(1, 32, 32); var sm = new MeshNormalMaterial(); { var label = new TextSprite$1(""); label.setTextColor({ r: 140, g: 250, b: 140, a: 1.0 }); label.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); label.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); label.fontsize = 16; label.material.depthTest = false; label.material.opacity = 1; azimuth.label = label; } azimuth.center = new Mesh(sg, sm); azimuth.target = new Mesh(sg, sm); azimuth.north = new Mesh(sg, sm); azimuth.centerToNorth = createLine(); azimuth.centerToTarget = createLine(); azimuth.centerToTargetground = createLine(); azimuth.targetgroundToTarget = createLine(); azimuth.circle = createCircle(); azimuth.node = new Object3D(); azimuth.node.add(azimuth.centerToNorth, azimuth.centerToTarget, azimuth.centerToTargetground, azimuth.targetgroundToTarget, azimuth.circle, azimuth.label, azimuth.center, azimuth.target, azimuth.north); return azimuth; } class Measure extends Object3D { constructor() { super(); this.constructor.counter = this.constructor.counter === undefined ? 0 : this.constructor.counter + 1; this.name = 'Measure_' + this.constructor.counter; this.points = []; this._showDistances = true; this._showCoordinates = false; this._showArea = false; this._closed = true; this._showAngles = false; this._showCircle = false; this._showHeight = false; this._showEdges = true; this._showAzimuth = false; this.maxMarkers = Number.MAX_SAFE_INTEGER; this.sphereGeometry = new SphereGeometry(0.4, 10, 10); this.color = new Color(0xff0000); this.spheres = []; this.edges = []; this.sphereLabels = []; this.edgeLabels = []; this.angleLabels = []; this.coordinateLabels = []; this.heightEdge = createHeightLine(); this.heightLabel = createHeightLabel(); this.areaLabel = createAreaLabel(); this.circleRadiusLabel = createCircleRadiusLabel(); this.circleRadiusLine = createCircleRadiusLine(); this.circleLine = createCircleLine(); this.circleCenter = createCircleCenter(); this.azimuth = createAzimuth(); this.add(this.heightEdge); this.add(this.heightLabel); this.add(this.areaLabel); this.add(this.circleRadiusLabel); this.add(this.circleRadiusLine); this.add(this.circleLine); this.add(this.circleCenter); this.add(this.azimuth.node); } createSphereMaterial() { var sphereMaterial = new MeshLambertMaterial({ //shading: THREE.SmoothShading, color: this.color, depthTest: false, depthWrite: false }); return sphereMaterial; } addMarker(point) { if (point.x != null) { point = { position: point }; } else if (point instanceof Array) { point = { position: new Vector3(...point) }; } this.points.push(point); // sphere var sphere = new Mesh(this.sphereGeometry, this.createSphereMaterial()); this.add(sphere); this.spheres.push(sphere); { // edges var lineGeometry = new LineGeometry(); lineGeometry.setPositions([0, 0, 0, 0, 0, 0]); var lineMaterial = new LineMaterial({ color: 0xff0000, linewidth: 2, resolution: new Vector2(1000, 1000) }); lineMaterial.depthTest = false; var edge = new Line2(lineGeometry, lineMaterial); edge.visible = true; this.add(edge); this.edges.push(edge); } { // edge labels var edgeLabel = new TextSprite$1(); edgeLabel.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); edgeLabel.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); edgeLabel.material.depthTest = false; edgeLabel.visible = false; edgeLabel.fontsize = 16; this.edgeLabels.push(edgeLabel); this.add(edgeLabel); } { // angle labels var angleLabel = new TextSprite$1(); angleLabel.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); angleLabel.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); angleLabel.fontsize = 16; angleLabel.material.depthTest = false; angleLabel.material.opacity = 1; angleLabel.visible = false; this.angleLabels.push(angleLabel); this.add(angleLabel); } { // coordinate labels var coordinateLabel = new TextSprite$1(); coordinateLabel.setBorderColor({ r: 0, g: 0, b: 0, a: 1.0 }); coordinateLabel.setBackgroundColor({ r: 0, g: 0, b: 0, a: 1.0 }); coordinateLabel.fontsize = 16; coordinateLabel.material.depthTest = false; coordinateLabel.material.opacity = 1; coordinateLabel.visible = false; this.coordinateLabels.push(coordinateLabel); this.add(coordinateLabel); } { // Event Listeners var drag = e => { var I = Utils.getMousePointCloudIntersection(e.drag.end, e.viewer.scene.getActiveCamera(), e.viewer, e.viewer.scene.pointclouds, { pickClipped: true }); if (I) { var i = this.spheres.indexOf(e.drag.object); if (i !== -1) { var _point = this.points[i]; // loop through current keys and cleanup ones that will be orphaned for (var key of Object.keys(_point)) { if (!I.point[key]) { delete _point[key]; } } for (var _key of Object.keys(I.point).filter(e => e !== 'position')) { _point[_key] = I.point[_key]; } this.setPosition(i, I.location); } } }; var drop = e => { var i = this.spheres.indexOf(e.drag.object); if (i !== -1) { this.dispatchEvent({ 'type': 'marker_dropped', 'measurement': this, 'index': i }); } }; var mouseover = e => e.object.material.emissive.setHex(0x888888); var mouseleave = e => e.object.material.emissive.setHex(0x000000); sphere.addEventListener('drag', drag); sphere.addEventListener('drop', drop); sphere.addEventListener('mouseover', mouseover); sphere.addEventListener('mouseleave', mouseleave); } var event = { type: 'marker_added', measurement: this, sphere: sphere }; this.dispatchEvent(event); this.setMarker(this.points.length - 1, point); } removeMarker(index) { this.points.splice(index, 1); this.remove(this.spheres[index]); var edgeIndex = index === 0 ? 0 : index - 1; this.remove(this.edges[edgeIndex]); this.edges.splice(edgeIndex, 1); this.remove(this.edgeLabels[edgeIndex]); this.edgeLabels.splice(edgeIndex, 1); this.coordinateLabels.splice(index, 1); this.remove(this.angleLabels[index]); this.angleLabels.splice(index, 1); this.spheres.splice(index, 1); this.update(); this.dispatchEvent({ type: 'marker_removed', measurement: this }); } setMarker(index, point) { this.points[index] = point; var event = { type: 'marker_moved', measure: this, index: index, position: point.position.clone() }; this.dispatchEvent(event); this.update(); } setPosition(index, position) { var point = this.points[index]; point.position.copy(position); var event = { type: 'marker_moved', measure: this, index: index, position: position.clone() }; this.dispatchEvent(event); this.update(); } getArea() { var area = 0; var j = this.points.length - 1; for (var i = 0; i < this.points.length; i++) { var p1 = this.points[i].position; var p2 = this.points[j].position; area += (p2.x + p1.x) * (p1.y - p2.y); j = i; } return Math.abs(area / 2); } getTotalDistance() { if (this.points.length === 0) { return 0; } var distance = 0; for (var i = 1; i < this.points.length; i++) { var prev = this.points[i - 1].position; var curr = this.points[i].position; var d = prev.distanceTo(curr); distance += d; } if (this.closed && this.points.length > 1) { var first = this.points[0].position; var last = this.points[this.points.length - 1].position; var _d = last.distanceTo(first); distance += _d; } return distance; } getAngleBetweenLines(cornerPoint, point1, point2) { var v1 = new Vector3().subVectors(point1.position, cornerPoint.position); var v2 = new Vector3().subVectors(point2.position, cornerPoint.position); // avoid the error printed by threejs if denominator is 0 var denominator = Math.sqrt(v1.lengthSq() * v2.lengthSq()); if (denominator === 0) { return 0; } else { return v1.angleTo(v2); } } getAngle(index) { if (this.points.length < 3 || index >= this.points.length) { return 0; } var previous = index === 0 ? this.points[this.points.length - 1] : this.points[index - 1]; var point = this.points[index]; var next = this.points[(index + 1) % this.points.length]; return this.getAngleBetweenLines(point, previous, next); } // updateAzimuth(){ // // if(this.points.length !== 2){ // // return; // // } // // const azimuth = this.azimuth; // // const [p0, p1] = this.points; // // const r = p0.position.distanceTo(p1.position); // } update() { if (this.points.length === 0) { return; } else if (this.points.length === 1) { var point = this.points[0]; var position = point.position; this.spheres[0].position.copy(position); { // coordinate labels var coordinateLabel = this.coordinateLabels[0]; var msg = position.toArray().map(p => Utils.addCommas(p.toFixed(2))).join(" / "); coordinateLabel.setText(msg); coordinateLabel.visible = this.showCoordinates; } return; } var lastIndex = this.points.length - 1; var centroid = new Vector3(); for (var i = 0; i <= lastIndex; i++) { var _point2 = this.points[i]; centroid.add(_point2.position); } centroid.divideScalar(this.points.length); for (var _i = 0; _i <= lastIndex; _i++) { var index = _i; var nextIndex = _i + 1 > lastIndex ? 0 : _i + 1; var previousIndex = _i === 0 ? lastIndex : _i - 1; var _point3 = this.points[index]; var nextPoint = this.points[nextIndex]; var previousPoint = this.points[previousIndex]; var sphere = this.spheres[index]; // spheres sphere.position.copy(_point3.position); sphere.material.color = this.color; { // edges var edge = this.edges[index]; edge.material.color = this.color; edge.position.copy(_point3.position); edge.geometry.setPositions([0, 0, 0, ...nextPoint.position.clone().sub(_point3.position).toArray()]); edge.geometry.verticesNeedUpdate = true; edge.geometry.computeBoundingSphere(); edge.computeLineDistances(); edge.visible = index < lastIndex || this.closed; if (!this.showEdges) { edge.visible = false; } } { // edge labels var edgeLabel = this.edgeLabels[_i]; var center = new Vector3().add(_point3.position); center.add(nextPoint.position); center = center.multiplyScalar(0.5); var distance = _point3.position.distanceTo(nextPoint.position); edgeLabel.position.copy(center); var suffix = ""; if (this.lengthUnit != null && this.lengthUnitDisplay != null) { distance = distance / this.lengthUnit.unitspermeter * this.lengthUnitDisplay.unitspermeter; //convert to meters then to the display unit suffix = this.lengthUnitDisplay.code; } var txtLength = Utils.addCommas(distance.toFixed(2)); edgeLabel.setText("".concat(txtLength, " ").concat(suffix)); edgeLabel.visible = this.showDistances && (index < lastIndex || this.closed) && this.points.length >= 2 && distance > 0; } { // angle labels var angleLabel = this.angleLabels[_i]; var angle = this.getAngleBetweenLines(_point3, previousPoint, nextPoint); var dir = nextPoint.position.clone().sub(previousPoint.position); dir.multiplyScalar(0.5); dir = previousPoint.position.clone().add(dir).sub(_point3.position).normalize(); var dist = Math.min(_point3.position.distanceTo(previousPoint.position), _point3.position.distanceTo(nextPoint.position)); dist = dist / 9; var labelPos = _point3.position.clone().add(dir.multiplyScalar(dist)); angleLabel.position.copy(labelPos); var _msg = Utils.addCommas((angle * (180.0 / Math.PI)).toFixed(1)) + '\u00B0'; angleLabel.setText(_msg); angleLabel.visible = this.showAngles && (index < lastIndex || this.closed) && this.points.length >= 3 && angle > 0; } } { // update height stuff var heightEdge = this.heightEdge; heightEdge.visible = this.showHeight; this.heightLabel.visible = this.showHeight; if (this.showHeight) { var sorted = this.points.slice().sort((a, b) => a.position.z - b.position.z); var lowPoint = sorted[0].position.clone(); var highPoint = sorted[sorted.length - 1].position.clone(); var min = lowPoint.z; var max = highPoint.z; var height = max - min; var start = new Vector3(highPoint.x, highPoint.y, min); var end = new Vector3(highPoint.x, highPoint.y, max); heightEdge.position.copy(lowPoint); heightEdge.geometry.setPositions([0, 0, 0, ...start.clone().sub(lowPoint).toArray(), ...start.clone().sub(lowPoint).toArray(), ...end.clone().sub(lowPoint).toArray()]); heightEdge.geometry.verticesNeedUpdate = true; // heightEdge.geometry.computeLineDistances(); // heightEdge.geometry.lineDistancesNeedUpdate = true; heightEdge.geometry.computeBoundingSphere(); heightEdge.computeLineDistances(); // heightEdge.material.dashSize = height / 40; // heightEdge.material.gapSize = height / 40; var heightLabelPosition = start.clone().add(end).multiplyScalar(0.5); this.heightLabel.position.copy(heightLabelPosition); var _suffix = ""; if (this.lengthUnit != null && this.lengthUnitDisplay != null) { height = height / this.lengthUnit.unitspermeter * this.lengthUnitDisplay.unitspermeter; //convert to meters then to the display unit _suffix = this.lengthUnitDisplay.code; } var txtHeight = Utils.addCommas(height.toFixed(2)); var _msg2 = "".concat(txtHeight, " ").concat(_suffix); this.heightLabel.setText(_msg2); } } { // update circle stuff var circleRadiusLabel = this.circleRadiusLabel; var circleRadiusLine = this.circleRadiusLine; var circleLine = this.circleLine; var circleCenter = this.circleCenter; var circleOkay = this.points.length === 3; circleRadiusLabel.visible = this.showCircle && circleOkay; circleRadiusLine.visible = this.showCircle && circleOkay; circleLine.visible = this.showCircle && circleOkay; circleCenter.visible = this.showCircle && circleOkay; if (this.showCircle && circleOkay) { var A = this.points[0].position; var B = this.points[1].position; var C = this.points[2].position; var AB = B.clone().sub(A); var AC = C.clone().sub(A); var N = AC.clone().cross(AB).normalize(); var _center = Potree.Utils.computeCircleCenter(A, B, C); var radius = _center.distanceTo(A); var scale = radius / 20; circleCenter.position.copy(_center); circleCenter.scale.set(scale, scale, scale); //circleRadiusLine.geometry.vertices[0].set(0, 0, 0); //circleRadiusLine.geometry.vertices[1].copy(B.clone().sub(center)); circleRadiusLine.geometry.setPositions([0, 0, 0, ...B.clone().sub(_center).toArray()]); circleRadiusLine.geometry.verticesNeedUpdate = true; circleRadiusLine.geometry.computeBoundingSphere(); circleRadiusLine.position.copy(_center); circleRadiusLine.computeLineDistances(); var target = _center.clone().add(N); circleLine.position.copy(_center); circleLine.scale.set(radius, radius, radius); circleLine.lookAt(target); circleRadiusLabel.visible = true; circleRadiusLabel.position.copy(_center.clone().add(B).multiplyScalar(0.5)); circleRadiusLabel.setText("".concat(radius.toFixed(3))); } } { // update area label this.areaLabel.position.copy(centroid); this.areaLabel.visible = this.showArea && this.points.length >= 3; var area = this.getArea(); var _suffix2 = ""; if (this.lengthUnit != null && this.lengthUnitDisplay != null) { area = area / Math.pow(this.lengthUnit.unitspermeter, 2) * Math.pow(this.lengthUnitDisplay.unitspermeter, 2); //convert to square meters then to the square display unit _suffix2 = this.lengthUnitDisplay.code; } var txtArea = Utils.addCommas(area.toFixed(1)); var _msg3 = "".concat(txtArea, " ").concat(_suffix2, "\xB2"); this.areaLabel.setText(_msg3); } // this.updateAzimuth(); } raycast(raycaster, intersects) { for (var i = 0; i < this.points.length; i++) { var sphere = this.spheres[i]; sphere.raycast(raycaster, intersects); } // recalculate distances because they are not necessarely correct // for scaled objects. // see https://github.com/mrdoob/three.js/issues/5827 // TODO: remove this once the bug has been fixed for (var _i2 = 0; _i2 < intersects.length; _i2++) { var I = intersects[_i2]; I.distance = raycaster.ray.origin.distanceTo(I.point); } intersects.sort(function (a, b) { return a.distance - b.distance; }); } get showCoordinates() { return this._showCoordinates; } set showCoordinates(value) { this._showCoordinates = value; this.update(); } get showAngles() { return this._showAngles; } set showAngles(value) { this._showAngles = value; this.update(); } get showCircle() { return this._showCircle; } set showCircle(value) { this._showCircle = value; this.update(); } get showAzimuth() { return this._showAzimuth; } set showAzimuth(value) { this._showAzimuth = value; this.update(); } get showEdges() { return this._showEdges; } set showEdges(value) { this._showEdges = value; this.update(); } get showHeight() { return this._showHeight; } set showHeight(value) { this._showHeight = value; this.update(); } get showArea() { return this._showArea; } set showArea(value) { this._showArea = value; this.update(); } get closed() { return this._closed; } set closed(value) { this._closed = value; this.update(); } get showDistances() { return this._showDistances; } set showDistances(value) { this._showDistances = value; this.update(); } } class PolygonClipVolume extends Object3D { constructor(camera) { super(); this.constructor.counter = this.constructor.counter === undefined ? 0 : this.constructor.counter + 1; this.name = "polygon_clip_volume_" + this.constructor.counter; this.camera = camera.clone(); this.camera.rotation.set(...camera.rotation.toArray()); // [r85] workaround because camera.clone() doesn't work on rotation this.camera.rotation.order = camera.rotation.order; this.camera.updateMatrixWorld(); this.camera.updateProjectionMatrix(); this.camera.matrixWorldInverse.copy(this.camera.matrixWorld).invert(); this.viewMatrix = this.camera.matrixWorldInverse.clone(); this.projMatrix = this.camera.projectionMatrix.clone(); // projected markers this.markers = []; this.initialized = false; } addMarker() { var marker = new Mesh(); var cancel; var drag = e => { var size = e.viewer.renderer.getSize(new Vector2()); var projectedPos = new Vector3(2.0 * (e.drag.end.x / size.width) - 1.0, -2.0 * (e.drag.end.y / size.height) + 1.0, 0); marker.position.copy(projectedPos); }; var drop = e => { cancel(); }; cancel = e => { marker.removeEventListener("drag", drag); marker.removeEventListener("drop", drop); }; marker.addEventListener("drag", drag); marker.addEventListener("drop", drop); this.markers.push(marker); } removeLastMarker() { if (this.markers.length > 0) { this.markers.splice(this.markers.length - 1, 1); } } } ; class Utils { static async loadShapefileFeatures(file, callback) { var features = []; var handleFinish = () => { callback(features); }; var source = await shapefile.open(file); while (true) { var result = await source.read(); if (result.done) { handleFinish(); break; } if (result.value && result.value.type === 'Feature' && result.value.geometry !== undefined) { features.push(result.value); } } } static toString(value) { if (value.x != null) { return value.x.toFixed(2) + ', ' + value.y.toFixed(2) + ', ' + value.z.toFixed(2); } else { return '' + value + ''; } } static normalizeURL(url) { var u = new URL(url); return u.protocol + '//' + u.hostname + u.pathname.replace(/\/+/g, '/'); } static pathExists(url) { var req = XHRFactory.createXMLHttpRequest(); req.open('GET', url, false); req.send(null); if (req.status !== 200) { return false; } return true; } static debugSphere(parent, position, scale, color) { var geometry = new SphereGeometry(1, 8, 8); var material; if (color !== undefined) { material = new MeshBasicMaterial({ color: color }); } else { material = new MeshNormalMaterial(); } var sphere = new Mesh(geometry, material); sphere.position.copy(position); sphere.scale.set(scale, scale, scale); parent.add(sphere); return sphere; } static debugLine(parent, start, end, color) { var material = new LineBasicMaterial({ color: color }); var geometry = new Geometry(); var p1 = new Vector3(0, 0, 0); var p2 = end.clone().sub(start); geometry.vertices.push(p1, p2); var tl = new Line(geometry, material); tl.position.copy(start); parent.add(tl); var line = { node: tl, set: (start, end) => { geometry.vertices[0].copy(start); geometry.vertices[1].copy(end); geometry.verticesNeedUpdate = true; } }; return line; } static debugCircle(parent, center, radius, normal, color) { var material = new LineBasicMaterial({ color: color }); var geometry = new Geometry(); var n = 32; for (var i = 0; i <= n; i++) { var u0 = 2 * Math.PI * (i / n); var u1 = 2 * Math.PI * (i + 1) / n; var p0 = new Vector3(Math.cos(u0), Math.sin(u0), 0); var p1 = new Vector3(Math.cos(u1), Math.sin(u1), 0); geometry.vertices.push(p0, p1); } var tl = new Line(geometry, material); tl.position.copy(center); tl.scale.set(radius, radius, radius); parent.add(tl); } static debugBox(parent, box) { var transform = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Matrix4(); var color = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0xFFFF00; var vertices = [[box.min.x, box.min.y, box.min.z], [box.min.x, box.min.y, box.max.z], [box.min.x, box.max.y, box.min.z], [box.min.x, box.max.y, box.max.z], [box.max.x, box.min.y, box.min.z], [box.max.x, box.min.y, box.max.z], [box.max.x, box.max.y, box.min.z], [box.max.x, box.max.y, box.max.z]].map(v => new Vector3(...v)); var edges = [[0, 4], [4, 5], [5, 1], [1, 0], [2, 6], [6, 7], [7, 3], [3, 2], [0, 2], [4, 6], [5, 7], [1, 3]]; var center = box.getCenter(new Vector3()); var centroids = [{ position: [box.min.x, center.y, center.z], color: 0xFF0000 }, { position: [box.max.x, center.y, center.z], color: 0x880000 }, { position: [center.x, box.min.y, center.z], color: 0x00FF00 }, { position: [center.x, box.max.y, center.z], color: 0x008800 }, { position: [center.x, center.y, box.min.z], color: 0x0000FF }, { position: [center.x, center.y, box.max.z], color: 0x000088 }]; for (var vertex of vertices) { var pos = vertex.clone().applyMatrix4(transform); Utils.debugSphere(parent, pos, 0.1, 0xFF0000); } for (var edge of edges) { var start = vertices[edge[0]].clone().applyMatrix4(transform); var end = vertices[edge[1]].clone().applyMatrix4(transform); Utils.debugLine(parent, start, end, color); } for (var centroid of centroids) { var _pos = new Vector3(...centroid.position).applyMatrix4(transform); Utils.debugSphere(parent, _pos, 0.1, centroid.color); } } static debugPlane(parent, plane) { var size = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; var color = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0x0000FF; var planehelper = new PlaneHelper(plane, size, color); parent.add(planehelper); } /** * adapted from mhluska at https://github.com/mrdoob/three.js/issues/1561 */ static computeTransformedBoundingBox(box, transform) { var vertices = [new Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform), new Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform), new Vector3(box.max.x, box.min.y, box.min.z).applyMatrix4(transform), new Vector3(box.min.x, box.max.y, box.min.z).applyMatrix4(transform), new Vector3(box.min.x, box.min.y, box.max.z).applyMatrix4(transform), new Vector3(box.min.x, box.max.y, box.max.z).applyMatrix4(transform), new Vector3(box.max.x, box.max.y, box.min.z).applyMatrix4(transform), new Vector3(box.max.x, box.min.y, box.max.z).applyMatrix4(transform), new Vector3(box.max.x, box.max.y, box.max.z).applyMatrix4(transform)]; var boundingBox = new Box3(); boundingBox.setFromPoints(vertices); return boundingBox; } //感觉就是bound.applyMatrix4(transform) /** * add separators to large numbers * * @param nStr * @returns */ static addCommas(nStr) { nStr += ''; var x = nStr.split('.'); var x1 = x[0]; var x2 = x.length > 1 ? '.' + x[1] : ''; var rgx = /(\d+)(\d{3})/; while (rgx.test(x1)) { x1 = x1.replace(rgx, '$1' + ',' + '$2'); } return x1 + x2; } static removeCommas(str) { return str.replace(/,/g, ''); } /** * create worker from a string * * code from http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string */ static createWorker(code) { var blob = new Blob([code], { type: 'application/javascript' }); var worker = new Worker(URL.createObjectURL(blob)); return worker; } static moveTo(scene, endPosition, endTarget) { var view = scene.view; var camera = scene.getActiveCamera(); var animationDuration = 500; var easing = TWEEN.Easing.Quartic.Out; { // animate camera position var tween = new TWEEN.Tween(view.position).to(endPosition, animationDuration); tween.easing(easing); tween.start(); } { // animate camera target var camTargetDistance = camera.position.distanceTo(endTarget); var target = new Vector3().addVectors(camera.position, camera.getWorldDirection(new Vector3()).clone().multiplyScalar(camTargetDistance)); var _tween = new TWEEN.Tween(target).to(endTarget, animationDuration); _tween.easing(easing); _tween.onUpdate(() => { view.lookAt(target); }); _tween.onComplete(() => { view.lookAt(target); }); _tween.start(); } } static loadSkybox(path) { var parent = new Object3D("skybox_root"); var camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 100000); camera.up.set(0, 0, 1); var scene = new Scene(); var format = '.jpg'; var urls = [path + 'px' + format, path + 'nx' + format, path + 'py' + format, path + 'ny' + format, path + 'pz' + format, path + 'nz' + format]; var materialArray = []; { var _loop = function _loop() { var material = new MeshBasicMaterial({ map: null, side: BackSide, depthTest: false, depthWrite: false, color: 0x424556 }); materialArray.push(material); var loader = new TextureLoader(); loader.load(urls[i], function loaded(texture) { material.map = texture; material.needsUpdate = true; material.color.setHex(0xffffff); }, function progress(xhr) { // console.log( (xhr.loaded / xhr.total * 100) + '% loaded' ); }, function error(xhr) { console.log('An error happened', xhr); }); }; for (var i = 0; i < 6; i++) { _loop(); } } var skyGeometry = new BoxGeometry(700, 700, 700); var skybox = new Mesh(skyGeometry, materialArray); scene.add(skybox); scene.traverse(n => n.frustumCulled = false); // z up scene.rotation.x = Math.PI / 2; parent.children.push(camera); camera.parent = parent; return { camera, scene, parent }; } static createGrid(width, length, spacing, color) { var material = new LineBasicMaterial({ color: color || 0x888888 }); var geometry = new Geometry(); for (var i = 0; i <= length; i++) { geometry.vertices.push(new Vector3(-(spacing * width) / 2, i * spacing - spacing * length / 2, 0)); geometry.vertices.push(new Vector3(+(spacing * width) / 2, i * spacing - spacing * length / 2, 0)); } for (var _i = 0; _i <= width; _i++) { geometry.vertices.push(new Vector3(_i * spacing - spacing * width / 2, -(spacing * length) / 2, 0)); geometry.vertices.push(new Vector3(_i * spacing - spacing * width / 2, +(spacing * length) / 2, 0)); } var line = new LineSegments(geometry, material, LinePieces); line.receiveShadow = true; return line; } static createBackgroundTexture(width, height) { function gauss(x, y) { return 1 / (2 * Math.PI) * Math.exp(-(x * x + y * y) / 2); } ; // map.magFilter = THREE.NearestFilter; var size = width * height; var data = new Uint8Array(3 * size); var chroma = [1, 1.5, 1.7]; var max = gauss(0, 0); for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var u = 2 * (x / width) - 1; var v = 2 * (y / height) - 1; var i = x + width * y; var d = gauss(2 * u, 2 * v) / max; var r = (Math.random() + Math.random() + Math.random()) / 3; r = (d * 0.5 + 0.5) * r * 0.03; r = r * 0.4; // d = Math.pow(d, 0.6); data[3 * i + 0] = 255 * (d / 15 + 0.05 + r) * chroma[0]; data[3 * i + 1] = 255 * (d / 15 + 0.05 + r) * chroma[1]; data[3 * i + 2] = 255 * (d / 15 + 0.05 + r) * chroma[2]; } } var texture = new DataTexture(data, width, height, RGBFormat); texture.needsUpdate = true; return texture; } static getMousePointCloudIntersection(mouse, camera, viewer, pointclouds) { var params = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var renderer = viewer.renderer; var nmouse = { x: mouse.x / renderer.domElement.clientWidth * 2 - 1, y: -(mouse.y / renderer.domElement.clientHeight) * 2 + 1 }; var pickParams = {}; if (params.pickClipped) { pickParams.pickClipped = params.pickClipped; } pickParams.x = mouse.x; pickParams.y = renderer.domElement.clientHeight - mouse.y; var raycaster = new Raycaster(); raycaster.setFromCamera(nmouse, camera); var ray = raycaster.ray; var selectedPointcloud = null; var closestDistance = Infinity; var closestIntersection = null; var closestPoint = null; for (var pointcloud of pointclouds) { var point = pointcloud.pick(viewer, camera, ray, pickParams); if (!point) { continue; } var distance = camera.position.distanceTo(point.position); if (distance < closestDistance) { closestDistance = distance; selectedPointcloud = pointcloud; closestIntersection = point.position; closestPoint = point; } } if (selectedPointcloud) { return { location: closestIntersection, distance: closestDistance, pointcloud: selectedPointcloud, point: closestPoint }; } else { return null; } } static pixelsArrayToImage(pixels, width, height) { var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); pixels = new pixels.constructor(pixels); for (var i = 0; i < pixels.length; i++) { pixels[i * 4 + 3] = 255; } var imageData = context.createImageData(width, height); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); var img = new Image(); img.src = canvas.toDataURL(); // img.style.transform = "scaleY(-1)"; return img; } static pixelsArrayToDataUrl(pixels, width, height) { var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); pixels = new pixels.constructor(pixels); for (var i = 0; i < pixels.length; i++) { pixels[i * 4 + 3] = 255; } var imageData = context.createImageData(width, height); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); var dataURL = canvas.toDataURL(); return dataURL; } static pixelsArrayToCanvas(pixels, width, height) { var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); pixels = new pixels.constructor(pixels); //for (let i = 0; i < pixels.length; i++) { // pixels[i * 4 + 3] = 255; //} // flip vertically var bytesPerLine = width * 4; for (var i = 0; i < parseInt(height / 2); i++) { var j = height - i - 1; var lineI = pixels.slice(i * bytesPerLine, i * bytesPerLine + bytesPerLine); var lineJ = pixels.slice(j * bytesPerLine, j * bytesPerLine + bytesPerLine); pixels.set(lineJ, i * bytesPerLine); pixels.set(lineI, j * bytesPerLine); } var imageData = context.createImageData(width, height); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); return canvas; } static removeListeners(dispatcher, type) { if (dispatcher._listeners === undefined) { return; } if (dispatcher._listeners[type]) { delete dispatcher._listeners[type]; } } static mouseToRay(mouse, camera, width, height) { var normalizedMouse = { x: mouse.x / width * 2 - 1, y: -(mouse.y / height) * 2 + 1 }; var vector = new Vector3(normalizedMouse.x, normalizedMouse.y, 0.5); var origin = camera.position.clone(); vector.unproject(camera); var direction = new Vector3().subVectors(vector, origin).normalize(); var ray = new Ray(origin, direction); return ray; } static projectedRadius(radius, camera, distance, screenWidth, screenHeight) { if (camera instanceof OrthographicCamera) { return Utils.projectedRadiusOrtho(radius, camera.projectionMatrix, screenWidth, screenHeight); } else if (camera instanceof PerspectiveCamera) { return Utils.projectedRadiusPerspective(radius, camera.fov * Math.PI / 180, distance, screenHeight); } else { throw new Error("invalid parameters"); } } static projectedRadiusPerspective(radius, fov, distance, screenHeight) { var projFactor = 1 / Math.tan(fov / 2) / distance; projFactor = projFactor * screenHeight / 2; return radius * projFactor; } static projectedRadiusOrtho(radius, proj, screenWidth, screenHeight) { var p1 = new Vector4(0); var p2 = new Vector4(radius); p1.applyMatrix4(proj); p2.applyMatrix4(proj); p1 = new Vector3(p1.x, p1.y, p1.z); p2 = new Vector3(p2.x, p2.y, p2.z); p1.x = (p1.x + 1.0) * 0.5 * screenWidth; p1.y = (p1.y + 1.0) * 0.5 * screenHeight; p2.x = (p2.x + 1.0) * 0.5 * screenWidth; p2.y = (p2.y + 1.0) * 0.5 * screenHeight; return p1.distanceTo(p2); } static topView(camera, node) { camera.position.set(0, 1, 0); camera.rotation.set(-Math.PI / 2, 0, 0); camera.zoomTo(node, 1); } static frontView(camera, node) { camera.position.set(0, 0, 1); camera.rotation.set(0, 0, 0); camera.zoomTo(node, 1); } static leftView(camera, node) { camera.position.set(-1, 0, 0); camera.rotation.set(0, -Math.PI / 2, 0); camera.zoomTo(node, 1); } static rightView(camera, node) { camera.position.set(1, 0, 0); camera.rotation.set(0, Math.PI / 2, 0); camera.zoomTo(node, 1); } static findClosestGpsTime(target, viewer) { var start = performance.now(); var nodes = []; for (var pc of viewer.scene.pointclouds) { nodes.push(pc.root); for (var child of pc.root.children) { if (child) { nodes.push(child); } } } var closestNode = null; var closestIndex = Infinity; var closestDistance = Infinity; var closestValue = 0; for (var node of nodes) { var isOkay = node.geometryNode != null && node.geometryNode.geometry != null && node.sceneNode != null; if (!isOkay) { continue; } var _geometry = node.geometryNode.geometry; var gpsTime = _geometry.attributes["gps-time"]; var range = gpsTime.potree.range; for (var i = 0; i < gpsTime.array.length; i++) { var value = gpsTime.array[i]; value = value * (range[1] - range[0]) + range[0]; var distance = Math.abs(target - value); if (distance < closestDistance) { closestIndex = i; closestDistance = distance; closestValue = value; closestNode = node; //console.log("found a closer one: " + value); } } } var geometry = closestNode.geometryNode.geometry; var position = new Vector3(geometry.attributes.position.array[3 * closestIndex + 0], geometry.attributes.position.array[3 * closestIndex + 1], geometry.attributes.position.array[3 * closestIndex + 2]); position.applyMatrix4(closestNode.sceneNode.matrixWorld); var end = performance.now(); var duration = end - start; console.log("duration: ".concat(duration.toFixed(3), "ms")); return { node: closestNode, index: closestIndex, position: position }; } /** * * 0: no intersection * 1: intersection * 2: fully inside */ static frustumSphereIntersection(frustum, sphere) { var planes = frustum.planes; var center = sphere.center; var negRadius = -sphere.radius; var minDistance = Number.MAX_VALUE; for (var i = 0; i < 6; i++) { var distance = planes[i].distanceToPoint(center); if (distance < negRadius) { return 0; } minDistance = Math.min(minDistance, distance); } return minDistance >= sphere.radius ? 2 : 1; } // code taken from three.js // ImageUtils - generateDataTexture() static generateDataTexture(width, height, color) { var size = width * height; var data = new Uint8Array(4 * width * height); var r = Math.floor(color.r * 255); var g = Math.floor(color.g * 255); var b = Math.floor(color.b * 255); for (var i = 0; i < size; i++) { data[i * 3] = r; data[i * 3 + 1] = g; data[i * 3 + 2] = b; } var texture = new DataTexture(data, width, height, RGBAFormat); texture.needsUpdate = true; texture.magFilter = NearestFilter; return texture; } // from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript static getParameterByName(name) { name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'); var results = regex.exec(document.location.search); return results === null ? null : decodeURIComponent(results[1].replace(/\+/g, ' ')); } static setParameter(name, value) { // value = encodeURIComponent(value); name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]'); var regex = new RegExp('([\\?&])(' + name + '=([^&#]*))'); var results = regex.exec(document.location.search); var url = window.location.href; if (results === null) { if (window.location.search.length === 0) { url = url + '?'; } else { url = url + '&'; } url = url + name + '=' + value; } else { var newValue = name + '=' + value; url = url.replace(results[2], newValue); } window.history.replaceState({}, '', url); } static createChildAABB(aabb, index) { var min = aabb.min.clone(); var max = aabb.max.clone(); var size = new Vector3().subVectors(max, min); if ((index & 0b0001) > 0) { min.z += size.z / 2; } else { max.z -= size.z / 2; } if ((index & 0b0010) > 0) { min.y += size.y / 2; } else { max.y -= size.y / 2; } if ((index & 0b0100) > 0) { min.x += size.x / 2; } else { max.x -= size.x / 2; } return new Box3(min, max); } // see https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript static clipboardCopy(text) { var textArea = document.createElement("textarea"); textArea.style.position = 'fixed'; textArea.style.top = 0; textArea.style.left = 0; textArea.style.width = '2em'; textArea.style.height = '2em'; textArea.style.padding = 0; textArea.style.border = 'none'; textArea.style.outline = 'none'; textArea.style.boxShadow = 'none'; textArea.style.background = 'transparent'; textArea.value = text; document.body.appendChild(textArea); textArea.select(); try { var success = document.execCommand('copy'); if (success) { console.log("copied text to clipboard"); } else { console.log("copy to clipboard failed"); } } catch (err) { console.log("error while trying to copy to clipboard"); } document.body.removeChild(textArea); } static getMeasurementIcon(measurement) { if (measurement instanceof Measure) { if (measurement.showDistances && !measurement.showArea && !measurement.showAngles) { return "".concat(Potree.resourcePath, "/icons/distance.svg"); } else if (measurement.showDistances && measurement.showArea && !measurement.showAngles) { return "".concat(Potree.resourcePath, "/icons/area.svg"); } else if (measurement.maxMarkers === 1) { return "".concat(Potree.resourcePath, "/icons/point.svg"); } else if (!measurement.showDistances && !measurement.showArea && measurement.showAngles) { return "".concat(Potree.resourcePath, "/icons/angle.png"); } else if (measurement.showHeight) { return "".concat(Potree.resourcePath, "/icons/height.svg"); } else { return "".concat(Potree.resourcePath, "/icons/distance.svg"); } } else if (measurement instanceof Profile) { return "".concat(Potree.resourcePath, "/icons/profile.svg"); } else if (measurement instanceof Volume) { return "".concat(Potree.resourcePath, "/icons/volume.svg"); } else if (measurement instanceof PolygonClipVolume) { return "".concat(Potree.resourcePath, "/icons/clip-polygon.svg"); } } static lineToLineIntersection(P0, P1, P2, P3) { var P = [P0, P1, P2, P3]; var d = (m, n, o, p) => { var result = (P[m].x - P[n].x) * (P[o].x - P[p].x) + (P[m].y - P[n].y) * (P[o].y - P[p].y) + (P[m].z - P[n].z) * (P[o].z - P[p].z); return result; }; var mua = (d(0, 2, 3, 2) * d(3, 2, 1, 0) - d(0, 2, 1, 0) * d(3, 2, 3, 2) /**-----------------------------------------------------------------**/) / (d(1, 0, 1, 0) * d(3, 2, 3, 2) - d(3, 2, 1, 0) * d(3, 2, 1, 0)); var mub = (d(0, 2, 3, 2) + mua * d(3, 2, 1, 0) /**--------------------------------------**/) / d(3, 2, 3, 2); var P01 = P1.clone().sub(P0); var P23 = P3.clone().sub(P2); var Pa = P0.clone().add(P01.multiplyScalar(mua)); var Pb = P2.clone().add(P23.multiplyScalar(mub)); var center = Pa.clone().add(Pb).multiplyScalar(0.5); return center; } static computeCircleCenter(A, B, C) { var AB = B.clone().sub(A); var AC = C.clone().sub(A); var N = AC.clone().cross(AB).normalize(); var ab_dir = AB.clone().cross(N).normalize(); var ac_dir = AC.clone().cross(N).normalize(); var ab_origin = A.clone().add(B).multiplyScalar(0.5); var ac_origin = A.clone().add(C).multiplyScalar(0.5); var P0 = ab_origin; var P1 = ab_origin.clone().add(ab_dir); var P2 = ac_origin; var P3 = ac_origin.clone().add(ac_dir); var center = Utils.lineToLineIntersection(P0, P1, P2, P3); return center; // Potree.Utils.debugLine(viewer.scene.scene, P0, P1, 0x00ff00); // Potree.Utils.debugLine(viewer.scene.scene, P2, P3, 0x0000ff); // Potree.Utils.debugSphere(viewer.scene.scene, center, 0.03, 0xff00ff); // const radius = center.distanceTo(A); // Potree.Utils.debugCircle(viewer.scene.scene, center, radius, new THREE.Vector3(0, 0, 1), 0xff00ff); } static getNorthVec(p1, distance, projection) { if (projection) { // if there is a projection, transform coordinates to WGS84 // and compute angle to north there proj4.defs("pointcloud", projection); var transform = proj4("pointcloud", "WGS84"); var llP1 = transform.forward(p1.toArray()); var llP2 = transform.forward([p1.x, p1.y + distance]); var polarRadius = Math.sqrt((llP2[0] - llP1[0]) ** 2 + (llP2[1] - llP1[1]) ** 2); llP2 = [llP1[0], llP1[1] + polarRadius]; var northVec = transform.inverse(llP2); return new Vector3(...northVec, p1.z).sub(p1); } else { // if there is no projection, assume [0, 1, 0] as north direction var vec = new Vector3(0, 1, 0).multiplyScalar(distance); return vec; } } static computeAzimuth(p1, p2, projection) { var azimuth = 0; if (projection) { // if there is a projection, transform coordinates to WGS84 // and compute angle to north there var transform; if (projection.includes('EPSG')) { transform = proj4(projection, "WGS84"); } else { proj4.defs("pointcloud", projection); transform = proj4("pointcloud", "WGS84"); } var llP1 = transform.forward(p1.toArray()); var llP2 = transform.forward(p2.toArray()); var dir = [llP2[0] - llP1[0], llP2[1] - llP1[1]]; azimuth = Math.atan2(dir[1], dir[0]) - Math.PI / 2; } else { // if there is no projection, assume [0, 1, 0] as north direction var _dir = [p2.x - p1.x, p2.y - p1.y]; azimuth = Math.atan2(_dir[1], _dir[0]) - Math.PI / 2; } // make clockwise azimuth = -azimuth; return azimuth; } static async loadScript(url) { return new Promise(resolve => { var element = document.getElementById(url); if (element) { resolve(); } else { var script = document.createElement("script"); script.id = url; script.onload = () => { resolve(); }; script.src = url; document.body.appendChild(script); } }); } static createSvgGradient(scheme) { // this is what we are creating: // // // // // // ... // // // // // // var gradientId = "".concat(Math.random(), "_").concat(Date.now()); var svgn = "http://www.w3.org/2000/svg"; var svg = document.createElementNS(svgn, "svg"); svg.setAttributeNS(null, "width", "2em"); svg.setAttributeNS(null, "height", "3em"); { // var defs = document.createElementNS(svgn, "defs"); var linearGradient = document.createElementNS(svgn, "linearGradient"); linearGradient.setAttributeNS(null, "id", gradientId); linearGradient.setAttributeNS(null, "gradientTransform", "rotate(90)"); for (var i = scheme.length - 1; i >= 0; i--) { var stopVal = scheme[i]; var percent = parseInt(100 - stopVal[0] * 100); var [r, g, b] = stopVal[1].toArray().map(v => parseInt(v * 255)); var stop = document.createElementNS(svgn, "stop"); stop.setAttributeNS(null, "offset", "".concat(percent, "%")); stop.setAttributeNS(null, "stop-color", "rgb(".concat(r, ", ").concat(g, ", ").concat(b, ")")); linearGradient.appendChild(stop); } defs.appendChild(linearGradient); svg.appendChild(defs); } var rect = document.createElementNS(svgn, "rect"); rect.setAttributeNS(null, "width", "100%"); rect.setAttributeNS(null, "height", "100%"); rect.setAttributeNS(null, "fill", "url(\"#".concat(gradientId, "\")")); rect.setAttributeNS(null, "stroke", "black"); rect.setAttributeNS(null, "stroke-width", "0.1em"); svg.appendChild(rect); return svg; } static async waitAny(promises) { return new Promise(resolve => { promises.map(promise => { promise.then(() => { resolve(); }); }); }); } } Utils.screenPass = new function () { this.screenScene = new Scene(); this.screenQuad = new Mesh(new PlaneBufferGeometry(2, 2, 1)); this.screenQuad.material.depthTest = true; this.screenQuad.material.depthWrite = true; this.screenQuad.material.transparent = true; this.screenScene.add(this.screenQuad); this.camera = new Camera(); this.render = function (renderer, material, target) { this.screenQuad.material = material; if (typeof target === 'undefined') { renderer.render(this.screenScene, this.camera); } else { renderer.render(this.screenScene, this.camera, target); } }; }(); class Version { constructor(version) { this.version = version; var vmLength = version.indexOf('.') === -1 ? version.length : version.indexOf('.'); this.versionMajor = parseInt(version.substr(0, vmLength)); this.versionMinor = parseInt(version.substr(vmLength + 1)); if (this.versionMinor.length === 0) { this.versionMinor = 0; } } newerThan(version) { var v = new Version(version); if (this.versionMajor > v.versionMajor) { return true; } else if (this.versionMajor === v.versionMajor && this.versionMinor > v.versionMinor) { return true; } else { return false; } } equalOrHigher(version) { var v = new Version(version); if (this.versionMajor > v.versionMajor) { return true; } else if (this.versionMajor === v.versionMajor && this.versionMinor >= v.versionMinor) { return true; } else { return false; } } upTo(version) { return !this.newerThan(version); } } //加载 解析点云 class BinaryLoader { constructor(version, boundingBox, scale) { if (typeof version === 'string') { this.version = new Version(version); } else { this.version = version; } this.boundingBox = boundingBox; this.scale = scale; } load(node) { if (node.loaded) { return; } var url = node.getURL(); if (this.version.equalOrHigher('1.4')) { url += '.bin'; } var xhr = XHRFactory.createXMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.overrideMimeType('text/plain; charset=x-user-defined'); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if ((xhr.status === 200 || xhr.status === 0) && xhr.response !== null) { var buffer = xhr.response; this.parse(node, buffer); } else { //console.error(`Failed to load file! HTTP status: ${xhr.status}, file: ${url}`); throw new Error("Failed to load file! HTTP status: ".concat(xhr.status, ", file: ").concat(url)); } } }; try { xhr.send(null); } catch (e) { console.log('fehler beim laden der punktwolke: ' + e); } } parse(node, buffer, callback) { var pointAttributes = node.pcoGeometry.pointAttributes; //定义自cloud.js var numPoints = buffer.byteLength / node.pcoGeometry.pointAttributes.byteSize; if (this.version.upTo('1.5')) { node.numPoints = numPoints; } var workerPath = Potree.scriptPath + '/workers/BinaryDecoderWorker.js'; var worker = Potree.workerPool.getWorker(workerPath); var maxRest = 3 * ((Potree.settings.splatSH + 1) * (Potree.settings.splatSH + 1) - 1); worker.onmessage = function (e) { var data = e.data; var buffers = data.attributeBuffers; var tightBoundingBox = new Box3(new Vector3().fromArray(data.tightBoundingBox.min), new Vector3().fromArray(data.tightBoundingBox.max)); Potree.workerPool.returnWorker(workerPath, worker); var geometry = new BufferGeometry(); for (var property in buffers) { var _buffer = buffers[property].buffer; var batchAttribute = buffers[property].attribute; if (property === "POSITION_CARTESIAN") { geometry.setAttribute('position', new BufferAttribute(new Float32Array(_buffer), 3)); } else if (property === "centersFloat") { //add geometry.setAttribute("centersFloat", new BufferAttribute(new Float32Array(_buffer), 3)); } else if (property === "centersInt") { //add geometry.setAttribute("centersInt", new BufferAttribute(new Int32Array(_buffer), 4)); } else if (property === "rgba") { geometry.setAttribute("rgba", new BufferAttribute(new Uint8Array(_buffer), 4, true)); } else if (property === "NORMAL_SPHEREMAPPED") { geometry.setAttribute('normal', new BufferAttribute(new Float32Array(_buffer), 3)); } else if (property === "NORMAL_OCT16") { geometry.setAttribute('normal', new BufferAttribute(new Float32Array(_buffer), 3)); } else if (property === "NORMAL") { geometry.setAttribute('normal', new BufferAttribute(new Float32Array(_buffer), 3)); } else if (property === "INDICES") { var bufferAttribute = new BufferAttribute(new Uint8Array(_buffer), 4); bufferAttribute.normalized = true; geometry.setAttribute('indices', bufferAttribute); } else if (property === "SPACING") { var _bufferAttribute = new BufferAttribute(new Float32Array(_buffer), 1); geometry.setAttribute('spacing', _bufferAttribute); } else if (property === "IR" || property === "TEMP") { //温度 var _bufferAttribute2 = new BufferAttribute(new Float32Array(_buffer), 1); _bufferAttribute2.tempRange = batchAttribute.range; geometry.setAttribute(property.toLowerCase(), _bufferAttribute2); } else if (property === "SEG") { //分类 var _bufferAttribute3 = new BufferAttribute(new Float32Array(_buffer), 1); geometry.setAttribute('classification', _bufferAttribute3); } else if (property === "covs") { //add var _bufferAttribute4 = new BufferAttribute(new Float32Array(_buffer), 6); geometry.setAttribute('covs', _bufferAttribute4); } else if (property === "sh") { //add var _bufferAttribute5 = new BufferAttribute(new Float32Array(_buffer), maxRest); geometry.setAttribute('sh', _bufferAttribute5); } else if (property === "scales") { //add test var _bufferAttribute6 = new BufferAttribute(new Float32Array(_buffer), 3); geometry.setAttribute('scales', _bufferAttribute6); } /* else if(property != 'GS3D'){//改 //geometry.setAttribute("rgba", new THREE.BufferAttribute(new Uint8Array(buffer), 4, true)); const bufferAttribute = new THREE.BufferAttribute(new Float32Array(buffer), 1); bufferAttribute.potree = { offset: buffers[property].offset, scale: buffers[property].scale, preciseBuffer: buffers[property].preciseBuffer, range: batchAttribute.range, }; geometry.setAttribute(property, bufferAttribute); const attribute = pointAttributes.attributes.find(a => a.name === batchAttribute.name); attribute.range[0] = Math.min(attribute.range[0], batchAttribute.range[0]); attribute.range[1] = Math.max(attribute.range[1], batchAttribute.range[1]); if(node.getLevel() === 0){ attribute.initialRange = batchAttribute.range; } } */ } tightBoundingBox.max.sub(tightBoundingBox.min); tightBoundingBox.min.set(0, 0, 0); var numPoints = e.data.buffer.byteLength / pointAttributes.byteSize; node.numPoints = numPoints; node.geometry = geometry; node.mean = new Vector3(...data.mean); node.tightBoundingBox = tightBoundingBox; node.loaded = true; node.loading = false; node.estimatedSpacing = data.estimatedSpacing; Potree.numNodesLoading--; callback(); //add }; var message = { buffer: buffer, pointAttributes: pointAttributes, version: this.version.version, min: [node.boundingBox.min.x, node.boundingBox.min.y, node.boundingBox.min.z], offset: [node.pcoGeometry.offset.x, node.pcoGeometry.offset.y, node.pcoGeometry.offset.z], scale: this.scale, spacing: node.spacing, hasChildren: node.hasChildren, name: node.name }; worker.postMessage(message, [message.buffer]); } } var ftCanvas = document.createElement('canvas'); var Features = function () { var gl = ftCanvas.getContext('webgl') || ftCanvas.getContext('experimental-webgl'); if (gl === null) { return {}; } // -- code taken from THREE.WebGLRenderer -- var _vertexShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.HIGH_FLOAT); var _vertexShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.MEDIUM_FLOAT); // Unused: let _vertexShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat(gl.VERTEX_SHADER, gl.LOW_FLOAT); var _fragmentShaderPrecisionHighpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.HIGH_FLOAT); var _fragmentShaderPrecisionMediumpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT); // Unused: let _fragmentShaderPrecisionLowpFloat = gl.getShaderPrecisionFormat(gl.FRAGMENT_SHADER, gl.LOW_FLOAT); var highpAvailable = _vertexShaderPrecisionHighpFloat.precision > 0 && _fragmentShaderPrecisionHighpFloat.precision > 0; var mediumpAvailable = _vertexShaderPrecisionMediumpFloat.precision > 0 && _fragmentShaderPrecisionMediumpFloat.precision > 0; // ----------------------------------------- var precision; if (highpAvailable) { precision = 'highp'; } else if (mediumpAvailable) { precision = 'mediump'; } else { precision = 'lowp'; } return { SHADER_INTERPOLATION: { isSupported: function isSupported() { var supported = true; supported = supported && gl.getExtension('EXT_frag_depth'); supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; return supported; } }, SHADER_SPLATS: { isSupported: function isSupported() { var supported = true; supported = supported && gl.getExtension('EXT_frag_depth'); supported = supported && gl.getExtension('OES_texture_float'); supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; return supported; } }, SHADER_EDL: { isSupported: function isSupported() { var supported = true; supported = supported && (gl.getExtension('EXT_frag_depth') || Potree.settings.isWebgl2); //麒麟系统firefox支持webgl2但这里用webgl1的content得不到EXT_frag_depth supported = supported && gl.getExtension('OES_texture_float'); supported = supported && gl.getParameter(gl.MAX_VARYING_VECTORS) >= 8; //supported = supported || (gl instanceof WebGL2RenderingContext); return supported; } }, //WEBGL2: { // isSupported: function(){ // return typeof WebGL2RenderingContext != 'undefined' && gl instanceof WebGL2RenderingContext; // } //}, precision: precision }; }(); /** * Some types of possible point attribute data formats * * @class */ var PointAttributeTypes = { DATA_TYPE_DOUBLE: { ordinal: 0, name: "double", size: 8 }, DATA_TYPE_FLOAT: { ordinal: 1, name: "float", size: 4 }, DATA_TYPE_INT8: { ordinal: 2, name: "int8", size: 1 }, DATA_TYPE_UINT8: { ordinal: 3, name: "uint8", size: 1 }, DATA_TYPE_INT16: { ordinal: 4, name: "int16", size: 2 }, DATA_TYPE_UINT16: { ordinal: 5, name: "uint16", size: 2 }, DATA_TYPE_INT32: { ordinal: 6, name: "int32", size: 4 }, DATA_TYPE_UINT32: { ordinal: 7, name: "uint32", size: 4 }, DATA_TYPE_INT64: { ordinal: 8, name: "int64", size: 8 }, DATA_TYPE_UINT64: { ordinal: 9, name: "uint64", size: 8 } }; var i$1 = 0; for (var obj in PointAttributeTypes) { PointAttributeTypes[i$1] = PointAttributeTypes[obj]; i$1++; } class PointAttribute { constructor(name, type, numElements) { this.name = name; this.type = type; this.numElements = numElements; this.byteSize = this.numElements * this.type.size; this.description = ""; this.range = [Infinity, -Infinity]; } } ; PointAttribute.POSITION_CARTESIAN = new PointAttribute("POSITION_CARTESIAN", PointAttributeTypes.DATA_TYPE_FLOAT, 3); PointAttribute.RGBA_PACKED = new PointAttribute("COLOR_PACKED", PointAttributeTypes.DATA_TYPE_INT8, 4); PointAttribute.COLOR_PACKED = PointAttribute.RGBA_PACKED; PointAttribute.RGB_PACKED = new PointAttribute("COLOR_PACKED", PointAttributeTypes.DATA_TYPE_INT8, 3); PointAttribute.NORMAL_FLOATS = new PointAttribute("NORMAL_FLOATS", PointAttributeTypes.DATA_TYPE_FLOAT, 3); PointAttribute.INTENSITY = new PointAttribute("INTENSITY", PointAttributeTypes.DATA_TYPE_UINT16, 1); PointAttribute.CLASSIFICATION = new PointAttribute("CLASSIFICATION", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.NORMAL_SPHEREMAPPED = new PointAttribute("NORMAL_SPHEREMAPPED", PointAttributeTypes.DATA_TYPE_UINT8, 2); PointAttribute.NORMAL_OCT16 = new PointAttribute("NORMAL_OCT16", PointAttributeTypes.DATA_TYPE_UINT8, 2); PointAttribute.NORMAL = new PointAttribute("NORMAL", PointAttributeTypes.DATA_TYPE_FLOAT, 3); PointAttribute.RETURN_NUMBER = new PointAttribute("RETURN_NUMBER", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.NUMBER_OF_RETURNS = new PointAttribute("NUMBER_OF_RETURNS", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.SOURCE_ID = new PointAttribute("SOURCE_ID", PointAttributeTypes.DATA_TYPE_UINT16, 1); PointAttribute.INDICES = new PointAttribute("INDICES", PointAttributeTypes.DATA_TYPE_UINT32, 1); PointAttribute.SPACING = new PointAttribute("SPACING", PointAttributeTypes.DATA_TYPE_FLOAT, 1); PointAttribute.GPS_TIME = new PointAttribute("GPS_TIME", PointAttributeTypes.DATA_TYPE_DOUBLE, 1); class PointAttributes { constructor(pointAttributes) { this.attributes = []; this.byteSize = 0; this.size = 0; this.vectors = []; if (pointAttributes != null) { for (var _i = 0; _i < pointAttributes.length; _i++) { var pointAttributeName = pointAttributes[_i]; var pointAttribute = PointAttribute[pointAttributeName]; this.attributes.push(pointAttribute); this.byteSize += pointAttribute.byteSize; this.size++; } } } add(pointAttribute) { this.attributes.push(pointAttribute); this.byteSize += pointAttribute.byteSize; this.size++; } addVector(vector) { this.vectors.push(vector); } hasNormals() { for (var name in this.attributes) { var pointAttribute = this.attributes[name]; if (pointAttribute === PointAttribute.NORMAL_SPHEREMAPPED || pointAttribute === PointAttribute.NORMAL_FLOATS || pointAttribute === PointAttribute.NORMAL || pointAttribute === PointAttribute.NORMAL_OCT16) { return true; } } return false; } } class Points$1 { constructor() { this.boundingBox = new Box3(); this.numPoints = 0; this.data = {}; } add(points) { var currentSize = this.numPoints; var additionalSize = points.numPoints; var newSize = currentSize + additionalSize; var thisAttributes = Object.keys(this.data); var otherAttributes = Object.keys(points.data); var attributes = new Set([...thisAttributes, ...otherAttributes]); for (var attribute of attributes) { if (thisAttributes.includes(attribute) && otherAttributes.includes(attribute)) { // attribute in both, merge var Type = this.data[attribute].constructor; var merged = new Type(this.data[attribute].length + points.data[attribute].length); merged.set(this.data[attribute], 0); merged.set(points.data[attribute], this.data[attribute].length); this.data[attribute] = merged; } else if (thisAttributes.includes(attribute) && !otherAttributes.includes(attribute)) { // attribute only in this; take over this and expand to new size var elementsPerPoint = this.data[attribute].length / this.numPoints; var _Type = this.data[attribute].constructor; var expanded = new _Type(elementsPerPoint * newSize); expanded.set(this.data[attribute], 0); this.data[attribute] = expanded; } else if (!thisAttributes.includes(attribute) && otherAttributes.includes(attribute)) { // attribute only in points to be added; take over new points and expand to new size var _elementsPerPoint = points.data[attribute].length / points.numPoints; var _Type2 = points.data[attribute].constructor; var _expanded = new _Type2(_elementsPerPoint * newSize); _expanded.set(points.data[attribute], _elementsPerPoint * currentSize); this.data[attribute] = _expanded; } } this.numPoints = newSize; this.boundingBox.union(points.boundingBox); } } class CSVExporter { static toString(points) { var string = ''; var attributes = Object.keys(points.data).filter(a => a !== 'normal').sort((a, b) => { if (a === 'position') return -1; if (b === 'position') return 1; if (a === 'rgba') return -1; if (b === 'rgba') return 1; }); var headerValues = []; for (var attribute of attributes) { var itemSize = points.data[attribute].length / points.numPoints; if (attribute === 'position') { headerValues = headerValues.concat(['x', 'y', 'z']); } else if (attribute === 'rgba') { headerValues = headerValues.concat(['r', 'g', 'b', 'a']); } else if (itemSize > 1) { for (var i = 0; i < itemSize; i++) { headerValues.push("".concat(attribute, "_").concat(i)); } } else { headerValues.push(attribute); } } string = headerValues.join(', ') + '\n'; for (var _i = 0; _i < points.numPoints; _i++) { var values = []; for (var _attribute of attributes) { var _itemSize = points.data[_attribute].length / points.numPoints; var value = points.data[_attribute].subarray(_itemSize * _i, _itemSize * _i + _itemSize).join(', '); values.push(value); } string += values.join(', ') + '\n'; } return string; } } ; class LASExporter { static toLAS(points) { // TODO Unused: let string = ''; var boundingBox = points.boundingBox; var offset = boundingBox.min.clone(); var diagonal = boundingBox.min.distanceTo(boundingBox.max); var scale = new Vector3(0.001, 0.001, 0.001); if (diagonal > 1000 * 1000) { scale = new Vector3(0.01, 0.01, 0.01); } else { scale = new Vector3(0.001, 0.001, 0.001); } var setString = function setString(string, offset, buffer) { var view = new Uint8Array(buffer); for (var i = 0; i < string.length; i++) { var charCode = string.charCodeAt(i); view[offset + i] = charCode; } }; var buffer = new ArrayBuffer(227 + 28 * points.numPoints); var view = new DataView(buffer); var u8View = new Uint8Array(buffer); // let u16View = new Uint16Array(buffer); setString('LASF', 0, buffer); u8View[24] = 1; u8View[25] = 2; // system identifier o:26 l:32 // generating software o:58 l:32 setString('Potree 1.7', 58, buffer); // file creation day of year o:90 l:2 // file creation year o:92 l:2 // header size o:94 l:2 view.setUint16(94, 227, true); // offset to point data o:96 l:4 view.setUint32(96, 227, true); // number of letiable length records o:100 l:4 // point data record format 104 1 u8View[104] = 2; // point data record length 105 2 view.setUint16(105, 28, true); // number of point records 107 4 view.setUint32(107, points.numPoints, true); // number of points by return 111 20 // x scale factor 131 8 view.setFloat64(131, scale.x, true); // y scale factor 139 8 view.setFloat64(139, scale.y, true); // z scale factor 147 8 view.setFloat64(147, scale.z, true); // x offset 155 8 view.setFloat64(155, offset.x, true); // y offset 163 8 view.setFloat64(163, offset.y, true); // z offset 171 8 view.setFloat64(171, offset.z, true); // max x 179 8 view.setFloat64(179, boundingBox.max.x, true); // min x 187 8 view.setFloat64(187, boundingBox.min.x, true); // max y 195 8 view.setFloat64(195, boundingBox.max.y, true); // min y 203 8 view.setFloat64(203, boundingBox.min.y, true); // max z 211 8 view.setFloat64(211, boundingBox.max.z, true); // min z 219 8 view.setFloat64(219, boundingBox.min.z, true); var boffset = 227; for (var i = 0; i < points.numPoints; i++) { var px = points.data.position[3 * i + 0]; var py = points.data.position[3 * i + 1]; var pz = points.data.position[3 * i + 2]; var ux = parseInt((px - offset.x) / scale.x); var uy = parseInt((py - offset.y) / scale.y); var uz = parseInt((pz - offset.z) / scale.z); view.setUint32(boffset + 0, ux, true); view.setUint32(boffset + 4, uy, true); view.setUint32(boffset + 8, uz, true); if (points.data.intensity) { view.setUint16(boffset + 12, points.data.intensity[i], true); } var rt = 0; if (points.data.returnNumber) { rt += points.data.returnNumber[i]; } if (points.data.numberOfReturns) { rt += points.data.numberOfReturns[i] << 3; } view.setUint8(boffset + 14, rt); if (points.data.classification) { view.setUint8(boffset + 15, points.data.classification[i]); } // scan angle rank // user data // point source id if (points.data.pointSourceID) { view.setUint16(boffset + 18, points.data.pointSourceID[i]); } if (points.data.rgba) { var rgba = points.data.rgba; view.setUint16(boffset + 20, rgba[4 * i + 0] * 255, true); view.setUint16(boffset + 22, rgba[4 * i + 1] * 255, true); view.setUint16(boffset + 24, rgba[4 * i + 2] * 255, true); } boffset += 28; } return buffer; } } /** * @author mrdoob / http://mrdoob.com/ https://github.com/mrdoob/eventdispatcher.js * * with slight modifications by mschuetz, http://potree.org * */ // The MIT License // // Copyright (c) 2011 Mr.doob // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. class EventDispatcher$1 { constructor() { this._listeners = {}; } addEventListener(type, listener) { var listeners = this._listeners; if (listeners[type] === undefined) { listeners[type] = []; } if (listeners[type].indexOf(listener) === -1) { listeners[type].push(listener); } } hasEventListener(type, listener) { var listeners = this._listeners; return listeners[type] !== undefined && listeners[type].indexOf(listener) !== -1; } removeEventListener(type, listener) { var listeners = this._listeners; var listenerArray = listeners[type]; if (listenerArray !== undefined) { var index = listenerArray.indexOf(listener); if (index !== -1) { listenerArray.splice(index, 1); } } } removeEventListeners(type) { if (this._listeners[type] !== undefined) { delete this._listeners[type]; } } dispatchEvent(event) { var listeners = this._listeners; var listenerArray = listeners[event.type]; if (listenerArray !== undefined) { event.target = this; for (var listener of listenerArray.slice(0)) { listener.call(this, event); } } } } class PointCloudTreeNode extends EventDispatcher$1 { constructor() { super(); this.needsTransformUpdate = true; } getChildren() { throw new Error('override function'); } getBoundingBox() { throw new Error('override function'); } isLoaded() { throw new Error('override function'); } isGeometryNode() { throw new Error('override function'); } isTreeNode() { throw new Error('override function'); } getLevel() { throw new Error('override function'); } getBoundingSphere() { throw new Error('override function'); } } ; class PointCloudTree extends Object3D { constructor() { super(); //this.spriteGroup = new THREE.Object3D //add } initialized() { return this.root !== null; } } ; class PointCloudOctreeGeometry { constructor() { this.url = null; this.octreeDir = null; this.spacing = 0; this.boundingBox = null; this.root = null; this.nodes = null; this.pointAttributes = null; this.hierarchyStepSize = -1; this.loader = null; } } class PointCloudOctreeGeometryNode extends PointCloudTreeNode { constructor(name, pcoGeometry, boundingBox) { super(); this.id = PointCloudOctreeGeometryNode.IDCount++; this.name = name; this.index = parseInt(name.charAt(name.length - 1)); this.pcoGeometry = pcoGeometry; this.geometry = null; this.boundingBox = boundingBox; this.boundingSphere = boundingBox.getBoundingSphere(new Sphere()); this.children = {}; this.numPoints = 0; this.level = null; this.loaded = false; this.oneTimeDisposeHandlers = []; } isGeometryNode() { return true; } getLevel() { return this.level; } isTreeNode() { return false; } isLoaded() { return this.loaded; } getBoundingSphere() { return this.boundingSphere; } getBoundingBox() { return this.boundingBox; } getChildren() { var children = []; for (var i = 0; i < 8; i++) { if (this.children[i]) { children.push(this.children[i]); } } return children; } getBoundingBox() { return this.boundingBox; } getURL() { var url = ''; var version = this.pcoGeometry.loader.version; if (version.equalOrHigher('1.5')) { url = this.pcoGeometry.octreeDir + '/' + this.getHierarchyPath() + '/' + this.name; } else if (version.equalOrHigher('1.4')) { url = this.pcoGeometry.octreeDir + '/' + this.name; } else if (version.upTo('1.3')) { url = this.pcoGeometry.octreeDir + '/' + this.name; } return url; } getHierarchyPath() { var path = 'r/'; var hierarchyStepSize = this.pcoGeometry.hierarchyStepSize; var indices = this.name.substr(1); var numParts = Math.floor(indices.length / hierarchyStepSize); for (var i = 0; i < numParts; i++) { path += indices.substr(i * hierarchyStepSize, hierarchyStepSize) + '/'; } path = path.slice(0, -1); return path; } addChild(child) { this.children[child.index] = child; child.parent = this; } load() { if (this.loading === true || this.loaded === true || Potree.numNodesLoading >= Potree.maxNodesLoading) { return; } this.loading = true; Potree.numNodesLoading++; if (this.pcoGeometry.loader.version.equalOrHigher('1.5')) { if (this.level % this.pcoGeometry.hierarchyStepSize === 0 && this.hasChildren) { this.loadHierachyThenPoints(); } else { this.loadPoints(); } } else { this.pcoGeometry.dispatchEvent({ type: 'updateNodeMaxLevel', level: this.level }); //add this.loadPoints(); } } loadPoints() { this.pcoGeometry.loader.load(this); } loadHierachyThenPoints() { var node = this; // load hierarchy var callback = function callback(node, hbuffer) { var tStart = performance.now(); var view = new DataView(hbuffer); var stack = []; var children = view.getUint8(0); var numPoints = view.getUint32(1, true); node.numPoints = numPoints; stack.push({ children: children, numPoints: numPoints, name: node.name }); var decoded = []; var offset = 5; while (stack.length > 0) { var snode = stack.shift(); var mask = 1; for (var i = 0; i < 8; i++) { if ((snode.children & mask) !== 0) { var childName = snode.name + i; var childChildren = view.getUint8(offset); var childNumPoints = view.getUint32(offset + 1, true); stack.push({ children: childChildren, numPoints: childNumPoints, name: childName }); decoded.push({ children: childChildren, numPoints: childNumPoints, name: childName }); offset += 5; } mask = mask * 2; } if (offset === hbuffer.byteLength) { break; } } // console.log(decoded); var nodes = {}; nodes[node.name] = node; var pco = node.pcoGeometry; for (var _i = 0; _i < decoded.length; _i++) { var name = decoded[_i].name; var decodedNumPoints = decoded[_i].numPoints; var index = parseInt(name.charAt(name.length - 1)); var parentName = name.substring(0, name.length - 1); var parentNode = nodes[parentName]; var level = name.length - 1; var boundingBox = Utils.createChildAABB(parentNode.boundingBox, index); var currentNode = new PointCloudOctreeGeometryNode(name, pco, boundingBox); currentNode.level = level; currentNode.numPoints = decodedNumPoints; currentNode.hasChildren = decoded[_i].children > 0; currentNode.spacing = pco.spacing / Math.pow(2, level); parentNode.addChild(currentNode); nodes[name] = currentNode; } var duration = performance.now() - tStart; if (duration > 5) { var msg = "duration: ".concat(duration, "ms, numNodes: ").concat(decoded.length); console.log(msg); } node.loadPoints(); }; if (node.level % node.pcoGeometry.hierarchyStepSize === 0) { // let hurl = node.pcoGeometry.octreeDir + "/../hierarchy/" + node.name + ".hrc"; var hurl = node.pcoGeometry.octreeDir + '/' + node.getHierarchyPath() + '/' + node.name + '.hrc'; var xhr = XHRFactory.createXMLHttpRequest(); xhr.open('GET', hurl, true); xhr.responseType = 'arraybuffer'; xhr.overrideMimeType('text/plain; charset=x-user-defined'); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200 || xhr.status === 0) { var hbuffer = xhr.response; callback(node, hbuffer); } else { console.log('Failed to load file! HTTP status: ' + xhr.status + ', file: ' + hurl); Potree.numNodesLoading--; } } }; try { xhr.send(null); } catch (e) { console.log('fehler beim laden der punktwolke: ' + e); } } } getNumPoints() { return this.numPoints; } dispose() { if (this.geometry && this.parent != null) { this.geometry.dispose(); this.geometry = null; this.loaded = false; this.dispatchEvent({ type: 'dispose' }); for (var i = 0; i < this.oneTimeDisposeHandlers.length; i++) { var handler = this.oneTimeDisposeHandlers[i]; handler(); } this.oneTimeDisposeHandlers = []; } } } PointCloudOctreeGeometryNode.IDCount = 0; // ------------------------------------------- // to get a ready to use gradient array from a chroma.js gradient: // http://gka.github.io/chroma.js/ // ------------------------------------------- // // let stops = []; // for(let i = 0; i <= 10; i++){ // let range = chroma.scale(['yellow', 'navy']).mode('lch').domain([10,0])(i)._rgb // .slice(0, 3) // .map(v => (v / 255).toFixed(4)) // .join(", "); // // let line = `[${i / 10}, new THREE.Color(${range})],`; // // stops.push(line); // } // stops.join("\n"); // // // // ------------------------------------------- // to get a ready to use gradient array from matplotlib: // ------------------------------------------- // import matplotlib.pyplot as plt // import matplotlib.colors as colors // // norm = colors.Normalize(vmin=0,vmax=1) // cmap = plt.cm.viridis // // for i in range(0,11): // u = i / 10 // rgb = cmap(norm(u))[0:3] // rgb = ["{0:.3f}".format(v) for v in rgb] // rgb = "[" + str(u) + ", new THREE.Color(" + ", ".join(rgb) + ")]," // print(rgb) var Gradients = { // From chroma spectral http://gka.github.io/chroma.js/ SPECTRAL: [[0, new Color(0.3686, 0.3098, 0.6353)], [0.1, new Color(0.1961, 0.5333, 0.7412)], [0.2, new Color(0.4000, 0.7608, 0.6471)], [0.3, new Color(0.6706, 0.8667, 0.6431)], [0.4, new Color(0.9020, 0.9608, 0.5961)], [0.5, new Color(1.0000, 1.0000, 0.7490)], [0.6, new Color(0.9961, 0.8784, 0.5451)], [0.7, new Color(0.9922, 0.6824, 0.3804)], [0.8, new Color(0.9569, 0.4275, 0.2627)], [0.9, new Color(0.8353, 0.2431, 0.3098)], [1, new Color(0.6196, 0.0039, 0.2588)]], PLASMA: [[0.0, new Color(0.241, 0.015, 0.610)], [0.1, new Color(0.387, 0.001, 0.654)], [0.2, new Color(0.524, 0.025, 0.653)], [0.3, new Color(0.651, 0.125, 0.596)], [0.4, new Color(0.752, 0.227, 0.513)], [0.5, new Color(0.837, 0.329, 0.431)], [0.6, new Color(0.907, 0.435, 0.353)], [0.7, new Color(0.963, 0.554, 0.272)], [0.8, new Color(0.992, 0.681, 0.195)], [0.9, new Color(0.987, 0.822, 0.144)], [1.0, new Color(0.940, 0.975, 0.131)]], YELLOW_GREEN: [[0, new Color(0.1647, 0.2824, 0.3451)], [0.1, new Color(0.1338, 0.3555, 0.4227)], [0.2, new Color(0.0610, 0.4319, 0.4864)], [0.3, new Color(0.0000, 0.5099, 0.5319)], [0.4, new Color(0.0000, 0.5881, 0.5569)], [0.5, new Color(0.1370, 0.6650, 0.5614)], [0.6, new Color(0.2906, 0.7395, 0.5477)], [0.7, new Color(0.4453, 0.8099, 0.5201)], [0.8, new Color(0.6102, 0.8748, 0.4850)], [0.9, new Color(0.7883, 0.9323, 0.4514)], [1, new Color(0.9804, 0.9804, 0.4314)]], VIRIDIS: [[0.0, new Color(0.267, 0.005, 0.329)], [0.1, new Color(0.283, 0.141, 0.458)], [0.2, new Color(0.254, 0.265, 0.530)], [0.3, new Color(0.207, 0.372, 0.553)], [0.4, new Color(0.164, 0.471, 0.558)], [0.5, new Color(0.128, 0.567, 0.551)], [0.6, new Color(0.135, 0.659, 0.518)], [0.7, new Color(0.267, 0.749, 0.441)], [0.8, new Color(0.478, 0.821, 0.318)], [0.9, new Color(0.741, 0.873, 0.150)], [1.0, new Color(0.993, 0.906, 0.144)]], INFERNO: [[0.0, new Color(0.077, 0.042, 0.206)], [0.1, new Color(0.225, 0.036, 0.388)], [0.2, new Color(0.373, 0.074, 0.432)], [0.3, new Color(0.522, 0.128, 0.420)], [0.4, new Color(0.665, 0.182, 0.370)], [0.5, new Color(0.797, 0.255, 0.287)], [0.6, new Color(0.902, 0.364, 0.184)], [0.7, new Color(0.969, 0.516, 0.063)], [0.8, new Color(0.988, 0.683, 0.072)], [0.9, new Color(0.961, 0.859, 0.298)], [1.0, new Color(0.988, 0.998, 0.645)]], GRAYSCALE: [[0, new Color(0, 0, 0)], [1, new Color(1, 1, 1)]], // 16 samples of the TURBU color scheme // values taken from: https://gist.github.com/mikhailov-work/ee72ba4191942acecc03fe6da94fc73f // original file licensed under Apache-2.0 TURBO: [[0.00, new Color(0.18995, 0.07176, 0.23217)], [0.07, new Color(0.25107, 0.25237, 0.63374)], [0.13, new Color(0.27628, 0.42118, 0.89123)], [0.20, new Color(0.25862, 0.57958, 0.99876)], [0.27, new Color(0.15844, 0.73551, 0.92305)], [0.33, new Color(0.09267, 0.86554, 0.7623)], [0.40, new Color(0.19659, 0.94901, 0.59466)], [0.47, new Color(0.42778, 0.99419, 0.38575)], [0.53, new Color(0.64362, 0.98999, 0.23356)], [0.60, new Color(0.80473, 0.92452, 0.20459)], [0.67, new Color(0.93301, 0.81236, 0.22667)], [0.73, new Color(0.99314, 0.67408, 0.20348)], [0.80, new Color(0.9836, 0.49291, 0.12849)], [0.87, new Color(0.92105, 0.31489, 0.05475)], [0.93, new Color(0.81608, 0.18462, 0.01809)], [1.00, new Color(0.66449, 0.08436, 0.00424)]], RAINBOW: [ // 火灾 [0, new Color(0.278, 0, 0.714)], [1 / 6, new Color(0, 0, 1)], [2 / 6, new Color(0, 1, 1)], [3 / 6, new Color(0, 1, 0)], [4 / 6, new Color(1, 1, 0)], [5 / 6, new Color(1, 0.64, 0)], [1, new Color(1, 0, 0)]], CONTOUR: [[0.00, new Color(0, 0, 0)], [0.03, new Color(0, 0, 0)], [0.04, new Color(1, 1, 1)], [1.00, new Color(1, 1, 1)]], ir: [ //红外 [0, new Color('#000000')], [1 / 5, new Color('#230463')], [2 / 5, new Color('#80068B')], [3 / 5, new Color('#F49107')], [4 / 5, new Color('#FAD725')], [1, new Color('#FFFFFF')]] }; var Shaders = {}; Shaders["pointcloud_new.vs"] = "\nprecision highp float;\nprecision highp int;\n \n#define PI 3.141592653589793\n\n\n\n\n#if defined(usePanoMap) \n \n uniform samplerCube pano0Map; //\u968F\u4FBF\u8BBE\u7F6E\u4E00\u4E2AsamplerCube\u53BB\u4F7F\u7528\u90FD\u4F1A\u8BA9\u70B9\u4E91\u6D88\u5931\n uniform samplerCube pano1Map;\n \n uniform float progress;\n uniform float easeInOutRatio;\n\n \n uniform vec3 pano0Position;\n uniform mat4 pano0Matrix; \n uniform vec3 pano1Position;\n uniform mat4 pano1Matrix;\n /*\n varying vec3 vWorldPosition0;\n varying vec3 vWorldPosition1;\n */\n#endif \n\n\n\n\n \n\n//--------------\n\n\n\n\n\nattribute vec3 position;\nattribute vec3 color;\nattribute float intensity;\nattribute float classification; //attribute float classification;\nattribute float returnNumber;\nattribute float numberOfReturns;\nattribute float pointSourceID;\nattribute vec4 indices; //\u6BCF\u4E2A\u70B9\u7684index\nattribute float spacing;\nattribute float gpsTime;\nattribute vec3 normal;\nattribute float aExtra;\n\nattribute float ir; //\u7EA2\u5916\u6E29\u5EA6 \u5F00\u5C14\u6587 \nattribute float temp; //\u706B\u707EAI\u6E29\u5EA6 \u5F00\u5C14\u6587 \n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat4 uViewInv;\n\n//uniform float uScreenWidth;\n//uniform float uScreenHeight;\nuniform vec2 resolution;\n\n\nuniform float fov;\nuniform float near;\nuniform float far;\n\n\n\n\nuniform bool uDebug;\n\nuniform bool uUseOrthographicCamera;\nuniform float uOrthoWidth;\nuniform float uOrthoHeight;\n\n#define CLIPTASK_NONE 0\n#define CLIPTASK_HIGHLIGHT 1\n#define CLIPTASK_SHOW_INSIDE 2\n#define CLIPTASK_SHOW_OUTSIDE 3\n\n#define CLIPMETHOD_INSIDE_ANY 0\n#define CLIPMETHOD_INSIDE_ALL 1\n\n//\u6700\u5916\u5C42\u88C1\u526A\uFF08\u4E0B\u8F7D\uFF09\n#if defined(bigClipInBox)\n\tuniform mat4 clipBoxBig_in;\n#endif\n//\u5185\u5C42\u88C1\u526A\n#if defined(num_in_clipboxes) && num_in_clipboxes > 0\n\tuniform mat4 clipBoxes_in[num_in_clipboxes];\n#endif\n#if defined(num_out_clipboxes) && num_out_clipboxes > 0\n\tuniform mat4 clipBoxes_out[num_out_clipboxes];\n#endif\n\n\n#if defined(num_clipspheres) && num_clipspheres > 0\n\tuniform mat4 uClipSpheres[num_clipspheres];\n#endif\n\n \n#if defined(num_highlightBox) && num_highlightBox > 0\n uniform mat4 boxes_highlight[num_highlightBox]; \n#endif \n\t\t \n\n#if defined(num_prism) && num_prism > 0\n uniform mat3 prismList[num_prism];\n uniform vec2 prismPoints[prismPointCountSum]; \n#endif \n\n#if defined(showBaseHeight) \n uniform sampler2D baseHeightAreaMap ;\n uniform vec2 baseHeightBoundZ;\n uniform vec4 baseHeightBoundXY; \n#endif \n\nuniform float size;\nuniform float minSize;\nuniform float maxSize; \nuniform float orthoMaxSize;//add\n\n\nuniform float uPCIndex;\nuniform float uOctreeSpacing;\nuniform float uNodeSpacing;\nuniform float uOctreeSize;\nuniform vec3 uBBSize;\nuniform float uLevel;\nuniform float levelPercent;//add\nuniform float uVNStart;\nuniform bool uIsLeafNode;\n\nuniform vec3 uColor;\nuniform float uOpacity; \nvarying float vOpacity; //add\n\n\n\nuniform vec2 elevationRange;\nuniform vec2 intensityRange;\n\nuniform vec2 uFilterReturnNumberRange;\nuniform vec2 uFilterNumberOfReturnsRange;\nuniform vec2 uFilterPointSourceIDClipRange;\nuniform vec2 uFilterGPSTimeClipRange;\n//uniform float ufilterByNormalThreshold; \n\nuniform float uGpsScale;\nuniform float uGpsOffset;\n\nuniform vec2 uNormalizedGpsBufferRange;\n\nuniform vec3 uIntensity_gbc;\nuniform vec3 uRGB_gbc;\nuniform vec3 uExtra_gbc;\n\nuniform float uTransition;\nuniform float wRGB;\nuniform float wIntensity;\nuniform float wElevation;\nuniform float wClassification;\nuniform float wReturnNumber;\nuniform float wSourceID;\n\nuniform vec2 uExtraNormalizedRange;\nuniform vec2 uExtraRange;\nuniform float uExtraScale;\nuniform float uExtraOffset;\n\nuniform vec3 uShadowColor;\n\nuniform sampler2D visibleNodes;\nuniform sampler2D gradient;\nuniform sampler2D classificationLUT;\n\n#if defined(color_type_matcap)\nuniform sampler2D matcapTextureUniform;\n#endif\nuniform bool backfaceCulling;\n\n#if defined(num_shadowmaps) && num_shadowmaps > 0\nuniform sampler2D uShadowMap[num_shadowmaps];\nuniform mat4 uShadowWorldView[num_shadowmaps];\nuniform mat4 uShadowProj[num_shadowmaps];\n#endif\n \nuniform vec2 temperRange;\n \n\nvarying vec3\tvColor;\nvarying float\tvLogDepth;\nvarying vec3\tvViewPosition;\nvarying float \tvRadius;\nvarying float \tvPointSize;\n\n \n\nfloat Round(float number){\n\treturn floor(number + 0.5);\n}\n\n// \n// ### ######## ### ######## ######## #### ## ## ######## ###### #### ######## ######## ###### \n// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ## ## ######## ## ## ## ## ###### ###### ## ## ###### ###### \n// ######### ## ## ######### ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ######## ## ## ## ## #### ### ######## ###### #### ######## ######## ###### \n// \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\n\n// ---------------------\n// OCTREE\n// ---------------------\n\n#if (defined(adaptive_point_size) || defined(color_type_level_of_detail)) && defined(tree_type_octree)\n/**\n * number of 1-bits up to inclusive index position\n * number is treated as if it were an integer in the range 0-255\n *\n */\nint numberOfOnes(int number, int index){\n\tint numOnes = 0;\n\tint tmp = 128;\n\tfor(int i = 7; i >= 0; i--){\n\t\t\n\t\tif(number >= tmp){\n\t\t\tnumber = number - tmp;\n\n\t\t\tif(i <= index){\n\t\t\t\tnumOnes++;\n\t\t\t}\n\t\t}\n\t\t\n\t\ttmp = tmp / 2;\n\t}\n\n\treturn numOnes;\n}\n\n\n/**\n * checks whether the bit at index is 1\n * number is treated as if it were an integer in the range 0-255\n *\n */\nbool isBitSet(int number, int index){\n\n\t// weird multi else if due to lack of proper array, int and bitwise support in WebGL 1.0\n\tint powi = 1;\n\tif(index == 0){\n\t\tpowi = 1;\n\t}else if(index == 1){\n\t\tpowi = 2;\n\t}else if(index == 2){\n\t\tpowi = 4;\n\t}else if(index == 3){\n\t\tpowi = 8;\n\t}else if(index == 4){\n\t\tpowi = 16;\n\t}else if(index == 5){\n\t\tpowi = 32;\n\t}else if(index == 6){\n\t\tpowi = 64;\n\t}else if(index == 7){\n\t\tpowi = 128;\n\t}else{\n\t\treturn false;\n\t}\n\n\tint ndp = number / powi;\n\n\treturn mod(float(ndp), 2.0) != 0.0;\n}\n\n\n/**\n * find the LOD at the point position\n */\nfloat getLOD(){//////we use this\n\t \n\tvec3 offset = vec3(0.0, 0.0, 0.0);\n\tint iOffset = int(uVNStart);\n\tfloat depth = uLevel;\n\tfor(float i = 0.0; i <= 30.0; i++){\n\t\tfloat nodeSizeAtLevel = uOctreeSize / pow(2.0, i + uLevel + 0.0);\n\t\t\n\t\tvec3 index3d = (position-offset) / nodeSizeAtLevel;\n\t\tindex3d = floor(index3d + 0.5);\n\t\tint index = int(Round(4.0 * index3d.x + 2.0 * index3d.y + index3d.z));\n\t\t\n\t\tvec4 value = texture2D(visibleNodes, vec2(float(iOffset) / 2048.0, 0.0));\n\t\tint mask = int(Round(value.r * 255.0));\n\n\t\tif(isBitSet(mask, index)){\n\t\t\t// there are more visible child nodes at this position\n\t\t\tint advanceG = int(Round(value.g * 255.0)) * 256;\n\t\t\tint advanceB = int(Round(value.b * 255.0));\n\t\t\tint advanceChild = numberOfOnes(mask, index - 1);\n\t\t\tint advance = advanceG + advanceB + advanceChild;\n\n\t\t\tiOffset = iOffset + advance;\n\t\t\t\n\t\t\tdepth++;\n\t\t}else{\n\t\t\t// no more visible child nodes at this position\n\t\t\t//return value.a * 255.0;\n\n\t\t\tfloat lodOffset = (255.0 * value.a) / 10.0 - 10.0;\n\n\t\t\treturn depth + lodOffset;\n\t\t}\n\t\t\n\t\toffset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;\n\t}\n\t\t\n\treturn depth;\n}\n\nfloat getSpacing(){\n\tvec3 offset = vec3(0.0, 0.0, 0.0);\n\tint iOffset = int(uVNStart);\n\tfloat depth = uLevel;\n\tfloat spacing = uNodeSpacing;\n\tfor(float i = 0.0; i <= 30.0; i++){\n\t\tfloat nodeSizeAtLevel = uOctreeSize / pow(2.0, i + uLevel + 0.0);\n\t\t\n\t\tvec3 index3d = (position-offset) / nodeSizeAtLevel;\n\t\tindex3d = floor(index3d + 0.5);\n\t\tint index = int(Round(4.0 * index3d.x + 2.0 * index3d.y + index3d.z));\n\t\t\n\t\tvec4 value = texture2D(visibleNodes, vec2(float(iOffset) / 2048.0, 0.0));\n\t\tint mask = int(Round(value.r * 255.0));\n\t\tfloat spacingFactor = value.a;\n\n\t\tif(i > 0.0){\n\t\t\tspacing = spacing / (255.0 * spacingFactor);\n\t\t}\n\t\t\n\n\t\tif(isBitSet(mask, index)){\n\t\t\t// there are more visible child nodes at this position\n\t\t\tint advanceG = int(Round(value.g * 255.0)) * 256;\n\t\t\tint advanceB = int(Round(value.b * 255.0));\n\t\t\tint advanceChild = numberOfOnes(mask, index - 1);\n\t\t\tint advance = advanceG + advanceB + advanceChild;\n\n\t\t\tiOffset = iOffset + advance;\n\n\t\t\t//spacing = spacing / (255.0 * spacingFactor);\n\t\t\t//spacing = spacing / 3.0;\n\t\t\t\n\t\t\tdepth++;\n\t\t}else{\n\t\t\t// no more visible child nodes at this position\n\t\t\treturn spacing;\n\t\t}\n\t\t\n\t\toffset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;\n\t}\n\t\t\n\treturn spacing;\n}\n\nfloat getPointSizeAttenuation(){\n\treturn pow(2.0, getLOD());\n}\n\n\n#endif\n\n\n// ---------------------\n// KD-TREE\n// ---------------------\n\n#if (defined(adaptive_point_size) || defined(color_type_level_of_detail)) && defined(tree_type_kdtree)\n\nfloat getLOD(){\n\tvec3 offset = vec3(0.0, 0.0, 0.0);\n\tfloat iOffset = 0.0;\n\tfloat depth = 0.0;\n\t\t\n\t\t\n\tvec3 size = uBBSize;\t\n\tvec3 pos = position;\n\t\t\n\tfor(float i = 0.0; i <= 1000.0; i++){\n\t\t\n\t\tvec4 value = texture2D(visibleNodes, vec2(iOffset / 2048.0, 0.0));\n\t\t\n\t\tint children = int(value.r * 255.0);\n\t\tfloat next = value.g * 255.0;\n\t\tint split = int(value.b * 255.0);\n\t\t\n\t\tif(next == 0.0){\n\t\t \treturn depth;\n\t\t}\n\t\t\n\t\tvec3 splitv = vec3(0.0, 0.0, 0.0);\n\t\tif(split == 1){\n\t\t\tsplitv.x = 1.0;\n\t\t}else if(split == 2){\n\t\t \tsplitv.y = 1.0;\n\t\t}else if(split == 4){\n\t\t \tsplitv.z = 1.0;\n\t\t}\n\t\t\n\t\tiOffset = iOffset + next;\n\t\t\n\t\tfloat factor = length(pos * splitv / size);\n\t\tif(factor < 0.5){\n\t\t\t// left\n\t\tif(children == 0 || children == 2){\n\t\t\t\treturn depth;\n\t\t\t}\n\t\t}else{\n\t\t\t// right\n\t\t\tpos = pos - size * splitv * 0.5;\n\t\t\tif(children == 0 || children == 1){\n\t\t\t\treturn depth;\n\t\t\t}\n\t\t\tif(children == 3){\n\t\t\t\tiOffset = iOffset + 1.0;\n\t\t\t}\n\t\t}\n\t\tsize = size * ((1.0 - (splitv + 1.0) / 2.0) + 0.5);\n\t\t\n\t\tdepth++;\n\t}\n\t\t\n\t\t\n\treturn depth;\t\n}\n\nfloat getPointSizeAttenuation(){\n\treturn 0.5 * pow(1.3, getLOD());\n}\n\n#endif\n\n\n\n// \n// ### ######## ######## ######## #### ######## ## ## ######## ######## ###### \n// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ######## ## ######## ## ## ## ###### ###### \n// ######### ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## \n// ## ## ## ## ## ## #### ######## ####### ## ######## ###### \n// \n\n\n\n// formula adapted from: http://www.dfstudios.co.uk/articles/programming/image-programming-algorithms/image-processing-algorithms-part-5-contrast-adjustment/\nfloat getContrastFactor(float contrast){\n\treturn (1.0158730158730156 * (contrast + 1.0)) / (1.0158730158730156 - contrast);\n}\n\nvec3 getRGB(){\n\tvec3 rgb = color;\n\t\n\trgb = pow(rgb, vec3(uRGB_gbc.x));\n\trgb = rgb + uRGB_gbc.y;\n\trgb = (rgb - 0.5) * getContrastFactor(uRGB_gbc.z) + 0.5;\n\trgb = clamp(rgb, 0.0, 1.0);\n\n\treturn rgb;\n}\n\nfloat getIntensity(){\n\tfloat w = (intensity - intensityRange.x) / (intensityRange.y - intensityRange.x);\n\tw = pow(w, uIntensity_gbc.x);\n\tw = w + uIntensity_gbc.y;\n\tw = (w - 0.5) * getContrastFactor(uIntensity_gbc.z) + 0.5;\n\tw = clamp(w, 0.0, 1.0);\n\n\treturn w;\n}\n\nvec3 getGpsTime(){\n\n\tfloat w = (gpsTime + uGpsOffset) * uGpsScale;\n\n\n\tvec3 c = texture2D(gradient, vec2(w, 1.0 - w)).rgb;\n\n\n\t// vec2 r = uNormalizedGpsBufferRange;\n\t// float w = gpsTime * (r.y - r.x) + r.x;\n\t// w = clamp(w, 0.0, 1.0);\n\t// vec3 c = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\t\n\treturn c;\n}\n\nvec3 getElevation(vec4 world){ \n\tfloat w = (world.z - elevationRange.x) / (elevationRange.y - elevationRange.x);\n\tvec3 cElevation = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\treturn cElevation;\n}\n\n\nvec3 getTemperature(){ //xzw add \n\n float temperature = 0.0;\n \n #if defined color_type_ir \n temperature = ir;\n #else\n temperature = temp;\n #endif\n\tfloat w = (temperature - temperRange.x) / (temperRange.y - temperRange.x);\n w = clamp(w,0.0,1.0);\n\tvec3 c = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\treturn c;\n}\n\n\nvec4 getClassification(){\n\tvec2 uv = vec2(float(classification) / 255.0, 0.5);\n\tvec4 classColor = texture2D(classificationLUT, uv);\n\t\n\treturn classColor;\n}\n\nvec3 getReturns(){\n\n\t// 0b 00_000_111\n\tfloat rn = mod(returnNumber, 8.0);\n\t// 0b 00_111_000\n\tfloat nr = mod(returnNumber / 8.0, 8.0);\n\n\tif(nr <= 1.0){\n\t\treturn vec3(1.0, 0.0, 0.0);\n\t}else{\n\t\treturn vec3(0.0, 1.0, 0.0);\n\t}\n\n\t// return vec3(nr / 4.0, 0.0, 0.0);\n\n\t// if(nr == 1.0){\n\t// \treturn vec3(1.0, 1.0, 0.0);\n\t// }else{\n\t// \tif(rn == 1.0){\n\t// \t\treturn vec3(1.0, 0.0, 0.0);\n\t// \t}else if(rn == nr){\n\t// \t\treturn vec3(0.0, 0.0, 1.0);\n\t// \t}else{\n\t// \t\treturn vec3(0.0, 1.0, 0.0);\n\t// \t}\n\t// }\n\n\t// if(numberOfReturns == 1.0){\n\t// \treturn vec3(1.0, 1.0, 0.0);\n\t// }else{\n\t// \tif(returnNumber == 1.0){\n\t// \t\treturn vec3(1.0, 0.0, 0.0);\n\t// \t}else if(returnNumber == numberOfReturns){\n\t// \t\treturn vec3(0.0, 0.0, 1.0);\n\t// \t}else{\n\t// \t\treturn vec3(0.0, 1.0, 0.0);\n\t// \t}\n\t// }\n}\n\nvec3 getReturnNumber(){\n\tif(numberOfReturns == 1.0){\n\t\treturn vec3(1.0, 1.0, 0.0);\n\t}else{\n\t\tif(returnNumber == 1.0){\n\t\t\treturn vec3(1.0, 0.0, 0.0);\n\t\t}else if(returnNumber == numberOfReturns){\n\t\t\treturn vec3(0.0, 0.0, 1.0);\n\t\t}else{\n\t\t\treturn vec3(0.0, 1.0, 0.0);\n\t\t}\n\t}\n}\n\nvec3 getNumberOfReturns(){\n\tfloat value = numberOfReturns;\n\n\tfloat w = value / 6.0;\n\n\tvec3 color = texture2D(gradient, vec2(w, 1.0 - w)).rgb;\n\n\treturn color;\n}\n\nvec3 getSourceID(){\n\tfloat w = mod(pointSourceID, 10.0) / 10.0;\n\treturn texture2D(gradient, vec2(w,1.0 - w)).rgb;\n}\n\nvec3 getCompositeColor(vec4 world){\n\tvec3 c;\n\tfloat w;\n\n\tc += wRGB * getRGB();\n\tw += wRGB;\n\t\n\tc += wIntensity * getIntensity() * vec3(1.0, 1.0, 1.0);\n\tw += wIntensity;\n\t\n\tc += wElevation * getElevation(world);\n\tw += wElevation;\n\t\n\tc += wReturnNumber * getReturnNumber();\n\tw += wReturnNumber;\n\t\n\tc += wSourceID * getSourceID();\n\tw += wSourceID;\n\t\n\tvec4 cl = wClassification * getClassification();\n\tc += cl.a * cl.rgb;\n\tw += wClassification * cl.a;\n\n\tc = c / w;\n\t\n\tif(w == 0.0){\n\t\t//c = color;\n\t\tgl_Position = vec4(100.0, 100.0, 100.0, 0.0);\n\t}\n\t\n\treturn c;\n}\n\n\nvec3 getNormal(){\n\t//vec3 n_hsv = vec3( modelMatrix * vec4( normal, 0.0 )) * 0.5 + 0.5; // (n_world.xyz + vec3(1.,1.,1.)) / 2.;\n\tvec3 n_view = normalize( vec3(modelViewMatrix * vec4( normal, 0.0 )) );\n\treturn n_view;\n}\nbool applyBackfaceCulling() {\n\t// Black not facing vertices / Backface culling\n \n\tvec3 e = normalize(vec3(modelViewMatrix * vec4( position, 1. )));\n\tvec3 n = getNormal(); // normalize( vec3(modelViewMatrix * vec4( normal, 0.0 )) );\n\n\tif((uUseOrthographicCamera && n.z <= 0.) || (!uUseOrthographicCamera && dot( n, e ) >= 0.)) { \n\t\treturn true;\n\t} else {\n\t\treturn false;\n\t}\n}\n\n#if defined(color_type_matcap)\n// Matcap Material\nvec3 getMatcap(){ \n\tvec3 eye = normalize( vec3( modelViewMatrix * vec4( position, 1. ) ) ); \n\tif(uUseOrthographicCamera) { \n\t\teye = vec3(0., 0., -1.);\n\t}\n\tvec3 r_en = reflect( eye, getNormal() ); // or r_en = e - 2. * dot( n, e ) * n;\n\tfloat m = 2. * sqrt(pow( r_en.x, 2. ) + pow( r_en.y, 2. ) + pow( r_en.z + 1., 2. ));\n\tvec2 vN = r_en.xy / m + .5;\n\treturn texture2D(matcapTextureUniform, vN).rgb; \n}\n#endif\n\nvec3 getExtra(){\n\n\tfloat w = (aExtra + uExtraOffset) * uExtraScale;\n\tw = clamp(w, 0.0, 1.0);\n\n\tvec3 color = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\n\t// vec2 r = uExtraNormalizedRange;\n\n\t// float w = aExtra * (r.y - r.x) + r.x;\n\n\t// w = (w - uExtraRange.x) / (uExtraRange.y - uExtraRange.x);\n\n\t// w = clamp(w, 0.0, 1.0);\n\n\t// vec3 color = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\n\treturn color;\n}\n\nvec3 getColor(vec4 world){\n\tvec3 color;\n\t\n\t#ifdef color_type_rgba\n\t\tcolor = getRGB();\n \n \n\t#elif defined color_type_height || defined color_type_elevation\n\t\tcolor = getElevation(world);\n\t#elif defined color_type_rgb_height\n\t\tvec3 cHeight = getElevation();\n\t\tcolor = (1.0 - uTransition) * getRGB() + uTransition * cHeight;\n #elif defined color_type_ir || defined color_type_temp // add\n color = getTemperature();\n\t#elif defined color_type_depth\n\t\tfloat linearDepth = gl_Position.w;\n\t\tfloat expDepth = (gl_Position.z / gl_Position.w) * 0.5 + 0.5;\n\t\tcolor = vec3(linearDepth, expDepth, 0.0);\n\t\t//color = vec3(1.0, 0.5, 0.3);\n\t#elif defined color_type_intensity\n\t\tfloat w = getIntensity();\n\t\tcolor = vec3(w, w, w);\n\t#elif defined color_type_gps_time\n\t\tcolor = getGpsTime();\n\t#elif defined color_type_intensity_gradient\n\t\tfloat w = getIntensity();\n\t\tcolor = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\t#elif defined color_type_color\n\t\tcolor = uColor;\n\t#elif defined color_type_level_of_detail\n\t\tfloat depth = getLOD();\n\t\tfloat w = depth / 10.0;\n\t\tcolor = texture2D(gradient, vec2(w,1.0-w)).rgb;\n\t#elif defined color_type_indices\n\t\tcolor = indices.rgb;\n\t#elif defined color_type_classification //xzw change \n\t\tvec4 cl = getClassification(); \n vec3 originC = getRGB();\n float blendRatio = 0.5;\n float classAlpha = cl.a * blendRatio; \n\t\tcolor = cl.rgb * classAlpha + originC * (1.0-classAlpha); //mix with old rgba\n\t#elif defined color_type_return_number\n\t\tcolor = getReturnNumber();\n\t#elif defined color_type_returns\n\t\tcolor = getReturns();\n\t#elif defined color_type_number_of_returns\n\t\tcolor = getNumberOfReturns();\n\t#elif defined color_type_source_id\n\t\tcolor = getSourceID();\n\t#elif defined color_type_point_source_id\n\t\tcolor = getSourceID();\n\t#elif defined color_type_normal\n\t\tcolor = (modelMatrix * vec4(normal, 0.0)).xyz;\n\t#elif defined color_type_phong\n\t\tcolor = color;\n\t#elif defined color_type_composite\n\t\tcolor = getCompositeColor(world);\n\t#elif defined color_type_matcap\n\t\tcolor = getMatcap();\n\t#elif defined color_type_heightCpt //add\n color = vec3(0.0,0.0,0.0);\n #else\n\t\tcolor = getExtra();\n\t#endif\n\t\n\tif (backfaceCulling && applyBackfaceCulling()){\n //color = vec3(0.);\n }\n //applyBackfaceCulling\u76F4\u63A5\u8FD4\u56DEfalse\u6216\u8005\u6CE8\u91CAcolor = vec3(0.);\u90FD\u6CA1\u95EE\u9898\n\treturn color;\n}\n\nfloat getPointSize(){\n\tfloat pointSize = 1.0;\n\tfloat maxSize_ = maxSize;\n\tfloat slope = tan(fov / 2.0);\n\tfloat projFactor = -0.5 * resolution.y / (slope * vViewPosition.z);\n \n \n \n /*\n\tfloat scale = length(\n\t\tmodelViewMatrix * vec4(0, 0, 0, 1) - \n\t\tmodelViewMatrix * vec4(uOctreeSpacing, 0, 0, 1)\n\t) / uOctreeSpacing;\n \n\tprojFactor = projFactor * scale;\n\t*/\n \n \n\tfloat r = uOctreeSpacing * 1.7;\n\t//vRadius = r;\n \n \n\t#if defined fixed_point_size\n\t\tpointSize = size;\n\t#elif defined attenuated_point_size\n\t\tif(uUseOrthographicCamera){ \n pointSize = size / uOrthoWidth * resolution.x; //\u6539\u6210\u8FD1\u4F3Cadaptive_point_size\u6839\u636E\u7A97\u53E3\u7F29\u653E\u3002\u5176\u5B9E\u5C31\u662Fsize * zoom (size\u5355\u4F4D\u8F6C\u5316\u4E3A\u7C73\uFF0C\u5982size\u4E3A1\u4EE3\u8868\u4E00\u4E2A\u70B9\u4E91\u5728\u573A\u666F\u4E2D1\u7C73\u5BBD,zoom\u4EE3\u88681px=1/zoom\u7C73)\n maxSize_ = orthoMaxSize; //for panoEditor, when zoom in, need more details, rather than always same size\n\n\t\t}else{ //\u8FD1\u5927\u8FDC\u5C0F\uFF0C\u6A21\u62DF\u771F\u5B9Emesh\uFF0C\u8FB9\u7F18\u653E\u5927\n\t\t\t//pointSize = size * spacing * projFactor; //spacing\u662Fattribute \u4E3A\u7A7A \u5982\u679C\u6709\u8FD9\u4E2A\u503C\u5C31\u80FD\u66F4\u81EA\u9002\u5E94\u586B\u8865\n //pointSize = size * uOctreeSpacing * projFactor / 18.0; //\u76F4\u63A5\u7528cloud\u7684spacing\u91CC\uFF0C\u4E0D\u8FC7\u56E0\u4E3A\u90FD\u4E00\u6837\u6240\u4EE5\u53EF\u80FD\u6CA1\u6709\u4EC0\u4E48\u610F\u4E49\n\t\t\t//pointSize = pointSize * projFactor;\n pointSize = size * projFactor ;\n\t\t}\n \n\t#elif defined adaptive_point_size\n\t\tif(uUseOrthographicCamera) {\n\t\t\tfloat worldSpaceSize = 1.0 * size * r / getPointSizeAttenuation();\n\t\t\tpointSize = (worldSpaceSize / uOrthoWidth) * resolution.x; //uScreenWidth;\n maxSize_ = orthoMaxSize;\n\t\t} else {\n\t\t\tfloat worldSpaceSize = 1.0 * size * r / getPointSizeAttenuation();\n\t\t\tpointSize = worldSpaceSize * projFactor;\n\t\t}\n \n\t#endif\n\n\tpointSize = max(minSize, pointSize);\n\tpointSize = min(maxSize_, pointSize);\n\t\n\tvRadius = pointSize / projFactor;\n\n\treturn pointSize;\n} \n\n\nbool insideBox(mat4 clipBox, vec4 worldPos){//add\n vec4 clipPosition = clipBox * worldPos;\n bool inside = -0.5 <= clipPosition.x && clipPosition.x <= 0.5;\n inside = inside && -0.5 <= clipPosition.y && clipPosition.y <= 0.5;\n inside = inside && -0.5 <= clipPosition.z && clipPosition.z <= 0.5;\n\n return inside;\n}\n\n\n\n#if defined(color_type_heightCpt)\n vec3 percentToByte(float num){\n //\u8F93\u51FA\u662F0-1\uFF0C shader\u7CBE\u5EA6\u4E0D\u591F\uFF0C\u53EA\u591F\u5B583\u4E2A\u6570\n float a = 1.0;\n float result[4];\n for(int i=0;i<3;i++){\n a = i == 2 ? a/255.0 : a / 256.0 ; \n //\u5206\u6210256\u4EFD\uFF0C\u5176\u4E2D255\u4EFD\u7ED9\u5F53\u524D\u4F4D\u7F6E\uFF0C\u6700\u540E\u4E00\u4EFD\u7ED9\u540E\u4E00\u4E2A\u4F4D\u7F6E\uFF0C\u800C\u5230\u4E86\u6700\u540E\u4E00\u4E2A\u4F4D\u7F6E\u65F6\u6CA1\u6709\u4E0B\u4E00\u4E2A\u4F4D\u7F6E\u4E86\u6240\u4EE5\u53EA\u5206\u6210255\u4EFD\n if(num > a){\n float c = num / a;\n float r = floor(c);\n r = min(255.0, r);\n result[i] = r;\n num -= r * a;\n }else{\n result[i] = 0.0;\n }\n } \n return vec3(result[0]/255.0, result[1]/255.0,result[2]/255.0); \n \n //\u9664\u4EE5\u591A\u5C11255\u8FD8\u662F256? \u53C2\u8003uPCIndex / 255.0 \u5E94\u8BE5\u662F255\n } \n#endif\n\n \n\n#if defined(num_prism) && num_prism > 0\n \n \n int insidePrism(mat3 prismInfo, int pointIndexStart, vec4 worldPos){//\u662F\u5426\u5728\u68F1\u67F1\u91CC \n \n float zMin = prismInfo[0][0];\n float zMid = prismInfo[0][1];\n float zMax = prismInfo[0][2]; \n float xMin = prismInfo[1][0];\n float xMax = prismInfo[1][1];\n float yMin = prismInfo[1][2];\n float yMax = prismInfo[2][0]; \n\n int pointCount = int(Round(prismInfo[2][1]));\n \n if( worldPos.x < xMin || worldPos.x > xMax || worldPos.y < yMin || worldPos.y > yMax)return 0;\n #ifndef showBaseHeight\n if( worldPos.z < zMin || worldPos.z > zMax) return 0;\n #endif\n \n \n bool inside = false;\n \n int j=pointCount-1;\n \n for(int i=0; i=pointCount)break;\n float xi = prismPoints[i+pointIndexStart].x, yi = prismPoints[i+pointIndexStart].y, xj = prismPoints[j+pointIndexStart].x, yj = prismPoints[j+pointIndexStart].y;\n\n if(((yi > worldPos.y) != (yj > worldPos.y)) && (worldPos.x < (xj - xi) * (worldPos.y - yi) / (yj - yi) + xi)){\n inside = !inside;\n }\n j=i; \n }\n \n if(inside){\n #ifdef showBaseHeight\n return 1;\n #else \n #ifdef color_type_heightCpt \n vColor = percentToByte((worldPos.z - zMin) / (zMax - zMin)); \n #endif \n #endif\n \n return worldPos.z < zMid ? 1 : 2;\n }else return 0; \n } \n \n#endif\n\n#ifdef showBaseHeight\n float byteToFloat(vec3 color){ \n float percent = 0.0;\n float a = 1.0; \n for(int i=0;i<3;i++){\n a = i == 2 ? a/255.0 : a/256.0; \n percent += a * color[i] ;\n }\n return percent * 255.0 ; \n } \n \n#endif\n\n\n \n\n\nvoid doClipping(vec4 world){\n\n\t/*{ \n\t\tvec4 cl = getClassification(); \n\t\tif(cl.a == 0.0){\n\t\t\tgl_Position = vec4(100.0, 100.0, 100.0, 0.0);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}*/\n\n\t#if defined(clip_return_number_enabled)\n\t{ // return number filter\n\t\tvec2 range = uFilterReturnNumberRange;\n\t\tif(returnNumber < range.x || returnNumber > range.y){\n\t\t\tgl_Position = vec4(100.0, 100.0, 100.0, 0.0);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}\n\t#endif\n\n\t#if defined(clip_number_of_returns_enabled)\n\t{ // number of return filter\n\t\tvec2 range = uFilterNumberOfReturnsRange;\n\t\tif(numberOfReturns < range.x || numberOfReturns > range.y){\n\t\t\tgl_Position = vec4(100.0, 100.0, 100.0, 0.0);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}\n\t#endif\n\n\t#if defined(clip_gps_enabled)\n\t{ // GPS time filter\n\t\tfloat time = (gpsTime + uGpsOffset) * uGpsScale;\n\t\tvec2 range = uFilterGPSTimeClipRange;\n\n\t\tif(time < range.x || time > range.y){\n\t\t\tgl_Position = vec4(100.0, 100.0, 100.0, 0.0);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}\n\t#endif\n\n\t#if defined(clip_point_source_id_enabled)\n\t{ // point source id filter\n\t\tvec2 range = uFilterPointSourceIDClipRange;\n\t\tif(pointSourceID < range.x || pointSourceID > range.y){\n\t\t\tgl_Position = vec4(100.0, 100.0, 100.0, 0.0);\n\t\t\t\n\t\t\treturn;\n\t\t}\n\t}\n\t#endif\n\n\t \n\n\n //\u603B\u5171\u4E09\u79CDbox : \u6700\u5916\u5C42\u53EF\u89C1\u3001\u5185\u5C42\u53EF\u89C1\u548C\u4E0D\u53EF\u89C1(\u5916\u5C42\u53EF\u89C1\u548C\u5185\u5C42\u53EF\u89C1\u662F\u4EA4\u96C6\uFF0C\u5185\u5C42\u53EF\u89C1\u548C\u5185\u5C42\u4E0D\u53EF\u89C1\u662F\u5E76\u96C6)\n \n #if defined(bigClipInBox) \n if(!insideBox(clipBoxBig_in, world)){ \n gl_Position = vec4(100.0, 100.0, 100.0, 1.0);\n return;;\n } \n #endif \n \n #if defined(num_in_clipboxes) && num_in_clipboxes > 0\n //\u5F53\u6709\u53EF\u89C1box\u65F6\uFF0C\u9700\u8981\u5728\u4EFB\u4E00\u53EF\u89C1box\u5185\u624D\u53EF\u89C1\n bool visi1 = false;\n\t\tfor(int i = 0; i < num_in_clipboxes; i++){ \n if(insideBox(clipBoxes_in[i], world)){\n visi1 = true;\n break;\n } \n\t\t}\n if(!visi1){\n gl_Position = vec4(100.0, 100.0, 100.0, 1.0);\n return;\n }\n\t#endif\n\n\n #if defined(num_out_clipboxes) && num_out_clipboxes > 0\n //\u5F53\u6709\u4E0D\u53EF\u89C1box\u65F6\uFF0C\u4E0D\u5728\u6240\u6709\u4E0D\u53EF\u89C1box\u5185\u624D\u53EF\u89C1\n bool visi2 = true;\n\t\tfor(int i = 0; i < num_out_clipboxes; i++){ \n if(insideBox(clipBoxes_out[i], world)){\n visi2 = false;\n break;\n } \n\t\t}\n if(!visi2){\n gl_Position = vec4(100.0, 100.0, 100.0, 1.0);\n return;\n }\n\t#endif \n \n \n #if defined(num_highlightBox) && num_highlightBox > 0\n //\u5F53\u6709\u9AD8\u4EAEbox\u65F6\uFF0C\u9700\u8981\u5728\u4EFB\u4E00\u53EF\u89C1\u9AD8\u4EAE\u5185\u90FD\u9AD8\u5BBD\n bool highlight = false;\n\t\tfor(int i = 0; i < num_highlightBox; i++){ \n if(insideBox(boxes_highlight[i], world)){\n highlight = true;\n break;\n } \n\t\t}\n if(highlight){\n vColor.r += 0.5; \n }\n\t#endif\n \n \n \n int highlight_ = 0; \n \n #if defined(num_prism) && num_prism > 0 \n //\u57FA\u4E8Eprism\u68F1\u67F1\u7684\u571F\u65B9\u91CF\u663E\u793A\u6216\u8BA1\u7B97\n \n int pointIndexStart = 0;\n\t\tfor(int i = 0; i < num_prism; i++){ \n highlight_ = insidePrism(prismList[i], pointIndexStart, world);\n \n if(highlight_>0){ \n #if !defined(showBaseHeight) && !defined(color_type_heightCpt)\n if(highlight_ == 1){\n vColor.r += 0.5; \n }else if(highlight_ == 2){\n vColor.g += 0.5;\n }\n #endif\n \n break;\n }\n \n \n pointIndexStart += int(Round(prismList[i][2][1])); \n\t\t} \n \n #ifdef color_type_heightCpt \n if(highlight_==0){ \n gl_Position = vec4(100.0, 100.0, 100.0, 1.0); //unvisible\n return;\n }\n #endif \n \n\t#endif\n \n \n \n \n #ifdef showBaseHeight\n //\u57FA\u4E8E\u6A21\u578B\u7684\u571F\u65B9\u91CF\u663E\u793A\u6216\u8BA1\u7B97\n bool outsidePrism = false;\n #if defined(num_prism) && num_prism > 0//\u9AD8\u4EAE\u4EA4\u96C6\n if( highlight_ == 0 ){\n outsidePrism = true;\n }\n #endif \n \n if(!outsidePrism){\n \n float zMin = baseHeightBoundZ.x; \n float zMax = baseHeightBoundZ.y; \n float xMin = baseHeightBoundXY.x;\n float xMax = baseHeightBoundXY.y;\n float yMin = baseHeightBoundXY.z;\n float yMax = baseHeightBoundXY.w; \n \n bool inside = false;\n if(world.z < zMin || world.z > zMax || world.x < xMin || world.x > xMax || world.y < yMin || world.y > yMax){\n inside = false;\n }else{\n vec2 uv = vec2((world.x - xMin) / (xMax - xMin), (world.y - yMin) / (yMax - yMin) );\n \n vec4 color = texture2D(baseHeightAreaMap,uv);\n //\u6297\u952F\u9F7F\u5F85\u5199\n \n \n if(color.a==1.0){ \n \n float currentHeight = (world.z - zMin) / (zMax - zMin) ; \n \n #ifdef color_type_heightCpt\n /*float diff = currentHeight - baseHeight;\n if(diff<0.0){\n diff = -diff;\n vOpacity = 0.5;//\u9700\u8981\u900F\u660E\u901A\u9053\uFF0C\u5BB9\u6613\u51FA\u9519\n }else{\n vOpacity = 1.0;\n }\n vColor = percentToByte(diff); \n */ \n vColor = percentToByte(currentHeight);\n //vColor = color.rgb;\n vOpacity = 1.0;\n \n #else\n //vColor = color.rgb;\n float baseHeight = byteToFloat(color.rgb);\n if(currentHeight > baseHeight){\n vColor.g += 0.5; \n }else{\n vColor.r += 0.5; \n }\n #endif\n \n inside = true;\n }else{\n inside = false;\n }\n \n \n \n } \n \n if(inside == false){\n #ifdef color_type_heightCpt\n gl_Position = vec4(100.0, 100.0, 100.0, 1.0); //unvisible\n return;\n #endif\n }\n \n }\n\t#endif\n}\n\n\n\n// \n// ## ## ### #### ## ## \n// ### ### ## ## ## ### ## \n// #### #### ## ## ## #### ## \n// ## ### ## ## ## ## ## ## ## \n// ## ## ######### ## ## #### \n// ## ## ## ## ## ## ### \n// ## ## ## ## #### ## ## \n//\n\n\nvec2 getSamplerCoord( vec3 direction ) \n{\n direction = normalize(direction);\n float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;\n float ty=acos(direction.z)/PI;\n\n return vec2(tx,ty);\n} \n\nvec3 transformAxis( vec3 direction ) //navvis->4dkk\n{\n float y = direction.y;\n direction.y = direction.z;\n direction.z = -y;\n return direction;\n}\n\nvoid main() {\n //bool filtered_by_normal = false; \n float normalZ = 0.0;\n\n\n\n #ifdef use_filter_by_normal\n /*if(abs(getNormal().z) > 0.4) { //ufilterByNormalThreshold \u6682\u5B9A 3\n\t\t\t// Move point outside clip space space to discard it.\n\t\t\t//gl_Position = vec4(0.0, 0.0, 2.0, 1.0); //gl_Position\u7684\u53EF\u89C6\u533A\u57DF\u662F x,y,z\u90FD\u662F[-1,1] \n //return;\n //filtered_by_normal = true; //\u6807\u8BB0\u4E00\u4E0B\u3002\u4E0D\u76F4\u63A5\u4E0D\u7ED8\u5236\uFF0C\u56E0\u4E3A\u6709\u7684\u6CD5\u7EBF\u90FD\u662F\u5782\u76F4\u5411\u4E0A\n \n\t\t}*/\n \n normalZ = abs(getNormal().z);\n #endif\n \n \n vec4 worldPos = modelMatrix * vec4(position, 1.0); \n vec4 mvPosition = modelViewMatrix * vec4(position, 1.0 ); //vec4 mvPosition = viewMatrix * worldPos;\n vViewPosition = mvPosition.xyz;\n gl_Position = projectionMatrix * mvPosition;\n vLogDepth = log2(-mvPosition.z);\n \n \n \n // COLOR\n //\u52A0-------------------\n #if defined(usePanoMap)\n \n vec3 positionLocalToPanoCenter0 = worldPos.xyz - pano0Position;\n vec3 vWorldPosition0 = (vec4(positionLocalToPanoCenter0, 1.0) * pano0Matrix).xyz; \n vWorldPosition0.x *= -1.0;\n vWorldPosition0 = transformAxis(vWorldPosition0);\n \n vec3 positionLocalToPanoCenter1 = worldPos.xyz - pano1Position;\n vec3 vWorldPosition1 = (vec4(positionLocalToPanoCenter1, 1.0) * pano1Matrix).xyz; \n vWorldPosition1.x *= -1.0;\n vWorldPosition1 = transformAxis(vWorldPosition1);\n \n /*\n vec2 samplerCoord0 = getSamplerCoord(vWorldPosition0.xyz);\n vec2 samplerCoord1 = getSamplerCoord(vWorldPosition1.xyz); \n vec4 colorFromPano0 = texture2D(pano0Map,samplerCoord0);\n vec4 colorFromPano1 = texture2D(pano1Map,samplerCoord1);\n */\n \n \n \n \n vec4 colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);\n vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);\n\n vColor = mix(colorFromPano0,colorFromPano1,progress).xyz; \n\n \n //float easeInOutRatio = 0.0; //\u7F13\u51B2\uFF0C\u6E10\u53D8\u70B9\u4E91\u5230\u8D34\u56FE\u7684\u989C\u8272 \n if(progress < easeInOutRatio){\n float easeProgress = (easeInOutRatio - progress) / easeInOutRatio;\n vec3 vColor1 = getColor(worldPos);\n vColor = mix(vColor,vColor1,easeProgress); \n }else if(progress > 1.0 - easeInOutRatio){ \n float easeProgress = (progress - (1.0 - easeInOutRatio) ) / easeInOutRatio;\n vec3 vColor1 = getColor(worldPos);\n vColor = mix(vColor,vColor1,easeProgress); \n }\n \n\n #else\n \n vColor = getColor(worldPos);\n \n #endif\n \n \n //------------------- \n \n if(uOpacity>=1.0 || uUseOrthographicCamera){ \n vOpacity = uOpacity; \n \n \n if(uOpacity<1.0){ //uUseOrthographicCamera \n //\u9632\u6B62\u7F29\u5C0F\u540E\u70B9\u4E91\u51E0\u4E4E\u770B\u4E0D\u89C1 \n vOpacity *= 4.0-3.0 * levelPercent; \n \n }\n \n }else{ //#ifdef attenuated_opacity zoom\u4E0D\u4F1A\u6539\u53D8z \u6240\u4EE5\u8FD9\u5E76\u4E0D\u662F\u7528\u5728\u5206\u5C4F\u65F6\u5019\u7684\n float v = -gl_Position.z-1.0 ; // \u8303\u56F4\u4ECE-2\u52300\uFF0C e\u7684-2\u52300\u6B21\u65B9\u7684\u8303\u56F4\u662F0.818\u52301 \n //vOpacity = uOpacity * exp(v/ (levelPercent * 20.0 ) ); \n \n //\u8FD1\u5904\u52A0\u6DF1\uFF0C\u8FDC\u5904\u53D8\u6DE1 gl_Position.z\u4F3C\u4E4E\u671D\u5411\u5C4F\u5E55\u91CC\u4E3A\u6B63\n float r = clamp( pow(1.1, v/10.0 ), 0.1, 1.0 ); //\u9664\u4EE5\u7684\u6570\u5B57\u8D8A\u5927\uFF0C\u8FD1\u9AD8\u8FDC\u4F4E\u7684\u7A0B\u5EA6\u8D8A\u5C0F\uFF0C\u8303\u56F4\u8D8A\u957F\u3002 \u7A0B\u5EA6\u592A\u9AD8\u8FDC\u5904\u5355\u8584\u7684\u533A\u57DF\u770B\u4E0D\u89C1 pow\u7684\u5E95\u6570\u8D8A\u5927\u6539\u53D8\u7387\u8D8A\u5927\n vOpacity = uOpacity * r ;\n \n \n } \n \n vOpacity *= max(0.3, pow((1.0 - normalZ),5.0));//\u5782\u76F4\u671D\u76F8\u673A\u65F6\u964D\u4F4E\u900F\u660E\u5EA6 \n //vOpacity = clamp(vOpacity, 0.0, 1.0); \n\n\n\n // POINT SIZE\n float pointSize = getPointSize();\n \n gl_PointSize = pointSize;\n vPointSize = pointSize;\n\n \n \n \n \n\n // only for \"replacing\" approaches\n // if(getLOD() != uLevel){\n // \tgl_Position = vec4(10.0, 10.0, 10.0, 1.0);\n // }\n\n\n #if defined hq_depth_pass\n float originalDepth = gl_Position.w;\n float adjustedDepth = originalDepth + 2.0 * vRadius;\n float adjust = adjustedDepth / originalDepth;\n\n mvPosition.xyz = mvPosition.xyz * adjust;\n gl_Position = projectionMatrix * mvPosition;\n #endif\n\n\n // CLIPPING\n doClipping(worldPos);\n\n #if defined(num_clipspheres) && num_clipspheres > 0\n for(int i = 0; i < num_clipspheres; i++){\n vec4 sphereLocal = uClipSpheres[i] * mvPosition;\n\n float distance = length(sphereLocal.xyz);\n\n if(distance < 1.0){\n float w = distance;\n vec3 cGradient = texture2D(gradient, vec2(w, 1.0 - w)).rgb;\n \n vColor = cGradient;\n //vColor = cGradient * 0.7 + vColor * 0.3;\n }\n }\n #endif\n\n #if defined(num_shadowmaps) && num_shadowmaps > 0\n\n const float sm_near = 0.1;\n const float sm_far = 10000.0;\n\n for(int i = 0; i < num_shadowmaps; i++){\n vec3 viewPos = (uShadowWorldView[i] * vec4(position, 1.0)).xyz;\n float distanceToLight = abs(viewPos.z);\n \n vec4 projPos = uShadowProj[i] * uShadowWorldView[i] * vec4(position, 1);\n vec3 nc = projPos.xyz / projPos.w;\n \n float u = nc.x * 0.5 + 0.5;\n float v = nc.y * 0.5 + 0.5;\n\n vec2 sampleStep = vec2(1.0 / (2.0*1024.0), 1.0 / (2.0*1024.0)) * 1.5;\n vec2 sampleLocations[9];\n sampleLocations[0] = vec2(0.0, 0.0);\n sampleLocations[1] = sampleStep;\n sampleLocations[2] = -sampleStep;\n sampleLocations[3] = vec2(sampleStep.x, -sampleStep.y);\n sampleLocations[4] = vec2(-sampleStep.x, sampleStep.y);\n\n sampleLocations[5] = vec2(0.0, sampleStep.y);\n sampleLocations[6] = vec2(0.0, -sampleStep.y);\n sampleLocations[7] = vec2(sampleStep.x, 0.0);\n sampleLocations[8] = vec2(-sampleStep.x, 0.0);\n\n float visibleSamples = 0.0;\n float numSamples = 0.0;\n\n float bias = vRadius * 2.0;\n\n for(int j = 0; j < 9; j++){\n vec4 depthMapValue = texture2D(uShadowMap[i], vec2(u, v) + sampleLocations[j]);\n\n float linearDepthFromSM = depthMapValue.x + bias;\n float linearDepthFromViewer = distanceToLight;\n\n if(linearDepthFromSM > linearDepthFromViewer){\n visibleSamples += 1.0;\n }\n\n numSamples += 1.0;\n }\n\n float visibility = visibleSamples / numSamples;\n\n if(u < 0.0 || u > 1.0 || v < 0.0 || v > 1.0 || nc.x < -1.0 || nc.x > 1.0 || nc.y < -1.0 || nc.y > 1.0 || nc.z < -1.0 || nc.z > 1.0){\n //vColor = vec3(0.0, 0.0, 0.2);\n }else{\n //vColor = vec3(1.0, 1.0, 1.0) * visibility + vec3(1.0, 1.0, 1.0) * vec3(0.5, 0.0, 0.0) * (1.0 - visibility);\n vColor = vColor * visibility + vColor * uShadowColor * (1.0 - visibility);\n }\n\n\n }\n\n #endif\n\n \n \n}\n"; Shaders["pointcloud_new.fs"] = "\n#if defined paraboloid_point_shape\n\t#extension GL_EXT_frag_depth : enable\n#endif\n#define PI 3.141592653589793\n\n\n\nprecision highp float;\nprecision highp int;\n\n/*\n#if defined(usePanoMap) \n \n uniform samplerCube pano0Map; //\u968F\u4FBF\u8BBE\u7F6E\u4E00\u4E2AsamplerCube\u53BB\u4F7F\u7528\u90FD\u4F1A\u8BA9\u70B9\u4E91\u6D88\u5931\n uniform samplerCube pano1Map;\n \n uniform float progress;\n uniform float easeInOutRatio;\n\n \n uniform vec3 pano0Position;\n uniform mat4 pano0Matrix; \n uniform vec3 pano1Position;\n uniform mat4 pano1Matrix;\n varying vec3 vWorldPosition0;\n varying vec3 vWorldPosition1;\n\n#endif \n*/\n\n\n\n//------------\n\n\n\n\n\nuniform mat4 viewMatrix;\nuniform mat4 uViewInv;\nuniform mat4 uProjInv;\nuniform vec3 cameraPosition;\n\n\nuniform mat4 projectionMatrix;\n//uniform float uOpacity;\nvarying float vOpacity; //add\n\nuniform float blendHardness;\nuniform float blendDepthSupplement;\nuniform float fov;\nuniform float uSpacing;\nuniform float near;\nuniform float far;\nuniform float uPCIndex;\nuniform float uScreenWidth;\nuniform float uScreenHeight;\n\nvarying vec3\tvColor;\nvarying float\tvLogDepth;\nvarying vec3\tvViewPosition;\nvarying float\tvRadius;\nvarying float \tvPointSize;\nvarying vec3 \tvPosition;\n\n\nfloat specularStrength = 1.0;\n\n\nvec2 getSamplerCoord( vec3 direction ) \n{\n direction = normalize(direction);\n float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;\n float ty=acos(direction.z)/PI;\n\n return vec2(tx,ty);\n} \n\n\n\n\nvoid main() {\n\n vec3 color = vColor; \n\t \n \n /*#if defined(usePanoMap) //\u52A0 \u7ECF\u6D4B\u8BD5\uFF0C\u5373\u4F7F\u5168\u90E8\u5199\u5728fragment\u91CC\u4E5F\u662F\u65E0\u8BBApointsize\u591A\u5927\u90FD\u662F\u4E00\u4E2A\u70B9\u4E00\u4E2A\u989C\u8272\uFF0C\u6240\u4EE5\u5E72\u8106\u5199\u5728vectex\u91CC\n \n \n vec4 colorFromPano0=textureCube(pano0Map,vWorldPosition0.xyz);\n vec4 colorFromPano1=textureCube(pano1Map,vWorldPosition1.xyz);\n \n color = mix(colorFromPano0,colorFromPano1,progress).xyz; \n \n \n //float easeInOutRatio = 0.0; //\u7F13\u51B2\uFF0C\u6E10\u53D8\u70B9\u4E91\u5230\u8D34\u56FE\u7684\u989C\u8272 \n if(progress < easeInOutRatio){\n float easeProgress = (easeInOutRatio - progress) / easeInOutRatio; \n color = mix(color,vColor,easeProgress); \n }else if(progress > 1.0 - easeInOutRatio){ \n float easeProgress = (progress - (1.0 - easeInOutRatio) ) / easeInOutRatio; \n color = mix(color,vColor,easeProgress); \n }\n \n \n #else \n color = vColor;\n #endif*/\n \n \n \n\tfloat depth = gl_FragCoord.z;\n\n\t#if defined(circle_point_shape) || defined(paraboloid_point_shape) \n\t\tfloat u = 2.0 * gl_PointCoord.x - 1.0;\n\t\tfloat v = 2.0 * gl_PointCoord.y - 1.0;\n\t#endif\n\t\n\t#if defined(circle_point_shape) \n\t\tfloat cc = u*u + v*v;\n\t\tif(cc > 1.0){\n\t\t\tdiscard;\n\t\t}\n\t#endif\n\t\n\n \n\n\t\n \n #if defined color_type_indices //pick point recognize\n\t\tgl_FragColor = vec4(color, uPCIndex / 255.0); //uPCIndex : node Index\n\t#else\n\t\tgl_FragColor = vec4(color, vOpacity);\n\t#endif\n \n \n \n \n \n \n \n \n\n\t#if defined paraboloid_point_shape\n\t\tfloat wi = 0.0 - ( u*u + v*v);\n\t\tvec4 pos = vec4(vViewPosition, 1.0);\n\t\tpos.z += wi * vRadius;\n\t\tfloat linearDepth = -pos.z;\n\t\tpos = projectionMatrix * pos;\n\t\tpos = pos / pos.w;\n\t\tfloat expDepth = pos.z;\n\t\tdepth = (pos.z + 1.0) / 2.0;\n\t\tgl_FragDepthEXT = depth;\n\t\tgl_FragDepthEXT = clamp(gl_FragDepthEXT, 0.0, 1.0); //add\n \n \n\t\t#if defined(color_type_depth)\n\t\t\tcolor.r = linearDepth;\n\t\t\tcolor.g = expDepth;\n\t\t#endif\n\t\t\n\t\t#if defined(use_edl)\n\t\t\tgl_FragColor.a = log2(linearDepth);\n\t\t#endif\n\t\t\n\t#else\n\t\t#if defined(use_edl)\n\t\t\tgl_FragColor.a = vLogDepth;\n\t\t#endif\n\t#endif\n\n\t#if defined(weighted_splats)\n\t\tfloat distance = 2.0 * length(gl_PointCoord.xy - 0.5);\n\t\tfloat weight = max(0.0, 1.0 - distance);\n\t\tweight = pow(weight, 1.5);\n\n\t\tgl_FragColor.a = weight;\n\t\tgl_FragColor.xyz = gl_FragColor.xyz * weight;\n\t#endif\n\n\t//gl_FragColor = vec4(0.0, 0.7, 0.0, 1.0);\n\t\n}\n\n\n"; Shaders["pointcloud_sm.vs"] = "\nprecision mediump float;\nprecision mediump int;\n\nattribute vec3 position;\nattribute vec3 color;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\n\nuniform float uScreenWidth;\nuniform float uScreenHeight;\nuniform float near;\nuniform float far;\n\nuniform float uSpacing;\nuniform float uOctreeSize;\nuniform float uLevel;\nuniform float uVNStart;\n\nuniform sampler2D visibleNodes;\n\nvarying float vLinearDepth;\nvarying vec3 vColor;\n\n#define PI 3.141592653589793\n\n\n\n// ---------------------\n// OCTREE\n// ---------------------\n\n#if defined(adaptive_point_size)\n/**\n * number of 1-bits up to inclusive index position\n * number is treated as if it were an integer in the range 0-255\n *\n */\nfloat numberOfOnes(float number, float index){\n\tfloat tmp = mod(number, pow(2.0, index + 1.0));\n\tfloat numOnes = 0.0;\n\tfor(float i = 0.0; i < 8.0; i++){\n\t\tif(mod(tmp, 2.0) != 0.0){\n\t\t\tnumOnes++;\n\t\t}\n\t\ttmp = floor(tmp / 2.0);\n\t}\n\treturn numOnes;\n}\n\n\n/**\n * checks whether the bit at index is 1\n * number is treated as if it were an integer in the range 0-255\n *\n */\nbool isBitSet(float number, float index){\n\treturn mod(floor(number / pow(2.0, index)), 2.0) != 0.0;\n}\n\n\n/**\n * find the LOD at the point position\n */\nfloat getLOD(){\n\t\n\tvec3 offset = vec3(0.0, 0.0, 0.0);\n\tfloat iOffset = uVNStart;\n\tfloat depth = uLevel;\n\tfor(float i = 0.0; i <= 30.0; i++){\n\t\tfloat nodeSizeAtLevel = uOctreeSize / pow(2.0, i + uLevel + 0.0);\n\t\t\n\t\tvec3 index3d = (position-offset) / nodeSizeAtLevel;\n\t\tindex3d = floor(index3d + 0.5);\n\t\tfloat index = 4.0 * index3d.x + 2.0 * index3d.y + index3d.z;\n\t\t\n\t\tvec4 value = texture2D(visibleNodes, vec2(iOffset / 2048.0, 0.0));\n\t\tfloat mask = value.r * 255.0;\n\t\tif(isBitSet(mask, index)){\n\t\t\t// there are more visible child nodes at this position\n\t\t\tiOffset = iOffset + value.g * 255.0 * 256.0 + value.b * 255.0 + numberOfOnes(mask, index - 1.0);\n\t\t\tdepth++;\n\t\t}else{\n\t\t\t// no more visible child nodes at this position\n\t\t\treturn depth;\n\t\t}\n\t\t\n\t\toffset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;\n\t}\n\t\t\n\treturn depth;\n}\n\n#endif\n\nfloat getPointSize(){\n\tfloat pointSize = 1.0;\n\t\n\tfloat slope = tan(fov / 2.0);\n\tfloat projFactor = -0.5 * uScreenHeight / (slope * vViewPosition.z);\n\t\n\tfloat r = uOctreeSpacing * 1.5;\n\tvRadius = r;\n\t#if defined fixed_point_size\n\t\tpointSize = size;\n\t#elif defined attenuated_point_size\n\t\tif(uUseOrthographicCamera){\n\t\t\tpointSize = size;\t\t\t\n\t\t}else{\n\t\t\tpointSize = pointSize * projFactor;\n\t\t}\n\t#elif defined adaptive_point_size\n\t\tif(uUseOrthographicCamera) {\n\t\t\tfloat worldSpaceSize = 1.5 * size * r / getPointSizeAttenuation();\n\t\t\tpointSize = (worldSpaceSize / uOrthoWidth) * uScreenWidth;\n\t\t} else {\n\t\t\tfloat worldSpaceSize = 1.5 * size * r / getPointSizeAttenuation();\n\t\t\tpointSize = worldSpaceSize * projFactor;\n\t\t}\n\t#endif\n\n\tpointSize = max(minSize, pointSize);\n\tpointSize = min(maxSize, pointSize);\n\t\n\tvRadius = pointSize / projFactor;\n\n\treturn pointSize;\n}\n\n\nvoid main() {\n\n\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n\tvLinearDepth = gl_Position.w;\n\n\tfloat pointSize = getPointSize();\n\tgl_PointSize = pointSize;\n\n}\n"; Shaders["pointcloud_sm.fs"] = "\nprecision mediump float;\nprecision mediump int;\n\nvarying vec3 vColor;\nvarying float vLinearDepth;\n\nvoid main() {\n\n\t//gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n\t//gl_FragColor = vec4(vColor, 1.0);\n\t//gl_FragColor = vec4(vLinearDepth, pow(vLinearDepth, 2.0), 0.0, 1.0);\n\tgl_FragColor = vec4(vLinearDepth, vLinearDepth / 30.0, vLinearDepth / 30.0, 1.0);\n\t\n}\n\n\n"; Shaders["normalize.vs"] = "\nprecision mediump float;\nprecision mediump int;\n\nattribute vec3 position;\nattribute vec2 uv;\n\nuniform mat4 projectionMatrix;\nuniform mat4 modelViewMatrix;\n\nvarying vec2 vUv;\n\nvoid main() {\n\tvUv = uv;\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);\n}"; Shaders["normalize.fs"] = "\n#extension GL_EXT_frag_depth : enable\n\nprecision mediump float;\nprecision mediump int;\n\nuniform sampler2D uWeightMap;\nuniform sampler2D uDepthMap;\n\nvarying vec2 vUv;\n\nvoid main() {\n\tfloat depth = texture2D(uDepthMap, vUv).r;\n\t\n\tif(depth >= 1.0){\n\t\tdiscard;\n\t}\n\n\tgl_FragColor = vec4(depth, 1.0, 0.0, 1.0);\n\n\tvec4 color = texture2D(uWeightMap, vUv); \n\tcolor = color / color.w;\n\t\n\tgl_FragColor = vec4(color.xyz, 1.0); \n\t\n\tgl_FragDepthEXT = depth;\n\n\n}"; Shaders["normalize_and_edl.fs"] = "\n#extension GL_EXT_frag_depth : enable\n\n// \n// adapted from the EDL shader code from Christian Boucheny in cloud compare:\n// https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL\n//\n\nprecision mediump float;\nprecision mediump int;\n\nuniform sampler2D uWeightMap;\nuniform sampler2D uEDLMap;\nuniform sampler2D uDepthMap;\n\nuniform float screenWidth;\nuniform float screenHeight;\nuniform vec2 neighbours[NEIGHBOUR_COUNT];\nuniform float edlStrength;\nuniform float radius;\n\nvarying vec2 vUv;\n\nfloat response(float depth){\n\tvec2 uvRadius = radius / vec2(screenWidth, screenHeight);\n\t\n\tfloat sum = 0.0;\n\t\n\tfor(int i = 0; i < NEIGHBOUR_COUNT; i++){\n\t\tvec2 uvNeighbor = vUv + uvRadius * neighbours[i];\n\t\t\n\t\tfloat neighbourDepth = texture2D(uEDLMap, uvNeighbor).a;\n\n\t\tif(neighbourDepth != 0.0){\n\t\t\tif(depth == 0.0){\n\t\t\t\tsum += 100.0;\n\t\t\t}else{\n\t\t\t\tsum += max(0.0, depth - neighbourDepth);\n\t\t\t}\n\t\t}\n\t}\n\t\n\treturn sum / float(NEIGHBOUR_COUNT);\n}\n\nvoid main() {\n\n\tfloat edlDepth = texture2D(uEDLMap, vUv).a;\n\tfloat res = response(edlDepth);\n\tfloat shade = exp(-res * 300.0 * edlStrength);\n\n\tfloat depth = texture2D(uDepthMap, vUv).r;\n\tif(depth >= 1.0 && res == 0.0){\n\t\tdiscard;\n\t}\n\t\n\tvec4 color = texture2D(uWeightMap, vUv); \n\tcolor = color / color.w;\n\tcolor = color * shade;\n\n\tgl_FragColor = vec4(color.xyz, 1.0); \n\n\tgl_FragDepthEXT = depth;\n}"; Shaders["edl_new.vs"] = "\nprecision mediump float;\nprecision mediump int;\n\nattribute vec3 position;\nattribute vec2 uv;\n\nuniform mat4 projectionMatrix;\nuniform mat4 modelViewMatrix;\n\nvarying vec2 vUv;\n\nvoid main() {\n\tvUv = uv;\n\t\n\tvec4 mvPosition = modelViewMatrix * vec4(position,1.0);\n\n\tgl_Position = projectionMatrix * mvPosition;\n}"; Shaders["edl_new.fs"] = "\n#extension GL_EXT_frag_depth : enable\n\n// \n// adapted from the EDL shader code from Christian Boucheny in cloud compare:\n// https://github.com/cloudcompare/trunk/tree/master/plugins/qEDL/shaders/EDL\n//\n\nprecision mediump float;\nprecision mediump int;\n\n//uniform float screenWidth;\n//uniform float screenHeight;\nuniform vec2 resolution;\n\n\nuniform vec2 neighbours[NEIGHBOUR_COUNT];\nuniform float edlStrength;\nuniform float radius;\nuniform float opacity;\n\n//uniform float uNear;\n//uniform float uFar;\n\nuniform mat4 uProj;\n\nuniform sampler2D uEDLColor;\nuniform sampler2D uEDLDepth;\n\nvarying vec2 vUv;\n\nuniform int useEDL;\n\nfloat response(float depth){\n\tvec2 uvRadius = radius / resolution; //vec2(screenWidth, screenHeight);\n\t\n\tfloat sum = 0.0;\n\t\n\tfor(int i = 0; i < NEIGHBOUR_COUNT; i++){\n\t\tvec2 uvNeighbor = vUv + uvRadius * neighbours[i];\n\t\t//\u83B7\u53D6\u5468\u56F4\u516B\u4E2A\u683C\u5B50\u7684\u503C\n\t\tfloat neighbourDepth = texture2D(uEDLColor, uvNeighbor).a;\n\t\tneighbourDepth = (neighbourDepth == 1.0) ? 0.0 : neighbourDepth;\n\n\t\tif(neighbourDepth != 0.0){\n\t\t\t //if(depth == 0.0){\n\t\t\t //\tsum += 100.0;\n\t\t\t //}else{\n\t\t\t\tsum += max(0.0, depth - neighbourDepth); //\u83B7\u53D6\u5DEE\u503C\n\t\t\t //}\n\t\t}\n\t}\n\t\n\treturn sum / float(NEIGHBOUR_COUNT);\n}\n\nvoid main(){\n\tvec4 cEDL = texture2D(uEDLColor, vUv);\n\t\n\tfloat depth = cEDL.a;\n\tdepth = (depth == 1.0) ? 0.0 : depth;\n \n if(depth == 0.0){ //\u53BB\u6389\u8FD9\u53E5\u5C31\u80FD\u5728\u65E0\u70B9\u4E91\u50CF\u7D20\u7684\u5730\u65B9\u6E32\u67D3outline\uFF0C\u4F46\u4F1A\u906E\u4F4F\u5176\u4ED6mesh\n\t\tdiscard;\n\t}\n \n \n if(useEDL == 1){\n float res = response(depth); \n float shade = exp(-res * 300.0 * edlStrength); //\u81EA\u7136\u5E38\u6570e\u4E3A\u5E95\u7684\u6307\u6570\u51FD\u6570 \n const float min = 0.2; //add \u4F7F\u8FB9\u7F18\u8272\u53D8\u6D45 \u8303\u56F4\u4ECE0-1 \u53D8\u4E3Amin-1\n shade = shade * (1.0-min) + min;\n gl_FragColor = vec4(cEDL.rgb * shade, opacity); \n }else{//\u52A0 \u4E0D\u6539\u989C\u8272\u7684\u60C5\u51B5 \n gl_FragColor = vec4(cEDL.rgb, opacity);\n } \n //\u6CE8\u610F\uFF0CedlStrength\u8D8A\u5F3A\uFF0C\u5728\u76F8\u540Cdepth\u7684\u53E0\u653E\u7684\u70B9\u4E91\u95F4\u7684\u8F6E\u5ED3\u7EBF\u989C\u8272\u8D8A\u6DF1\n //\u4F5C\u7528\uFF1A\u52FE\u52D2\u8FB9\u7F18\uFF0C\u5F3A\u5316\u6DF1\u5EA6\u5DEE\u5F02\uFF0C\u6A21\u62DF\u9634\u5F71\uFF0C\u52A0\u5F3A\u7ED3\u6784\u7ACB\u4F53\u611F\n \n { // write regular hyperbolic depth values to depth buffer \u4FEE\u6539\u6DF1\u5EA6\n float dl = pow(2.0, depth);\n\n vec4 dp = uProj * vec4(0.0, 0.0, -dl, 1.0);\n float pz = dp.z / dp.w;\n float fragDepth = (pz + 1.0) / 2.0;\n\n gl_FragDepthEXT = fragDepth;\n }\n\t\n}\n"; Shaders["blur.vs"] = "\nvarying vec2 vUv;\n\nvoid main() {\n\tvUv = uv;\n\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);\n}"; Shaders["blur.fs"] = "\nuniform mat4 projectionMatrix;\n\nuniform float screenWidth;\nuniform float screenHeight;\nuniform float near;\nuniform float far;\n\nuniform sampler2D map;\n\nvarying vec2 vUv;\n\nvoid main() {\n\n\tfloat dx = 1.0 / screenWidth;\n\tfloat dy = 1.0 / screenHeight;\n\n\tvec3 color = vec3(0.0, 0.0, 0.0);\n\tcolor += texture2D(map, vUv + vec2(-dx, -dy)).rgb;\n\tcolor += texture2D(map, vUv + vec2( 0, -dy)).rgb;\n\tcolor += texture2D(map, vUv + vec2(+dx, -dy)).rgb;\n\tcolor += texture2D(map, vUv + vec2(-dx, 0)).rgb;\n\tcolor += texture2D(map, vUv + vec2( 0, 0)).rgb;\n\tcolor += texture2D(map, vUv + vec2(+dx, 0)).rgb;\n\tcolor += texture2D(map, vUv + vec2(-dx, dy)).rgb;\n\tcolor += texture2D(map, vUv + vec2( 0, dy)).rgb;\n\tcolor += texture2D(map, vUv + vec2(+dx, dy)).rgb;\n\n\tcolor = color / 9.0;\n\t\n\tgl_FragColor = vec4(color, 1.0);\n}"; Shaders["depthBasic.vs"] = "\n \n#if defined use_map \n varying vec2 vUv;\n \n #ifdef UV_Transform \n uniform mat3 uvTransform;\n #endif\n#endif \n\nvoid main() {\n #if defined use_map \n #ifdef UV_Transform \n vUv = ( uvTransform * vec3( uv, 1 ) ).xy; //include \n #else\n vUv = uv;\n #endif\n #endif\n \n \n vec4 mvPosition = vec4(position, 1.0);\n \n //add instanceMesh\n #ifdef USE_INSTANCING \n mvPosition = instanceMatrix * mvPosition;\n #endif\n \n gl_Position = projectionMatrix * modelViewMatrix * mvPosition;\n} \n "; Shaders["depthBasic.fs"] = "\nuniform float opacity;\n//uniform float mapScale;\nuniform vec3 baseColor;\n\n\n\n\n\n#if defined use_map\n varying vec2 vUv;\n uniform sampler2D map; \n uniform vec3 mapColor;\n \n vec4 applyMapColor(vec4 color){ \n vec2 uv = vUv;\n /*if(mapScale != 1.0){ \n uv = (vUv - 0.5) / mapScale + 0.5; \n if(uv.x > 1.0 || uv.y > 1.0 || uv.x < 0.0 || uv.y < 0.0){\n //color = vec4(0.0,0.0,0.0,0.0);\n discard;\n } \n }*/\n vec4 colorFromMap = texture2D(map, uv);\n \n #if defined mapOverlay\n //\u8D34\u56FE\u53E0\u52A0\u5728\u57FA\u7840\u8272\u4E0A\uFF0C\u800C\u975E\u76F8\u4E58\n colorFromMap.rgb *= mapColor;\n color = color * (1.0-colorFromMap.a) + colorFromMap;\n #else\n color *= colorFromMap;\n \n #endif\n \n return color;\n }\n#endif\n \n\n#if defined(GL_EXT_frag_depth) && defined(useDepth)\n //\u4F3C\u4E4E\u901A\u8FC7gl.getExtension('EXT_frag_depth')\u5F97\u5230\u7684GL_EXT_frag_depth\n \n uniform sampler2D depthTexture;\n \n uniform vec2 resolution;\n uniform vec2 viewportOffset; // viewportOffset \u8303\u56F4\u4ECE0-\u6574\u4E2A\u753B\u5E03\u7684\u50CF\u7D20\n uniform vec3 backColor;\n uniform float occlusionDistance;\n uniform float clipDistance;\n uniform float startClipDis;\n uniform float startOcclusDis;\n uniform float maxClipFactor;\n uniform float maxOcclusionFactor;\n //uniform bool uUseOrthographicCamera; \n\n \n#endif \n\n#if defined(GL_EXT_frag_depth) && defined(useDepth) || defined(FadeFar) \n uniform float nearPlane;\n uniform float farPlane; \n float convertToLinear(float zValue){\n //if(uUseOrthographicCamera){\n // return zValue*(farPlane-nearPlane)+nearPlane;\n //}else{ \n float z = zValue * 2.0 - 1.0;\n return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - z * (farPlane - nearPlane));\n //} \n } \n#endif\n\n#if defined(FadeFar)\n uniform float fadeFar;\n#endif\n//#include \n \nvoid main() {\n //#include \n \n vec4 color = vec4(baseColor, opacity);\n \n \n \n #if defined(GL_EXT_frag_depth) && defined(useDepth) || defined(FadeFar) \n \n float fragDepth = convertToLinear(gl_FragCoord.z);\n \n #if defined(FadeFar) \n float fadeOutFar = fadeFar * 1.3; //\u5B8C\u5168\u6D88\u5931\u8DDD\u79BB\n if(fragDepth > fadeOutFar){\n discard;\n }else if(fragDepth > fadeFar){\n color.a *= (fadeOutFar - fragDepth) / (fadeOutFar - fadeFar);\n \n } \n #endif\n \n \n float mixFactor = 0.0;\n float clipFactor = 0.0;\n \n #if defined(GL_EXT_frag_depth) && defined(useDepth)\n \n vec2 depthTxtCoords = vec2(gl_FragCoord.x-viewportOffset.x, gl_FragCoord.y - viewportOffset.y) / resolution;\n //gl_FragCoord\u5927\u5C0F\u4E3A viewport client\u5927\u5C0F \n \n float textureDepth = convertToLinear(texture2D(depthTexture, depthTxtCoords).r);\n\n \n float delta = fragDepth - textureDepth;\n\n if (delta > 0.0)//\u5DEE\u8DDD\n {\n // occlusionDistance and clipDistance define the width of the respective zones and\n // mixFactor and clipFactor express the interpolation between the two colors depending on the position\n // of the current fragment withing those zones.\n \n mixFactor = clamp((delta - startOcclusDis) / (occlusionDistance - startOcclusDis), 0.0, maxOcclusionFactor);\n clipFactor = clamp((delta - startClipDis) / (clipDistance - startClipDis), 0.0, maxClipFactor);\n \n \n } \n #endif\n \n \n // If the fragment is totally transparent, don't bother drawing it\n if (clipFactor == 1.0)\n {\n discard;\n }else{\n \n #if defined use_map\n color = applyMapColor(color); \n #endif\n \n #if defined(GL_EXT_frag_depth) && defined(useDepth)\n color = vec4(mix(color.rgb, backColor, mixFactor), color.a * (1.0 - clipFactor));\n #endif \n }\n \n \n #else\n #if defined use_map\n color = applyMapColor(color); \n #endif \n #endif\n \n gl_FragColor = color;\n \n}\n"; Shaders["copyCubeMap.vs"] = "varying vec3 vWorldPos;\nvec3 transformAxis( vec3 direction ) //navvis->4dkk\n{\n float y = direction.y;\n direction.y = direction.z;\n direction.z = -y;\n return direction;\n}\nvoid main() {\n vWorldPos = vec3(-position.x, -position.y, position.z);\n //vWorldPos = transformAxis(vWorldPos);\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n} \n "; Shaders["copyCubeMap.fs"] = "varying vec3 vWorldPos;\nuniform float alpha;\nuniform samplerCube tDiffuse;\n\n\nvoid main() {\n vec4 texColor = textureCube(tDiffuse, vWorldPos);\n gl_FragColor = vec4(texColor.rgb, texColor.a * alpha);\n} \n "; Shaders["basicTextured.vs"] = " \n//#include \n \nvarying vec2 vUv;\n\n\n\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n \n /*#include \n #include \n #include */\n} \n "; Shaders["basicTextured.fs"] = "varying vec2 vUv;\nuniform float opacity;\n \n\n#ifdef HasMap\n uniform sampler2D map; \n#endif\n#ifdef HasColor\n uniform vec3 color;\n#endif\n\nvoid main() {\n vec4 color_;\n #ifdef HasColor\n color_ = vec4(color, opacity); \n #else\n color_ = vec4(1.0,1.0,1.0, opacity);\n #endif\n \n #ifdef HasMap\n vec4 texColor = texture2D(map, vUv); \n gl_FragColor = texColor * color_;\n \n /*#ifdef InverseColor\n bool inverse = false; //gl_FragColor.r < 0.3 || gl_FragColor.g < 0.3 || gl_FragColor.b < 0.3 ||\n \n //\u6DF1\u8272\u5B57\u548C\u767D\u8272\u5730\u9762\u8981\u53CD\u8F6C\n \n if(gl_FragColor.r > 0.9 && gl_FragColor.g > 0.9 && gl_FragColor.b > 0.9){\n //gl_FragColor.r = 1.0 - gl_FragColor.r;\n //gl_FragColor.g = 1.0 - gl_FragColor.g;\n //gl_FragColor.b = 1.0 - gl_FragColor.b; \n gl_FragColor = vec4(0.07,0.22, 0.5,1.0);\n }else{//\u6D77\u548C\u8349\u576A\u989C\u8272\u52A0\u6DF1\n \n gl_FragColor = texColor * vec4(0.3,0.5,0.7,1.0);\n } \n \n #endif*/\n \n #else\n gl_FragColor = color_;\n #endif\n \n \n}\n"; Shaders["skybox.vs"] = "uniform mat4 matrix; \nvarying vec3 vWorldPosition;\n\n\n\nvoid main() \n{ \n \n vWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz ; // - cameraPosition; \n vWorldPosition = (vec4(vWorldPosition, 1.0) * matrix).xyz;\n vWorldPosition.x *= -1.0;\n\n\n gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}"; Shaders["skybox.fs"] = "varying vec3 vWorldPosition;\nuniform sampler2D tDiffuse; \n\n#define PI 3.141592653 \n\n/*vec2 getSamplerCoord( vec3 direction ) \n{\n direction = normalize(direction);\n float tx=atan(direction.x,direction.z)/(PI*2.0)+0.5;\n float ty=acos(direction.y)/PI;\n\n return vec2(tx,ty);\n}*/\nvec2 getSamplerCoord( vec3 direction ) \n{\n direction = normalize(direction);\n float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;\n float ty=acos(direction.z)/PI;\n\n return vec2(tx,ty);\n} \nvoid main() \n{\n vec2 samplerCoord = getSamplerCoord(vWorldPosition);\n vec4 color = texture2D(tDiffuse, samplerCoord);\n //gl_FragColor = RGBEToLinear( color ); //\u5BF9\u4E8Ehdr\u5FC5\u987B\u52A0\u8FD9\u4E00\u53E5\uFF0C\u5426\u5219\u6548\u679C\u5F88\u5947\u602A\u3002copy\u81EAmeshBasicMaterial\u91CC\u7684mat_fragment\u7684mapTexelToLinear\u51FD\u6570\uFF0C\u53EA\u8981\u6709map\u5C31\u4F1A\u4F7F\u7528\u8BE5\u51FD\u6570\n gl_FragColor = color;\n //#include \n //////#include \n\n} \n"; var ClassificationScheme = { DEFAULT: { 0: { visible: true, name: 'never classified', color: [0.5, 0.5, 0.5, 1.0] }, 1: { visible: true, name: 'unclassified', color: [0.5, 0.5, 0.5, 1.0] }, 2: { visible: true, name: 'ground', color: [0.63, 0.32, 0.18, 1.0] }, 3: { visible: true, name: 'low vegetation', color: [0.0, 1.0, 0.0, 1.0] }, 4: { visible: true, name: 'medium vegetation', color: [0.0, 0.8, 0.0, 1.0] }, 5: { visible: true, name: 'high vegetation', color: [0.0, 0.6, 0.0, 1.0] }, 6: { visible: true, name: 'building', color: [1.0, 0.66, 0.0, 1.0] }, 7: { visible: true, name: 'low point(noise)', color: [1.0, 0.0, 1.0, 1.0] }, 8: { visible: true, name: 'key-point', color: [1.0, 0.0, 0.0, 1.0] }, 9: { visible: true, name: 'water', color: [0.0, 0.0, 1.0, 1.0] }, 12: { visible: true, name: 'overlap', color: [1.0, 1.0, 0.0, 1.0] }, DEFAULT: { visible: true, name: 'default', color: [0.3, 0.6, 0.6, 0.5] } }, user: { //add on 2025 1: { visible: true, name: 'concrete', color: '#CCCCCC' }, //混凝土 2: { visible: true, name: 'wood', color: '#47FF53' }, 3: { visible: true, name: 'metal', color: '#FFD633' }, 4: { visible: true, name: 'glass', color: '#1C6BFF' }, 5: { visible: true, name: 'plastic', color: '#47FFFF' }, 6: { visible: true, name: 'electric_wire', color: '#FF3939' } } }; Object.defineProperty(ClassificationScheme, 'RANDOM', { get: function get() { var scheme = {}; for (var i = 0; i <= 255; i++) { scheme[i] = new Vector4(Math.random(), Math.random(), Math.random()); } scheme["DEFAULT"] = new Vector4(Math.random(), Math.random(), Math.random()); return scheme; } }); class EnumItem { constructor(object) { for (var key of Object.keys(object)) { this[key] = object[key]; } } inspect() { return "Enum(".concat(this.name, ": ").concat(this.value, ")"); } } ; class Enum { constructor(object) { this.object = object; for (var key of Object.keys(object)) { var value = object[key]; if (typeof value === "object") { value.name = key; } else { value = { name: key, value: value }; } this[key] = new EnumItem(value); } } fromValue(value) { for (var key of Object.keys(this.object)) { if (this[key].value === value) { return this[key]; } } throw new Error("No enum for value: ".concat(value)); } } ; var CameraMode = { ORTHOGRAPHIC: 0, PERSPECTIVE: 1, VR: 2 }; var ClipTask = { NONE: 0, HIGHLIGHT: 1, SHOW_INSIDE: 2, SHOW_OUTSIDE: 3 }; var ClipMethod = { INSIDE_ANY: 0, INSIDE_ALL: 1 }; var ElevationGradientRepeat = { CLAMP: 0, REPEAT: 1, MIRRORED_REPEAT: 2 }; var MOUSE$1 = { LEFT: 0b0001, RIGHT: 0b0010, MIDDLE: 0b0100 }; var PointSizeType = { FIXED: 0, ATTENUATED: 1, ADAPTIVE: 2 }; var PointShape$1 = { SQUARE: 0, CIRCLE: 1, PARABOLOID: 2 }; var TreeType = { OCTREE: 0, KDTREE: 1 }; var LengthUnits = { METER: { code: 'm', unitspermeter: 1.0 }, FEET: { code: 'ft', unitspermeter: 3.28084 }, INCH: { code: '\u2033', unitspermeter: 39.3701 } }; // // how to calculate the radius of a projected sphere in screen space // http://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space // http://stackoverflow.com/questions/3717226/radius-of-projected-sphere // class PointCloudMaterial$1 extends RawShaderMaterial { //base constructor() { var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); this.visibleNodesTexture = Utils.generateDataTexture(2048, 1, new Color(0xffffff)); this.visibleNodesTexture.minFilter = NearestFilter; this.visibleNodesTexture.magFilter = NearestFilter; var getValid = (a, b) => { if (a !== undefined) { return a; } else { return b; } }; var pointSize = getValid(parameters.size, 1.0); var minSize = getValid(parameters.minSize, 2.0); var maxSize = getValid(parameters.maxSize, 50.0); var treeType = getValid(parameters.treeType, TreeType.OCTREE); this._pointSizeType = PointSizeType.FIXED; this._shape = PointShape$1.SQUARE; this._useClipBox = false; this.clipBoxes = []; this.clipPolygons = []; this._weighted = false; this._gradient = Gradients.SPECTRAL; this.gradientTexture = PointCloudMaterial$1.generateGradientTexture(this._gradient); //this._matcap = "matcap.jpg"; //this.matcapTexture = PointCloudMaterial.generateMatcapTexture(this._matcap); this.lights = false; this.fog = false; this._treeType = treeType; this._useEDL = false; this.defines = new Map(); this.ranges = new Map(); this._activeAttributeName = null; this._defaultIntensityRangeChanged = false; this._defaultElevationRangeChanged = false; { var [width, height] = [256, 1]; var data = new Uint8Array(width * 4); var texture = new DataTexture(data, width, height, RGBAFormat); texture.magFilter = NearestFilter; texture.needsUpdate = true; this.classificationTexture = texture; } this.attributes = { position: { type: 'fv', value: [] }, color: { type: 'fv', value: [] }, normal: { type: 'fv', value: [] }, intensity: { type: 'f', value: [] }, classification: { type: 'f', value: [] }, returnNumber: { type: 'f', value: [] }, numberOfReturns: { type: 'f', value: [] }, pointSourceID: { type: 'f', value: [] }, indices: { type: 'fv', value: [] } }; this.uniforms = { level: { type: "f", value: 0.0 }, vnStart: { type: "f", value: 0.0 }, spacing: { type: "f", value: 1.0 }, blendHardness: { type: "f", value: 2.0 }, blendDepthSupplement: { type: "f", value: 0.0 }, fov: { type: "f", value: 1.0 }, screenWidth: { type: "f", value: 1.0 }, screenHeight: { type: "f", value: 1.0 }, near: { type: "f", value: 0.1 }, far: { type: "f", value: 1.0 }, uColor: { type: "c", value: new Color(0xffffff) }, uOpacity: { type: "f", value: 1.0 }, size: { type: "f", value: pointSize }, minSize: { type: "f", value: minSize }, maxSize: { type: "f", value: maxSize }, octreeSize: { type: "f", value: 0 }, bbSize: { type: "fv", value: [0, 0, 0] }, elevationRange: { type: "2fv", value: [0, 0] }, clipBoxCount: { type: "f", value: 0 }, //clipSphereCount: { type: "f", value: 0 }, clipPolygonCount: { type: "i", value: 0 }, clipBoxes: { type: "Matrix4fv", value: [] }, //clipSpheres: { type: "Matrix4fv", value: [] }, clipPolygons: { type: "3fv", value: [] }, clipPolygonVCount: { type: "iv", value: [] }, clipPolygonVP: { type: "Matrix4fv", value: [] }, visibleNodes: { type: "t", value: this.visibleNodesTexture }, pcIndex: { type: "f", value: 0 }, gradient: { type: "t", value: this.gradientTexture }, classificationLUT: { type: "t", value: this.classificationTexture }, uHQDepthMap: { type: "t", value: null }, toModel: { type: "Matrix4f", value: [] }, diffuse: { type: "fv", value: [1, 1, 1] }, transition: { type: "f", value: 0.5 }, intensityRange: { type: "fv", value: [Infinity, -Infinity] }, intensity_gbc: { type: "fv", value: [1, 0, 0] }, uRGB_gbc: { type: "fv", value: [1, 0, 0] }, // intensityGamma: { type: "f", value: 1 }, // intensityContrast: { type: "f", value: 0 }, // intensityBrightness:{ type: "f", value: 0 }, // rgbGamma: { type: "f", value: 1 }, // rgbContrast: { type: "f", value: 0 }, // rgbBrightness: { type: "f", value: 0 }, wRGB: { type: "f", value: 1 }, wIntensity: { type: "f", value: 0 }, wElevation: { type: "f", value: 0 }, wClassification: { type: "f", value: 0 }, wReturnNumber: { type: "f", value: 0 }, wSourceID: { type: "f", value: 0 }, useOrthographicCamera: { type: "b", value: false }, elevationGradientRepat: { type: "i", value: ElevationGradientRepeat.CLAMP }, clipTask: { type: "i", value: 1 }, clipMethod: { type: "i", value: 1 }, uShadowColor: { type: "3fv", value: [0, 0, 0] }, uExtraScale: { type: "f", value: 1 }, uExtraOffset: { type: "f", value: 0 }, uExtraRange: { type: "2fv", value: [0, 1] }, uExtraGammaBrightContr: { type: "3fv", value: [1, 0, 0] }, uFilterReturnNumberRange: { type: "fv", value: [0, 7] }, uFilterNumberOfReturnsRange: { type: "fv", value: [0, 7] }, uFilterGPSTimeClipRange: { type: "fv", value: [0, 7] }, uFilterPointSourceIDClipRange: { type: "fv", value: [0, 65535] }, //matcapTextureUniform: { type: "t", value: this.matcapTexture }, backfaceCulling: { type: "b", value: false } }; this.classification = ClassificationScheme.DEFAULT; this.defaultAttributeValues.normal = [0, 0, 0]; this.defaultAttributeValues.classification = [0, 0, 0]; this.defaultAttributeValues.indices = [0, 0, 0, 0]; this.vertexShader = Shaders['pointcloud.vs']; this.fragmentShader = Shaders['pointcloud.fs']; this.vertexColors = VertexColors; this.updateShaderSource(); } setDefine(key, value) { if (value !== undefined && value !== null) { if (this.defines.get(key) !== value) { this.defines.set(key, value); this.updateShaderSource(); } } else { this.removeDefine(key); } } removeDefine(key) { this.defines.delete(key); } updateShaderSource() { var vs = Shaders['pointcloud.vs']; var fs = Shaders['pointcloud.fs']; var definesString = this.getDefines(); var vsVersionIndex = vs.indexOf("#version "); var fsVersionIndex = fs.indexOf("#version "); if (vsVersionIndex >= 0) { vs = vs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { vs = "".concat(definesString, "\n").concat(vs); } if (fsVersionIndex >= 0) { fs = fs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { fs = "".concat(definesString, "\n").concat(fs); } this.vertexShader = vs; this.fragmentShader = fs; if (this.opacity === 1.0) { this.blending = NoBlending; this.transparent = false; this.depthTest = true; this.depthWrite = true; this.depthFunc = LessEqualDepth; } else if (this.opacity < 1.0 && !this.useEDL) { this.blending = AdditiveBlending; this.transparent = true; this.depthTest = false; this.depthWrite = true; this.depthFunc = AlwaysDepth; } if (this.weighted) { this.blending = AdditiveBlending; this.transparent = true; this.depthTest = true; this.depthWrite = false; } this.needsUpdate = true; } getDefines() { var defines = []; if (this.pointSizeType === PointSizeType.FIXED) { defines.push('#define fixed_point_size'); } else if (this.pointSizeType === PointSizeType.ATTENUATED) { defines.push('#define attenuated_point_size'); } else if (this.pointSizeType === PointSizeType.ADAPTIVE) { defines.push('#define adaptive_point_size'); } if (this.shape === PointShape$1.SQUARE) { defines.push('#define square_point_shape'); } else if (this.shape === PointShape$1.CIRCLE) { defines.push('#define circle_point_shape'); } else if (this.shape === PointShape$1.PARABOLOID) { defines.push('#define paraboloid_point_shape'); } if (this._useEDL) { defines.push('#define use_edl'); } if (this.activeAttributeName) { var attributeName = this.activeAttributeName.replace(/[^a-zA-Z0-9]/g, '_'); defines.push("#define color_type_".concat(attributeName)); } if (this._treeType === TreeType.OCTREE) { defines.push('#define tree_type_octree'); } else if (this._treeType === TreeType.KDTREE) { defines.push('#define tree_type_kdtree'); } if (this.weighted) { defines.push('#define weighted_splats'); } for (var [key, value] of this.defines) { defines.push(value); } return defines.join("\n"); } setClipBoxes(clipBoxes) { if (!clipBoxes) { return; } var doUpdate = this.clipBoxes.length !== clipBoxes.length && (clipBoxes.length === 0 || this.clipBoxes.length === 0); this.uniforms.clipBoxCount.value = this.clipBoxes.length; this.clipBoxes = clipBoxes; if (doUpdate) { this.updateShaderSource(); } this.uniforms.clipBoxes.value = new Float32Array(this.clipBoxes.length * 16); for (var i = 0; i < this.clipBoxes.length; i++) { var box = clipBoxes[i]; this.uniforms.clipBoxes.value.set(box.inverse.elements, 16 * i); } for (var _i = 0; _i < this.uniforms.clipBoxes.value.length; _i++) { if (Number.isNaN(this.uniforms.clipBoxes.value[_i])) { this.uniforms.clipBoxes.value[_i] = Infinity; } } } setClipPolygons(clipPolygons, maxPolygonVertices) { if (!clipPolygons) { return; } this.clipPolygons = clipPolygons; var doUpdate = this.clipPolygons.length !== clipPolygons.length; if (doUpdate) { this.updateShaderSource(); } } get gradient() { return this._gradient; } set gradient(value) { //海拔贴图 if (this._gradient !== value) { this._gradient = value; this.gradientTexture = PointCloudMaterial$1.generateGradientTexture(this._gradient); this.uniforms.gradient.value = this.gradientTexture; } } /* get matcap(){ return this._matcap; } set matcap (value) { if (this._matcap !== value) { this._matcap = value; this.matcapTexture = Potree.PointCloudMaterial.generateMatcapTexture(this._matcap); this.uniforms.matcapTextureUniform.value = this.matcapTexture; } } */ get useOrthographicCamera() { return this.uniforms.useOrthographicCamera.value; } set useOrthographicCamera(value) { if (this.uniforms.useOrthographicCamera.value !== value) { this.uniforms.useOrthographicCamera.value = value; } } get backfaceCulling() { return this.uniforms.backfaceCulling.value; } set backfaceCulling(value) { if (this.uniforms.backfaceCulling.value !== value) { this.uniforms.backfaceCulling.value = value; this.dispatchEvent({ type: 'backface_changed', target: this }); } } recomputeClassification() { var classification = this.classification; var data = this.classificationTexture.image.data; if (!classification) return; var width = 256; var black = [1, 1, 1, 1]; //1111 var valuesChanged = false; for (var i = 0; i < width; i++) { var color = void 0; var visible = true; if (classification[i]) { color = classification[i].color; visible = classification[i].visible; } else if (classification[i % 32]) { color = classification[i % 32].color; visible = classification[i % 32].visible; } else if (classification.DEFAULT) { color = classification.DEFAULT.color; visible = classification.DEFAULT.visible; } else { color = black; } var r = parseInt(255 * color[0]); var g = parseInt(255 * color[1]); var b = parseInt(255 * color[2]); var a = visible ? parseInt(255 * color[3]) : 0; if (data[4 * i + 0] !== r) { data[4 * i + 0] = r; valuesChanged = true; } if (data[4 * i + 1] !== g) { data[4 * i + 1] = g; valuesChanged = true; } if (data[4 * i + 2] !== b) { data[4 * i + 2] = b; valuesChanged = true; } if (data[4 * i + 3] !== a) { data[4 * i + 3] = a; valuesChanged = true; } } if (valuesChanged) { this.classificationTexture.needsUpdate = true; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get spacing() { return this.uniforms.spacing.value; } set spacing(value) { if (this.uniforms.spacing.value !== value) { // 即 uOctreeSpacing 来自cloud.js里 this.uniforms.spacing.value = value; } } get useClipBox() { return this._useClipBox; } set useClipBox(value) { if (this._useClipBox !== value) { this._useClipBox = value; this.updateShaderSource(); } } get clipTask() { return this.uniforms.clipTask.value; } set clipTask(mode) { this.uniforms.clipTask.value = mode; } get elevationGradientRepat() { return this.uniforms.elevationGradientRepat.value; } set elevationGradientRepat(mode) { this.uniforms.elevationGradientRepat.value = mode; } get clipMethod() { return this.uniforms.clipMethod.value; } set clipMethod(mode) { this.uniforms.clipMethod.value = mode; } get weighted() { return this._weighted; } set weighted(value) { if (this._weighted !== value) { this._weighted = value; this.updateShaderSource(); } } get fov() { return this.uniforms.fov.value; } set fov(value) { if (this.uniforms.fov.value !== value) { this.uniforms.fov.value = value; // this.updateShaderSource(); } } get screenWidth() { return this.uniforms.screenWidth.value; } set screenWidth(value) { if (this.uniforms.screenWidth.value !== value) { this.uniforms.screenWidth.value = value; // this.updateShaderSource(); } } get screenHeight() { return this.uniforms.screenHeight.value; } set screenHeight(value) { if (this.uniforms.screenHeight.value !== value) { this.uniforms.screenHeight.value = value; // this.updateShaderSource(); } } get near() { return this.uniforms.near.value; } set near(value) { if (this.uniforms.near.value !== value) { this.uniforms.near.value = value; } } get far() { return this.uniforms.far.value; } set far(value) { if (this.uniforms.far.value !== value) { this.uniforms.far.value = value; } } get opacity() { return this.uniforms.uOpacity.value; } set opacity(value) { if (this.uniforms && this.uniforms.uOpacity) { if (this.uniforms.uOpacity.value !== value) { this.uniforms.uOpacity.value = value; this.updateShaderSource(); this.dispatchEvent({ type: 'opacity_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } } get activeAttributeName() { return this._activeAttributeName; } set activeAttributeName(value) { if (this._activeAttributeName !== value) { //console.error('_activeAttributeName!!!!!!!',value) this._activeAttributeName = value; this.updateShaderSource(); this.dispatchEvent({ type: 'active_attribute_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get pointSizeType() { return this._pointSizeType; } set pointSizeType(value) { if (this._pointSizeType !== value) { this._pointSizeType = value; this.updateShaderSource(); //这句表明这个属性频繁更改会卡顿 this.dispatchEvent({ type: 'point_size_type_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get useEDL() { return this._useEDL; } set useEDL(value) { if (this._useEDL !== value) { this._useEDL = value; this.updateShaderSource(); } } get color() { return this.uniforms.uColor.value; } set color(value) { if (!this.uniforms.uColor.value.equals(value)) { this.uniforms.uColor.value.copy(value); this.dispatchEvent({ type: 'color_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get shape() { return this._shape; } set shape(value) { if (this._shape !== value) { this._shape = value; this.updateShaderSource(); this.dispatchEvent({ type: 'point_shape_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get treeType() { return this._treeType; } set treeType(value) { if (this._treeType !== value) { this._treeType = value; this.updateShaderSource(); } } get bbSize() { return this.uniforms.bbSize.value; } set bbSize(value) { this.uniforms.bbSize.value = value; } get size() { return this.uniforms.size.value; } set size(value) { if (this.uniforms.size.value !== value) { this.uniforms.size.value = value; this.dispatchEvent({ type: 'point_size_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get minSize() { return this.uniforms.minSize.value; } set minSize(value) { if (this.uniforms.minSize.value !== value) { this.uniforms.minSize.value = value; this.dispatchEvent({ type: 'point_size_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get elevationRange() { return this.uniforms.elevationRange.value; } set elevationRange(value) { var changed = this.uniforms.elevationRange.value[0] !== value[0] || this.uniforms.elevationRange.value[1] !== value[1]; if (changed) { this.uniforms.elevationRange.value = value; this._defaultElevationRangeChanged = true; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get heightMin() { return this.uniforms.elevationRange.value[0]; } set heightMin(value) { this.elevationRange = [value, this.elevationRange[1]]; } get heightMax() { return this.uniforms.elevationRange.value[1]; } set heightMax(value) { this.elevationRange = [this.elevationRange[0], value]; } get transition() { return this.uniforms.transition.value; } set transition(value) { this.uniforms.transition.value = value; } get intensityRange() { return this.uniforms.intensityRange.value; } set intensityRange(value) { if (!(value instanceof Array && value.length === 2)) { return; } if (value[0] === this.uniforms.intensityRange.value[0] && value[1] === this.uniforms.intensityRange.value[1]) { return; } this.uniforms.intensityRange.value = value; this._defaultIntensityRangeChanged = true; this.dispatchEvent({ type: 'material_property_changed', target: this }); } get intensityGamma() { return this.uniforms.intensity_gbc.value[0]; } set intensityGamma(value) { if (this.uniforms.intensity_gbc.value[0] !== value) { this.uniforms.intensity_gbc.value[0] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get intensityContrast() { return this.uniforms.intensity_gbc.value[2]; } set intensityContrast(value) { if (this.uniforms.intensity_gbc.value[2] !== value) { this.uniforms.intensity_gbc.value[2] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get intensityBrightness() { return this.uniforms.intensity_gbc.value[1]; } set intensityBrightness(value) { if (this.uniforms.intensity_gbc.value[1] !== value) { this.uniforms.intensity_gbc.value[1] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get rgbGamma() { return this.uniforms.uRGB_gbc.value[0]; } set rgbGamma(value) { if (this.uniforms.uRGB_gbc.value[0] !== value) { this.uniforms.uRGB_gbc.value[0] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get rgbContrast() { return this.uniforms.uRGB_gbc.value[2]; } set rgbContrast(value) { if (this.uniforms.uRGB_gbc.value[2] !== value) { this.uniforms.uRGB_gbc.value[2] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get rgbBrightness() { return this.uniforms.uRGB_gbc.value[1]; } set rgbBrightness(value) { if (this.uniforms.uRGB_gbc.value[1] !== value) { this.uniforms.uRGB_gbc.value[1] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get extraGamma() { return this.uniforms.uExtraGammaBrightContr.value[0]; } set extraGamma(value) { if (this.uniforms.uExtraGammaBrightContr.value[0] !== value) { this.uniforms.uExtraGammaBrightContr.value[0] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get extraBrightness() { return this.uniforms.uExtraGammaBrightContr.value[1]; } set extraBrightness(value) { if (this.uniforms.uExtraGammaBrightContr.value[1] !== value) { this.uniforms.uExtraGammaBrightContr.value[1] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get extraContrast() { return this.uniforms.uExtraGammaBrightContr.value[2]; } set extraContrast(value) { if (this.uniforms.uExtraGammaBrightContr.value[2] !== value) { this.uniforms.uExtraGammaBrightContr.value[2] = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } getRange(attributeName) { return this.ranges.get(attributeName); } setRange(attributeName, newRange) { var rangeChanged = false; var oldRange = this.ranges.get(attributeName); if (oldRange != null && newRange != null) { rangeChanged = oldRange[0] !== newRange[0] || oldRange[1] !== newRange[1]; } else { rangeChanged = true; } this.ranges.set(attributeName, newRange); if (rangeChanged) { this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get extraRange() { return this.uniforms.uExtraRange.value; } set extraRange(value) { if (!(value instanceof Array && value.length === 2)) { return; } if (value[0] === this.uniforms.uExtraRange.value[0] && value[1] === this.uniforms.uExtraRange.value[1]) { return; } this.uniforms.uExtraRange.value = value; this._defaultExtraRangeChanged = true; this.dispatchEvent({ type: 'material_property_changed', target: this }); } get weightRGB() { return this.uniforms.wRGB.value; } set weightRGB(value) { if (this.uniforms.wRGB.value !== value) { this.uniforms.wRGB.value = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get weightIntensity() { return this.uniforms.wIntensity.value; } set weightIntensity(value) { if (this.uniforms.wIntensity.value !== value) { this.uniforms.wIntensity.value = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get weightElevation() { return this.uniforms.wElevation.value; } set weightElevation(value) { if (this.uniforms.wElevation.value !== value) { this.uniforms.wElevation.value = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get weightClassification() { return this.uniforms.wClassification.value; } set weightClassification(value) { if (this.uniforms.wClassification.value !== value) { this.uniforms.wClassification.value = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get weightReturnNumber() { return this.uniforms.wReturnNumber.value; } set weightReturnNumber(value) { if (this.uniforms.wReturnNumber.value !== value) { this.uniforms.wReturnNumber.value = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } get weightSourceID() { return this.uniforms.wSourceID.value; } set weightSourceID(value) { if (this.uniforms.wSourceID.value !== value) { this.uniforms.wSourceID.value = value; this.dispatchEvent({ type: 'material_property_changed', target: this }); } } static generateGradientTexture(gradient) { var size = 64; // create canvas var canvas = document.createElement('canvas'); canvas.width = size; canvas.height = size; // get context var context = canvas.getContext('2d'); // draw gradient context.rect(0, 0, size, size); var ctxGradient = context.createLinearGradient(0, 0, size, size); for (var i = 0; i < gradient.length; i++) { var step = gradient[i]; ctxGradient.addColorStop(step[0], '#' + step[1].getHexString()); } context.fillStyle = ctxGradient; context.fill(); //let texture = new THREE.Texture(canvas); var texture = new CanvasTexture(canvas); texture.needsUpdate = true; texture.minFilter = LinearFilter; texture.wrap = RepeatWrapping; texture.repeat = 2; // textureImage = texture.image; return texture; } static generateMatcapTexture(matcap) { var url = new URL(Potree.resourcePath + "/textures/matcap/" + matcap).href; var texture = new TextureLoader().load(url); texture.magFilter = texture.minFilter = LinearFilter; texture.needsUpdate = true; // PotreeConverter_1.6_2018_07_29_windows_x64\PotreeConverter.exe autzen_xyzrgbXYZ_ascii.xyz -f xyzrgbXYZ -a RGB NORMAL -o autzen_xyzrgbXYZ_ascii_a -p index --overwrite // Switch matcap texture on the fly : viewer.scene.pointclouds[0].material.matcap = 'matcap1.jpg'; // For non power of 2, use LinearFilter and dont generate mipmaps, For power of 2, use NearestFilter and generate mipmaps : matcap2.jpg 1 2 8 11 12 13 return texture; } disableEvents() { if (this._hiddenListeners === undefined) { this._hiddenListeners = this._listeners; this._listeners = {}; } } enableEvents() { this._listeners = this._hiddenListeners; this._hiddenListeners = undefined; } } // copyFrom(from){ // var a = 10; // for(let name of Object.keys(this.uniforms)){ // this.uniforms[name].value = from.uniforms[name].value; // } // } // copy(from){ // this.copyFrom(from); // } //见ExtendPointCloudOctree class PointCloudOctreeNode extends PointCloudTreeNode { constructor() { super(); //this.children = {}; this.children = []; this.sceneNode = null; this.octree = null; } getNumPoints() { return this.geometryNode.numPoints; } isLoaded() { return true; } isTreeNode() { return true; } isGeometryNode() { return false; } getLevel() { return this.geometryNode.level; } getBoundingSphere() { return this.geometryNode.boundingSphere; } getBoundingBox() { return this.geometryNode.boundingBox; } getChildren() { var children = []; for (var i = 0; i < 8; i++) { if (this.children[i]) { children.push(this.children[i]); } } return children; } getPointsInBox(boxNode) { if (!this.sceneNode) { return null; } var buffer = this.geometryNode.buffer; var posOffset = buffer.offset("position"); var stride = buffer.stride; var view = new DataView(buffer.data); var worldToBox = boxNode.matrixWorld.clone().invert(); var objectToBox = new Matrix4().multiplyMatrices(worldToBox, this.sceneNode.matrixWorld); var inBox = []; var pos = new Vector4(); for (var i = 0; i < buffer.numElements; i++) { var x = view.getFloat32(i * stride + posOffset + 0, true); var y = view.getFloat32(i * stride + posOffset + 4, true); var z = view.getFloat32(i * stride + posOffset + 8, true); pos.set(x, y, z, 1); pos.applyMatrix4(objectToBox); if (-0.5 < pos.x && pos.x < 0.5) { if (-0.5 < pos.y && pos.y < 0.5) { if (-0.5 < pos.z && pos.z < 0.5) { pos.set(x, y, z, 1).applyMatrix4(this.sceneNode.matrixWorld); inBox.push(new Vector3(pos.x, pos.y, pos.z)); } } } } return inBox; } get name() { return this.geometryNode.name; } } ; class PointCloudOctree extends PointCloudTree { //base constructor(geometry, material) { var _this2; super(); _this2 = this; this.pointBudget = Infinity; //一直是这个值 this.pcoGeometry = geometry; this.boundingBox = this.pcoGeometry.boundingBox; this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere()); this.material = material || new PointCloudMaterial$1(); this.visiblePointsTarget = 2 * 1000 * 1000; this.minimumNodePixelSize = 150; this.level = 0; this.position.copy(geometry.offset); this.updateMatrix(); { var priorityQueue = ["rgba", "rgb", "intensity", "classification"]; var selected = "rgba"; var _loop = function _loop(attributeName) { var attribute = _this2.pcoGeometry.pointAttributes.attributes.find(a => a.name === attributeName); if (!attribute) { return 0; // continue } var min = attribute.range[0].constructor.name === "Array" ? attribute.range[0] : [attribute.range[0]]; var max = attribute.range[1].constructor.name === "Array" ? attribute.range[1] : [attribute.range[1]]; var range_min = new Vector3(...min); var range_max = new Vector3(...max); var range = range_min.distanceTo(range_max); if (range === 0) { return 0; // continue } selected = attributeName; return 1; // break }, _ret; for (var attributeName of priorityQueue) { _ret = _loop(attributeName); if (_ret === 0) continue; if (_ret === 1) break; } this.material.activeAttributeName = selected; } this.showBoundingBox = false; this.boundingBoxNodes = []; this.loadQueue = []; this.visibleBounds = new Box3(); this.visibleNodes = []; this.visibleGeometry = []; this.generateDEM = false; this.profileRequests = []; this.name = ''; this._visible = true; { var box = [this.pcoGeometry.tightBoundingBox, this.getBoundingBoxWorld()].find(v => v !== undefined); this.updateMatrixWorld(true); box = Utils.computeTransformedBoundingBox(box, this.matrixWorld); var bMin = box.min.z; var bMax = box.max.z; this.material.heightMin = bMin; this.material.heightMax = bMax; } // TODO read projection from file instead this.projection = geometry.projection; this.fallbackProjection = geometry.fallbackProjection; this.root = this.pcoGeometry.root; } setName(name) { if (this.name !== name) { this.name = name; this.dispatchEvent({ type: 'name_changed', name: name, pointcloud: this }); } } getName() { return this.name; } getAttribute(name) { var attribute = this.pcoGeometry.pointAttributes.attributes.find(a => a.name === name); if (attribute) { return attribute; } else { return null; } } getAttributes() { return this.pcoGeometry.pointAttributes; } toTreeNode(geometryNode, parent) { var node = new PointCloudOctreeNode(); // if(geometryNode.name === "r40206"){ // console.log("creating node for r40206"); // } var sceneNode = new Points(geometryNode.geometry, this.material); sceneNode.name = geometryNode.name; sceneNode.position.copy(geometryNode.boundingBox.min); sceneNode.frustumCulled = false; sceneNode.onBeforeRender = (_this, scene, camera, geometry, material, group) => { if (material.program) { _this.getContext().useProgram(material.program.program); if (material.program.getUniforms().map.level) { var level = geometryNode.getLevel(); material.uniforms.level.value = level; material.program.getUniforms().map.level.setValue(_this.getContext(), level); } if (this.visibleNodeTextureOffsets && material.program.getUniforms().map.vnStart) { var vnStart = this.visibleNodeTextureOffsets.get(node); material.uniforms.vnStart.value = vnStart; material.program.getUniforms().map.vnStart.setValue(_this.getContext(), vnStart); } if (material.program.getUniforms().map.pcIndex) { var i = node.pcIndex ? node.pcIndex : this.visibleNodes.indexOf(node); material.uniforms.pcIndex.value = i; material.program.getUniforms().map.pcIndex.setValue(_this.getContext(), i); } } }; // { // DEBUG // let sg = new THREE.SphereGeometry(1, 16, 16); // let sm = new THREE.MeshNormalMaterial(); // let s = new THREE.Mesh(sg, sm); // s.scale.set(5, 5, 5); // s.position.copy(geometryNode.mean) // .add(this.position) // .add(geometryNode.boundingBox.min); // // viewer.scene.scene.add(s); // } node.geometryNode = geometryNode; node.sceneNode = sceneNode; node.pointcloud = this; node.children = []; //for (let key in geometryNode.children) { // node.children[key] = geometryNode.children[key]; //} for (var i = 0; i < 8; i++) { node.children[i] = geometryNode.children[i]; } if (!parent) { this.root = node; this.add(sceneNode); } else { var childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]); parent.sceneNode.add(sceneNode); parent.children[childIndex] = node; } var disposeListener = function disposeListener() { var childIndex = parseInt(geometryNode.name[geometryNode.name.length - 1]); parent.sceneNode.remove(node.sceneNode); parent.children[childIndex] = geometryNode; }; geometryNode.oneTimeDisposeHandlers.push(disposeListener); return node; } updateVisibleBounds() { var leafNodes = []; for (var i = 0; i < this.visibleNodes.length; i++) { var node = this.visibleNodes[i]; var isLeaf = true; for (var j = 0; j < node.children.length; j++) { var child = node.children[j]; if (child instanceof PointCloudOctreeNode) { isLeaf = isLeaf && !child.sceneNode.visible; } else if (child instanceof PointCloudOctreeGeometryNode) { isLeaf = true; } } if (isLeaf) { leafNodes.push(node); } } this.visibleBounds.min = new Vector3(Infinity, Infinity, Infinity); this.visibleBounds.max = new Vector3(-Infinity, -Infinity, -Infinity); for (var _i = 0; _i < leafNodes.length; _i++) { var _node = leafNodes[_i]; this.visibleBounds.expandByPoint(_node.getBoundingBox().min); this.visibleBounds.expandByPoint(_node.getBoundingBox().max); } } updateMaterial(material, visibleNodes, camera, renderer) { material.fov = camera.fov * (Math.PI / 180); material.screenWidth = renderer.domElement.clientWidth; material.screenHeight = renderer.domElement.clientHeight; material.spacing = this.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z); material.near = camera.near; material.far = camera.far; material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.getSize(new Vector3()).x; } computeVisibilityTextureData(nodes, camera) { if (Potree.measureTimings) performance.mark("computeVisibilityTextureData-start"); var data = new Uint8Array(nodes.length * 4); var visibleNodeTextureOffsets = new Map(); // copy array nodes = nodes.slice(); // sort by level and index, e.g. r, r0, r3, r4, r01, r07, r30, ... var sort = function sort(a, b) { var na = a.geometryNode.name; var nb = b.geometryNode.name; if (na.length !== nb.length) return na.length - nb.length; if (na < nb) return -1; if (na > nb) return 1; return 0; }; nodes.sort(sort); var worldDir = new Vector3(); var nodeMap = new Map(); var offsetsToChild = new Array(nodes.length).fill(Infinity); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; nodeMap.set(node.name, node); visibleNodeTextureOffsets.set(node, i); if (i > 0) { var index = parseInt(node.name.slice(-1)); var parentName = node.name.slice(0, -1); var parent = nodeMap.get(parentName); var parentOffset = visibleNodeTextureOffsets.get(parent); var parentOffsetToChild = i - parentOffset; offsetsToChild[parentOffset] = Math.min(offsetsToChild[parentOffset], parentOffsetToChild); data[parentOffset * 4 + 0] = data[parentOffset * 4 + 0] | 1 << index; data[parentOffset * 4 + 1] = offsetsToChild[parentOffset] >> 8; data[parentOffset * 4 + 2] = offsetsToChild[parentOffset] % 256; } var density = node.geometryNode.density; if (typeof density === "number") { var lodOffset = Math.log2(density) / 2 - 1.5; var offsetUint8 = (lodOffset + 10) * 10; data[i * 4 + 3] = offsetUint8; } else { data[i * 4 + 3] = 100; } } if (Potree.measureTimings) { performance.mark("computeVisibilityTextureData-end"); performance.measure("render.computeVisibilityTextureData", "computeVisibilityTextureData-start", "computeVisibilityTextureData-end"); } return { data: data, offsets: visibleNodeTextureOffsets }; } nodeIntersectsProfile(node, profile) { var bbWorld = node.boundingBox.clone().applyMatrix4(this.matrixWorld); var bsWorld = bbWorld.getBoundingSphere(new Sphere()); var intersects = false; for (var i = 0; i < profile.points.length - 1; i++) { var start = new Vector3(profile.points[i + 0].x, profile.points[i + 0].y, bsWorld.center.z); var end = new Vector3(profile.points[i + 1].x, profile.points[i + 1].y, bsWorld.center.z); var closest = new Line3(start, end).closestPointToPoint(bsWorld.center, true, new Vector3()); var distance = closest.distanceTo(bsWorld.center); intersects = intersects || distance < bsWorld.radius + profile.width; } //console.log(`${node.name}: ${intersects}`); return intersects; } deepestNodeAt(position) { var toObjectSpace = this.matrixWorld.clone().invert(); var objPos = position.clone().applyMatrix4(toObjectSpace); var current = this.root; while (true) { var containingChild = null; for (var child of current.children) { if (child !== undefined) { if (child.getBoundingBox().containsPoint(objPos)) { containingChild = child; } } } if (containingChild !== null && containingChild instanceof PointCloudOctreeNode) { current = containingChild; } else { break; } } var deepest = current; return deepest; } nodesOnRay(nodes, ray) { var nodesOnRay = []; var _ray = ray.clone(); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var sphere = node.getBoundingSphere().clone().applyMatrix4(this.matrixWorld); if (_ray.intersectsSphere(sphere)) { nodesOnRay.push(node); } } return nodesOnRay; } updateMatrixWorld(force) { if (this.matrixAutoUpdate === true) this.updateMatrix(); if (this.matrixWorldNeedsUpdate === true || force === true) { if (!this.parent) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } } hideDescendants(object) { var stack = []; for (var i = 0; i < object.children.length; i++) { var child = object.children[i]; if (child.visible) { stack.push(child); } } while (stack.length > 0) { var _object = stack.shift(); _object.visible = false; for (var _i2 = 0; _i2 < _object.children.length; _i2++) { var _child = _object.children[_i2]; if (_child.visible) { stack.push(_child); } } } } moveToOrigin() { this.position.set(0, 0, 0); this.updateMatrixWorld(true); var box = this.boundingBox; var transform = this.matrixWorld; var tBox = Utils.computeTransformedBoundingBox(box, transform); this.position.set(0, 0, 0).sub(tBox.getCenter(new Vector3())); } moveToGroundPlane() { this.updateMatrixWorld(true); var box = this.boundingBox; var transform = this.matrixWorld; var tBox = Utils.computeTransformedBoundingBox(box, transform); this.position.y += -tBox.min.y; } getBoundingBoxWorld() { this.updateMatrixWorld(true); var box = this.boundingBox; var transform = this.matrixWorld; var tBox = Utils.computeTransformedBoundingBox(box, transform); return tBox; } /** * returns points inside the profile points * * maxDepth: search points up to the given octree depth * * * The return value is an array with all segments of the profile path * let segment = { * start: THREE.Vector3, * end: THREE.Vector3, * points: {} * project: function() * }; * * The project() function inside each segment can be used to transform * that segments point coordinates to line up along the x-axis. * * */ getPointsInProfile(profile, maxDepth, callback) { if (callback) { var request = new Potree.ProfileRequest(this, profile, maxDepth, callback); this.profileRequests.push(request); return request; } var points = { segments: [], boundingBox: new Box3(), projectedBoundingBox: new Box2() }; // evaluate segments for (var i = 0; i < profile.points.length - 1; i++) { var start = profile.points[i]; var end = profile.points[i + 1]; var ps = this.getProfile(start, end, profile.width, maxDepth); var segment = { start: start, end: end, points: ps, project: null }; points.segments.push(segment); points.boundingBox.expandByPoint(ps.boundingBox.min); points.boundingBox.expandByPoint(ps.boundingBox.max); } // add projection functions to the segments var mileage = new Vector3(); for (var _i3 = 0; _i3 < points.segments.length; _i3++) { var _segment = points.segments[_i3]; var _start2 = _segment.start; var _end2 = _segment.end; var project = function (_start, _end, _mileage, _boundingBox) { var start = _start; var end = _end; var mileage = _mileage; var boundingBox = _boundingBox; var xAxis = new Vector3(1, 0, 0); var dir = new Vector3().subVectors(end, start); dir.y = 0; dir.normalize(); var alpha = Math.acos(xAxis.dot(dir)); if (dir.z > 0) { alpha = -alpha; } return function (position) { var toOrigin = new Matrix4().makeTranslation(-start.x, -boundingBox.min.y, -start.z); var alignWithX = new Matrix4().makeRotationY(-alpha); var applyMileage = new Matrix4().makeTranslation(mileage.x, 0, 0); var pos = position.clone(); pos.applyMatrix4(toOrigin); pos.applyMatrix4(alignWithX); pos.applyMatrix4(applyMileage); return pos; }; }(_start2, _end2, mileage.clone(), points.boundingBox.clone()); _segment.project = project; mileage.x += new Vector3(_start2.x, 0, _start2.z).distanceTo(new Vector3(_end2.x, 0, _end2.z)); mileage.y += _end2.y - _start2.y; } points.projectedBoundingBox.min.x = 0; points.projectedBoundingBox.min.y = points.boundingBox.min.y; points.projectedBoundingBox.max.x = mileage.x; points.projectedBoundingBox.max.y = points.boundingBox.max.y; return points; } /** * returns points inside the given profile bounds. * * start: * end: * width: * depth: search points up to the given octree depth * callback: if specified, points are loaded before searching * * */ getProfile(start, end, width, depth, callback) { var request = new Potree.ProfileRequest(start, end, width, depth, callback); this.profileRequests.push(request); } getVisibleExtent() { return this.visibleBounds.applyMatrix4(this.matrixWorld); } intersectsPoint(position) { var rootAvailable = this.pcoGeometry.root && this.pcoGeometry.root.geometry; if (!rootAvailable) { return false; } if (typeof this.signedDistanceField === "undefined") { var resolution = 32; var field = new Float32Array(resolution ** 3).fill(Infinity); var positions = this.pcoGeometry.root.geometry.attributes.position; var boundingBox = this.boundingBox; var n = positions.count; for (var i = 0; i < n; i = i + 3) { var x = positions.array[3 * i + 0]; var y = positions.array[3 * i + 1]; var z = positions.array[3 * i + 2]; var ix = parseInt(Math.min(resolution * (x / boundingBox.max.x), resolution - 1)); var iy = parseInt(Math.min(resolution * (y / boundingBox.max.y), resolution - 1)); var iz = parseInt(Math.min(resolution * (z / boundingBox.max.z), resolution - 1)); var index = ix + iy * resolution + iz * resolution * resolution; field[index] = 0; } var sdf = { resolution: resolution, field: field }; this.signedDistanceField = sdf; } { var _sdf = this.signedDistanceField; var _boundingBox2 = this.boundingBox; var toObjectSpace = this.matrixWorld.clone().invert(); var objPos = position.clone().applyMatrix4(toObjectSpace); var _resolution = _sdf.resolution; var _ix = parseInt(_resolution * (objPos.x / _boundingBox2.max.x)); var _iy = parseInt(_resolution * (objPos.y / _boundingBox2.max.y)); var _iz = parseInt(_resolution * (objPos.z / _boundingBox2.max.z)); if (_ix < 0 || _iy < 0 || _iz < 0) { return false; } if (_ix >= _resolution || _iy >= _resolution || _iz >= _resolution) { return false; } var _index = _ix + _iy * _resolution + _iz * _resolution * _resolution; var value = _sdf.field[_index]; if (value === 0) { return true; } } return false; } /** * * * * params.pickWindowSize: Look for points inside a pixel window of this size. * Use odd values: 1, 3, 5, ... * * * TODO: only draw pixels that are actually read with readPixels(). * */ pick(viewer, camera, ray) { var params = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var renderer = viewer.renderer; var pRenderer = viewer.pRenderer; performance.mark("pick-start"); var getVal = (a, b) => a !== undefined ? a : b; var pickWindowSize = getVal(params.pickWindowSize, 65); var pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false); var size = renderer.getSize(new Vector2()); var width = Math.ceil(getVal(params.width, size.width)); var height = Math.ceil(getVal(params.height, size.height)); var pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType); var pointSize = getVal(params.pointSize, this.material.size); var nodes = this.nodesOnRay(this.visibleNodes, ray); if (nodes.length === 0) { return null; } if (!this.pickState) { var scene = new Scene(); var material = new Potree.PointCloudMaterial(); material.activeAttributeName = "indices"; var renderTarget = new WebGLRenderTarget(1, 1, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat }); this.pickState = { renderTarget: renderTarget, material: material, scene: scene }; } ; var pickState = this.pickState; var pickMaterial = pickState.material; { // update pick material pickMaterial.pointSizeType = pointSizeType; //pickMaterial.shape = this.material.shape; pickMaterial.shape = Potree.PointShape.PARABOLOID; pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value; pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value; pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value; pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value; pickMaterial.activeAttributeName = "indices"; pickMaterial.size = pointSize; pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value; pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value; pickMaterial.classification = this.material.classification; pickMaterial.recomputeClassification(); if (params.pickClipped) { pickMaterial.clipBoxes = this.material.clipBoxes; pickMaterial.uniforms.clipBoxes = this.material.uniforms.clipBoxes; if (this.material.clipTask === Potree.ClipTask.HIGHLIGHT) { pickMaterial.clipTask = Potree.ClipTask.NONE; } else { pickMaterial.clipTask = this.material.clipTask; } pickMaterial.clipMethod = this.material.clipMethod; } else { pickMaterial.clipBoxes = []; } this.updateMaterial(pickMaterial, nodes, camera, renderer); } pickState.renderTarget.setSize(width, height); var pixelPos = new Vector2(params.x, params.y); var gl = renderer.getContext(); gl.enable(gl.SCISSOR_TEST); gl.scissor(parseInt(pixelPos.x - (pickWindowSize - 1) / 2), parseInt(pixelPos.y - (pickWindowSize - 1) / 2), parseInt(pickWindowSize), parseInt(pickWindowSize)); renderer.state.buffers.depth.setTest(pickMaterial.depthTest); renderer.state.buffers.depth.setMask(pickMaterial.depthWrite); renderer.state.setBlending(NoBlending); { // RENDER renderer.setRenderTarget(pickState.renderTarget); gl.clearColor(0, 0, 0, 0); renderer.clear(true, true, true); var tmp = this.material; this.material = pickMaterial; pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget); this.material = tmp; } var clamp = (number, min, max) => Math.min(Math.max(min, number), max); var x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width)); var y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height)); var w = parseInt(Math.min(x + pickWindowSize, width) - x); var h = parseInt(Math.min(y + pickWindowSize, height) - y); var pixelCount = w * h; var buffer = new Uint8Array(4 * pixelCount); gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer); renderer.setRenderTarget(null); renderer.state.reset(); renderer.setScissorTest(false); gl.disable(gl.SCISSOR_TEST); var pixels = buffer; var ibuffer = new Uint32Array(buffer.buffer); // find closest hit inside pixelWindow boundaries var min = Number.MAX_VALUE; var hits = []; for (var u = 0; u < pickWindowSize; u++) { for (var v = 0; v < pickWindowSize; v++) { var offset = u + v * pickWindowSize; var distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2); var pcIndex = pixels[4 * offset + 3]; pixels[4 * offset + 3] = 0; var pIndex = ibuffer[offset]; if (!(pcIndex === 0 && pIndex === 0) && pcIndex !== undefined && pIndex !== undefined) { var hit = { pIndex: pIndex, pcIndex: pcIndex, distanceToCenter: distance }; if (params.all) { hits.push(hit); } else { if (hits.length > 0) { if (distance < hits[0].distanceToCenter) { hits[0] = hit; } } else { hits.push(hit); } } } } } // { // DEBUG: show panel with pick image // let img = Utils.pixelsArrayToImage(buffer, w, h); // let screenshot = img.src; // if(!this.debugDIV){ // this.debugDIV = $(` //
`); // $(document.body).append(this.debugDIV); // } // this.debugDIV.empty(); // this.debugDIV.append($(``)); // //$(this.debugWindow.document).append($(``)); // //this.debugWindow.document.write(''); // } for (var _hit of hits) { var point = {}; if (!nodes[_hit.pcIndex]) { return null; } var node = nodes[_hit.pcIndex]; var pc = node.sceneNode; var geometry = node.geometryNode.geometry; var _loop2 = function _loop2() { var attribute = geometry.attributes[attributeName]; if (attributeName === 'position') { var _x = attribute.array[3 * _hit.pIndex + 0]; var _y = attribute.array[3 * _hit.pIndex + 1]; var z = attribute.array[3 * _hit.pIndex + 2]; var position = new Vector3(_x, _y, z); position.applyMatrix4(pc.matrixWorld); point[attributeName] = position; } else if (attributeName === 'indices') {} else { var values = attribute.array.slice(attribute.itemSize * _hit.pIndex, attribute.itemSize * (_hit.pIndex + 1)); if (attribute.potree) { var { scale, offset: _offset } = attribute.potree; values = values.map(v => v / scale + _offset); } point[attributeName] = values; //debugger; //if (values.itemSize === 1) { // point[attribute.name] = values.array[hit.pIndex]; //} else { // let value = []; // for (let j = 0; j < values.itemSize; j++) { // value.push(values.array[values.itemSize * hit.pIndex + j]); // } // point[attribute.name] = value; //} } }; for (var attributeName in geometry.attributes) { _loop2(); } _hit.point = point; } performance.mark("pick-end"); performance.measure("pick", "pick-start", "pick-end"); if (params.all) { return hits.map(hit => hit.point); } else { if (hits.length === 0) { return null; } else { return hits[0].point; //let sorted = hits.sort( (a, b) => a.distanceToCenter - b.distanceToCenter); //return sorted[0].point; } } } *getFittedBoxGen(boxNode) { var start = performance.now(); var shrinkedLocalBounds = new Box3(); var worldToBox = boxNode.matrixWorld.clone().invert(); for (var node of this.visibleNodes) { if (!node.sceneNode) { continue; } var buffer = node.geometryNode.buffer; var posOffset = buffer.offset("position"); var stride = buffer.stride; var view = new DataView(buffer.data); var objectToBox = new Matrix4().multiplyMatrices(worldToBox, node.sceneNode.matrixWorld); var pos = new Vector4(); for (var i = 0; i < buffer.numElements; i++) { var x = view.getFloat32(i * stride + posOffset + 0, true); var y = view.getFloat32(i * stride + posOffset + 4, true); var z = view.getFloat32(i * stride + posOffset + 8, true); pos.set(x, y, z, 1); pos.applyMatrix4(objectToBox); if (-0.5 < pos.x && pos.x < 0.5) { if (-0.5 < pos.y && pos.y < 0.5) { if (-0.5 < pos.z && pos.z < 0.5) { shrinkedLocalBounds.expandByPoint(pos); } } } } yield; } var fittedPosition = shrinkedLocalBounds.getCenter(new Vector3()).applyMatrix4(boxNode.matrixWorld); var fitted = new Object3D(); fitted.position.copy(fittedPosition); fitted.scale.copy(boxNode.scale); fitted.rotation.copy(boxNode.rotation); var ds = new Vector3().subVectors(shrinkedLocalBounds.max, shrinkedLocalBounds.min); fitted.scale.multiply(ds); var duration = performance.now() - start; console.log("duration: ", duration); yield fitted; } getFittedBox(boxNode) { var maxLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Infinity; maxLevel = Infinity; var start = performance.now(); var shrinkedLocalBounds = new Box3(); var worldToBox = boxNode.matrixWorld.clone().invert(); for (var node of this.visibleNodes) { if (!node.sceneNode || node.getLevel() > maxLevel) { continue; } var buffer = node.geometryNode.buffer; var posOffset = buffer.offset("position"); var stride = buffer.stride; var view = new DataView(buffer.data); var objectToBox = new Matrix4().multiplyMatrices(worldToBox, node.sceneNode.matrixWorld); var pos = new Vector4(); for (var i = 0; i < buffer.numElements; i++) { var x = view.getFloat32(i * stride + posOffset + 0, true); var y = view.getFloat32(i * stride + posOffset + 4, true); var z = view.getFloat32(i * stride + posOffset + 8, true); pos.set(x, y, z, 1); pos.applyMatrix4(objectToBox); if (-0.5 < pos.x && pos.x < 0.5) { if (-0.5 < pos.y && pos.y < 0.5) { if (-0.5 < pos.z && pos.z < 0.5) { shrinkedLocalBounds.expandByPoint(pos); } } } } } var fittedPosition = shrinkedLocalBounds.getCenter(new Vector3()).applyMatrix4(boxNode.matrixWorld); var fitted = new Object3D(); fitted.position.copy(fittedPosition); fitted.scale.copy(boxNode.scale); fitted.rotation.copy(boxNode.rotation); var ds = new Vector3().subVectors(shrinkedLocalBounds.max, shrinkedLocalBounds.min); fitted.scale.multiply(ds); var duration = performance.now() - start; console.log("duration: ", duration); return fitted; } get progress() { return this.visibleNodes.length / this.visibleGeometry.length; } find(name) { var node = null; for (var char of name) { if (char === "r") { node = this.root; } else { node = node.children[char]; } } return node; } get visible() { return this._visible; } set visible(value) { if (value !== this._visible) { this._visible = value; this.dispatchEvent({ type: 'visibility_changed', pointcloud: this }); } } } class PointCloudArena4DNode extends PointCloudTreeNode { constructor() { super(); this.left = null; this.right = null; this.sceneNode = null; this.kdtree = null; } getNumPoints() { return this.geometryNode.numPoints; } isLoaded() { return true; } isTreeNode() { return true; } isGeometryNode() { return false; } getLevel() { return this.geometryNode.level; } getBoundingSphere() { return this.geometryNode.boundingSphere; } getBoundingBox() { return this.geometryNode.boundingBox; } toTreeNode(child) { var geometryNode = null; if (this.left === child) { geometryNode = this.left; } else if (this.right === child) { geometryNode = this.right; } if (!geometryNode.loaded) { return; } var node = new PointCloudArena4DNode(); var sceneNode = PointCloud(geometryNode.geometry, this.kdtree.material); sceneNode.visible = false; node.kdtree = this.kdtree; node.geometryNode = geometryNode; node.sceneNode = sceneNode; node.parent = this; node.left = this.geometryNode.left; node.right = this.geometryNode.right; } getChildren() { var children = []; if (this.left) { children.push(this.left); } if (this.right) { children.push(this.right); } return children; } } ; class PointCloudArena4D$1 extends PointCloudTree { constructor(geometry) { super(); this.root = null; if (geometry.root) { this.root = geometry.root; } else { geometry.addEventListener('hierarchy_loaded', () => { this.root = geometry.root; }); } this.visiblePointsTarget = 2 * 1000 * 1000; this.minimumNodePixelSize = 150; this.position.sub(geometry.offset); this.updateMatrix(); this.numVisibleNodes = 0; this.numVisiblePoints = 0; this.boundingBoxNodes = []; this.loadQueue = []; this.visibleNodes = []; this.pcoGeometry = geometry; this.boundingBox = this.pcoGeometry.boundingBox; this.boundingSphere = this.pcoGeometry.boundingSphere; this.material = new PointCloudMaterial$1({ vertexColors: VertexColors, size: 0.05, treeType: TreeType.KDTREE }); this.material.sizeType = PointSizeType.ATTENUATED; this.material.size = 0.05; this.profileRequests = []; this.name = ''; } getBoundingBoxWorld() { this.updateMatrixWorld(true); var box = this.boundingBox; var transform = this.matrixWorld; var tBox = Utils.computeTransformedBoundingBox(box, transform); return tBox; } setName(name) { if (this.name !== name) { this.name = name; this.dispatchEvent({ type: 'name_changed', name: name, pointcloud: this }); } } getName() { return this.name; } getLevel() { return this.level; } toTreeNode(geometryNode, parent) { var node = new PointCloudArena4DNode(); var sceneNode = new Points(geometryNode.geometry, this.material); sceneNode.frustumCulled = false; sceneNode.onBeforeRender = (_this, scene, camera, geometry, material, group) => { if (material.program) { _this.getContext().useProgram(material.program.program); if (material.program.getUniforms().map.level) { var level = geometryNode.getLevel(); material.uniforms.level.value = level; material.program.getUniforms().map.level.setValue(_this.getContext(), level); } if (this.visibleNodeTextureOffsets && material.program.getUniforms().map.vnStart) { var vnStart = this.visibleNodeTextureOffsets.get(node); material.uniforms.vnStart.value = vnStart; material.program.getUniforms().map.vnStart.setValue(_this.getContext(), vnStart); } if (material.program.getUniforms().map.pcIndex) { var i = node.pcIndex ? node.pcIndex : this.visibleNodes.indexOf(node); material.uniforms.pcIndex.value = i; material.program.getUniforms().map.pcIndex.setValue(_this.getContext(), i); } } }; node.geometryNode = geometryNode; node.sceneNode = sceneNode; node.pointcloud = this; node.left = geometryNode.left; node.right = geometryNode.right; if (!parent) { this.root = node; this.add(sceneNode); } else { parent.sceneNode.add(sceneNode); if (parent.left === geometryNode) { parent.left = node; } else if (parent.right === geometryNode) { parent.right = node; } } var disposeListener = function disposeListener() { parent.sceneNode.remove(node.sceneNode); if (parent.left === node) { parent.left = geometryNode; } else if (parent.right === node) { parent.right = geometryNode; } }; geometryNode.oneTimeDisposeHandlers.push(disposeListener); return node; } updateMaterial(material, visibleNodes, camera, renderer) { material.fov = camera.fov * (Math.PI / 180); material.screenWidth = renderer.domElement.clientWidth; material.screenHeight = renderer.domElement.clientHeight; material.spacing = this.pcoGeometry.spacing; material.near = camera.near; material.far = camera.far; // reduce shader source updates by setting maxLevel slightly higher than actually necessary if (this.maxLevel > material.levels) { material.levels = this.maxLevel + 2; } // material.uniforms.octreeSize.value = this.boundingBox.size().x; var bbSize = this.boundingBox.getSize(new Vector3()); material.bbSize = [bbSize.x, bbSize.y, bbSize.z]; } updateVisibleBounds() {} hideDescendants(object) { var stack = []; for (var i = 0; i < object.children.length; i++) { var child = object.children[i]; if (child.visible) { stack.push(child); } } while (stack.length > 0) { var _child = stack.shift(); _child.visible = false; if (_child.boundingBoxNode) { _child.boundingBoxNode.visible = false; } for (var _i = 0; _i < _child.children.length; _i++) { var childOfChild = _child.children[_i]; if (childOfChild.visible) { stack.push(childOfChild); } } } } updateMatrixWorld(force) { // node.matrixWorld.multiplyMatrices( node.parent.matrixWorld, node.matrix ); if (this.matrixAutoUpdate === true) this.updateMatrix(); if (this.matrixWorldNeedsUpdate === true || force === true) { if (this.parent === undefined) { this.matrixWorld.copy(this.matrix); } else { this.matrixWorld.multiplyMatrices(this.parent.matrixWorld, this.matrix); } this.matrixWorldNeedsUpdate = false; force = true; } } nodesOnRay(nodes, ray) { var nodesOnRay = []; var _ray = ray.clone(); for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var sphere = node.getBoundingSphere().clone().applyMatrix4(node.sceneNode.matrixWorld); // TODO Unused: let box = node.getBoundingBox().clone().applyMatrix4(node.sceneNode.matrixWorld); if (_ray.intersectsSphere(sphere)) { nodesOnRay.push(node); } // if(_ray.isIntersectionBox(box)){ // nodesOnRay.push(node); // } } return nodesOnRay; } pick(viewer, camera, ray) { var params = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var renderer = viewer.renderer; var pRenderer = viewer.pRenderer; performance.mark("pick-start"); var getVal = (a, b) => a !== undefined ? a : b; var pickWindowSize = getVal(params.pickWindowSize, 17); var pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false); var size = renderer.getSize(new Vector2()); var width = Math.ceil(getVal(params.width, size.width)); var height = Math.ceil(getVal(params.height, size.height)); var pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType); var pointSize = getVal(params.pointSize, this.material.size); var nodes = this.nodesOnRay(this.visibleNodes, ray); if (nodes.length === 0) { return null; } if (!this.pickState) { var scene = new Scene(); var material = new PointCloudMaterial$1(); material.activeAttributeName = "indices"; var renderTarget = new WebGLRenderTarget(1, 1, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat }); this.pickState = { renderTarget: renderTarget, material: material, scene: scene }; } ; var pickState = this.pickState; var pickMaterial = pickState.material; { // update pick material pickMaterial.pointSizeType = pointSizeType; pickMaterial.shape = this.material.shape; pickMaterial.size = pointSize; pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value; pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value; pickMaterial.classification = this.material.classification; if (params.pickClipped) { pickMaterial.clipBoxes = this.material.clipBoxes; if (this.material.clipTask === ClipTask.HIGHLIGHT) { pickMaterial.clipTask = ClipTask.NONE; } else { pickMaterial.clipTask = this.material.clipTask; } } else { pickMaterial.clipBoxes = []; } this.updateMaterial(pickMaterial, nodes, camera, renderer); } pickState.renderTarget.setSize(width, height); var pixelPos = new Vector2(params.x, params.y); var gl = renderer.getContext(); gl.enable(gl.SCISSOR_TEST); gl.scissor(parseInt(pixelPos.x - (pickWindowSize - 1) / 2), parseInt(pixelPos.y - (pickWindowSize - 1) / 2), parseInt(pickWindowSize), parseInt(pickWindowSize)); renderer.state.buffers.depth.setTest(pickMaterial.depthTest); renderer.state.buffers.depth.setMask(pickMaterial.depthWrite); renderer.state.setBlending(NoBlending); renderer.clearTarget(pickState.renderTarget, true, true, true); { // RENDER renderer.setRenderTarget(pickState.renderTarget); gl.clearColor(0, 0, 0, 0); renderer.clearTarget(pickState.renderTarget, true, true, true); var tmp = this.material; this.material = pickMaterial; pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget); this.material = tmp; } var clamp = (number, min, max) => Math.min(Math.max(min, number), max); var x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width)); var y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height)); var w = parseInt(Math.min(x + pickWindowSize, width) - x); var h = parseInt(Math.min(y + pickWindowSize, height) - y); var pixelCount = w * h; var buffer = new Uint8Array(4 * pixelCount); gl.readPixels(x, y, pickWindowSize, pickWindowSize, gl.RGBA, gl.UNSIGNED_BYTE, buffer); renderer.setRenderTarget(null); renderer.state.reset(); renderer.setScissorTest(false); gl.disable(gl.SCISSOR_TEST); var pixels = buffer; var ibuffer = new Uint32Array(buffer.buffer); // find closest hit inside pixelWindow boundaries var min = Number.MAX_VALUE; var hits = []; for (var u = 0; u < pickWindowSize; u++) { for (var v = 0; v < pickWindowSize; v++) { var offset = u + v * pickWindowSize; var distance = Math.pow(u - (pickWindowSize - 1) / 2, 2) + Math.pow(v - (pickWindowSize - 1) / 2, 2); var pcIndex = pixels[4 * offset + 3]; pixels[4 * offset + 3] = 0; var pIndex = ibuffer[offset]; if (!(pcIndex === 0 && pIndex === 0) && pcIndex !== undefined && pIndex !== undefined) { var hit = { pIndex: pIndex, pcIndex: pcIndex, distanceToCenter: distance }; if (params.all) { hits.push(hit); } else { if (hits.length > 0) { if (distance < hits[0].distanceToCenter) { hits[0] = hit; } } else { hits.push(hit); } } } } } for (var _hit of hits) { var point = {}; if (!nodes[_hit.pcIndex]) { return null; } var node = nodes[_hit.pcIndex]; var pc = node.sceneNode; var geometry = node.geometryNode.geometry; for (var attributeName in geometry.attributes) { var attribute = geometry.attributes[attributeName]; if (attributeName === 'position') { var _x = attribute.array[3 * _hit.pIndex + 0]; var _y = attribute.array[3 * _hit.pIndex + 1]; var z = attribute.array[3 * _hit.pIndex + 2]; var position = new Vector3(_x, _y, z); position.applyMatrix4(pc.matrixWorld); point[attributeName] = position; } else if (attributeName === 'indices') {} else { //if (values.itemSize === 1) { // point[attribute.name] = values.array[hit.pIndex]; //} else { // let value = []; // for (let j = 0; j < values.itemSize; j++) { // value.push(values.array[values.itemSize * hit.pIndex + j]); // } // point[attribute.name] = value; //} } } _hit.point = point; } performance.mark("pick-end"); performance.measure("pick", "pick-start", "pick-end"); if (params.all) { return hits.map(hit => hit.point); } else { if (hits.length === 0) { return null; } else { return hits[0].point; } } } computeVisibilityTextureData(nodes) { if (exports.measureTimings) performance.mark("computeVisibilityTextureData-start"); var data = new Uint8Array(nodes.length * 3); var visibleNodeTextureOffsets = new Map(); // copy array nodes = nodes.slice(); // sort by level and number var sort = function sort(a, b) { var la = a.geometryNode.level; var lb = b.geometryNode.level; var na = a.geometryNode.number; var nb = b.geometryNode.number; if (la !== lb) return la - lb; if (na < nb) return -1; if (na > nb) return 1; return 0; }; nodes.sort(sort); var visibleNodeNames = []; for (var i = 0; i < nodes.length; i++) { visibleNodeNames.push(nodes[i].geometryNode.number); } for (var _i2 = 0; _i2 < nodes.length; _i2++) { var node = nodes[_i2]; visibleNodeTextureOffsets.set(node, _i2); var b1 = 0; // children var b2 = 0; // offset to first child var b3 = 0; // split if (node.geometryNode.left && visibleNodeNames.indexOf(node.geometryNode.left.number) > 0) { b1 += 1; b2 = visibleNodeNames.indexOf(node.geometryNode.left.number) - _i2; } if (node.geometryNode.right && visibleNodeNames.indexOf(node.geometryNode.right.number) > 0) { b1 += 2; b2 = b2 === 0 ? visibleNodeNames.indexOf(node.geometryNode.right.number) - _i2 : b2; } if (node.geometryNode.split === 'X') { b3 = 1; } else if (node.geometryNode.split === 'Y') { b3 = 2; } else if (node.geometryNode.split === 'Z') { b3 = 4; } data[_i2 * 3 + 0] = b1; data[_i2 * 3 + 1] = b2; data[_i2 * 3 + 2] = b3; } if (exports.measureTimings) { performance.mark("computeVisibilityTextureData-end"); performance.measure("render.computeVisibilityTextureData", "computeVisibilityTextureData-start", "computeVisibilityTextureData-end"); } return { data: data, offsets: visibleNodeTextureOffsets }; } get progress() { if (this.pcoGeometry.root) { return exports.numNodesLoading > 0 ? 0 : 1; } else { return 0; } } } ; // Copied from three.js: WebGLRenderer.js function paramThreeToGL(_gl, 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$1) 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$1) 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; } ; var attributeLocations = { "position": { name: "position", location: 0 }, "color": { name: "color", location: 1 }, "rgba": { name: "color", location: 1 }, "intensity": { name: "intensity", location: 2 }, "classification": { name: "classification", location: 3 }, "returnNumber": { name: "returnNumber", location: 4 }, "return number": { name: "returnNumber", location: 4 }, "returns": { name: "returnNumber", location: 4 }, "numberOfReturns": { name: "numberOfReturns", location: 5 }, "number of returns": { name: "numberOfReturns", location: 5 }, "pointSourceID": { name: "pointSourceID", location: 6 }, "source id": { name: "pointSourceID", location: 6 }, "point source id": { name: "pointSourceID", location: 6 }, "indices": { name: "indices", location: 7 }, "normal": { name: "normal", location: 8 }, "spacing": { name: "spacing", location: 9 }, "gps-time": { name: "gpsTime", location: 10 }, "aExtra": { name: "aExtra", location: 11 } }; class Shader { constructor(gl, name, vsSource, fsSource) { this.gl = gl; this.name = name; this.vsSource = vsSource; this.fsSource = fsSource; this.cache = new Map(); this.vs = null; this.fs = null; this.program = null; this.uniformLocations = {}; this.attributeLocations = {}; this.uniformBlockIndices = {}; this.uniformBlocks = {}; this.uniforms = {}; this.update(vsSource, fsSource); } update(vsSource, fsSource) { this.vsSource = vsSource; this.fsSource = fsSource; this.linkProgram(); } compileShader(shader, source) { var gl = this.gl; gl.shaderSource(shader, source); gl.compileShader(shader); var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!success) { var info = gl.getShaderInfoLog(shader); var numberedSource = source.split("\n").map((a, i) => "".concat(i + 1).padEnd(5) + a).join("\n"); throw "could not compile shader ".concat(this.name, ": ").concat(info, ", \n").concat(numberedSource); } } linkProgram() { var tStart = performance.now(); var gl = this.gl; this.uniformLocations = {}; this.attributeLocations = {}; this.uniforms = {}; gl.useProgram(null); var cached = this.cache.get("".concat(this.vsSource, ", ").concat(this.fsSource)); if (cached) { this.program = cached.program; this.vs = cached.vs; this.fs = cached.fs; this.attributeLocations = cached.attributeLocations; this.uniformLocations = cached.uniformLocations; this.uniformBlocks = cached.uniformBlocks; this.uniforms = cached.uniforms; return; } else { this.vs = gl.createShader(gl.VERTEX_SHADER); this.fs = gl.createShader(gl.FRAGMENT_SHADER); this.program = gl.createProgram(); for (var name of Object.keys(attributeLocations)) { var location = attributeLocations[name].location; var glslName = attributeLocations[name].name; gl.bindAttribLocation(this.program, location, glslName); } this.compileShader(this.vs, this.vsSource); this.compileShader(this.fs, this.fsSource); var program = this.program; gl.attachShader(program, this.vs); gl.attachShader(program, this.fs); gl.linkProgram(program); gl.detachShader(program, this.vs); gl.detachShader(program, this.fs); var success = gl.getProgramParameter(program, gl.LINK_STATUS); if (!success) { var info = gl.getProgramInfoLog(program); throw "could not link program ".concat(this.name, ": ").concat(info); } { // attribute locations var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (var i = 0; i < numAttributes; i++) { var attribute = gl.getActiveAttrib(program, i); var _location = gl.getAttribLocation(program, attribute.name); this.attributeLocations[attribute.name] = _location; } } { // uniform locations var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (var _i = 0; _i < numUniforms; _i++) { var uniform = gl.getActiveUniform(program, _i); var _location2 = gl.getUniformLocation(program, uniform.name); this.uniformLocations[uniform.name] = _location2; this.uniforms[uniform.name] = { location: _location2, value: null }; } } // uniform blocks if (typeof WebGL2RenderingContext != 'undefined' && gl instanceof WebGL2RenderingContext) { var numBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS); for (var _i2 = 0; _i2 < numBlocks; _i2++) { var blockName = gl.getActiveUniformBlockName(program, _i2); var blockIndex = gl.getUniformBlockIndex(program, blockName); this.uniformBlockIndices[blockName] = blockIndex; gl.uniformBlockBinding(program, blockIndex, blockIndex); var dataSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); var uBuffer = gl.createBuffer(); gl.bindBuffer(gl.UNIFORM_BUFFER, uBuffer); gl.bufferData(gl.UNIFORM_BUFFER, dataSize, gl.DYNAMIC_READ); gl.bindBufferBase(gl.UNIFORM_BUFFER, blockIndex, uBuffer); gl.bindBuffer(gl.UNIFORM_BUFFER, null); this.uniformBlocks[blockName] = { name: blockName, index: blockIndex, dataSize: dataSize, buffer: uBuffer }; } } var _cached = { program: this.program, vs: this.vs, fs: this.fs, attributeLocations: this.attributeLocations, uniformLocations: this.uniformLocations, uniforms: this.uniforms, uniformBlocks: this.uniformBlocks }; this.cache.set("".concat(this.vsSource, ", ").concat(this.fsSource), _cached); } var tEnd = performance.now(); var duration = tEnd - tStart; console.log("shader compile duration: ".concat(duration.toFixed(3))); } setUniformMatrix4(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } var tmp = new Float32Array(value.elements); gl.uniformMatrix4fv(location, false, tmp); } setUniform1f(name, value) { var gl = this.gl; var uniform = this.uniforms[name]; if (uniform === undefined) { return; } if (uniform.value === value) { return; } uniform.value = value; gl.uniform1f(uniform.location, value); } setUniformBoolean(name, value) { var gl = this.gl; var uniform = this.uniforms[name]; if (uniform === undefined) { return; } if (uniform.value === value) { return; } uniform.value = value; gl.uniform1i(uniform.location, value); } setUniformTexture(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform1i(location, value); } setUniform2f(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform2f(location, value[0], value[1]); } setUniform3f(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform3f(location, value[0], value[1], value[2]); } setUniform(name, value) { if (value.constructor === Matrix4) { this.setUniformMatrix4(name, value); } else if (typeof value === "number") { this.setUniform1f(name, value); } else if (typeof value === "boolean") { this.setUniformBoolean(name, value); } else if (value instanceof WebGLTexture) { this.setUniformTexture(name, value); } else if (value instanceof Array) { if (value.length === 2) { this.setUniform2f(name, value); } else if (value.length === 3) { this.setUniform3f(name, value); } } else { console.error("unhandled uniform type: ", name, value); } } setUniform1i(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform1i(location, value); } } ; class WebGLTexture { constructor(gl, texture) { this.gl = gl; this.texture = texture; this.id = gl.createTexture(); this.target = gl.TEXTURE_2D; this.version = -1; this.update(texture); } update() { if (!this.texture.image) { this.version = this.texture.version; return; } var gl = this.gl; var texture = this.texture; if (this.version === texture.version) { return; } this.target = gl.TEXTURE_2D; gl.bindTexture(this.target, this.id); var level = 0; var internalFormat = paramThreeToGL(gl, texture.format); var width = texture.image.width; var height = texture.image.height; var border = 0; var srcFormat = internalFormat; var srcType = paramThreeToGL(gl, texture.type); var data; 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); if (texture instanceof DataTexture) { data = texture.image.data; gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter)); gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter)); gl.texImage2D(this.target, level, internalFormat, width, height, border, srcFormat, srcType, data); } else if (texture instanceof CanvasTexture || texture instanceof Texture) { data = texture.image; gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, paramThreeToGL(gl, texture.wrapS)); gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, paramThreeToGL(gl, texture.wrapT)); gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL(gl, texture.magFilter)); gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL(gl, texture.minFilter)); gl.texImage2D(this.target, level, internalFormat, internalFormat, srcType, data); if (texture instanceof Texture) { gl.generateMipmap(gl.TEXTURE_2D); } } gl.bindTexture(this.target, null); this.version = texture.version; } } ; class WebGLBuffer { constructor() { this.numElements = 0; this.vao = null; this.vbos = new Map(); } } ; class Renderer { constructor(threeRenderer) { this.threeRenderer = threeRenderer; this.gl = this.threeRenderer.getContext(); this.buffers = new Map(); this.shaders = new Map(); this.textures = new Map(); this.glTypeMapping = new Map(); this.glTypeMapping.set(Float32Array, this.gl.FLOAT); this.glTypeMapping.set(Uint8Array, this.gl.UNSIGNED_BYTE); this.glTypeMapping.set(Uint16Array, this.gl.UNSIGNED_SHORT); this.toggle = 0; } deleteBuffer(geometry) { var gl = this.gl; var webglBuffer = this.buffers.get(geometry); if (webglBuffer != null) { for (var attributeName in geometry.attributes) { gl.deleteBuffer(webglBuffer.vbos.get(attributeName).handle); } this.buffers.delete(geometry); } } createBuffer(geometry) { var gl = this.gl; var webglBuffer = new WebGLBuffer(); webglBuffer.vao = gl.createVertexArray(); webglBuffer.numElements = geometry.attributes.position.count; gl.bindVertexArray(webglBuffer.vao); for (var attributeName in geometry.attributes) { var bufferAttribute = geometry.attributes[attributeName]; var vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW); var normalized = bufferAttribute.normalized; var type = this.glTypeMapping.get(bufferAttribute.array.constructor); if (attributeLocations[attributeName] === undefined) { //attributeLocation = attributeLocations["aExtra"]; } else { var attributeLocation = attributeLocations[attributeName].location; gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } webglBuffer.vbos.set(attributeName, { handle: vbo, name: attributeName, count: bufferAttribute.count, itemSize: bufferAttribute.itemSize, type: geometry.attributes.position.array.constructor, version: 0 }); } gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); var disposeHandler = event => { this.deleteBuffer(geometry); geometry.removeEventListener("dispose", disposeHandler); }; geometry.addEventListener("dispose", disposeHandler); return webglBuffer; } updateBuffer(geometry) { var gl = this.gl; var webglBuffer = this.buffers.get(geometry); gl.bindVertexArray(webglBuffer.vao); for (var attributeName in geometry.attributes) { var bufferAttribute = geometry.attributes[attributeName]; var normalized = bufferAttribute.normalized; var type = this.glTypeMapping.get(bufferAttribute.array.constructor); var vbo = null; if (!webglBuffer.vbos.has(attributeName)) { vbo = gl.createBuffer(); webglBuffer.vbos.set(attributeName, { handle: vbo, name: attributeName, count: bufferAttribute.count, itemSize: bufferAttribute.itemSize, type: geometry.attributes.position.array.constructor, version: bufferAttribute.version }); } else { vbo = webglBuffer.vbos.get(attributeName).handle; webglBuffer.vbos.get(attributeName).version = bufferAttribute.version; } gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW); if (attributeLocations[attributeName] === undefined) { //attributeLocation = attributeLocations["aExtra"]; } else { var attributeLocation = attributeLocations[attributeName].location; gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } } gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); } traverse(scene) { var octrees = []; var stack = [scene]; while (stack.length > 0) { var node = stack.pop(); if (node instanceof PointCloudTree) { octrees.push(node); continue; } var visibleChildren = node.children.filter(c => c.visible); stack.push(...visibleChildren); } var result = { octrees: octrees }; return result; } renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params) { var _this = this; if (exports.measureTimings) performance.mark("renderNodes-start"); var gl = this.gl; var material = params.material ? params.material : octree.material; var shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps; var view = camera.matrixWorldInverse; if (params.viewOverride) { view = params.viewOverride; } var worldView = new Matrix4(); var mat4holder = new Float32Array(16); var i = 0; var _loop = function _loop() { if (exports.debug.allowedNodes !== undefined) { if (!exports.debug.allowedNodes.includes(node.name)) { return 1; // continue } } var world = node.sceneNode.matrixWorld; worldView.multiplyMatrices(view, world); if (visibilityTextureData) { var vnStart = visibilityTextureData.offsets.get(node); shader.setUniform1f("uVNStart", vnStart); } var level = node.getLevel(); if (node.debug) { shader.setUniform("uDebug", true); } else { shader.setUniform("uDebug", false); } // let isLeaf = false; // if(node instanceof PointCloudOctreeNode){ // isLeaf = Object.keys(node.children).length === 0; // }else if(node instanceof PointCloudArena4DNode){ // isLeaf = node.geometryNode.isLeaf; // } // shader.setUniform("uIsLeafNode", isLeaf); // let isLeaf = node.children.filter(n => n != null).length === 0; // if(!isLeaf){ // continue; // } // TODO consider passing matrices in an array to avoid uniformMatrix4fv overhead var lModel = shader.uniformLocations["modelMatrix"]; if (lModel) { mat4holder.set(world.elements); gl.uniformMatrix4fv(lModel, false, mat4holder); } var lModelView = shader.uniformLocations["modelViewMatrix"]; //mat4holder.set(worldView.elements); // faster then set in chrome 63 for (var j = 0; j < 16; j++) { mat4holder[j] = worldView.elements[j]; } gl.uniformMatrix4fv(lModelView, false, mat4holder); { // Clip Polygons if (material.clipPolygons && material.clipPolygons.length > 0) { var clipPolygonVCount = []; var worldViewProjMatrices = []; for (var clipPolygon of material.clipPolygons) { var _view = clipPolygon.viewMatrix; var proj = clipPolygon.projMatrix; var worldViewProj = proj.clone().multiply(_view).multiply(world); clipPolygonVCount.push(clipPolygon.markers.length); worldViewProjMatrices.push(worldViewProj); } var flattenedMatrices = [].concat(...worldViewProjMatrices.map(m => m.elements)); var flattenedVertices = new Array(8 * 3 * material.clipPolygons.length); for (var _i3 = 0; _i3 < material.clipPolygons.length; _i3++) { var _clipPolygon = material.clipPolygons[_i3]; for (var _j = 0; _j < _clipPolygon.markers.length; _j++) { flattenedVertices[_i3 * 24 + (_j * 3 + 0)] = _clipPolygon.markers[_j].position.x; flattenedVertices[_i3 * 24 + (_j * 3 + 1)] = _clipPolygon.markers[_j].position.y; flattenedVertices[_i3 * 24 + (_j * 3 + 2)] = _clipPolygon.markers[_j].position.z; } } var lClipPolygonVCount = shader.uniformLocations["uClipPolygonVCount[0]"]; gl.uniform1iv(lClipPolygonVCount, clipPolygonVCount); var lClipPolygonVP = shader.uniformLocations["uClipPolygonWVP[0]"]; gl.uniformMatrix4fv(lClipPolygonVP, false, flattenedMatrices); var lClipPolygons = shader.uniformLocations["uClipPolygonVertices[0]"]; gl.uniform3fv(lClipPolygons, flattenedVertices); } } //shader.setUniformMatrix4("modelMatrix", world); //shader.setUniformMatrix4("modelViewMatrix", worldView); shader.setUniform1f("uLevel", level); shader.setUniform1f("uNodeSpacing", node.geometryNode.estimatedSpacing); shader.setUniform1f("uPCIndex", i); // uBBSize if (shadowMaps.length > 0) { var lShadowMap = shader.uniformLocations["uShadowMap[0]"]; shader.setUniform3f("uShadowColor", material.uniforms.uShadowColor.value); var bindingStart = 5; var bindingPoints = new Array(shadowMaps.length).fill(bindingStart).map((a, i) => a + i); gl.uniform1iv(lShadowMap, bindingPoints); for (var _i4 = 0; _i4 < shadowMaps.length; _i4++) { var shadowMap = shadowMaps[_i4]; var bindingPoint = bindingPoints[_i4]; var glTexture = _this.threeRenderer.properties.get(shadowMap.target.texture).__webglTexture; gl.activeTexture(gl["TEXTURE".concat(bindingPoint)]); gl.bindTexture(gl.TEXTURE_2D, glTexture); } { var worldViewMatrices = shadowMaps.map(sm => sm.camera.matrixWorldInverse).map(view => new Matrix4().multiplyMatrices(view, world)); var _flattenedMatrices = [].concat(...worldViewMatrices.map(c => c.elements)); var lWorldView = shader.uniformLocations["uShadowWorldView[0]"]; gl.uniformMatrix4fv(lWorldView, false, _flattenedMatrices); } { var _flattenedMatrices2 = [].concat(...shadowMaps.map(sm => sm.camera.projectionMatrix.elements)); var lProj = shader.uniformLocations["uShadowProj[0]"]; gl.uniformMatrix4fv(lProj, false, _flattenedMatrices2); } } var geometry = node.geometryNode.geometry; if (geometry.attributes["gps-time"]) { var bufferAttribute = geometry.attributes["gps-time"]; var attGPS = octree.getAttribute("gps-time"); var initialRange = attGPS.initialRange; var initialRangeSize = initialRange[1] - initialRange[0]; var globalRange = attGPS.range; var globalRangeSize = globalRange[1] - globalRange[0]; var scale = initialRangeSize / globalRangeSize; var offset = -(globalRange[0] - initialRange[0]) / initialRangeSize; scale = Number.isNaN(scale) ? 1 : scale; offset = Number.isNaN(offset) ? 0 : offset; shader.setUniform1f("uGpsScale", scale); shader.setUniform1f("uGpsOffset", offset); //shader.setUniform2f("uFilterGPSTimeClipRange", [-Infinity, Infinity]); var uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value; // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0] // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1] // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]); var normalizedClipRange = [(uFilterGPSTimeClipRange[0] - globalRange[0]) / globalRangeSize, (uFilterGPSTimeClipRange[1] - globalRange[0]) / globalRangeSize]; shader.setUniform2f("uFilterGPSTimeClipRange", normalizedClipRange); // // ranges in full gps coordinate system // const globalRange = attGPS.range; // const bufferRange = bufferAttribute.potree.range; // // ranges in [0, 1] // // normalizedGlobalRange = [0, 1] // // normalizedBufferRange: norm buffer within norm global range e.g. [0.2, 0.8] // const globalWidth = globalRange[1] - globalRange[0]; // const normalizedBufferRange = [ // (bufferRange[0] - globalRange[0]) / globalWidth, // (bufferRange[1] - globalRange[0]) / globalWidth, // ]; // shader.setUniform2f("uNormalizedGpsBufferRange", normalizedBufferRange); // let uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value; // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0] // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1] // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]); // shader.setUniform1f("uGpsScale", bufferAttribute.potree.scale); // shader.setUniform1f("uGpsOffset", bufferAttribute.potree.offset); } { var uFilterReturnNumberRange = material.uniforms.uFilterReturnNumberRange.value; var uFilterNumberOfReturnsRange = material.uniforms.uFilterNumberOfReturnsRange.value; var uFilterPointSourceIDClipRange = material.uniforms.uFilterPointSourceIDClipRange.value; shader.setUniform2f("uFilterReturnNumberRange", uFilterReturnNumberRange); shader.setUniform2f("uFilterNumberOfReturnsRange", uFilterNumberOfReturnsRange); shader.setUniform2f("uFilterPointSourceIDClipRange", uFilterPointSourceIDClipRange); } var webglBuffer = null; if (!_this.buffers.has(geometry)) { webglBuffer = _this.createBuffer(geometry); _this.buffers.set(geometry, webglBuffer); } else { webglBuffer = _this.buffers.get(geometry); for (var attributeName in geometry.attributes) { var attribute = geometry.attributes[attributeName]; if (attribute.version > webglBuffer.vbos.get(attributeName).version) { _this.updateBuffer(geometry); } } } gl.bindVertexArray(webglBuffer.vao); var isExtraAttribute = attributeLocations[material.activeAttributeName] === undefined && Object.keys(geometry.attributes).includes(material.activeAttributeName); if (isExtraAttribute) { var attributeLocation = attributeLocations["aExtra"].location; for (var _attributeName in geometry.attributes) { var _bufferAttribute = geometry.attributes[_attributeName]; var _vbo = webglBuffer.vbos.get(_attributeName); gl.bindBuffer(gl.ARRAY_BUFFER, _vbo.handle); gl.disableVertexAttribArray(attributeLocation); } var attName = material.activeAttributeName; var _bufferAttribute2 = geometry.attributes[attName]; var vbo = webglBuffer.vbos.get(attName); if (_bufferAttribute2 !== undefined && vbo !== undefined) { var type = _this.glTypeMapping.get(_bufferAttribute2.array.constructor); var normalized = _bufferAttribute2.normalized; gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle); gl.vertexAttribPointer(attributeLocation, _bufferAttribute2.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } { var attExtra = octree.pcoGeometry.pointAttributes.attributes.find(a => a.name === attName); var range = material.getRange(attName); if (!range) { range = attExtra.range; } if (!range) { range = [0, 1]; } var _initialRange = attExtra.initialRange; var _initialRangeSize = _initialRange[1] - _initialRange[0]; var _globalRange = range; var _globalRangeSize = _globalRange[1] - _globalRange[0]; var _scale = _initialRangeSize / _globalRangeSize; var _offset = -(_globalRange[0] - _initialRange[0]) / _initialRangeSize; _scale = Number.isNaN(_scale) ? 1 : _scale; _offset = Number.isNaN(_offset) ? 0 : _offset; shader.setUniform1f("uExtraScale", _scale); shader.setUniform1f("uExtraOffset", _offset); } } else { for (var _attributeName2 in geometry.attributes) { var _bufferAttribute3 = geometry.attributes[_attributeName2]; var _vbo2 = webglBuffer.vbos.get(_attributeName2); if (attributeLocations[_attributeName2] !== undefined) { var _attributeLocation = attributeLocations[_attributeName2].location; var _type = _this.glTypeMapping.get(_bufferAttribute3.array.constructor); var _normalized = _bufferAttribute3.normalized; gl.bindBuffer(gl.ARRAY_BUFFER, _vbo2.handle); gl.vertexAttribPointer(_attributeLocation, _bufferAttribute3.itemSize, _type, _normalized, 0, 0); gl.enableVertexAttribArray(_attributeLocation); } } } var numPoints = webglBuffer.numElements; gl.drawArrays(gl.POINTS, 0, numPoints); i++; }; for (var node of nodes) { if (_loop()) continue; } gl.bindVertexArray(null); if (exports.measureTimings) { performance.mark("renderNodes-end"); performance.measure("render.renderNodes", "renderNodes-start", "renderNodes-end"); } } renderOctree(octree, nodes, camera, target) { var params = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; var gl = this.gl; var material = params.material ? params.material : octree.material; var shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps; var view = camera.matrixWorldInverse; var viewInv = camera.matrixWorld; if (params.viewOverride) { view = params.viewOverride; viewInv = view.clone().invert(); } var proj = camera.projectionMatrix; var projInv = proj.clone().invert(); //let worldView = new THREE.Matrix4(); var shader = null; var visibilityTextureData = null; var currentTextureBindingPoint = 0; if (material.pointSizeType >= 0) { if (material.pointSizeType === PointSizeType.ADAPTIVE || material.activeAttributeName === "level of detail") { var vnNodes = params.vnTextureNodes != null ? params.vnTextureNodes : nodes; visibilityTextureData = octree.computeVisibilityTextureData(vnNodes, camera); var vnt = material.visibleNodesTexture; var data = vnt.image.data; data.set(visibilityTextureData.data); vnt.needsUpdate = true; } } { // UPDATE SHADER AND TEXTURES if (!this.shaders.has(material)) { var [vs, fs] = [material.vertexShader, material.fragmentShader]; var _shader = new Shader(gl, "pointcloud", vs, fs); this.shaders.set(material, _shader); } shader = this.shaders.get(material); //if(material.needsUpdate){ { var [_vs, _fs] = [material.vertexShader, material.fragmentShader]; var numSnapshots = material.snapEnabled ? material.numSnapshots : 0; var numClipBoxes = material.clipBoxes && material.clipBoxes.length ? material.clipBoxes.length : 0; var numClipSpheres = params.clipSpheres && params.clipSpheres.length ? params.clipSpheres.length : 0; var numClipPolygons = material.clipPolygons && material.clipPolygons.length ? material.clipPolygons.length : 0; var defines = ["#define num_shadowmaps ".concat(shadowMaps.length), "#define num_snapshots ".concat(numSnapshots), "#define num_clipboxes ".concat(numClipBoxes), "#define num_clipspheres ".concat(numClipSpheres), "#define num_clippolygons ".concat(numClipPolygons)]; if (octree.pcoGeometry.root.isLoaded()) { var attributes = octree.pcoGeometry.root.geometry.attributes; if (attributes["gps-time"]) { defines.push("#define clip_gps_enabled"); } if (attributes["return number"]) { defines.push("#define clip_return_number_enabled"); } if (attributes["number of returns"]) { defines.push("#define clip_number_of_returns_enabled"); } if (attributes["source id"] || attributes["point source id"]) { defines.push("#define clip_point_source_id_enabled"); } } var definesString = defines.join("\n"); var vsVersionIndex = _vs.indexOf("#version "); var fsVersionIndex = _fs.indexOf("#version "); if (vsVersionIndex >= 0) { _vs = _vs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { _vs = "".concat(definesString, "\n").concat(_vs); } if (fsVersionIndex >= 0) { _fs = _fs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { _fs = "".concat(definesString, "\n").concat(_fs); } shader.update(_vs, _fs); material.needsUpdate = false; } for (var uniformName of Object.keys(material.uniforms)) { var uniform = material.uniforms[uniformName]; if (uniform.type == "t") { var texture = uniform.value; if (!texture) { continue; } if (!this.textures.has(texture)) { var webglTexture = new WebGLTexture(gl, texture); this.textures.set(texture, webglTexture); } var webGLTexture = this.textures.get(texture); webGLTexture.update(); } } } gl.useProgram(shader.program); var transparent = false; if (params.transparent !== undefined) { transparent = params.transparent && material.opacity < 1; } else { transparent = material.opacity < 1; } if (transparent) { gl.enable(gl.BLEND); gl.blendFunc(gl.SRC_ALPHA, gl.ONE); gl.depthMask(false); gl.disable(gl.DEPTH_TEST); } else { gl.disable(gl.BLEND); gl.depthMask(true); gl.enable(gl.DEPTH_TEST); } if (params.blendFunc !== undefined) { gl.enable(gl.BLEND); gl.blendFunc(...params.blendFunc); } if (params.depthTest !== undefined) { if (params.depthTest === true) { gl.enable(gl.DEPTH_TEST); } else { gl.disable(gl.DEPTH_TEST); } } if (params.depthWrite !== undefined) { if (params.depthWrite === true) { gl.depthMask(true); } else { gl.depthMask(false); } } { // UPDATE UNIFORMS shader.setUniformMatrix4("projectionMatrix", proj); shader.setUniformMatrix4("viewMatrix", view); shader.setUniformMatrix4("uViewInv", viewInv); shader.setUniformMatrix4("uProjInv", projInv); var screenWidth = target ? target.width : material.screenWidth; var screenHeight = target ? target.height : material.screenHeight; shader.setUniform1f("uScreenWidth", screenWidth); shader.setUniform1f("uScreenHeight", screenHeight); shader.setUniform1f("fov", Math.PI * camera.fov / 180); shader.setUniform1f("near", camera.near); shader.setUniform1f("far", camera.far); if (camera instanceof OrthographicCamera) { shader.setUniform("uUseOrthographicCamera", true); shader.setUniform("uOrthoWidth", camera.right - camera.left); shader.setUniform("uOrthoHeight", camera.top - camera.bottom); } else { shader.setUniform("uUseOrthographicCamera", false); } if (material.clipBoxes.length + material.clipPolygons.length === 0) { shader.setUniform1i("clipTask", ClipTask.NONE); } else { shader.setUniform1i("clipTask", material.clipTask); } shader.setUniform1i("clipMethod", material.clipMethod); if (material.clipBoxes && material.clipBoxes.length > 0) { //let flattenedMatrices = [].concat(...material.clipBoxes.map(c => c.inverse.elements)); //const lClipBoxes = shader.uniformLocations["clipBoxes[0]"]; //gl.uniformMatrix4fv(lClipBoxes, false, flattenedMatrices); var lClipBoxes = shader.uniformLocations["clipBoxes[0]"]; gl.uniformMatrix4fv(lClipBoxes, false, material.uniforms.clipBoxes.value); } // TODO CLIPSPHERES if (params.clipSpheres && params.clipSpheres.length > 0) { var clipSpheres = params.clipSpheres; var matrices = []; for (var clipSphere of clipSpheres) { //let mScale = new THREE.Matrix4().makeScale(...clipSphere.scale.toArray()); //let mTranslate = new THREE.Matrix4().makeTranslation(...clipSphere.position.toArray()); //let clipToWorld = new THREE.Matrix4().multiplyMatrices(mTranslate, mScale); var clipToWorld = clipSphere.matrixWorld; var viewToWorld = camera.matrixWorld; var worldToClip = clipToWorld.clone().invert(); var viewToClip = new Matrix4().multiplyMatrices(worldToClip, viewToWorld); matrices.push(viewToClip); } var flattenedMatrices = [].concat(...matrices.map(matrix => matrix.elements)); var lClipSpheres = shader.uniformLocations["uClipSpheres[0]"]; gl.uniformMatrix4fv(lClipSpheres, false, flattenedMatrices); //const lClipSpheres = shader.uniformLocations["uClipSpheres[0]"]; //gl.uniformMatrix4fv(lClipSpheres, false, material.uniforms.clipSpheres.value); } shader.setUniform1f("size", material.size); shader.setUniform1f("maxSize", material.uniforms.maxSize.value); shader.setUniform1f("minSize", material.uniforms.minSize.value); // uniform float uPCIndex shader.setUniform1f("uOctreeSpacing", material.spacing); shader.setUniform("uOctreeSize", material.uniforms.octreeSize.value); //uniform vec3 uColor; shader.setUniform3f("uColor", material.color.toArray()); //uniform float opacity; shader.setUniform1f("uOpacity", material.opacity); shader.setUniform2f("elevationRange", material.elevationRange); shader.setUniform2f("intensityRange", material.intensityRange); shader.setUniform3f("uIntensity_gbc", [material.intensityGamma, material.intensityBrightness, material.intensityContrast]); shader.setUniform3f("uRGB_gbc", [material.rgbGamma, material.rgbBrightness, material.rgbContrast]); shader.setUniform1f("uTransition", material.transition); shader.setUniform1f("wRGB", material.weightRGB); shader.setUniform1f("wIntensity", material.weightIntensity); shader.setUniform1f("wElevation", material.weightElevation); shader.setUniform1f("wClassification", material.weightClassification); shader.setUniform1f("wReturnNumber", material.weightReturnNumber); shader.setUniform1f("wSourceID", material.weightSourceID); shader.setUniform("backfaceCulling", material.uniforms.backfaceCulling.value); var vnWebGLTexture = this.textures.get(material.visibleNodesTexture); if (vnWebGLTexture) { shader.setUniform1i("visibleNodesTexture", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(vnWebGLTexture.target, vnWebGLTexture.id); currentTextureBindingPoint++; } var gradientTexture = this.textures.get(material.gradientTexture); shader.setUniform1i("gradient", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(gradientTexture.target, gradientTexture.id); var repeat = material.elevationGradientRepeat; if (repeat === ElevationGradientRepeat.REPEAT) { gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.REPEAT); } else if (repeat === ElevationGradientRepeat.MIRRORED_REPEAT) { gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); } else { gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } currentTextureBindingPoint++; var classificationTexture = this.textures.get(material.classificationTexture); shader.setUniform1i("classificationLUT", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(classificationTexture.target, classificationTexture.id); currentTextureBindingPoint++; var matcapTexture = this.textures.get(material.matcapTexture); shader.setUniform1i("matcapTextureUniform", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(matcapTexture.target, matcapTexture.id); currentTextureBindingPoint++; if (material.snapEnabled === true) { { var lSnapshot = shader.uniformLocations["uSnapshot[0]"]; var lSnapshotDepth = shader.uniformLocations["uSnapshotDepth[0]"]; var bindingStart = currentTextureBindingPoint; var lSnapshotBindingPoints = new Array(5).fill(bindingStart).map((a, i) => a + i); var lSnapshotDepthBindingPoints = new Array(5).fill(1 + Math.max(...lSnapshotBindingPoints)).map((a, i) => a + i); currentTextureBindingPoint = 1 + Math.max(...lSnapshotDepthBindingPoints); gl.uniform1iv(lSnapshot, lSnapshotBindingPoints); gl.uniform1iv(lSnapshotDepth, lSnapshotDepthBindingPoints); for (var i = 0; i < 5; i++) { var _texture = material.uniforms["uSnapshot"].value[i]; var textureDepth = material.uniforms["uSnapshotDepth"].value[i]; if (!_texture) { break; } var snapTexture = this.threeRenderer.properties.get(_texture).__webglTexture; var snapTextureDepth = this.threeRenderer.properties.get(textureDepth).__webglTexture; var bindingPoint = lSnapshotBindingPoints[i]; var depthBindingPoint = lSnapshotDepthBindingPoints[i]; gl.activeTexture(gl["TEXTURE".concat(bindingPoint)]); gl.bindTexture(gl.TEXTURE_2D, snapTexture); gl.activeTexture(gl["TEXTURE".concat(depthBindingPoint)]); gl.bindTexture(gl.TEXTURE_2D, snapTextureDepth); } } { var _flattenedMatrices3 = [].concat(...material.uniforms.uSnapView.value.map(c => c.elements)); var lSnapView = shader.uniformLocations["uSnapView[0]"]; gl.uniformMatrix4fv(lSnapView, false, _flattenedMatrices3); } { var _flattenedMatrices4 = [].concat(...material.uniforms.uSnapProj.value.map(c => c.elements)); var lSnapProj = shader.uniformLocations["uSnapProj[0]"]; gl.uniformMatrix4fv(lSnapProj, false, _flattenedMatrices4); } { var _flattenedMatrices5 = [].concat(...material.uniforms.uSnapProjInv.value.map(c => c.elements)); var lSnapProjInv = shader.uniformLocations["uSnapProjInv[0]"]; gl.uniformMatrix4fv(lSnapProjInv, false, _flattenedMatrices5); } { var _flattenedMatrices6 = [].concat(...material.uniforms.uSnapViewInv.value.map(c => c.elements)); var lSnapViewInv = shader.uniformLocations["uSnapViewInv[0]"]; gl.uniformMatrix4fv(lSnapViewInv, false, _flattenedMatrices6); } } } this.renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params); gl.activeTexture(gl.TEXTURE2); gl.bindTexture(gl.TEXTURE_2D, null); gl.activeTexture(gl.TEXTURE0); } render(scene, camera) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var params = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var gl = this.gl; // PREPARE if (target != null) { this.threeRenderer.setRenderTarget(target); } //camera.updateProjectionMatrix(); // camera.matrixWorldInverse.invert(camera.matrixWorld); var traversalResult = this.traverse(scene); // RENDER for (var octree of traversalResult.octrees) { var nodes = octree.visibleNodes; this.renderOctree(octree, nodes, camera, target, params); } // CLEANUP gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, null); gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); this.threeRenderer.resetState(); } } ; function copyMaterial(source, target) { for (var name of Object.keys(target.uniforms)) { target.uniforms[name].value = source.uniforms[name].value; } target.gradientTexture = source.gradientTexture; target.visibleNodesTexture = source.visibleNodesTexture; target.classificationTexture = source.classificationTexture; target.matcapTexture = source.matcapTexture; target.activeAttributeName = source.activeAttributeName; target.ranges = source.ranges; //target.updateShaderSource(); } class Batch { constructor(geometry, material) { this.geometry = geometry; this.material = material; this.sceneNode = new Points(geometry, material); this.geometryNode = { estimatedSpacing: 1.0, geometry: geometry }; } getLevel() { return 0; } } class ProfileFakeOctree extends PointCloudTree { constructor(octree) { super(); this.trueOctree = octree; this.pcoGeometry = octree.pcoGeometry; this.points = []; this.visibleNodes = []; //this.material = this.trueOctree.material; this.material = new PointCloudMaterial$1(); //this.material.copy(this.trueOctree.material); copyMaterial(this.trueOctree.material, this.material); this.material.pointSizeType = PointSizeType.FIXED; this.batchSize = 100 * 1000; this.currentBatch = null; } getAttribute(name) { return this.trueOctree.getAttribute(name); } dispose() { for (var node of this.visibleNodes) { node.geometry.dispose(); } this.visibleNodes = []; this.currentBatch = null; this.points = []; } addPoints(data) { // since each call to addPoints can deliver very very few points, // we're going to batch them into larger buffers for efficiency. if (this.currentBatch === null) { this.currentBatch = this.createNewBatch(data); } this.points.push(data); var updateRange = { start: this.currentBatch.geometry.drawRange.count, count: 0 }; var projectedBox = new Box3(); var truePos = new Vector3(); for (var i = 0; i < data.numPoints; i++) { if (updateRange.start + updateRange.count >= this.batchSize) { // current batch full, start new batch for (var key of Object.keys(this.currentBatch.geometry.attributes)) { var attribute = this.currentBatch.geometry.attributes[key]; attribute.updateRange.offset = updateRange.start; attribute.updateRange.count = updateRange.count; attribute.needsUpdate = true; } this.currentBatch.geometry.computeBoundingBox(); this.currentBatch.geometry.computeBoundingSphere(); this.currentBatch = this.createNewBatch(data); updateRange = { start: 0, count: 0 }; } truePos.set(data.data.position[3 * i + 0] + this.trueOctree.position.x, data.data.position[3 * i + 1] + this.trueOctree.position.y, data.data.position[3 * i + 2] + this.trueOctree.position.z); var x = data.data.mileage[i]; var y = 0; var z = truePos.z; projectedBox.expandByPoint(new Vector3(x, y, z)); var index = updateRange.start + updateRange.count; var geometry = this.currentBatch.geometry; for (var attributeName of Object.keys(data.data)) { var source = data.data[attributeName]; var target = geometry.attributes[attributeName]; var numElements = target.itemSize; for (var item = 0; item < numElements; item++) { target.array[numElements * index + item] = source[numElements * i + item]; } } { var position = geometry.attributes.position; position.array[3 * index + 0] = x; position.array[3 * index + 1] = y; position.array[3 * index + 2] = z; } updateRange.count++; this.currentBatch.geometry.drawRange.count++; } for (var _key of Object.keys(this.currentBatch.geometry.attributes)) { var _attribute = this.currentBatch.geometry.attributes[_key]; _attribute.updateRange.offset = updateRange.start; _attribute.updateRange.count = updateRange.count; _attribute.needsUpdate = true; } data.projectedBox = projectedBox; this.projectedBox = this.points.reduce((a, i) => a.union(i.projectedBox), new Box3()); } createNewBatch(data) { var geometry = new BufferGeometry(); // create new batches with batch_size elements of the same type as the attribute for (var attributeName of Object.keys(data.data)) { var buffer = data.data[attributeName]; var numElements = buffer.length / data.numPoints; // 3 for pos, 4 for col, 1 for scalars var _constructor = buffer.constructor; var normalized = false; if (this.trueOctree.root.sceneNode) { if (this.trueOctree.root.sceneNode.geometry.attributes[attributeName]) { normalized = this.trueOctree.root.sceneNode.geometry.attributes[attributeName].normalized; } } var batchBuffer = new _constructor(numElements * this.batchSize); var bufferAttribute = new BufferAttribute(batchBuffer, numElements, normalized); bufferAttribute.potree = { range: [0, 1] }; geometry.setAttribute(attributeName, bufferAttribute); } geometry.drawRange.start = 0; geometry.drawRange.count = 0; var batch = new Batch(geometry, this.material); this.visibleNodes.push(batch); return batch; } computeVisibilityTextureData() { var data = new Uint8Array(this.visibleNodes.length * 4); var offsets = new Map(); for (var i = 0; i < this.visibleNodes.length; i++) { var node = this.visibleNodes[i]; offsets[node] = i; } return { data: data, offsets: offsets }; } } class ProfileWindow extends EventDispatcher$1 { constructor(viewer) { super(); this.viewer = viewer; this.elRoot = $('#profile_window'); this.renderArea = this.elRoot.find('#profileCanvasContainer'); this.svg = d3.select('svg#profileSVG'); this.mouseIsDown = false; this.projectedBox = new Box3(); this.pointclouds = new Map(); this.numPoints = 0; this.lastAddPointsTimestamp = undefined; this.mouse = new Vector2(0, 0); this.scale = new Vector3(1, 1, 1); this.autoFitEnabled = true; // completely disable/enable this.autoFit = false; // internal var cwIcon = "".concat(exports.resourcePath, "/icons/arrow_cw.svg"); $('#potree_profile_rotate_cw').attr('src', cwIcon); var ccwIcon = "".concat(exports.resourcePath, "/icons/arrow_ccw.svg"); $('#potree_profile_rotate_ccw').attr('src', ccwIcon); var forwardIcon = "".concat(exports.resourcePath, "/icons/arrow_up.svg"); $('#potree_profile_move_forward').attr('src', forwardIcon); var backwardIcon = "".concat(exports.resourcePath, "/icons/arrow_down.svg"); $('#potree_profile_move_backward').attr('src', backwardIcon); var csvIcon = "".concat(exports.resourcePath, "/icons/file_csv_2d.svg"); $('#potree_download_csv_icon').attr('src', csvIcon); var lasIcon = "".concat(exports.resourcePath, "/icons/file_las_3d.svg"); $('#potree_download_las_icon').attr('src', lasIcon); var closeIcon = "".concat(exports.resourcePath, "/icons/close.svg"); $('#closeProfileContainer').attr("src", closeIcon); this.initTHREE(); this.initSVG(); this.initListeners(); this.pRenderer = new Renderer(this.renderer); this.elRoot.i18n(); } initListeners() { $(window).resize(() => { if (this.enabled) { this.render(); } }); this.renderArea.mousedown(e => { this.mouseIsDown = true; }); this.renderArea.mouseup(e => { this.mouseIsDown = false; }); var viewerPickSphereSizeHandler = () => { var camera = this.viewer.scene.getActiveCamera(); var domElement = this.viewer.renderer.domElement; var distance = this.viewerPickSphere.position.distanceTo(camera.position); var pr = Utils.projectedRadius(1, camera, distance, domElement.clientWidth, domElement.clientHeight); var scale = 10 / pr; this.viewerPickSphere.scale.set(scale, scale, scale); }; this.renderArea.mousemove(e => { if (this.pointclouds.size === 0) { return; } var rect = this.renderArea[0].getBoundingClientRect(); var x = e.clientX - rect.left; var y = e.clientY - rect.top; var newMouse = new Vector2(x, y); if (this.mouseIsDown) { // DRAG this.autoFit = false; this.lastDrag = new Date().getTime(); var cPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)]; var ncPos = [this.scaleX.invert(newMouse.x), this.scaleY.invert(newMouse.y)]; this.camera.position.x -= ncPos[0] - cPos[0]; this.camera.position.z -= ncPos[1] - cPos[1]; this.render(); } else if (this.pointclouds.size > 0) { // FIND HOVERED POINT var radius = Math.abs(this.scaleX.invert(0) - this.scaleX.invert(40)); var mileage = this.scaleX.invert(newMouse.x); var elevation = this.scaleY.invert(newMouse.y); var closest = this.selectPoint(mileage, elevation, radius); if (closest) { var point = closest.point; var position = new Float64Array([point.position[0] + closest.pointcloud.position.x, point.position[1] + closest.pointcloud.position.y, point.position[2] + closest.pointcloud.position.z]); this.elRoot.find('#profileSelectionProperties').fadeIn(200); this.pickSphere.visible = true; this.pickSphere.scale.set(0.5 * radius, 0.5 * radius, 0.5 * radius); this.pickSphere.position.set(point.mileage, 0, position[2]); this.viewerPickSphere.position.set(...position); if (!this.viewer.scene.scene.children.includes(this.viewerPickSphere)) { this.viewer.scene.scene.add(this.viewerPickSphere); if (!this.viewer.hasEventListener("update", viewerPickSphereSizeHandler)) { this.viewer.addEventListener("update", viewerPickSphereSizeHandler); } } var info = this.elRoot.find('#profileSelectionProperties'); var html = ''; var _loop = function _loop() { var value = point[attributeName]; var attribute = closest.pointcloud.getAttribute(attributeName); var transform = value => value; if (attribute && attribute.type.size > 4) { var range = attribute.initialRange; var scale = 1 / (range[1] - range[0]); var offset = range[0]; transform = value => value / scale + offset; } if (attributeName === 'position') { var values = [...position].map(v => Utils.addCommas(v.toFixed(3))); html += "\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t"); } else if (attributeName === 'rgba') { html += "\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t"); } else if (attributeName === 'normal') { return 1; // continue } else if (attributeName === 'mileage') { html += "\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t"); } else { html += "\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t"); } }; for (var attributeName of Object.keys(point)) { if (_loop()) continue; } html += '
x".concat(values[0], "
y").concat(values[1], "
z").concat(values[2], "
".concat(attributeName, "").concat(value.join(', '), "
".concat(attributeName, "").concat(value.toFixed(3), "
".concat(attributeName, "").concat(transform(value), "
'; info.html(html); this.selectedPoint = point; } else { // this.pickSphere.visible = false; // this.selectedPoint = null; this.viewer.scene.scene.add(this.viewerPickSphere); var index = this.viewer.scene.scene.children.indexOf(this.viewerPickSphere); if (index >= 0) { this.viewer.scene.scene.children.splice(index, 1); } this.viewer.removeEventListener("update", viewerPickSphereSizeHandler); } this.render(); } this.mouse.copy(newMouse); }); var onWheel = e => { this.autoFit = false; var delta = 0; if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9 delta = e.wheelDelta; } else if (e.detail !== undefined) { // Firefox delta = -e.detail; } var ndelta = Math.sign(delta); var cPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)]; if (ndelta > 0) { // + 10% this.scale.multiplyScalar(1.1); } else { // - 10% this.scale.multiplyScalar(100 / 110); } this.updateScales(); var ncPos = [this.scaleX.invert(this.mouse.x), this.scaleY.invert(this.mouse.y)]; this.camera.position.x -= ncPos[0] - cPos[0]; this.camera.position.z -= ncPos[1] - cPos[1]; this.render(); this.updateScales(); }; $(this.renderArea)[0].addEventListener('mousewheel', onWheel, false); $(this.renderArea)[0].addEventListener('DOMMouseScroll', onWheel, false); // Firefox $('#closeProfileContainer').click(() => { this.hide(); }); var getProfilePoints = () => { var points = new Points$1(); for (var [pointcloud, entry] of this.pointclouds) { for (var pointSet of entry.points) { var originPos = pointSet.data.position; var trueElevationPosition = new Float32Array(originPos); for (var i = 0; i < pointSet.numPoints; i++) { trueElevationPosition[3 * i + 2] += pointcloud.position.z; } pointSet.data.position = trueElevationPosition; points.add(pointSet); pointSet.data.position = originPos; } } return points; }; $('#potree_download_csv_icon').click(() => { var points = getProfilePoints(); var string = CSVExporter.toString(points); var blob = new Blob([string], { type: "text/string" }); $('#potree_download_profile_ortho_link').attr('href', URL.createObjectURL(blob)); }); $('#potree_download_las_icon').click(() => { var points = getProfilePoints(); var buffer = LASExporter.toLAS(points); var blob = new Blob([buffer], { type: "application/octet-binary" }); $('#potree_download_profile_link').attr('href', URL.createObjectURL(blob)); }); } selectPoint(mileage, elevation, radius) { var closest = { distance: Infinity, pointcloud: null, points: null, index: null }; var pointBox = new Box2(new Vector2(mileage - radius, elevation - radius), new Vector2(mileage + radius, elevation + radius)); var numTested = 0; var numSkipped = 0; var numTestedPoints = 0; var numSkippedPoints = 0; for (var [pointcloud, entry] of this.pointclouds) { for (var points of entry.points) { var collisionBox = new Box2(new Vector2(points.projectedBox.min.x, points.projectedBox.min.z), new Vector2(points.projectedBox.max.x, points.projectedBox.max.z)); var intersects = collisionBox.intersectsBox(pointBox); if (!intersects) { numSkipped++; numSkippedPoints += points.numPoints; continue; } numTested++; numTestedPoints += points.numPoints; for (var i = 0; i < points.numPoints; i++) { var m = points.data.mileage[i] - mileage; var e = points.data.position[3 * i + 2] - elevation + pointcloud.position.z; var r = Math.sqrt(m * m + e * e); var withinDistance = r < radius && r < closest.distance; var unfilteredClass = true; if (points.data.classification) { var classification = pointcloud.material.classification; var pointClassID = points.data.classification[i]; var pointClassValue = classification[pointClassID]; if (pointClassValue && (!pointClassValue.visible || pointClassValue.color.w === 0)) { unfilteredClass = false; } } if (withinDistance && unfilteredClass) { closest = { distance: r, pointcloud: pointcloud, points: points, index: i }; } } } } //console.log(`nodes: ${numTested}, ${numSkipped} || points: ${numTestedPoints}, ${numSkippedPoints}`); if (closest.distance < Infinity) { var _points = closest.points; var point = {}; var attributes = Object.keys(_points.data); for (var attribute of attributes) { var attributeData = _points.data[attribute]; var itemSize = attributeData.length / _points.numPoints; var value = attributeData.subarray(itemSize * closest.index, itemSize * closest.index + itemSize); if (value.length === 1) { point[attribute] = value[0]; } else { point[attribute] = value; } } closest.point = point; return closest; } else { return null; } } initTHREE() { this.renderer = new WebGLRenderer({ alpha: true, premultipliedAlpha: false }); this.renderer.setClearColor(0x000000, 0); this.renderer.setSize(10, 10); this.renderer.autoClear = false; this.renderArea.append($(this.renderer.domElement)); this.renderer.domElement.tabIndex = '2222'; $(this.renderer.domElement).css('width', '100%'); $(this.renderer.domElement).css('height', '100%'); { var gl = this.renderer.getContext(); if (gl.createVertexArray == null) { var extVAO = gl.getExtension('OES_vertex_array_object'); if (!extVAO) { throw new Error("OES_vertex_array_object extension not supported"); } gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO); gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO); } } this.camera = new OrthographicCamera(-1000, 1000, 1000, -1000, -1000, 1000); this.camera.up.set(0, 0, 1); this.camera.rotation.order = "ZXY"; this.camera.rotation.x = Math.PI / 2.0; this.scene = new Scene(); this.profileScene = new Scene(); var sg = new SphereGeometry(1, 16, 16); var sm = new MeshNormalMaterial(); this.pickSphere = new Mesh(sg, sm); this.scene.add(this.pickSphere); this.viewerPickSphere = new Mesh(sg, sm); } initSVG() { var width = this.renderArea[0].clientWidth; var height = this.renderArea[0].clientHeight; var marginLeft = this.renderArea[0].offsetLeft; this.svg.selectAll('*').remove(); this.scaleX = d3.scale.linear().domain([this.camera.left + this.camera.position.x, this.camera.right + this.camera.position.x]).range([0, width]); this.scaleY = d3.scale.linear().domain([this.camera.bottom + this.camera.position.z, this.camera.top + this.camera.position.z]).range([height, 0]); this.xAxis = d3.svg.axis().scale(this.scaleX).orient('bottom').innerTickSize(-height).outerTickSize(1).tickPadding(10).ticks(width / 50); this.yAxis = d3.svg.axis().scale(this.scaleY).orient('left').innerTickSize(-width).outerTickSize(1).tickPadding(10).ticks(height / 20); this.elXAxis = this.svg.append('g').attr('class', 'x axis').attr('transform', "translate(".concat(marginLeft, ", ").concat(height, ")")).call(this.xAxis); this.elYAxis = this.svg.append('g').attr('class', 'y axis').attr('transform', "translate(".concat(marginLeft, ", 0)")).call(this.yAxis); } addPoints(pointcloud, points) { if (points.numPoints === 0) { return; } var entry = this.pointclouds.get(pointcloud); if (!entry) { entry = new ProfileFakeOctree(pointcloud); this.pointclouds.set(pointcloud, entry); this.profileScene.add(entry); var materialChanged = () => { this.render(); }; materialChanged(); pointcloud.material.addEventListener('material_property_changed', materialChanged); this.addEventListener("on_reset_once", () => { pointcloud.material.removeEventListener('material_property_changed', materialChanged); }); } entry.addPoints(points); this.projectedBox.union(entry.projectedBox); if (this.autoFit && this.autoFitEnabled) { var width = this.renderArea[0].clientWidth; var height = this.renderArea[0].clientHeight; var size = this.projectedBox.getSize(new Vector3()); var sx = width / size.x; var sy = height / size.z; var scale = Math.min(sx, sy); var center = this.projectedBox.getCenter(new Vector3()); this.scale.set(scale, scale, 1); this.camera.position.copy(center); //console.log("camera: ", this.camera.position.toArray().join(", ")); } //console.log(entry); this.render(); var numPoints = 0; for (var [key, value] of this.pointclouds.entries()) { numPoints += value.points.reduce((a, i) => a + i.numPoints, 0); } $("#profile_num_points").html(Utils.addCommas(numPoints)); } reset() { this.lastReset = new Date().getTime(); this.dispatchEvent({ type: "on_reset_once" }); this.removeEventListeners("on_reset_once"); this.autoFit = true; this.projectedBox = new Box3(); for (var [key, entry] of this.pointclouds) { entry.dispose(); } this.pointclouds.clear(); this.mouseIsDown = false; this.mouse.set(0, 0); if (this.autoFitEnabled) { this.scale.set(1, 1, 1); } this.pickSphere.visible = false; this.elRoot.find('#profileSelectionProperties').hide(); this.render(); } show() { this.elRoot.fadeIn(); this.enabled = true; } hide() { this.elRoot.fadeOut(); this.enabled = false; } updateScales() { var width = this.renderArea[0].clientWidth; var height = this.renderArea[0].clientHeight; var left = -width / 2 / this.scale.x; var right = +width / 2 / this.scale.x; var top = +height / 2 / this.scale.y; var bottom = -height / 2 / this.scale.y; this.camera.left = left; this.camera.right = right; this.camera.top = top; this.camera.bottom = bottom; this.camera.updateProjectionMatrix(); this.scaleX.domain([this.camera.left + this.camera.position.x, this.camera.right + this.camera.position.x]).range([0, width]); this.scaleY.domain([this.camera.bottom + this.camera.position.z, this.camera.top + this.camera.position.z]).range([height, 0]); var marginLeft = this.renderArea[0].offsetLeft; this.xAxis.scale(this.scaleX).orient('bottom').innerTickSize(-height).outerTickSize(1).tickPadding(10).ticks(width / 50); this.yAxis.scale(this.scaleY).orient('left').innerTickSize(-width).outerTickSize(1).tickPadding(10).ticks(height / 20); this.elXAxis.attr('transform', "translate(".concat(marginLeft, ", ").concat(height, ")")).call(this.xAxis); this.elYAxis.attr('transform', "translate(".concat(marginLeft, ", 0)")).call(this.yAxis); } requestScaleUpdate() { var threshold = 100; var allowUpdate = this.lastReset === undefined || this.lastScaleUpdate === undefined || new Date().getTime() - this.lastReset > threshold && new Date().getTime() - this.lastScaleUpdate > threshold; if (allowUpdate) { this.updateScales(); this.lastScaleUpdate = new Date().getTime(); this.scaleUpdatePending = false; } else if (!this.scaleUpdatePending) { setTimeout(this.requestScaleUpdate.bind(this), 100); this.scaleUpdatePending = true; } } render() { var width = this.renderArea[0].clientWidth; var height = this.renderArea[0].clientHeight; var { renderer, pRenderer, camera, profileScene, scene } = this; var { scaleX, pickSphere } = this; renderer.setSize(width, height); renderer.setClearColor(0x000000, 0); renderer.clear(true, true, false); for (var pointcloud of this.pointclouds.keys()) { var source = pointcloud.material; var target = this.pointclouds.get(pointcloud).material; copyMaterial(source, target); target.size = 2; } pRenderer.render(profileScene, camera, null); var radius = Math.abs(scaleX.invert(0) - scaleX.invert(5)); if (radius === 0) { pickSphere.visible = false; } else { pickSphere.scale.set(radius, radius, radius); pickSphere.visible = true; } renderer.render(scene, camera); this.requestScaleUpdate(); } } ; class ProfileWindowController { constructor(viewer) { this.viewer = viewer; this.profileWindow = viewer.profileWindow; this.profile = null; this.numPoints = 0; this.threshold = 60 * 1000; this.rotateAmount = 10; this.scheduledRecomputeTime = null; this.enabled = true; this.requests = []; this._recompute = () => { this.recompute(); }; this.viewer.addEventListener("scene_changed", e => { e.oldScene.removeEventListener("pointcloud_added", this._recompute); e.scene.addEventListener("pointcloud_added", this._recompute); }); this.viewer.scene.addEventListener("pointcloud_added", this._recompute); $("#potree_profile_rotate_amount").val(parseInt(this.rotateAmount)); $("#potree_profile_rotate_amount").on("input", e => { var str = $("#potree_profile_rotate_amount").val(); if (!isNaN(str)) { var value = parseFloat(str); this.rotateAmount = value; $("#potree_profile_rotate_amount").css("background-color", ""); } else { $("#potree_profile_rotate_amount").css("background-color", "#ff9999"); } }); var rotate = radians => { var profile = this.profile; var points = profile.points; var start = points[0]; var end = points[points.length - 1]; var center = start.clone().add(end).multiplyScalar(0.5); var mMoveOrigin = new Matrix4().makeTranslation(-center.x, -center.y, -center.z); var mRotate = new Matrix4().makeRotationZ(radians); var mMoveBack = new Matrix4().makeTranslation(center.x, center.y, center.z); //const transform = mMoveOrigin.multiply(mRotate).multiply(mMoveBack); var transform = mMoveBack.multiply(mRotate).multiply(mMoveOrigin); var rotatedPoints = points.map(point => point.clone().applyMatrix4(transform)); this.profileWindow.autoFitEnabled = false; for (var i = 0; i < points.length; i++) { profile.setPosition(i, rotatedPoints[i]); } }; $("#potree_profile_rotate_cw").click(() => { var radians = MathUtils.degToRad(this.rotateAmount); rotate(-radians); }); $("#potree_profile_rotate_ccw").click(() => { var radians = MathUtils.degToRad(this.rotateAmount); rotate(radians); }); $("#potree_profile_move_forward").click(() => { var profile = this.profile; var points = profile.points; var start = points[0]; var end = points[points.length - 1]; var dir = end.clone().sub(start).normalize(); var up = new Vector3(0, 0, 1); var forward = up.cross(dir); var move = forward.clone().multiplyScalar(profile.width / 2); this.profileWindow.autoFitEnabled = false; for (var i = 0; i < points.length; i++) { profile.setPosition(i, points[i].clone().add(move)); } }); $("#potree_profile_move_backward").click(() => { var profile = this.profile; var points = profile.points; var start = points[0]; var end = points[points.length - 1]; var dir = end.clone().sub(start).normalize(); var up = new Vector3(0, 0, 1); var forward = up.cross(dir); var move = forward.clone().multiplyScalar(-profile.width / 2); this.profileWindow.autoFitEnabled = false; for (var i = 0; i < points.length; i++) { profile.setPosition(i, points[i].clone().add(move)); } }); } setProfile(profile) { if (this.profile !== null && this.profile !== profile) { this.profile.removeEventListener('marker_moved', this._recompute); this.profile.removeEventListener('marker_added', this._recompute); this.profile.removeEventListener('marker_removed', this._recompute); this.profile.removeEventListener('width_changed', this._recompute); } this.profile = profile; { this.profile.addEventListener('marker_moved', this._recompute); this.profile.addEventListener('marker_added', this._recompute); this.profile.addEventListener('marker_removed', this._recompute); this.profile.addEventListener('width_changed', this._recompute); } this.recompute(); } reset() { this.profileWindow.reset(); this.numPoints = 0; if (this.profile) { for (var request of this.requests) { request.cancel(); } } } progressHandler(pointcloud, progress) { for (var segment of progress.segments) { this.profileWindow.addPoints(pointcloud, segment.points); this.numPoints += segment.points.numPoints; } } cancel() { for (var request of this.requests) { request.cancel(); // request.finishLevelThenCancel(); } this.requests = []; } finishLevelThenCancel() { for (var request of this.requests) { request.finishLevelThenCancel(); } this.requests = []; } recompute() { var _this = this; if (!this.profile) { return; } if (this.scheduledRecomputeTime !== null && this.scheduledRecomputeTime > new Date().getTime()) { return; } else { this.scheduledRecomputeTime = new Date().getTime() + 100; } this.scheduledRecomputeTime = null; this.reset(); var _loop2 = function _loop2(pointcloud) { var request = pointcloud.getPointsInProfile(_this.profile, null, { 'onProgress': event => { if (!_this.enabled) { return; } _this.progressHandler(pointcloud, event.points); if (_this.numPoints > _this.threshold) { _this.finishLevelThenCancel(); } }, 'onFinish': event => { if (!_this.enabled) {} }, 'onCancel': () => { if (!_this.enabled) {} } }); _this.requests.push(request); }; for (var pointcloud of this.viewer.scene.pointclouds.filter(p => p.visible)) { _loop2(pointcloud); } } } ; //import { EventDispatcher } from "../EventDispatcher.js"; class VolumeTool extends EventDispatcher { constructor(viewer) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.addEventListener('start_inserting_volume', e => { this.viewer.dispatchEvent({ type: 'cancel_insertions' }); }); this.scene = new Scene(); this.scene.name = 'scene_volume'; this.viewer.inputHandler.registerInteractiveScene(this.scene); this.onRemove = e => { this.scene.remove(e.volume); }; this.onAdd = e => { this.scene.add(e.volume); }; for (var volume of viewer.scene.volumes) { this.onAdd({ volume: volume }); } this.viewer.inputHandler.addEventListener('delete', e => { var volumes = e.selection.filter(e => e instanceof Volume); volumes.forEach(e => this.viewer.scene.removeVolume(e)); }); viewer.addEventListener("update", this.update.bind(this)); viewer.addEventListener("render.pass.scene", e => this.render(e)); viewer.addEventListener("scene_changed", this.onSceneChange.bind(this)); viewer.scene.addEventListener('volume_added', this.onAdd); viewer.scene.addEventListener('volume_removed', this.onRemove); } onSceneChange(e) { if (e.oldScene) { e.oldScene.removeEventListeners('volume_added', this.onAdd); e.oldScene.removeEventListeners('volume_removed', this.onRemove); } e.scene.addEventListener('volume_added', this.onAdd); e.scene.addEventListener('volume_removed', this.onRemove); } startInsertion() { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var volume; if (args.type) { volume = new args.type(); } else { volume = new BoxVolume(); } volume.clip = args.clip || false; volume.name = args.name || 'Volume'; this.dispatchEvent({ type: 'start_inserting_volume', volume: volume }); this.viewer.scene.addVolume(volume); this.scene.add(volume); var cancel = { callback: null }; var drag = e => { var camera = this.viewer.scene.getActiveCamera(); var I = Utils.getMousePointCloudIntersection(e.drag.end, this.viewer.scene.getActiveCamera(), this.viewer, this.viewer.scene.pointclouds, { pickClipped: false }); if (I) { volume.position.copy(I.location); var wp = volume.getWorldPosition(new Vector3()).applyMatrix4(camera.matrixWorldInverse); // let pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(camera.projectionMatrix); var w = Math.abs(wp.z / 5); volume.scale.set(w, w, w); } }; var drop = e => { volume.removeEventListener('drag', drag); volume.removeEventListener('drop', drop); cancel.callback(); }; cancel.callback = e => { volume.removeEventListener('drag', drag); volume.removeEventListener('drop', drop); this.viewer.removeEventListener('cancel_insertions', cancel.callback); }; volume.addEventListener('drag', drag); volume.addEventListener('drop', drop); this.viewer.addEventListener('cancel_insertions', cancel.callback); this.viewer.inputHandler.startDragging(volume); return volume; } update() { if (!this.viewer.scene) { return; } var camera = this.viewer.scene.getActiveCamera(); var renderAreaSize = this.viewer.renderer.getSize(new Vector2()); var clientWidth = renderAreaSize.width; var clientHeight = renderAreaSize.height; var volumes = this.viewer.scene.volumes; for (var volume of volumes) { var label = volume.label; { var distance = label.position.distanceTo(camera.position); var pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight); var scale = 70 / pr; label.scale.set(scale, scale, scale); } var calculatedVolume = volume.getVolume(); calculatedVolume = calculatedVolume / Math.pow(this.viewer.lengthUnit.unitspermeter, 3) * Math.pow(this.viewer.lengthUnitDisplay.unitspermeter, 3); //convert to cubic meters then to the cubic display unit var text = Utils.addCommas(calculatedVolume.toFixed(3)) + ' ' + this.viewer.lengthUnitDisplay.code + '\u00B3'; label.setText(text); } } render(params) { var renderer = this.viewer.renderer; var oldTarget = renderer.getRenderTarget(); if (params.renderTarget) { renderer.setRenderTarget(params.renderTarget); } renderer.render(this.scene, this.viewer.scene.getActiveCamera()); renderer.setRenderTarget(oldTarget); } } /** * * code adapted from three.js BoxHelper.js * https://github.com/mrdoob/three.js/blob/dev/src/helpers/BoxHelper.js * * @author mrdoob / http://mrdoob.com/ * @author Mugen87 / http://github.com/Mugen87 * @author mschuetz / http://potree.org */ class Box3Helper$1 extends LineSegments { constructor(box, color) { var depthTest = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; 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([box.min.x, box.min.y, box.min.z, box.max.x, box.min.y, box.min.z, box.max.x, box.min.y, box.max.z, box.min.x, box.min.y, box.max.z, box.min.x, box.max.y, box.min.z, box.max.x, box.max.y, box.min.z, box.max.x, box.max.y, box.max.z, box.min.x, box.max.y, box.max.z]); var geometry = new BufferGeometry(); geometry.setIndex(new BufferAttribute(indices, 1)); geometry.setAttribute('position', new BufferAttribute(positions, 3)); var material = new LineBasicMaterial({ color: color, depthTest }); super(geometry, material); } } var KeyCodes = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, DELETE: 46, A: 'A'.charCodeAt(0), S: 'S'.charCodeAt(0), D: 'D'.charCodeAt(0), W: 'W'.charCodeAt(0), Q: 'Q'.charCodeAt(0), E: 'E'.charCodeAt(0), R: 'R'.charCodeAt(0), F: 'F'.charCodeAt(0) }; class NormalizationMaterial extends RawShaderMaterial { constructor() { var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); var uniforms = { uDepthMap: { type: 't', value: null }, uWeightMap: { type: 't', value: null } }; this.setValues({ uniforms: uniforms, vertexShader: this.getDefines() + Shaders['normalize.vs'], fragmentShader: this.getDefines() + Shaders['normalize.fs'] }); } getDefines() { var defines = ''; return defines; } updateShaderSource() { var vs = this.getDefines() + Shaders['normalize.vs']; var fs = this.getDefines() + Shaders['normalize.fs']; this.setValues({ vertexShader: vs, fragmentShader: fs }); this.needsUpdate = true; } } class NormalizationEDLMaterial extends RawShaderMaterial { constructor() { var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); var uniforms = { screenWidth: { type: 'f', value: 0 }, screenHeight: { type: 'f', value: 0 }, edlStrength: { type: 'f', value: 1.0 }, radius: { type: 'f', value: 1.0 }, neighbours: { type: '2fv', value: [] }, uEDLMap: { type: 't', value: null }, uDepthMap: { type: 't', value: null }, uWeightMap: { type: 't', value: null } }; this.setValues({ uniforms: uniforms, vertexShader: this.getDefines() + Shaders['normalize.vs'], fragmentShader: this.getDefines() + Shaders['normalize_and_edl.fs'] }); this.neighbourCount = 8; } getDefines() { var defines = ''; defines += '#define NEIGHBOUR_COUNT ' + this.neighbourCount + '\n'; return defines; } updateShaderSource() { var vs = this.getDefines() + Shaders['normalize.vs']; var fs = this.getDefines() + Shaders['normalize_and_edl.fs']; this.setValues({ vertexShader: vs, fragmentShader: fs }); this.uniforms.neighbours.value = this.neighbours; this.needsUpdate = true; } get neighbourCount() { return this._neighbourCount; } set neighbourCount(value) { if (this._neighbourCount !== value) { this._neighbourCount = value; this.neighbours = new Float32Array(this._neighbourCount * 2); for (var c = 0; c < this._neighbourCount; c++) { this.neighbours[2 * c + 0] = Math.cos(2 * c * Math.PI / this._neighbourCount); this.neighbours[2 * c + 1] = Math.sin(2 * c * Math.PI / this._neighbourCount); } this.updateShaderSource(); } } } //在potree.shim中修改。 。official中用不到且有bug class HQSplatRenderer { constructor(viewer) { this.viewer = viewer; this.depthMaterials = new Map(); this.attributeMaterials = new Map(); this.normalizationMaterial = null; this.rtDepth = null; this.rtAttribute = null; this.gl = viewer.renderer.getContext(); this.initialized = false; } init() { if (this.initialized) { return; } this.normalizationMaterial = new NormalizationMaterial(); this.normalizationMaterial.depthTest = true; this.normalizationMaterial.depthWrite = true; this.normalizationMaterial.transparent = true; this.normalizationEDLMaterial = new NormalizationEDLMaterial(); this.normalizationEDLMaterial.depthTest = true; this.normalizationEDLMaterial.depthWrite = true; this.normalizationEDLMaterial.transparent = true; this.rtDepth = new WebGLRenderTarget(1024, 1024, { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat, type: FloatType, depthTexture: new DepthTexture(undefined, undefined, UnsignedIntType) }); this.rtAttribute = new WebGLRenderTarget(1024, 1024, { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat, type: FloatType, depthTexture: this.rtDepth.depthTexture }); this.initialized = true; } resize(width, height) { this.rtDepth.setSize(width, height); this.rtAttribute.setSize(width, height); } clearTargets() { var viewer = this.viewer; var { renderer } = viewer; var oldTarget = renderer.getRenderTarget(); renderer.setClearColor(0x000000, 0); renderer.setRenderTarget(this.rtDepth); renderer.clear(true, true, true); renderer.setRenderTarget(this.rtAttribute); renderer.clear(true, true, true); renderer.setRenderTarget(oldTarget); } clear() { this.init(); var { renderer, background } = this.viewer; if (background === "skybox") { renderer.setClearColor(0x000000, 0); } else if (background === 'gradient') { renderer.setClearColor(0x000000, 0); } else if (background === 'black') { renderer.setClearColor(0x000000, 1); } else if (background === 'white') { renderer.setClearColor(0xFFFFFF, 1); } else { renderer.setClearColor(0x000000, 0); } renderer.clear(); this.clearTargets(); } render(params) { this.init(); var viewer = this.viewer; var camera = params.camera ? params.camera : viewer.scene.getActiveCamera(); var { width, height } = this.viewer.renderer.getSize(new Vector2()); viewer.dispatchEvent({ type: "render.pass.begin", viewer: viewer }); this.resize(width, height); var visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible); var originalMaterials = new Map(); for (var pointcloud of visiblePointClouds) { originalMaterials.set(pointcloud, pointcloud.material); if (!this.attributeMaterials.has(pointcloud)) { var attributeMaterial = new PointCloudMaterial$1(); this.attributeMaterials.set(pointcloud, attributeMaterial); } if (!this.depthMaterials.has(pointcloud)) { var depthMaterial = new PointCloudMaterial$1(); depthMaterial.setDefine("depth_pass", "#define hq_depth_pass"); depthMaterial.setDefine("use_edl", "#define use_edl"); this.depthMaterials.set(pointcloud, depthMaterial); } } { // DEPTH PASS for (var _pointcloud of visiblePointClouds) { var octreeSize = _pointcloud.pcoGeometry.boundingBox.getSize(new Vector3()).x; var material = originalMaterials.get(_pointcloud); var _depthMaterial = this.depthMaterials.get(_pointcloud); _depthMaterial.size = material.size; _depthMaterial.minSize = material.minSize; _depthMaterial.maxSize = material.maxSize; _depthMaterial.pointSizeType = material.pointSizeType; _depthMaterial.visibleNodesTexture = material.visibleNodesTexture; _depthMaterial.weighted = false; _depthMaterial.screenWidth = width; _depthMaterial.shape = PointShape$1.CIRCLE; _depthMaterial.screenHeight = height; _depthMaterial.uniforms.visibleNodes.value = material.visibleNodesTexture; _depthMaterial.uniforms.octreeSize.value = octreeSize; _depthMaterial.spacing = _pointcloud.pcoGeometry.spacing; // * Math.max(...pointcloud.scale.toArray()); _depthMaterial.classification = material.classification; _depthMaterial.uniforms.classificationLUT.value.image.data = material.uniforms.classificationLUT.value.image.data; _depthMaterial.classificationTexture.needsUpdate = true; _depthMaterial.uniforms.uFilterReturnNumberRange.value = material.uniforms.uFilterReturnNumberRange.value; _depthMaterial.uniforms.uFilterNumberOfReturnsRange.value = material.uniforms.uFilterNumberOfReturnsRange.value; _depthMaterial.uniforms.uFilterGPSTimeClipRange.value = material.uniforms.uFilterGPSTimeClipRange.value; _depthMaterial.uniforms.uFilterPointSourceIDClipRange.value = material.uniforms.uFilterPointSourceIDClipRange.value; _depthMaterial.clipTask = material.clipTask; _depthMaterial.clipMethod = material.clipMethod; _depthMaterial.setClipBoxes(material.clipBoxes); _depthMaterial.setClipPolygons(material.clipPolygons); _pointcloud.material = _depthMaterial; } viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtDepth, { clipSpheres: viewer.scene.volumes.filter(v => v instanceof SphereVolume$1) }); } { // ATTRIBUTE PASS for (var _pointcloud2 of visiblePointClouds) { var _octreeSize = _pointcloud2.pcoGeometry.boundingBox.getSize(new Vector3()).x; var _material = originalMaterials.get(_pointcloud2); var _attributeMaterial = this.attributeMaterials.get(_pointcloud2); _attributeMaterial.size = _material.size; _attributeMaterial.minSize = _material.minSize; _attributeMaterial.maxSize = _material.maxSize; _attributeMaterial.pointSizeType = _material.pointSizeType; _attributeMaterial.activeAttributeName = _material.activeAttributeName; _attributeMaterial.visibleNodesTexture = _material.visibleNodesTexture; _attributeMaterial.weighted = true; _attributeMaterial.screenWidth = width; _attributeMaterial.screenHeight = height; _attributeMaterial.shape = PointShape$1.CIRCLE; _attributeMaterial.uniforms.visibleNodes.value = _material.visibleNodesTexture; _attributeMaterial.uniforms.octreeSize.value = _octreeSize; _attributeMaterial.spacing = _pointcloud2.pcoGeometry.spacing; // * Math.max(...pointcloud.scale.toArray()); _attributeMaterial.classification = _material.classification; _attributeMaterial.uniforms.classificationLUT.value.image.data = _material.uniforms.classificationLUT.value.image.data; _attributeMaterial.classificationTexture.needsUpdate = true; _attributeMaterial.uniforms.uFilterReturnNumberRange.value = _material.uniforms.uFilterReturnNumberRange.value; _attributeMaterial.uniforms.uFilterNumberOfReturnsRange.value = _material.uniforms.uFilterNumberOfReturnsRange.value; _attributeMaterial.uniforms.uFilterGPSTimeClipRange.value = _material.uniforms.uFilterGPSTimeClipRange.value; _attributeMaterial.uniforms.uFilterPointSourceIDClipRange.value = _material.uniforms.uFilterPointSourceIDClipRange.value; _attributeMaterial.elevationGradientRepeat = _material.elevationGradientRepeat; _attributeMaterial.elevationRange = _material.elevationRange; _attributeMaterial.gradient = _material.gradient; _attributeMaterial.matcap = _material.matcap; _attributeMaterial.intensityRange = _material.intensityRange; _attributeMaterial.intensityGamma = _material.intensityGamma; _attributeMaterial.intensityContrast = _material.intensityContrast; _attributeMaterial.intensityBrightness = _material.intensityBrightness; _attributeMaterial.rgbGamma = _material.rgbGamma; _attributeMaterial.rgbContrast = _material.rgbContrast; _attributeMaterial.rgbBrightness = _material.rgbBrightness; _attributeMaterial.weightRGB = _material.weightRGB; _attributeMaterial.weightIntensity = _material.weightIntensity; _attributeMaterial.weightElevation = _material.weightElevation; _attributeMaterial.weightRGB = _material.weightRGB; _attributeMaterial.weightClassification = _material.weightClassification; _attributeMaterial.weightReturnNumber = _material.weightReturnNumber; _attributeMaterial.weightSourceID = _material.weightSourceID; _attributeMaterial.color = _material.color; _attributeMaterial.clipTask = _material.clipTask; _attributeMaterial.clipMethod = _material.clipMethod; _attributeMaterial.setClipBoxes(_material.clipBoxes); _attributeMaterial.setClipPolygons(_material.clipPolygons); _pointcloud2.material = _attributeMaterial; } var gl = this.gl; viewer.renderer.setRenderTarget(null); viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtAttribute, { clipSpheres: viewer.scene.volumes.filter(v => v instanceof SphereVolume$1), //material: this.attributeMaterial, blendFunc: [gl.SRC_ALPHA, gl.ONE], //depthTest: false, depthWrite: false }); } for (var [_pointcloud3, _material2] of originalMaterials) { _pointcloud3.material = _material2; } viewer.renderer.setRenderTarget(null); if (viewer.background === "skybox") { viewer.renderer.setClearColor(0x000000, 0); viewer.renderer.clear(); viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation); viewer.skybox.camera.fov = viewer.scene.cameraP.fov; viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect; viewer.skybox.parent.rotation.x = 0; viewer.skybox.parent.updateMatrixWorld(); viewer.skybox.camera.updateProjectionMatrix(); viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera); } else if (viewer.background === 'gradient') { viewer.renderer.setClearColor(0x000000, 0); viewer.renderer.clear(); viewer.renderer.render(viewer.scene.sceneBG, viewer.scene.cameraBG); } else if (viewer.background === 'black') { viewer.renderer.setClearColor(0x000000, 1); viewer.renderer.clear(); } else if (viewer.background === 'white') { viewer.renderer.setClearColor(0xFFFFFF, 1); viewer.renderer.clear(); } else { viewer.renderer.setClearColor(0x000000, 0); viewer.renderer.clear(); } { // NORMALIZATION PASS var normalizationMaterial = this.useEDL ? this.normalizationEDLMaterial : this.normalizationMaterial; if (this.useEDL) { normalizationMaterial.uniforms.edlStrength.value = viewer.edlStrength; normalizationMaterial.uniforms.radius.value = viewer.edlRadius; normalizationMaterial.uniforms.screenWidth.value = width; normalizationMaterial.uniforms.screenHeight.value = height; normalizationMaterial.uniforms.uEDLMap.value = this.rtDepth.texture; } normalizationMaterial.uniforms.uWeightMap.value = this.rtAttribute.texture; normalizationMaterial.uniforms.uDepthMap.value = this.rtAttribute.depthTexture; Utils.screenPass.render(viewer.renderer, normalizationMaterial); } viewer.renderer.render(viewer.scene.scene, camera); viewer.dispatchEvent({ type: "render.pass.scene", viewer: viewer }); viewer.renderer.clearDepth(); viewer.transformationTool.update(); viewer.dispatchEvent({ type: "render.pass.perspective_overlay", viewer: viewer }); viewer.renderer.render(viewer.controls.sceneControls, camera); viewer.renderer.render(viewer.clippingTool.sceneVolume, camera); viewer.renderer.render(viewer.transformationTool.scene, camera); viewer.renderer.setViewport(width - viewer.navigationCube.width, height - viewer.navigationCube.width, viewer.navigationCube.width, viewer.navigationCube.width); viewer.renderer.render(viewer.navigationCube, viewer.navigationCube.camera); viewer.renderer.setViewport(0, 0, width, height); viewer.dispatchEvent({ type: "render.pass.end", viewer: viewer }); } } class LRUItem { constructor(node) { this.previous = null; this.next = null; this.node = node; } } /** * * @class A doubly-linked-list of the least recently used elements. */ class LRU { constructor() { //类似链表存储 // the least recently used item this.first = null; // the most recently used item this.last = null; // a list of all items in the lru list this.items = {}; //按node的id存储。(id为0的就是root,name='r') this.elements = 0; this.numPoints = 0; } size() { return this.elements; } contains(node) { return this.items[node.id] == null; } touch(node) { //链接node,并且永远放在最后. (每次updatePointClouds都要刷新一次链表) if (!node.loaded) { return; } var item; if (this.items[node.id] == null) { // add to list item = new LRUItem(node); item.previous = this.last; this.last = item; if (item.previous !== null) { item.previous.next = item; } this.items[node.id] = item; this.elements++; if (this.first === null) { this.first = item; } this.numPoints += node.numPoints; } else { // update in list item = this.items[node.id]; if (item.previous === null) { // handle touch on first element if (item.next !== null) { this.first = item.next; this.first.previous = null; item.previous = this.last; item.next = null; this.last = item; item.previous.next = item; } } else if (item.next === null) { // handle touch on last element } else { //从原来的位置挑出放最后 // handle touch on any other element item.previous.next = item.next; item.next.previous = item.previous; item.previous = this.last; item.next = null; this.last = item; item.previous.next = item; } } } //因为需要显示的都放末尾,所以不显示的部分都在前面,删除时从头删除。(但并不代表开头的一定是不显示的,所以如果第一个仍是显示的,它很可能是root,也就是name='r'的nodeGeo,删除它也就会删除全部) remove(node) { var lruItem = this.items[node.id]; if (lruItem) { if (this.elements === 1) { this.first = null; this.last = null; } else { if (!lruItem.previous) { this.first = lruItem.next; this.first.previous = null; } if (!lruItem.next) { this.last = lruItem.previous; this.last.next = null; } if (lruItem.previous && lruItem.next) { lruItem.previous.next = lruItem.next; lruItem.next.previous = lruItem.previous; } } delete this.items[node.id]; this.elements--; this.numPoints -= node.numPoints; } } getLRUItem() { if (this.first === null) { return null; } var lru = this.first; return lru.node; } toString() { var string = '{ '; var curr = this.first; while (curr !== null) { string += curr.node.id; if (curr.next !== null) { string += ', '; } curr = curr.next; } string += '}'; string += '(' + this.size() + ')'; return string; } freeMemory() { if (this.elements <= 1) { return; } while (this.numPoints > Potree.pointLoadLimit) { var element = this.first; var node = element.node; this.disposeDescendants(node); } } disposeDescendants(node) { var stack = []; stack.push(node); while (stack.length > 0) { var current = stack.pop(); // console.log(current); current.dispose(); //真正删除geometry等 this.remove(current); for (var key in current.children) { if (current.children.hasOwnProperty(key)) { var child = current.children[key]; if (child.loaded) { stack.push(current.children[key]); } } } } } } // // how to calculate the radius of a projected sphere in screen space // http://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space // http://stackoverflow.com/questions/3717226/radius-of-projected-sphere // class ExtendPointCloudMaterial extends PointCloudMaterial$1 { constructor() { var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(parameters); var getValid = (a, b) => { if (a !== undefined) { return a; } else { return b; } }; var maxSize = getValid(parameters.maxSize, 200.0); var orthoMaxSize = getValid(parameters.orthoMaxSize, 3.0); this._gradient = Gradients.ir; //Gradients.SPECTRAL;//海拔贴图种类,火灾也是这个 this.gradientTexture = ExtendPointCloudMaterial.generateGradientTexture(this._gradient); this.classificationName = null; delete this.uniforms.screenWidth; delete this.uniforms.screenHeight; delete this.uniforms.clipBoxes; delete this.uniforms.clipPolygons; delete this.uniforms.clipPolygonVCount; delete this.uniforms.clipPolygonVP; delete this.uniforms.clipBoxCount; //注意:这里修改了uniforms后,还需要在PotreeRender中手动传递到shader, like: gl.uniformMatrix4fv(.... Object.assign(this.uniforms, { resolution: { type: 'v2', value: new Vector2() }, maxSize: { type: "f", value: maxSize }, orthoMaxSize: { type: "f", value: orthoMaxSize }, gradient: { type: "t", value: this.gradientTexture }, clipBoxes_in: { type: "Matrix4fv", value: [] }, clipBoxes_out: { type: "Matrix4fv", value: [] }, clipBoxBig_in: { type: "Matrix4fv", value: [] }, boxes_highlight: { type: "Matrix4fv", value: [] }, progress: { type: "f", value: 0 }, easeInOutRatio: { type: "f", value: 0.3 }, pano0Map: { type: "t", value: null }, pano0Position: { type: "v3", value: new Vector3() }, pano0Matrix: { type: "m4", value: new Matrix4() }, pano1Map: { type: "t", value: null }, pano1Position: { type: "v3", value: new Vector3() }, pano1Matrix: { type: "m4", value: new Matrix4() }, prismList: { type: "Matrix3fv", value: null }, prismPoints: { type: "vec2fv", value: null }, baseHeightAreaMap: { type: 't', value: null }, baseHeightBoundZ: { type: 'vec2', value: new Vector2() }, baseHeightBoundXY: { type: 'vec4', value: new Vector4() }, temperRange: { //温度范围 type: 'vec2', value: new Vector2(math.getKelvinFromCelsius(99), math.getKelvinFromCelsius(1500)) //火场温度 } }); delete this.clipBoxes; this.clipBoxes_in = []; this.clipBoxes_out = []; this.highlightBoxes = []; this.prisms = []; var { vs, fs } = Common.changeShaderToWebgl2(Shaders['pointcloud_new.vs'], Shaders['pointcloud_new.fs'], 'selfBuild'); Shaders['pointcloud_new.vs'] = vs; Shaders['pointcloud_new.fs'] = fs; { var setClass = () => { this.classification = viewer.classifications; this.recomputeClassification(); }; viewer.addEventListener('classifications_changed', setClass); viewer.addEventListener('classification_visibility_changed', setClass); setClass(); } this.updateShaderSource(); } /* get classificationName(){ return this.classificationName_ } set classificationName(name){//add if(this.classificationName_ != name){ this.classificationName_ = name this.classification = ClassificationScheme[name] this.recomputeClassification(); } } */ updateShaderSource() { var vs = Shaders['pointcloud_new.vs']; //改 var fs = Shaders['pointcloud_new.fs']; //改 var definesString = this.getDefines(); var vsVersionIndex = vs.indexOf("#version "); var fsVersionIndex = fs.indexOf("#version "); if (vsVersionIndex >= 0) { vs = vs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { vs = "".concat(definesString, "\n").concat(vs); } if (fsVersionIndex >= 0) { fs = fs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { fs = "".concat(definesString, "\n").concat(fs); } this.vertexShader = vs; this.fragmentShader = fs; if (this.opacity === 1.0 && !this.useFilterByNormal) { //add useFilterByNormal this.blending = NoBlending; this.transparent = false; this.depthTest = true; this.depthWrite = true; this.depthFunc = LessEqualDepth; } else if ((this.opacity < 1.0 || this.useFilterByNormal) && !this.useEDL) { //add useFilterByNormal this.blending = AdditiveBlending; this.transparent = true; this.depthTest = false; this.depthWrite = true; this.depthFunc = AlwaysDepth; } if (this.weighted) { this.blending = AdditiveBlending; this.transparent = true; this.depthTest = true; this.depthWrite = false; } this.shaderNeedsUpdate = true; } getDefines() { var defines = []; if (this.pointSizeType === PointSizeType.FIXED) { defines.push('#define fixed_point_size'); } else if (this.pointSizeType === PointSizeType.ATTENUATED) { defines.push('#define attenuated_point_size'); } else if (this.pointSizeType === PointSizeType.ADAPTIVE) { defines.push('#define adaptive_point_size'); } if (!Features.EXT_DEPTH.isSupported(viewer.renderer.getContext()) && this.shape === PointShape$1.PARABOLOID) { this.shape = PointShape$1.SQUARE; //强行替换 } if (this.shape === PointShape$1.SQUARE) { defines.push('#define square_point_shape'); } else if (this.shape === PointShape$1.CIRCLE) { defines.push('#define circle_point_shape'); } else if (this.shape === PointShape$1.PARABOLOID) { defines.push('#define paraboloid_point_shape'); } //console.log('this.shape PARABOLOID', this.shape, this.shape === PointShape.PARABOLOID) if (this._useEDL || this.fakeEDL) { defines.push('#define use_edl'); } if (this.activeAttributeName) { var attributeName = this.activeAttributeName.replace(/[^a-zA-Z0-9]/g, '_'); defines.push("#define color_type_".concat(attributeName)); } if (this._treeType === TreeType.OCTREE) { defines.push('#define tree_type_octree'); } else if (this._treeType === TreeType.KDTREE) { defines.push('#define tree_type_kdtree'); } if (this.weighted) { defines.push('#define weighted_splats'); } for (var [key, value] of this.defines) { defines.push(value); } return defines.join("\n"); } get pointSizeType() { return this._pointSizeType; } set pointSizeType(value) { if (typeof value == 'string') value = PointSizeType[value]; super.pointSizeType = value; /* if (this._pointSizeType !== value) { this._pointSizeType = value; this.updateShaderSource(); //这句表明这个属性频繁更改会卡顿 this.dispatchEvent({ type: 'point_size_type_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); } */ } get color() { return this.uniforms.uColor.value; } set color(value) { //改 if (value == this.color_) return; var color = value; //if (!this.uniforms.uColor.value.equals(value)) { if (typeof value == 'string') { var colorArr = Potree.config.colors[value]; if (!colorArr) { //console.warn('没找到该颜色值'+ value) } else { color = new Color().fromArray(colorArr).multiplyScalar(1 / 255); } } this.uniforms.uColor.value.set(color); //this.uniforms.uColor.value.copy(value); this.dispatchEvent({ type: 'color_changed', target: this }); this.dispatchEvent({ type: 'material_property_changed', target: this }); //} this.color_ = value; //记录下str } ////////////////////////add setProjectedPanos(pano0, pano1, progressValue, easeInOutRatio) { //this.uniforms.usePanoMap.value = 1 this.usePanoMap = true; progressValue != void 0 && (this.uniforms.progress.value = progressValue); //pano0.ensureSkyboxReadyForRender(); this.uniforms.pano0Map.value = pano0.getSkyboxTexture(); this.uniforms.pano0Position.value.copy(pano0.position); this.uniforms.pano0Matrix.value.copy(pano0.panoMatrix); //pano1.ensureSkyboxReadyForRender(); this.uniforms.easeInOutRatio.value = easeInOutRatio || 0; //之前做点云和全景混合时加的,为了让点云颜色柔和切换到全景颜色。如不混合就0 this.uniforms.pano1Map.value = pano1.getSkyboxTexture(); this.uniforms.pano1Position.value.copy(pano1.position); this.uniforms.pano1Matrix.value.copy(pano1.panoMatrix); //this.updateShaderSource() //this.needsUpdate = true; } stopProjectedPanos() { //this.uniforms.usePanoMap.value = 0 this.usePanoMap = false; } setClipBoxes(bigClipInBox, clipBoxes_in, clipBoxes_out, highlightBoxes) { var prismPolygons = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; if (!clipBoxes_in || !clipBoxes_out) { return; } this.uniforms.clipBoxBig_in.value = bigClipInBox && bigClipInBox.inverse; this.uniforms.clipBoxes_in.value = new Float32Array(clipBoxes_in.length * 16); this.uniforms.clipBoxes_out.value = new Float32Array(clipBoxes_out.length * 16); this.uniforms.boxes_highlight.value = new Float32Array(highlightBoxes.length * 16); for (var i = 0; i < clipBoxes_in.length; i++) { var box = clipBoxes_in[i]; this.uniforms.clipBoxes_in.value.set(box.inverse.elements, 16 * i); } for (var _i = 0; _i < clipBoxes_out.length; _i++) { var _box = clipBoxes_out[_i]; this.uniforms.clipBoxes_out.value.set(_box.inverse.elements, 16 * _i); } for (var _i2 = 0; _i2 < highlightBoxes.length; _i2++) { var _box2 = highlightBoxes[_i2]; this.uniforms.boxes_highlight.value.set(_box2.inverse.elements, 16 * _i2); } /* for (let i = 0; i < this.uniforms.clipBoxes.value.length; i++) {?? if (Number.isNaN(this.uniforms.clipBoxes.value[i])) { this.uniforms.clipBoxes.value[i] = Infinity; } } */ if (prismPolygons.length) { var pointsCount = prismPolygons.pointsCount = prismPolygons.reduce((w, c) => { return w + c.points.length; }, 0); this.uniforms.prismList.value = new Float32Array(9 * prismPolygons.length); this.uniforms.prismPoints.value = new Float32Array(2 * pointsCount); prismPolygons.maxPointsCount = 0; //单个prism最大点个数 var pointIndex = 0; for (var _i3 = 0; _i3 < prismPolygons.length; _i3++) { var bound = prismPolygons[_i3].prismBound; var z = prismPolygons[_i3].horizonZ; //z = Potree.browser.urlHasValue('zmin',true) || zs[0] this.uniforms.prismList.value.set([bound.min.z, z, bound.max.z, bound.min.x, bound.max.x, bound.min.y, bound.max.y, prismPolygons[_i3].points.length], 9 * _i3); for (var j = 0; j < prismPolygons[_i3].points.length; j++) { this.uniforms.prismPoints.value.set([prismPolygons[_i3].points[j].x, prismPolygons[_i3].points[j].y], 2 * j + pointIndex); } pointIndex += 2 * prismPolygons[_i3].points.length; prismPolygons.maxPointsCount = Math.max(prismPolygons.maxPointsCount, prismPolygons[_i3].points.length); } } var doUpdate = this.clipBoxes_in.length !== clipBoxes_in.length || this.clipBoxes_out.length != clipBoxes_out.length || this.highlightBoxes.length !== highlightBoxes.length || !this.bigClipInBox != !bigClipInBox || this.prisms.length != prismPolygons.length || this.prisms.maxPointsCount != prismPolygons.maxPointsCount; //this.clipBoxes = clipBoxes; if (doUpdate) { this.shaderNeedsUpdate = true; viewer.dispatchEvent('content_changed'); } this.bigClipInBox = bigClipInBox; this.clipBoxes_in = clipBoxes_in; this.clipBoxes_out = clipBoxes_out; this.highlightBoxes = highlightBoxes; this.prisms = prismPolygons; } setTempRange(min, max) { //温度范围 this.uniforms.temperRange.value.set(math.getKelvinFromCelsius(min), math.getKelvinFromCelsius(max)); } } var defaultColor = new Color(1, 1, 1); //config.applicationName == "zhiHouse" ? Colors.zhiBlue : Colors.lightGreen; function dealPosArr(points) { //识别是否每个点都不一样,把连续点变为不连续的片段连接 var add = points => { var points2 = [], len = points.length; for (var i = 0; i < len - 1; i++) { points2.push(points[i], points[i + 1]); } return points2; }; if (points[0] && points[0] instanceof Array) { //多组,每组间连续,但组之间不连续 var points2 = []; points.forEach(ps => points2.push(...add(ps))); return points2; } else if (points.length > 2 && !points[2].equals(points[1])) { return add(points); } else return points; } var LineDraw = { createLine: function createLine(posArr) { var o = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; //多段普通线 (第二个点和第三个点之间是没有线段的, 所以不用在意线段顺序) var mat; if (o.mat) { mat = o.mat; } else { var prop = Object.assign({ lineWidth: o.lineWidth || 1, //windows无效。 似乎mac/ios上粗细有效 ? color: o.color || defaultColor }, o); if (o.deshed) { prop.dashSize = o.dashSize || 0.1, prop.gapSize = o.gapSize || 0.1; } mat = new THREE$1[o.deshed ? "LineDashedMaterial" : "LineBasicMaterial"](prop); } var line = new LineSegments(new BufferGeometry(), mat); line.renderOrder = o.renderOrder || Potree.config.renderOrders.line; this.moveLine(line, posArr); return line; }, moveLine: function moveLine(line, posArr) { //if(posArr.length == 0)return if (!line.uncontinuous || posArr[0] && posArr[0] instanceof Array) posArr = dealPosArr(posArr); var position = []; posArr.forEach(e => position.push(e.x, e.y, e.z)); line.geometry.setAttribute('position', new Float32BufferAttribute( /* new Float32Array( */position /* ) */, 3)); line.geometry.attributes.position.needsUpdate = true; line.geometry.computeBoundingSphere(); if (line.material instanceof LineDashedMaterial) { line.computeLineDistances(); //line.geometry.attributes.lineDistance.needsUpdate = true; line.geometry.verticesNeedUpdate = true; //没用 } }, createFatLineMat: function createFatLineMat(o) { var supportExtDepth = !!Features.EXT_DEPTH.isSupported(); var params = $.extend({}, { //默认 lineWidth: 5, color: 0xffffff, transparent: true, depthWrite: false, depthTest: false, dashSize: 0.1, gapSize: 0.1 }, o, { //修正覆盖: dashed: o.dashWithDepth ? supportExtDepth && !!o.dashed : !!o.dashed, dashWithDepth: !!o.dashWithDepth, //只在被遮住的部分显示虚线 useDepth: !!o.useDepth, supportExtDepth }); var mat = new LineMaterial(params); //if(o.dashed)(mat.defines.USE_DASH = "") return mat; }, /* 创建可以改变粗细的线。 */ createFatLine: function createFatLine(posArr, o) { var geometry = new LineGeometry(); geometry.setColors(o.color || [1, 1, 1]); var matLine = o.mat || this.createFatLineMat(o); var line = new Line2(geometry, matLine); //line.computeLineDistances(); line.uncontinuous = o.uncontinuous; //线不连续,由线段组成 line.scale.set(1, 1, 1); line.renderOrder = Potree.config.renderOrders.line; this.moveFatLine(line, posArr); return line; }, moveFatLine: function moveFatLine(line, posArr) { var geometry = line.geometry; var positions = []; if (!line.uncontinuous || posArr[0] && posArr[0] instanceof Array) posArr = dealPosArr(posArr); posArr.forEach(e => { positions.push(...e.toArray()); }); if (!geometry) { geometry = line.geometry = new LineGeometry(); } if (geometry.attributes.instanceEnd && geometry.attributes.instanceEnd.data.array.length != positions.length) { //positions个数改变会有部分显示不出来,所以重建 geometry.dispose(); geometry = new LineGeometry(); line.geometry = geometry; } geometry.setPositions(positions); if (line.material.defines.USE_DASH != void 0) { //line.geometry.verticesNeedUpdate = true; //没用 line.geometry.computeBoundingSphere(); //for raycaster line.computeLineDistances(); } }, updateLine: function updateLine(line, posArr) { if (line instanceof Line2) { LineDraw.moveFatLine(line, posArr); } else { LineDraw.moveLine(line, posArr); } }, /* 为line创建用于检测鼠标的透明mesh,实际是个1-2段圆台。 由于近大远小的原因,假设没有透视畸变、创建的是等粗的圆柱的话, 所看到的线上每个位置的粗细应该和距离成反比。所以将圆柱改为根据距离线性渐变其截面半径的圆台,在最近点(相机到线的垂足)最细。如果最近点在线段上,则分成两段圆台,否则一段。 */ createBoldLine: function createBoldLine(points, o) { o = o || {}; var cylinder = o && o.cylinder; var CD = points[1].clone().sub(points[0]); var rotate = function rotate() { //根据端点旋转好模型 cylinder.lastVector = CD; //记录本次的端点向量 var AB = new Vector3(0, -1, 0); var axisVec = AB.clone().cross(CD).normalize(); //得到垂直于它们的向量,也就是旋转轴 var rotationAngle = AB.angleTo(CD); cylinder.quaternion.setFromAxisAngle(axisVec, rotationAngle); }; if (o && o.type == "init") { cylinder = new Mesh(); cylinder.material = o.mat; if (CD.length() == 0) return cylinder; rotate(); } if (CD.length() == 0) return cylinder; if (o.type != "update") { var CDcenter = points[0].clone().add(points[1]).multiplyScalar(.5); cylinder.position.copy(CDcenter); if (!cylinder.lastVector || o.type == "moveAndRotate") rotate();else if (cylinder.lastVector && CD.angleTo(cylinder.lastVector) > 0) rotate(); //线方向改了or线反向了 重新旋转一下模型 if (config.isEdit && !objects.mainDesign.editing) return cylinder; //节省初始加载时间? } //为了保证线段任何地方的可检测点击范围看起来一样大,更新圆台的结构(但是在镜头边缘会比中心看起来大) var height = points[0].distanceTo(points[1]); var standPos = o && o.standPos || objects.player.position; var k = config.isMobile ? 20 : 40; var dis1 = points[0].distanceTo(standPos); var dis2 = points[1].distanceTo(standPos); var foot = math.getFootPoint(standPos, points[0], points[1]); //垂足 if (o.constantBold || objects.player.mode != "panorama") { var width = 0.1; //0.08; var pts = [new Vector2(width, height / 2), new Vector2(width, -height / 2)]; } else if (foot.clone().sub(points[0]).dot(foot.clone().sub(points[1])) > 0) { //foot不在线段上 var pts = [new Vector2(dis1 / k, height / 2), new Vector2(dis2 / k, -height / 2)]; } else { //在线段上的话,要在垂足这加一个节点,因它距离站位最近,而两端较远 var dis3 = foot.distanceTo(standPos); var len = foot.distanceTo(points[0]); var pts = [new Vector2(dis1 / k, height / 2), new Vector2(dis3 / k, height / 2 - len), new Vector2(dis2 / k, -height / 2)]; } cylinder.geometry && cylinder.geometry.dispose(); //若不删除会占用内存 cylinder.geometry = new LatheBufferGeometry(pts, 4 /* Math.min(dis1,dis2)<10?4:3 */); cylinder.renderOrder = 2; return cylinder; }, updateBoldLine: function updateBoldLine(cylinder, points, type, standPos, constantBold) { this.createBoldLine(points, { type: type, cylinder: cylinder, standPos: standPos, constantBold }); //type:move:平移 会改长短 , type:update根据距离和角度更新 不改长短 } }; var MeshDraw = { getShape: function getShape(shapes, holes) { //不一定闭合 暂时所有shapes共享holes。如果要单独的话, shapes改为[{shape:[],holes:[]},{}]的形式 if (shapes[0] && !(shapes[0] instanceof Array)) { //仅是一个shape的点 shapes = [shapes]; } var holesArr = []; if (holes) { //挖空 holes.forEach(points => { var holePath = new Path(); holePath.moveTo(points[0].x, points[0].y); for (var i = 1, len = points.length; i < len; i++) { holePath.lineTo(points[i].x, points[i].y); } holesArr.push(holePath); }); } var shapesArr = shapes.map(points => { var shape = new Shape(); shape.moveTo(points[0].x, points[0].y); for (var i = 1, len = points.length; i < len; i++) { shape.lineTo(points[i].x, points[i].y); } shape.holes.push(...holesArr); shape.dontClose = points.dontClose; //add 有的shape不需要闭合 return shape; }); return shapesArr; }, /* getShape:function(shapes, holes){ var shape = new THREE.Shape(); if(shapes[0] && !(shapes[0] instanceof Array) ){//仅是一个shape的点 shapes = [shapes] } shapes.forEach((points)=>{ shape.moveTo( points[0].x, points[0].y ); for(var i=1,len=points.length; i{ var holePath = new THREE.Path() holePath.moveTo( points[0].x, points[0].y ) for(var i=1,len=points.length; i 2 && arguments[2] !== undefined ? arguments[2] : 0.03; var UtoTMapArr = arguments.length > 3 ? arguments[3] : undefined; //减少点数(拐弯的部分紧凑些,直线部分宽松些): var count = points.length; var newUtoTMapArr = []; var newPoints = []; var pointIndexs = []; var lastVec; var startTime = performance.now(); /* if(UtoTMapArr){ for(let n=1;ne>= n / (oldCount-1) ) ) } } */ //console.log('cost dur:', performance.now() - startTime ) var nextUtoTIndex = 1; for (var i = 0; i < count; i++) { var point = points[i]; var last = points[i - 1]; var next = points[i + 1]; if (i == 0 || i == count - 1) { newPoints.push(point); //直接加入 UtoTMapArr && newUtoTMapArr.push(i == 0 ? 0 : 1); } else { var curVec = new Vector3().subVectors(next, point); if (!lastVec) lastVec = curVec; if (i > 1) { // 和上一个加入点的vec之间的夹角如果过大就加入 var reachNextUToT = void 0; //找出新点中对应原先控制点的index,这些点必须加入拐点,否则会出现控制点偏移path(当它所在部分接近直线时) while (UtoTMapArr[i] > nextUtoTIndex / (oldCount - 1)) { //可能多个控制点对应一个点,当控制点很近时 reachNextUToT = true; nextUtoTIndex++; } if ( /* pointIndexs.includes(i) || */reachNextUToT || curVec.angleTo(lastVec) > minRad) { //最小角度 (注意原始点不能太稀疏) newPoints.push(point); UtoTMapArr && newUtoTMapArr.push(UtoTMapArr[i]); lastVec = curVec; } } } } return { newUtoTMapArr, newPoints }; }, getExtrudeGeo: function getExtrudeGeo(shapes, holes) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : { openEnded: false, shapeDontClose: false }; //获得挤出棱柱,可以选择传递height,或者extrudePath var shape = this.getShape(shapes, holes); //points是横截面 [vector2,...] if (options.extrudePath) { // 路径 :[vector3,...] var length = options.extrudePath.reduce((total, currentValue, currentIndex, arr) => { if (currentIndex == 0) return 0; return total + currentValue.distanceTo(arr[currentIndex - 1]); }, 0); //options.extrudePath = new THREE.CatmullRomCurve3(options.extrudePath) if (options.extrudePath.length == 2) { options.tension = 0; //否则一端扭曲 options.steps = 1; } { //去掉重复的点 var path = []; var minDis = options.dontSmooth ? 0 : 0.2; //CatmullRomCurve3 经常扭曲,如果两个点靠得很近可能会扭曲,这里去除靠的太近的点。但去除后依旧会出现一定扭曲. options.extrudePath.forEach((p, i) => { if (i == 0 || i == options.extrudePath.length - 1) return path.push(p); //首尾直接加入 var last = path[path.length - 1]; //和上一个比 var dis = last.distanceTo(p); if (dis <= minDis) { console.log("\u7B2C".concat(i, "\u4E2A\u70B9(").concat(p.toArray(), ")\u56E0\u4E3A\u548C\u4E0A\u4E00\u4E2A\u6570\u636E(").concat(last.toArray(), ")\u592A\u63A5\u8FD1(dis:").concat(dis, ")\u6240\u4EE5\u5220\u9664")); } else if (i == options.extrudePath.length - 2) { //因为最后一个必定加入,所以倒数第二个还也不能太靠近最后一个 last = options.extrudePath[options.extrudePath.length - 1]; //和下一个(最后一个比) if (dis <= minDis) { console.log("\u7B2C".concat(i, "\u4E2A\u70B9(").concat(p.toArray(), ")\u56E0\u4E3A\u548C\u4E0B\u4E00\u4E2A\u6570\u636E(").concat(last.toArray(), ")\u592A\u63A5\u8FD1(dis:").concat(dis, ")\u6240\u4EE5\u5220\u9664")); } else { path.push(p); } } else { path.push(p); } }); options.extrudePath = path; } if (!options.dontSmooth) { //平滑连续的曲线(但经常会有扭曲的问题,tension:0能缓解, 另外shape和path都最好在原点附近,也就是点需减去bound.min ) options.extrudePath = new CatmullRomCurve3(options.extrudePath, options.closed, 'catmullrom' /* 'centripetal' */, options.tension); //tension:张力, 越大弯曲越大。 随着长度增长,该值需要减小,否则会扭曲 if (options.lessPoints !== false) { //曲线但压缩直线部分点数量 options.extrudePath.UtoTMapArr = []; //用于存储 getSpacedPoints得到的点对应points的百分比对应 var count = Math.max(2, Math.round(length * (options.lessSpace || 200))); //为了防止有大拐弯才设置这么高 var points = options.extrudePath.getSpacedPoints(count - 1); //拆分为更密集的点 var result = this.lessCurvePoints(points, options.extrudePath.points.length, options.minRad, options.extrudePath.UtoTMapArr); //传UtoTMapArr的话点太多了卡住了 //options.extrudePath = points options.extrudePath = result.newPoints; options.dontSmooth = true; } } if (options.dontSmooth) { var curvePath = new CurvePath(); //通用的曲线路径对象,它可以包含直线段和曲线段。在这里只做折线 curvePath.points = options.extrudePath; //add for (var i = 0; i < options.extrudePath.length - 1; i++) { var curve3 = new LineCurve3(options.extrudePath[i], options.extrudePath[i + 1]); //添加线段 curvePath.add(curve3); } options.steps = options.extrudePath.length - 1; options.extrudePath = curvePath; options.tension = 0; //已修改过three,原本会平分细分,现在dontSmooth时会直接按照控制点来分段 } } var extrudeSettings = $.extend(options, { steps: options.steps != void 0 ? options.steps : options.extrudePath ? Math.round(length / (options.spaceDis || 0.2)) : 1, //分成几段 spaceDis每段长度 bevelEnabled: false //不加的话,height为0时会有圆弧高度 //openEnded默认false }); var geometry = new ExtrudeBufferGeometry(shape, extrudeSettings); //修改了three.js文件, buildLidFaces处,创建顶底面加了选项,可以选择开口。 return geometry; /* tension = 0:曲线会变成一条直线,没有弯曲。 tension = 0.5:曲线会经过所有控制点,并保持自然的弯曲。 tension > 0.5:曲线会更平滑,远离控制点之间的路径。 tension < 0.5:曲线会更贴近控制点之间的路径,弯曲更明显。 */ }, getUnPosPlaneGeo: function () { //获取还没有赋值位置的plane geometry var e = new Uint16Array([0, 1, 2, 0, 2, 3]) // , t = new Float32Array([-.5, -.5, 0, .5, -.5, 0, .5, .5, 0, -.5, .5, 0]) , i = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]), g = new BufferGeometry(); g.setIndex(new BufferAttribute(e, 1)), //g.addAttribute("position", new n.BufferAttribute(t, 3)), g.setAttribute("uv", new BufferAttribute(i, 2)); return function () { return g; }; }(), getPlaneGeo: function getPlaneGeo(A, B, C, D) { var geo = this.getUnPosPlaneGeo().clone(); var pos = [A.x, A.y, A.z, B.x, B.y, B.z, C.x, C.y, C.z, D.x, D.y, D.z]; //geo.addAttribute("position", new THREE.BufferAttribute(pos, 3)) geo.setAttribute('position', new Float32BufferAttribute(pos, 3)); geo.computeVertexNormals(); geo.computeBoundingSphere(); //for raycaster return geo; }, drawPlane: function drawPlane(A, B, C, D, material) { var wall = new Mesh(this.getPlaneGeo(A, B, C, D), material); return wall; }, movePlane: function movePlane(mesh, A, B, C, D) { var pos = new Float32Array([A.x, A.y, A.z, B.x, B.y, B.z, C.x, C.y, C.z, D.x, D.y, D.z]); mesh.geometry.addAttribute("position", new BufferAttribute(pos, 3)); mesh.geometry.computeBoundingSphere(); //for checkIntersect }, createGeometry: function createGeometry(posArr, faceArr, uvArr, normalArr) { //创建复杂mesh. faceArr:[[0,1,2],[0,2,3]] var geo = new BufferGeometry(); var positions = []; posArr.forEach(p => positions.push(p.x, p.y, p.z)); geo.setAttribute('position', new Float32BufferAttribute(positions, 3)); if (faceArr) { var indice = []; faceArr.forEach(f => indice.push(...f)); geo.setIndex(indice); // auto set Uint16BufferAttribute or Uint32BufferAttribute } if (uvArr) { var uvs = []; uvArr.forEach(uv => uvs.push(uv.x, uv.y)); geo.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } if (normalArr) { var normals = []; normalArr.forEach(n => normals.push(n.x, n.y, n.z)); geo.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } /* geo.computeVertexNormals() geo.computeBoundingSphere() //for raycaster */ return geo; }, updateGeometry: function updateGeometry(geo, posArr, faceArr, uvArr, normalArr) { //创建复杂mesh. faceArr:[[0,1,2],[0,2,3]] var positions = []; posArr.forEach(p => positions.push(p.x, p.y, p.z)); geo.setAttribute('position', new Float32BufferAttribute(positions, 3)); geo.attributes.position.needsUpdate = true; if (faceArr) { var indice = []; faceArr.forEach(f => indice.push(...f)); geo.setIndex(indice); // auto set Uint16BufferAttribute or Uint32BufferAttribute } if (uvArr) { var uvs = []; uvArr.forEach(uv => uvs.push(uv.x, uv.y)); geo.setAttribute("uv", new Float32BufferAttribute(uvs, 2)); } if (normalArr) { var normals = []; normalArr.forEach(n => normals.push(n.x, n.y, n.z)); geo.setAttribute("normal", new Float32BufferAttribute(normals, 3)); } /* geo.computeVertexNormals() */ geo.computeBoundingSphere(); //for raycaster and visi return geo; } }; class DepthBasicMaterial extends ShaderMaterial { constructor() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var { width, height } = viewer.renderer.getSize(new Vector2()); var uniforms = { resolution: { type: 'v2', value: new Vector2(width, height) }, viewportOffset: { type: 'v2', value: new Vector2(0, 0) }, //left, top //uUseOrthographicCamera:{ type: "b", value: false }, nearPlane: { type: 'f', value: 0.1 }, farPlane: { type: 'f', value: 10000 }, depthTexture: { type: 't', value: null }, opacity: { type: 'f', value: 1 }, map: { type: 't', value: o.map }, mapColor: { type: 'v3', value: o.mapColor ? new Color(o.mapColor) : new Color("#ffffff") }, baseColor: { type: 'v3', value: o.color ? new Color(o.color) : new Color("#ffffff") }, backColor: { type: 'v3', value: o.backColor ? new Color(o.backColor) : new Color("#ddd") }, clipDistance: { type: 'f', value: o.clipDistance || 4 }, //消失距离 occlusionDistance: { type: 'f', value: o.occlusionDistance || 1 }, //变为backColor距离 maxClipFactor: { type: 'f', value: o.maxClipFactor || 1 }, //0-1 maxOcclusionFactor: { type: 'f', value: o.maxOcclusionFactor || 1 }, //0-1 //mapScale: { type: 'f', value: o.mapScale || 1 }, //0-1 startClipDis: { type: 'f', value: o.startClipDis || 0 }, //开始逐渐消失的距离 startOcclusDis: { type: 'f', value: o.startOcclusDis || 0 }, //开始逐渐褪色的距离 fadeFar: { value: 10 } //渐变消失 }; var { vs, fs } = Common.changeShaderToWebgl2(Shaders['depthBasic.vs'], Shaders['depthBasic.fs'], 'ShaderMaterial'); super({ uniforms, vertexShader: vs, fragmentShader: fs, depthWrite: false, depthTest: false, transparent: o.transparent == void 0 ? true : o.transparent }); this.events = { setSize: e => { //如果出现横条状的异常,往往是viewportOffset出错 //地图不需要 if (!this.realUseDepth || !e.viewport) return; var viewport = e.viewport; var viewportOffset = viewport.offset || new Vector2(); this.uniforms.resolution.value.copy(viewport.resolution2); //2023.6.12突然发现ratio>1的用resolution不对,得用2才对。但是之前明明记得不是这样 this.uniforms.viewportOffset.value.copy(viewportOffset); }, render: e => { //before render 如果有大于两个viewport的话,不同viewport用不同的depthTex this.updateDepthParams(e); } /* cameraChange:(e)=>{ if(e.changeInfo.projectionChanged){//resize时也会触发。虽然保守起见的话加上resize比较好//所以当时为何不用resize //console.log('projectionChanged') this.events.setSize(e) } } */ }; //-----其他---- /*this.autoDepthTest = o.autoDepthTest if(o.opacity != void 0){ this.opacity = o.opacity } this.useDepth = o.useDepth this.fadeFar = o.fadeFar this.map = o.map */ this.setValues(o); } get useDepth() { return this.useDepth_; } set useDepth(value) { value = value && Features.EXT_DEPTH.isSupported(); //如果不支持 EXT_DEPTH 的话会失效 if (this.useDepth_ != value) { this.setRealDepth(value); this.useDepth_ = value; if (value) { viewer.addEventListener("render.begin", this.events.render); //viewer.addEventListener('camera_changed', this.events.cameraChange) viewer.addEventListener('resize', this.events.setSize); this.updateDepthParams(); } else { viewer.removeEventListener("render.begin", this.events.render); viewer.removeEventListener('resize', this.events.setSize); } } } setRealDepth(useDepth, viewport) { //确实使用到depthTex if (this.realUseDepth != useDepth) { if (useDepth) { this.defines.useDepth = ''; } else { delete this.defines.useDepth; } this.realUseDepth = useDepth; if (this.autoDepthTest) this.depthWrite = this.depthTest = !useDepth; //如果useDepth = false,使用原始的depthTest this.needsUpdate = true; if (!viewport) viewport = viewer.mainViewport; //暂时这么设置 useDepth && this.events.setSize({ viewport }); } } get map() { return this.uniforms.map.value; } set map(map) { var oldDefines = Common.CloneObject(this.defines); this.uniforms.map.value = map; if (map) { this.defines.use_map = ''; if (map.repeat.x != 1 || map.repeat.y != 1) { this.setUV(); } } else { delete this.defines.use_map; delete this.defines.UV_Transform; } Common.ifSame(oldDefines, this.defines) || (this.needsUpdate = true); } get opacity() { return this.uniforms.opacity.value; } set opacity(o) { this.uniforms && o != void 0 && (this.uniforms.opacity.value = o); } get color() { return this.uniforms.baseColor.value; } set color(c) { this.uniforms && this.uniforms.baseColor.value.set(c); } set fadeFar(far) { var needsUpdate = !this.fadeFar != !far; //null为全部范围可见 //console.log('fadeFar needsUpdate', needsUpdate) if (far) { this.defines.FadeFar = true; this.uniforms.fadeFar.value = far; } else { delete this.defines.FadeFar; } needsUpdate && (this.needsUpdate = true); } get fadeFar() { return 'FadeFar' in this.defines && this.uniforms.fadeFar.value; } copy(source) { super.copy(source); this.useDepth = source.useDepth; this.map = source.map; this.fadeFar = source.fadeFar; return this; } updateDepthParams() { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //主要用于点云遮住mesh var viewport = e.viewport || viewer.mainViewport; var camera = viewport.camera; var hasDepth = this.useDepth && camera.isPerspectiveCamera && (Potree.settings.pointEnableRT || Potree.settings.displayMode == 'showPanos' || viewer.useEDL); this.setRealDepth(hasDepth, viewport); if (hasDepth) { this.uniforms.depthTexture.value = viewer.getPRenderer().getRtEDL(viewport).depthTexture; //其实只赋值一次就行 } //hasDepth or FadeFar: this.uniforms.nearPlane.value = camera.near; this.uniforms.farPlane.value = camera.far; //this.uniforms.uUseOrthographicCamera.value = !camera.isPerspectiveCamera } setUV() { if (!this.map) return; this.map.updateMatrix(); this.uniforms.uvTransform = { value: this.map.matrix.clone() }; this.defines.UV_Transform = true; } } var geo = new PlaneBufferGeometry(1, 1); class Sprite$2 extends Mesh { constructor() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(geo, options.mat || new DepthBasicMaterial(options)); /* ({map:options.map, useDepth:options.useDepth})) */ this.root = options.root || this; this.renderOrder = options.renderOrder != void 0 ? options.renderOrder : 4; this.pickOrder = options.pickOrder || 0; this.sizeInfo = options.sizeInfo; this.dontFixOrient = options.dontFixOrient; this.options = options; //this.position.y = options.disToLine || 0 //离线距离 options.transform2D && (this.position.x = options.transform2D.x, this.position.y = options.transform2D.y); //偏移 this.matrixAutoUpdate = false; this.matrixMap = new Map(); if (this.root != this) { this.matrixMapRoot = new Map(); this.root.matrixAutoUpdate = false; } this.visiMap = new Map(); this.name = options.name || 'sprite'; this.useViewport = null; this.viewports = options.viewports; //指定更新的viewports this.visible_ = true; var clear = e => { this.matrixMap.clear(); //清空后在所有viewport上都必须更新才能渲染 //this.needsUpdate = true }; viewer.mapViewer && viewer.mapViewer.addEventListener("camera_changed", clear); viewer.addEventListener("camera_changed", clear); /* if(viewer.viewports.length == 1){//直接更新。如果有多个不在这更新,在"render.begin" this.update(e) } */ var applyMatrix = e => { this.applyMatrix(e); }; viewer.addEventListener("raycaster", applyMatrix); //before render viewer.addEventListener("render.begin", applyMatrix); //before render viewer.addEventListener("render.begin2", applyMatrix); viewer.addEventListener("cameraSetLayers", applyMatrix); this.addEventListener('dispose', () => { viewer.mapViewer && viewer.mapViewer.removeEventListener("camera_changed", clear); viewer.removeEventListener("camera_changed", clear); viewer.removeEventListener("raycaster", applyMatrix); //before render viewer.removeEventListener("render.begin", applyMatrix); viewer.removeEventListener("render.begin2", applyMatrix); }); } /* set visible(v){//注释原因renderoverlay时会反复隐藏显示。没必要加,显示后因matrixmap是空的会自动update let oldV = this.visible_ this.visible_ = v if(v && !oldV){ this.matrixMap && this.matrixMap.clear() //this.update() //update内有unableCompute会无限回调 } } get visible(){ return this.visible_ } */ realVisible(viewport, interactables /* , raycaster */) { if (interactables) { if (!interactables.some(object => { //interactables中是否能找到this var finded; object.traverse(object => { if (object == this.root) { finded = true; return { stopContinue: true }; } }); return finded; })) return; } /* if(interactables && viewport.name == 'mapViewport'){ console.log(this) } */ if (!( /* raycaster || */viewport.camera).layers.test(this.layers)) { //如地图上一般不可见测量线 return false; } if (!this.visible && this.unvisibleReasons && this.unvisibleReasons.some(e => e.reason != 'unableCompute')) { return false; } var v = true; var parent = this.root; var lastParent = this; while (parent) { if (parent.visible === false) { v = false; break; } lastParent = parent; parent = parent.parent; } if (v && !(lastParent instanceof Scene)) { //已被删除 v = false; } /* if(!this.latestRealVisi && v){//变为可见后先update this.latestRealVisi = true setTimeout(()=>{ this.update() },1)//延迟 防止无限调用 return false } this.latestRealVisi = v */ return v; } waitUpdate() { this.matrixMap.clear(); //清空后在所有viewport上都必须更新才能渲染 //viewer.dispatchEvent('content_changed') } update() { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!e.viewport) { var viewports = this.viewports || viewer.viewports; if (!viewports) return; viewports.forEach(view => { this.update({ viewport: view }); }); return; } if (!this.root || !this.realVisible(e.viewport, e.interactables) /* this.visible */) return; if (this.viewports && !this.viewports.includes(e.viewport)) return; if (e.viewport.name == 'magnifier') return; var camera = e.viewport.camera; //rotation if (!this.dontFixOrient) { //orthoCamera一般要加dontFixOrient var orient2dAngle; if (this.root.lineDir) { this.root.updateMatrix(); //先更新,getWorldPosition才能得到正确的 this.root.updateMatrixWorld(true); var center = this.root.getWorldPosition(new Vector3()); //由于两个端点容易在屏幕外,所以使用center和center加dir var setVisi = state => { this.visiMap.set(e.viewport, state); Potree.Utils.updateVisible(this, 'unableCompute', !!state); //赋值原先的,否则之后每次render都触发update: this.matrixMap.set(e.viewport, this.matrix.clone()); this.matrixMapRoot.set(e.viewport, this.root.matrix.clone()); }; var renderer = e.viewer ? e.viewer.renderer : e.renderer; var r1 = Potree.Utils.getPos2d(center, e.viewport, renderer.domElement.parentElement, renderer); //let r1 = Potree.Utils.getPos2d(center, e.viewport, viewer.renderArea, viewer.renderer); if (!r1.trueSide) return setVisi(false); // 但这句会使realVisible为false从而无法更新//console.error('!r1.trueSide') //中心点如果在背面直接不渲染了 var r2, point2; var p2State = '', len = 1, p2StateHistory = []; while (p2State != 'got' && p2StateHistory.length < 10) { point2 = center.clone().add(this.root.lineDir.clone().multiplyScalar(len)); r2 = Potree.Utils.getPos2d(point2, e.viewport, renderer.domElement.parentElement, renderer); if (!r2.trueSide) { //很少遇到点2在背面的 if (!p2StateHistory.includes('tooLong-reverse')) { p2State = 'tooLong-reverse'; //先尝试反向 len = -len; } else { p2State = 'tooLong'; len = len / 2; } } else { var _dis = r2.pos.distanceTo(r1.pos); if (math.closeTo(_dis, 0)) { //console.log('dis == 0') return setVisi(false); break; } if (_dis < 10 && !p2StateHistory.includes('tooLong')) { //和r1的屏幕距离太近,要加长,否则精度过低 p2State = 'tooShort'; len = 100 / _dis * len; } else { p2State = 'got'; break; } } p2StateHistory.push(p2State); } //console.log(p2StateHistory,len) if (!r2.trueSide) { return setVisi(false); //, console.log(' !r2.trueSide', ) } var p1 = r1.pos, p2 = r2.pos; if (p2StateHistory.filter(e => e == 'tooLong-reverse').length % 2 == 1) { //反,for marker p2 = r1.pos, p1 = r2.pos; } var vec = new Vector2().subVectors(p1, p2); orient2dAngle = -vec.angle(); //根据测量线在屏幕上的角度在旋转label,使之和屏幕上的二维线平行。 var y = Math.abs(this.position.y); var facePlane = this.root.measure && this.root.measure.facePlane; var eyeDir = new Vector3().subVectors(center, camera.position); var clockWise = facePlane && facePlane.normal.dot(eyeDir /* e.viewport.view.direction */) < 0; if (p1.x < p2.x) { orient2dAngle += Math.PI; //避免字是倒着的情况。(使字一直在线的下方) clockWise != void 0 && (this.position.y = clockWise ? y : -y); } else { clockWise != void 0 && (this.position.y = clockWise ? -y : y); //使area类型的edgeLabel都在外侧 } //this.parent.text && console.log(this.parent.text, clockWise, this.position.y, e.viewport.name /* THREE.Math.radToDeg(angle), p1.x < p2.x */ ) setVisi(true); } var parentQua = this.root.parent.getWorldQuaternion(new Quaternion()); this.root.quaternion.multiplyQuaternions(parentQua.invert(), camera.quaternion); //乘上parentQua.invert()是为了中和掉父结点的qua,使只剩下camera.quaternion if (orient2dAngle) { var qua = new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), orient2dAngle); this.root.quaternion.multiply(qua); } } //scale var info = this.sizeInfo; if (info) { this.root.updateMatrix(); //先更新,getWorldPosition才能得到正确的 this.root.updateMatrixWorld(true); var scale; if (info.nearBound == void 0 && info.farBound != void 0 || info.nearBound != void 0 && info.farBound == void 0) { //仅限制最大或最小的话,不判断像素大小,直接限制mesh的scale //这个判断也可以写到getScaleForConstantSize里,可以更严谨控制像素宽度,这里只简单计算大小 var dis = camera.position.distanceTo(this.root.getWorldPosition(new Vector3())); if (info.farBound == void 0 && dis < info.nearBound) { scale = info.scale * dis / info.nearBound; } else if (info.nearBound == void 0 && dis > info.farBound) { scale = info.scale * dis / info.farBound; } else { scale = info.scale; } } else { scale = math.getScaleForConstantSize($.extend(info, { //规定下最小最大像素 camera, position: this.root.getWorldPosition(new Vector3()), resolution: e.viewport.resolution //2 })); } if (!isNaN(scale)) { this.root.scale.set(scale, scale, scale); } } this.updateMatrix(); //this.root.updateMatrixWorld(true) //console.log(this.root.text, this.root.matrix.elements) this.matrixMap.set(e.viewport, this.matrix.clone()); if (this.root != this) { this.root.updateMatrix(); //因this.position可能在两个viewport不同 this.matrixMapRoot.set(e.viewport, this.root.matrix.clone()); } //this.parent.text && console.log('update',this.parent.text, this.matrix.elements, this.root.matrix.elements) this.needsUpdate = false; this.useViewport = e.viewport; this.dispatchEvent('spriteUpdated'); } applyMatrix(e) { if (!e) e = { viewport: viewer.mainViewport }; //随便写一个viewport var visi = this.visiMap.get(e.viewport); //还原可见性 Potree.Utils.updateVisible(this, 'unableCompute', visi == false ? false : true); /* if(e.viewport.name == 'mapViewport' && visi && this.visiMap.get(viewer.mainViewport) == false){ console.log(1) } */ if (e.viewport.name == 'magnifier') return; if (this.viewports && !this.viewports.includes(e.viewport)) return; if (!this.root || !this.realVisible(e.viewport, e.interactables)) return; var matrix = this.matrixMap.get(e.viewport); if (!matrix) { this.update(e); matrix = this.matrixMap.get(e.viewport); if (!matrix) return; //maybe unvisible } if (e.viewport == this.useViewport) { return; } this.useViewport = e.viewport; this.matrix.copy(matrix); if (this.root != this) { var matrix2 = this.matrixMapRoot.get(e.viewport); this.root.matrix.copy(matrix2); } e.raycaster && this.root.updateMatrixWorld(true); //渲染前会自动updateMatrixWorld,但raycaster不会 //console.log(this.root.name + e.viewport.name + " : "+this.root.matrixWorld.elements) } setUniforms(name, value) { this.material.setUniforms(name, value); } dispose() { this.removeAllListeners(); this.parent && this.parent.remove(this); this.dispatchEvent('dispose'); } } /* let orient2d if(this.lineDir){ this.root.updateMatrix();//先更新,getWorldPosition才能得到正确的 this.root.updateMatrixWorld(true) let center = this.root.getWorldPosition(new THREE.Vector3()) //由于两个端点容易在屏幕外,所以使用center和center加dir let lineDir = this.lineDir.clone(); let r1 = Potree.Utils.getPos2d(center, camera, viewer.renderArea, e.viewport); if(!r1.trueSide)return Potree.Utils.updateVisible(this, 'unableCompute', false);// 但这句会使realVisible为false从而无法更新//console.error('!r1.trueSide') //中心点如果在背面直接不渲染了 let r2, point2 let p2State = '', len=1, p2StateHistory = [] while(p2State != 'got' && p2StateHistory.length<10){ point2 = center.clone().add(lineDir.multiplyScalar(len)); r2 = Potree.Utils.getPos2d(point2, camera, viewer.renderArea, e.viewport); if(!r2.trueSide){ //很少遇到点2在背面的 if(!p2StateHistory.includes('tooLong-reverse')){ p2State = 'tooLong-reverse' //先尝试反向 len = -len }else{ p2State = 'tooLong' len = len / 2 } }else{ let dis = r2.pos.distanceTo(r1.pos) if(dis == 0){ //console.log('dis == 0') Potree.Utils.updateVisible(this, 'unableCompute', false) return break } if(dis<10 && !p2StateHistory.includes('tooLong')){//和r1的屏幕距离太近,要加长,否则精度过低 p2State = 'tooShort' len = 100/dis * len }else{ p2State = 'got'; break; } } p2StateHistory.push(p2State) } //console.log(p2StateHistory,len) if(!r2.trueSide){ return Potree.Utils.updateVisible(this, 'unableCompute', false)//, console.log(' !r2.trueSide', ) } Potree.Utils.updateVisible(this, 'unableCompute', true) let p1 = r1.pos, p2 = r2.pos let vec = new THREE.Vector2().subVectors(p1,p2); let angle = -vec.angle() //根据测量线在屏幕上的角度在旋转label,使之和屏幕上的二维线平行。 if(p1.x < p2.x) angle += Math.PI //避免字是倒着的情况 orient2d = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), angle) //console.log(this.parent.text, THREE.Math.radToDeg(angle), p1.x < p2.x ) } let parentQua = this.root.parent.getWorldQuaternion(new THREE.Quaternion) this.root.quaternion.multiplyQuaternions(parentQua.invert(),camera.quaternion) //乘上parentQua.invert()是为了中和掉父结点的qua,使只剩下camera.quaternion if(this.lineDir){ this.root.quaternion.multiply(orient2d) } */ // /** class TextSprite$2 extends Object3D { //注:为了分两层控制scale,不直接extend Sprite constructor() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); var map = new Texture(); map.minFilter = LinearFilter; //清晰一些? map.magFilter = LinearFilter; this.sprite = new Sprite$2(Object.assign({ root: this }, options, { map })); this.add(this.sprite); this.fontWeight = options.fontWeight == void 0 ? 'Bold' : options.fontWeight; this.rectBorderThick = options.rectBorderThick || 0; this.textBorderThick = options.textBorderThick || 0; this.fontface = 'Arial'; this.fontsize = options.fontsize || 16; this.lineSpace = options.lineSpace; this.textBorderColor = options.textBorderColor ? Common.CloneObject(options.textBorderColor) : { r: 0, g: 0, b: 0, a: 1.0 }; this.backgroundColor = options.backgroundColor ? Common.CloneObject(options.backgroundColor) : { r: 255, g: 255, b: 255, a: 1.0 }; this.textColor = options.textColor ? Common.CloneObject(options.textColor) : { r: 0, g: 0, b: 0, a: 1.0 }; this.borderColor = options.borderColor ? Common.CloneObject(options.borderColor) : { r: 0, g: 0, b: 0, a: 0.0 }; this.borderRadius = options.borderRadius || 6; this.margin = options.margin; this.textAlign = options.textAlign || 'center'; this.name = options.name; this.transform2Dpercent = options.transform2Dpercent; this.maxLineWidth = options.maxLineWidth; this.setText(options.text); } setText(text) { if (text == void 0) text = ''; if (this.text !== text) { if (!(text instanceof Array)) { this.text = text.split('\n'); //如果是input手动输入的\n这里会是\\n且不会被拆分, 绘制的依然是\n。 //this.text = [text + ''] } else this.text = text; this.updateTexture(); } } /* setText(text){ if(text == void 0)text = '' if (this.text !== text) { if (!(text instanceof Array)) { this.text = text.split('\n') //如果是input手动输入的\n这里会是\\n且不会被拆分, 绘制的依然是\n。 if(this.maxRowWordsCount){//每行显示最大字数 this.text.forEach(str=>{ if(str.length > this.maxRowWordsCount){ } }) } //this.text = [text + ''] } else this.text = text this.updateTexture() } } */ setTextColor(color) { this.textColor = Common.CloneObject(color); this.updateTexture(); } setBorderColor(color) { this.borderColor = Common.CloneObject(color); this.updateTexture(); } setBackgroundColor(color) { this.backgroundColor = Common.CloneObject(color); this.updateTexture(); } setPos(pos) { this.position.copy(pos); this.sprite.waitUpdate(); } updatePose() { this.sprite.waitUpdate(); } setUniforms(name, value) { this.sprite.setUniforms(name, value); } updateTexture() { //canvas原点在左上角 var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); var r = window.devicePixelRatio; //不乘会模糊 context.font = this.fontWeight + ' ' + this.fontsize * r + 'px ' + this.fontface; //context["font-weight"] = 100; //语法与 CSS font 属性相同。 var textMaxWidth = 0, infos = []; context.textBaseline = 'alphabetic'; // "middle" //设置文字基线。当起点y设置为0时,只有该线以下的部分被绘制出来。middle时文字显示一半(但是对该字体所有字的一半,有的字是不一定显示一半的,尤其汉字),alphabetic时是英文字母的那条基线。 var textHeightAll = 0; var texts = []; if (this.maxLineWidth) { this.text.forEach(words => { if (!words) { texts.push(""); return; } texts = texts.concat(breakLinesForCanvas(words, context, this.maxLineWidth)); }); } else { texts = this.text; } for (var text of texts) { var metrics = context.measureText(text); var textWidth = metrics.width; infos.push(metrics); textMaxWidth = Math.max(textMaxWidth, textWidth); textHeightAll += metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent; //文字真实高度 } var margin = (this.margin ? new Vector2().copy(this.margin) : new Vector2(this.fontsize, this.fontsize * 0.8)).multiplyScalar(r); var lineSpace = (this.lineSpace || this.fontsize * 0.5) * r; var rectBorderThick = this.rectBorderThick * r, textBorderThick = this.textBorderThick * r; var spriteWidth = 2 * (margin.x + rectBorderThick + textBorderThick) + textMaxWidth; //还要考虑this.textshadowColor,太麻烦了不写了 var spriteHeight = 2 * (margin.y + rectBorderThick + textBorderThick * texts.length) + lineSpace * (texts.length - 1) + textHeightAll; //canvas宽高只会向下取整数,所以为了防止拉伸模糊这里必须先取整 spriteWidth = Math.floor(spriteWidth); spriteHeight = Math.floor(spriteHeight); context.canvas.width = spriteWidth; context.canvas.height = spriteHeight; context.font = this.fontWeight + ' ' + this.fontsize * r + 'px ' + this.fontface; //为何要再写一遍?? if (spriteWidth > 4000) { console.error('spriteWidth', spriteWidth, 'spriteHeight', spriteHeight, this.fontsize, r, texts, margin); } var expand = 0; //Math.max(1, Math.pow(this.fontsize / 16, 1.1)) * r // 针对英文大部分在baseLine之上所以降低一点,或者可以识别当不包含jgqp时才加这个值 . 但即使都是汉字也会不同,如"哈哈"和"粉色",前者居中后者不 context.strokeStyle = 'rgba(' + this.borderColor.r + ',' + this.borderColor.g + ',' + this.borderColor.b + ',' + this.borderColor.a + ')'; context.lineWidth = rectBorderThick; context.fillStyle = 'rgba(' + this.backgroundColor.r + ',' + this.backgroundColor.g + ',' + this.backgroundColor.b + ',' + this.backgroundColor.a + ')'; this.roundRect(context, rectBorderThick / 2, rectBorderThick / 2, spriteWidth - rectBorderThick, spriteHeight - rectBorderThick, this.borderRadius * r); context.fillStyle = 'rgba(' + this.textColor.r + ',' + this.textColor.g + ',' + this.textColor.b + ',' + this.textColor.a + ')'; var y = margin.y + rectBorderThick; for (var i = 0; i < texts.length; i++) { //文字y向距离从textBaseline向上算 var actualBoundingBoxAscent = infos[i].fontBoundingBoxAscent == void 0 ? this.fontsize * r * 0.8 : infos[i].fontBoundingBoxAscent; //有的流览器没有。只能大概给一个 y += actualBoundingBoxAscent + textBorderThick; var textLeftSpace = this.textAlign == 'center' ? (textMaxWidth - infos[i].width) / 2 : this.textAlign == 'left' ? 0 : textMaxWidth - infos[i].width; var x = rectBorderThick + textBorderThick + margin.x + textLeftSpace; // text color if (this.textBorderThick) { context.strokeStyle = 'rgba(' + this.textBorderColor.r + ',' + this.textBorderColor.g + ',' + this.textBorderColor.b + ',' + this.textBorderColor.a + ')'; context.lineWidth = this.textBorderThick * r; context.strokeText(texts[i], x, y); } if (this.textshadowColor) { context.shadowOffsetX = 0; context.shadowOffsetY = 0; context.shadowColor = this.textshadowColor; //'red' context.shadowBlur = (this.textShadowBlur || this.fontSize / 3) * r; } context.fillText(texts[i], x, y); var actualBoundingBoxDescent = infos[i].fontBoundingBoxDescent == void 0 ? this.fontsize * r * 0.2 : infos[i].fontBoundingBoxDescent; y += actualBoundingBoxDescent + textBorderThick + lineSpace; } var texture = new Texture(canvas); texture.minFilter = LinearFilter; texture.magFilter = LinearFilter; texture.needsUpdate = true; //this.material.needsUpdate = true; if (this.sprite.material.map) { this.sprite.material.map.dispose(); } this.sprite.material.map = texture; var oldScale = this.sprite.scale.clone(); this.sprite.scale.set(spriteWidth * 0.01 / r, spriteHeight * 0.01 / r, 1.0); if (!oldScale.equals(this.sprite.scale)) { this.updateTransform2D(); this.sprite.waitUpdate(); //重新计算各个viewport的matrix } } roundRect(ctx, x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.arcTo(x + w, y, x + w, y + r, r); //圆弧。前四个参数同quadraticCurveTo //ctx.quadraticCurveTo(x + w, y, x + w, y + r); //二次贝塞尔曲线需要两个点。第一个点是用于二次贝塞尔计算中的控制点,第二个点是曲线的结束点。 ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r); ctx.lineTo(x + r, y + h); ctx.arcTo(x, y + h, x, y + h - r, r); ctx.lineTo(x, y + r); ctx.arcTo(x, y, x + r, y, r); ctx.closePath(); ctx.fill(); ctx.stroke(); } updateTransform2D() { if (this.transform2Dpercent) { ['x', 'y'].forEach(axis => { var percent = this.transform2Dpercent[axis]; this.sprite.position.y = this.sprite.scale.y * percent; }); } } dispose() { this.sprite.material.uniforms.map.value.dispose(); this.parent && this.parent.remove(this); this.sprite.dispose(); this.removeAllListeners(); this.dispatchEvent('dispose'); } } function findBreakPoint(text, width, context) { var min = 0; var max = text.length - 1; while (min <= max) { var middle = Math.floor((min + max) / 2); var middleWidth = context.measureText(text.substr(0, middle)).width; var oneCharWiderThanMiddleWidth = context.measureText(text.substr(0, middle + 1)).width; if (middleWidth <= width && oneCharWiderThanMiddleWidth > width) { return middle; } if (middleWidth < width) { min = middle + 1; } else { max = middle - 1; } } return -1; } function breakLinesForCanvas(text, context, width, font) { var result = []; var breakPoint = 0; if (font) { context.font = font; } while ((breakPoint = findBreakPoint(text, width, context)) !== -1) { result.push(text.substr(0, breakPoint)); text = text.substr(breakPoint); } if (text) { result.push(text); } return result; } //'使用很寻常的二分查找,如果某一个位置之前的文字宽度小于等于设定的宽度,并且它之后一个字之前的文字宽度大于设定的宽度,那么这个位置就是文本的换行点。上面只是找到一个换行点,对于输入的一段文本,需要循环查找,直到不存在这样的换行点为止, 完整的代码如下', //待改进:breakLinesForCanvas后的lineSpace稍小于预设的[]换行的lineSpace, 用于测量标签 /* function wrapText(text, maxWidth) { // 创建一个 canvas 元素来测量文本宽度 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // 设置字体样式 ctx.font = '16px Arial'; let currentLine = ''; const lines = []; // 拆分文本为单词数组 const words = text.split(' '); for (let i = 0; i < words.length; i++) { const word = words[i]; const wordWidth = ctx.measureText(word).width; const currentLineWidth = ctx.measureText(currentLine).width; if (currentLineWidth + wordWidth < maxWidth) { // 如果当前行加上这个单词不会超出最大宽度,就将它添加到当前行 currentLine += (currentLine ? ' ' : '') + word; } else { // 如果当前行加上这个单词会超出最大宽度,就将当前行添加到结果数组,并开始新的一行 lines.push(currentLine.trim()); currentLine = word; } } // 添加最后一行 if (currentLine) { lines.push(currentLine.trim()); } return lines; } */ /* z | | | | x <-------| 中心为点云position加boudingbox中心 / / y */ var lineLen$1 = 2, stemLen = 4, arrowLen = 2, lineDisToStem = 5; var opacity = 0.5; class Axis extends Object3D { // 坐标轴 constructor() { super(); this.getArrow(); this.createArrows(); //this.position.copy(position) 点云的中心点就是在(0,0,0) //this.scale.set(2,2,2) /* viewer.addEventListener('camera_changed', e => { if(e.viewport.name != 'MainView')return //只调整mainView,否则需要每次渲染前调整。缺点:地图上的大小变来变去 let s = Potree.math.getScaleForConstantSize( {//规定下最小最大像素 width2d:50, camera:e.camera , position:this.position, resolution: e.viewport.resolution//2 }) this.scale.set(s,s,s) }) */ } getArrow() { var arrowGroup = new Object3D(); var line = LineDraw.createLine([new Vector3(), new Vector3(0, 0, lineLen$1)]); var stem = new Mesh(new BoxGeometry(0.3, 0.3, stemLen)); stem.position.set(0, 0, lineLen$1 + lineDisToStem + stemLen / 2); var arrow = new Mesh(new CylinderBufferGeometry(0, 0.6, arrowLen, 12, 1, false)); //radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 8, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2 arrow.position.set(0, 0, lineLen$1 + lineDisToStem + stemLen + arrowLen / 2); arrow.rotation.set(Math.PI / 2, 0, 0); arrowGroup.add(stem); arrowGroup.add(line); arrowGroup.add(arrow); this.arrowGroup = arrowGroup; } createArrows() { var material = new MeshBasicMaterial({ color: "#00d7df", side: 2, transparent: true, opacity: 0.8, depthWrite: false }); ['x', 'y', 'z'].forEach(axisText => { var color = new Color().set(Potree.config.axis[axisText].color); var group = this.arrowGroup.clone(); group.children.forEach(e => { e.material = e.material.clone(); /* e.material.opacity = opacity e.material.transparent = true */ e.material.color.copy(color); }); var label = this.createLabel(axisText, color); label.position.set(0, 0, lineLen$1 + stemLen + arrowLen + lineDisToStem + 3); group.add(label); if (axisText == 'y') { group.rotation.x = -Math.PI / 2; } else if (axisText == 'x') { group.rotation.y = Math.PI / 2; } this.add(group); }); } createLabel(text, color) { var label = new TextSprite$2({ //无法解决 因其祖先有设定quaternion, 无法对着镜头 backgroundColor: { r: 0, g: 0, b: 0, a: 0 }, textColor: { r: color.r * 255, g: color.g * 255, b: color.b * 255, a: 1 }, fontsize: 120, //useDepth : true , renderOrder: 5, // pickOrder:5, text, name: 'axis' }); label.scale.set(3, 3, 3); return label; } /* createLabel(text,color){ var canvas = document.createElement("canvas") var context = canvas.getContext("2d"); canvas.width = 256, canvas.height = 256; var fontSize = 120 context.fillStyle = color //"#00ffee"; context.font = "normal " + fontSize + "px 微软雅黑" var textWidth = context.measureText(text).width; context.clearRect(0,0,canvas.width,canvas.height); context.fillText(text, (canvas.width - textWidth) / 2 , (canvas.height + fontSize) / 2); var tex = new THREE.Texture(canvas); tex.needsUpdate = true tex.minFilter = THREE.NearestFilter//防止边缘发黑 tex.magFilter = THREE.NearestFilter//防止边缘发黑 var sprite = new THREE.Sprite(new THREE.SpriteMaterial({ map: tex , // depthWrite:false, })) sprite.renderOrder = 1//防止在透明后还是出现白矩形挡住其他mesh sprite.scale.set(3,3,3) return sprite } */ } class Action extends EventDispatcher$1 { constructor() { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); this.icon = args.icon || ''; this.tooltip = args.tooltip; if (args.onclick !== undefined) { this.onclick = args.onclick; } } onclick(event) {} pairWith(object) {} setIcon(newIcon) { var oldIcon = this.icon; if (newIcon === oldIcon) { return; } this.icon = newIcon; this.dispatchEvent({ type: 'icon_changed', action: this, icon: newIcon, oldIcon: oldIcon }); } } ; //Potree.Actions = {}; // //Potree.Actions.ToggleAnnotationVisibility = class ToggleAnnotationVisibility extends Potree.Action { // constructor (args = {}) { // super(args); // // this.icon = Potree.resourcePath + '/icons/eye.svg'; // this.showIn = 'sidebar'; // this.tooltip = 'toggle visibility'; // } // // pairWith (annotation) { // if (annotation.visible) { // this.setIcon(Potree.resourcePath + '/icons/eye.svg'); // } else { // this.setIcon(Potree.resourcePath + '/icons/eye_crossed.svg'); // } // // annotation.addEventListener('visibility_changed', e => { // let annotation = e.annotation; // // if (annotation.visible) { // this.setIcon(Potree.resourcePath + '/icons/eye.svg'); // } else { // this.setIcon(Potree.resourcePath + '/icons/eye_crossed.svg'); // } // }); // } // // onclick (event) { // let annotation = event.annotation; // // annotation.visible = !annotation.visible; // // if (annotation.visible) { // this.setIcon(Potree.resourcePath + '/icons/eye.svg'); // } else { // this.setIcon(Potree.resourcePath + '/icons/eye_crossed.svg'); // } // } //}; class Annotation extends EventDispatcher$1 { constructor() { var _this; var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(); _this = this; this.scene = null; this._title = args.title || 'No Title'; this._description = args.description || ''; this.offset = new Vector3(); this.uuid = MathUtils.generateUUID(); if (!args.position) { this.position = null; } else if (args.position.x != null) { this.position = args.position; } else { this.position = new Vector3(...args.position); } this.cameraPosition = args.cameraPosition instanceof Array ? new Vector3().fromArray(args.cameraPosition) : args.cameraPosition; this.cameraTarget = args.cameraTarget instanceof Array ? new Vector3().fromArray(args.cameraTarget) : args.cameraTarget; this.radius = args.radius; this.view = args.view || null; this.keepOpen = false; this.descriptionVisible = false; this.showDescription = true; this.actions = args.actions || []; this.isHighlighted = false; this._visible = true; this.__visible = true; this._display = true; this._expand = false; this.collapseThreshold = [args.collapseThreshold, 100].find(e => e !== undefined); this.children = []; this.parent = null; this.boundingBox = new Box3(); var iconClose = exports.resourcePath + '/icons/close.svg'; this.domElement = $("\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t").concat(this._description, "\n\t\t\t\t
\n\t\t\t
\n\t\t")); this.elTitlebar = this.domElement.find('.annotation-titlebar'); this.elTitle = this.elTitlebar.find('.annotation-label'); this.elTitle.append(this._title); this.elDescription = this.domElement.find('.annotation-description'); this.elDescriptionClose = this.elDescription.find('.annotation-description-close'); // this.elDescriptionContent = this.elDescription.find(".annotation-description-content"); this.clickTitle = () => { if (this.hasView()) { this.moveHere(this.scene.getActiveCamera()); } this.dispatchEvent({ type: 'click', target: this }); }; this.elTitle.click(this.clickTitle); this.actions = this.actions.map(a => { if (a instanceof Action) { return a; } else { return new Action(a); } }); for (var action of this.actions) { action.pairWith(this); } var actions = this.actions.filter(a => a.showIn === undefined || a.showIn.includes('scene')); var _loop = function _loop(_action) { var elButton = $("")); _this.elTitlebar.append(elButton); elButton.click(() => _action.onclick({ annotation: _this })); }; for (var _action of actions) { _loop(_action); } this.elDescriptionClose.hover(e => this.elDescriptionClose.css('opacity', '1'), e => this.elDescriptionClose.css('opacity', '0.5')); this.elDescriptionClose.click(e => this.setHighlighted(false)); // this.elDescriptionContent.html(this._description); this.domElement.mouseenter(e => this.setHighlighted(true)); this.domElement.mouseleave(e => this.setHighlighted(false)); this.domElement.on('touchstart', e => { this.setHighlighted(!this.isHighlighted); }); this.display = false; //this.display = true; } installHandles(viewer) { if (this.handles !== undefined) { return; } var domElement = $("\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t"); var svg = domElement.find("svg")[0]; var elLine = domElement.find("line")[0]; var elStart = domElement.find("circle")[0]; var elEnd = domElement.find("circle")[1]; var setCoordinates = (start, end) => { elStart.setAttribute("cx", "".concat(start.x)); elStart.setAttribute("cy", "".concat(start.y)); elEnd.setAttribute("cx", "".concat(end.x)); elEnd.setAttribute("cy", "".concat(end.y)); elLine.setAttribute("x1", start.x); elLine.setAttribute("y1", start.y); elLine.setAttribute("x2", end.x); elLine.setAttribute("y2", end.y); var box = svg.getBBox(); svg.setAttribute("width", "".concat(box.width)); svg.setAttribute("height", "".concat(box.height)); svg.setAttribute("viewBox", "".concat(box.x, " ").concat(box.y, " ").concat(box.width, " ").concat(box.height)); var ya = start.y - end.y; var xa = start.x - end.x; if (ya > 0) { start.y = start.y - ya; } if (xa > 0) { start.x = start.x - xa; } domElement.css("left", "".concat(start.x, "px")); domElement.css("top", "".concat(start.y, "px")); }; $(viewer.renderArea).append(domElement); var annotationStartPos = this.position.clone(); var annotationStartOffset = this.offset.clone(); $(this.domElement).draggable({ start: (event, ui) => { annotationStartPos = this.position.clone(); annotationStartOffset = this.offset.clone(); $(this.domElement).find(".annotation-titlebar").css("pointer-events", "none"); console.log($(this.domElement).find(".annotation-titlebar")); }, stop: () => { $(this.domElement).find(".annotation-titlebar").css("pointer-events", ""); }, drag: (event, ui) => { var renderAreaWidth = viewer.renderer.getSize(new Vector2()).width; //let renderAreaHeight = viewer.renderer.getSize().height; var diff = { x: ui.originalPosition.left - ui.position.left, y: ui.originalPosition.top - ui.position.top }; var nDiff = { x: -(diff.x / renderAreaWidth) * 2, y: diff.y / renderAreaWidth * 2 }; var camera = viewer.scene.getActiveCamera(); var oldScreenPos = new Vector3().addVectors(annotationStartPos, annotationStartOffset).project(camera); var newScreenPos = oldScreenPos.clone(); newScreenPos.x += nDiff.x; newScreenPos.y += nDiff.y; var newPos = newScreenPos.clone(); newPos.unproject(camera); var newOffset = new Vector3().subVectors(newPos, this.position); this.offset.copy(newOffset); } }); var updateCallback = () => { var position = this.position; var scene = viewer.scene; var renderAreaSize = viewer.renderer.getSize(new Vector2()); var renderAreaWidth = renderAreaSize.width; var renderAreaHeight = renderAreaSize.height; var start = this.position.clone(); var end = new Vector3().addVectors(this.position, this.offset); var toScreen = position => { var camera = scene.getActiveCamera(); var screenPos = new Vector3(); var worldView = new Matrix4().multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); var ndc = new Vector4(position.x, position.y, position.z, 1.0).applyMatrix4(worldView); // limit w to small positive value, in case position is behind the camera ndc.w = Math.max(ndc.w, 0.1); ndc.divideScalar(ndc.w); screenPos.copy(ndc); screenPos.x = renderAreaWidth * (screenPos.x + 1) / 2; screenPos.y = renderAreaHeight * (1 - (screenPos.y + 1) / 2); return screenPos; }; start = toScreen(start); end = toScreen(end); setCoordinates(start, end); }; viewer.addEventListener("update", updateCallback); this.handles = { domElement: domElement, setCoordinates: setCoordinates, updateCallback: updateCallback }; } removeHandles(viewer) { if (this.handles === undefined) { return; } //$(viewer.renderArea).remove(this.handles.domElement); this.handles.domElement.remove(); viewer.removeEventListener("update", this.handles.updateCallback); delete this.handles; } get visible() { return this._visible; } set visible(value) { if (this._visible === value) { return; } this._visible = value; //this.traverse(node => { // node.display = value; //}); this.dispatchEvent({ type: 'visibility_changed', annotation: this }); } get display() { return this._display; } set display(display) { if (this._display === display) { return; } this._display = display; if (display) { // this.domElement.fadeIn(200); this.domElement.show(); } else { // this.domElement.fadeOut(200); this.domElement.hide(); } } get expand() { return this._expand; } set expand(expand) { if (this._expand === expand) { return; } if (expand) { this.display = false; } else { this.display = true; this.traverseDescendants(node => { node.display = false; }); } this._expand = expand; } get title() { return this._title; } set title(title) { if (this._title === title) { return; } this._title = title; this.elTitle.empty(); this.elTitle.append(this._title); this.dispatchEvent({ type: "annotation_changed", annotation: this }); } get description() { return this._description; } set description(description) { if (this._description === description) { return; } this._description = description; var elDescriptionContent = this.elDescription.find(".annotation-description-content"); elDescriptionContent.empty(); elDescriptionContent.append(this._description); this.dispatchEvent({ type: "annotation_changed", annotation: this }); } add(annotation) { if (!this.children.includes(annotation)) { this.children.push(annotation); annotation.parent = this; var descendants = []; annotation.traverse(a => { descendants.push(a); }); for (var descendant of descendants) { var c = this; while (c !== null) { c.dispatchEvent({ 'type': 'annotation_added', 'annotation': descendant }); c = c.parent; } } } } level() { if (this.parent === null) { return 0; } else { return this.parent.level() + 1; } } hasChild(annotation) { return this.children.includes(annotation); } remove(annotation) { if (this.hasChild(annotation)) { annotation.removeAllChildren(); annotation.dispose(); this.children = this.children.filter(e => e !== annotation); annotation.parent = null; } } removeAllChildren() { this.children.forEach(child => { if (child.children.length > 0) { child.removeAllChildren(); } this.remove(child); }); } updateBounds() { var box = new Box3(); if (this.position) { box.expandByPoint(this.position); } for (var child of this.children) { child.updateBounds(); box.union(child.boundingBox); } this.boundingBox.copy(box); } traverse(handler) { var expand = handler(this); if (expand === undefined || expand === true) { for (var child of this.children) { child.traverse(handler); } } } traverseDescendants(handler) { for (var child of this.children) { child.traverse(handler); } } flatten() { var annotations = []; this.traverse(annotation => { annotations.push(annotation); }); return annotations; } descendants() { var annotations = []; this.traverse(annotation => { if (annotation !== this) { annotations.push(annotation); } }); return annotations; } setHighlighted(highlighted) { if (highlighted) { this.domElement.css('opacity', '0.8'); this.elTitlebar.css('box-shadow', '0 0 5px #fff'); this.domElement.css('z-index', '1000'); if (this._description) { this.descriptionVisible = true; this.elDescription.fadeIn(200); this.elDescription.css('position', 'relative'); } } else { this.domElement.css('opacity', '0.5'); this.elTitlebar.css('box-shadow', ''); this.domElement.css('z-index', '100'); this.descriptionVisible = false; this.elDescription.css('display', 'none'); } this.isHighlighted = highlighted; } hasView() { var hasPosTargetView = this.cameraTarget.x != null; hasPosTargetView = hasPosTargetView && this.cameraPosition.x != null; var hasRadiusView = this.radius !== undefined; var hasView = hasPosTargetView || hasRadiusView; return hasView; } moveHere(camera) { if (!this.hasView()) { return; } var view = this.scene.view; var animationDuration = 500; var easing = TWEEN.Easing.Quartic.Out; var endTarget; if (this.cameraTarget) { endTarget = this.cameraTarget; } else if (this.position) { endTarget = this.position; } else { endTarget = this.boundingBox.getCenter(new Vector3()); } if (this.cameraPosition) { var endPosition = this.cameraPosition; Utils.moveTo(this.scene, endPosition, endTarget); } else if (this.radius) { var direction = view.direction; var _endPosition = endTarget.clone().add(direction.multiplyScalar(-this.radius)); var startRadius = view.radius; var endRadius = this.radius; { // animate camera position var tween = new TWEEN.Tween(view.position).to(_endPosition, animationDuration); tween.easing(easing); tween.start(); } { // animate radius var t = { x: 0 }; var _tween = new TWEEN.Tween(t).to({ x: 1 }, animationDuration).onUpdate(function () { view.radius = this.x * endRadius + (1 - this.x) * startRadius; }); _tween.easing(easing); _tween.start(); } } } dispose() { if (this.domElement.parentElement) { this.domElement.parentElement.removeChild(this.domElement); } } toString() { return 'Annotation: ' + this._title; } } ; class Scene$1 extends EventDispatcher$1 { //base constructor() { super(); this.annotations = new Annotation(); this.scene = new Scene(); this.sceneBG = new Scene(); this.scenePointCloud = new Scene(); this.cameraP = new PerspectiveCamera(this.fov, 1, 0.1, 1000 * 1000); this.cameraO = new OrthographicCamera(-1, 1, 1, -1, 0.1, 1000 * 1000); this.cameraVR = new PerspectiveCamera(); this.cameraBG = new Camera(); this.cameraScreenSpace = new OrthographicCamera(-1, 1, 1, -1, 0.1, 10); this.cameraMode = CameraMode.PERSPECTIVE; this.overrideCamera = null; this.pointclouds = []; this.measurements = []; this.profiles = []; this.volumes = []; this.polygonClipVolumes = []; this.cameraAnimations = []; this.orientedImages = []; this.images360 = []; this.geopackages = []; this.fpControls = null; this.orbitControls = null; this.earthControls = null; this.geoControls = null; this.deviceControls = null; this.inputHandler = null; this.view = new ExtendView(); this.directionalLight = null; this.initialize(); } estimateHeightAt(position) { var height = null; var fromSpacing = Infinity; for (var pointcloud of this.pointclouds) { if (pointcloud.root.geometryNode === undefined) { continue; } var pHeight = null; var pFromSpacing = Infinity; var lpos = position.clone().sub(pointcloud.position); lpos.z = 0; var ray = new Ray(lpos, new Vector3(0, 0, 1)); var stack = [pointcloud.root]; while (stack.length > 0) { var node = stack.pop(); var box = node.getBoundingBox(); var inside = ray.intersectBox(box); if (!inside) { continue; } var h = node.geometryNode.mean.z + pointcloud.position.z + node.geometryNode.boundingBox.min.z; if (node.geometryNode.spacing <= pFromSpacing) { pHeight = h; pFromSpacing = node.geometryNode.spacing; } for (var index of Object.keys(node.children)) { var child = node.children[index]; if (child.geometryNode) { stack.push(node.children[index]); } } } if (height === null || pFromSpacing < fromSpacing) { height = pHeight; fromSpacing = pFromSpacing; } } return height; } getBoundingBox() { var pointclouds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.pointclouds; var box = new Box3(); this.scenePointCloud.updateMatrixWorld(true); this.referenceFrame.updateMatrixWorld(true); for (var pointcloud of pointclouds) { pointcloud.updateMatrixWorld(true); var pointcloudBox = pointcloud.pcoGeometry.tightBoundingBox ? pointcloud.pcoGeometry.tightBoundingBox : pointcloud.boundingBox; var boxWorld = Utils.computeTransformedBoundingBox(pointcloudBox, pointcloud.matrixWorld); box.union(boxWorld); } return box; } addPointCloud(pointcloud) { this.pointclouds.push(pointcloud); this.scenePointCloud.add(pointcloud); this.dispatchEvent({ type: 'pointcloud_added', pointcloud: pointcloud }); } addVolume(volume) { this.volumes.push(volume); this.dispatchEvent({ 'type': 'volume_added', 'scene': this, 'volume': volume }); viewer.dispatchEvent('content_changed'); } addOrientedImages(images) { this.orientedImages.push(images); this.scene.add(images.node); this.dispatchEvent({ 'type': 'oriented_images_added', 'scene': this, 'images': images }); } removeOrientedImages(images) { var index = this.orientedImages.indexOf(images); if (index > -1) { this.orientedImages.splice(index, 1); this.dispatchEvent({ 'type': 'oriented_images_removed', 'scene': this, 'images': images }); } } add360Images(images) { this.images360.push(images); this.scene.add(images.node); this.dispatchEvent({ 'type': '360_images_added', 'scene': this, 'images': images }); } remove360Images(images) { var index = this.images360.indexOf(images); if (index > -1) { this.images360.splice(index, 1); this.dispatchEvent({ 'type': '360_images_removed', 'scene': this, 'images': images }); } } addGeopackage(geopackage) { this.geopackages.push(geopackage); this.scene.add(geopackage.node); this.dispatchEvent({ 'type': 'geopackage_added', 'scene': this, 'geopackage': geopackage }); } removeGeopackage(geopackage) { var index = this.geopackages.indexOf(geopackage); if (index > -1) { this.geopackages.splice(index, 1); this.dispatchEvent({ 'type': 'geopackage_removed', 'scene': this, 'geopackage': geopackage }); } } removeVolume(volume) { var index = this.volumes.indexOf(volume); if (index > -1) { this.volumes.splice(index, 1); this.dispatchEvent({ 'type': 'volume_removed', 'scene': this, 'volume': volume }); } viewer.dispatchEvent('content_changed'); } addCameraAnimation(animation) { this.cameraAnimations.push(animation); this.dispatchEvent({ 'type': 'camera_animation_added', 'scene': this, 'animation': animation }); } removeCameraAnimation(animation) { var index = this.cameraAnimations.indexOf(volume); if (index > -1) { this.cameraAnimations.splice(index, 1); this.dispatchEvent({ 'type': 'camera_animation_removed', 'scene': this, 'animation': animation }); } } addPolygonClipVolume(volume) { this.polygonClipVolumes.push(volume); this.dispatchEvent({ "type": "polygon_clip_volume_added", "scene": this, "volume": volume }); } removePolygonClipVolume(volume) { var index = this.polygonClipVolumes.indexOf(volume); if (index > -1) { this.polygonClipVolumes.splice(index, 1); this.dispatchEvent({ "type": "polygon_clip_volume_removed", "scene": this, "volume": volume }); } } addMeasurement(measurement) { measurement.lengthUnit = this.lengthUnit; measurement.lengthUnitDisplay = this.lengthUnitDisplay; this.measurements.push(measurement); this.dispatchEvent({ 'type': 'measurement_added', 'scene': this, 'measurement': measurement }); viewer.dispatchEvent('content_changed'); } removeMeasurement(measurement) { var index = this.measurements.indexOf(measurement); if (index > -1) { this.measurements.splice(index, 1); this.dispatchEvent({ 'type': 'measurement_removed', 'scene': this, 'measurement': measurement }); viewer.dispatchEvent('content_changed'); } } addProfile(profile) { this.profiles.push(profile); this.dispatchEvent({ 'type': 'profile_added', 'scene': this, 'profile': profile }); } removeProfile(profile) { var index = this.profiles.indexOf(profile); if (index > -1) { this.profiles.splice(index, 1); this.dispatchEvent({ 'type': 'profile_removed', 'scene': this, 'profile': profile }); } } removeAllMeasurements() { while (this.measurements.length > 0) { this.removeMeasurement(this.measurements[0]); } while (this.profiles.length > 0) { this.removeProfile(this.profiles[0]); } while (this.volumes.length > 0) { this.removeVolume(this.volumes[0]); } } removeAllClipVolumes() { var clipVolumes = this.volumes.filter(volume => volume.clip === true); for (var clipVolume of clipVolumes) { this.removeVolume(clipVolume); } while (this.polygonClipVolumes.length > 0) { this.removePolygonClipVolume(this.polygonClipVolumes[0]); } } getActiveCamera() { if (this.overrideCamera) { return this.overrideCamera; } if (this.cameraMode === CameraMode.PERSPECTIVE) { return this.cameraP; } else if (this.cameraMode === CameraMode.ORTHOGRAPHIC) { return this.cameraO; } else if (this.cameraMode === CameraMode.VR) { return this.cameraVR; } return null; } initialize() { this.referenceFrame = new Object3D(); this.referenceFrame.matrixAutoUpdate = false; this.scenePointCloud.add(this.referenceFrame); this.cameraP.up.set(0, 0, 1); this.cameraP.position.set(1000, 1000, 1000); this.cameraO.up.set(0, 0, 1); this.cameraO.position.set(1000, 1000, 1000); //this.camera.rotation.y = -Math.PI / 4; //this.camera.rotation.x = -Math.PI / 6; this.cameraScreenSpace.lookAt(new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector3(0, 1, 0)); this.directionalLight = new DirectionalLight(0xffffff, 0.5); this.directionalLight.position.set(10, 10, 10); this.directionalLight.lookAt(new Vector3(0, 0, 0)); this.scenePointCloud.add(this.directionalLight); var light = new AmbientLight(0x555555); // soft white light this.scenePointCloud.add(light); { // background var texture = Utils.createBackgroundTexture(512, 512); texture.minFilter = texture.magFilter = NearestFilter; texture.minFilter = texture.magFilter = LinearFilter; var bg = new Mesh(new PlaneBufferGeometry(2, 2, 1), new MeshBasicMaterial({ map: texture })); bg.material.depthTest = false; bg.material.depthWrite = false; this.sceneBG.add(bg); } // { // lights // { // let light = new THREE.DirectionalLight(0xffffff); // light.position.set(10, 10, 1); // light.target.position.set(0, 0, 0); // this.scene.add(light); // } // { // let light = new THREE.DirectionalLight(0xffffff); // light.position.set(-10, 10, 1); // light.target.position.set(0, 0, 0); // this.scene.add(light); // } // { // let light = new THREE.DirectionalLight(0xffffff); // light.position.set(0, -10, 20); // light.target.position.set(0, 0, 0); // this.scene.add(light); // } // } } addAnnotation(position) { var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (position instanceof Array) { args.position = new Vector3().fromArray(position); } else if (position.x != null) { args.position = position; } var annotation = new Annotation(args); this.annotations.add(annotation); return annotation; } getAnnotations() { return this.annotations; } removeAnnotation(annotationToRemove) { this.annotations.remove(annotationToRemove); } } ; class ExtendScene extends Scene$1 { constructor() { super(); delete this.sceneBG; this.overlayScene = new Scene(); viewer.addEventListener("render.pass.perspective_overlay", this.renderOverlay.bind(this)); this.cameraP = new PerspectiveCamera(this.fov, 1, Potree.config.view.near, Potree.config.view.cameraFar); this.cameraO = new OrthographicCamera(-1, 1, 1, -1, Potree.config.view.near, Potree.settings.cameraFar); this.cameraP.limitFar = true; //add this.initializeExtend(); //------------- this.axisArrow = new Axis(); this.scene.add(this.axisArrow); if (!Potree.settings.isDebug && !Potree.settings.showAxis) this.axisArrow.visible = false; Potree.Utils.setObjectLayers(this.axisArrow, 'bothMapAndScene'); } estimateHeightAt(position) { var height = null; var fromSpacing = Infinity; for (var pointcloud of this.pointclouds) { if (pointcloud.root.geometryNode === undefined) { continue; } var pHeight = null; var pFromSpacing = Infinity; var lpos = position.clone().sub(pointcloud.position); lpos.z = 0; var ray = new Ray(lpos, new Vector3(0, 0, 1)); var stack = [pointcloud.root]; while (stack.length > 0) { var node = stack.pop(); var box = node.getBoundingBox(); var inside = ray.intersectBox(box); if (!inside) { continue; } var h = node.geometryNode.mean.z + pointcloud.position.z + node.geometryNode.boundingBox.min.z; if (node.geometryNode.spacing <= pFromSpacing) { pHeight = h; pFromSpacing = node.geometryNode.spacing; } for (var index of Object.keys(node.children)) { var child = node.children[index]; if (child.geometryNode) { stack.push(node.children[index]); } } } if (height === null || pFromSpacing < fromSpacing) { height = pHeight; fromSpacing = pFromSpacing; } } return height; } //add: removePointCloud(pointcloud) { var index = this.pointclouds.indexOf(pointcloud); if (index == -1) return; this.pointclouds.splice(index, 1); this.scenePointCloud.remove(pointcloud); pointcloud.panos.forEach(pano => { pano.dispose(); }); } removeCameraAnimation(animation) { var index = this.cameraAnimations.indexOf(animation); if (index > -1) { this.cameraAnimations.splice(index, 1); this.dispatchEvent({ 'type': 'camera_animation_removed', 'scene': this, 'animation': animation }); } } getActiveCamera() { return viewer.mainViewport.camera; } initialize() {//不用旧的 因为还没创建完变量 } initializeExtend() { //add 新的initialize this.referenceFrame = new Object3D(); this.referenceFrame.matrixAutoUpdate = false; this.scenePointCloud.add(this.referenceFrame); if (window.axisYup) {} else { this.cameraP.up.set(0, 0, 1); this.cameraO.up.set(0, 0, 1); } this.cameraP.position.set(1000, 1000, 1000); this.cameraO.position.set(1000, 1000, 1000); //this.camera.rotation.y = -Math.PI / 4; //this.camera.rotation.x = -Math.PI / 6; this.cameraScreenSpace.lookAt(new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector3(0, 1, 0)); this.directionalLight = new DirectionalLight(0xffffff, 0.5); this.directionalLight.position.set(10, 10, 10); this.directionalLight.lookAt(new Vector3(0, 0, 0)); this.scenePointCloud.add(this.directionalLight); var light = new AmbientLight(0x555555); // soft white light this.scenePointCloud.add(light); //add:------给空间模型的box 或其他obj------ var light2 = new AmbientLight(16777215, 0.5); Potree.Utils.setObjectLayers(light2, 'light' /* 'bothMapAndScene' */); this.scene.add(light2); var light3 = new DirectionalLight(16777215, 0.7); light3.position.set(10, 10, 10); light3.lookAt(new Vector3(0, 0, 0)); Potree.Utils.setObjectLayers(light3, 'light'); this.scene.add(light3); var light4 = new DirectionalLight(16777215, 0.3); //补光 light4.position.set(-10, -5, -7); light4.lookAt(new Vector3(0, 0, 0)); Potree.Utils.setObjectLayers(light4, 'light'); this.scene.add(light4); //-------------------------------------------- { // background var texture = Utils.createBackgroundTexture(512, 512); texture.minFilter = texture.magFilter = NearestFilter; texture.minFilter = texture.magFilter = LinearFilter; var bg = new Mesh(new PlaneBufferGeometry(2, 2, 1), new MeshBasicMaterial({ map: texture })); bg.material.depthTest = false; bg.material.depthWrite = false; bg.name = 'bg'; //this.sceneBG.add(bg); this.scene.add(bg); bg.layers.set(Potree.config.renderLayers.bg); } { // background color var bg2 = new Mesh(new PlaneBufferGeometry(2, 2, 1), new MeshBasicMaterial({ transparent: true })); bg2.material.depthTest = false; bg2.material.depthWrite = false; bg2.name = 'bg2'; this.scene.add(bg2); bg2.layers.set(Potree.config.renderLayers.bg2); this.bg2 = bg2; } } renderOverlay() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // measure tag if (this.overlayScene.children.filter(e => e.visible).length == 0) return; var renderer = o.renderer || this.viewer.renderer; Potree.Utils.setCameraLayers(o.camera, ['sceneObjects']); viewer.dispatchEvent({ type: "render.begin2", name: 'overlays', viewport: o.viewport, renderer: o.renderer }); renderer.render(this.overlayScene, o.camera); } } ; KeyCodes.BACKSPACE = 8; //注意,这时候Potree.js中export的内容还不在Potree变量中 var texLoader$1 = new TextureLoader(); texLoader$1.crossOrigin = "anonymous"; { //defines: Potree.defines = {}; Potree.defines.Buttons = { // MouseEvent.buttons //buttons,设置按下了鼠标哪些键,是一个3个比特位的二进制值,默认为0。1表示按下主键(通常是左键),2表示按下次要键(通常是右键),4表示按下辅助键(通常是中间的键)。 NONE: 0, //add LEFT: 0b0001, RIGHT: 0b0010, MIDDLE: 0b0100 }; /* 如果访问的是button, 用THREE.MOUSE来判断: button,设置按下了哪一个鼠标按键,默认为0。-1表示没有按键,0表示按下主键(通常是左键),1表示按下辅助键(通常是中间的键),2表示按下次要键(通常是右键) */ Potree.browser = browser; /////////// add ////////////////////////////////// Potree.defines.GLCubeFaces = { GL_TEXTURE_CUBE_MAP_POSITIVE_X: 0, GL_TEXTURE_CUBE_MAP_NEGATIVE_X: 1, GL_TEXTURE_CUBE_MAP_POSITIVE_Y: 2, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: 3, GL_TEXTURE_CUBE_MAP_POSITIVE_Z: 4, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: 5 }; Potree.defines.PanoSizeClass = { BASE: 1, STANDARD: 2, HIGH: 3, ULTRAHIGH: 4 }; Potree.defines.PanoRendererEvents = { PanoRenderComplete: "panorama.render.complete", TileRenderFailure: "panorama.tile.render.failed", TileRenderSuccess: "panorama.tile.render.success", TileUploadAttempted: "panorama.tile.upload.attempted", UploadAttemptedForAllTiles: "panorama.upload.attempted.all.tiles", ZoomLevelRenderStarted: "panorama.zoom.render.started" }; Potree.defines.SceneRendererEvents = { ContextCreated: "scene-renderer-context-created", AfterRender: "after-render", MemoryUsageUpdated: "scene-renderer-memory-usage-updated" }; Potree.defines.TileDownloaderEvents = { TileDownloadSuccess: "tiledownloader.download.success", TileDownloadFailure: "tiledownloader.download.failure", PanoDownloadComplete: "tiledownloader.pano.download.complete" }; Potree.defines.Vectors = { UP: new Vector3(0, 1, 0), DOWN: new Vector3(0, -1, 0), LEFT: new Vector3(-1, 0, 0), RIGHT: new Vector3(1, 0, 0), FORWARD: new Vector3(0, 0, -1), BACK: new Vector3(0, 0, 1) }; Potree.defines.gs3d = { DepthMapRange: 1 << 16, MemoryPageSize: 65536, BytesPerFloat: 4, BytesPerInt: 4, MaxScenes: 32, ProgressiveLoadSectionSize: 262144, ProgressiveLoadSectionDelayDuration: 15, SphericalHarmonics8BitCompressionRange: 3 }; Potree.defines.DownloadStatus = Object.freeze({ None: 0, Queued: 1, ForceQueued: 2, Downloading: 3, Downloaded: 4, DownloadFailed: 5 }); Potree.defines.ModelManagerEvents = { ModelAdded: "model-added", ActiveModelChanged: "active-model-changed" }; Potree.defines.PanoramaEvents = { Enter: 'panorama.enter', Exit: 'panorama.exit', LoadComplete: "panorama.load.complete", LoadFailed: "panorama.load.failed", TileLoaded: "panorama.tile.loaded", VideoRendered: "panorama.video.rendered" }; ClipTask.SHOW_INSIDE_Big = 4; } { //Features var gl_, webgl2Support; Features.EXT_DEPTH = { isSupported: function isSupported(gl) { gl = gl || gl_; gl_ = gl; if (browser.detectIOS()) { var { major, minor, patch } = browser.iosVersion(); //console.warn('iosVersion',major,minor,patch) if (major == 15 && minor == 4 && patch == 1) { console.warn('检测到是ios15.4.1, 关闭EXT_frag_depth'); //该版本ext_depth有问题,导致clear错乱。没有解决办法先关闭。 return false; } } return typeof WebGL2RenderingContext != 'undefined' && gl instanceof WebGL2RenderingContext || gl.getExtension('EXT_frag_depth'); //shader中的GL_EXT_frag_depth需要判断一下detectIOS吗。。 } }; /* Features.getMaxMapSize = (gl)=>{ // 查询最大立方体贴图纹理尺寸 gl = gl || gl_ gl_ = gl let info = { cubeMap: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE), tex: gl.getParameter( gl.MAX_TEXTURE_SIZE ) } if(info.cubeMap < 4096){ console.warn('cubeMap最大仅支持', info.cubeMap) } return info } */ Features.webgl2RealSupport = () => { if (webgl2Support != void 0) { return webgl2Support; } var gl; try { var canvas = document.createElement('canvas'); if (window.WebGL2RenderingContext) { //遇到有设备(iphone8 plus ios14.1 型号MQ8F2CH/A)直接获取webgl2后会点云和全景图闪烁,WebGL2RenderingContext和得到的context是undefined。但是为何4dkk不会闪烁 gl = canvas.getContext('webgl2'); //麒麟系统chromium 128 到这一步才获取失败 如果直接对最终的canvas获取webgl2,会造成多viewport无法单独渲染以及clearAlpha透明失败 } } catch (e) { console.log(e); } webgl2Support = !!gl; return webgl2Support; }; } Utils.loadSkybox = function (path, oldSky, callback) { var camera, scene, parent, cameraOrtho; if (!oldSky) { parent = new Object3D("skybox_root"); camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 100000); cameraOrtho = new OrthographicCamera(-1, 1, 1, -1, Potree.config.view.near, Potree.settings.cameraFar); if (!window.axisYup) camera.up.set(0, 0, 1); //add scene = new Scene(); var skyboxBgWidth = Potree.config.skyboxBgWidth; var skyGeometry = new BoxBufferGeometry(skyboxBgWidth, skyboxBgWidth, skyboxBgWidth); var skybox = new Mesh(skyGeometry, new ShaderMaterial({ vertexShader: Shaders['skybox.vs'], fragmentShader: Shaders['skybox.fs'], side: BackSide, uniforms: { tDiffuse: { type: "t", value: null }, matrix: { type: "m4", value: new Matrix4() } }, depthTest: false, depthWrite: false })); scene.add(skybox); scene.traverse(n => n.frustumCulled = false); // z up //scene.rotation.x = Math.PI / 2; parent.children.push(camera); camera.parent = parent; } else { camera = oldSky.camera, scene = oldSky.scene; parent = oldSky.parent; cameraOrtho = oldSky.cameraOrtho; } var texture = texLoader$1.load(path, () => { console.log('loadSkybox成功', path); texture.wrapS = RepeatWrapping; texture.flipY = false; texture.magFilter = LinearFilter; texture.minFilter = LinearFilter; scene.children[0].material.uniforms.tDiffuse.value = texture; callback && callback(); viewer.dispatchEvent('content_changed'); }, null, e => { //error console.error('loadSkybox失败', path); }); return { camera, scene, parent, cameraOrtho }; }; Utils.getMousePointCloudIntersection = function (viewport, mouse, pointer, camera, viewer, pointclouds) { var pickParams = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : {}; //getIntersectByDepthTex /* let result = viewer.edlRenderer.depthTexSampler.sample(viewport, mouse)//add if(result != 'unsupport')return result */ if (!pointclouds || pointclouds.filter(e => Potree.Utils.getObjVisiByReason(e, 'datasetSelection')).length == 0) return; //console.log('getMousePointCloudIntersection') var renderer = viewer.renderer; var resolution = pickParams.resolution || (viewport === null || viewport === void 0 ? void 0 : viewport.resolution) || renderer.getSize(new Vector2()); if (pickParams.ifCenter) { pickParams.x = Math.round(resolution.x / 2); pickParams.y = Math.round(resolution.y / 2); } else { /* if(viewport){ //转换到类似整个画面时 pickParams.x = mouse.x; pickParams.y = viewport.resolution.y - mouse.y; }else{ pickParams.x = mouse.x; pickParams.y = renderer.domElement.clientHeight - mouse.y; } */ pickParams.x = mouse.x; pickParams.y = resolution.y - mouse.y; } //console.log('getMousePointCloudIntersection') /* if(!raycaster){ raycaster = new THREE.Raycaster(); raycaster.setFromCamera(pointer, camera); } */ var raycaster = new Raycaster(); raycaster.setFromCamera(pointer, camera); var ray = raycaster.ray; var selectedPointcloud = null; var closestDistance = Infinity; var closestIntersection = null; var closestPoint = null; //-----------add-------------------- var old_clipBoxes_in = new Map(); var old_clipBoxes_out = new Map(); var old_bigClipInBox = new Map(); var old_highlightBoxes = new Map(); var old_visibleNodes = new Map(); //bigClipInBox 最好也写下 var density; var sizeType; var size = new Map(); var visiMap = new Map(); var needsUpdate = false; if (pickParams.measuring || Potree.settings.displayMode == 'showPanos') { //测量或无深度图时的全景模式提高精准度. (全景模式有深度图时不会执行到这) density = Potree.settings.pointDensity; Potree.settings.pointDensity = 'magnifier'; //加载最高level pointclouds.forEach(e => { //因为全景模式的pointSizeType是fixed所以要还原下 visiMap.set(e, e.visible); e.visible = Potree.Utils.getObjVisiByReason(e, 'datasetSelection'); //先将隐藏的点云显示 if (!e.visible) return; size.set(e, e.temp.pointSize); sizeType = e.material.pointSizeType; e.material.pointSizeType = Potree.config.material.pointSizeType; //e.changePointSize(Potree.config.material.realPointSize*2, true)//更改点云大小到能铺满为止,否则容易识别不到 }); needsUpdate = true; } else { if (viewer.viewports.filter(e => !e.noPointcloud && e.active).length > 1 || pickParams.cameraChanged) { //在pick时相机和渲染时不一样的话 viewport.beforeRender && viewport.beforeRender(); needsUpdate = true; //不updatePointClouds的话hover久了会不准 因node是错的 //但依旧需要camera真的移动到那个位置才能加载出点云 } } if (!pickParams.pickClipped) { // 无视clipBoxes for (var pointcloud of pointclouds) { old_clipBoxes_in.set(pointcloud, pointcloud.clipBoxes_in); old_clipBoxes_out.set(pointcloud, pointcloud.clipBoxes_out); old_bigClipInBox.set(pointcloud, pointcloud.bigClipInBox); old_highlightBoxes.set(pointcloud, pointcloud.highlightBoxes); pointcloud.material.setClipBoxes(null, [], [], []); } needsUpdate = true; } if (needsUpdate) { for (var _pointcloud of pointclouds) { old_visibleNodes.set(_pointcloud, _pointcloud.visibleNodes); } if (window.notViewOffset) { Potree.updatePointClouds(pointclouds, camera, viewport.resolution); } else { //尽量减少点云加载的范围,集中在pick的空间(这部分以外还是会加载一些散点的) var viewWidth = Math.max(pickParams.pickWindowSize || 80, 80); //viewer.magnifier ? viewer.magnifier.viewport.resolution.x : 200 var camera_ = camera.clone(); camera_.setViewOffset(resolution.x, resolution.y, pickParams.x - viewWidth / 2, resolution.y - pickParams.y - viewWidth / 2, viewWidth, viewWidth); //注意offsetY是从上到下,和一般的不同 Potree.updatePointClouds(pointclouds, camera_, resolution); } } //------------------------------------------------ var allPointclouds = []; for (var _pointcloud2 of pointclouds) { var point = _pointcloud2.pick(viewer, viewport, camera, ray, pickParams); if (!point) { continue; } allPointclouds.push(_pointcloud2); var distance = camera.position.distanceTo(point.position); if (distance < closestDistance) { closestDistance = distance; selectedPointcloud = _pointcloud2; closestIntersection = point.position; closestPoint = point; } } //恢复 if (pickParams.measuring || Potree.settings.displayMode == 'showPanos') { Potree.settings.pointDensity = density; pointclouds.forEach(e => { if (e.visible) { e.material.pointSizeType = sizeType; //e.changePointSize(size.get(e)) } e.visible = visiMap.get(e); }); } else { /* if(viewer.viewports.filter(e=>!e.noPointcloud).length>1){ viewport.afterRender && viewport.afterRender() } */ } if (!pickParams.pickClipped) { //add for (var _pointcloud3 of pointclouds) { _pointcloud3.material.setClipBoxes(old_bigClipInBox.get(_pointcloud3), old_clipBoxes_in.get(_pointcloud3), old_clipBoxes_out.get(_pointcloud3), old_highlightBoxes.get(_pointcloud3)); } } if (needsUpdate) { for (var _pointcloud4 of pointclouds) { //不恢复的话(尤其cameraChanged时),在下次render前,再次pick可能是错的。表现为多数据集刚开始reticule消失了,直到ifPointBlockedByIntersect停止 _pointcloud4.visibleNodes = old_visibleNodes.get(_pointcloud4); } } if (selectedPointcloud) { var localNormal = closestPoint.normal && new Vector3().fromArray(closestPoint.normal); return { location: closestIntersection, distance: closestDistance, pointcloud: selectedPointcloud, point: closestPoint, pointclouds: allPointclouds, //add localNormal: localNormal, normal: localNormal === null || localNormal === void 0 ? void 0 : localNormal.clone().applyMatrix4(selectedPointcloud.rotateMatrix) //add }; } else { return null; } }; Utils.pixelsArrayToDataUrl = function (pixels, width, height) { var compressRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0.7; var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); pixels = new pixels.constructor(pixels); /* for (let i = 0; i < pixels.length; i++) { pixels[i * 4 + 3] = 255; } */ // flip vertically var bytesPerLine = width * 4; for (var i = 0; i < parseInt(height / 2); i++) { var j = height - i - 1; var lineI = pixels.slice(i * bytesPerLine, i * bytesPerLine + bytesPerLine); var lineJ = pixels.slice(j * bytesPerLine, j * bytesPerLine + bytesPerLine); pixels.set(lineJ, i * bytesPerLine); pixels.set(lineI, j * bytesPerLine); } var imageData = context.createImageData(width, height); imageData.data.set(pixels); context.putImageData(imageData, 0, 0); var dataURL = canvas.toDataURL(compressRatio); return dataURL; }; Utils.renderTargetToDataUrl = function (renderTarget, width, height, renderer) { var compressRatio = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0.7; var pixelCount = width * height; var buffer = new Uint8Array(4 * pixelCount); renderer.readRenderTargetPixels(renderTarget, 0, 0, width, height, buffer); var dataUrl = Utils.pixelsArrayToDataUrl(buffer, width, height, compressRatio); return dataUrl; }; Utils.mouseToRay = function (pointer, camera) { var vector = new Vector3(pointer.x, pointer.y, 1); var origin = new Vector3(pointer.x, pointer.y, -1); //不能用camera.position,在orbitCamera时不准 vector.unproject(camera); origin.unproject(camera); var direction = new Vector3().subVectors(vector, origin).normalize(); var ray = new Ray(origin, direction); return ray; }; Utils.getPos2d = function (point, viewport, dom, renderer) { //获取一个三维坐标对应屏幕中的二维坐标 var pos; if (math.closeTo(viewport.camera.position, point, 1e-5)) { //和相机位置重合时显示会四处飘,看是要改成一直显示中间还是隐藏? pos = new Vector3(0, 0, 1.5); //1.5是为了不可见 } else { pos = point.clone().project(viewport.camera); //比之前hotspot的计算方式写得简单 project用于3转2(求法同shader); unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera); } var size = renderer && renderer.getSize(new Vector2()); //如果是渲染到renderTarget上,resolution和dom的大小不一致。如果输出的结果给前端2d用,就使用clinetWidth,如果自己场景用,用renderer.size var w = renderer ? size.x : dom.clientWidth; var h = renderer ? size.y : dom.clientHeight; var x, y, left, top; x = (pos.x + 1) / 2 * w * viewport.width; y = (1 - (pos.y + 1) / 2) * h * viewport.height; left = viewport.left * w; top = (1 - viewport.bottom - viewport.height) * h; var inSight = pos.x <= 1 && pos.x >= -1 //是否在屏幕中 && pos.x <= 1 && pos.y >= -1; return { pos: new Vector2(left + x, top + y), // 屏幕像素坐标 vector: pos, //(范围 -1 ~ 1) trueSide: pos.z < 1, //trueSide为false时,即使在屏幕范围内可见,也是反方向的另一个不可以被渲染的点 参见Tag.update inSight: inSight, //在屏幕范围内可见, posInViewport: new Vector2(x, y) }; }; Utils.getPointerPosAtHeight = function () { var planeZ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; var pointer = arguments.length > 1 ? arguments[1] : undefined; var camera = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : viewer.mainViewport.camera; var origin = new Vector3(pointer.x, pointer.y, -1).unproject(camera), end = new Vector3(pointer.x, pointer.y, 1).unproject(camera); var dir = end.sub(origin); var r = (planeZ - origin.z) / dir.z; var x = r * dir.x + origin.x; var y = r * dir.y + origin.y; return { x, y }; }; Utils.screenPass = new function () { this.screenScene = new Scene(); this.screenQuad = new Mesh(new PlaneBufferGeometry(2, 2, 1)); this.screenQuad.material.depthTest = true; this.screenQuad.material.depthWrite = true; this.screenQuad.material.transparent = true; this.screenScene.add(this.screenQuad); this.camera = new Camera(); this.render = function (renderer, material, target, composer) { this.screenQuad.material = material; if (typeof target === 'undefined') { (composer || renderer).render(this.screenScene, this.camera); } else { var oldTarget = renderer.getRenderTarget(); renderer.setRenderTarget(target); //renderer.clear(); //有时候不能clear,如renderBG后再 (composer || renderer).render(this.screenScene, this.camera); renderer.setRenderTarget(oldTarget); } }; }(); //add Utils.computePointcloudsBound = function (pointclouds) { var boundingBox = new Box3(); pointclouds.forEach(pointcloud => { pointcloud.updateBound(); boundingBox.union(pointcloud.bound2); }); var boundSize = boundingBox.getSize(new Vector3()); var center = boundingBox.getCenter(new Vector3()); return { boundSize, center, boundingBox }; }; Utils.convertScreenPositionToNDC = function (pointer, mouse, width, height) { return pointer = pointer || new Vector2(), pointer.x = mouse.x / width * 2 - 1, pointer.y = 2 * -(mouse.y / height) + 1, pointer; }; Utils.convertNDCToScreenPosition = function (pointer, mouse, width, height) { return mouse = mouse || new Vector2(), mouse.x = Math.round((pointer.x + 1) / 2 * width), mouse.y = Math.round(-(pointer.y - 1) / 2 * height), mouse; }; Utils.getOrthoCameraMoveVec = function (pointerDelta, camera) { //获取当camera为Ortho型时 屏幕点1 到 屏幕点2 的三维距离 var cameraViewWidth = camera.right / camera.zoom; var cameraViewHeight = camera.top / camera.zoom; var moveVec = new Vector3(); moveVec.set(pointerDelta.x * cameraViewWidth, pointerDelta.y * cameraViewHeight, 0).applyQuaternion(camera.quaternion); return moveVec; }; Utils.VectorFactory = { fromArray: function fromArray(t) { if (t) { if (t.length < 2 || t.length > 3) console.error("Wrong number of ordinates for a point!"); return 3 === t.length ? new Vector3().fromArray(t) : new Vector2().fromArray(t); } }, fromArray3: function fromArray3(t) { if (t) { if (3 !== t.length) console.error("Wrong number of ordinates for a point!"); return new Vector3().fromArray(t); } }, fromArray2: function fromArray2(t) { if (t) { if (2 !== t.length) console.error("Wrong number of ordinates for a point!"); return new Vector2().fromArray(t); } }, toString: function toString(t) { return t.x.toFixed(8) + "," + t.y.toFixed(8) + "," + t.z.toFixed(3); } }; Utils.QuaternionFactory = { rot90: new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), MathUtils.degToRad(-90)), fromArray: function fromArray(t) { if (t) { if (4 !== t.length) console.error("Wrong number of ordinates for a quaternion!"); return new Quaternion(t[1], t[2], t[3], t[0]).multiply(this.rot90); } }, toArray: function toArray(t) { if (t) { var e = t.clone().multiply(a).toArray(); return [e[3], e[0], e[1], e[2]]; } }, fromLonLat: function fromLonLat(t) { if (t) return new Quaternion().setFromEuler(new Euler(t.lon, t.lat, 0)); }, toLonLat: function toLonLat(t) { if (t) { var e = new Euler().setFromQuaternion(t); return { lon: e.x, lat: e.y }; } } }; Utils.datasetPosTransform = function () { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var pointcloud = o.pointcloud || viewer.scene.pointclouds.find(e => e.dataset_id == o.datasetId); var tranMatrix; if (pointcloud) { if (Potree.settings.editType == 'merge') { tranMatrix = o.fromDataset ? pointcloud.matrixWorld : new Matrix4().copy(pointcloud.matrixWorld).invert(); } else { tranMatrix = o.fromDataset ? pointcloud.transformMatrix : pointcloud.transformInvMatrix; } } else { if (Potree.settings.intersectOnObjs) { var object = o.object || viewer.objs.children.find(e => e.dataset_id == o.datasetId); if (object) { tranMatrix = o.fromDataset ? object.matrixWorld : new Matrix4().copy(object.matrixWorld).invert(); } } } if (tranMatrix) { return new Vector3().copy(o.position).applyMatrix4(tranMatrix); } else { if (o.datasetId != void 0) { console.error("datasetPosTransform\u627E\u4E0D\u5230datasetId\u4E3A".concat(o.datasetId, "\u7684\u6570\u636E\u96C6\u6216\u6A21\u578B\uFF0C\u8BF7\u68C0\u67E5\u6570\u636E\uFF0C \u6A21\u578B\u672A\u521B\u5EFA\u6216\u5220\u9664")); //很可能是旧的热点,需要删除 } } }; Utils.datasetRotTransform = function () { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var object = o.pointcloud || viewer.scene.pointclouds.find(e => e.dataset_id == o.datasetId) || o.object || viewer.objs.children.find(e => e.dataset_id == o.datasetId); if (object) { var matrix, newMatrix, result; if (o.rotation) { matrix = new Matrix4().makeRotationFromEuler(o.rotation); } else if (o.quaternion) { matrix = new Matrix4().makeRotationFromQuaternion(o.quaternion); } else if (o.matrix) { matrix = o.matrix.clone(); } else { return; } var rotateMatrix = o.fromDataset ? object.rotateMatrix : object.rotateInvMatrix; if (!rotateMatrix) { rotateMatrix = new Matrix4().makeRotationFromEuler(object.rotation); //如果是没有漫游点的模型,在此临时获取一个,但和有漫游点的比会有所不同,因为没有初始旋转(如转90度) if (o.toDataset) { rotateMatrix.invert(); } } newMatrix = new Matrix4().multiplyMatrices(rotateMatrix, matrix); if (o.getRotation) { result = new Euler().setFromRotationMatrix(newMatrix); } else if (o.getQuaternion) { result = new Quaternion().setFromRotationMatrix(newMatrix); } else if (o.getMatrix) { result = newMatrix; } return result; } }; Utils.isInsideFrustum = function (bounding, camera) { // bounding是否在视野范围内有可见部分(视野就是一个锥状box) var frustumMatrix = new Matrix4(); frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); var frustum = new Frustum(); frustum.setFromProjectionMatrix(frustumMatrix); if (bounding instanceof Sphere) { return frustum.intersectsSphere(bounding); } else { return frustum.intersectsBox(bounding); } }; Utils.isIntersectBox = function (object, boxMatrix) { //object是否有在box中的部分。 object可以是点或者bounding, box原为1*1*1,但可能形变 //let frustum = new THREE.Frustum(); //frustum.setFromProjectionMatrix(boxMatrixInverse) --错 var px = new Vector3(+0.5, 0, 0).applyMatrix4(boxMatrix); var nx = new Vector3(-0.5, 0, 0).applyMatrix4(boxMatrix); var py = new Vector3(0, +0.5, 0).applyMatrix4(boxMatrix); var ny = new Vector3(0, -0.5, 0).applyMatrix4(boxMatrix); var pz = new Vector3(0, 0, +0.5).applyMatrix4(boxMatrix); var nz = new Vector3(0, 0, -0.5).applyMatrix4(boxMatrix); var pxN = new Vector3().subVectors(nx, px).normalize(); var nxN = pxN.clone().multiplyScalar(-1); var pyN = new Vector3().subVectors(ny, py).normalize(); var nyN = pyN.clone().multiplyScalar(-1); var pzN = new Vector3().subVectors(nz, pz).normalize(); var nzN = pzN.clone().multiplyScalar(-1); var pxPlane = new Plane().setFromNormalAndCoplanarPoint(pxN, px); var nxPlane = new Plane().setFromNormalAndCoplanarPoint(nxN, nx); var pyPlane = new Plane().setFromNormalAndCoplanarPoint(pyN, py); var nyPlane = new Plane().setFromNormalAndCoplanarPoint(nyN, ny); var pzPlane = new Plane().setFromNormalAndCoplanarPoint(pzN, pz); var nzPlane = new Plane().setFromNormalAndCoplanarPoint(nzN, nz); var frustum = new Frustum(pxPlane, nxPlane, pyPlane, nyPlane, pzPlane, nzPlane); if (object instanceof Box3) { var boxBound = new Box3(new Vector3(-0.5, -0.5, -0.5), new Vector3(0.5, 0.5, 0.5)).applyMatrix4(boxMatrix); //large boundingbox if (!object.intersectsBox(boxBound)) return; return frustum.intersectsBox(object); //根据该函数, 若存在某个plane在box上的对应点都在plane背面,则不相交. 可得知在box构成的frustum倾斜时不准确,不相交也判断为相交,甚至不如bound相交准确。所以前面加步骤排除下,但仍不完全准确。(可在裁剪中将box放置到数据集上方旋转下校验) } else if (object instanceof Array) { //点合集, 只能粗略计算下 var sphere = new Sphere(); sphere.setFromPoints(object); return this.isIntersectBox(sphere, boxMatrix); } else if (object instanceof Sphere) { return frustum.intersectsSphere(object); } else if (object instanceof Vector3) { return frustum.containsPoint(object); } else if (object instanceof Matrix4) {//第一个参数如果和第二个参数一样都是box的worldMatrix } /* containsPoint: ƒ containsPoint( point ) intersectsBox: ƒ intersectsBox( box ) intersectsObject: ƒ intersectsObject( object )//geo intersectsSphere: ƒ intersectsSphere( sphere ) intersectsSprite: ƒ intersectsSprite( sprite ) */ }; Utils.getIntersect = function (camera, meshes, pointer, raycaster) { //获取鼠标和meshes交点 if (!raycaster) { //getMouseIntersect camera.updateMatrixWorld(); raycaster = new Raycaster(); var origin = new Vector3(pointer.x, pointer.y, -1).unproject(camera), end = new Vector3(pointer.x, pointer.y, 1).unproject(camera); var dir = end.sub(origin).normalize(); raycaster.set(origin, dir); } meshes.forEach(e => { raycaster.layers.enable(math.getBaseLog(2, e.layers.mask)); }); var n = raycaster.intersectObjects(meshes); if (0 === n.length) return null; return n[0]; }; Utils.addOrRemoveDefine = function (material, defineName, type) { var value = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : ''; var defines = material.defines; if (type == 'add') { if (defines[defineName] != void 0 && defines[defineName] == value) return; defines[defineName] = value; } else { if (defines[defineName] == void 0) return; delete defines[defineName]; } material.needsUpdate = true; }; Utils.makeTexDontResize = function (map) { //避免贴图因非2的次方而缩小。小心使用 if (!map || !map.image) { return console.log('!map || !map.image', map, map && map.image); } if (MathUtils.isPowerOfTwo(map.image.width) && MathUtils.isPowerOfTwo(map.image.height)) return; map.wrapS = map.wrapT = ClampToEdgeWrapping; //原默认 RepeatWrapping map.minFilter = LinearFilter; // or THREE.NearestFilter 原默认 LinearMipmapLinearFilter map.generateMipmaps = false; map.needsUpdate = true; }; Utils.updateVisible = function (object, reason, ifShow) { var level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var type = arguments.length > 4 ? arguments[4] : undefined; var needRender = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true; //当所有加入的条件都不为false时才显示. reason='force'一般是强制、临时的 if (!object.unvisibleReasons) object.unvisibleReasons = []; //如果length>0代表不可见 if (!object.visibleReasons) object.visibleReasons = []; //在同级时,优先可见 var update = function update() { //先按从高到低的level排列 object.unvisibleReasons = object.unvisibleReasons.sort((a, b) => b.level - a.level); object.visibleReasons = object.visibleReasons.sort((a, b) => b.level - a.level); var maxVisiLevel = object.visibleReasons[0] ? object.visibleReasons[0].level : -1; var maxunVisiLevel = object.unvisibleReasons[0] ? object.unvisibleReasons[0].level : -1; var shouldVisi = maxVisiLevel >= maxunVisiLevel; var visiBefore = object.visible; if (visiBefore != shouldVisi) { object.visible = shouldVisi; object.dispatchEvent({ type: 'isVisible', visible: shouldVisi, reason }); needRender && viewer.dispatchEvent('content_changed'); } }; if (ifShow) { var index = object.unvisibleReasons.findIndex(e => e.reason == reason); if (index > -1) { type = 'cancel'; object.unvisibleReasons.splice(index, 1); } if (type == 'add') { if (!object.visibleReasons.some(e => e.reason == reason)) { object.visibleReasons.push({ reason, level }); } } } else { var index = object.visibleReasons.findIndex(e => e.reason == reason); if (index > -1) { type = 'cancel'; object.visibleReasons.splice(index, 1); } if (type != 'cancel') { if (!object.unvisibleReasons.some(e => e.reason == reason)) { object.unvisibleReasons.push({ reason, level }); } } } update(); }; /* 复杂案例: 如果物体默认隐藏, 当符合任何一个其他条件时可见,则可: Potree.Utils.updateVisible(this, "default", false, 0 ) //默认隐藏 Potree.Utils.updateVisible(this, 条件名, ifShow, 1, ifShow?'add':'cancel' ) //其他的条件 */ Utils.getObjVisiByReason = function (object, reason) { //获取在某条件下是否可见. 注: 用户在数据集选择可不可见为"datasetSelection" if (object.visible) return true;else { return !object.unvisibleReasons || !object.unvisibleReasons.some(e => e.reason == reason); } }; Utils.setCameraLayers = function (camera, enableLayers) { var extraEnableLayers = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; //add camera.layers.disableAll(); enableLayers.concat(extraEnableLayers).forEach(e => { var layer = Potree.config.renderLayers[e]; if (layer == void 0) { console.error('setCameraLayer没找到layer!', e); return; } camera.layers.enable(layer); }); }; Utils.setObjectLayers = function (object, layerName) { //add var layer = Potree.config.renderLayers[layerName]; if (layer == void 0) { console.error('setObjectLayers没找到layer!', layerName); return; } object.traverse(e => { e.layers.set(layer); }); }; Utils.imgAddText = async (img, text, labelInfo) => { var label = new Potree.TextSprite(Object.assign({ //如果直接在canvas里写字,要另外写很多和canvas.drawText有关的,所以还是借助textSprite吧 backgroundColor: { r: 0, g: 0, b: 0, a: 0 }, textColor: { r: 255, g: 255, b: 255, a: 1 }, margin: { x: 3, y: 3 }, renderOrder: 50, fontsize: 20, text }, labelInfo)); var labelImg = new Image(); labelImg.src = label.sprite.material.map.image.toDataURL('image/png'); return new Promise((resolve, reject) => { labelImg.onload = () => { if (labelInfo.horizonCenter) { //水平居中(对img来说) labelInfo.leftRatioToImg = 0.5 - labelImg.width / img.width / 2; } var result = Common.imgAddLabel(img, labelImg, labelInfo); label.dispose(); resolve(result); }; }); }; Utils.combineImgs = async (imgs, compressRatio, width, height) => { //拼合图片,顺序从上到下从左到右, 每张图大小不一定一致,但同列的宽一致,同行宽一致 return new Promise((resolve, reject) => { var item = imgs[0][0]; var wc = imgs.length, hc = imgs[0].length, loadCount = 0, amount = wc * hc; width = width || imgs.reduce((w, c) => w + c[0].width, 0), //相加得到的可能比想要得到的大几个像素,之后会重叠 height = height || imgs[0].reduce((w, c) => w + c.height, 0); var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; var context = canvas.getContext('2d'); for (var i = 0; i < wc; i++) { var _loop = function _loop() { var img = new Image(); img.src = imgs[i][j].dataUrl; img.index = { i, j }; img.onload = () => { loadCount++; context.drawImage(img, img.index.i * img.width, img.index.j * img.height, img.width, img.height); if (loadCount == amount) { var dataUrl = canvas.toDataURL('image/png', compressRatio); context.clearRect(0, 0, width, height); resolve(dataUrl); } }; }; for (var j = 0; j < hc; j++) { _loop(); } } }); }; BinaryLoader.prototype.load = function (node, callback) { //解析点云 if (node.loaded) { return; } var url = node.getURL(); if (this.version.equalOrHigher('1.4')) { url += '.bin'; } url += '?m=' + node.pcoGeometry.timeStamp; //add var xhr = XHRFactory.createXMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'arraybuffer'; xhr.overrideMimeType('text/plain; charset=x-user-defined'); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if ((xhr.status === 200 || xhr.status === 0) && xhr.response !== null) { var buffer = xhr.response; this.parse(node, buffer, callback); } else { node.loadFailed = 'status:' + xhr.status + ",url:" + url; Potree.numNodesLoading--; //console.error(`Failed to load file! HTTP status: ${xhr.status}, file: ${url}`); throw new Error("Failed to load file! HTTP status: ".concat(xhr.status, ", file: ").concat(url)); } } }; try { xhr.send(null); } catch (e) { node.loadFailed = 'catchError'; Potree.numNodesLoading--; console.error('加载点云node出错 ', url, e); } }; PointAttribute.RGBA_PACKED = new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_INT8, 4); PointAttribute.COLOR_PACKED = PointAttribute.RGBA_PACKED; PointAttribute.INTENSITY = new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1); PointAttribute.CLASSIFICATION = new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1); PointAttribute.GPS_TIME = new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1); ProfileWindow.prototype.initTHREE = function () { this.renderer = new WebGLRenderer({ alpha: true, premultipliedAlpha: false }); this.renderer.setClearColor(0x000000, 0); this.renderer.setSize(10, 10); this.renderer.autoClear = false; this.renderArea.append($(this.renderer.domElement)); this.renderer.domElement.tabIndex = '2222'; $(this.renderer.domElement).css('width', '100%'); $(this.renderer.domElement).css('height', '100%'); { var gl = this.renderer.getContext(); if (gl.createVertexArray == null) { var extVAO = gl.getExtension('OES_vertex_array_object'); if (!extVAO) { throw new Error("OES_vertex_array_object extension not supported"); } gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO); gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO); } } this.camera = new OrthographicCamera(-1000, 1000, 1000, -1000, -1000, 1000); this.camera.up.set(0, 0, 1); this.camera.rotation.order = "ZXY"; this.camera.rotation.x = Math.PI / 2.0; this.scene = new Scene(); this.profileScene = new Scene(); var sg = new SphereGeometry(1, 16, 16); var sm = new MeshNormalMaterial(); this.pickSphere = new Mesh(sg, sm); this.scene.add(this.pickSphere); this.viewerPickSphere = new Mesh(sg, sm); }; //Potree_update_visibility Potree.updatePointClouds = function (pointclouds, camera, areaSize) { viewer.addTimeMark('updateClouds', 'start'); for (var pointcloud of pointclouds) { var start = performance.now(); for (var profileRequest of pointcloud.profileRequests) { profileRequest.update(); var _duration = performance.now() - start; if (_duration > 5) { break; } } var duration = performance.now() - start; } var result = Potree.updateVisibility(pointclouds, camera, areaSize); for (var _pointcloud5 of pointclouds) { //pointcloud.updateMaterial(pointcloud.material, pointcloud.visibleNodes, camera, renderer);//转移到渲染时 _pointcloud5.updateVisibleBounds(); } Potree.lru.freeMemory(); //即Potree.lru 能看到所有在加载的node viewer.addTimeMark('updateClouds', 'end'); return result; }; Potree.updateVisibilityStructures = function (pointclouds, camera, areaSize) { var frustums = {}; var camObjPositions = {}; var camObjDirs = {}; //add var priorityQueue = new BinaryHeap(function (x) { return -x.weight; /* 1 / x.weight; */ }); //二叉堆。 改,之前的weight不支持负数 viewer.addTimeMark('visiStructure', 'start'); //camera.updateMatrixWorld(); var viewI = camera.matrixWorldInverse; var proj = camera.projectionMatrix; var view = camera.matrixWorld; var projViewI = new Matrix4().multiply(proj).multiply(viewI); /* let list = pointclouds // stopWhenAllUsed = !viewer.lastFrameChanged let min = 5, max = Math.max(20 , Math.round(list.length / 10 )) let result = Common.batchHandling.getSlice('pcGetFrustum', list, { min,max, durBound1: 3, durBound2: 10} ) //iphonex稳定后大概在7-10。 */ for (var i = 0; i < pointclouds.length; i++) { var pointcloud = pointclouds[i]; if (!pointcloud.initialized()) { continue; } /* let info = history.get(pointcloud) if() */ pointcloud.numVisibleNodes = 0; pointcloud.numVisiblePoints = 0; pointcloud.deepestVisibleLevel = 0; pointcloud.visibleNodes = []; pointcloud.visibleGeometry = []; // 因漫游模式而隐藏的话 依旧需要加入visibleNodes,因为pick需要 /* if (pointcloud.visible && pointcloud.root !== null) { priorityQueue.push({pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE}); } */ if (pointcloud.visible || !pointcloud.hasDepthTex && pointcloud.unvisibleReasons && pointcloud.unvisibleReasons.length == 1 && pointcloud.unvisibleReasons[0].reason == 'displayMode' && pointcloud.root !== null) { //改 visible -> priorityQueue.push({ pointcloud: i, node: pointcloud.root, weight: Number.MAX_VALUE }); } else { continue; } // frustum in object space var frustum = new Frustum(); var world = pointcloud.matrixWorld; // use close near plane for frustum intersection /* let frustumCam = camera.clone(); frustumCam.zoom = camera.zoom //add frustumCam.near = Math.min(camera.near, 0.1); frustumCam.updateProjectionMatrix(); */ //----没用到frustumCam,删了 var fm = new Matrix4().multiply(projViewI).multiply(world); frustum.setFromProjectionMatrix(fm); frustums[i] = frustum; //frustums.push(frustum); // camera position in object space var worldI = pointcloud.matrixWorldInverse; var camMatrixObject = new Matrix4().multiply(worldI).multiply(view); //假设点云无变换的话,相机相对于点云的变换矩阵 var camObjPos = new Vector3().setFromMatrixPosition(camMatrixObject); camObjPositions[i] = camObjPos; //camObjPositions.push(camObjPos); var quaternion = new Quaternion().setFromRotationMatrix(camMatrixObject); var camDir = new Vector3(0, 0, -1).applyQuaternion(quaternion); camObjDirs[i] = camDir; // hide all previously visible nodes // if(pointcloud.root instanceof PointCloudOctreeNode){ // pointcloud.hideDescendants(pointcloud.root.sceneNode); // } if (pointcloud.root.isTreeNode()) { pointcloud.hideDescendants(pointcloud.root.sceneNode); } for (var j = 0; j < pointcloud.boundingBoxNodes.length; j++) { pointcloud.boundingBoxNodes[j].visible = false; } } viewer.addTimeMark('visiStructure', 'end'); return { 'frustums': frustums, 'camObjPositions': camObjPositions, 'priorityQueue': priorityQueue, camObjDirs }; }; Potree.updateVisibility = function (pointclouds, camera, areaSize) { var numVisibleNodes = 0; var numVisiblePoints = 0; var numVisiblePointsInPointclouds = new Map(pointclouds.map(pc => [pc, 0])); var visibleNodes = []; var visibleGeometry = []; var unloadedGeometry = []; var lowestSpacing = Infinity; // calculate object space frustum and cam pos and setup priority queue var s = Potree.updateVisibilityStructures(pointclouds, camera, areaSize); //得到相机可见范围 var frustums = s.frustums; var camObjPositions = s.camObjPositions; var priorityQueue = s.priorityQueue; var camObjDirs = s.camObjDirs; var loadedToGPUThisFrame = 0; var domWidth = areaSize.x; //renderer.domElement.clientWidth; var domHeight = areaSize.y; //renderer.domElement.clientHeight; var fov = camera.fov * Math.PI / 180; var slope = Math.tan(fov / 2); var projFactor0 = 0.5 * domHeight / slope; // check if pointcloud has been transformed // some code will only be executed if changes have been detected if (!Potree._pointcloudTransformVersion) { Potree._pointcloudTransformVersion = new Map(); } var pointcloudTransformVersion = Potree._pointcloudTransformVersion; for (var pointcloud of pointclouds) { if (pointcloud.hasDepthTex ? !pointcloud.visible : !Potree.Utils.getObjVisiByReason(pointcloud, 'datasetSelection')) { //改 visible -> continue; } //if(!pointcloud.visible) continue pointcloud.updateMatrixWorld(); if (!pointcloudTransformVersion.has(pointcloud)) { pointcloudTransformVersion.set(pointcloud, { number: 0, transform: pointcloud.matrixWorld.clone() }); } else { var version = pointcloudTransformVersion.get(pointcloud); if (!version.transform.equals(pointcloud.matrixWorld)) { version.number++; version.transform.copy(pointcloud.matrixWorld); pointcloud.dispatchEvent({ type: "transformation_changed", target: pointcloud }); } } } var _loop2 = function _loop2() { var element = priorityQueue.pop(); //取出权重最大的一个 var node = element.node; var parent = element.parent; var pointcloud = pointclouds[element.pointcloud]; // { // restrict to certain nodes for debugging // let allowedNodes = ["r", "r0", "r4"]; // if(!allowedNodes.includes(node.name)){ // continue; // } // } var box = node.getBoundingBox(); var frustum = frustums[element.pointcloud]; var camObjPos = camObjPositions[element.pointcloud]; if (!frustum) return 0; // continue //add var camObjDir = camObjDirs[element.pointcloud]; var insideFrustum = frustum.intersectsBox(box); var maxLevel = pointcloud.maxLevel == void 0 ? Infinity : pointcloud.maxLevel; var minLevel = pointcloud.minLevel == void 0 ? 0 : pointcloud.minLevel; //add var level = node.getLevel(); var visible = insideFrustum; visible = visible && !(numVisiblePoints + node.getNumPoints() > Potree.pointBudget); visible = visible && !(numVisiblePointsInPointclouds.get(pointcloud) + node.getNumPoints() > pointcloud.pointBudget); // pointcloud.pointBudget一直是Infinity visible = visible && level <= maxLevel /* && level >= minLevel */; //< 改为 <= //visible = visible || node.getLevel() <= 2; var pcWorldInverse = pointcloud.matrixWorld.clone().invert(); /* let m = pcWorldInverse.elements let pcWorldInvM3 = new THREE.Matrix3().set(m[0],m[4],m[12],m[1],m[5],m[13],m[3],m[7],m[15]) //去掉z的 */ //pointcloud.pcMatrix3 = new THREE.Matrix3().set(m[0],m[4],m[12],m[1],m[5],m[13],m[3],m[7],m[15]) //去掉z的 var intersectBox = clipBox => { var toPCObject = pcWorldInverse.clone().multiply(clipBox.box.matrixWorld); //box乘上点云逆矩阵 return Potree.Utils.isIntersectBox(box, toPCObject); }; //改 总共两种box : 可见和不可见(都是并集) var clipBoxes_in = pointcloud.material.clipBoxes_in; var clipBoxes_out = pointcloud.material.clipBoxes_out; var bigClipInBox = pointcloud.material.bigClipInBox; if (visible && bigClipInBox) { //不在剪裁下载的框内 if (!intersectBox(bigClipInBox)) { visible = false; } } if (visible && clipBoxes_in.length > 0) { //当有可见box时,需要在任一可见box内才可见 var visi = false; for (var _i = 0, length = clipBoxes_in.length; _i < length; _i++) { if (intersectBox(clipBoxes_in[_i])) { visi = true; break; } } if (!visi) { visible = false; } } //outside不做处理。因为node必须完全在clipBox内才能完全隐藏,而这里的intersect只能识别出部分在clipBox内。因而只能说明不在任意一个box内绝对可见,没有意义,这里需要找出不可见的。 if (visible) { var prism = pointcloud.material.activeAttributeName == 'prismHeight' && pointcloud.material.prisms && pointcloud.material.prisms.find(e => e.computing); if (prism) { var bound = box.clone().applyMatrix4(pointcloud.matrixWorld); if (bound.intersectsBox(prism.prismBound)) { /* //node box是否包含points中的一个点 let box2 = new THREE.Box2().copy(box) let points2d = prisms.points.map(e=>new THREE.Vector2().copy(e).applyMatrix3(pcWorldInvM3)) let intersect = points2d.some(e=>{ return box2.containsPoint(e) }) if(!intersect){ //或者多边形中是否包含node box中的一个点 intersect = [ new THREE.Vector2(box.min.x, box.min.y), new THREE.Vector2(box.max.x, box.max.y), new THREE.Vector2(box.min.x, box.max.y), new THREE.Vector2(box.max.x, box.min.y), ].some(e=>{ if(math.isPointInArea(points2d, null, e) ){ return true } }) //z是不是在外层已经判断好了? if(!intersect){ visible = false } } */ //会有两个互不包含点但是交叉了的情况,所以就不仔细判断了(如横竖两个矩形构成十字架) } else visible = false; } } if (node.spacing) { lowestSpacing = Math.min(lowestSpacing, node.spacing); } else if (node.geometryNode && node.geometryNode.spacing) { lowestSpacing = Math.min(lowestSpacing, node.geometryNode.spacing); } if (numVisiblePoints + node.getNumPoints() > Potree.pointBudget) { viewer.dispatchEvent({ type: 'overPointBudget', restQueueSize: priorityQueue.size(), numVisiblePoints }); return 1; // break } if (!visible) { return 0; // continue } // TODO: not used, same as the declaration? // numVisibleNodes++; numVisiblePoints += node.getNumPoints(); var numVisiblePointsInPointcloud = numVisiblePointsInPointclouds.get(pointcloud); numVisiblePointsInPointclouds.set(pointcloud, numVisiblePointsInPointcloud + node.getNumPoints()); pointcloud.numVisibleNodes++; pointcloud.numVisiblePoints += node.getNumPoints(); if (node.isGeometryNode() && (!parent || parent.isTreeNode())) { if (node.isLoaded() && loadedToGPUThisFrame < 2) { node = pointcloud.toTreeNode(node, parent); loadedToGPUThisFrame++; } else { //console.log('unloadedGeometry',node) unloadedGeometry.push({ pointcloud, node }); //加载点云。虽然还没加载,但也计入了visibleNodes,只是无children,numPoints=0 visibleGeometry.push(node); } } if (node.isTreeNode()) { Potree.lru.touch(node.geometryNode); //在缓存中计入点云 node.sceneNode.visible = true; node.sceneNode.material = pointcloud.material; level >= minLevel && visibleNodes.push(node); level >= minLevel && pointcloud.visibleNodes.push(node); //if(Potree.settings.sortNodesDis){//add if (pointcloud.material.opacity < 1 && Potree.settings.notAdditiveBlending) { var nodePos = node.getBoundingSphere().center; var toCam = new Vector3().subVectors(nodePos, camObjPos); toCam.projectOnVector(camObjDir); node.disSqToCamZ_ = toCam.lengthSq(); } if (node._transformVersion === undefined) { node._transformVersion = -1; } var transformVersion = pointcloudTransformVersion.get(pointcloud); if (node._transformVersion !== transformVersion.number) { node.sceneNode.updateMatrix(); //node.sceneNode.matrixWorld.multiplyMatrices(pointcloud.matrixWorld, node.sceneNode.matrix); node.sceneNode.matrixWorld.multiplyMatrices(pointcloud.matrixWorld, node.sceneNode.matrix); node._transformVersion = transformVersion.number; } if (pointcloud.showBoundingBox && !node.boundingBoxNode && node.getBoundingBox) { var colorHue = level / (maxLevel + 1); var _s = 0.1 + level / (maxLevel + 1); var color = new Color().setHSL(colorHue, _s, _s); var boxHelper = new Box3Helper$1(node.getBoundingBox(), color); boxHelper.matrixAutoUpdate = false; pointcloud.boundingBoxNodes.push(boxHelper); node.boundingBoxNode = boxHelper; node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld); } else if (pointcloud.showBoundingBox) { node.boundingBoxNode.visible = true; node.boundingBoxNode.matrix.copy(pointcloud.matrixWorld); } else if (!pointcloud.showBoundingBox && node.boundingBoxNode) { node.boundingBoxNode.visible = false; } // if(node.boundingBoxNode !== undefined && exports.debug.allowedNodes !== undefined){ // if(!exports.debug.allowedNodes.includes(node.name)){ // node.boundingBoxNode.visible = false; // } // } } // add child nodes to priorityQueue 由近及远、由大及小逐渐加载 var children = node.getChildren(); for (var _i2 = 0; _i2 < children.length; _i2++) { var child = children[_i2]; var weight = 0; if (camera.isPerspectiveCamera) { var sphere = child.getBoundingSphere(); var center = sphere.center; var dd = sphere.center.distanceToSquared(camObjPos); var addPow = 0.2; //viewer.mainViewport.view.isFlying() ? 0 : 0.5 //0-0.5,正常原本是0. 数字越大近处加载越快。但会造成远处加载慢甚至因pointBudge限制不加载。 isFlying:漫游时需要尽量加载一下远处的点云 //addPow *= window.devicePixelRatio //devicePixelRatio高的手机需要优先加载最近的高级点云,减少远处的中高级点云。 var distance = Math.pow(dd, 0.5 + addPow); //Math.sqrt(dd); //提高距离权重,为了提高近处加载速度。 某些场景近处加载慢优化明显,如SS-t-cqCAL6rJ5i //let attenuateDis = 10;//add var radius = sphere.radius; var projFactor = projFactor0 / distance; var screenPixelRadius = radius * projFactor; /* if(distance > attenuateDis){ screenPixelRadius -= (distance - attenuateDis) * Math.sqrt(radius) * projFactor0 * 0.002 } */ //screenPixelRadius 和 domHeight 成正比,所以手机横屏后screenPixelRadius会变小。这是正常的,因为vhov不变,相同物体高度在横屏后高度变小,所需要的密度不需要那么高了。但hfov横屏后扩大,所以可见的node范围变大,又增加了一些可见node;只是总体的可见node还是减少了。 //使用hfov和domWidth计算结果相同。 if (screenPixelRadius < pointcloud.minimumNodePixelSize / Math.pow(dd, addPow)) { //理论上因手机像素小,更不容易堆叠铺满,minimumNodePixelSize应该除以window.deviceRatio 但会造成加载过多,而内存小 continue; } weight = screenPixelRadius; if (!sphere.containsPoint(camObjPos)) { //add 优先加载屏幕中央的点云(手机端缩小离远效果明显,不会那么稀疏) var dir = new Vector3().subVectors(center, camObjPos).normalize(); var cos = 1 + dir.dot(camObjDir); //0-2 weight *= cos / 2; //Math.pow(cos,0.5) //幂越高,旁边的容易加载不到,出现缺块 如SS-t-7DUfWAUZ3V } if (distance - radius < 0) { weight = Number.MAX_VALUE; } //如果能得到每个方向上的密度,也就是node数量,密度大的远处少加载,因为被遮挡了显示也没有意义,就好了。 } else { // TODO ortho visibility //let bb = child.getBoundingBox(); var _sphere = child.getBoundingSphere(); //let diagonal = bb.max.clone().sub(bb.min).length(); var reduce = 0; //0-0.5,正常原本是0. if (_sphere.radius * /* Math.pow( */camera.zoom /* ,1-reduce) */ < pointcloud.minimumNodePixelSize) { continue; } var _distance = _sphere.center.distanceToSquared(camObjPos); //先加载中间然后四周 weight = _sphere.radius / _distance; /* let vec = new THREE.Vector3().subVectors(sphere.center, camObjPos) let disOnCamDir = vec.dot(camObjDir) let vecOnCamDir = camObjDir.clone().multiplyScalar(disOnCamDir) let vecSide = new THREE.Vector3().subVectors(vec, vecOnCamDir) //在屏幕上从中心到该node的向量 let disSide = vecSide.length() //weight = sphere.radius / disSide * camera.zoom - disOnCamDir * 2; //如果用除的,ortho的camera离远了的话dis的影响就小了 weight = sphere.radius / ( disSide * 0.1 + disOnCamDir * 14 ) */ //weight = diagonal; } priorityQueue.push({ pointcloud: element.pointcloud, node: child, parent: node, weight: weight }); //貌似好像二叉堆中子节点和父节点没什么关系,就只是为了方便排序层层遍历 } //手机上像素点更小,所以远处感觉会更稀疏 }, _ret; while (priorityQueue.size() > 0) { _ret = _loop2(); if (_ret === 0) continue; if (_ret === 1) break; } // end priority queue loop { // update DEM 这是什么 var maxDEMLevel = 4; var candidates = pointclouds.filter(p => p.generateDEM && p.dem instanceof Potree.DEM); for (var _pointcloud6 of candidates) { var updatingNodes = _pointcloud6.visibleNodes.filter(n => n.getLevel() <= maxDEMLevel); _pointcloud6.dem.update(updatingNodes); } } unloadedGeometry = unloadedGeometry.filter(e => !e.loadFailed); //过滤加载失败的,否则有失败的就无法发送加载完成 if (unloadedGeometry.length) { //加载点云 var idleCount = Common.getBestCountFPS('unloadedGeometry', false, 1, 3); //即使静止,因为加载不仅影响这一帧,所以低fps还是加载少一些 var maxNodesLoading = Common.getBestCount('unloadedGeometry', viewer.lastFrameChanged ? 1 : idleCount, idleCount + 4, 3, 14 /* , true */); //dur在iphoneX中静止有7,pc是2 //!lastFrameChanged静止时加速下载 //THREE.Math.clamp(Math.round(9 - dur), 1, 6 ) //console.log('unloadedGeometry', unloadedGeometry.length) //主要在手机端有效果。不改之前在展示的点云较多时前进会卡。 for (var i = 0; i < Math.min(maxNodesLoading, unloadedGeometry.length); i++) { unloadedGeometry[i].node.load(unloadedGeometry[i].pointcloud.pcoGeometry); } if (!Potree.pointsLoading) { Potree.pointsLoading = true; //console.log('startLoad') viewer.dispatchEvent('startLoadPoints'); } } else { if (Potree.pointsLoading) { Potree.pointsLoading = false; //console.log('load done!') setTimeout(() => { Potree.pointsLoading || viewer.dispatchEvent('pointsLoaded'); }, document.hidden ? 3000 : 50); //hidden时可能好几秒才更新一次,所以这个并不准 } } Potree.unloadedGeometry = unloadedGeometry; //add: Potree.numVisiblePoints = numVisiblePoints; Potree.visibleNodes = visibleNodes; //if(Potree.settings.sortNodesDis){ //let s = performance.now() for (var _pointcloud7 of pointclouds) { if (_pointcloud7.material.opacity < 1 && Potree.settings.notAdditiveBlending) { //排序。如果能所有点云一起排序更好,这样遮挡更正确 _pointcloud7.visibleNodes.sort((a, b) => { return b.disSqToCamZ_ - a.disSqToCamZ_; }); } } //console.log(performance.now() - s) //} return { visibleNodes: visibleNodes, numVisiblePoints: numVisiblePoints, lowestSpacing: lowestSpacing }; }; Potree.numVisiblePoints = 0; /* note: 缓存中的点数 Potree.lru.numPoints 一般会 大于 每个点云显示点总数的numVisiblePoints 当超出缓冲区最大点云数时,加载的点云节点会被dispose彻底消除;否则,隐藏的节点就会等待再次被使用显示 由于加载按照由近及远、由大及小的顺序,要降低卡顿,就只需要降低Potree.pointBudget即可。但目前只设置了三个层次;另外提供maxLevel细节调节,能显示更均匀. 最好多一个调节pointBudge的滑动条 Potree.lru.numPoints Potree.numVisiblePoints viewer.scene.pointclouds[0].visibleNodes.length */ { //HQSplatRenderer var oldInit = HQSplatRenderer.prototype.init; HQSplatRenderer.prototype.init = function () { oldInit(); viewer.addEventListener('resize', this.resize.bind(this)); }; HQSplatRenderer.prototype.resize = function (e) { this.rtDepth.setSize(e.canvasWidth, e.canvasHeight); this.rtAttribute.setSize(e.canvasWidth, e.canvasHeight); }; HQSplatRenderer.prototype.clear = function () { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this.init(); var { renderer, background } = this.viewer; if (background === "skybox") { renderer.setClearColor(0x000000, 0); } else if (background === 'gradient') { renderer.setClearColor(0x000000, 0); } else if (background === 'black') { renderer.setClearColor(0x000000, 1); } else if (background === 'white') { renderer.setClearColor(0xFFFFFF, 1); } else { renderer.setClearColor(0x000000, 0); } params.target || renderer.clear(); this.clearTargets(params); }; HQSplatRenderer.prototype.render = function () { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; this.init(); var viewer = this.viewer; var camera = params.camera ? params.camera : viewer.scene.getActiveCamera(); var { width, height } = params.width ? params : this.viewer.renderer.getSize(new Vector2()); viewer.renderer.setRenderTarget(params.target || null); viewer.dispatchEvent({ type: "render.pass.begin", viewer: viewer }); //params.target || this.resize(width, height); var visiblePointClouds = viewer.scene.pointclouds.filter(pc => pc.visible); var originalMaterials = new Map(); for (var pointcloud of visiblePointClouds) { originalMaterials.set(pointcloud, pointcloud.material); if (!this.attributeMaterials.has(pointcloud)) { var attributeMaterial = new ExtendPointCloudMaterial(); this.attributeMaterials.set(pointcloud, attributeMaterial); } if (!this.depthMaterials.has(pointcloud)) { var depthMaterial = new ExtendPointCloudMaterial(); depthMaterial.setDefine("depth_pass", "#define hq_depth_pass"); depthMaterial.setDefine("use_edl", "#define use_edl"); this.depthMaterials.set(pointcloud, depthMaterial); } } { // DEPTH PASS for (var _pointcloud8 of visiblePointClouds) { var octreeSize = _pointcloud8.pcoGeometry.boundingBox.getSize(new Vector3()).x; var material = originalMaterials.get(_pointcloud8); var _depthMaterial = this.depthMaterials.get(_pointcloud8); _depthMaterial.size = material.size; _depthMaterial.minSize = material.minSize; _depthMaterial.maxSize = material.maxSize; _depthMaterial.pointSizeType = material.pointSizeType; _depthMaterial.visibleNodesTexture = material.visibleNodesTexture; _depthMaterial.weighted = false; _depthMaterial.screenWidth = width; _depthMaterial.shape = PointShape.CIRCLE; _depthMaterial.screenHeight = height; _depthMaterial.uniforms.visibleNodes.value = material.visibleNodesTexture; _depthMaterial.uniforms.octreeSize.value = octreeSize; _depthMaterial.spacing = _pointcloud8.pcoGeometry.spacing; // * Math.max(...pointcloud.scale.toArray()); _depthMaterial.classification = material.classification; _depthMaterial.uniforms.classificationLUT.value.image.data = material.uniforms.classificationLUT.value.image.data; _depthMaterial.classificationTexture.needsUpdate = true; _depthMaterial.uniforms.uFilterReturnNumberRange.value = material.uniforms.uFilterReturnNumberRange.value; _depthMaterial.uniforms.uFilterNumberOfReturnsRange.value = material.uniforms.uFilterNumberOfReturnsRange.value; _depthMaterial.uniforms.uFilterGPSTimeClipRange.value = material.uniforms.uFilterGPSTimeClipRange.value; _depthMaterial.uniforms.uFilterPointSourceIDClipRange.value = material.uniforms.uFilterPointSourceIDClipRange.value; _depthMaterial.clipTask = material.clipTask; _depthMaterial.clipMethod = material.clipMethod; _depthMaterial.setClipBoxes(material.clipBoxes); _depthMaterial.setClipPolygons(material.clipPolygons); _pointcloud8.material = _depthMaterial; } viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, params.rtEDL || this.rtDepth, { clipSpheres: viewer.scene.volumes.filter(v => v instanceof SphereVolume) }); } { // ATTRIBUTE PASS for (var _pointcloud9 of visiblePointClouds) { var _octreeSize = _pointcloud9.pcoGeometry.boundingBox.getSize(new Vector3()).x; var _material = originalMaterials.get(_pointcloud9); var _attributeMaterial = this.attributeMaterials.get(_pointcloud9); _attributeMaterial.size = _material.size; _attributeMaterial.minSize = _material.minSize; _attributeMaterial.maxSize = _material.maxSize; _attributeMaterial.pointSizeType = _material.pointSizeType; _attributeMaterial.activeAttributeName = _material.activeAttributeName; _attributeMaterial.visibleNodesTexture = _material.visibleNodesTexture; _attributeMaterial.weighted = true; _attributeMaterial.screenWidth = width; _attributeMaterial.screenHeight = height; _attributeMaterial.shape = PointShape.CIRCLE; _attributeMaterial.uniforms.visibleNodes.value = _material.visibleNodesTexture; _attributeMaterial.uniforms.octreeSize.value = _octreeSize; _attributeMaterial.spacing = _pointcloud9.pcoGeometry.spacing; // * Math.max(...pointcloud.scale.toArray()); _attributeMaterial.classification = _material.classification; _attributeMaterial.uniforms.classificationLUT.value.image.data = _material.uniforms.classificationLUT.value.image.data; _attributeMaterial.classificationTexture.needsUpdate = true; _attributeMaterial.uniforms.uFilterReturnNumberRange.value = _material.uniforms.uFilterReturnNumberRange.value; _attributeMaterial.uniforms.uFilterNumberOfReturnsRange.value = _material.uniforms.uFilterNumberOfReturnsRange.value; _attributeMaterial.uniforms.uFilterGPSTimeClipRange.value = _material.uniforms.uFilterGPSTimeClipRange.value; _attributeMaterial.uniforms.uFilterPointSourceIDClipRange.value = _material.uniforms.uFilterPointSourceIDClipRange.value; _attributeMaterial.elevationGradientRepeat = _material.elevationGradientRepeat; _attributeMaterial.elevationRange = _material.elevationRange; _attributeMaterial.gradient = _material.gradient; _attributeMaterial.matcap = _material.matcap; _attributeMaterial.intensityRange = _material.intensityRange; _attributeMaterial.intensityGamma = _material.intensityGamma; _attributeMaterial.intensityContrast = _material.intensityContrast; _attributeMaterial.intensityBrightness = _material.intensityBrightness; _attributeMaterial.rgbGamma = _material.rgbGamma; _attributeMaterial.rgbContrast = _material.rgbContrast; _attributeMaterial.rgbBrightness = _material.rgbBrightness; _attributeMaterial.weightRGB = _material.weightRGB; _attributeMaterial.weightIntensity = _material.weightIntensity; _attributeMaterial.weightElevation = _material.weightElevation; _attributeMaterial.weightRGB = _material.weightRGB; _attributeMaterial.weightClassification = _material.weightClassification; _attributeMaterial.weightReturnNumber = _material.weightReturnNumber; _attributeMaterial.weightSourceID = _material.weightSourceID; _attributeMaterial.color = _material.color; _attributeMaterial.clipTask = _material.clipTask; _attributeMaterial.clipMethod = _material.clipMethod; _attributeMaterial.setClipBoxes(_material.clipBoxes); _attributeMaterial.setClipPolygons(_material.clipPolygons); _pointcloud9.material = _attributeMaterial; } var gl = this.gl; //viewer.renderer.setRenderTarget(null); viewer.pRenderer.render(viewer.scene.scenePointCloud, camera, this.rtAttribute, { clipSpheres: viewer.scene.volumes.filter(v => v instanceof SphereVolume), //material: this.attributeMaterial, blendFunc: [gl.SRC_ALPHA, gl.ONE], //depthTest: false, depthWrite: false }); } for (var [_pointcloud10, _material2] of originalMaterials) { _pointcloud10.material = _material2; } if (viewer.background === "skybox") { viewer.renderer.setClearColor(0x000000, 0); viewer.renderer.clear(); viewer.skybox.camera.rotation.copy(viewer.scene.cameraP.rotation); viewer.skybox.camera.fov = viewer.scene.cameraP.fov; viewer.skybox.camera.aspect = viewer.scene.cameraP.aspect; viewer.skybox.parent.rotation.x = 0; viewer.skybox.parent.updateMatrixWorld(); viewer.skybox.camera.updateProjectionMatrix(); viewer.renderer.render(viewer.skybox.scene, viewer.skybox.camera); } else if (viewer.background === 'gradient') { viewer.renderer.setClearColor(0x000000, 0); viewer.renderer.clear(); viewer.renderer.render(viewer.scene.sceneBG, viewer.scene.cameraBG); } else if (viewer.background === 'black') { viewer.renderer.setClearColor(0x000000, 1); viewer.renderer.clear(); } else if (viewer.background === 'white') { viewer.renderer.setClearColor(0xFFFFFF, 1); viewer.renderer.clear(); } else { viewer.renderer.setClearColor(0x000000, 0); viewer.renderer.clear(); } { // NORMALIZATION PASS var normalizationMaterial = this.useEDL ? this.normalizationEDLMaterial : this.normalizationMaterial; if (this.useEDL) { normalizationMaterial.uniforms.edlStrength.value = viewer.edlStrength; normalizationMaterial.uniforms.radius.value = viewer.edlRadius; normalizationMaterial.uniforms.screenWidth.value = width; normalizationMaterial.uniforms.screenHeight.value = height; normalizationMaterial.uniforms.uEDLMap.value = (params.rtEDL || this.rtDepth).texture; } normalizationMaterial.uniforms.uWeightMap.value = this.rtAttribute.texture; normalizationMaterial.uniforms.uDepthMap.value = this.rtAttribute.depthTexture; Utils.screenPass.render(viewer.renderer, normalizationMaterial); } viewer.renderer.render(viewer.scene.scene, camera); viewer.dispatchEvent({ type: "render.pass.scene", viewer: viewer }); viewer.renderer.render(viewer.scene.sceneOverlay, camera); // add 透明贴图层 viewer.renderer.clearDepth(); viewer.transformationTool.update(); if (!params.target) { //测量线 viewer.dispatchEvent({ type: "render.pass.perspective_overlay", viewer: viewer, camera }); viewer.renderer.render(viewer.overlay, camera); //从 viewer.renderDefault搬过来,为了reticule不遮住测量线 } viewer.renderer.render(viewer.controls.sceneControls, camera); viewer.renderer.render(viewer.clippingTool.sceneVolume, camera); viewer.renderer.render(viewer.transformationTool.scene, camera); viewer.renderer.setViewport(width - viewer.navigationCube.width, height - viewer.navigationCube.width, viewer.navigationCube.width, viewer.navigationCube.width); viewer.renderer.render(viewer.navigationCube, viewer.navigationCube.camera); viewer.renderer.setViewport(0, 0, width, height); viewer.dispatchEvent({ type: "render.pass.end", viewer: viewer }); viewer.renderer.setRenderTarget(null); }; } //PointCloudOctreeGeometry.js PointCloudOctreeGeometryNode.prototype.loadHierachyThenPoints = function (pointcloud) { var node = this; // load hierarchy var callback = function callback(node, hbuffer) { var tStart = performance.now(); var view = new DataView(hbuffer); var stack = []; var children = view.getUint8(0); var numPoints = view.getUint32(1, true); node.numPoints = numPoints; stack.push({ children: children, numPoints: numPoints, name: node.name }); var decoded = []; var offset = 5; while (stack.length > 0) { var snode = stack.shift(); var mask = 1; for (var i = 0; i < 8; i++) { if ((snode.children & mask) !== 0) { var childName = snode.name + i; var childChildren = view.getUint8(offset); var childNumPoints = view.getUint32(offset + 1, true); stack.push({ children: childChildren, numPoints: childNumPoints, name: childName }); decoded.push({ children: childChildren, numPoints: childNumPoints, name: childName }); offset += 5; } mask = mask * 2; } if (offset === hbuffer.byteLength) { break; } } // console.log(decoded); var nodes = {}; nodes[node.name] = node; var pco = node.pcoGeometry; var maxLevel_ = 0; for (var _i3 = 0; _i3 < decoded.length; _i3++) { var name = decoded[_i3].name; var decodedNumPoints = decoded[_i3].numPoints; var index = parseInt(name.charAt(name.length - 1)); var parentName = name.substring(0, name.length - 1); var parentNode = nodes[parentName]; var level = name.length - 1; maxLevel_ = Math.max(maxLevel_, level); //add var boundingBox = Utils.createChildAABB(parentNode.boundingBox, index); var currentNode = new PointCloudOctreeGeometryNode(name, pco, boundingBox); currentNode.level = level; currentNode.numPoints = decodedNumPoints; currentNode.hasChildren = decoded[_i3].children > 0; currentNode.spacing = pco.spacing / Math.pow(2, level); parentNode.addChild(currentNode); nodes[name] = currentNode; } pco.dispatchEvent({ type: 'updateNodeMaxLevel', level: maxLevel_ }); //add var duration = performance.now() - tStart; if (duration > 5) { /* let msg = `duration: ${duration}ms, numNodes: ${decoded.length}`; console.log(msg); */ } node.loadPoints(); }; if (node.level % node.pcoGeometry.hierarchyStepSize === 0) { // let hurl = node.pcoGeometry.octreeDir + "/../hierarchy/" + node.name + ".hrc"; var hurl = node.pcoGeometry.octreeDir + '/' + node.getHierarchyPath() + '/' + node.name + '.hrc'; hurl += '?m=' + node.pcoGeometry.timeStamp; //add var xhr = XHRFactory.createXMLHttpRequest(); xhr.open('GET', hurl, true); xhr.responseType = 'arraybuffer'; xhr.overrideMimeType('text/plain; charset=x-user-defined'); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (xhr.status === 200 || xhr.status === 0) { var hbuffer = xhr.response; callback(node, hbuffer); } else { console.log('Failed to load file! HTTP status: ' + xhr.status + ', file: ' + hurl); Potree.numNodesLoading--; } } }; try { xhr.send(null); } catch (e) { console.log('fehler beim laden der punktwolke: ' + e); } } }; PointCloudOctreeGeometryNode.prototype.loadPoints = function () { var name = this.name; var log = this.pcoGeometry.octreeDir.includes('webcloud_3dgs'); //查看线上的bin加载速度 this.pcoGeometry.loader.load(this, () => { //callback viewer.dispatchEvent('pointcloud_changed'); log && console.log('loadPoints success ', name); }); }; //加载点云成功->准备渲染画面->更新点云可见性updateVisibility->请求加载新的点云 PointCloudOctreeGeometryNode.prototype.traverse = function (t, e) { //add from navvis 25.js void 0 === e && (e = !0); for (var n, i = e ? [this] : []; void 0 !== (n = i.pop());) { t(n); for (var o = 0, r = n.children; o < r.length; o++) { var a = r[o]; null !== a && i.push(a); } } }; Object.assign(PointCloudOctreeGeometry.prototype, EventDispatcher.prototype); LRU.prototype.freeMemory = function () { if (this.elements <= 1) { return; } var memoryRatio = browser.isMobile() ? 2 : 5; //改成navvis的,使用pointBudget,否则四屏点云闪烁。 (似乎要比updateVisiblede的node时限制要宽些,作为缓存继续存着。否则会闪烁) var max = MathUtils.clamp(viewer.viewports.length * memoryRatio * Potree.pointBudget, 0, Potree.settings.maxLRUPoints); for (; this.numPoints > max;) { var node = this.getLRUItem(); node && this.disposeDescendants(node); } }; VolumeTool.prototype.update = function () {}; VolumeTool.prototype.startInsertion = function () { var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var volume; if (args.type) { volume = new args.type(); } else { volume = new Potree.BoxVolume(Object.assign(args, { clip: true })); } volume.highlight = true; volume.name = args.name || 'Volume-' + args.clipTask; volume.isNew = true; viewer.transformObject(null); //先清空 //console.log('startInsertion',volume.uuid) var oldVisiBoxes; if (args.clipTask == Potree.ClipTask.SHOW_INSIDE) { //如果是显示类型,需要将所有同类型的解除效果,否则看不到效果。 (或者可以在添加非第一个时去除highlight效果,会更自然,但看不清全貌) oldVisiBoxes = viewer.scene.volumes.filter(v => v.clipTask == Potree.ClipTask.SHOW_INSIDE && !v.highlight); oldVisiBoxes.forEach(box => box.highlight = true); } var updatePose = () => { //保证在视野中的大小一致: var camera = this.viewer.scene.getActiveCamera(); var w = math.getScaleForConstantSize({ width2d: 300, camera, position: volume.getWorldPosition(new Vector3()), resolution: viewer.mainViewport.resolution //2 }); /* let wp = volume.getWorldPosition(new THREE.Vector3()).applyMatrix4(camera.matrixWorldInverse); // let pp = new THREE.Vector4(wp.x, wp.y, wp.z).applyMatrix4(camera.projectionMatrix); let w = Math.abs((wp.z / 3));*/ if (!isNaN(w)) volume.scale.set(w, w, w); { //使水平朝向与camera一致 var direction = viewer.mainViewport.view.direction.setZ(0); volume.quaternion.copy(math.getQuaByAim(direction)); } }; this.dispatchEvent({ type: 'start_inserting_volume', volume: volume }); updatePose(); this.viewer.scene.addVolume(volume); this.scene.add(volume); var drag = e => { if (e.hoverViewport.name == 'mapViewport') return; var I = Utils.getMousePointCloudIntersection(viewer.mainViewport, viewer.inputHandler.mouse, viewer.inputHandler.pointer, this.viewer.scene.getActiveCamera(), this.viewer, this.viewer.scene.pointclouds, { pickClipped: args.clipTask == Potree.ClipTask.SHOW_OUTSIDE } //无视clip状态 ); var worldPos = I && I.location; if (!worldPos) { return; } volume.position.copy(worldPos); updatePose(); }; var cancel = () => { end('remove'); }; var end = e => { if (e.button == MOUSE.RIGHT && e.pressDistance <= Potree.config.clickMaxDragDis) { //remove e = 'remove'; } //console.log('end',volume.uuid, e) if (e != 'remove' && (!e.isAtDomElement || e.pressDistance > Potree.config.clickMaxDragDis)) return continueDrag(); volume.removeEventListener('drag', drag); volume.removeEventListener('drop', end); this.viewer.removeEventListener('cancel_insertions', cancel); volume.isNew = false; viewer.removeEventListener('camera_changed', updatePose); if (e == 'remove') { viewer.scene.removeVolume(volume); //删除没完成的 } else { viewer.transformObject(volume); volume.highlight = false; } volume.dispatchEvent({ type: 'createFinish', success: e != 'remove' }); oldVisiBoxes && oldVisiBoxes.forEach(box => box.highlight = false); }; var continueDrag = () => { //console.log('continueDrag',volume.uuid ) var timer = setTimeout(() => { //等 drag=null之后 //右键拖拽结束后需要重新得到drag if (volume.parent && volume.isNew) { viewer.inputHandler.startDragging(volume, { notPressMouse: true } /* {endDragFun: e.drag.endDragFun, notPressMouse:e.drag.notPressMouse, dragViewport:e.drag.dragViewport} */); } }, 1); return timer; }; volume.addEventListener('drag', drag); volume.addEventListener('drop', end); this.viewer.addEventListener('cancel_insertions', cancel); viewer.addEventListener('camera_changed', updatePose); this.viewer.inputHandler.startDragging(volume, { notPressMouse: true }); return volume; }; LineGeometry.prototype.setPositions = function (array) { //xzw改成类似LineSegments的多段线 (第二个点和第三个点之间是没有线段的, 所以不用在意线段顺序) var points = new Float32Array(array); LineSegmentsGeometry.prototype.setPositions.call(this, points); return this; }; Object.assign(ExtendView.prototype, EventDispatcher.prototype); Object.assign(ExtendScene.prototype, EventDispatcher.prototype); window.THREE = THREE$1; var baseZ = 0; //所以数据集高度都要减去这个值。在laser场景里该值为初始数据集的高程 var transformPointcloud = (pointcloud, dataset) => { var _dataset$quaternion; var locationLonLat = dataset.location.slice(0, 3); // [lon,lat,高程海拔] //当只有一个dataset时,无论如何transform 点云和漫游点都能对应上。 var location = viewer.transform.lonlatToLocal.forward(locationLonLat); //transform.inverse() //初始化位置 viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud); //dataset.orientation = 0 var Alignment = viewer.modules.Alignment; Alignment.rotate(pointcloud, null, ((_dataset$quaternion = dataset.quaternion) === null || _dataset$quaternion === void 0 ? void 0 : _dataset$quaternion.length) == 4 ? dataset.quaternion : dataset.orientation); Alignment.translate(pointcloud, new Vector3(location[0], location[1], dataset.location[2] - baseZ)); //要使初始数据集的z为0,所以要减去初始数据集的z pointcloud.updateMatrixWorld(); Potree.Log("\u70B9\u4E91".concat(pointcloud.dataset_id, "(").concat(pointcloud.name, ")\u65CB\u8F6C\u503C: qua ").concat(dataset.quaternion || '', "\u3001orient ").concat(math.toPrecision(dataset.orientation, 3), ", \n \u4F4D\u7F6E").concat(math.toPrecision(pointcloud.translateUser.toArray(), 3), ", \u7ECF\u7EAC\u5EA6 ").concat(dataset.location, ", spacing ").concat(pointcloud.material.spacing), { font: { color: "#f49", fontSize: 13 } }); }; function start(dom, mapDom, number, options) { //t-Zvd3w0m /* { let obj = JSON.parse(localStorage.getItem('setting')) for(let i in obj){ console.log(i + ': ' + obj[i]) } } */ console.log('2024.9'); Potree.settings.number = number || 't-o5YMR13'; // 't-iksBApb'// 写在viewer前 if (!Potree.settings.isOfficial) { if ( /* Potree.settings.isTest && */browser.isMobile()) { changeLog(); } } if (browser.urlHasValue('google')) Potree.settings.mapCompany = 'google'; if (browser.urlHasValue('timing')) Potree.measureTimings = 1; var viewer = new Potree.Viewer(dom, mapDom, options); var Alignment = viewer.modules.Alignment; viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); Potree.settings.cameraFar = Potree.config.view.far; if (!Potree.settings.isOfficial) { viewer.loadGUI(() => { viewer.setLanguage('en'); //$("#menu_appearance").next().show(); $("#menu_tools").next().show(); $("#menu_scene").next().show(); $("#siteModel").show(); //$("#alignment").show(); viewer.toggleSidebar(); }); Potree.settings.sizeFitToLevel = true; //当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 Potree.settings.rotAroundPoint = false; } Potree.loadDatasetsCallback = function (data, ifReload) { if (!data || data.length == 0) return console.error('getDataSet加载的数据为空'); Potree.datasetData = data; viewer.transform = null; var datasetLength = data.length; var pointcloudLoaded = 0; var panosLoaded = 0; var pointcloudLoadDone = function pointcloudLoadDone() {//点云cloud.js加载完毕后 }; var panosLoadDone = function panosLoadDone() { var _viewer$mapViewer; viewer.images360.loadDone(); viewer.scene.add360Images(viewer.images360); (_viewer$mapViewer = viewer.mapViewer) === null || _viewer$mapViewer === void 0 || _viewer$mapViewer.addListener(viewer.images360); viewer.updateModelBound(); //需等pano加载完 var { boundSize, center } = viewer.bound; if (!Potree.settings.isOfficial) { viewer.mapViewer && Potree.loadMapEntity('all'); //加载floorplan } Potree.loadNeighborFile(); Potree.setNeighborGui(); if (!ifReload) { viewer.dispatchEvent({ type: 'loadPointCloudDone' }); if (!Potree.settings.UserPointDensity) { Potree.settings.UserPointDensity = 'high'; //'middle' } Potree.Log('loadPointCloudDone 点云加载完毕'); } { //初始位置 var panoId = browser.urlHasValue('pano', true); if (panoId !== '') { var pos; var pano = viewer.images360.panos.find(e => e.id == panoId); if (pano) { viewer.images360.focusPano({ pano, duration: 0, callback: () => {/* Potree.settings.displayMode = 'showPanos' */} }); } } else { //考虑到多数据集距离很远,或者像隧道那种场景,要使视野范围内一定能看到点云,最好初始点设置在漫游点上 var { boundSize: _boundSize, center: _center } = viewer.bound; var _pano = viewer.images360.findNearestPano(_center); if (_pano) { viewer.images360.flyToPano({ pano: _pano, duration: 0, target: viewer.images360.bound.center.setZ(_pano.position.z) //平视中心区域(但也不能保证这个方向一定能看到点云密集区,如果在边缘的话) }); } else { // 无漫游点 避免加载时看不到点云 SG-t-DXmdymgZ2sX SG-t-rVB03a5GXr8 //SG-t-XPf1k9pv3Zg 总有极端的场景,如这是倾斜的桥 - -, 只能调整为看到全局了 viewer.mainViewport.view.pitch = -0.7; //相对俯视 var bound = viewer.bound.boundingBox.clone(); viewer.focusOnObject({ boundingBox: bound }, 'boundingBox', 0, { dontChangeCamDir: true }); if (viewer.bound.boundSize.length() > 20) { //否则有可能超出far范围 viewer.mainViewport.camera.far = 10000; viewer.mainViewport.camera.updateProjectionMatrix(); viewer.fixCamFar = true; //不允许修改camera.far //等有点云加载出来后,再去focus其中一个,使camera.far不超过最大值 var count_ = 0; var done = () => { viewer.fixCamFar = false; viewer.mainViewport.camera.far = Potree.settings.cameraFar; viewer.mainViewport.camera.updateProjectionMatrix(); viewer.removeEventListener('pageVisible', focusPoint); clearTimeout(timer); }; var timer; var focusPoint = e => { //拉近到某个点 if (e && e.v === false) return; viewer.removeEventListener('pageVisible', focusPoint); var pointcloud = viewer.scene.pointclouds.find(e => e.root.geometryNode); console.log('初始加载focus点云', e, pointcloud); if (!pointcloud) { if (count_ < 10) { //可能没加载到,可能被隐藏 if (document.hidden) { //等回到页面再focus console.log('focus hidden'); return viewer.addEventListener('pageVisible', focusPoint); } count_++; //如果在别的 timer = setTimeout(focusPoint, 200); } else { //放弃 console.log('初始加载focus点云 放弃'); done(); } return console.warn('no!!!!!!!!!!!!!!'); } viewer.flyToDataset({ focusOnPoint: true, pointcloud, duration: 0 }); console.warn('ok!!!!!!!!!!!!!!!!'); done(); }; var focus = () => { timer = setTimeout(focusPoint, 300); }; viewer.addEventListener('setPose', () => { //设置了初始画面 viewer.removeEventListener('pointcloud_changed', focus); done(); }); viewer.addEventListener('pointcloud_changed', focus, { once: true }); //加载了点之后 } } } } viewer.addVideo(); //addFire() viewer.scene.pointclouds.some(e => !e.hasDepthTex) && (Potree.config.shelterMargin *= 3); //没有深度图的话在全景模式测量有偏差 console.log('allLoaded'); viewer.addTube({ 't-8KbK1JjubE': [{ //地上管子 黄色 path: [{ "x": -109.83, "y": -68.33, "z": -7.52 }, { "x": -95.17, "y": -59.3, "z": -7.38 }, { "x": -38.75, "y": -24.01, "z": -6.01 }, { "x": 0.5, "y": 0.19, "z": -3.89 }, { "x": 39.29, "y": 24.41, "z": -1.31 }, { "x": 43.58, "y": 27.7, "z": -0.97 }, { "x": 40.22, "y": 35.37, "z": -0.67 } // 拐弯向右 , { "x": 39.18, "y": 36.71, "z": 0.35 }, { "x": 38.69, "y": 36.04, "z": 18.04 } // 拐弯向上 } ], color: '#b86', radius: 0.08, height: 0.13, tension: 0.3, lessSpace: 60 }, { //地下管子 藍色 path: [{ "x": -108.24, "y": -70.61, "z": -7.52 }, { "x": -57.8, "y": -39.31, "z": -6.72 }, { "x": -18.8, "y": -15.35, "z": -5.01 }, { "x": 55.87, "y": 31.67, "z": -0.04 }, { "x": 110.53, "y": 66.48, "z": 5.14 }], color: '#48a', radius: 0.08, height: -0.5, lessSpace: 60 }] /* 'SG-t-h0I7LnfGFwj':[ //港华M层 { path:[[20.295,-10.269,-1.152],[19.996,-10.252,-1.153],[19.98,-10.253,-1.156],[19.967,-10.252,-1.165],[19.957,-10.251,-1.179],[19.953,-10.251,-1.225],[19.961,-10.242,-1.267],[19.968,-10.223,-1.296],[19.967,-10.193,-1.304],[20.007,-10.092,-1.321],[19.997,-10.063,-1.309],[19.979,-10.039,-1.284],[19.989,-10.028,-1.256],[19.993,-10.024,-1.23],[19.972,-10.026,-1.181],[20.009,-10.045,-1.026],[20.025,-10.048,-1.013],[20.045,-10.05,-1.006],[20.056,-10.052,-1.001],[20.413,-10.073,-1.016],[20.461,-10.075,-1.02],[20.498,-10.053,-1.018],[20.525,-10.022,-1.025],[20.538,-9.983,-1.029],[20.554,-9.536,-1.111],[20.554,-9.487,-1.118],[20.545,-9.445,-1.129],[20.521,-9.418,-1.14],[20.478,-9.396,-1.156],[20.427,-9.379,-1.182],[20.367,-9.369,-1.198],[19.971,-9.304,-1.221],[19.749,-9.197,-1.345]] ,color:'#8c99aa', radius:0.025, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房' }, { path:[[19.708,-2.921,-1.145],[19.721,-2.924,-1.119],[20.704,-2.881,-1.134],[20.736,-2.87,-1.138],[20.76,-2.852,-1.147],[20.773,-2.828,-1.148],[20.777,-2.809,-1.148],[20.794,-2.493,-1.167],[20.793,-2.461,-1.169],[20.776,-2.436,-1.174],[20.742,-2.425,-1.198],[20.14,-2.459,-1.309],[20.123,-2.457,-1.309],[20.108,-2.451,-1.31],[20.101,-2.439,-1.311],[20.095,-2.42,-1.313],[20.094,-2.399,-1.311],[20.092,-1.966,-1.295],[20.093,-1.945,-1.286],[20.094,-1.931,-1.269],[20.093,-1.929,-1.251],[20.09,-1.921,-1.197],[20.096,-1.914,-1.181],[20.107,-1.909,-1.174],[20.176,-1.893,-1.141],[20.699,-1.848,-1.076],[20.759,-1.824,-1.068],[20.81,-1.754,-1.076],[20.837,-1.71,-1.086],[20.85,-1.634,-1.1],[20.815,-1.01,-1.104],[20.824,-0.979,-1.115],[20.83,-0.957,-1.124],[20.843,-0.936,-1.132],[20.871,-0.925,-1.139],[21.065,-0.936,-1.166],[21.096,-0.935,-1.167],[21.115,-0.919,-1.167],[21.129,-0.887,-1.165],[21.136,-0.783,-1.167],[21.136,0.555,-1.201],[21.128,0.599,-1.201],[21.1,0.627,-1.199],[21.058,0.639,-1.198],[20.832,0.616,-1.179],[20.782,0.62,-1.186],[20.748,0.637,-1.19],[20.728,0.698,-1.21],[20.731,1.311,-1.233],[20.68,2.081,-1.292],[20.654,2.157,-1.307],[20.576,2.215,-1.314],[20.484,2.274,-1.321],[20.035,2.354,-1.353],[20.013,2.367,-1.363],[20.008,2.39,-1.371],[19.989,3.124,-1.384],[19.993,3.175,-1.39],[19.995,3.208,-1.393],[19.995,3.233,-1.374],[20.002,3.246,-1.324],[20.006,3.248,-1.266],[20.027,3.243,-1.245],[20.063,3.241,-1.239],[20.62,3.229,-1.226],[20.645,3.235,-1.204],[20.658,3.251,-1.191],[20.667,3.278,-1.184],[20.675,3.312,-1.193],[20.716,3.71,-1.24],[20.722,3.767,-1.251],[20.704,3.823,-1.268],[20.652,3.864,-1.279],[20.026,3.901,-1.368],[19.999,3.919,-1.37],[19.982,3.95,-1.361],[19.933,4.69,-1.285],[19.972,4.812,-1.332],[19.978,4.817,-1.233],[19.992,4.814,-1.203],[20.047,4.81,-1.196],[20.738,4.794,-1.196]] ,color:'#8c99aa', radius:0.025, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房' }, { path:[[19.685,-2.896,-1.115],[19.699,-2.93,-1.274],[19.701,-2.936,-1.295],[19.707,-2.94,-1.316],[19.712,-2.951,-1.34],[19.714,-2.965,-1.353],[19.716,-2.986,-1.359],[19.7,-3.595,-1.309],[19.703,-3.619,-1.303],[19.704,-3.628,-1.274],[19.719,-3.632,-1.256],[20.063,-3.633,-1.275],[20.089,-3.637,-1.254],[20.103,-3.64,-1.227],[20.11,-3.642,-1.191],[20.083,-3.669,-1.027],[20.075,-3.68,-0.999],[20.053,-3.694,-0.978],[20.053,-3.805,-0.957],[20.063,-3.827,-0.963],[20.045,-3.836,-0.962],[20.01,-3.841,-0.957],[19.737,-3.818,-0.964],[19.712,-3.814,-0.985],[19.699,-3.819,-1.059],[19.724,-3.817,-1.247],[19.725,-3.827,-1.28],[19.719,-3.842,-1.309],[19.719,-3.858,-1.33],[19.714,-3.881,-1.341],[19.699,-4.56,-1.339],[19.7,-4.62,-1.336],[19.7,-4.643,-1.334],[19.699,-4.66,-1.326],[19.698,-4.675,-1.31],[19.697,-4.678,-1.279],[19.697,-4.678,-1.21],[19.704,-4.682,-1.187],[19.716,-4.682,-1.173],[19.736,-4.683,-1.164],[20.481,-4.817,-1.026],[20.529,-4.847,-0.984],[20.562,-4.89,-0.935],[20.585,-4.932,-0.909],[20.59,-4.992,-0.887],[20.565,-5.98,-0.847],[20.547,-6.08,-0.846],[20.541,-6.17,-0.86],[20.476,-6.21,-0.858],[19.847,-6.207,-0.808],[19.803,-6.22,-0.827],[19.775,-6.217,-0.885],[19.75,-6.188,-1.289],[19.743,-6.193,-1.341],[19.742,-6.207,-1.375],[19.75,-6.232,-1.401],[19.722,-6.905,-1.359],[19.723,-6.958,-1.352],[19.722,-6.987,-1.342],[19.721,-7.001,-1.31],[19.721,-7.004,-1.231],[19.734,-7.03,-0.976],[19.742,-7.041,-0.947],[19.747,-7.059,-0.922],[19.755,-7.284,-0.764],[19.761,-7.312,-0.731],[19.776,-7.337,-0.711],[19.795,-7.355,-0.702],[20.502,-7.453,-0.715],[20.578,-7.496,-0.715],[20.631,-7.557,-0.719],[20.66,-7.653,-0.718],[20.658,-8.639,-0.766],[20.654,-8.789,-0.763],[20.639,-8.856,-0.763],[20.605,-8.89,-0.76],[20.57,-8.916,-0.761],[20.539,-8.945,-0.773],[19.879,-9.028,-0.814],[19.853,-9.032,-0.828],[19.834,-9.033,-0.842],[19.83,-9.033,-0.876],[19.857,-9.033,-1.292],[19.837,-9.037,-1.333],[19.819,-9.051,-1.354],[19.812,-9.082,-1.381],[19.79,-9.184,-1.386]] ,color:'#8c99aa', radius:0.02, height:-0, fromDataset:true, tension:0.01, spaceDis:0.02,visiEntity:'厨房' } ], 'SG-t-RGUFiJAoxvL':[ //济南工地覆土后 {//--旧的 整条 path: [[23.522,-19.432,-1.744],[25.344,20.312,-1.855],[25.344,21.302,-1.861],[25.253,21.904,-1.861],[25.088,22.381,-1.859],[24.748,22.772,-1.861],[24.222,22.893,-1.873],[-24.547,16.774,-1.319]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0.01,// spaceDis:0.1 }, { path:[[22.726,22.678,-1.938],[-24.541,16.782,-1.319]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0,// spaceDis:0.1 }, { path:[[23.519,-19.473,-1.744],[25.117,15.447,-1.783]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, tension:0,// spaceDis:0.1 } ], 'SG-t-TK0S5EqWBxd':[ //济南工地 { path:[[-25.768,-12.695,-1.305],[-26.334,-21.834,-1.511],[-26.355,-22.153,-1.514],[-26.366,-22.418,-1.513],[-26.31,-22.66,-1.515],[-26.126,-22.841,-1.519],[-25.871,-22.968,-1.525],[-25.443,-22.969,-1.532],[-24.953,-22.956,-1.585],[-23.697,-22.854,-1.637]] ,color:'#445', radius:0.08, height:-1.16, fromDataset:true, spaceDis:0.2// tension:0.01, }, ],*/ }); viewer.dispatchEvent('allLoaded'); window.addExtPath = function (data) { console.log(data); window.pathPoints = data; var points = []; data.forEach((e, i) => { if (data[i - 1] && data[i - 1][0] == e[0] && data[i - 1][1] == e[1] && data[i - 1][2] == e[2]) return; //e[2]+=0.2 var pos = /* Potree.math.convertVector.YupToZup( */new Vector3().fromArray(e); /* ) */ var x = pos.x, y = pos.y, z = pos.z; pos.applyMatrix4(viewer.scene.pointclouds[0].transformMatrix); //是4dkk场景里坐标 points.push(pos); }); /* let fakeMeasure ={ "measureType": "Hor MulDistance", "unit": "metric","color": "#00c8af", points , "datasetId": null, "title": "test path", showDistances:false, "bus": { "all": {} }, } viewer.measuringTool.createMeasureFromData(fakeMeasure) viewer.scene.measurements[0].edgeLabels.forEach(e=>Potree.Utils.updateVisible(e,'f',false,10)) */ var fakeMeasure = { type: 'Path', "unit": "metric", points, width: 0.1 }; var path = viewer.measuringTool.createMeasureFromData(fakeMeasure); for (var i = 0; i < points.length; i++) { path.setMarkerTitle(i, ''); } }; if (browser.urlHasValue('path')) { //https://192.168.0.59:1234/examples/4dkk.html?m=SG-r0dwv5D8vY8&formal&showAxis&path var fileName = 'pathPointsNew'; setTimeout(() => { Potree.loadFile(Potree.resourcePath + '/' + fileName + '.json', null, data => { addExtPath(data); }); }, 1000); } }; if (!Potree.settings.originDatasetId) Potree.settings.originDatasetId = data[0].id; var originDataset = data.find(e => e.id == Potree.settings.originDatasetId); baseZ = originDataset.location[2]; /* originDataset.location[0] = 113.60608878174709 originDataset.location[1] = 22.381189423935155 let ano = data.find(e=>e.name == 'SG-t-Jw0xyhL6oSY') ano.location[0] = 113.6060509967498 ano.location[1] = 22.381061273279244 */ { var _viewer$mapViewer2; //拿初始数据集作为基准。它的位置要放到000 var locationLonLat = originDataset.location.slice(0, 2); Potree.setLonlat(locationLonLat[0], locationLonLat[1]); (_viewer$mapViewer2 = viewer.mapViewer) === null || _viewer$mapViewer2 === void 0 || _viewer$mapViewer2.mapLayer.maps[0].updateProjection(); } data.forEach((dataset, index) => { if (!ifReload) { var datasetCode = dataset.sceneCode || dataset.name; //对应4dkk的场景码 if (Potree.settings.isLocal && dataset.mapping) { var cloudPath = "".concat(Potree.settings.urls.prefix1, "/").concat(dataset.mapping, "/").concat(dataset.webBin); //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } else { var cloudPath = "".concat(Potree.settings.urls.prefix1, "/").concat(dataset.webBin); //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } //var cloudPath = `${Potree.scriptPath}/data/test/${dataset.name}/cloud.js` var timeStamp = dataset.updateTime ? dataset.updateTime.replace(/[^0-9]/ig, '') : ''; //每重算一次后缀随updateTime更新一次 //console.log('开始加载点云', dataset.name ) Potree.loadPointCloud(cloudPath, dataset.name, datasetCode, timeStamp, e => { var scene = viewer.scene; var pointcloud = e.pointcloud; var config = Potree.config.material; var material = pointcloud.material; pointcloud.datasetData = dataset; pointcloud.dataset_id = dataset.id; //供漫游点找到属于的dataset点云 pointcloud.hasDepthTex = Potree.settings.useDepthTex && (!!dataset.has_depth || Potree.settings.isLocalhost && Potree.settings.number == 'SS-t-7DUfWAUZ3V'); //test pointcloud.hasTempTex = !!dataset.has_ir; material.minSize = config.minSize; material.maxSize = config.maxSize; material.pointSizeType = /* Potree.settings.isOfficial ? */config.pointSizeType; /* : 'ADAPTIVE' */ //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(config.realPointSize); //material.size = config.pointSize; pointcloud.changePointOpacity(1); material.shape = Potree.PointShape.SQUARE; pointcloud.color = pointcloud.material.color = dataset.color; pointcloud.timeStamp = timeStamp; transformPointcloud(pointcloud, dataset); scene.addPointCloud(pointcloud); if (!Potree.settings.isOfficial) { Potree.settings.floorplanEnables[dataset.id] = true; Potree.settings.floorplanType[dataset.id] = 'default'; } pointcloudLoaded++; if (pointcloudLoaded == datasetLength) pointcloudLoadDone(); Potree.loadPanos(dataset.id, data => { //console.log('loadPanos',dataset.sceneCode, dataset.id, data) viewer.images360.addPanoData(data, pointcloud); panosLoaded++; var f = () => { if (panosLoaded == datasetLength) { Potree.loadImgVersion(function () { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; Potree.settings.panoVersion = e.imgVersion; //全景图被替换后 panosLoadDone(); }); } }; if (dataset.has_ir) { //红外热成像漫游点数据 /* if(Potree.settings.isLocal && dataset.mapping){ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } */ var mapping = Potree.settings.isLocal && dataset.mapping ? dataset.mapping : ''; pointcloud.typesUrl = "".concat(Potree.settings.urls.prefix1, "/").concat(mapping, "/").concat(Potree.settings.webSite2 || Potree.settings.webSite, "/").concat(pointcloud.sceneCode, "/data/").concat(pointcloud.sceneCode, "/imagemap/"); var typesUrl = pointcloud.typesUrl + 'types.json'; Potree.loadFile(typesUrl, null, e => { e.imagemap_types.forEach(o => { o.uuids.forEach(uuid => { //if(o.property == 'temp')return //只要ir var pano = viewer.images360.getPano("".concat(pointcloud.dataset_id, "|").concat(uuid), 'sid'); pano['has_' + o.property] = true; }); }); f(); }, () => { console.error('红外热成像数据加载失败 '); f(); }); } else { f(); } }); }); } else { var pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == dataset.id); if (!pointcloud) { Potree.Log('数据集id变了,自动使用第一个', { font: { color: '#500' } }); pointcloud = viewer.scene.pointclouds[0]; } //先归零 Alignment.translate(pointcloud, pointcloud.translateUser.clone().negate()); Alignment.rotate(pointcloud, null, -pointcloud.orientationUser); transformPointcloud(pointcloud, dataset); } }); if (ifReload) { //loadDone() } }; number && Potree.loadDatasets(Potree.loadDatasetsCallback); /* //调试用,加载多个本地 Potree.loadDatasetsCallback([ {name:'webcloud_0',id:0, orientation:0, location:[0,0,0]}, {name:'webcloud_1',id:1, orientation:0.047234761795199476, location:[-0.07925513345058573,-0.0010590072536559839,-2.403613132687564]}, {name:'webcloud_2',id:2, orientation:-1.5299545647758208, location:[1.5603736310030292, -0.0009340812579088904, -2.4464530770974139]}, ]) */ window.testTransform = function (locationLonLat, location1, location2) { proj4.defs("NAVVIS:test", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); var transform = proj4("WGS84", "NAVVIS:test"); //这个ok navvis里也是这两种转换 见proj4Factory if (location1) { //经纬度 return transform.forward(location1); } else { return transform.inverse(location2); } }; window.buttonFunction = function () { viewer.scene.pointclouds.forEach(e => e.predictNodeMaxLevel()); /* viewer.startScreenshot({type:'measure', measurement:viewer.scene.measurements[0]}) viewer.modules.RouteGuider.routeStart = new THREE.Vector3(0,0,-1.3) viewer.modules.RouteGuider.routeEnd = new THREE.Vector3(-10,0,-1.3) */ }; if (Potree.settings.isLocalhost) { var before = {}; viewer.inputHandler.addEventListener('keydown', e => { //测试的代码 if (e.event.key == 't') { viewer.images360.cube.visible = true; viewer.images360.cube.material.wireframe = true; } else if (e.event.key == 'y') { viewer.images360.cube.material.wireframe = false; viewer.images360.cube.visible = Potree.settings.displayMode == 'showPanos'; } }); //-------------------------------- /* if(!number){ Potree.settings.boundAddObjs = true Potree.settings.intersectOnObjs = true // Load untextured bunny from ply viewer.loadModel({ fileType:'ply', url:Potree.resourcePath + "/models/indoor.ply", name:'test', }, (object)=>{ object.isModel = true viewer.updateModelBound() } ) } */ } } //======================================================================= /* 漫游点编辑 */ //======================================================================= function panoEditStart(dom, number, EditCloudsArgs) { Potree.settings.editType = 'pano'; Potree.settings.number = number; Potree.settings.sizeFitToLevel = true; //当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 var datasetData; var viewer = new Potree.Viewer(dom); var Alignment = viewer.modules.Alignment; viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); var datasetLoaded = 0; if (!Potree.settings.isOfficial) { viewer.loadGUI(() => { viewer.setLanguage('en'); $("#menu_tools").next().show(); $("#panos").show(); $("#alignment").show(); viewer.toggleSidebar(); }); Potree.settings.sizeFitToLevel = true; } var pointcloudLoadDone = function pointcloudLoadDone() { //所有点云cloud.js加载完毕后 viewer.scene.pointclouds.forEach(c => { transformPointcloud(c); }); viewer.images360.loadDone(); viewer.scene.add360Images(viewer.images360); viewer.updateModelBound(); var { boundSize, center } = viewer.bound; Potree.Log("\u4E2D\u5FC3\u70B9: ".concat(math.toPrecision(center.toArray(), 2), ", boundSize: ").concat(math.toPrecision(boundSize.toArray(), 2), " ")); viewer.scene.view.setView({ position: center.clone().add(new Vector3(10, 5, 10)), target: center }); viewer.dispatchEvent({ type: 'loadPointCloudDone' }); if (!Potree.settings.UserPointDensity) { Potree.settings.UserPointDensity = 'panoEdit'; //'middle' } Potree.Log('loadPointCloudDone 点云加载完毕', { font: [null, 10] }); viewer.dispatchEvent('allLoaded'); }; var transformPointcloud = pointcloud => { //初始化位置 viewer.sidebar && viewer.sidebar.addAlignmentButton(pointcloud); /* let orientation = pointcloud.panos[0].dataRotation.z + Math.PI let location = pointcloud.panos[0].dataPosition.clone()//.negate() Alignment.rotate(pointcloud, null, orientation ) Alignment.translate(pointcloud, location ) */ viewer.modules.PanoEditor.initCloud(pointcloud); pointcloud.updateMatrixWorld(); }; var loadPanosDone = Potree.loadPanosDone = (datasetId, panoData) => { //一个数据集获取到它的panos后 Potree.settings.datasetsPanos[datasetId] = { panoData, panos: [] }; console.log('panoData', datasetId, panoData); var panoCount = panoData.length; var pointcloudLoaded = 0; var datasetsCount = Object.keys(Potree.settings.datasetsPanos).length; var datasetData = Potree.datasetData.find(e => e.datasetId = datasetId); panoData.forEach((pano, index) => { //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${Potree.settings.number}/data/bundle_${Potree.settings.number}/building/uuidcloud/${pano.uuid}/cloud.js` //2024.12.9: 加mapping var mapping = Potree.settings.isLocal && (datasetData ? datasetData.mapping : browser.urlHasValue('mapping', true)) || ''; mapping && (mapping += '/'); var cloudPath = "".concat(Potree.settings.urls.prefix1, "/").concat(mapping).concat(Potree.settings.webSite, "/").concat(Potree.settings.number, "/data/bundle_").concat(Potree.settings.number, "/building/uuidcloud/").concat(pano.uuid, "/cloud.js"); /* if(Potree.settings.isLocal && dataset.mapping){ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.mapping}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 }else{ var cloudPath = `${Potree.settings.urls.prefix1}/${dataset.webBin}` //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 } */ var name = datasetId + '-' + pano.uuid; var timeStamp = 0; pano.index = index; //注意:index不等于uuid,因为有的uuid缺失。但是visibles中存的是下标! Potree.loadPointCloud(cloudPath, name, number, timeStamp, e => { //开始加载点云 var scene = viewer.scene; var pointcloud = e.pointcloud; var config = Potree.config.material; var material = pointcloud.material; material.minSize = config.minSize; material.maxSize = config.maxSize; material.pointSizeType = /* 'ADAPTIVE'// */config.pointSizeType; //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(0.1 /* config.realPointSize */); //material.size = config.pointSize; pointcloud.changePointOpacity(1); material.shape = Potree.PointShape.SQUARE; pointcloud.color = config.pointColor; pointcloud.dataset_id = datasetId; //多个点云指向一个datasetId pointcloud.panoUuid = pano.uuid; pointcloud.timeStamp = timeStamp; if (datasetData) { //非4dkk pointcloud.datasetData = datasetData; pointcloud.hasDepthTex = Potree.settings.useDepthTex && !!datasetData.has_depth; } //transformPointcloud(pointcloud, pano) scene.addPointCloud(pointcloud); pointcloudLoaded++; if (pointcloudLoaded == panoCount) { datasetLoaded++; viewer.images360.addPanoData(panoData, pointcloud); if (datasetLoaded == datasetsCount) { pointcloudLoadDone(); } } }); }); }; if (!Potree.settings.isOfficial) { Potree.settings.datasetsPano = { 'testDataset': null }; Potree.loadPanosInfo(data => { loadPanosDone('testDataset', data.sweepLocations); }); } Potree.loadPanosCloudStart = EditCloudsArgs => { Potree.loadDatasets(datasets => { Potree.datasetData = datasets; //4dkk的加载为空[] Potree.loadImgVersion(function () { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; Potree.settings.panoVersion = e.imgVersion; //全景图被替换后 if (!(EditCloudsArgs instanceof Array)) EditCloudsArgs = [EditCloudsArgs]; EditCloudsArgs.forEach((e, i) => { //加载点云和漫游点 if (e.datasetId == void 0) { console.error('没有datasetId '); // 看看和看见没有或为0 e.datasetId = i; //经常没有datasetId所以自己加 } Potree.settings.datasetsPanos[e.datasetId] = null; Potree.loadPanosDone(e.datasetId, e.clouds); }); }); }, number); }; EditCloudsArgs && Potree.loadPanosCloudStart(EditCloudsArgs); } function mergeEditStart(dom, mapDom) { Potree.settings.editType = 'merge'; Potree.settings.intersectOnObjs = true; Potree.settings.boundAddObjs = true; Potree.settings.unableNavigate = true; if (Potree.settings.queryCloudLonLatUrl) { //点云使用其经纬度作为默认位置 Potree.loadControlPoint = /* async */function (callback, sceneCode, onError, prefix) { //点云绑定地图 return Potree.loadFile(Potree.settings.queryCloudLonLatUrl.replace('{sceneCode}', sceneCode), { fetchMethod: 'post' }, callback, onError); }; } var viewer = new Potree.Viewer(dom, mapDom); var Alignment = viewer.modules.Alignment; viewer.setEDLEnabled(false); viewer.setFOV(Potree.config.view.fov); //viewer.loadSettingsFromURL(); { viewer.mainViewport.view.position.set(30, 30, 30); viewer.mainViewport.view.lookAt(0, 0, 0); viewer.updateModelBound(); //init //this.bound = new THREE.Box3(new THREE.Vector3(-1,-1,-1),new THREE.Vector3(1,1,1)) viewer.transformationTool.setModeEnable(['translation', 'rotation']); //viewer.ssaaRenderPass.sampleLevel = 1 // sampleLevel为1 的话,ground就不会变黑 viewer.inputHandler.fixSelection = true; //不通过点击屏幕而切换transfrom选中状态 } Potree.settings.sizeFitToLevel = true; //当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 Potree.loadPointCloudScene = function (url, type, id, title, done, onError, prefix1, prefix2) { //对应4dkk的场景码 var dataset, useLonLat; var loadCloud = _ref => { var { cloudPath, sceneName, sceneCode, timeStamp, color } = _ref; Potree.loadPointCloud(cloudPath, sceneName, sceneCode, timeStamp, e => { var scene = viewer.scene; var pointcloud = e.pointcloud; var config = Potree.config.material; var material = pointcloud.material; pointcloud.datasetData = dataset; pointcloud.hasDepthTex = dataset && Potree.settings.useDepthTex && !!dataset.has_depth; pointcloud.initialPosition = pointcloud.position.clone(); pointcloud.pos1MatrixInvert = new Matrix4().setPosition(pointcloud.initialPosition).invert(); material.minSize = config.minSize; material.maxSize = config.maxSize; material.pointSizeType = config.pointSizeType; //Potree.PointSizeType[config.pointSizeType]//Potree.PointSizeType.ADAPTIVE;//FIXED pointcloud.changePointSize(config.realPointSize); //material.size = config.pointSize; pointcloud.changePointOpacity(1); dataset && useLonLat && (transformPointcloud(pointcloud, dataset), pointcloud.hasLonLat = true); material.shape = Potree.PointShape.SQUARE; color && (pointcloud.color = pointcloud.material.color = color); pointcloud.timeStamp = timeStamp; scene.addPointCloud(pointcloud); { viewer.updateModelBound(); var { boundSize, center } = viewer.bound; viewer.dispatchEvent({ type: 'loadPointCloudDone' }); if (!Potree.settings.UserPointDensity) { Potree.settings.UserPointDensity = 'high'; //'middle' } Potree.Log(' 点云加载完毕', sceneName, sceneCode); } viewer.dispatchEvent('allLoaded'); done(pointcloud); }, onError); }; if (type == 'laser') { var sceneCode = url; var load = () => { Potree.loadDatasets(data => { var originDataset = data.find(e => e.sceneCode == sceneCode); //只加载初始数据集 /* if(!originDataset){ //应该是data为空,原因未知 } */ Potree.settings.cloudAddMapping && (prefix2 += '/' + originDataset.mapping); var timeStamp = originDataset.updateTime ? originDataset.updateTime.replace(/[^0-9]/ig, '') : ''; //每重算一次后缀随updateTime更新一次 //let cloudPath = `${Potree.settings.urls.prefix1}/${Potree.settings.webSite}/${sceneCode}/data/${sceneCode}/webcloud/cloud.js` var cloudPath = "".concat(prefix2, "/").concat(originDataset.webBin); //webBin添加原因:每次裁剪之类的操作会换路径,因为oss文件缓存太严重,更新慢 dataset = originDataset; loadCloud({ cloudPath, sceneName: originDataset.sceneName, sceneCode, timeStamp, color: originDataset.color }); }, sceneCode, onError, prefix1); }; if (Potree.settings.queryCloudLonLatUrl) { Potree.loadControlPoint(ctlData => { useLonLat = ctlData.controlPoint.status == 1; //设置了地理位置 load(); }, sceneCode, e => { console.error('loadControlPoint error', e); load(); }); } else { load(); } } else { //las or ply 直接用url var name = type + '|' + id + '|' + title; //有漫游点吗 if (url instanceof Array) { if (url.length == 1) { url = url[0]; } else { console.error('有多个点云?暂时还不支持', url, name); //多个点云要一起移动没想好怎么写 } } var cloudPath = url + '/cloud.js'; loadCloud({ cloudPath, sceneName: name, sceneCode: name, timeStamp: '' }); } }; var setMatrix = pointcloud => { //为了漫游点变换,要算一下 类似setMatrix /* pointcloud.transformMatrix = new THREE.Matrix4().multiplyMatrices(pointcloud.matrix, pointcloud.pos1MatrixInvert)//还原一点位移 pointcloud.transformInvMatrix.copy(pointcloud.transformMatrix).invert() pointcloud.rotateMatrix = new THREE.Matrix4().makeRotationFromEuler(pointcloud.rotation); pointcloud.rotateInvMatrix.copy(pointcloud.rotateMatrix).invert() pointcloud.panos.forEach(e=>e.transformByPointcloud()) */ //pointcloud.updateBound() //pointcloud.getPanosBound() viewer.updateModelBound(); }; var moveModelWhenLoad = false; var moveModel = e => { //根据鼠标移动的位置改变位置 var { x, y } = Potree.Utils.getPointerPosAtHeight(0, e.pointer); //过后改为根据intersect的点来设置底部高度;这样的话,需要发送高度 /*let pos = new THREE.Vector3(x,y, planeZ ) modelEditing.updateMatrixWorld() let boundCenter = modelEditing.boundingBox.getCenter(new THREE.Vector3).applyMatrix4(modelEditing.matrixWorld); */ MergeEditor.moveBoundCenterTo(modelEditing, new Vector3(x, y, modelEditing.boundCenter.z)); //使模型中心的xy在鼠标所在位置 modelEditing.dispatchEvent("position_changed"); }; var cancelMove = () => { modelEditing = null; viewer.removeEventListener('global_mousemove', moveModel); viewer.removeEventListener('global_click', confirmPos); }; var confirmPos = () => { MergeEditor.focusOn(modelEditing); cancelMove(); return { stopContinue: true }; }; var modelType, modelEditing, MergeEditor = viewer.modules.MergeEditor; Potree.addModel = function (prop, done, onProgress, onError) { //加载模型 var loadDone = model => { model.dataset_id = prop.id; //唯一标识 { //设置下默认经纬度位置,当点击恢复默认时要恢复到此位置 if (!model.isPointcloud) { //有经纬度 3dtiles model.rotation.copy(prop.baseRotation); //有的需要翻转90度 var lonlat = prop.raw.wgs84 || prop.raw.rtkLocation; //前者为素材库的osgb的 if (lonlat) { var locationLonLat = lonlat.split(',').map(e => parseFloat(e)); var location = new Vector3().fromArray(viewer.transform.lonlatToLocal.forward(locationLonLat)); model.hasLonLat = true; if (model.fileType == '3dTiles' && prop.is4dkkModel) { //深时深光mesh model.position.fromArray(model.runtime.getTileset().tileset.root.boundingVolume.box.slice(0, 3)); //必须要平移一段才能重合 model.position.copy(Potree.math.convertVector.ZupToYup(model.position)); //饶原点旋转 类似Alignment.setMatrix 和点云一样处理 if (prop.raw.orientation) { model.updateMatrixWorld(); //此时和点云没有旋转平移时一样。 var rotMatrix = new Matrix4().makeRotationAxis(new Vector3(0, 0, 1), parseFloat(prop.raw.orientation)); var pos2Matrix = new Matrix4().setPosition(location); //最后是平移 var matrix = new Matrix4().multiplyMatrices(pos2Matrix, rotMatrix); model.matrix.premultiply(matrix); model.matrix.decompose(model.position, model.quaternion, model.scale); } else { model.position.add(location); } } else { MergeEditor.moveBoundCenterTo(model, location); if (prop.raw.orientation) { model.rotation.y = parseFloat(prop.raw.orientation); } if (prop.is4dkkModel) { console.warn('遇到is4dkkModel的且有经纬度的mesh,但不是3dtiles! 位置估计不准', model); //看见的场景说是市场不会带rtk的。所以我们这边标品也没存rtk的坐标信息。和产品聊了,不处理。意思就是,只有激光场景开启了rtk的rtkLocation才有值 /* if(model.panos?.length){//只能通过漫游点经纬度来校准 //但这时候panos还没加载。。。。 let sceneCode = ...从url中解析 Potree.loadDatasets((data)=>{ //获取datasetId let originDataset = data.find(e=>e.sceneCode == sceneCode);//只加载初始数据集 Potree.loadPanos(originDataset.datasetId,()=>{ shouldPos = 获取坐标 data[0].location经纬度 model.position.add(new THREE.Vector3().subVectors(shouldPos, model.panos[0].position)) }) }, sceneCode, (e)=>{ console.log(e) } , prop.prefix) } */ } } } } if (model.fileType == 'shp') { if (model.prjNotSure) { MergeEditor.moveBoundCenterTo(model, new Vector3()); //因为不确定坐标类型,而点坐标可能几万米,所以放原点好一些 } model.hasLonLat = true; } MergeEditor.setModelBtmHeight(model, 0); // 离地高度为0 (因为不想在地图下方所以高程不管了,都在地面上即可) if (model.hasLonLat) { model.lonLatPos = model.position.clone(); model.lonLatRot = model.rotation.clone(); } } if (prop.position) { if (prop.position.x != 0 || prop.position.y != 0 || prop.position.z != 0) { //移动过后使用移动后的坐标 model.position.copy(prop.position); } else {//用户没设置位置(首次添加后没按保存) } } if (prop.rotation && (prop.rotation.x != 0 || prop.rotation.y != 0 || prop.rotation.z != 0)) { //model.rotation.setFromVector3(prop.rotation) model.rotation.copy(prop.rotation); } if (prop.scale != void 0) { model.scale.set(prop.scale, prop.scale, prop.scale); } if (model.isPointcloud) { model.renderOrder = Potree.config.renderOrders.model; //same as glb } if (Potree.settings.maintainBtmZ) { //transform --------维持离地高度和中心点的版本(local ver) var updateBound = () => { model.updateMatrixWorld(); viewer.updateModelBound(); }; var maintainBtmZAndCenter = () => { MergeEditor.maintainBoundXY(model); MergeEditor.setModelBtmHeight(model); updateBound(); model.dispatchEvent('transformChanged'); }; model.addEventListener('position_changed', () => { updateBound(); MergeEditor.getBoundCenter(model); //更新boundcenter MergeEditor.computeBtmHeight(model); if (prop.bottomRange && (model.btmHeight > prop.bottomRange.max || model.btmHeight < prop.bottomRange.min)) { model.btmHeight = MathUtils.clamp(model.btmHeight, prop.bottomRange.min, prop.bottomRange.max); MergeEditor.setModelBtmHeight(model); updateBound(); } model.dispatchEvent('transformChanged'); }); model.addEventListener("rotation_changed", maintainBtmZAndCenter); model.addEventListener("scale_changed", maintainBtmZAndCenter); model.addEventListener('transformChanged', () => { MergeEditor.modelTransformCallback(model); }); //离地高度只是boundingbox在transform后的最低点的高度,而非模型transform后的最低点的高度,所以旋转过后看起来不太准确 } else { //transform --------维持中心点的版本 var _updateBound = () => { model.updateMatrixWorld(); viewer.updateModelBound(); }; var maintainCenter = e => { //MergeEditor.maintainBoundXY(model) e.by2d || MergeEditor.maintainBoundCenter(model); _updateBound(); model.dispatchEvent({ type: 'transformChanged', byControl: e.byControl }); }; model.addEventListener('position_changed', e => { //要先发送position_changed再其他 _updateBound(); MergeEditor.getBoundCenter(model); //更新boundcenter model.dispatchEvent({ type: 'transformChanged', byControl: e.byControl }); }); model.addEventListener("rotation_changed", maintainCenter); model.addEventListener("scale_changed", maintainCenter); model.addEventListener('transformChanged', () => { MergeEditor.modelTransformCallback(model); }); } model.updateMatrixWorld(); viewer.updateModelBound(); MergeEditor.getBoundCenter(model); //初始化 //model.lastMatrixWorld = model.matrixWorld.clone() model.lastMatrixWorld = new Matrix4(); MergeEditor.modelTransformCallback(model, true); prop.scale != void 0 && model.isPointcloud && model.changePointSize(); //有的被缩放的很小导致testMaxNodeLevel时距离较远时被return 但点云过大急需changesize done(model); // 先发送成功,因为2d界面会随机执行changePosition等初始化,然后这边再将模型移到中心地面上 if (prop.isFirstLoad) { if (model.hasLonLat || !moveModelWhenLoad) { setTimeout(() => { MergeEditor.focusOn(model); }, 1); } else { MergeEditor.moveBoundCenterTo(model, new Vector3(0, 0, 0)); } MergeEditor.setModelBtmHeight(model, 0); //初始加载设置离地高度为0 if (prop.mode != 'single') { //如果不是模型展示页,模型会随着鼠标位置移动 modelEditing = model; if (!model.hasLonLat && moveModelWhenLoad) { viewer.addEventListener('global_mousemove', moveModel); viewer.addEventListener('global_click', confirmPos, { importance: 3 }); } } model.dispatchEvent("position_changed"); } else { //MergeEditor.setModelBtmHeight(model, prop.bottom || 0) //默认离地高度为0 modelEditing = null; } MergeEditor.modelAdded(model); }; if (prop.type == 'obj' || prop.type == 'glb') { var callback = object => { object.isModel = true; object.traverse(e => e.material && (e.material.transparent = true)); loadDone(object); }; var info = { fileType: prop.type, id: prop.id, unlit: prop.unlit, url: prop.url, name: prop.title }; viewer.loadModel(info, callback, onProgress, onError); } else if (prop.type == 'osgb' || prop.type == 'b3dm') { //3d tiles var _callback = object => { object.isModel = true; loadDone(object); }; viewer.loadModel({ fileType: '3dTiles', id: prop.id, name: prop.title, maximumScreenSpaceError: prop.maximumScreenSpaceError, url: prop.url }, _callback, onprogress); } else if (prop.type == 'shp') { var _callback2 = object => { object.isModel = true; loadDone(object); }; viewer.loadModel({ fileType: 'shp', id: prop.id, name: prop.title, url: prop.url }, _callback2, onprogress); } else if (prop.type == '3dgs') { var _callback3 = object => { object.isModel = true; loadDone(object); }; viewer.loadModel({ fileType: '3dgs', id: prop.id, name: prop.title, url: prop.url }, _callback3, onprogress); } else { prop.url instanceof Array && (prop.url = prop.url[0]); //deal bug var cloudPrefix = Potree.settings.urls.getPrefix(1, prop); Potree.loadPointCloudScene(prop.url, prop.type, prop.modelId, prop.title, pointcloud => { { pointcloud.matrixAutoUpdate = true; if (pointcloud.hasLonLat) { pointcloud.matrix.decompose(pointcloud.position, pointcloud.quaternion, pointcloud.scale); //将数据集的经纬度和旋转应用到rotation和position (注意position和translateUser并不一样) } else if (!prop.isFirstLoad) { //点云一般加载后position都不是0, 但后台初始化为0所以先归零要不然撤销后容易错 pointcloud.position.set(0, 0, 0); } } if (Potree.settings.mergeType2 && pointcloud.datasetData) { Potree.loadPanos(pointcloud.datasetData.id, data => { viewer.images360.addPanoData(data, pointcloud); viewer.images360.loadDone(); viewer.scene.add360Images(viewer.images360); loadDone(pointcloud); }, prop.url); } else { loadDone(pointcloud); } }, onError, prop.prefix, cloudPrefix); } }; return { THREE: THREE$1 }; } var changeLog = () => { var textarea = document.createElement('textarea'); textarea.id = "consoleLog"; textarea.style.width = '160px'; textarea.style.height = '200px'; textarea.style.position = 'fixed'; textarea.style.left = 0; textarea.style.bottom = '50px'; textarea.style['z-index'] = 9999; textarea.style.color = 'black'; textarea.style.opacity = 0.9; textarea.style['font-size'] = '12px'; textarea.style['backgroundColor'] = '#ffffff'; document.getElementsByTagName("body")[0].appendChild(textarea); var list = ["log", "error", "warn", "debug", "info", "time", "timeEnd"]; var exchange = function exchange(o) { console["old" + o] = console[o]; console[o] = function () { var args = Array.from(arguments); console["old" + o].apply(this, arguments); var t = document.getElementById("consoleLog").innerHTML; var str = ''; args.forEach(a => { str += a + ' '; }); document.getElementById("consoleLog").innerHTML = str + "\n\n" + t; }; }; for (var i = 0; i < list.length; i++) { exchange(list[i]); } }; /* 坐标转换问题: 由于控制点可以随便输入,所以本地和地理位置的转换也是可拉伸的。而navvis的转换是等比由中心展开, 所以对比两种转化方式时误差较大。 另外地理注册控制点是有参考数据集的,若参考数据集和我放置在0,0,0的数据集一致,就可直接使用,否则要转换。 --------- lonlat和空间坐标其实并非线性关系,因为lonlat其实是角度。当两个数据集在地球两端时,它们之间的夹角都相差180度了。 所以若要准确展示的话,需要将点云内所有物体,如漫游点,都先获取lonlat再去算local。或者直接将点云整体的transformMatrix考虑上在地球上相对于初始数据集的偏转。 支持ctrl+z、ctrl+Y 撤销回退的页面有: 测量、土方量、空间模型 这三个页面的点线拖拽;点云裁剪、点云下载中的裁剪 的框; 点云编辑的变换; 数据集校准; (所有数据一旦删除则无效 ) 其他快捷键: 按alt鼠标滚轮或WS键放慢。 测量or土方量: 按Alt键可以平行拖拽点。&dragPolyBeyondPoint 后缀则可平行拖拽到无点云区域 。 按M键拖拽点可以复制出当前点 点云按空格键+左键拖拽场景,可以不改相机位置的旋转视角 */ //xzw add var labelorder = 5; var config$1 = { //配置参数 不可修改 displayMode: { showPointCloud: { atPano: { showPoint: true, showSkybox: false, pointUsePanoTex: false }, transition: { showPoint: true, showSkybox: false, pointUsePanoTex: false }, canLeavePano: true //是否能离开pano位置 }, showPanos: { atPano: { showPoint: false, showSkybox: true, pointUsePanoTex: false }, transition: { //showPoint: true, showSkybox: true //pointUsePanoTex: true //是否使用全景贴图 }, canLeavePano: false }, showBoth: { atPano: { showPoint: true, showSkybox: true, pointUsePanoTex: false //? }, transition: { showPoint: true, showSkybox: true, pointUsePanoTex: true }, canLeavePano: true //是否能离开pano位置 离开后自动变为showPointCloud }, //test: pointUsePanoTex: { //---静止时调点云 atPano: { showPoint: true, showSkybox: false, pointUsePanoTex: true }, transition: { showPoint: true, showSkybox: true, pointUsePanoTex: true //是否使用全景贴图 }, canLeavePano: false } }, urls: { //prefix: //当前网站域名 prefix1: 'https://laser-oss.4dkankan.com', //点云oss prefix2: 'https://testlaser.4dkankan.com', prefix3: 'https://4dkk.4dage.com', //全景图oss prefix4: 'https://uat-laser.4dkankan.com', //测试服 线上当前网站接口域名 prefix5: 'https://laser.4dkankan.com/backend', //正式服 线上当前网站接口域名 prefix6: 'https://mix3d.4dkankan.com/backend', //融合 prefix7: 'https://xfhd.4dkankan.com/backend', //融合 handlePrefix: prefix => { //去掉最后一个/, 否则国外的一些服务器不能访问 // if (prefix.slice(-1) == '/') { prefix = prefix.slice(0, prefix.length - 1); } return prefix; }, getName(num) { var name; if (num === 1) { name = 'laserOSSRoot'; } else if (num === 3) { name = 'panoOSSRoot'; } else if (num === 8) { name = 'panoRoot'; } return name; }, setPrefix(num, url) { //本地版融合有经过次设置 this['prefix' + num] = url; this[this.getName(num)] = url; }, getPrefix(num, object) { var prefix, name = this.getName(num); var getFromModel = () => { var _ref; var prop = (_ref = object.props || object) === null || _ref === void 0 ? void 0 : _ref.raw; return prop && prop[name]; }; prefix = this[name] || getFromModel() || this['prefix' + num]; return prefix; }, templates: {} }, transitionsTime: { flyMinTime: 650, // 毫秒/米 flytimeDistanceMultiplier: 120 }, view: { fov: 70, //navvis:50 near: 0.05, // too small will result in z-fighting far: 10000 }, map: { //mapViewer mapHeight: -1000, //要比点云低。最低 cameraHeight: 1000 //最高 ,注意(如sitemodel)其他的物体不能超过这个高度 }, minNodeSize: 30, // perspectiveCamera允许加载的node的最小可见像素宽度。越大越省性能 tiles3DMaxMemory: 200, //M. 最大支持3dTiles的内存大小 超出会崩溃。 改太小太大都会卡,太大崩溃 //崩溃历史: 200罗敏电脑崩 pointDensity: { magnifier: { maxLevelPercent: 1, pointBudget: 1 * 1000 * 1000, //至少显示这么多 minNodeSize: 5 //pick时调高精度 }, panorama: { //显示全景时的漫游。因为点只能显示1个像素的大小,所以必须很密集,但又要限制点的数量 maxLevelPercent: 0.6, pointBudget: /* 4*1000*1000// */browser.isMobile() ? 0.2 * 1000 * 1000 : 0.4 * 1000 * 1000, //点云总最大数 minNodeSize: 100 }, fourViewports: { //分四屏时防止卡顿 maxLevelPercent: 0.9, pointBudget: 3 * 1000 * 1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000) minNodeSize: 70 }, fourViewportsMain: { //分四屏时防止卡顿 maxLevelPercent: 0.9, pointBudget: 3 * 1000 * 1000, // 只要限制这个就足够 (为什么分屏focus区域不同会闪烁,navvis不会)(navvis:maxLevel:5,pointBudget:1*1000*1000) minNodeSize: 70 }, panoEdit: { maxLevelPercent: 1, //在远处时由于pointBudget限制而展示稀疏,凑近时就变为最高质量了 pointBudget: 4 * 1000 * 1000, //要使点云达到200个以上时还不卡 percentByUser: true, minNodeSize: 80 //点云多的话远处的尽量就不可见吧 }, low: { //highPerformance maxLevelPercent: 0.4, //最小为0 percentByUser: true, //如果用户定义了percent,使用用户的 pointBudget: browser.isMobile() ? 1 * 1000 * 1000 : 2 * 1000 * 1000, minNodeSize: 40 / window.devicePixelRatio }, middle: { //balanced //不同场景相同级别所产生的numVisibleNodes和numVisiblePoints不同,如果分层比较细,可能要到level8才能看清,那么level5看到的点就很大且很少,如隧道t-e2Kb2iU maxLevelPercent: 0.7, percentByUser: true, pointBudget: browser.isMobile() ? 1.5 * 1000 * 1000 : 3.5 * 1000 * 1000, minNodeSize: 30 / window.devicePixelRatio }, high: { //highQuality maxLevelPercent: 1, percentByUser: true, pointBudget: browser.isMobile() ? 3 * 1000 * 1000 : 6 * 1000 * 1000, //原本最高是8,但是大部分电脑都太卡了,降 minNodeSize: 20 / window.devicePixelRatio //手机上因为像素点小,远一点的时候更需要加载密集的点云。(没事,有pointBudget限制着,会先从近处加载高级node,再远就不加载了) }, screenshot: { maxLevelPercent: 1, pointBudget: browser.isMobile() ? 4 * 1000 * 1000 : 10 * 1000 * 1000, //一般只有电脑需要截图,手机的加载多会崩 minNodeSize: 40 / window.devicePixelRatio }, screenshot2: { maxLevelPercent: 1, pointBudget: browser.isMobile() ? 8 * 1000 * 1000 : 15 * 1000 * 1000, minNodeSize: 20 / window.devicePixelRatio }, ultraHigh: { maxLevelPercent: 1, pointBudget: 20 * 1000 * 1000, minNodeSize: 10 / window.devicePixelRatio } //数值由testLevelSteps得来,其中nodeMaxLevel为2时,low和middle的都是1,如果真有这么低的点云就单独处理下。 //多个viewport尽量保证pointBudget一样,或者pointBudget不能太低于所需,否则会反复加载了又清除 }, clip: { color: '#FFC266' //map }, measure: { color: '#00C8AF', default: { color: "#64C8BB", //"#00c7b2", opacity: 0.7 }, highlight: { color: '#00C8AF', //"#00c7b2", opacity: 1, labelOpacity: 0.7 //1会挡住线和端点 }, guide: { color: '#FFFFFF', opacity: 0.8 }, backColor: '#333333', lineWidth: 3, textColor: "#000000", //"#FFFFFF" mulLabelHideFaraway: false, // 多折线根据远近显示label adsorptMinDis: 30, //最小吸附距离(像素) showName: true }, material: { //初始化 pointSize: 0.1, realPointSize: 0.1, //实际上的ui滑动条默认大小(兼容旧的版本) minSize: 0.1, maxSize: 10000, pointSizeType: 'ATTENUATED', //'ADAPTIVE'//'ADAPTIVE' \ FIXED //ADAPTIVE的在小房间里大小会不太匹配,但在远景似乎更好 /* ATTENUATED : 衰减 真实大小,靠近时感觉是点云一点点变多,缝隙变小 ADAPTIVE: 自适应 大小根据level变化,越高越小。靠近时感觉点云由大慢慢细分成小份。这个感觉更佳但是navvis为何不用这个 */ absolutePanoramaSize: 1.3, //全景漫游时的size 是fixed的模式 //sizeAtPanoRtEDL : 2000, pointColor: '#ffffff' //sizeAddAtPanoRtEDL : 0.5, //全景模式静止时的size //ADAPTIVE : 字会失真扭曲 //'ATTENUATED' 往旁边看有缝隙、点在浮动 }, skyboxBgWidth: 100, renderLayers: { //渲染层,方便分批渲染管理,替代scene的创建。数字不代表顺序。(数字不能太大) sceneObjects: 0, //default skybox: 1, pointcloud: 2, model: 3, monitor: 4, light: 5, //measure:4, //tags:5, magnifier: 6, magnifierContent: 7, volume: 8, transformationTool: 9, map: 10, mapObjects: 11, //default bothMapAndScene: 12, onlyMapVisi: 13, //只能mapViewer可见 mapUnvisi: 14, //只有mapViewer不可见 sideVisi: 15, //只有侧面可见 bg: 16, bg2: 17, layer1: 18, // 备用1 layer2: 19 // 备用2 }, renderOrders: { //会影响到绘制、pick时的顺序。 line: 3, reticule: 5, measureMarker: 6, measureLabelSub: 7, measureLabel: 8, sorptionSign: 10, model: 10, path: { label: labelorder, edge: labelorder, line: labelorder, //会被edge遮住一些,不过无碍 marker: labelorder + 1 //cover edge }, tag: { label: labelorder, spot: labelorder, line: labelorder - 1, onMesh: { line: labelorder, spot: labelorder - 1 } }, magnifier: 50 /* renderOrder: 理想情况是大家渲染层级都一样,用真实的遮挡情况。(depthTest为false的话会three会自动根据深度调整渲染顺序, 只是随着角度变化常有错) 但有时候为了处理特殊情况,如希望热点的线永在热点之后不穿出,需要增加层级。但热点和热点之间、热点和其他物品是平等的,而降低的层级总被远处的高层级遮挡。 因此无论何时尽量减少层级数目。如有必要,可临时将容易被忽视穿帮的线条设置为低层级。 */ }, siteModel: { names: { 'building': '建筑', 'floor': '楼层', 'room': '房间' }, floorHeightDefault: 5 //一层楼的高度 }, panosEdit: {}, tiling: { panoPreRenderRepeatDelay: 2500, panoPreRenderDelay: 500, preRenderTourPanos: browser.valueFromHash("tileprerender", 0), tilingFlagNames: ["usetiles", "tiles"], maxNavPanoQuality: browser.valueFromHash("maxtileq", null), maxZoomPanoQuality: browser.valueFromHash("maxztileq", null), overlayStyle: browser.valueFromHash("tileoverlay", 0), uploadIntervalDelay: browser.valueFromHash("tileupdelay", 10), initialIntervalDelay: browser.valueFromHash("itiledelay", 0), maxNonBaseUploadsPerFrame: browser.valueFromHash("maxnbtpf", 1), maxBaseUploadsPerFrame: browser.valueFromHash("maxbtpf", 6), customCompression: browser.valueFromHash("tilecustcomp", 0), mobileHighQualityOverride: !1, allowUltraHighResolution: !0 }, navigation: { panoScores: !1, mouseDirection: !0, filterStrictness: .75, angleFactor: -30, directionFactor: 10, distanceFactor: -1, optionalityFactor: 3 }, axis: { 'x': { color: '#ea3f3f' /* '#d0021b' */ /* 'red' */ }, 'y': { color: '#86c215' /* '#86c542' */ /* 'green' */ }, 'z': { color: '#3396f8' /* '#3399c8' */ /* 'blue' */ }, 'xyz': { color: '#ccc' } }, shelterMargin: 0.15, //多少米内不算遮挡 (有的场景深度图不准,和点云差别蛮大如SG-t-24F0iT3pKAO) highQualityMaxZoom: 2, ultraHighQualityMaxZoom: 3, panoFieldRadius: 10, //当前位置多远范围内可以切全景模式 clickMaxDragDis: 3, clickMaxPressTime: 200, //ms doubleClickTime: 300, //双击间隔时间 testNodeCount1: browser.isMobile() ? 6 : 4, //testMaxNode次数达到这个数字时,changePointSize才使用nodeMaxLevel。 (调试时比较卡,在线上实际只需要3) background: '#232323', mapBG: /* '#232323', */'#F5F5F5', //地图的clearColor pickFrontPointRatio: 50, colors: { //from navvis red: [213, 0, 0], pink: [197, 17, 98], purple: [170, 0, 255], "deep purple": [98, 0, 234], blue: [41, 98, 255], "light blue": [0, 145, 234], cyan: [0, 184, 212], teal: [0, 191, 165], green: [0, 200, 83], "light green": [100, 221, 23], lime: [174, 234, 0], yellow: [255, 214, 0], amber: [255, 171, 0], orange: [255, 109, 0], "deep orange": [255, 61, 0] }, maxLRUPoints: 40e6, depthTexUVyLimit: 0.141, // 在这个范围内是没有深度的,从图片算的0.14003, 设置为稍大于这个数值 maxSplatCount: 2000000 //整个网页最大支持显示多少个 //neighbourPath:'data/1111/extraNeighbours.js' //本地版手动额外配置,权重最高 }; config$1.OrthoCameraLimit = { standard: { zoom: { min: 0.0004, max: 500 }, //如果camera缩太小,地图会因为数字边界问题而扭曲 latPad: 20, xBound: [-4e6, 4e6] }, expand: { zoom: { min: 0.0004, max: 500 }, //如果camera缩太小,地图会因为数字边界问题而扭曲 latPad: 20, xBound: [-6e6, 6e6] } }; /* 显示模式: 1只显示点云: 滚轮为前进后退,方向键可以行走。进入漫游点时自动变为混合(这样全景可以弥补缝隙),过渡时只显示点云。 2只显示全景: 不能任意行走。 过渡时显示贴图材质非edl的点云(否则有折痕不贴合)。 3混合:都显示。 不能任意行走。过渡时显示贴图材质非edl的点云(因为只显示点云的话不太美,点云很碎,不细腻) */ window.testLevelSteps = function (steps) { //[0.4,0.7,1] if (!steps) { var s = Potree.config.pointDensity; steps = [s.low.maxLevelPercent, s.middle.maxLevelPercent, s.high.maxLevelPercent]; } var max = 1; while (++max <= 12) { var r1 = steps.map(e => e * max); var r2 = steps.map(e => Math.round(e * max)); console.log("\u5F53nodeMaxLevel\u4E3A".concat(max, "\u65F6\uFF0C\u6BCF\u4E00\u7EA7\u7684level\u5206\u522B\u4E3A").concat(r2, ", (\u5C0F\u6570\uFF1A").concat(r1, ")")); } console.log('请检查每一层的三个level是否有重复'); }; function getPrefix() { var u = window.location.href.split('//'); var v = u[1].split('/'); return v[0]; } var isTest = browser.urlHasValue('test'); var settings = { //设置 可修改 editType: '', number: '', //场景序号 originDatasetId: '', //场景原本的数据集id,应该就是数据集第一个吧 isOfficial: false, webSite: 'testdata', //正式:'datav1', //不同环境对应的静态文件的地址不同 // language : "zh" isLocal: false, //是否本地 局域网版本 libsUrl: '../libs/', displayMode: '', isTest, prefix: getPrefix(), maxLRUPoints: config$1.maxLRUPoints, pointDensity: '', UserPointDensity: '', //pointDensity会随着进入不同的模块而自动改变,UserPointDensity记录了用户的设置 UserDensityPercent: null, //点云密度百分比 ifShowMarker: true, //显示漫游点 floorplanType: {}, //平面图类型 'default' | 'diy' 不同数据集不同{datasetId:...} floorplanEnable: false, floorplanEnables: {}, floorplanRequests: {}, //开始加载了的 mapEnable: true, //地图区域是否加载地图 cameraFar: config$1.view.far, //相机最远范围 1-300 //limitFar: true, //是否使用setting的cameraFar来限制(如在点云裁剪时为false) showPanoMesh: false, //显示小球, dblToFocusPoint: false, //调试时如果需要双击飞向某个点云的点,就打开。此时不在漫游点的话单击将无法漫游。//因和单击漫游冲突 unableNavigate: false, //进入如裁剪界面时 禁止漫游 sizeFitToLevel: false, //当type为衰减模式时自动根据level调节大小。每长一级,大小就除以2 zoom: { enabled: true, min: 1, max: config$1.highQualityMaxZoom }, navConstantly: true, navTileClass: /* browser.isMobile() ? '1k' : */'2k', //默认加载到 tileClass: '4k', //最高可达 /* loadTilesWhenUnfocus:false, //页面unfocus时也仍在加载tiles loadPointsWhenUnfocus:true, //页面unfocus时也仍在加载点云 */ //initialShowPano:true drawEntityData: false, //包括marker、线 zoomFromPointert: { //定点缩放(包括点云模式、全景模式、地图) whenPanos: true, whenPointCloud: true, map: true }, rotAroundPoint: true, //点云模式是否能绕intersectPoint旋转 tourTestCameraMove: false, //测试镜头时,不移动真实的镜头, 只移动frustum cameraAniSmoothRatio: 20, //镜头动画平滑系数,越高越平滑 urls: $.extend({}, config$1.urls, { prefix: config$1.urls.prefix4 //主要使用的 是测试环境,根据不同工程更改 }), useDepthTex: true, //使用深度贴图,但不代表一定有(得到的intersect更快速准确和稳定) SS-t-7DUfWAUZ3V //matUseDepth:false, //panoEdit: datasetsPanos: {}, //mergeModel: boundAddObjs: false, intersectOnObjs: false, intersectWhenHover: true, depTexLocBindDataset: true, //是否在pano模式下,使用深度图得到intersect的话,改intersect能属于该pano所在的点云。也就相当于在全景模式下intersect的点属于该全景图 notAdditiveBlending: false, //点云是否使用普通的blend, 否则会曝光过渡 precision: 2, // 两位小数 unit: 'm', useV4url: true, //v4的全景图等路径不一样 scene_view_data useRTskybox: true, //直接使用rtEDL绘制到屏幕,当是全景模式时. 在降4倍时能给render节省1毫秒,gpu时间未测 useRTPoint: true, //直接使用rtEDL绘制到屏幕,当是点云模式时。可以大大节省gpu时间。但有锯齿 pointEnableRT: false, //点云模式时是否绘制到rtEDL。如果不需要遮挡效果就不需绘制 cloudSameMat: true, //因为点云个数较多,就使用相同的材质,可见降低绘制速度(要保证所有点云的maxNodelevel一样,且要算出 material.spacing的平均值) showCompass: false, showAxis: isTest, //testCube : true, // moveToCenter:true, //针对数据集间隔很远的场景 dis>5000 容易抖动 tiles3DMaxMemory: config$1.tiles3DMaxMemory, adsorption: false, //测量时吸附点 pickFrontPointRatio: config$1.pickFrontPointRatio, //默认pick点云时选中靠近镜头的点的偏向 dragPolyBeyondPoint: browser.urlHasValue('dragPolyBeyondPoint'), //ctrlPolygon是否可以拖拽到没点云的地方 panoZoomByPointer: false, //全景图是否定点缩放 areaAtNotPlane: false, showNeighSetGui: browser.urlHasValue('neighGui'), selectShowBox: true, //fastTran: isTest pathSmooth: true, // window.location.href.includes('192.168.0.59') //true //smooth曲线, 非折线 maxClipFadeTime: 0.6 }; Potree.config = config$1; Potree.settings = settings; settings.isLocalhost = settings.prefix.includes('localhost:') || settings.prefix.includes('localhost:'); settings.isFormal = browser.urlHasValue('formal'); //正式环境 本地测试 if (settings.isFormal) { settings.urls.prefix = settings.urls.prefix5; settings.webSite = 'datav1'; } /* let sid = 0 let getName = ()=>{ return sid ++ } */ class BasicMaterial extends ShaderMaterial { constructor() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(Object.assign({}, { uniforms: { color: { type: 'v3', value: o.color || new Color("#FFF") }, map: { type: 't', value: o.map }, opacity: { type: 'f', value: o.opacity == void 0 ? 1 : o.opacity } }, vertexShader: Shaders['basicTextured.vs'], fragmentShader: Shaders['basicTextured.fs'], defines: { HasColor: '' } }, o)); //this.opacity = o.opacity == void 0 ? 1 : o.opacity } copy(source) { super.copy(source); //console.log('copy', source.name111, this.name111, !!source.map ) this.map = source.map; return this; } set opacity(o) { this.uniforms && (this.uniforms.opacity.value = o); //this.transparent = o<1 } get opacity() { return this.uniforms.opacity.value; } set map(o) { this.uniforms.map.value = o; if (o) { this.defines.HasMap = ''; } else { delete this.defines.HasMap; } //可能需要needsUpdate //console.log('hasMap', !!o, this.name111 ) } get map() { return this.uniforms.map.value; } } var texLoader$2 = new TextureLoader(); texLoader$2.crossOrigin = "anonymous"; var createErrorMaterial = function createErrorMaterial() { var t = new MeshBasicMaterial({ transparent: !0, depthWrite: !1, depthTest: !0, opacity: 1, side: DoubleSide }); return t.color = new Color(3355443), t; }; var tempVector = new Vector3(), //sharedata face1 = new Face3(0, 1, 2), face2 = new Face3(2, 3, 0), errorMaterial = createErrorMaterial(), uv00 = new Vector2(0, 0), uv01 = new Vector2(0, 1), uv10 = new Vector2(1, 0), uv11 = new Vector2(1, 1), face1UV = [uv00, uv10, uv11], face2UV = [uv11, uv01, uv00]; var HALF_WORLD_SIZE = 21e6; //略大于半个周长(mapSizeM/2) var MAX_VERTICAL_DIST = 2; var MAX_VERTICAL_DIST_TO_BEST = 1; function defineLocalProj(locationLonLat) { proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat[0].toPrecision(15) + " +lat_0=" + locationLonLat[1].toPrecision(15)); } var getSid = function () { var sid = 0; return function () { return sid++; }; }(); //高德坐标拾取工具 : https://lbs.amap.com/tools/picker class MapLayer extends EventDispatcher { // 包括了 MapLayerBase SceneLayer constructor(viewer_, viewport) { super(); this.sceneGroup = new Object3D(); this.sceneGroup.name = "MapLayer"; this.waitQueue = []; //等待加载的 this.loadingInProgress = 0; this.maps = []; this.frustum = new Frustum(); this.frustumMatrix = new Matrix4(); this.tileColor = new Color(16777215); this.viewport = viewport; this.changeViewer(viewer_); //添加地图 var map = new TiledMapOpenStreetMap(this, this.tileColor); this.addMap(map); //map.setEnable(false) this.sceneGroup.addEventListener('isVisible', () => { this.viewer.mapChanged = true; }); } addMapEntity(data, datasetId) { if (!data || !data[0]) { Potree.Log('平面图无数据', { font: 'red' }); return; } data[0].datasetId = datasetId; var floorplan = new TiledMapFromEntity(this, this.tileColor, data[0]); //[0]? if (floorplan) { floorplan.name += "_" + datasetId; this.addMap(floorplan); floorplan.updateProjection(); floorplan.updateObjectGroup(); var visible = false; if (datasetId in Potree.settings.floorplanEnables) { visible = Potree.settings.floorplanEnables[datasetId]; } else { visible = Potree.settings.floorplanEnable; } if (visible) { this.needUpdate = true; } else { floorplan.setEnable(false); } this.dispatchEvent({ type: 'floorplanLoaded', floorplan }); } return floorplan; } getFloorplan(datasetId) { return this.maps.find(e => e.name == 'floorplan' + "_" + datasetId); } addMap(t) { this.maps.push(t); //this.view.invalidateScene() this.needUpdate = true; this.viewer.mapChanged = true; } removeMap(t) { var e = this.maps.indexOf(t); if (e >= 0) { t.removeFromSceneGroup(this.sceneGroup); this.maps.splice(e, 1); } /* this.view.invalidateScene() */ this.needUpdate = true; this.viewer.mapChanged = true; } changeViewer(viewer_) { //add this.viewer = viewer_; } initProjection() { this.maps.forEach(map => { map.updateProjection(); map.updateObjectGroup(); }); } visibilityChanged() { if (!this.visible) for (var t = 0, e = this.maps; t < e.length; t++) { e[t].removeFromSceneGroup(this.sceneGroup); } } update() { this.needUpdate = false; if (this.disabled || !this.maps.find(e => !e.disabled) || !this.maps.find(e => e.objectGroup.visible)) return; //add this.viewer.mapChanged = true; var e, n, i, r, o; this.updateTimer = void 0, e = this.viewport.camera, n = e.projectionMatrix.clone(); var expandRatio = 1.3; n.elements[0] /= expandRatio; n.elements[5] /= expandRatio; // 为了缓存吗,使边界处也提前加载,扩大显示区域 this.frustumMatrix.multiplyMatrices(n, e.matrixWorldInverse), this.frustum.setFromProjectionMatrix(this.frustumMatrix), this.frustum.planes[4].setComponents(0, 0, 0, 0), this.frustum.planes[5].setComponents(0, 0, 0, 0), i = !0; //console.log('-------------update-----------') for (r = 0; r < this.maps.length; r++) { var map = this.maps[r]; i = map.update(this.frustum, this.sceneGroup) && i; } return [2, i]; } updateProjection() { for (var t = 0, e = this.maps; t < e.length; t++) { var n = e[t]; n.clearProjection(), n.updateObjectGroup(); } } } class TiledMapBase extends EventDispatcher { constructor(name, mapLayer, tileColor, projection) { super(); this.name = name; this.mapLayer = mapLayer, this.tileColor = tileColor, this.bias = 0; this.zIndex = -1; this.objectGroup = new Object3D(); this.objectGroup.name = name; this.objectGroupAdded = !1, this.baseTile = new MapTile(this, this.objectGroup, this.tileColor, null, '0'), this.isTileVisibleBox = new Box3(), this.isTileVisibleVec = new Vector3(); this.projection = projection; this._zoomLevel = 0; //1-20 this.objectGroup.addEventListener('isVisible', () => { this.mapLayer.viewer.mapChanged = true; }); this.computeCount = 0; this.maxLoading = 3; this.loadFailCount = 0; this.loadingInProgress = 0; } get zoomLevel() { return this._zoomLevel; } set zoomLevel(zoomLevel) { if (this._zoomLevel != zoomLevel) { this._zoomLevel = zoomLevel; this.dispatchEvent({ type: 'zoomLevelChange', zoomLevel }); //if(this.name == 'map')console.log(zoomLevel,viewer.mapViewer.camera.zoom) } } updateObjectGroup() { this.position && this.objectGroup.position.copy(this.position).setZ(0), this.quaternion && this.objectGroup.quaternion.copy(this.quaternion), this.objectGroup.updateMatrixWorld(!0); } updateProjection() { if (!this.transformMapToLocal) { this.transformMapToLocal = proj4(this.projection, "LOCAL_MAP"); } } clearProjection() { this.transformMapToLocal = void 0; this.projection !== 'LOCAL_MAP' && this.baseTile.remove(); } setEnable(enable) { //add if (!this.disabled == enable) return; if (enable) { //console.log('setEnable',true) } this.disabled = !enable; Potree.Utils.updateVisible(this.objectGroup, 'setEnable', enable); if (!enable) { this.baseTile.remove(); } else { this.mapLayer.needUpdate = true; } } update(e, n) { this.computeCount = 0; var unavailable = this.disabled || !this.objectGroup.visible; //地图即使不显示也要获得zoomlevel if (this.name != 'map' && unavailable) return; this.updateProjection(); if (!this.transformMapToLocal) return; if (!this.isTileVisible(new Vector3(0, 0, 0), this.mapSizeM, e)) return this.removeFromSceneGroup(n), !0; var viewport = this.mapLayer.viewport; var i = new Vector3(-.5 * this.mapSizeM, 0, 0); i.applyMatrix4(this.objectGroup.matrixWorld), i.project(viewport.camera); var o = new Vector3(.5 * this.mapSizeM, 0, 0); o.applyMatrix4(this.objectGroup.matrixWorld), o.project(viewport.camera); var a = viewport.resolution.x, s = viewport.resolution.y; if (a <= 0 || s <= 0 || isNaN(i.x) || isNaN(o.x)) return !1; i.sub(o), i.x *= a / 2, i.y *= s / 2; var scale; if (this.name == 'map') { //add 高纬度的因倾斜而造成tile较小,所以放大些,否则会造成显示的tile过多而卡 var lonlat = viewer.transform.lonlatToLocal.inverse(viewport.camera.position.clone()); var cos = Math.cos(MathUtils.degToRad(lonlat.y)); //越小就在纬度上越高,tile表现越小 //为什么lonlat.y会超出90? /* if(lonlat.y>90){ console.log('lonlat.y>90',lonlat.y) } */ cos = MathUtils.clamp(cos, 0, 1); var lonShift = Math.abs(viewer.mapViewer.camera.position.x / this.mapSizeM * 16); //越大就在经度离中心越远,tile表现越大 。 lonShift = MathUtils.clamp(lonShift, 0, Math.PI); lonShift = (1 - Math.sin(1 / 2 * lonShift + Math.PI / 2)) * Math.PI; // 0-Math.PI sin增速向上 scale = 0.5 * cos * (1 + lonShift) + 0.5 * Math.pow(cos, lonShift); } else { scale = 1; } var c = this.tileSizePx / i.length() / scale //多除以一个scale缩放因子,scale越大level越小 , level = Math.ceil(-Math.log(c) / Math.log(2) - this.bias); if (this.style == 'dark-standard') { //该模式贴图比较小放大点 level -= 1; } level = Math.max(level, 0); level = Math.min(level, void 0 === this.maxDepth ? 1 / 0 : this.maxDepth); this.zoomLevel = level; //add /* if(isNaN(this.zoomLevel )){ console.log(level, cos , scale , lonlat ) } */ if (!unavailable) { this.addToSceneGroup(n); return this.baseTile.update(this, e, level, this.mapSizeM, 0, 0, ""); } } isTileVisible(e, n, i) { if (n > HALF_WORLD_SIZE) return !0; var r = .5 * n; //简单版: this.transformMapToLocal.forward(e); //e转化为local this.isTileVisibleBox.makeEmpty(); this.isTileVisibleVec.set(e.x - r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld); this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec); this.isTileVisibleVec.set(e.x - r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld); this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec); this.isTileVisibleVec.set(e.x + r, e.y - r, e.z).applyMatrix4(this.objectGroup.matrixWorld); this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec); this.isTileVisibleVec.set(e.x + r, e.y + r, e.z).applyMatrix4(this.objectGroup.matrixWorld); this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec); //仿造createMesh写的准确版,但会因为大的tile非矩形,而视口是矩形,若视口刚好在tile的曲线边缘外却识别为可见,就会创建冗余tile。 但上面那个简单版在zoomlevel低的时候地球边缘容易有识别不到的tile,造成黑色三角形。 //容易出现奇怪的mesh /* this.isTileVisibleBox.makeEmpty() this.isTileVisibleVec.set(e.x - r, e.y - r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x - r, e.y + r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x + r, e.y - r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) this.isTileVisibleVec.set(e.x + r, e.y + r, e.z) this.transformMapToLocal.forward(this.isTileVisibleVec) this.isTileVisibleVec.applyMatrix4(this.objectGroup.matrixWorld) this.isTileVisibleBox.expandByPoint(this.isTileVisibleVec) */ return i.intersectsBox(this.isTileVisibleBox); } addToSceneGroup(t) { this.objectGroupAdded || (t.add(this.objectGroup), this.objectGroupAdded = !0); } removeFromSceneGroup(t) { this.baseTile.remove(), this.objectGroupAdded && (t.remove(this.objectGroup), this.objectGroupAdded = !1); } } var loadDone = (tile, success) => { tile.map.mapLayer.loadingInProgress--; tile.map.loadingInProgress--; //console.log('loaddone', tile.name, 'loadingInProgress',tile.map.mapLayer.loadingInProgress, Date.now()) tile.loading = false; var next = tile.map.mapLayer.waitQueue[0]; if (next) { addLoadTile(next); } else { if (tile.map.mapLayer.loadingInProgress == 0) { //注意这时候不一定就加载完了,300ms后可能还会有新的tile加载 //console.log('loadDone All ?', Date.now()) tile.map.mapLayer.dispatchEvent('loadDone'); } } tile.mesh && (tile.mesh.material.needsUpdate = true); }; function addLoadTile(tile) { /* if(tile.texURL && tile.texURL.includes('testdata') ){ console.error('addLoadTile', tile.texURL.split('map_tiles/')[1] ) } */ if (tile.map.loadingInProgress < tile.map.maxLoading) { if (!tile.mesh) return; //有时候会遇到这种情况, 为什么没有被cancelLoad呢? tile.map.mapLayer.loadingInProgress++; tile.map.loadingInProgress++; tile.map.mapLayer.dispatchEvent('startLoad'); //console.log('addLoad', 'loadingInProgress',tile.map.mapLayer.loadingInProgress, Date.now()) //tile.texURL && tile.texURL.includes('testdata') && console.log('startloadTile ', tile.texURL.split('map_tiles/')[1] ) tile.loading = true; var index = tile.map.mapLayer.waitQueue.indexOf(tile); index > -1 && tile.map.mapLayer.waitQueue.splice(index, 1); var tex = tile.mesh.material.map = texLoader$2.load(tile.texURL, tex => { //如果一直加载不了会影响其他的加载,如google地图没有vpn会使全景图一直加载不了 if (tile.mesh) { //如果还要显示的话 tile.textureLoaded = true; tile.mesh.material.opacity = 1; tile.map.mapLayer.viewer.mapChanged = true; tile.map.mapLayer.needUpdate = true; //表示还要继续update(以removeChildren) if (tile.map instanceof TiledMapOpenStreetMap) { tile.map.maxLoading = browser.isMobile() ? 5 : 10; } } else { //tile.texURL && tile.texURL.includes('testdata') && console.log('loadDone and dispose', tile.texURL.split('map_tiles/')[1] ) tex.dispose(); } loadDone(tile, true); }, void 0, () => { //error tile.textureLoaded = !0; if (tile.mesh) { tile.mesh.material.dispose(); tile.mesh.material = errorMaterial; tile.map.mapLayer.viewer.mapChanged = true; } loadDone(tile, false); tile.map.loadFailCount++; if (tile.map instanceof TiledMapOpenStreetMap && Potree.settings.mapCompany == 'google' && tile.map.loadFailCount > 3) { //极有可能没有vpn为了防止影响到其他资源加载,减少加载的个数 tile.map.maxLoading = 2; } }); tex.anisotropy = 0; tex.generateMipmaps = !1; tex.minFilter = LinearFilter; tex.magFilter = LinearFilter; } else { tile.map.mapLayer.waitQueue.includes(tile) || tile.map.mapLayer.waitQueue.push(tile); } } function cancelLoad(tile, log) { //如果等待加载,但还没开始加载,取消加载 if (!tile.loading) { var index = tile.map.mapLayer.waitQueue.indexOf(tile); index > -1 && tile.map.mapLayer.waitQueue.splice(index, 1); //index > -1 && tile.texURL && tile.texURL.includes('testdata') && console.log('cancelLoad', tile.texURL.split('map_tiles/')[1]/* , (log && waitQueue.indexOf(tile)>-1) ? log:'' , tile.loading */ ) } } class MapTile { constructor(map, e, n, parent, name) { this.map = map; this.name = name; this.parent = parent; this.objectGroup = e, this.tileColor = n, this.meshAdded = !1, this.textureLoaded = !1, this.children = []; this.id = getSid(); } update(e, n, i, r, o, a, s) { return !!this.doesNotContainTilesToBeDisplayed(e) || (0 === i ? this.updateTile(e, r, o, a) : this.updateSubTiles(e, n, i, r, o, a, s)); } doesNotContainTilesToBeDisplayed(t) { return t.tilePresenceMap && t.tilePresenceMap.empty; } updateTile(t, e, n, i) { //真正显示mesh的是这一层,最高level //if(this.map.name.includes('floorplan'))console.log('updateTile',this.name) if (!this.mesh) { this.createTileObject(t, e, n, i); } if (!this.meshAdded) { this.objectGroup.add(this.mesh); this.meshAdded = !0; } if (this.textureLoaded) { //贴图加载完就不需要子集了 this.removeChildren(); } else { this.cancelChildren(); //add 停止加载子集 } return this.textureLoaded; } updateSubTiles(entity, n, level, o, a, s, c) { //if(entity.name.includes('floorplan'))console.log('updateSubTiles',this.name) //名字越长代表level越高 for (var childrenLoaded = !0, u = [-.25 * o, .25 * o, -.25 * o, .25 * o], d = [.25 * o, .25 * o, -.25 * o, -.25 * o], p = 0; p < 4; ++p) { var h = c + p.toString(10); //一级(512):0 1 2 3分别为左上、右上、左下、右下。二级(1024)就是把一级的每一块分裂,如00 01 02 03分别是0的左上、右上、左下、右下…… if (!entity.tilePresenceMap || entity.tilePresenceMap[h]) { //去掉判断,直接显示 var f = a + u[p], m = s + d[p]; tempVector.set(f, m, 0); this.map.computeCount++; //console.log(this.map.computeCount, this.name, 'level:',level) if (entity.isTileVisible(tempVector, .5 * o, n)) { this.children[p] || (this.children[p] = new MapTile(this.map, this.objectGroup, this.tileColor, this, this.name + p)); //childrenLoaded = childrenLoaded && this.children[p].update(entity, n, level - 1, .5 * o, f, m, h) //这句会使若有一个tile还在加载,就阻断了。原版是这么写的。但是为了加快加载速度,改成下面两行。感觉直接全部updateTile也没太卡,不知道很大的场景会不会卡,单帧updateTile次数超过100次的话(应该不会吧,地图大小会限制住个数) -- 2023.12 var childLoaded = this.children[p].update(entity, n, level - 1, .5 * o, f, m, h); childrenLoaded = childrenLoaded && childLoaded; } else { if (this.children[p]) { this.children[p].remove(); delete this.children[p]; } } } } return childrenLoaded && this.removeObject3D(), childrenLoaded; //子项加载完,母项mesh可以去除。(最后母项的母项以及前面的都会被删除,只留最后的叶子结点) } /* 一层层往后加载。加入第一次加载到第4层(因为level精细度是第4层),给第4层可见tile加上mesh。 然后下一次加载到第5层,那么第4层的mesh就要被清空(当它所属的第5层子集都加载完后) */ createTileObject(t, e, n, a) { var s = this; this.mesh = this.createMesh(t.transformMapToLocal, e, n, a), this.textureLoaded = !1; var c = t.mapSizeM / e, l = Math.log(c) / Math.log(2), u = n / e + .5 * (c - 1), d = -a / e + .5 * (c - 1), p = t.getTileUrl(Math.round(l), Math.round(u), Math.round(d)); Potree.Utils.setObjectLayers(this.mesh, 'map'); this.mesh.renderOrder = -(1e6 - l - 100 * (t.zIndex || 0)); this.mesh.name = this.name; //add this.texURL = p; /* let area = math.getArea(this.mesh.geometry.vertices.slice(0,3)); if(area >0){ this.mesh.visible = false console.log('area>0',this.mesh.name) } */ addLoadTile(this); } createMesh(t, e, n, o) { var a = new Geometry(); return tempVector.set(n - e / 2, o - e / 2, 0), a.vertices.push(new Vector3().copy(t.forward(tempVector))), tempVector.set(n + e / 2, o - e / 2, 0), a.vertices.push(new Vector3().copy(t.forward(tempVector))), tempVector.set(n + e / 2, o + e / 2, 0), a.vertices.push(new Vector3().copy(t.forward(tempVector))), tempVector.set(n - e / 2, o + e / 2, 0), a.vertices.push(new Vector3().copy(t.forward(tempVector))), a.faces.push(face1), a.faces.push(face2), a.faceVertexUvs[0].push(face1UV), a.faceVertexUvs[0].push(face2UV), new Mesh(a, this.createMaterial()); } createMaterial() { var t = new MeshBasicMaterial({ transparent: !0, depthWrite: !1, depthTest: !0, opacity: 0, side: DoubleSide }); /* var t = new BasicMaterial({ transparent: !0, depthWrite: !1, depthTest: !0, opacity: 0, side: THREE.DoubleSide }) t.defines.InverseColor = '' */ if (Potree.settings.isTest) { var colorHue = Math.random(); t.color = new Color().setHSL(colorHue, 0.6, 0.92); } else { t.color = this.tileColor ? this.tileColor : new Color(16777215); } return t; } traverse(f) { //add return Mesh.prototype.traverse.call(this, f); } remove() { this.removeObject3D(), this.removeChildren(); } removeObject3D() { var hasMesh = !!this.mesh; if (this.mesh) { this.objectGroup.remove(this.mesh); if (this.textureLoaded) { var t = this.mesh.material.map; t && t.dispose(); } else { cancelLoad(this); } this.mesh.material.dispose(); //o.disposeMeshMaterial(this.mesh), this.mesh.geometry.dispose(); this.mesh = void 0; } this.meshAdded = !1, this.textureLoaded = !1; //this.texURL && this.texURL.includes('testdata') && console.log('removeObject3D', this.id, 'hasMesh',hasMesh, this.texURL.split('map_tiles/')[1] ) } removeChildren() { for (var t = 0, e = this.children; t < e.length; t++) { var n = e[t]; n && (n.removeObject3D(), n.removeChildren()); } this.children.length = 0; } cancelChildren() { //子集全部停止加载 for (var t = 0, e = this.children; t < e.length; t++) { var n = e[t]; n && (cancelLoad(n, '提前'), n.cancelChildren()); } } } proj4.defs("EPSG:3857", "+title=WGS 84 / Pseudo-Mercator +proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"); //这里地图世界的中心是不是lon:0,lat:0 class TiledMapOpenStreetMap extends TiledMapBase { constructor(mapLayer, tileColor) { //Potree.settings.mapCompany = 'google' super('map', mapLayer, tileColor /* , projection */); //EPSG projection //this.baseUrl = "https://wprd03.is.autonavi.com/appmaptile?style=7&x=${x}&y=${y}&z=${z}", //this.baseUrl = "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x=${x}&y=${y}&z=${z}" //最高只到18 level this.switchStyle(); this.tileSizePx = 256; this.mapSizeM = 40075017; //总占据多少米(地球赤道周长) 和三维空间的不一样 - -, 空间上的是直径,地图上的是半个圆周 this.bias = 0.5; if (Potree.settings.mapCompany == 'google') { this.attribution = "© PopSmart, © 谷歌地图"; this.projection = "EPSG:900913"; //"EPSG:4326"//4550 } else { this.attribution = "© PopSmart, © 高德地图"; this.projection = "EPSG:3857"; } } getTileUrl(t, e, n) { return this.baseUrl.replace(/\${z}/, t.toString(10)).replace(/\${x}/, e.toString(10)).replace(/\${y}/, n.toString(10)); } switchStyle() { var style = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'standard'; //if(Potree.settings.mapCompany == 'google')return if (style == this.style) return; if (Potree.settings.mapCompany == 'google') { var [lang, glPos] = { 'zh': ["zh-CN", "cn"], 'en': ["en-US"], //范围大所以不指定地理位置 。 'ja': ["ja-JP", "JP"], 'kr': ["ko-KR", "KR"] }[Potree.settings.language] || []; this.baseUrl = "https://mt2.google.com/vt/lyrs=" + (style == 'satellite' ? "y" : "m") + "@159000000" + (lang ? "&hl=" + lang : '') + (glPos ? "&gl=" + glPos : '') + "&x=${x}&y=${y}&z=${z}&s=mt1"; this.maxDepth = 22; /* 1)lyrs= 表示的是图层类型,即瓦片类型,具体含义如下: m:路线图 t:地形图 p:带标签的地形图 s:卫星图 y:带标签的卫星图 h:标签层(路名、地名等) 2)& gl=CN 指定地理区域 谷歌地图针对中国有两套坐标,一套做了偏移,一套没有。经测试在url加入gl=cn地图会有偏移。 Tips:如果谷歌地图和RTK测量的WGS84坐标有偏差,可以尝试在url里去掉& gl=cn。(摘自网上) 通义千问:如果不指定gl参数,Google将使用默认值或基于其他因素(如IP地址)来推断用户的地理区域。这通常不会导致地图数据的不准确性,但可能会影响本地化信息的显示。地图上的标签(如街道名称、城市名称等)可能不会以最合适的语言显示。地址格式和拼写可能不符合当地标准 5)&hl= 设置地图注记文字语言类型,缺省默认为中文。 hl=nl 中英双语 hl=zh-CN 中文 */ } else { //baseUrl = "https://webst01.is.autonavi.com/appmaptile?lang=zh_cn&style=6&yrs=m&x=${x}&y=${y}&z=${z}" //卫星 maxDepth = 18 //搜索高德地图瓦片url if (Potree.settings.isJiangMen) { if (style == 'satellite') { //卫星 this.maxDepth = 18; this.baseUrl = "http://a.map.jms.gd/tile/weixing/${z}/${x}/${y}.png"; } else { this.maxDepth = 18; this.baseUrl = "http://a.map.jms.gd/tile/gd_xiangtu/${z}/${x}/${y}.png"; } } else { if (style == 'satellite') { //卫星 this.maxDepth = 18; this.baseUrl = "https://webst01.is.autonavi.com/appmaptile?lang=zh_cn&style=6&yrs=m&x=${x}&y=${y}&z=${z}"; } else if (style == 'dark-standard') { this.maxDepth = 18; this.baseUrl = "https://wprd01.is.autonavi.com/appmaptile?lang=zh_cn&style=8&yrs=m&x=${x}&y=${y}&z=${z}"; //路网 加上scl=2后去掉名字 } else { this.maxDepth = 19; this.baseUrl = "https://wprd04.is.autonavi.com/appmaptile?lang=zh_cn&style=7&yrs=m&x=${x}&y=${y}&z=${z}"; // } } //详解 https://www.cnblogs.com/lucio110/p/17310054.html } this.style = style; this.setEnable(false); this.setEnable(true); viewer.dispatchEvent('content_changed'); } } class TiledMapFromEntity extends TiledMapBase { constructor(mapLayer, tileColor, data) { super('floorplan', mapLayer, tileColor, "LOCAL" /* "EPSG:3857" */ /* "WGS84" */); //直接就是本地坐标,没有projec var entity = this.tiledMapEntity = this.fillFromData(data); var time = entity.updateTime || entity.createTime; this.tileSizePx = entity.tileSizePx, this.mapSizeM = entity.mapSizeM, this.maxDepth = entity.maxDepth; this.postStamp = time ? time.replace(/[^0-9]/ig, '') : new Date().getTime(); //this.projection = n.crsLocal, this.zIndex = 0, this.tilePresenceMap = this.decodeBitStream(this.tiledMapEntity.quadtree); //包含tile分裂信息,如果写错了会造成tile显示不全 this.maxLoading = 5; this.bias = 0.5; //越大加载层级越低,越模糊 if (window.devicePixelRatio >= 2 && window.innerHeight * window.innerWidth < 768 * 1024) { //手机还是加载高清点(反正也不需要截图等待),但平板太大了,要铺满屏幕可能慢,所以稍微模糊点?(反正可以继续放大去看) this.bias = 0; //level更高些 } } fillFromData(e) { var data = {}; data.id = e.id; data.globalLocation = Potree.Utils.VectorFactory.fromArray3(e.location); data.orientation = Potree.Utils.QuaternionFactory.fromArray(e.orientation); //数据集支持其他方向旋转后不用这个了 var pointcloud = viewer.scene.pointclouds.find(p => p.dataset_id == e.datasetId); if (pointcloud.datasetData.mapping) { data.filePath = "".concat(Potree.settings.urls.prefix1, "/").concat(pointcloud.datasetData.mapping).concat(e.file_path); } else { data.filePath = "".concat(Potree.settings.urls.prefix1).concat(e.file_path); } this.pointcloud = pointcloud; data.fileName = '$DEPTH/$X/$Y.png'; data.type = e.type, data.mapSizeM = e.map_size_m, data.tileSizePx = e.tile_size_px, data.maxDepth = e.max_depth, data.quadtree = e.quadtree, data.floorId = e.floor_id, data.bundleId = e.bundle_id; //this.computeLocalCoordinates() return data; } computeLocalCoordinates() { if (proj4.defs("LOCAL_MAP")) { var lonlat = this.tiledMapEntity.globalLocation; /* if(window.AMapWith84){//需要转换 lonlat = AMapWith84.wgs84ToAMap(lonlat) } */ lonlat = viewer.transform.lonlatToLocal.forward(lonlat); this.tiledMapEntity.location = new Vector3().copy(lonlat); } } updateProjection() { super.updateProjection(); if (!this.position) { this.computeLocalCoordinates(); } /* this.projection = this.TransformService.crsLocal, t.prototype.updateProjection.call(this) */ } get position() { return this.tiledMapEntity.location; } get quaternion() { //return this.tiledMapEntity.orientation//数据集支持其他方向旋转后不用这个了 return typeof this.pointcloud.orientationUser == 'number' ? this.tiledMapEntity.orientation : this.pointcloud.orientationUser.clone(); } getTileUrl(t, e, n) { var i = (this.tiledMapEntity.filePath + "/" + this.tiledMapEntity.fileName).replace(/\$DEPTH/g, t.toString(10)).replace(/\$X/g, e.toString(10)).replace(/\$Y/g, n.toString(10)); return i += "?t=" + this.postStamp; //this.RestService.addAuthorizationQueryParameter(i) //???? } decodeBitStream(t) { if (!t) return { empty: !0 }; for (var e = {}, n = [e], i = 0; i < t.length; i++) { var r = n.shift(), o = parseInt(t.substr(i, 1), 16); if (1 & o) { var a = {}; r[0] = a, n.push(a); } 2 & o && (a = {}, r[1] = a, n.push(a)), 4 & o && (a = {}, r[2] = a, n.push(a)), 8 & o && (a = {}, r[3] = a, n.push(a)); } var s = { empty: !0 }; return this.computeHashes(s, e, ""), s; } computeHashes(t, e, n) { for (var i = 0; i < 4; i++) e[i] && (t[n + i.toString(10)] = !0, t.empty = !1, this.computeHashes(t, e[i], n + i.toString(10))); } } /* note: 目前缩小了能看出形态是一个地球。相机在高空朝下观测,地球平放着。 所以越靠近赤道和地球朝上的那面所在的中央经度(也就是local 0,0,0所对应的初始经度),tile越接近正方形。 所以在两极地区要怎么显示? 注册地理坐标时需要滚动地球吗?(修改初始经度、重定义NAVVIS:TMERC, 就需要更新所有三维世界中的物体位置) 切换中心点: var locationLonLat = viewer.transform.lonlatToLocal.inverse(viewer.mapViewer.camera.position.clone()) proj4.defs("LOCAL_MAP", "+proj=tmerc +ellps=WGS84 +lon_0=" + locationLonLat.x.toPrecision(15) + " +lat_0=" + locationLonLat.y.toPrecision(15)); viewer.mapViewer.mapLayer.maps[0].transformMapToLocal = null 地理注册部分地图上的1和2标记有两层意思。当地图全屏展示时,标记的是当前右侧经纬度的位置;当地图为小窗时,标记的是对应场景里三维位置。(所以感觉最好换一个ui?)且在2023.2.1之前才改好,之前都是后者。 为什么边缘总是有奇怪的mesh,是因为有顶点到背面了吗 https://lbs.amap.com/tools/picker 高德坐标拾取器(手机登录),但和这里展示的不一样, 要用AMapWith84.aMapToWgs84({x: ,y: })转成84。 (qq or 手机登录) 所以potree本地和有AMapWith84函数的laser地图展现不一样 https://www.google.com/maps/@77.7730021,-34.4952712,4z google取点 打印所有mapTile的名字,字符串最长的代表有显示的mesh。 viewer.mapViewer.mapLayer.maps[0].baseTile.traverse(function(e){console.log(e.name)}) 能查看有几个显示的mesh viewer.mapViewer.mapLayer.maps[0].objectGroup.children 图片地址中 tiles/4/3/9.png 第一个数字越高代表level越高,放得越大 . (tile.name.length也能反映出 viewer.mapViewer.mapLayer.maps[1].objectGroup.children.map(e=>e.name.length-1) 目前看的几个场景floorplan原图是1米=33.03个像素 图宽度= 512*2^(max_depth-1) , map_size_m 代表整个地图是多少米 由于floorplan宽度有限制,最大32768所以不一定是33.03px, scale = resolution.width/(bound.x_max-bound.x_min), 参数见算法那边的json 经纬度精度小数点后5位为米级别,6位为分米,7位是厘米 */ /** * @author mschuetz / http://mschuetz.at * * adapted from THREE.OrbitControls by * * @author qiao / https://github.com/qiao * @author mrdoob / http://mrdoob.com * @author alteredq / http://alteredqualia.com/ * @author WestLangley / http://github.com/WestLangley * @author erich666 / http://erichaines.com * * * */ var Buttons = Potree.defines.Buttons; class FirstPersonControls extends EventDispatcher { constructor(viewer, viewport) { var _this; super(); _this = this; this.viewer = viewer; this.renderer = viewer.renderer; this.scene = viewer.scene; this.rotationSpeed = 200; this.moveSpeed = 10; this.setCurrentViewport({ hoverViewport: viewport, force: true }); //this.currentViewport = viewport this.keys = { FORWARD: ['W'.charCodeAt(0), 38], BACKWARD: ['S'.charCodeAt(0), 40], LEFT: ['A'.charCodeAt(0), 37], RIGHT: ['D'.charCodeAt(0), 39], UP: ['Q'.charCodeAt(0)], DOWN: ['E'.charCodeAt(0)], //SHIFT : [16], ALT: [18], SPACE: [32], Rotate_LEFT: ['L'.charCodeAt(0)], Rotate_RIGHT: ['J'.charCodeAt(0)], Rotate_UP: ['K'.charCodeAt(0)], Rotate_DOWN: ['I'.charCodeAt(0)] }; this.fadeFactor = 20; this.yawDelta = 0; this.pitchDelta = 0; this.translationDelta = new Vector3(0, 0, 0); this.translationWorldDelta = new Vector3(0, 0, 0); this.tweens = []; this.dollyStart = new Vector2(); this.dollyEnd = new Vector2(); //this.enableChangePos = true this.viewer.addEventListener('camera_changed', e => { if (this.viewer.name == 'mapViewer' || e.changeInfo && e.changeInfo.positionChanged && !viewer.mainViewport.view.isFlying()) { this.setFPCMoveSpeed(e.viewport); } }); var drag = e => { if (!this.enabled) return; var viewport = e.dragViewport; if (!viewport) return; var camera = viewport.camera; var mode; if (e.isTouch) { if (e.touches.length == 1) { mode = !e.dragViewport || e.dragViewport.name == 'MainView' ? 'rotate' : 'pan'; } else if (e.touches.length == 2) { mode = Potree.settings.displayMode == 'showPanos' ? 'scale' : 'pan-scale'; } else { mode = !e.dragViewport || e.dragViewport.name == 'MainView' ? 'pan' : 'scale'; } } else { //mode = e.buttons === Buttons.LEFT && (!e.dragViewport || e.dragViewport.name == 'MainView') ? 'rotate' : 'pan' mode = e.buttons === Buttons.LEFT && camera.type != 'OrthographicCamera' ? 'rotate' : 'pan'; } //console.log('mode ', mode ) var moveSpeed = this.currentViewport.getMoveSpeed(); if (e.drag.startHandled === undefined) { ///??????? e.drag.startHandled = true; this.dispatchEvent({ type: 'start' }); } if (mode.includes('rotate')) { //旋转 //来自panoramaControl updateRotation if (!this.pointerDragStart) { return this.pointerDragStart = e.pointer.clone(); } var view = this.scene.view; if (this.rotateStartInfo.rotAroundPoint) { //定点旋转: 以当前intersect的点为target旋转,不改点在屏幕中的位置 var distance = camera.position.distanceTo(this.rotateStartInfo.rotCenter /* this.intersectStart.location */); //不按下ctrl的话 var posDir = new Vector3().subVectors(this.rotateStartInfo.rotCenter, view.position); if (posDir.dot(view.direction) < 0) distance *= -1; //在背面 //按照orbitControl的方式旋转: var rotationSpeed = 2; this.yawDelta -= e.drag.pointerDelta.x * rotationSpeed; this.pitchDelta += e.drag.pointerDelta.y * rotationSpeed; /* //旋转方向和偏移量尽量和在漫游点处旋转方式的一样 this.yawDelta += e.drag.pointerDelta.x / 500 * viewport.resolution.x this.pitchDelta -= e.drag.pointerDelta.y / 500 * viewport.resolution.y */ //先更新一下相机: this.update(); view.applyToCamera(camera); //然后得到新的相机角度下,原先点在屏幕中的位置所对应的3d点现在的坐标。只需要平移一下新旧坐标差值即可。//感觉貌似也可以用project和unproject后的差值的方式,还不用判断z背面 var newPointerDir = viewer.inputHandler.getMouseDirection(this.rotateStartInfo.rotCenter2d).direction.clone().multiplyScalar(distance); var pivot = new Vector3().addVectors(camera.position, newPointerDir); //新的3d点 var moveVec = new Vector3().subVectors(pivot, this.rotateStartInfo.rotCenter); this.translationWorldDelta.copy(moveVec.negate()); //立即更新下,防止因update和此drag频率不同而打滑。 this.update(); view.applyToCamera(camera); } else { var _matrixWorld = camera.matrixWorld; camera.matrixWorld = new Matrix4(); //unproject 前先把相机置于原点 var e1 = new Vector3(this.pointerDragStart.x, this.pointerDragStart.y, -1).unproject(camera), t = new Vector3(e.pointer.x, e.pointer.y, -1).unproject(camera), i = Math.sqrt(e1.x * e1.x + e1.z * e1.z), n = Math.sqrt(t.x * t.x + t.z * t.z), o = Math.atan2(e1.y, i), a = Math.atan2(t.y, n); this.pitchDelta += o - a; //上下旋转 e1.y = 0, t.y = 0; var s = Math.acos(e1.dot(t) / e1.length() / t.length()); if (!isNaN(s)) { var yawDelta = s; //左右旋转 this.pointerDragStart.x > e.pointer.x && (yawDelta *= -1); this.yawDelta += yawDelta; } //console.log('rotate:', this.pitchDelta, e.pointer.toArray(), this.pointerDragStart.toArray()) this.pointerDragStart.copy(e.pointer); camera.matrixWorld = _matrixWorld; } } if (mode.includes('pan')) { //平移 if (!this.canMovePos(viewport)) { return; } if (camera.type == "OrthographicCamera") { //console.log(e.drag.pointerDelta, e.pointer, e.drag.end) var _moveVec = Utils.getOrthoCameraMoveVec(e.drag.pointerDelta, camera); //最近一次移动向量 var pointclouds; var Alignment = window.viewer.modules.Alignment; var MergeEditor = window.viewer.modules.MergeEditor; var handleState = Alignment.handleState; //右键平移视图、左键操作点云 var _a = e.buttons === Buttons.LEFT && viewport.alignment && handleState && viewport.alignment[handleState]; if (Potree.settings.editType == 'pano') { var PanoEditor = window.viewer.modules.PanoEditor; if (_a && PanoEditor.selectedPano) { if ( /* !PanoEditor.selectedGroup || */!PanoEditor.checkIfAllLinked({ group: PanoEditor.selectedGroup })) { if (handleState == 'translate' && (e.drag.intersectStart.pointclouds && Common.getMixedSet(PanoEditor.selectedClouds, e.drag.intersectStart.pointclouds).length || PanoEditor.selectedPano.hovered) //平移时 拖拽到点云上 或 circle。(其中点云只需要intersect的点云中包含选择的点云中之一即可) || handleState == 'rotate') //旋转模式不需要intersect { pointclouds = PanoEditor.selectedClouds; } } else { PanoEditor.dispatchEvent('needToDisConnect'); console.warn('选中的漫游点连通了整个数据集,不允许移动'); } } if (!pointclouds && e.buttons === Buttons.LEFT && viewport.rotateSide) { //侧视图 (有时候会卡顿,是mousemove执行延迟了,一般发生在突然加载很多点云时) //console.log('rotateSide', -e.drag.pointerDelta.x ) return PanoEditor.rotateSideCamera(-e.drag.pointerDelta.x); } } else if (Potree.settings.editType == 'merge') { if (e.buttons === Buttons.LEFT && viewport.rotateSide) { return MergeEditor.rotateSideCamera(-e.drag.pointerDelta.x); } } else { /* if(Alignment.selectedClouds && Alignment.selectedClouds.length){//多个点云 pointclouds = a && e.drag.intersectStart.pointclouds && Common.getMixedSet(Alignment.selectedClouds, e.drag.intersectStart.pointclouds).length && Alignment.selectedClouds }else{ */ //pointclouds = a && e.drag.intersectStart.pointcloud && [e.drag.intersectStart.pointcloud] //} if (_a && e.drag.intersectStart.pointcloud) { pointclouds = [e.drag.intersectStart.pointcloud]; if (e.drag.intersectStart.pointcloud.dataset_id == Potree.settings.originDatasetId) { var p = e.drag.intersectStart.pointclouds.find(p => p.dataset_id != Potree.settings.originDatasetId); if (p) pointclouds = [p]; } } } if (pointclouds) { if (handleState == 'translate' && viewport.alignment.translateVec) { //只能沿某个方向移动 _moveVec.projectOnVector(viewport.alignment.translateVec); } this.dispatchEvent({ type: "transformPointcloud", intersect: e.intersect.orthoIntersect, intersectStart: e.drag.intersectStart.orthoIntersect, moveVec: _moveVec, pointclouds, camera }); } else { this.translationWorldDelta.add(_moveVec.negate()); } } else { //perspectiveCamera: if (e.drag.intersectStart) { //如果拖拽着点云 var ifInit = e.drag.z == void 0; var pointerStartPos2d = e.drag.intersectStart.location.clone().project(camera); //识别到的点云点的位置 e.drag.z = pointerStartPos2d.z; //记录z,保持拖拽物体到屏幕距离不变,所以z深度不变(如果拖拽过程中没有缩放,这个z其实不变) if (ifInit) { //拖拽开始 e.drag.projectionMatrixInverse = camera.projectionMatrixInverse.clone(); //防止吸附到最近点上(因为鼠标所在位置并非识别到的点云点的位置,需要得到鼠标所在位置的3d坐标。) var pointerStartPos2dReal = new Vector3(this.pointerDragStart.x, this.pointerDragStart.y, e.drag.z); e.drag.translateStartPos = pointerStartPos2dReal.clone().unproject(camera); //console.log('开始拖拽', e.pointer.clone()) } //拖拽的过程中将projectionMatrixInverse替换成开始拖拽时的,因为near、far一直在变,会导致unproject计算出的3d坐标改变很大而闪烁。 var _projectionMatrixInverse = camera.projectionMatrixInverse; camera.projectionMatrixInverse = e.drag.projectionMatrixInverse; var newPos2d = new Vector3(e.pointer.x, e.pointer.y, e.drag.z); var newPos3d = newPos2d.clone().unproject(camera); var _moveVec2 = newPos3d.clone().sub(e.drag.translateStartPos /* e.drag.intersectStart.location */); //移动相机,保持鼠标下的位置永远不变,所以用鼠标下的新位置减去鼠标下的原始位置 camera.projectionMatrixInverse = _projectionMatrixInverse; this.translationWorldDelta.copy(_moveVec2.negate()); //这里没法用add,原因未知,打开console时会跳动 //console.log('pan 1', this.translationWorldDelta.clone()) //四指松开剩三指时会偏移一下,暂不知道哪里的问题,或许跟开头防止点云吸附有关? } else { //如果鼠标没有找到和点云的交点,就假设移动整个模型(也可以去扩大范围寻找最近点云) /* let center = viewer.scene.pointclouds[0].position; let radius = camera.position.distanceTo(center); let ratio = radius * Math.tan(THREE.Math.degToRad(camera.fov)/2) / 1000 */ /* let speed = this.currentViewport.getMoveSpeed() if(FirstPersonControls.boundPlane){ speed = FirstPersonControls.boundPlane.distanceToPoint(this.currentViewport.position) speed = Math.max(1 , speed) } */ var lastIntersect = this.target || viewport.lastIntersect && (viewport.lastIntersect.location || viewport.lastIntersect); //该viewport的最近一次鼠标和点云的交点 if (!lastIntersect || !(lastIntersect instanceof Vector3)) lastIntersect = viewer.bound.center; var speed = camera.position.distanceTo(lastIntersect); var fov = cameraLight.getHFOVForCamera(camera, true); var ratio = speed * Math.tan(fov / 2); this.translationDelta.x -= e.drag.pointerDelta.x * ratio; this.translationDelta.z -= e.drag.pointerDelta.y * ratio; //console.log('pan2', e.drag.pointerDelta) } } this.useAttenuation = false; } if (mode.includes('scale')) { //触屏缩放 this.dollyEnd.subVectors(e.touches[0].pointer, e.touches[1].pointer); //if(!this.dollyStart)return var scale = this.dollyEnd.length() / this.dollyStart.length(); var pointer = new Vector2().addVectors(e.touches[0].pointer, e.touches[1].pointer).multiplyScalar(0.5); //两个指头的中心点 dolly({ pointer, scale, camera, drag: e.drag }); this.dollyStart.copy(this.dollyEnd); } //最好按ctrl可以变为dollhouse的那种旋转 }; var drop = e => { if (!this.enabled) return; this.dispatchEvent({ type: 'end' }); }; var dolly = function dolly() { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (Potree.settings.displayMode == 'showPanos' && _this.currentViewport == viewer.mainViewport /* this.currentViewport.unableChangePos */) { //全景时 _this.dispatchEvent({ type: 'dollyStopCauseUnable', delta: e.delta, scale: e.scale }); return; } var camera = e.camera; if (camera.type == "OrthographicCamera") { var ratio; if (e.delta != void 0) { //滚轮缩放 if (e.delta == 0) { //mac return; } else if (e.delta < 0) { ratio = 0.9; } else if (e.delta > 0) { ratio = 1.12; //增加的需要比减少的多一些,否则缩放相同次数下,r0 * ([1+s)(1-s)]^n = r0 * (1-s^2)^n < r0 } } else { ratio = e.scale; //触屏缩放 } var zoom = camera.zoom * ratio; var limit = camera.zoomLimit; if (limit) zoom = MathUtils.clamp(zoom, limit.min, limit.max); var pointerPos = new Vector3(e.pointer.x, e.pointer.y, 0.5); var oldPos = pointerPos.clone().unproject(camera); if (camera.zoom != zoom) { camera.zoom = zoom; camera.updateProjectionMatrix(); } var newPos = pointerPos.clone().unproject(camera); //定点缩放, 恢复一下鼠标所在位置的位置改变量 var moveVec = new Vector3().subVectors(newPos, oldPos); _this.translationWorldDelta.add(moveVec.negate()); _this.useAttenuation = false; } else { var speed = _this.currentViewport.getMoveSpeed() * 7, direction; if (e.delta != void 0) { //滚轮缩放 if (e.delta == 0) return; //mac /*if(this.target && !e.intersect){//如果没有intersect点云且有target的话,就朝target的方向. 但无限靠近时有问题,且到背面时前进却是后退 direction = new THREE.Vector3().subVectors(this.target, camera.position).normalize() }else{ */ direction = _this.viewer.inputHandler.getMouseDirection().direction; //定点缩放 if (e.intersect && e.intersect.location) { //和intersect的墙越接近,速度越慢,便于focus细节 var dis = camera.position.distanceTo(e.intersect.location); speed = MathUtils.clamp(dis * 0.1, 0.3, speed); } if (e.delta < 0) { speed *= -1; } var slightly = _this.keys.ALT.some(e => _this.viewer.inputHandler.pressedKeys[e]); slightly && (speed *= 0.2); _this.useAttenuation = true; } else { //触屏缩放 direction = _this.viewer.inputHandler.getMouseDirection(e.pointer).direction; //定点缩放 if (e.drag.intersectStart) { //和intersect的墙越接近,速度越慢,便于focus细节 var _dis = camera.position.distanceTo(e.drag.intersectStart.location); var r = 1 - 1 / e.scale; var closeMin = 0.1, standardMin = 0.001, disBound1 = 2, disBound2 = 5; if (math.closeTo(e.scale, 1, 0.03)) { //如果偏差小于0.01,就不限制最小值,因为平移容易正负抖动,近距离有最小值的话抖动明显 closeMin = 0; //所以若缩放不明显(双指滑动慢),就不设置最低值。(这时候穿越障碍物会比较困难。) } //console.log('closeMin',closeMin) var min = math.linearClamp(_dis, [disBound1, disBound2], [closeMin, standardMin]); //触屏和滚轮不一样,触发较为连续,所以最小值设低一点。若要保持双指相对点云位置不变,理想最小值是0,但那样就无法穿越点云(最小值太小的话穿越密集点云如树丛很困难;太大会打滑)所以当离点云近时增大最小值 speed = Math.sign(r) * MathUtils.clamp(_dis * Math.abs(r), min, speed); //console.log(speed, dis, e.scale) } else { _this.useAttenuation = true; var accelerate = 40; if (math.closeTo(e.scale, 1, 0.02)) { //缩放小的时候很可能是双指平移时,容易抖动,所以降低移动速度 accelerate *= Math.min(40 * Math.abs(e.scale - 1), 0.8); } // console.log('accelerate',accelerate) var constantDis = _this.currentViewport.getMoveSpeed() * accelerate; //constantDis = 10;//常量系数,当放大一倍时前进的距离。可以调整 speed = (e.scale - 1) * constantDis; } } var vec = direction.multiplyScalar(speed); //this.translationWorldDelta.copy(vec) _this.translationWorldDelta.add(vec); //console.log(direction.toArray(), speed, e.scale) } return true; }; var scroll = e => { if (!this.enabled || !e.hoverViewport) return; this.setCurrentViewport(e); e.camera = e.hoverViewport.camera; dolly(e); }; var dblclick = e => { if (!this.enabled) return; if (!Potree.settings.dblToFocusPoint) return; //调试时才可双击 if (Potree.settings.displayMode == 'showPointCloud' /* !viewer.images360.isAtPano() */) this.zoomToLocation(e.mouse); }; this.viewer.addEventListener('global_drag', drag); /* this.viewer.addEventListener('global_touchmove', (e)=>{ if(!this.enabled)return if(e.touches.length>1){//单指的就触发上一句 //console.log('global_touchmove' ) drag(e) } }); */ this.viewer.addEventListener('global_drop', drop); this.viewer.addEventListener('global_mousewheel', scroll); this.viewer.addEventListener('global_dblclick', dblclick); var prepareScale = e => { //触屏的scale this.dollyStart.subVectors(e.touches[0].pointer, e.touches[1].pointer); e.drag.camDisToPointStart = null; }; var prepareRotate = e => { this.pointerDragStart = e.pointer.clone(); if (e.viewer.name != 'mainViewer') return; var intersect = e.intersect || e.dragViewport.lastIntersect; //在数据集外部时绕中心点旋转,在数据集内部时绕intersect点旋转(其他数据集的点也可以) 或者 原地旋转镜头 var rotAroundPoint = Potree.settings.rotAroundPoint && e.dragViewport.camera.type != 'OrthographicCamera' /* && (viewer.atDatasets.length == 0 || intersect) */ && this.canMovePos(viewport) && !viewer.images360.isAtPano() && !this.viewer.inputHandler.pressedKeys[32]; var rotCenter2d, rotCenter; if (rotAroundPoint) { var pivotType = this.target ? 'target' : viewer.atDatasets.length > 0 || Potree.settings.editType == 'pano' ? 'intersect' : viewer.inputHandler.selection.length ? 'selection' : this.target2 ? 'target2' : 'boundCenter'; rotCenter = pivotType == 'target' ? this.target : pivotType == 'intersect' ? intersect === null || intersect === void 0 ? void 0 : intersect.location : pivotType == 'selection' ? viewer.inputHandler.selection[0].position : pivotType == 'target2' ? this.target2 : viewer.bound && viewer.bound.center; if (rotCenter) { rotCenter2d = rotCenter.clone().project(e.dragViewport.camera); //点在屏幕中的位置。 若z>1 则在背面 或 超出far范围 } else { rotAroundPoint = false; } } this.rotateStartInfo = { rotAroundPoint, //定点旋转 rotCenter, rotCenter2d }; //缺点:多数据集绕中心点转很难操作,感觉可以也改为绕lastIntersect //console.log('prepareRotate' ) }; var preparePan = e => { //触屏的pan点云 还是会偏移 this.pointerDragStart = e.pointer.clone(); e.drag.z = void 0; //清空 drag(e); //触屏点击时更新的pointer直接用一次drag //console.log('preparePan ' ) }; this.viewer.addEventListener('global_mousedown' /* 'startDragging' */, e => { if (!this.enabled) return; this.setCurrentViewport(e); prepareRotate(e); }); //注意,每次增减指头都会修改pointer,需要更新下状态 this.viewer.addEventListener('global_touchstart', e => { if (!this.enabled) return; if (e.touches.length == 2) { //只监听开头两个指头 prepareScale(e); preparePan(e); } else if (e.touches.length >= 3) { preparePan(e); } }); this.viewer.addEventListener('global_touchend', e => { //e.touches是剩余的指头 if (!this.enabled) return; if (e.touches.length == 2) { //停止平移,开始scale prepareScale(e); preparePan(e); } else if (e.touches.length == 1) { //停止scale,开始rotate prepareRotate(e); } else if (e.touches.length >= 3) { //重新准备下平移(因为抬起的指头可能包含平移使用的数据),否则抬起时漂移 preparePan(e); } }); /* this.viewer.addEventListener('enableChangePos', (e)=>{ if(!this.enabled)return this.enableChangePos = e.canLeavePano }) */ } canMovePos(viewport) { if (viewport == viewer.mainViewport && (Potree.settings.displayMode == 'showPanos' || viewer.images360.bumping || viewer.images360.latestToPano)) return false;else return true; } setEnable(enabled) { this.enabled = enabled; if (!enabled) { this.yawDelta = this.pitchDelta = 0; this.translationDelta.set(0, 0, 0); this.translationWorldDelta.set(0, 0, 0); } } setTarget(target, index) { //绕该点旋转,类似orbitControl if (index == 2) this.target2 = target;else this.target = target; } setFPCMoveSpeed(viewport) { if (viewport.camera.type == 'OrthographicCamera') { var s = 1 / viewport.camera.zoom; viewport.setMoveSpeed(s); } else { //根据和漫游点的最短距离算moveSpeed。缺点:对于导入的无漫游点的数据集没有意义。 if (viewport == viewer.mainViewport && viewer.images360) { var position = viewer.mainViewport.view.position; var speed; var pano = viewer.images360.findNearestPano(); if (!pano) { if (!viewer.bound || viewer.bound.boundSize.x == 0) return; var boundFloor = viewer.bound.boundingBox.clone(); boundFloor.max.z = boundFloor.min.z; speed = boundFloor.distanceToPoint(viewer.mainViewport.view.position); speed = Math.sqrt(speed) / 50; } else { var dis = pano.position.distanceTo(position); var minSpeed = 0.05, minDis = 3, multiplier = 0.005; speed = dis <= minDis ? minSpeed : minSpeed + (dis - minDis) * multiplier; //console.log('dis', dis, 'speed', speed, pano.id ) } viewer.setMoveSpeed(speed * 2); } //调试场景t-FhDWmV5xur 两个数据集,大的数据集没有漫游点。 } } setCurrentViewport() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //add if (!this.enabled && !o.force) return; if (o.hoverViewport && this.currentViewport != o.hoverViewport) { this.currentViewport = o.hoverViewport; //this.viewer.setMoveSpeed(this.currentViewport.radius/100); this.setFPCMoveSpeed(this.currentViewport); } if (this.currentViewport.camera.type == 'OrthographicCamera') { this.lockElevationOri = true; this.lockRotation = true; } else { this.lockElevationOri = false; this.lockRotation = false; } } setScene(scene) { this.scene = scene; } stop() { this.yawDelta = 0; this.pitchDelta = 0; this.translationDelta.set(0, 0, 0); } zoomToLocation(mouse) { if (!this.enabled) return; var camera = this.scene.getActiveCamera(); /* let I = Utils.getMousePointCloudIntersection( mouse, camera, this.viewer, this.scene.pointclouds); */ var I = this.viewer.inputHandler.intersect; if (!I) { return; } var targetRadius = 0; { var minimumJumpDistance = 0.2; var domElement = this.renderer.domElement; var ray = Utils.mouseToRay(this.viewer.inputHandler.pointer, camera); var { origin, direction } = this.viewer.inputHandler.getMouseDirection(); var raycaster = new Raycaster(); raycaster.ray.set(origin, direction); var nodes = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, ray); var nodes2 = I.pointcloud.nodesOnRay(I.pointcloud.visibleNodes, raycaster.ray); var lastNode = nodes[nodes.length - 1]; var radius = lastNode.getBoundingSphere(new Sphere()).radius; targetRadius = Math.min(this.scene.view.radius, radius); targetRadius = Math.max(minimumJumpDistance, targetRadius); } var d = this.scene.view.direction.multiplyScalar(-1); var cameraTargetPosition = new Vector3().addVectors(I.location, d.multiplyScalar(targetRadius)); // TODO Unused: let controlsTargetPosition = I.location; var animationDuration = 600; var easing = TWEEN.Easing.Quartic.Out; { // animate var value = { x: 0 }; var tween = new TWEEN.Tween(value).to({ x: 1 }, animationDuration); tween.easing(easing); this.tweens.push(tween); var startPos = this.scene.view.position.clone(); var targetPos = cameraTargetPosition.clone(); var startRadius = this.scene.view.radius; var _targetRadius = cameraTargetPosition.distanceTo(I.location); tween.onUpdate(() => { var t = value.x; this.scene.view.position.x = (1 - t) * startPos.x + t * targetPos.x; this.scene.view.position.y = (1 - t) * startPos.y + t * targetPos.y; this.scene.view.position.z = (1 - t) * startPos.z + t * targetPos.z; this.scene.view.radius = (1 - t) * startRadius + t * _targetRadius; this.viewer.setMoveSpeed(this.scene.view.radius / 2.5); }); tween.onComplete(() => { this.tweens = this.tweens.filter(e => e !== tween); }); tween.start(); } } update() { var delta = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; if (!this.enabled) return; //console.log('update') var view = this.currentViewport.view; { // cancel move animations on user input var changes = [this.yawDelta, this.pitchDelta, this.translationDelta.length(), this.translationWorldDelta.length()]; var changeHappens = changes.some(e => Math.abs(e) > 0.001); if (changeHappens && this.tweens.length > 0) { this.tweens.forEach(e => e.stop()); this.tweens = []; } } { // accelerate while input is given var ih = this.viewer.inputHandler; var moveForward = this.keys.FORWARD.some(e => ih.pressedKeys[e]); var moveBackward = this.keys.BACKWARD.some(e => ih.pressedKeys[e]); var moveLeft = this.keys.LEFT.some(e => ih.pressedKeys[e]); var moveRight = this.keys.RIGHT.some(e => ih.pressedKeys[e]); var moveUp = this.keys.UP.some(e => ih.pressedKeys[e]); var moveDown = this.keys.DOWN.some(e => ih.pressedKeys[e]); var rotateLeft = this.keys.Rotate_LEFT.some(e => ih.pressedKeys[e]); var rotateRight = this.keys.Rotate_RIGHT.some(e => ih.pressedKeys[e]); var rotateUp = this.keys.Rotate_UP.some(e => ih.pressedKeys[e]); var rotateDown = this.keys.Rotate_DOWN.some(e => ih.pressedKeys[e]); this.lockElevation = this.lockElevationOri || this.keys.SPACE.some(e => ih.pressedKeys[e]); var slightly = this.keys.ALT.some(e => ih.pressedKeys[e]); if (!this.lockRotation) { if (rotateLeft) { this.yawDelta -= 0.01; } else if (rotateRight) { this.yawDelta += 0.01; } if (rotateUp) { this.pitchDelta -= 0.01; } else if (rotateDown) { this.pitchDelta += 0.01; } } if (this.canMovePos(this.currentViewport) && !this.lockKey) { if (this.lockElevation) { var dir = view.direction; dir.z = 0; dir.normalize(); if (moveForward && moveBackward) { this.translationWorldDelta.set(0, 0, 0); } else if (moveForward) { this.translationWorldDelta.copy(dir.multiplyScalar(this.currentViewport.getMoveSpeed())); } else if (moveBackward) { this.translationWorldDelta.copy(dir.multiplyScalar(-this.currentViewport.getMoveSpeed())); } } else { if (moveForward && moveBackward) { this.translationDelta.y = 0; } else if (moveForward) { this.translationDelta.y = this.currentViewport.getMoveSpeed() * (slightly ? 0.2 : 1); } else if (moveBackward) { this.translationDelta.y = -this.currentViewport.getMoveSpeed() * (slightly ? 0.2 : 1); } } if (moveLeft && moveRight) { this.translationDelta.x = 0; } else if (moveLeft) { this.translationDelta.x = -this.currentViewport.getMoveSpeed(); } else if (moveRight) { this.translationDelta.x = this.currentViewport.getMoveSpeed(); } if (moveUp && moveDown) { this.translationWorldDelta.z = 0; } else if (moveUp) { this.translationWorldDelta.z = this.currentViewport.getMoveSpeed(); } else if (moveDown) { this.translationWorldDelta.z = -this.currentViewport.getMoveSpeed(); } if (moveUp || moveDown || moveForward || moveBackward) { this.useAttenuation = false; } } } { // apply rotation var yaw = view.yaw; var pitch = view.pitch; yaw += this.yawDelta; /* * delta; */ pitch += this.pitchDelta; /* * delta; */ view.yaw = yaw; view.pitch = pitch; if (this.yawDelta || this.pitchDelta) { view.cancelFlying('rotate'); } this.yawDelta = 0; this.pitchDelta = 0; } /* if(this.translationWorldDelta.length()>0) { // console.log('translationDelta') } */ { // apply translation view.translate(this.translationDelta.x, /* * delta, */ this.translationDelta.y, /* * delta, */ this.translationDelta.z /* * delta */); this.translationDelta.set(0, 0, 0); //if(this.translationWorldDelta.length())console.log(translationWorldDelta) view.translateWorld(this.translationWorldDelta.x /* * delta */, this.translationWorldDelta.y /* * delta */, this.translationWorldDelta.z /* * delta */); //this.translationWorldDelta.set(0,0,0) } {// set view target according to speed //view.radius = 1 * this.currentViewport.getMoveSpeed(); /* if(viewer.bound) view.radius = view.position.distanceTo(viewer.bound.center) let speed = view.radius/100; this.viewer.setMoveSpeed(speed); */ //this.setMoveSpeed() } if (this.useAttenuation) { //只有滚轮缩放时开启 var attenuation = Math.max(0, 1 - this.fadeFactor * delta); /*this.yawDelta *= attenuation; this.pitchDelta *= attenuation; this.translationDelta.multiplyScalar(attenuation);*/ this.translationWorldDelta.multiplyScalar(attenuation); } else { this.translationWorldDelta.set(0, 0, 0); } } } ; /** * @author mschuetz / http://mschuetz.at * * */ var { Buttons: Buttons$1 } = Potree.defines; class InputHandler extends EventDispatcher { constructor(viewer, scene) { super(); this.viewer = viewer; this.renderer = viewer.renderer; this.domElement = this.renderer.domElement; this.enabled = true; this.scene = scene; this.interactiveScenes = []; this.interactiveObjects = new Set(); this.inputListeners = []; this.blacklist = new Set(); this.drag = null; this.mouse = new Vector2(0, 0); //add: this.pointer = new Vector2(0, 0); //交互点的屏幕坐标,有别于DOM坐标,在此存放NDC坐标。(NDC,三维常用坐标系,二维坐标,整个屏幕映射范围(-1,1),屏幕中心为原点,+Y朝上,+X朝右) this.mouseDownMouse = new Vector2(0, 0); this.selection = []; this.hoveredElements = []; this.pressedKeys = {}; this.wheelDelta = 0; this.speed = 1; this.logMessages = false; if (this.domElement.tabIndex === -1) { this.domElement.tabIndex = 2222; } this.lastPointerUpTime = 0; this.touches = []; this.interactHistory = { move: 0 }; //add this.hoverViewport = viewer.viewports[0]; document.addEventListener('mouseup', function (event) { event.preventDefault(); }, { passive: false }); this.domElement.addEventListener('contextmenu', event => { event.preventDefault(); }, false); this.domElement.addEventListener('click', this.onMouseClick.bind(this), false); this.domElement.addEventListener('mousedown', this.onMouseDown.bind(this), { passive: false, useCapture: false }); window.addEventListener('mouseup', this.onMouseUp.bind(this), { passive: false, useCapture: false }); if (Potree.isIframeChild) { //子页面的话在父页面也要加侦听(应该不会有多层吧?否则要一直加到最外层) //window.parent.addEventListener('mouseup', this.onMouseUp.bind(this), false); //可能跨域 //window.parent.postMessage('listenMouseup', '*'); window.addEventListener('mouseout', this.onMouseUp.bind(this), false); //cancel drag } this.domElement.addEventListener('mouseout', () => { this.containsMouse = false; }, false); this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), { passive: false, useCapture: false }); //add /* this.domElement.addEventListener("pointerout", this.onMouseUp.bind(this)), this.domElement.addEventListener("pointercancel", this.onMouseUp.bind(this)), */ this.domElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), false); this.domElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), false); // Firefox //this.domElement.addEventListener('dblclick', this.onDoubleClick.bind(this)); //因为双击时间间隔是跟随系统的所以不好判断 window.addEventListener('keydown', this.onKeyDown.bind(this)); //原先是this.domElement,这样的话一开始要点击屏幕后才能监听到 window.addEventListener('keyup', this.onKeyUp.bind(this)); window.addEventListener('blur', this.onKeyUp.bind(this)); //add this.domElement.addEventListener('touchstart', this.onTouchStart.bind(this)); this.domElement.addEventListener('touchend', this.onTouchEnd.bind(this)); this.domElement.addEventListener('touchmove', this.onTouchMove.bind(this)); { this.measuring = []; //正在编辑的measure //let mesureInfo = new THREE.EventDispatcher() this.addEventListener('measuring', e => { //true优先级高于false, 正在添加时dropMarker也不会停止 //Potree.Utils.updateVisible(mesureInfo, e.situation, e.v, 0, e.v?'add':'cancel' )//借用该函数,使true优先级高于false,防止正在添加时dropMarker而停止 if (e.v) { this.measuring.includes(e.object) || this.measuring.push(e.object); } else { var index = this.measuring.indexOf(e.object); index > -1 && this.measuring.splice(index, 1); } if (this.measuring.length == 0 && this.measuring.length > 0) { this.viewer.viewports.forEach(viewport => { this.collectClosePoints(viewport, true); //forceUpdate }); } //console.log('measuring',e.v, e.cause, e.situation, this.measuring.length ) }); } window.viewer.addEventListener('loopStart', () => { this.interactHistory = {}; //清空 }); } /* addInputListener (listener) { this.inputListeners.push(listener); } removeInputListener (listener) { this.inputListeners = this.inputListeners.filter(e => e !== listener); } getSortedListeners(){ return this.inputListeners.sort( (a, b) => { let ia = (a.importance !== undefined) ? a.importance : 0; let ib = (b.importance !== undefined) ? b.importance : 0; return ib - ia; }); } */ //统一跟第一个触碰的viewport相同 updateTouchesInfo(e) { var viewport, pointer, camera; var oldTouches = this.touches; var changedTouches = Array.from(e.changedTouches); var touches = Array.from(e.touches); this.touches = touches.map(touch => { var touch_ = oldTouches.find(a => a.touch.identifier == touch.identifier); var pointer = touch_ && touch_.pointer; //复制原先的值 return { touch, pointer }; }); if (e.touches.length > 0) { var newTouches = touches.filter(e => !oldTouches.some(a => a.touch.identifier == e.identifier) && !changedTouches.some(a => a.identifier == e.identifier)); //从按钮处划过时e.touches中会出现this.touches和changedTouches中都没有的identifier if (newTouches.length > 0) { console.warn('has new', newTouches.map(e => e.identifier)); } newTouches.concat(changedTouches).forEach(touch => { //修改changedTouches的 var touch_ = this.touches.find(a => a.touch.identifier == touch.identifier); if (touch_) { var a = this.getPointerInViewport(touch.pageX, touch.pageY, this.dragViewport || viewport, new Vector2()); touch_.pointer = a.pointer.clone(); viewport = a.viewport; camera = a.camera; } }); //使用当前touches的平均 if (e.touches.length > 1) { var pageX = Common.average(e.touches, "pageX"); var pageY = Common.average(e.touches, "pageY"); var a = this.getPointerInViewport(pageX, pageY, viewport, new Vector2()); this.pointer.copy(a.pointer); //console.log('updateTouchesInfo', this.pointer.clone()) } else { this.pointer = this.touches[0].pointer.clone(); //更新,使用当前touches中的第一个 } /* if(this.touches.find(e=>!e.pointer)){ console.error(' touches has no pointer', oldTouches.map(e=>e.touch.identifier), Array.from(e.touches).map(e=>e.identifier), Array.from(e.changedTouches).map(e=>e.identifier) ) } */ //console.log(this.touches) //console.log('更新pointer1',this.pointer.toArray()) return { viewport, camera /* , pointer:this.pointer */ }; } } onTouchStart(e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchStart'); e.preventDefault(); /* if (e.touches.length === 1 || !this.drag) { //!this.drag代表一次性下了两个指头 let rect = this.domElement.getBoundingClientRect(); let x = e.touches[0].pageX let y = e.touches[0].pageY this.dealPointerDown(x,y,e,true) }else{ this.updateTouchesInfo(e) this.drag.end.copy(this.pointer) } */ this.dealPointerDown(e, true); this.viewer.dispatchEvent($.extend(this.getEventDesc(e, true), { type: 'global_' + e.type, changedTouches: e.changedTouches })); /* console.log('targetTouches :', Array.from(e.targetTouches).map(e=>'| identifier: '+ e.identifier), 'changedTouches :', Array.from(e.changedTouches).map(e=>'| identifier: '+ e.identifier) ) */ //console.log('') } onTouchMove(e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchMove'); e.preventDefault(); /* if (e.touches.length === 1) { let rect = this.domElement.getBoundingClientRect(); let x = e.touches[0].pageX; let y = e.touches[0].pageY; }else{ this.updateTouchesInfo(e) this.drag.pointerDelta.subVectors(this.pointer, this.drag.end) this.drag.end.copy(this.pointer) } */ this.dealPointerMove(e, true); this.viewer.dispatchEvent($.extend(this.getEventDesc(e, true), { type: 'global_' + e.type, changedTouches: e.changedTouches })); /* console.log('targetTouches :', Array.from(e.targetTouches).map(e=>'| identifier: '+ e.identifier), 'changedTouches :', Array.from(e.changedTouches).map(e=>'| identifier: '+ e.identifier) ) */ } onTouchEnd(e) { if (this.logMessages) console.log(this.constructor.name + ': onTouchEnd'); e.preventDefault(); //console.log('onTouchEnd') this.updateTouchesInfo(e); /* if (e.touches.length === 0) { let rect = this.domElement.getBoundingClientRect(); let x = e.changedTouches[0].pageX //万一一次松开两个指头的怎么办 let y = e.changedTouches[0].pageY this.dealPointerUp(x,y,e,true) }else { this.drag.end.copy(this.pointer) } */ this.dealPointerUp(e, true); this.viewer.dispatchEvent($.extend(this.getEventDesc(e, true), { type: 'global_' + e.type })); //console.log('touchend length '+e.touches.length, this.touches.length) } onKeyDown(e) { if (this.logMessages) console.log(this.constructor.name + ': onKeyDown'); if (e.target.nodeName == 'INPUT' || e.target.nodeName == 'TEXTAREA') return; //正在输入文字 或e.srcElement if (!this.containsMouse) return; //在别的ui上无效 // DELETE /* if (e.keyCode === KeyCodes.DELETE && this.selection.length > 0) { this.dispatchEvent({ type: 'delete', selection: this.selection }); this.deselectAll(); } */ this.dispatchEvent({ type: 'keydown', keyCode: e.keyCode, event: e }); // for(let l of this.getSortedListeners()){ // l.dispatchEvent({ // type: "keydown", // keyCode: e.keyCode, // event: e // }); // } this.pressedKeys[e.keyCode] = true; // e.preventDefault(); } onKeyUp(e) { if (this.logMessages) console.log(this.constructor.name + ': onKeyUp'); if (e.keyCode != void 0) { delete this.pressedKeys[e.keyCode]; } else { this.pressedKeys = {}; } e.preventDefault(); } onDoubleClick(e) { if (this.logMessages) console.log(this.constructor.name + ': onDoubleClick'); var consumed = false; for (var hovered of this.hoveredElements) { if (hovered._listeners && hovered._listeners['dblclick']) { hovered.object.dispatchEvent({ type: 'dblclick', mouse: this.mouse, object: hovered.object }); consumed = true; break; } } if (!consumed) { /* for (let inputListener of this.getSortedListeners()) { inputListener. */ this.viewer.dispatchEvent({ type: 'global_dblclick', mouse: this.mouse, object: null }); //} } this.needSingleClick = false; //add e.preventDefault(); } onMouseClick(e) { if (this.logMessages) console.log(this.constructor.name + ': onMouseClick'); e.preventDefault(); } dealPointerDown(e, isTouch) { e.preventDefault(); //重新获取一下pointer, 因点击了浏览器的按钮展开列表时 move回来不会触发onmousemove,所以pointer是旧的 if (isTouch) { var { camera, viewport } = this.updateTouchesInfo(e); if (this.drag) { //因为触屏在按下前缺少pointermove所以要更新下 this.drag.end = this.pointer.clone(); } } else { var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY); } this.dragViewport = this.hoverViewport = viewport; //if(isTouch || !Potree.settings.intersectWhenHover ){ if (isTouch || !this.dragViewport.view.isFlying() && Potree.settings.intersectWhenHover && Potree.settings.editType != 'pano') { //漫游点编辑如果拖拽前getIntersect旋转会延迟 //isTouch必须更新 否则是旧的 this.hoveredElements = this.getHoveredElements(); var dontIntersect = false; this.intersect = this.getIntersect({ viewport, dontIntersect, clientX: e.clientX, clientY: e.clientY }); //更新intersect,避免在没有mousemove但flyToPano后intersect未更新。 //this.intersect = this.getWholeIntersect() } if (!viewport) return; //why add this? if (!this.drag) { var target = (isTouch || e.button == MOUSE.LEFT) && this.hoveredElements.find(el => //只有左键能拖拽 el.object._listeners && el.object._listeners['drag'] && el.object._listeners['drag'].length > 0); if (target) { this.startDragging(target.object, { location: target.point }); } else { this.startDragging(null); } } this.drag.intersectStart = this.intersect; if (!isTouch || e.touches.length == 1) { var consumed = false; var consume = () => { return consumed = true; }; //if (this.hoveredElements.length === 0) { this.viewer.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'global_mousedown' })); for (var hovered of this.hoveredElements) { var object = hovered.object; object.dispatchEvent({ type: 'mousedown', viewer: this.viewer, consume: consume }); if (consumed) { break; } } } this.mouseDownMouse = this.mouse.clone(); this.pointerDownTime = Date.now(); } onMouseDown(e) { if (this.logMessages) console.log(this.constructor.name + ': onMouseDown'); this.dealPointerDown(e); } /* getWholeIntersect(hoveredElements, intersectPoint){//add hoveredElements = hoveredElements || this.hoveredElements intersectPoint = intersectPoint || this.intersectPoint if(Potree.settings.intersectOnObjs && hoveredElements[0] && hoveredElements[0].object.isModel){ return {//模拟点云的intersectPoint的结构写法 hoveredElement : hoveredElements[0] , location: hoveredElements[0].point, point: {normal: hoveredElements[0].face.normal }, distance: hoveredElements[0].distance, object: hoveredElements[0].object } }else return intersectPoint } */ getEventDesc(e, isTouch) { //搜集dispatchEvent要给的一般数据 var o = { viewer: this.viewer, mouse: this.mouse, pointer: this.pointer, drag: this.drag, isTouch, dragViewport: this.dragViewport, hoverViewport: this.hoverViewport, // button: isTouch ? 0 : e.button, //intersectPoint:this.intersectPoint, hoveredElement: this.hoveredElements[0], intersect: this.intersect //this.getWholeIntersect() , //可能包含mesh上的,针对融合页面 }; if (e) { o.isAtDomElement = e.target == this.domElement; } if (isTouch) { o.touches = this.touches; } else if (e) { o.button = e.button; o.buttons = e.buttons; } return o; } dealPointerUp(e, isTouch) { if (!this.drag) { // 在canvas外mousedown return; } this.drag.end.copy(this.pointer); if (isTouch && e.touches.length >= 1) { return; } var now = Date.now(); if (this.logMessages) console.log(this.constructor.name + ': onMouseUp'); e.preventDefault(); var pressDistance = this.mouseDownMouse.distanceTo(this.mouse); var pressTime = now - this.pointerDownTime; var noMovement = this.drag.pointerDelta.length() == 0; //this.getNormalizedDrag().length() === 0; var consumed = false; var consume = () => { return consumed = true; }; this.viewer.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'global_mouseup', pressDistance, consume })); if (this.hoveredElements.length > 0) { var hovered = this.hoveredElements.map(e => e.object).find(e => e._listeners && e._listeners['mouseup']); if (hovered) { hovered.dispatchEvent({ type: 'mouseup', viewer: this.viewer, consume: consume }); } } if (this.drag) { //拖拽结束 if (this.drag.object /* && e.button == THREE.MOUSE.LEFT */) { //add LEFT if (this.logMessages) console.log("".concat(this.constructor.name, ": drop ").concat(this.drag.object.name)); this.drag.object.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'drop', pressDistance })); } else { this.viewer.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'global_drop', pressDistance })); } // check for a click if (!Potree.settings.disableClick && pressDistance < Potree.config.clickMaxDragDis && pressTime < Potree.config.clickMaxPressTime && !e.unableClick) { var clickElement; if (this.hoveredElements) { clickElement = this.hoveredElements.find(e => e.object._listeners['click']); if (clickElement) { var canceled; var cancel = () => { return canceled = true; }; //console.log('clickElement',clickElement) if (this.logMessages) console.log("".concat(this.constructor.name, ": click ").concat(clickElement.name)); clickElement.object.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'click', pressDistance, cancel, consume })); if (canceled) { //比如只需要右键的话,可以忽视左键的点击 clickElement = null; } } } var selectable; if ( /* !consumed && */!this.fixSelection) { if (e.button === MOUSE.LEFT) { //if (noMovement) { selectable = this.hoveredElements.find(el => el.object._listeners && el.object._listeners['select']); if (selectable) { selectable = selectable.object; if (this.isSelected(selectable)) { this.deselectAll(); } else { this.deselectAll(); this.toggleSelection(selectable); } consumed = true; //add } else { if (this.selection.length > 0) consumed = true; //add 取消选择后,阻断后续 this.deselectAll(); } //} } else if (e.button === MOUSE.RIGHT /* && noMovement */) { this.deselectAll(); } } var desc = this.getEventDesc(e, isTouch); if (!consumed) { this.viewer.dispatchEvent($.extend(desc, { type: 'global_click', pressDistance, clickElement: clickElement /* || selectable */, consume })); } //增加 单击: this.needSingleClick = true; consumed || setTimeout(() => { if (this.needSingleClick) { this.viewer.dispatchEvent($.extend(desc, { type: 'global_single_click', pressDistance, clickElement })); } }, Potree.config.doubleClickTime + 1); //自行执行双击: if (!consumed && now - this.lastClickTime < Potree.config.doubleClickTime) { this.onDoubleClick(e); } consumed || (this.lastClickTime = now); } this.drag = null; } this.dragViewport = null; } onMouseUp(e) { this.dealPointerUp(e); } getPointerInViewport(clientX, clientY, viewForceAt, pointer) { var rect = this.domElement.getBoundingClientRect(); var x = clientX - rect.left; var y = clientY - rect.top; var camera; var viewport; pointer = pointer || this.pointer; //if(this.viewer.viewports || viewForceAt){ var getDimension = view => { var left = Math.ceil(this.domElement.clientWidth * view.left), bottom = Math.ceil(this.domElement.clientHeight * view.bottom), width = Math.ceil(this.domElement.clientWidth * view.width), height = Math.ceil(this.domElement.clientHeight * view.height), top = this.domElement.clientHeight - bottom - height; return { left, bottom, width, height, top }; }; var getView = (view, left, bottom, width, height, top) => { this.mouse.set(x - left, y - top); Utils.convertScreenPositionToNDC(pointer, this.mouse, width, height); //console.log('更新pointer2',this.pointer.toArray()) camera = view.camera; viewport = view; }; if (viewForceAt) { var { left: _left, bottom: _bottom, width: _width, height: _height, top: _top } = getDimension(viewForceAt); getView(viewForceAt, _left, _bottom, _width, _height, _top); } else { var length = this.viewer.viewports.length; //var getif = false for (var i = 0; i < length; i++) { var view = this.viewer.viewports[i]; if (!view.active) continue; var { left, bottom, width, height, top } = getDimension(view); if (x >= left && x <= left + width && y >= top && y <= top + height) { getView(view, left, bottom, width, height, top); //getif = true break; } } } return { camera, viewport, pointer }; } ifBlockedByIntersect() { var { point, margin = 0, cameraPos, pickWindowSize, pano, useDepthTex, viewport } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //某点是否被遮挡(不允许camera修改位置, 因为depthTex不好置换) viewport = viewport || this.hoverViewport || viewer.mainViewport; var intersect = this.getIntersect({ viewport, onlyGetIntersect: true, pickWindowSize, useDepthTex, point, cameraPos, pano }); var cameraPos_ = !cameraPos && pano ? pano.position : cameraPos || viewport.view.position; if (intersect && intersect.distance + margin <= point.distanceTo(cameraPos_)) { return intersect; //被遮挡 } //点云模式,对没加载出的点云不准确。 尤其是需要修改相机位置时,因临时修改并不能使点云加载。 } collectClosePoints(viewport, forceUpdate) { //获取吸附点 if (!Potree.settings.adsorption) return; var point2ds = []; //吸附测量线端点 viewer.scene.measurements.forEach(e => { if (this.measuring.includes(e)) return; //不吸附到正在拖拽的自身 point2ds.push(...e.getPointsPos2d(viewport, forceUpdate)); }); return point2ds; } getIntersect() { var { viewport, onlyGetIntersect, pickWindowSize, dontIntersect, usePointcloud, useDepthTex, all, highLevelCloud, cameraPos, pointSize, pointclouds, point, pano, clientX, clientY, resolution } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; // usePointcloud:必须使用点云 var intersect, intersectPoint, intersectOnModel, allElements; var camera = viewport.camera; var raycaster; viewer.addTimeMark('getIntersect', 'start'); var getByDepthTex = () => { var intersect; if (point) { var _cameraPos = pano ? pano.position : camera.position; var _dir = new Vector3().subVectors(point, _cameraPos).normalize(); intersect = { dir: _dir }; } else { intersect = Utils.getIntersect(camera, [viewer.images360.cube], this.pointer, raycaster); } intersectPoint = viewer.images360.depthSampler.sample(intersect, pano, !!point); //可能不准确, 因pano可能未加载depthTex if (intersectPoint && Potree.settings.depTexLocBindDataset) { intersectPoint.pointcloud = (pano || viewer.images360.currentPano).pointcloud; //在全景模式下,虽然深度图上的点可能对应别的pointcloud,但因为是在当前全景图处得到的,所以即使将原本对应的点云移走,该点也不移动是有道理的。它可以永远跟着该全景图。 } }; var getByCloud = () => { var pointer, mouse, ifCenter; if (point) { //指定了目标点,而非只是用pointer所在位置 cameraPos && camera.position.copy(cameraPos); camera.lookAt(point); camera.updateMatrixWorld(); ifCenter = true; //pointer = this.pointer.clone() //mouse = this.mouse.clone() //this.pointer.set(0,0) //画布中心 //this.mouse.set(Math.round(viewport.resolution.x/2), Math.round(viewport.resolution.y/2)) } intersectPoint = viewport.noPointcloud || dontIntersect ? null : Utils.getMousePointCloudIntersection(viewport, this.mouse, this.pointer, camera, this.viewer, pointclouds || this.viewer.scene.pointclouds, { pickClipped: true, ifCenter, all, measuring: this.measuring.length > 0 || highLevelCloud, pickWindowSize, cameraChanged: !!point, pointSize, resolution }); //恢复 if (point) { viewport.view.applyToCamera(camera); //this.pointer.copy(pointer) //this.mouse.copy(mouse) } }; if (this.measuring.length && Potree.settings.adsorption) { //吸附 var points = this.collectClosePoints(viewport); var points2 = points.filter(e => e.trueSide && e.inSight && math.closeTo(this.mouse, e.posInViewport, Potree.config.measure.adsorptMinDis)); var disArr = points2.map(e => e.pos.distanceToSquared(this.mouse)); var min = points2.slice().sort((a, b) => disArr[points.indexOf(a)] - disArr[points.indexOf(b)]); if (min[0]) { intersect = { //hoveredElement location: min[0].pos3d, //point: {normal: allElements[0].face.normal }, //normal //distance object: min[0].object, adsorption: true }; console.log('找到吸附点', min[0].pos3d, min[0].object.uuid); } } if (!intersect) { var _intersect; var canUseDepthTex = (Potree.settings.displayMode == 'showPanos' || useDepthTex) && viewer.images360.currentPano.pointcloud.hasDepthTex && viewport == viewer.mainViewport && !usePointcloud; if (canUseDepthTex) getByDepthTex();else if (Potree.settings.mergeType2 && Potree.settings.displayMode == 'showPanos' && !viewer.images360.currentPano.pointcloud.isPointcloud) {} //融合页面进入非点云的场景的全景模式不用pick else getByCloud(); /* if(canUseDepthTex && this.measuring.length){ getByDepthTex() }else{ getByCloud() if(!intersectPoint && canUseDepthTex ){ //若在测量,先尝试点云,再用全景 //后来发现有深度图的点云全景visibleNode为空,pick不到的 getByDepthTex() } } */ //console.log(viewport.name , intersectPoint && intersectPoint.location ) if (Potree.settings.intersectOnObjs && !dontIntersect) { if (point) { raycaster = new Raycaster(); var dir = new Vector3().subVectors(point, camera.position).normalize(); raycaster.set(camera.position, dir); //var origin = new THREE.Vector3(pointer.x, pointer.y, -1).unproject(camera), } allElements = this.getHoveredElements(viewer.objs.children, true, raycaster); if (allElements[0]) { var normal, localNormal; if (allElements[0].object.fileType == '3dgs') { normal = allElements[0].normal; } else { localNormal = allElements[0].face && allElements[0].face.normal; if (localNormal) { var quaternion = new Quaternion(); allElements[0].oriObject.matrixWorld.decompose(new Vector3(), quaternion, new Vector3()); normal = localNormal.clone().applyQuaternion(quaternion); allElements[0].object.matrix.decompose(new Vector3(), quaternion, new Vector3()); localNormal = normal.clone().applyQuaternion(quaternion.invert()); //改为在最外层model上无旋转时的normal } } intersectOnModel = { //模拟点云的intersectPoint的结构写法 hoveredElement: allElements[0], location: allElements[0].point, localNormal, normal, distance: allElements[0].distance, object: allElements[0].object }; } } if (intersectPoint && intersectOnModel) { if (intersectPoint.distance < intersectOnModel.distance) { intersect = intersectPoint; } else { intersect = intersectOnModel; } } else { intersect = intersectOnModel || intersectPoint; } if ((_intersect = intersect) !== null && _intersect !== void 0 && _intersect.normal) { //尤其是点云屋顶上的normal都指向屋内了,需要根据站位反向一下 if (viewport.view.direction.angleTo(intersect.normal) < Math.PI / 2) { var _intersect$localNorma; intersect.normal.negate(); (_intersect$localNorma = intersect.localNormal) === null || _intersect$localNorma === void 0 || _intersect$localNorma.negate(); } } } if (viewport.camera.type == 'OrthographicCamera' /* == 'mapViewport' */) { var pos3d = new Vector3(this.pointer.x, this.pointer.y, -1).unproject(viewport.camera); //z:-1朝外 if (!intersect) { intersect = {}; } intersect.orthoIntersect = pos3d.clone(); } //记录全部hover到的: if (intersect) { intersect.allElements = allElements; intersect.pointclouds = intersectPoint ? intersectPoint.pointclouds : []; } viewer.addTimeMark('getIntersect', 'end'); //点云费时:2-15ms //深度图费时: 0.1-0.2ms /* intersect && intersect.location && intersect.location.applyMatrix4(viewer.scene.scene.matrix)//add */ if (onlyGetIntersect) { return intersect; } if (intersect) { if (viewer.showCoordType) { //显示坐标位置时 var pos = intersect.point.position.toArray(); if (viewer.showCoordType == "local") {} else if (viewer.showCoordType == "lonlat") { pos = viewer.transform.lonlatToLocal.inverse(pos); } else { pos = viewer.transform.lonlatToLocal.inverse(pos); pos = viewer.transform.lonlatTo4550.forward(pos); } viewer.dispatchEvent({ type: "coordinateChange", pos }); } } //console.log('getIntersect', !!intersectPoint) this.intersect = intersect; intersect && (this.hoverViewport.lastIntersect = intersect); return intersect; } onMouseMove(e) { return this.dealPointerMove(e); } dealPointerMove(e, isTouch) { if (this.interactHistory.move) return; //一帧只触发一次 this.interactHistory.move = 1; if (isTouch) { var { camera, viewport } = this.updateTouchesInfo(e); } else { var { camera, viewport } = this.getPointerInViewport(e.clientX, e.clientY, this.dragViewport); } this.hoverViewport = viewport; if (!viewport) return; //刚变化viewport时会找不到 var isFlying = this.viewer.viewports.some(e => e.view.isFlying()) || viewer.scene.cameraAnimations.some(c => c.onUpdate); var intersect; if (e.onlyGetIntersect || !this.drag || this.drag.object || viewport.alignment) { //没有拖拽物体,但按下鼠标了的话,不intersect。触屏的就能直接避免intersect var dontIntersect = this.drag && viewport.alignment || isFlying || !Potree.settings.intersectWhenHover; // flying 时可能卡顿 //console.log('dontIntersectPointcloud',dontIntersectPointcloud) intersect = this.getIntersect(Object.assign({}, e, { viewport, dontIntersect, clientX: e.clientX, clientY: e.clientY })); //数据集多的时候卡顿 } if (e.onlyGetIntersect) { return intersect; } e.preventDefault(); if (this.drag) { //有拖拽(不一定拖拽了物体, 也不一定按下了鼠标) this.drag.mouse = isTouch ? 1 : e.buttons; //add: //this.drag.pointer = this.pointer.clone(); //this.drag.hoverViewport = this.hoverViewport this.drag.pointerDelta.subVectors(this.pointer, this.drag.end); this.drag.end.copy(this.pointer); var dragConsumed = false; if (this.drag.object && (e.buttons == Buttons$1.NONE || !this.drag.notPressMouse)) { //如果是本不需要按鼠标的拖拽,但按下了鼠标,就不执行这段(改为拖拽场景,如添加测量时突然拖拽画面) if (this.logMessages) console.log(this.constructor.name + ': drag: ' + this.drag.object.name); var refused; this.drag.object.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'drag', //拖拽物体 refuse: () => { refused = true; } })); if (!refused) dragConsumed = true; viewer.dispatchEvent('content_changed'); } if (!dragConsumed) { if (this.logMessages) console.log(this.constructor.name + ': drag: '); this.viewer.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'global_drag' //拖拽画面 //consume: () => {dragConsumed = true;} })); } } if (!isTouch || e.touches.length == 1) { if ((!this.drag || this.drag.notPressMouse || Potree.settings.intersectOnObjs && this.drag.object) && !isFlying) { var _curr$_listeners; /* let blacklist = this.drag && this.drag */ var hoveredElements = this.getHoveredElements(); if (hoveredElements.length > 0) { var names = hoveredElements.map(h => h.object.name).join(", "); if (this.logMessages) console.log("".concat(this.constructor.name, ": onMouseMove; hovered: '").concat(names, "'")); } var cur = hoveredElements.find(a => a.object); var curr = cur && cur.object; //hoveredElements.map(a => a.object).find(a => true);//只取第一个 var prev = this.lastMouseoverElement; //this.hoveredElements.map(a => a.object).find(a => true); if (curr !== prev) { if (curr) { if (this.logMessages) console.log("".concat(this.constructor.name, ": mouseover: ").concat(curr.name)); curr.dispatchEvent({ type: 'mouseover', object: curr, hoveredElement: cur }); } if (prev) { if (this.logMessages) console.log("".concat(this.constructor.name, ": mouseleave: ").concat(prev.name)); prev.dispatchEvent({ type: 'mouseleave', object: prev }); } this.lastMouseoverElement = curr; viewer.dispatchEvent('content_changed'); } if (curr !== null && curr !== void 0 && (_curr$_listeners = curr._listeners) !== null && _curr$_listeners !== void 0 && _curr$_listeners.mousemove) { if (curr) { //xzw改为只取第一个 curr.dispatchEvent($.extend(this.getEventDesc(e), { type: 'mousemove', hoveredElement: cur })); } } /* if(hoveredElements.length > 0){ let object = hoveredElements .map(e => e.object) .find(e => (e._listeners && e._listeners['mousemove'])); if(object){ object.dispatchEvent($.extend( this.getEventDesc(e), { type: 'mousemove', hoveredElement: hoveredElements.find(e=>e.object == object) } )); } } */ this.hoveredElements = hoveredElements; } //this.intersect = this.getWholeIntersect() this.viewer.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'global_mousemove' })); this.containsMouse = true; } } onMouseWheel(e) { if (!this.enabled) return; if (this.logMessages) console.log(this.constructor.name + ": onMouseWheel"); e.preventDefault(); var delta = 0; if (e.wheelDelta !== undefined) { // WebKit / Opera / Explorer 9 delta = e.wheelDelta; } else if (e.detail !== undefined) { // Firefox delta = -e.detail; } var ndelta = Math.sign(delta); // this.wheelDelta += Math.sign(delta); if (!this.hoverViewport) { //调试手机版时会无 var { viewport } = this.getPointerInViewport(e.clientX, e.clientY); this.hoverViewport = viewport; } if (this.hoveredElement) { this.hoveredElement.object.dispatchEvent($.extend(this.getEventDesc(e, isTouch), { type: 'mousewheel', delta: ndelta, object: this.hoveredElement.object })); } else { this.viewer.dispatchEvent($.extend(this.getEventDesc(e), { type: 'global_mousewheel', delta: ndelta })); } setTimeout(() => { this.dealPointerMove(e); //add 在更新完view后重新获取intersect 和 drag }, 1); //只延迟1会崩溃吗 } startDragging(object) { var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; var name = object ? object.name : "no name"; if (this.logMessages) console.log("".concat(this.constructor.name, ": startDragging: '").concat(name, "'")); this.drag = { start: this.pointer.clone(), end: this.pointer.clone(), pointerDelta: new Vector2(0, 0), object: object, hoverViewport: this.hoverViewport, //会变化 dragViewport: this.hoverViewport //不变 }; if (args) { for (var key of Object.keys(args)) { this.drag[key] = args[key]; } } if (object) { object.dispatchEvent($.extend(this.getEventDesc(), { type: 'startDragging' })); } } /* getMousePointCloudIntersection (mouse) { return Utils.getMousePointCloudIntersection( this.mouse, this.scene.getActiveCamera(), this.viewer, this.scene.pointclouds); } */ toggleSelection(object) { var oldSelection = this.selection; var index = this.selection.indexOf(object); if (index === -1) { this.selection.push(object); object.dispatchEvent({ type: 'select' }); } else { this.selection.splice(index, 1); object.dispatchEvent({ type: 'deselect' }); } this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); viewer.dispatchEvent('content_changed'); } deselect(object) { var oldSelection = this.selection; var index = this.selection.indexOf(object); if (index >= 0) { this.selection.splice(index, 1); object.dispatchEvent({ type: 'deselect' }); this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } viewer.dispatchEvent('content_changed'); } deselectAll() { for (var object of this.selection) { object.dispatchEvent({ type: 'deselect' }); } var oldSelection = this.selection; if (this.selection.length > 0) { this.selection = []; this.dispatchEvent({ type: 'selection_changed', oldSelection: oldSelection, selection: this.selection }); } viewer.dispatchEvent('content_changed'); } isSelected(object) { var index = this.selection.indexOf(object); return index !== -1; } registerInteractiveObject(object) { this.interactiveObjects.add(object); } removeInteractiveObject(object) { this.interactiveObjects.delete(object); } registerInteractiveScene(scene) { var index = this.interactiveScenes.indexOf(scene); if (index === -1) { this.interactiveScenes.push(scene); } } unregisterInteractiveScene(scene) { var index = this.interactiveScenes.indexOf(scene); if (index > -1) { this.interactiveScenes.splice(index, 1); } } getHoveredElement() { var hoveredElements = this.getHoveredElements(); if (hoveredElements.length > 0) { return hoveredElements[0]; } else { return null; } } getHoveredElements(interactables, dontCheckDis, raycaster) { if (!interactables) { var scenes = this.hoverViewport.interactiveScenes || this.interactiveScenes.concat(this.scene); var interactableListeners = ['mouseup', 'mousemove', 'mouseover', 'mouseleave', 'drag', 'drop', 'click', 'select', 'deselect']; interactables = []; for (var scene of scenes) { scene.traverseVisible(node => { //检测加了侦听的object if (node._listeners && node.visible && !this.blacklist.has(node)) { var hasInteractableListener = interactableListeners.filter(e => { return node._listeners[e] !== undefined; }).length > 0; if (hasInteractableListener) { interactables.push(node); } } }); } } else interactables = interactables.filter(e => e.visible); var camera = this.hoverViewport.camera; if (!raycaster) { var ray = Utils.mouseToRay(this.pointer, camera); raycaster = new Raycaster(); raycaster.ray.set(ray.origin, ray.direction); raycaster.camera = camera; //add } if (camera.type == "OrthographicCamera") { //使无论多远,threshold区域都是一样宽的 raycaster.params.Line.threshold = 20 / camera.zoom; } else { raycaster.params.Line.threshold = 0.04; //相对长度 } raycaster.params.Line2 = { threshold: browser.isMobile() ? 100 : 20 }; //拓宽的lineWidth //raycaster.layers.enableAll()//add var layers = ['sceneObjects', 'mapObjects', /* 'measure', */'transformationTool', 'model', 'bothMapAndScene']; //设置能识别到的layers(如空间模型里只有mapViewer能识别到marker) if (Potree.settings.mergeType2 && Potree.settings.modelSkybox && Potree.settings.displayMode == 'showPanos' && !viewer.images360.currentPano.pointcloud.hasDepthTex) layers.push('skybox'); //model变成skybox了 Potree.Utils.setCameraLayers(raycaster, layers, this.hoverViewport && this.hoverViewport.extraEnableLayers); //this.hoverViewport.beforeRender && this.hoverViewport.beforeRender() viewer.dispatchEvent({ type: 'raycaster', viewport: this.hoverViewport, raycaster, viewer: this.viewer, interactables }); //add var intersections = raycaster.intersectObjects(interactables, true, null, true); //原本是false 检测不到children var intersectionsCopy = intersections.slice(); if (this.intersect && this.intersect.distance != void 0 && !dontCheckDis) { //add intersections = intersections.filter(e => { var _material$defines; if (this.intersect.hoveredElement && this.intersect.hoveredElement.oriObject == e.object) return true; var material = e.object.material; return e.object.pickDontCheckDis || (((_material$defines = material.defines) === null || _material$defines === void 0 ? void 0 : _material$defines.FadeFar) == void 0 || e.distance < material.uniforms.fadeFar.value * 1.2) && ( //在几乎完全消失的距离内 (material.depthTest == false || material.depthWrite == false) && !material.realUseDepth //!material.depthTestWhenPick || (material.useDepth ? e.distance <= this.intersect.distance + material.uniforms.clipDistance.value : e.distance <= this.intersect.distance)); //maxClipFactor是否需要考虑? }); } intersections = intersections.map(e => { //add 转化为interactables var object = e.object; do { if (interactables.includes(object)) { e.oriObject = e.object; e.object = object; break; } object = object.parent; } while (object); return e; }); //add for测量线,在检测到sphere时优先选中sphere而非线 //intersections = intersections.sort(function(a,b){return b.object.renderOrder-a.object.renderOrder}) // 降序 intersections = intersections.sort(function (a, b) { var order2 = b.object.pickOrder || 0; var order1 = a.object.pickOrder || 0; return order2 - order1; }); // 降序 //console.log('getHoveredElement ', intersections) return intersections; } /* setScene (scene) { this.deselectAll(); this.scene = scene; } */ update(delta) {} /*getNormalizedDrag () { if (!this.drag) { return new THREE.Vector2(0, 0); } let diff = new THREE.Vector2().subVectors(this.drag.end, this.drag.start); diff.x = diff.x / this.domElement.clientWidth; diff.y = diff.y / this.domElement.clientHeight; return diff; } getNormalizedLastDrag () { if (!this.drag) { return new THREE.Vector2(0, 0); } let mouseDelta = this.drag.mouseDelta.clone(); mouseDelta.x = mouseDelta.x / this.domElement.clientWidth; mouseDelta.y = mouseDelta.y / this.domElement.clientHeight; return mouseDelta; } */ getMouseDirection(pointer) { //add pointer = pointer || this.pointer; var camera = this.hoverViewport.camera; var t = new Vector3(pointer.x, pointer.y, -1).unproject(camera), i = new Vector3(pointer.x, pointer.y, 1).unproject(camera); return { origin: t, direction: i.clone().sub(t).normalize() }; } } class ViewerBase extends EventDispatcher { constructor(domElement) { var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; super(); this.name = args.name; this.renderArea = domElement; this.oldResolution = new Vector2(); this.oldResolution2 = new Vector2(); this.screenSizeInfo = { W: 0, H: 0, pixelRatio: 1, windowWidth: 0, windowHeight: 0 }; this.initContext(args); this.addEventListener('content_changed', () => { //画面改变,需要渲染 this.needRender = true; //console.log('needRender') }); } initContext(args) { //console.log(`initializing three.js ${THREE.REVISION}`); var width = this.renderArea.clientWidth; var height = this.renderArea.clientHeight; var contextAttributes = { alpha: true, //支持透明 depth: true, stencil: false, antialias: !!args.antialias, preserveDrawingBuffer: args.preserveDrawingBuffer || false, powerPreference: "high-performance" }; var canvas = document.createElement("canvas"); //Potree.settings.renderAllViewports = browser.maybeQilin() && this.renderer.capabilities.maxCubemapSize <= 2048 && //麒麟chromium若获取过webgl2只渲染一个viewport的话其他的会变黑 var webglVer = args.webgl1 || Potree.browser.urlHasValue('webgl1') || !Potree.Features.webgl2RealSupport() ? 'webgl' : 'webgl2'; var context = canvas.getContext(webglVer, contextAttributes); this.renderer = new WebGLRenderer({ premultipliedAlpha: false, canvas: canvas, context: context }); this.renderer.sortObjects = true; //原先false 打开了renderOrder才奏效 //this.renderer.setSize(width, height); this.renderer.autoClear = args.autoClear || false; //args.clearColor = args.clearColor || '#aa0033' args.clearColor && this.renderer.setClearColor(args.clearColor); this.renderArea.appendChild(this.renderer.domElement); this.renderer.domElement.tabIndex = '2222'; this.renderer.domElement.style.position = 'absolute'; this.renderer.domElement.addEventListener('mousedown', () => { this.renderer.domElement.focus(); }); { var oldRender = this.renderer.render; this.renderer.render = function (scene, camera) { Potree.currentRender = { renderer: this, scene, camera }; oldRender.apply(this, arguments); }; } //this.renderer.domElement.focus(); // NOTE: If extension errors occur, pass the string into this.renderer.extensions.get(x) before enabling // enable frag_depth extension for the interpolation shader, if available var gl = this.renderer.getContext(); gl.getExtension('EXT_frag_depth'); gl.getExtension('WEBGL_depth_texture'); gl.getExtension('WEBGL_color_buffer_float'); // Enable explicitly for more portability, EXT_color_buffer_float is the proper name in WebGL 2 if (gl.createVertexArray == null) { var extVAO = gl.getExtension('OES_vertex_array_object'); if (!extVAO) { throw new Error("OES_vertex_array_object extension not supported"); } gl.createVertexArray = extVAO.createVertexArrayOES.bind(extVAO); gl.bindVertexArray = extVAO.bindVertexArrayOES.bind(extVAO); } /* let oldClear = gl.clear; gl.clear = (bits)=>{ console.error('clear') } */ } updateScreenSize() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //有可能需要让viewport来判断,当窗口大小不变但viewport大小变时 if (this.screenshoting && !o.forceUpdateSize) return; //截图时不允许因窗口改变大小而updateScreenSize var render = false, ratio, w, h; //记录应当render的大小 if (o.width != void 0 && o.height != void 0) { w = o.width; h = o.height; render = true; ratio = 1; } else { w = this.renderArea.clientWidth; h = this.renderArea.clientHeight; if (w !== this.screenSizeInfo.W || h !== this.screenSizeInfo.H || o.forceUpdateSize || this.screenSizeInfo.pixelRatio != window.devicePixelRatio) { this.screenSizeInfo.W = w; this.screenSizeInfo.H = h; render = true; this.screenSizeInfo.pixelRatio = window.devicePixelRatio; //如果player放在小窗口了,也要监测devicePixelRatio,因为缩放时client宽高不会改变 //config.isMobile ? (ratio = Math.min(window.devicePixelRatio, 2)) : (ratio = window.devicePixelRatio) ratio = window.devicePixelRatio; } } if (render) { this.setSize(w, h, ratio, o.forTarget); } } setSize(width, height, devicePixelRatio, onlyForTarget) { //console.log('setSize', width) if (!onlyForTarget) { //onlyForTarget表示不更改当前renderer,只是为了rendertarget才要改变viewport this.renderer.setPixelRatio(devicePixelRatio); this.renderer.setSize(width, height); // resize之后会自动clear(似乎因为setScissor ),所以一定要立刻绘制,所以setSize要在cameraChanged、update之前 } //this.composer && this.composer.setSize(width, height); if (this.viewports) { this.viewports.forEach((view, i) => { //if(!view.active)return var width_ = width * view.width; var height_ = height * view.height; view.setResolution(Math.ceil(width_), Math.ceil(height_), width, height); if (height_ == 0) return; //avoid NAN var aspect = width_ / height_; //camera的参数精确些,不用视口的归整的resolution像素值,否则hasChange无法为true, 导致canvasResize了但map没update从而闪烁 view.camera.aspect = aspect; if (view.camera.type == "OrthographicCamera") { /* //不改宽度 同4dkk var heightHalf = view.camera.right / aspect view.camera.top = heightHalf view.camera.bottom = -heightHalf */ //高宽都改 使大小不随视口大小改变 navvis (直接和视口大小一致即可,通过zoom来定大小) view.camera.left = -width_ / 2; view.camera.right = width_ / 2; view.camera.bottom = -height_ / 2; view.camera.top = height_ / 2; } else {} view.camera.updateProjectionMatrix(); }); } if (!onlyForTarget) { //因为onlyForTarget不传递devicePixelRatio所以不发送了 this.dispatchEvent('viewerResize'); this.viewports.forEach(e => { this.ifEmitResize({ viewport: e, deviceRatio: devicePixelRatio }); }); } } ifEmitResize(e) { //切换viewport渲染时, 若这些viewport大小不同就发送一次, 通知一些材质更新resolution。 //console.log('ifEmitResize',e.viewport.name,e.viewport.resolution2 ) if (!e.viewport.resolution.equals(this.oldResolution) || !e.viewport.resolution2.equals(this.oldResolution2)) { this.dispatchEvent($.extend(e, { type: 'resize' })); this.oldResolution.copy(e.viewport.resolution); this.oldResolution2.copy(e.viewport.resolution2); } } cameraChanged() { //判断相机是否改变 var changed = false; /* if(this.needRender){ this.needRender = false return true } */ for (var i = 0, j = this.viewports.length; i < j; i++) { var viewport = this.viewports[i]; var changeInfo = viewport.cameraChanged(); if (changeInfo.changed) { changed = true; //if(!this.changeTime ||this.changeTime<100){ this.dispatchEvent({ type: "camera_changed", camera: viewport.camera, viewport, changeInfo }); //this.changeTime = (this.changeTime || 0) +1 //} viewport.needRender = true; //直接写这咯 if (changeInfo.resolutionChanged) { this.ifEmitResize({ viewport }); //for map } } } return changed; } makeScreenshot(size, viewports, compressRatio) { //暂时不要指定viewports渲染,但也可以 var { width, height } = size; /* let oldBudget = Potree.pointBudget; Potree.pointBudget = Math.max(10 * 1000 * 1000, 2 * oldBudget); let result = Potree.updatePointClouds(this.scene.pointclouds, camera, size ); Potree.pointBudget = oldBudget; this.dispatchEvent({ //resize everything such as lines targets type: 'resize', resolution: new THREE.Vector2(width,height), });*/ var target = new WebGLRenderTarget(width, height, { format: RGBAFormat }); this.setSize(width, height, 1, true); this.render({ target, //camera , viewports: viewports || this.viewports, screenshot: true, width, height, resize: true //需要resize }); var dataUrl = Potree.Utils.renderTargetToDataUrl(target, width, height, this.renderer, compressRatio); /* let pixelCount = width * height; let buffer = new Uint8Array(4 * pixelCount); this.renderer.readRenderTargetPixels(target, 0, 0, width, height, buffer); let dataUrl = Potree.Utils.pixelsArrayToDataUrl(buffer, width, height, compressRatio) */ target.dispose(); //resize back //this.updateScreenSize({forceUpdateSize:true}) return { width, height, dataUrl }; } dispose() { var scene = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.scene; scene.clear(); this.renderer.dispose(); this.renderer.forceContextLoss(); var gl = this.renderer.getContext(); gl.getExtension("WEBGL_lose_context") && gl.getExtension("WEBGL_lose_context").loseContext(); this.renderArea.removeChild(this.renderer.domElement); this.dispatchEvent('dispose'); } } class Viewport extends EventDispatcher { constructor(view, camera) { var prop = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; //目前不支持换camera super(); this.left = prop.left; this.bottom = prop.bottom; this.width = prop.width; this.height = prop.height; this.name = prop.name; this.view = view; this.camera = camera; this.active = true; this.unableChangePos = false; this.noPointcloud; //this.keys = [...] firstPersonCtl.... this.resolution = new Vector2(); this.resolution2 = new Vector2(); this.offset = new Vector2(); //viewportOffset 范围从0-整个画布的像素 this.extraEnableLayers = prop.extraEnableLayers || []; //额外可展示的层 this.cameraLayers = prop.cameraLayers; this.pixelRatio = prop.pixelRatio; //如果规定pixelRatio的话要传,这样就覆盖devicePicelRatio, 如magnifier } clone() { return Common.CloneClassObject(this); } getMoveSpeed() { return this.moveSpeed; } setMoveSpeed(e) { this.moveSpeed = e; } layersAdd(name) { this.extraEnableLayers.includes(name) || this.extraEnableLayers.push(name); } layersRemove(name) { var index = this.extraEnableLayers.indexOf(name); if (index > -1) { this.extraEnableLayers.splice(index, 1); } } cameraChanged() { var copy = () => { this.previousState = { projectionMatrix: this.camera.projectionMatrix.clone(), //worldMatrix在this.control时归零了所以不用了吧,用position和qua也一样 position: this.camera.position.clone(), quaternion: this.camera.quaternion.clone(), active: this.active, resolution: this.resolution.clone(), resolution2: this.resolution2.clone() //有时clientWidth没变但是ratio缩放了 }; }; var projectionChanged = true, positionChanged = true, quaternionChanged = true, activeChanged = true, resolutionChanged = true; var getChanged = () => { return { projectionChanged, positionChanged, quaternionChanged, activeChanged, resolutionChanged, changed: projectionChanged || positionChanged || quaternionChanged || activeChanged || resolutionChanged }; }; if (this.previousState) { projectionChanged = !this.camera.projectionMatrix.equals(this.previousState.projectionMatrix); positionChanged = !this.camera.position.equals(this.previousState.position); quaternionChanged = !this.camera.quaternion.equals(this.previousState.quaternion); activeChanged = this.active != this.previousState.active; resolutionChanged = !this.resolution.equals(this.previousState.resolution) || !this.resolution2.equals(this.previousState.resolution2); } copy(); return getChanged(); } setResolution(w, h) { var wholeW = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var wholeH = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; this.resolution.set(w, h); //是client的width height this.resolution2.copy(this.resolution).multiplyScalar(this.pixelRatio || window.devicePixelRatio); this.offset.set(wholeW, wholeH).multiply(new Vector2(this.left, this.bottom)); //.multiplyScalar(window.devicePixelRatio) this.dispatchEvent({ type: 'resize' }); } } var prefixVertex = "precision highp float;\nprecision highp int;\n\nuniform mat4 modelMatrix;\nuniform mat4 modelViewMatrix;\nuniform mat4 projectionMatrix;\nuniform mat4 viewMatrix;\nuniform mat3 normalMatrix;\nuniform vec3 cameraPosition;\n attribute vec3 position;\n attribute vec3 normal;\n attribute vec2 uv;\n"; var prefixFragment = "#extension GL_EXT_frag_depth : enable \n precision highp float;\nprecision highp int;\n\nuniform mat4 viewMatrix;\nuniform vec3 cameraPosition;\n"; // otherwise error: 'GL_EXT_frag_depth' : extension is disabled var getClassificationLUT = () => { var [width, height] = [256, 1]; var data = new Uint8Array(width * 4); var tex = new DataTexture(data, width, height, RGBAFormat); tex.magFilter = NearestFilter; tex.needsUpdate = true; return tex; }; var shader = { uniforms: { opacity: { type: "f" // value: 1 }, progress: { type: "f", value: 0 }, pano0Map: { type: "t", value: null }, pano1Map: { type: "t", value: null }, depthMap0: { type: "t", value: null }, depthMap1: { type: "t", value: null }, pano0ClassMap: { type: "t", value: null }, pano1ClassMap: { type: "t", value: null }, pano0TempMap: { type: "t", value: null }, pano1TempMap: { type: "t", value: null }, gradient: { type: "t", value: null }, classificationLUT: { type: "t", value: null }, pano0Position: { type: "v3", value: new Vector3() }, pano0Matrix: { type: "m4", value: new Matrix4() }, pano1Position: { type: "v3", value: new Vector3() }, pano1Matrix: { type: "m4", value: new Matrix4() }, /* pano1Matrix2: { type: "m4", value: new THREE.Matrix4 }, */ inverseProjectionMatrix: { value: new Matrix4() }, /* projectionMatrix:{//需要再写一遍吗 value: new THREE.Matrix4 }, */ viewport: { value: new Vector4() }, //如 {x: 0, y: 0, z: 428, w: 969} xy应该是offset, zw是宽高 cameraHeight0: { type: "f", value: 1 }, cameraHeight1: { type: "f", value: 1 }, ceilHeight0: { type: "f", value: 2 }, ceilHeight1: { type: "f", value: 2 }, temperRange: { //温度范围 type: 'vec2', value: new Vector2(math.getKelvinFromCelsius(15), math.getKelvinFromCelsius(60)) } }, vertexShader: prefixVertex + "\n\n uniform vec3 pano0Position;\n uniform mat4 pano0Matrix;\n \n uniform vec3 pano1Position;\n uniform mat4 pano1Matrix; \n\n \n varying vec2 vUv; \n varying vec3 vWorldPosition0;\n varying vec3 vWorldPosition1;\n varying vec3 vWorldPosition12;\n \n vec3 transformAxis( vec3 direction ) //navvis->4dkk\n {\n float y = direction.y;\n direction.y = direction.z;\n direction.z = -y;\n return direction;\n }\n \n \n void main() {\n \n vUv = uv;\n vec4 worldPosition = modelMatrix * vec4(position, 1.0);\n \n \n \n vec3 positionLocalToPanoCenter0 = worldPosition.xyz - pano0Position;\n vWorldPosition0 = (vec4(positionLocalToPanoCenter0, 1.0) * pano0Matrix).xyz;\n vWorldPosition0.x *= -1.0;\n vWorldPosition0 = transformAxis(vWorldPosition0);\n \n vec3 positionLocalToPanoCenter1 = worldPosition.xyz - pano1Position;\n vWorldPosition1 = (vec4(positionLocalToPanoCenter1, 1.0) * pano1Matrix).xyz;\n vWorldPosition1.x *= -1.0;\n vWorldPosition1 = transformAxis(vWorldPosition1);\n \n /* \n vec3 positionLocalToPanoCenter12 = worldPosition.xyz - pano1Position;\n vWorldPosition12 = (vec4(positionLocalToPanoCenter12, 1.0) * pano1Matrix2).xyz;\n vWorldPosition12.x *= -1.0;\n vWorldPosition12 = transformAxis(vWorldPosition12);\n */\n \n \n \n gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n \n }\n\n ", fragmentShader: prefixFragment + "\n \n \n #define PI 3.141592653 \n \n \n uniform float modelAlpha;\n uniform float opacity;\n uniform float progress;\n uniform int tranType;\n uniform vec3 pano0Position;\n uniform vec3 pano1Position;\n uniform float maxDistance;\n uniform float minDistance;\n uniform float minOpa;\n \n uniform samplerCube pano0Map;\n uniform samplerCube pano1Map;\n #if defined(useTempMap0) || defined(useTempMap1)\n uniform sampler2D gradient; \n uniform vec2 temperRange; \n #endif \n #if defined(useClassMap0) || defined(useClassMap1)\n uniform sampler2D classificationLUT;\n #endif\n #if defined(useTempMap0)\n uniform sampler2D pano0TempMap;\n #endif\n #if defined(useTempMap1)\n uniform sampler2D pano1TempMap;\n #endif\n #if defined(useClassMap0)\n uniform sampler2D pano0ClassMap;\n #endif\n #if defined(useClassMap1)\n uniform sampler2D pano1ClassMap;\n #endif\n varying vec2 vUv; \n varying vec3 vWorldPosition0;\n varying vec3 vWorldPosition1; \n \n \n /* vec2 getSamplerCoord( vec3 direction ) \n {\n direction = normalize(direction);\n float tx=atan(direction.x,-direction.y)/(PI*2.0)+0.5;\n float ty=acos(direction.z)/PI;\n\n return vec2(tx,ty);\n } */\n\n vec2 getSamplerCoord2( vec3 direction ) \n { \n direction = normalize(direction);\n float tx=atan(direction.x,direction.z)/(PI*2.0)+0.5;\n float ty=acos(direction.y)/PI;\n\n return vec2(tx,ty); \n }\n \n \n #if defined(GL_EXT_frag_depth) && defined(hasDepthTex) \n uniform sampler2D depthMap0;\n uniform sampler2D depthMap1;\n uniform mat4 inverseProjectionMatrix;\n uniform mat4 projectionMatrix;\n uniform vec4 viewport; \n uniform float cameraHeight0;\n uniform float cameraHeight1;\n uniform float ceilHeight0;\n uniform float ceilHeight1;\n \n \n vec2 getDepth(vec3 dir, sampler2D depthMap, float heightDown, float heightUp, vec4 eyePos){\n vec2 depthValue = vec2(0.0, 0.0);\n vec2 uv2 = getSamplerCoord2( dir.xyz); //\u6682\u65F6\u53EA\u7528\u57FA\u4E8E\u76EE\u6807\u6F2B\u6E38\u70B9\u7684\u65B9\u5411\n uv2.x -= 0.25; //\u5168\u666F\u56FE\u548CCube\u7684\u6C34\u5E73\u91C7\u6837\u8D77\u59CB\u5750\u6807\u76F8\u5DEE90\u5EA6\uFF0C\u8FD9\u91CC\u77EB\u6B63 0.25 \u4E2A\u91C7\u6837\u504F\u79FB\n vec4 depth = texture2D(depthMap, uv2);\n //float distance = depth.r + 256. * (depth.g + 256. * depth.b);\n //distance *= 255. * .001; // distance is now in meters\n \n //\u66F4\u6539\n float distance = (depth.g + depth.r / 256.) * 255.; \n \n if(distance == 0.0){//\u6F2B\u6E38\u70B9\u5E95\u90E8\u8BC6\u522B\u4E0D\u5230\u7684\u533A\u57DF\uFF0C\u7ED9\u4E00\u4E2A\u5730\u677F\u9AD8\u5EA6 \n if(uv2.y < depthTexUVyLimit) distance = heightUp / dir.y; \n else if(uv2.y > 1.0 - depthTexUVyLimit) distance = heightDown / -dir.y; \n else distance = 100000.0;//\u7ED9\u4E2A\u8D85\u7EA7\u8FDC\u7684\u503C\n } \n \n if(distance == 0.0)distance = 100000.0;//\u7ED9\u4E2A\u8D85\u7EA7\u8FDC\u7684\u503C\n \n depthValue.x = distance;\n \n distance += .1; // add a safety margin\n\n vec4 eyePos2 = vec4(normalize(eyePos.xyz) * distance, 1.);\n vec4 clipPos2 = projectionMatrix * eyePos2;\n vec4 ndcPos2 = clipPos2 * 1. / clipPos2.w;\n\n \n depthValue.y = 0.5 * ((gl_DepthRange.far - gl_DepthRange.near) * ndcPos2.z\n + gl_DepthRange.near + gl_DepthRange.far); \n \n #if defined(depth_background) \n //\u540E\u6392\u7684 skybox \u4E0D\u80FD\u6321\u4F4Fchunk \n depthValue.y += 0.3;\n #endif\n \n \n \n return depthValue; \n }\n //\u6CE8\uFF1A\u672A\u52A0\u8F7D\u597D\u7684\u8BDD\uFF0Cdepth\u4E3A0\uFF0C\u5BFC\u81F4\u7B2C\u4E00\u6B21\u6F2B\u6E38\u8FC7\u53BB\u7684\u65F6\u5019\u8BB8\u591Amesh\u4F1A\u7ACB\u523B\u88AB\u906E\u6321\uFF0C\u6240\u4EE5\u8981\u786E\u4FDD\u52A0\u8F7D\u5B8C\n #endif\n \n \n \n int getIntFromColor(sampler2D map, vec3 dir){//uint16-> int 0-65536 \n vec2 uv = getSamplerCoord2(dir.xyz); \n uv.x -= 0.25; \n vec4 color = texture2D(map, uv); \n return int(round(color.r * 255.0)) + (int(round(color.g * 255.0)) << 8) ; //some phone needs round,such as vivo x30\n } \n \n \n /*vec4 getColorFromMap(sampler2D map, vec3 dir){\n vec2 uv = getSamplerCoord2(dir.xyz); \n uv.x -= 0.25; \n vec4 color = texture2D(map, uv); \n return color;\n }*/\n \n \n //ir\u70ED\u6210\u50CF \n #if defined(useTempMap0) || defined(useTempMap1)\n \n vec4 getTemperature(sampler2D map, vec3 dir){ \n \n float temperature = float(getIntFromColor(map, dir))/ 10.0 ; \n \n float w = (temperature - temperRange.x) / (temperRange.y - temperRange.x);\n w = clamp(w,0.0,1.0);\n vec4 color = vec4(texture2D(gradient, vec2(w,1.0-w)).rgb, 1.0); \n return color;\n \n }\n #endif \n \n #if defined(useClassMap0) || defined(useClassMap1)\n \n /*vec4 getClassification(sampler2D map, vec3 dir, vec4 originColor){ \n vec4 color = getColorFromMap(map, dir); \n float v = color.r * 255.0; \n if(v<=2.01&&v>=1.99){\n color = vec4(1.0,0.0,0.0,1.0);\n } else{\n color = vec4(0.0,0.0,0.0,1.0);\n } \n return color;\n } */\n \n \n \n vec4 getClassification(sampler2D map, vec3 dir, vec4 originColor){ \n \n int classIndex = getIntFromColor(map, dir); \n \n vec2 uv = vec2(float(classIndex) / 255.0, 0.5); //copy from pointcloud_new.vs\n vec4 classColor = texture2D(classificationLUT, uv);\n\t\n float blendRatio = 0.5;\n float classAlpha = classColor.a * blendRatio; \n vec4 color = vec4(classColor.rgb * classAlpha + originColor.rgb * (1.0-classAlpha), 1.0); //mix with old rgba\n return color;\n } \n #endif \n \n \n void main()\n { \n \n \n vec3 vWorldPosition0N = normalize(vWorldPosition0);\n vec3 vWorldPosition1N = normalize(vWorldPosition1);\n float progress_ = progress;\n \n vec4 colorFromPano0 = vec4(0.0,0.0,0.0,0.0);\n #if defined(usePanoMap0)\n //\u5373progress < 1.0 \u901A\u5E38\u662F1 \n \n #if defined(useTempMap0) \n colorFromPano0 = getTemperature(pano0TempMap,vWorldPosition0N.xyz); \n #else \n colorFromPano0 = textureCube(pano0Map,vWorldPosition0N.xyz);\n #if defined(useClassMap0) \n colorFromPano0 = getClassification(pano0ClassMap,vWorldPosition0N.xyz, colorFromPano0); \n #endif\n #endif\n \n #else \n progress_ = 1.0;\n #endif\n \n \n vec4 colorFromPano1 = vec4(0.0,0.0,0.0,0.0);\n #if defined(useTempMap1) \n colorFromPano1 = getTemperature(pano1TempMap,vWorldPosition1N.xyz);\n #else \n colorFromPano1 = textureCube(pano1Map,vWorldPosition1N.xyz);\n #if defined(useClassMap1) \n colorFromPano1 = getClassification(pano1ClassMap,vWorldPosition1N.xyz, colorFromPano1); \n #endif\n #endif\n \n \n gl_FragColor = mix(colorFromPano0,colorFromPano1,progress_);\n \n \n \n \n \n //\u6DF1\u5EA6\u56FE\u4FEE\u6539\u6DF1\u5EA6\n \n #if defined(GL_EXT_frag_depth) && defined(hasDepthTex) \n vec4 ndcPos;\n ndcPos.xy = ((2.0 * gl_FragCoord.xy) - (2.0 * viewport.xy)) / (viewport.zw) - 1.;\n ndcPos.z = (2.0 * gl_FragCoord.z - gl_DepthRange.near - gl_DepthRange.far) /\n (gl_DepthRange.far - gl_DepthRange.near);\n ndcPos.w = 1.0;\n\n vec4 clipPos = ndcPos / gl_FragCoord.w;\n vec4 eyePos = inverseProjectionMatrix * clipPos;\n vec2 depth0 = vec2(0.0,0.0);\n vec2 depth1 = vec2(0.0,0.0);\n float depth = 0.0;\n \n bool useDepth0 = false;\n bool useDepth1 = true;\n \n \n #if defined(usePanoMap0) \n useDepth0 = true;\n #if defined(UnableMixTwoDepth) \n if(progress_<0.5){\n useDepth1 = false;\n }else{\n useDepth0 = false; \n }\n #endif\n if(useDepth0) depth0 = getDepth(vWorldPosition0N, depthMap0, cameraHeight0, ceilHeight0, eyePos);\n #endif\n \n if(useDepth1) depth1 = getDepth(vWorldPosition1N, depthMap1, cameraHeight1, ceilHeight1, eyePos);\n \n \n #if defined(UnableMixTwoDepth)\n depth = useDepth0 ? depth0.y : depth1.y;//\u4E0D\u652F\u6301\u53E0\u52A0\uFF0C\u53EA\u80FD\u7528\u5176\u4E2D\u4E00\u4E2A\uFF0C\u8FC7\u6E21\u65F6\u65E0\u6CD5\u6E10\u53D8\n #else\n depth = mix(depth0.y,depth1.y,progress_); \n #endif\n gl_FragDepthEXT = clamp(depth, 0.0, 1.0); //\u9632\u6B62\u90E8\u5206\u624B\u673A\u51FA\u73B0\u9ED1\u5757\u3002ios 16 \u3002 \u56E0\u4E3A\u6211\u7ED9\u7684\u8D85\u8FDC\u503C\u8D85\u51FA\u8303\u56F4\n \n\n #endif\n\n \n }\n " }; //注:gl_FragDepthEXT 修改了确实能像真实mesh那样遮挡住在后面的物体。但是为过渡时不能直接像有模型那样,和角度有关。 class ModelTextureMaterial extends RawShaderMaterial { constructor() { var defines = { depthTexUVyLimit: Potree.config.depthTexUVyLimit }; if (Potree.browser.maybeQilin()) { defines.UnableMixTwoDepth = 1; //该系统在开启硬件加速后,webgl容易出bug。如过渡时黑屏报错,因无法将两个depth叠加。见bug记录 } var { vs, fs } = Common.changeShaderToWebgl2(shader.vertexShader, shader.fragmentShader, 'RawShaderMaterial'); super({ fragmentShader: fs, vertexShader: vs, uniforms: UniformsUtils.clone(shader.uniforms), side: DoubleSide, name: "ModelTextureMaterial", defines }); this.glslVersion = Potree.settings.isWebgl2 && '300 es'; this.uniforms.classificationLUT.value = this.classificationTexture = getClassificationLUT(); var setSize = e => { var viewport = e.viewport; //let viewportOffset = viewport.offset || new Vector2() var resolution = viewport.resolution2; //this.uniforms.viewport.value.set(viewportOffset.x, viewportOffset.y, resolution.x, resolution.y) this.uniforms.viewport.value.set(0, 0, resolution.x, resolution.y); // xy是在viewport中的left和bottom,和整个窗口没有关系,所以不是viewportOffset。几乎都是0,0 }; var viewport = viewer.mainViewport; setSize({ viewport }); viewer.addEventListener('resize', e => { if (e.viewport.name != "MainView") return; setSize(e); }); //var supportExtDepth = !!Features.EXT_DEPTH.isSupported() { //add viewer.addEventListener('camera_changed', e => { if (e.viewport.name != "MainView") return; //this.uniforms.projectionMatrix.value.copy(e.camera.projectionMatrix) e.camera && this.uniforms.inverseProjectionMatrix.value.copy(e.camera.projectionMatrixInverse); }); var setClass = () => { this.classification = viewer.classifications; ExtendPointCloudMaterial.prototype.recomputeClassification.call(this); }; viewer.addEventListener('classifications_changed', setClass); setClass(); this._gradient = Gradients.ir; //海拔贴图种类,火灾也是这个 this.uniforms.gradient.value = ExtendPointCloudMaterial.generateGradientTexture(this._gradient); } var progress = 0; Object.defineProperty(this.uniforms.progress, 'value', { get: function get() { return progress; }, set: e => { if (e < 1 && !Potree.settings.fastTran) { if (!('usePanoMap0' in this.defines)) { this.defines.usePanoMap0 = ''; this.needsUpdate = true; } } else { if ('usePanoMap0' in this.defines) { delete this.defines.usePanoMap0; this.needsUpdate = true; } } progress = e; } }); //------------------------------------- } setProjectedPanos(pano0, pano1, progressValue) { progressValue != void 0 && (this.uniforms.progress.value = progressValue); //pano0.ensureSkyboxReadyForRender(); if (pano0) { this.uniforms.pano0Map.value = pano0.getSkyboxTexture(); //pano0.texture this.uniforms.pano0Position.value.copy(pano0.position); this.uniforms.pano0Matrix.value.copy(pano0.panoMatrix /* pano0.mesh.matrixWorld */); //pano1.ensureSkyboxReadyForRender(); } this.uniforms.pano1Map.value = pano1.getSkyboxTexture(); //pano1.texture; this.uniforms.pano1Position.value.copy(pano1.position); this.uniforms.pano1Matrix.value.copy(pano1.panoMatrix /* pano1.mesh.matrixWorld */); this.pano0 = pano0; this.pano1 = pano1; this.updateDepthTex(pano0); this.updateDepthTex(pano1); this.updateTempEnable(); this.updateClassEnable(); //console.log('setProjectedPanos', pano0&&pano0.id, pano1&&pano1.id) this.needsUpdate = true; } updateDepthTex(pano, extra) { if (!Potree.settings.useDepthTex || !pano || !pano.depthTex || pano != this.pano0 && pano != this.pano1) return; //console.log('updateDepthTex', pano.id, this.pano0 && this.pano0.id, this.pano1 && this.pano1.id) if (this.pano0) { this.uniforms.depthMap0.value = this.pano0.entered ? this.pano0.depthTex : null; //dispose了就不要赋值否则dispose会失败 this.uniforms.cameraHeight0.value = this.pano0.floorPosition.distanceTo(this.pano0.position); this.uniforms.ceilHeight0.value = this.pano0.getCeilHeight() - this.pano0.position.z; } if (this.pano1) { this.uniforms.depthMap1.value = this.pano1.depthTex; //pano1还没entered时也需要,可能在飞入 this.uniforms.cameraHeight1.value = this.pano1.floorPosition.distanceTo(this.pano1.position); this.uniforms.ceilHeight1.value = this.pano1.getCeilHeight() - this.pano1.position.z; } if (this.dontChangeDepth) return; var hasDepthTex = this.pano0 && this.pano1 && this.pano0.pointcloud.hasDepthTex && this.pano1.pointcloud.hasDepthTex; //暂时不知道一个有图一个没图怎么写所以 Potree.Utils.addOrRemoveDefine(this, 'hasDepthTex', hasDepthTex ? 'add' : 'remove'); } updateTempEnable() { var tex0 = Potree.settings.showHotTemp && this.pano0.tempTex || Potree.settings.showHotIr && this.pano0.irTex; var tex1 = Potree.settings.showHotTemp && this.pano1.tempTex || Potree.settings.showHotIr && this.pano1.irTex; this.uniforms.pano0TempMap.value = tex0; this.uniforms.pano1TempMap.value = tex1; Potree.Utils.addOrRemoveDefine(this, 'useTempMap0', tex0 ? 'add' : 'remove'); Potree.Utils.addOrRemoveDefine(this, 'useTempMap1', tex1 ? 'add' : 'remove'); viewer.dispatchEvent('content_changed'); } updateClassEnable() { var hasClassTex0 = Potree.settings.showClass && this.pano0.segTex; var hasClassTex1 = Potree.settings.showClass && this.pano1.segTex; this.uniforms.pano0ClassMap.value = this.pano0.segTex; this.uniforms.pano1ClassMap.value = this.pano1.segTex; Potree.Utils.addOrRemoveDefine(this, 'useClassMap0', hasClassTex0 ? 'add' : 'remove'); Potree.Utils.addOrRemoveDefine(this, 'useClassMap1', hasClassTex1 ? 'add' : 'remove'); viewer.dispatchEvent('content_changed'); } } ModelTextureMaterial.prototype.setTempRange = ExtendPointCloudMaterial.prototype.setTempRange; class FastTranPass { constructor(renderer) { this.renderer = renderer; this.coverRenderTarget = new WebGLRenderTarget(100, 100, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat }); this.coverTex = this.coverRenderTarget.texture; this.enabled = false; /* this.oldClearColor = new THREE.Color(); this.oldClearAlpha = 1; this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 ); this.scene = new THREE.Scene(); */ this.material = this.getMaskMaterial(); /* var copyShader = THREE.CopyShader; this.materialCopy = new THREE.ShaderMaterial( { uniforms: this.copyUniforms, vertexShader: copyShader.vertexShader, fragmentShader: copyShader.fragmentShader, blending: THREE.NoBlending, depthTest: false, depthWrite: false, transparent: true } ); this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), this.material); this.quad.frustumCulled = false; // Avoid getting clipped this.scene.add( this.quad ); this.renderToScreen = true*/ } setSize(width, height) { this.coverRenderTarget.setSize(width, height); } start() { this.enabled = true; var { x, y } = viewer.mainViewport.resolution2; this.setSize(x, y); //draw coverTex var oldTarget = this.renderer.getRenderTarget(); //let oldClearColor = this.renderer.getClearColor() this.renderer.setRenderTarget(this.coverRenderTarget); //this.renderer.setClearColor( 0x000000, 0) var oldLayer = viewer.mainViewport.camera.layers.mask; Potree.Utils.setCameraLayers(viewer.mainViewport.camera, ['skybox']); this.renderer.render(viewer.scene.scene, viewer.mainViewport.camera); //this.renderer.setClearColor( 0x000000, 0) this.renderer.setRenderTarget(oldTarget); viewer.mainViewport.camera.layers.mask = oldLayer; this.material.uniforms.progress.value = 1; console.log('start111'); } render(scene, camera, viewports, renderer, writeBuffer, readBuffer) { /* var oldAutoClear = renderer.autoClear; renderer.autoClear = false; */ var { x, y } = viewer.mainViewport.resolution2; var uniforms = this.material.uniforms; //uniforms.bgTex.value = readBuffer.texture; //更新 uniforms.coverTex.value = this.coverTex; uniforms.progress.value = viewer.images360.cube.material.uniforms.progress.value; uniforms.screenRatio.value = x / y; // 使波纹为圆形 uniforms.screenRatio.value *= uniforms.screenRatio.value; Potree.Utils.screenPass.render(viewer.renderer, viewer.images360.fastTranMaskPass.material); //renderer.autoClear = oldAutoClear; } stop() { this.enabled = false; //console.log('stop111') } getMaskMaterial() { return new ShaderMaterial({ uniforms: { coverTex: { type: "t", value: null }, progress: { type: "f", value: 0 }, screenRatio: { type: "f", value: 1 } }, vertexShader: " \n varying vec2 vUv;\n \n void main() \n {\n vUv = uv;\n \n gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n } \n \n ", fragmentShader: " \n uniform sampler2D coverTex; \n uniform float progress; \n uniform float screenRatio;\n varying vec2 vUv;\n \n void main() {\n \n const float maxRadius = 0.708; // sqrt(0.5^2+0.5^2)\n const float minRadius = 0.0 ;\n \n float radius = screenRatio>1.0 ? sqrt((vUv.x - 0.5)*(vUv.x - 0.5) + (vUv.y - 0.5)*(vUv.y - 0.5)/screenRatio) : sqrt((vUv.x - 0.5)*(vUv.x - 0.5)*screenRatio+ (vUv.y - 0.5)*(vUv.y - 0.5));\n float diff = 0.292; //1.0-maxRadius;\n float radiusIn = maxRadius * progress + minRadius * (1.0-progress);\n float radiusOut = radiusIn + diff;\n if(radius < radiusIn) {\n \n discard;\n \n }else if(radius>radiusOut){\n gl_FragColor = texture2D(coverTex, vUv) ;\n //gl_FragColor = vec4(1.0,1.0,0.0,1.0);//\n \n }else{\n \n /* vec4 color1 = texture2D(bgTex, vUv);\n vec4 color2 = texture2D(coverTex, vUv);\n float rotio = smoothstep(radiusIn ,radiusOut,radius);\n \n gl_FragColor = mix(color1, color2, rotio); */\n \n float rotio = smoothstep(radiusIn ,radiusOut, radius);\n \n vec4 color2 = texture2D(coverTex, vUv);\n color2.a = rotio;\n \n \n }\n }\n " }); } /* getMaskMaterial(){ return new THREE.ShaderMaterial( { uniforms: { coverTex: { type: "t", value: null }, bgTex: { type: "t", value: null }, progress:{ type: "f", value: 0 }, screenRatio:{ type: "f", value: 1 } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); } `, fragmentShader: ` uniform sampler2D coverTex; uniform sampler2D bgTex; uniform float progress; uniform float screenRatio; varying vec2 vUv; void main() { const float maxRadius = 0.708; // sqrt(0.5^2+0.5^2) const float minRadius = 0.0 ; float radius = screenRatio>1.0 ? sqrt((vUv.x - 0.5)*(vUv.x - 0.5) + (vUv.y - 0.5)*(vUv.y - 0.5)/screenRatio) : sqrt((vUv.x - 0.5)*(vUv.x - 0.5)*screenRatio+ (vUv.y - 0.5)*(vUv.y - 0.5)); float diff = 0.292; //1.0-maxRadius; float radiusIn = maxRadius * progress + minRadius * (1.0-progress); float radiusOut = radiusIn + diff; if(radius < radiusIn) { gl_FragColor = texture2D(bgTex, vUv); //gl_FragColor = vec4(0.0,0.0,1.0,1.0);// }else if(radius>radiusOut){ gl_FragColor = texture2D(coverTex, vUv) ; //gl_FragColor = vec4(1.0,1.0,0.0,1.0);// }else{ vec4 color1 = texture2D(bgTex, vUv); vec4 color2 = texture2D(coverTex, vUv); float rotio = smoothstep(radiusIn ,radiusOut,radius); gl_FragColor = mix(color1, color2, rotio); } } ` } ); } */ } var GLCubeFaces$1 = Potree.defines.GLCubeFaces; var TileUtils = {}; TileUtils.TILE_SIZE = 512, TileUtils.FACES_PER_PANO = 6, TileUtils.LocationOnTile = { Center: 0, UpperLeft: 1, UpperRight: 2, LowerRight: 3, LowerLeft: 4 }, /* * 获取某tile在cube中的方向 direction (向量起点在cube中心,终点在tile图的指定位置)。spherical通过先求uv,再直接得到dir * @param {*} size 面分辨率 * @param {*} cubeFace 所在面 * @param {*} Center 在tile上的目标位置,默认为中心,其他位置就是四个顶点 * @param {*} c 似乎是在tile的缩进百分比,根据所在面的不同,分别向不同方向缩进,但都是向tile的中心 * @param {*} dir 所求方向 */ TileUtils.getTileVector = function () { //获取某tile在cube中的方向 direction (向量起点在cube中心,终点在tile图的中心) return function (size, tileSize, cubeFace, tileX, tileY, Center, c, dir) { //c似乎是缩进百分比 Center = Center || TileUtils.LocationOnTile.Center; //假设该cube边长为2: var u = size / tileSize, d = tileX / u; tileY = -tileY + (u - 1); var p = tileY / u, f = tileSize / size, g = 2 * f //一个tile的宽度 (乘以2是因为cube边长是2) , m = g / 2, v = 2 * d - 1 + m, A = 2 * p - 1 + m; switch (Center) { //计算在tile中指定位置带来的偏移 case TileUtils.LocationOnTile.UpperLeft: //1 v -= m, A += m, v += c * g; //似乎是向内缩进 break; case TileUtils.LocationOnTile.UpperRight: v += m, A += m, A -= c * g; break; case TileUtils.LocationOnTile.LowerRight: v += m, A -= m, v -= c * g; break; case TileUtils.LocationOnTile.LowerLeft: v -= m, A -= m, A += c * g; break; case TileUtils.LocationOnTile.Center: //0 } switch (cubeFace) { case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_X: MathLight.setVector(dir, -1, A, -v); break; case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_X: MathLight.setVector(dir, 1, A, v); break; case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Y: //顶面 MathLight.setVector(dir, -v, 1, -A); break; case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: MathLight.setVector(dir, -v, -1, A); break; case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Z: MathLight.setVector(dir, -v, A, 1); break; case GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: MathLight.setVector(dir, v, A, -1); } MathLight.normalize(dir); }; }(), /* * 获取该tile在第几个面(简易装载法) */ TileUtils.getFaceForTile = function (size, index) { //获取该tile在第几个面 var tileSize = TileUtils.TILE_SIZE; size < TileUtils.TILE_SIZE && (tileSize = size); var n = Math.floor(size / tileSize), sum = n * n; //得每个面tile总数 return Math.floor(index / sum); }, TileUtils.getTileLocation = function (size, t, result) { var tileSize = TileUtils.TILE_SIZE; size < TileUtils.TILE_SIZE && (tileSize = size); var r = TileUtils.getFaceForTile(size, t), a = Math.floor(size / tileSize), s = a * a, l = t - r * s; result.tileX = l % a; result.tileY = Math.floor(l / a); result.face = r; result.faceTileIndex = l; return result; }, /* * 求size分辨率需要多少张tile */ TileUtils.getTileCountForSize = function (e) { if (e <= TileUtils.TILE_SIZE) return TileUtils.FACES_PER_PANO; var t = Math.floor(e / TileUtils.TILE_SIZE), i = t * t, n = i * TileUtils.FACES_PER_PANO; return n; }, TileUtils.getRelativeDirection = function () { var e = new MathLight.Matrix4(), t = new MathLight.Quaternion(); return function (i, n) { //i是pano.quaternion, n是camera的direction t.copy(i), t.inverse(), e.makeRotationFromQuaternion(t), e.applyToVector3(n), MathLight.normalize(n); }; }(), /* * 根据方向寻找合适的tile加载 */ TileUtils.matchingTilesInDirection = function () { var e = new MathLight.Vector3(), t = new MathLight.Vector3(0, 0, -1), i = new MathLight.Quaternion(), n = function n(e, t) { e.push({ face: t.face, faceTileIndex: t.faceTileIndex, tileX: t.tileX, tileY: t.tileY }); }, a = function () { var e = { face: -1, faceTileIndex: -1, tileX: -1, tileY: -1 }; return function (size, i, r) { for (var a = TileUtils.getTileCountForSize(size), s = 0, l = 0; l < a; l++) TileUtils.getTileLocation(size, l, e), i && !i(e) || (s++, r && n(r, e)); return s; }; }(); return function (pano, size, dir, hFov, vFov, result) { var d = size < TileUtils.TILE_SIZE ? size : TileUtils.TILE_SIZE; //TileUtils.getTileCountForSize(size); if (!hFov && !vFov) return a(size, null, result); var p = !!vFov; vFov = vFov || hFov, vFov = Math.max(0, Math.min(vFov, 360)), hFov = Math.max(0, Math.min(hFov, 360)), MathLight.copyVector(dir, e), TileUtils.getRelativeDirection(pano.quaternion4dkk, e); if (p) { //如果有vFov hFov i.setFromUnitVectors(e, t); var f = function f(e) { return TileUtils.isTileWithinFrustum(size, d, e.face, e.tileX, e.tileY, i, hFov, vFov); //在视野中的 }; return a(size, f, result); } var g = function g(t) { //如果仅有hFov return TileUtils.isTileWithinFOV(size, d, t.face, t.tileX, t.tileY, e, hFov); }; return a(size, g, result); }; }(), /* * 是否在屏幕范围内 */ TileUtils.isTileWithinFrustum = function () { var e = new MathLight.Vector3(), t = 1e-5; return function (i, n, a, s, l, c, h, u) { for (var d = Math.tan(.5 * u * MathLight.RADIANS_PER_DEGREE), p = -d, f = Math.tan(.5 * h * MathLight.RADIANS_PER_DEGREE), g = -f, m = TileUtils.mapFaceToCubemapFace(a), v = 0, A = 0, y = 0, C = 0, I = 0, E = 0, b = TileUtils.LocationOnTile.Center; b <= TileUtils.LocationOnTile.LowerLeft; b++) { TileUtils.getTileVector(i, n, m, s, l, b, 0, e), //get e // size, tileSize, cubeFace, tileX, tileY, Center, c, dir MathLight.applyQuaternionToVector(c, e); if (e.z >= -t) //似乎是在相机背面 I++;else { var w = -1 / e.z, _ = e.x * w, T = e.y * w; T > d ? v++ : T < p && A++, //这四种似乎代表在这个画框之外,如在左、在上、在下、在右 _ > f ? y++ : _ < g && C++, E++; } } return A !== E && v !== E && y !== E && C !== E; //如果有一项和E相等代表要么是在相机背面要么是tile的四个顶点都画在画布的同一边,所以肯定不在画布上 }; }(), /* * 是否在FOV范围内 */ TileUtils.isTileWithinFOV = function () { var e = new MathLight.Vector3(), t = new MathLight.Vector3(0, 1, 0), i = new MathLight.Vector3(1, 0, 0); return function (panoSize, tileSize, face, tileX, tileY, direction, fov) { //direction是作用了pano.quaternion的camera.direction var d = TileUtils.mapFaceToCubemapFace(face); MathLight.cross(direction, t, i); //get i 好像没用到 TileUtils.getTileVector(panoSize, tileSize, d, tileX, tileY, TileUtils.LocationOnTile.Center, 0, e); if (TileUtils.isWithinFOV(e, direction, fov, null)) //先判断tile中心在不在FOV内 return !0; for (var p = fov / 360, f = Math.floor(1 / p), g = 0, m = 0; m < f; m++) { for (var v = TileUtils.LocationOnTile.UpperLeft; v <= TileUtils.LocationOnTile.LowerLeft; v++) if (TileUtils.getTileVector(panoSize, tileSize, d, tileX, tileY, v, g, e), TileUtils.isWithinFOV(e, direction, fov, null)) return !0; g += p; //可能是考虑到有可能tile比fov覆盖了fov(虽然一般不可能,除非fov特别小),所以将tile分成若干段,取tile中的点再检测下 } return !1; }; }(), TileUtils.isWithinFOV = function () { var e = new MathLight.Vector3(), t = new MathLight.Vector3(); return function (dir, cameraDir, fov, a) { if (MathLight.copyVector(dir, t), a) { MathLight.copyVector(a, e), MathLight.normalize(e); var s = MathLight.dot(e, dir); e.x *= s, e.y *= s, e.z *= s, MathLight.subVector(t, e); } var l = fov / 2 * MathLight.RADIANS_PER_DEGREE, c = Math.cos(l), h = MathLight.dot(t, cameraDir); return h >= c; }; }(), TileUtils.mapFaceToCubemapFace = function () { var e = { 0: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 1: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 2: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 3: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 4: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 5: GLCubeFaces$1.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y }; return function (t) { return e[t]; }; }(); var { PanoRendererEvents, PanoramaEvents, PanoSizeClass } = Potree.defines; var texLoader$3 = new TextureLoader(); var markerOpacitys = { default: 0.4, hovered: 1 }; var labelProp = { sizeInfo: { minSize: 200, maxSize: 250, nearBound: 0.8, farBound: 10 }, backgroundColor: { r: 255, g: 255, b: 255, a: 0.4 }, textColor: { r: 0, g: 0, b: 0, a: 1 }, borderRadius: 15, renderOrder: 10, useDepth: true, clipDistance: 30, maxClipFactor: 0.3, occlusionDistance: 3 }; var labelProp2 = { //sizeInfo: {minSize : 200 , maxSize : 250, nearBound : 0.8, farBound : 10}, backgroundColor: { r: 255, g: 255, b: 255, a: 0 }, textColor: { r: 255, g: 255, b: 255, a: 1 }, textBorderColor: { r: 30, g: 30, b: 30, a: 1 }, textBorderThick: 3, dontFixOrient: true, renderOrder: 10, fontsize: 30 }; var markerTex; //显示全景图时marker没有被遮挡,如果需要,要换成depthBasicMaterial 或者直接把skybox的深度修改(拿到深度贴图后更如此) var planeGeo = new PlaneBufferGeometry(0.4, 0.4); var sg = new SphereGeometry(0.1, 8, 8); var smHovered = new MeshBasicMaterial({ /* side: THREE.BackSide, */color: 0xff0000 }); var sm = new MeshBasicMaterial({/* side: THREE.BackSide */}); var rot90 = new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2); //使用的是刚好适合全景图的,给cube贴图需要转90° //var rot90 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1,0,0), -Math.PI/2 ); //4dkk->navvis //var rot901 = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,1,0), -Math.PI/2 ); //整张球幕图要旋转下 //rot90 = new THREE.Quaternion().multiplyQuaternions( rot901, rot90) var rotQua2 = new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), Math.PI); var old = null; /* 转成四维看看的axis: var a = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.Math.degToRad(-90)) 因为四维的要绕y转90 这里的quaternion.multiply(a); 先乘再换顺序 w : q.w, x:q.x , y:-q.z, z:q.y */ //暂时直接用4dkkconsole输出的数据 class Panorama extends EventDispatcher { constructor(o, images360) { super(); this.id = o.id; //唯一标识 this.images360 = images360; this.visible = true; //for updateVisible this.enabled = true; //是否可以走 this.addEventListener('isVisible', e => { //是否显示该点的mesh(不显示也能走) //console.log('pano isVisible', this.id, e.visible) Potree.Utils.updateVisible(this.marker, 'panoVisi', e.visible); Potree.settings.showPanoMesh && (this.mesh.visible = e.visible); if (e.reason == 'screenshot' || e.visible) { this.label && (this.label.visible = e.visible); //截图时隐藏下 } this.label2 && Potree.Utils.updateVisible(this.label2, 'panoVisi', e.visible); }); /* 漫游点可见性:新 level reason 类型 2(最高)buildingChange(不在此楼层) unvisible 1 ifShowMarker(marker显示开关) unvisible 0 pointcloudVisi(隐藏了数据集) unvisible */ if (Potree.settings.editType == 'pano') { //漫游点拼合编辑 this.uuid = this.originID = o.uuid; //对应4dkk中的id,可能不连续 this.index = o.index; //下标, 用于visibles this.pointcloud = viewer.scene.pointclouds.find(e => e.panoUuid == o.uuid); this.pointcloud.panos.push(this); this.sid = this.pointcloud.dataset_id + '|' + this.uuid; //不会更改的标记 用于entity.panos里的标记 delete o.pointcloud; this.panoData = o; /* //数据中原本的位置朝向 this.dataPosition = new THREE.Vector3().copy(o.pose.translation) this.dataQuaternion = new THREE.Quaternion().copy(o.pose.rotation) this.dataRotation = new THREE.Euler().setFromQuaternion(this.dataQuaternion) */ //因为位置朝向随着点云位置改变,所以直接改变点云,这里清零 this.originPosition = new Vector3(); this.quaternion = new Quaternion().copy(rotQua2); this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion); //4dkk内使用的quaternion this.quaternion2 = this.quaternion.clone(); this.quaternion = new Quaternion().multiplyQuaternions(this.quaternion, rot90); //全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度 var height = 1.4; //相机高度 this.originFloorPosition = this.originPosition.clone(); this.originFloorPosition.z -= height; } else { this.originPosition = new Vector3().fromArray(o.dataset_location); //完全对应vision.txt的translation this.originFloorPosition = new Vector3().fromArray(o.dataset_floor_location); this.originID = parseInt(o.file_id); //"file_id":"00022" 对应是4dkk的id --来自vision.txt this.pointcloud = o.pointcloud; //viewer.scene.pointclouds.find(e=>e.dataset_id == o.dataset_id) || viewer.scene.pointclouds[0] this.pointcloud.panos.push(this); //this.sid = this.pointcloud.sceneCode + '|' + this.originID //不会更改的标记 this.sid = this.pointcloud.dataset_id + '|' + this.originID; //不会更改的标记 //全景图和Cube的水平采样起始坐标相差90度 /* if(from4dkk){ var qua = o.dataset_orientation var quaternion = new THREE.Quaternion().fromArray(qua) quaternion = new THREE.Quaternion().multiplyQuaternions(quaternion, rot901);//整张球幕图要旋转下 因为在4dkk里转过,还原。如果是tiles的不用 this.quaternion = new THREE.Quaternion(quaternion.x, -quaternion.z, quaternion.y, quaternion.w) //转化坐标 }else{ */ var qua = o.dataset_orientation; //完全对应vision.txt的rotation qua = [qua[1], qua[2], qua[3], qua[0]]; this.quaternion = new Quaternion().fromArray(qua); this.quaternion4dkk = math.convertVisionQuaternion(this.quaternion); //4dkk内使用的quaternion this.quaternion2 = this.quaternion.clone(); this.quaternion = new Quaternion().multiplyQuaternions(this.quaternion, rot90); //全景图和Cube的水平采样起始坐标相差90度,cubeTex转90度 this.rotation4dkk = new Euler().setFromQuaternion(this.quaternion4dkk); } this.neighbours = []; this.rotation = new Euler().setFromQuaternion(this.quaternion); this.build(); this.transformByPointcloud(); //初始化位移 { //tile this.minimumTiledPanoLoaded = !1; this.highestPartialTileRenderOpCompleted = 0; this.highestFullTileRenderOpCompleted = 0; this.shouldRedrawOnBaseLoaded = !1; this.resolutionPromise = {}; this.tiledPanoRenderTarget = null; this.zoomed = !1; images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderSuccess, this.onTileRendered.bind(this)); images360.panoRenderer.addEventListener(PanoRendererEvents.PanoRenderComplete, this.onPanoRendered.bind(this)); images360.panoRenderer.addEventListener(PanoRendererEvents.TileRenderFailure, this.onTileRenderFail.bind(this)); images360.panoRenderer.addEventListener(PanoRendererEvents.UploadAttemptedForAllTiles, this.onUploadAttemptedForAllTiles.bind(this)); } this.addEventListener('hoverOn', e => { //from Map if (!e.byMainView) { this.hoverOn(e); } }); this.addEventListener('hoverOff', e => { if (!e.byMainView) { this.hoverOff(e); } }); } get noNeighbour() { //是否绝对到不到的孤立点 for (var i = 0, j = this.images360.panos.length; i < j; i++) { if (this.images360.neighbourMap[this.id][i] !== false) { return false; } } return true; //return this.neighbours.length == 0 } setEnable(enable) { //是否可以走 Potree.Utils.updateVisible(this, 'isEnabled', enable); //令所有marker不可见 this.enabled = enable; viewer.dispatchEvent('content_changed'); //如果当前在全景模式且在这个点,需要切换显示吗? 目前用不到 } loadDepthImg() { if (!this.pointcloud.hasDepthTex || this.depthTex || this.depthTexLoading) return; this.depthTexLoading = true; var mapping = Potree.settings.isLocal2 ? '' : this.pointcloud.datasetData.mapping; //非离线包的话加mapping var src; if (Potree.settings.urls.templates.depthTex) { src = Potree.Common.replaceAll(Potree.settings.urls.templates.depthTex, '{sceneCode}', this.pointcloud.sceneCode) + "/".concat(this.originID, ".png"); } else { var prefix = Potree.settings.urls.getPrefix(1, this.pointcloud); //Potree.settings.urls.laserOSSRoot || this.pointcloud.prop?.raw?.laserOssRoot || Potree.settings.urls.prefix1 src = "".concat(prefix, "/").concat(mapping ? mapping + '/' : '').concat(Potree.settings.webSite, "/").concat(this.pointcloud.sceneCode, "/data/").concat(this.pointcloud.sceneCode, "/depthmap/").concat(this.originID, ".png"); } //console.log('开始下载depthImg', this.id) var texture = texLoader$3.load(src, () => { this.depthTex = texture; this.dispatchEvent({ type: 'loadedDepthImg', loaded: true }); this.images360.dispatchEvent({ type: 'loadedDepthImg', pano: this }); this.depthTexLoading = false; this.images360.updateDepthTex(this); }, null, e => { //error console.error('loadDepthImg失败, 数据集sceneCode' + this.pointcloud.sceneCode, this.id); this.pointcloud.hasDepthTex = false; this.dispatchEvent({ type: 'loadedDepthImg' }); }); texture.wrapS = RepeatWrapping; texture.flipY = false; texture.magFilter = LinearFilter; texture.minFilter = LinearFilter; texture.generateMipmaps = false; //平均一张0.75M。2048*1024。 和tile一样加载后永不删除,是否会造成崩溃? } /* loadTypeImg(type){ if(!this['has_'+type] || this[type+'Tex'] || this[type+'Loading'])return let url = `${Potree.settings.urls.prefix1}/testdata/${Potree.settings.number}/data/${this.pointcloud.sceneCode}/imagemap/${this.originID}_${type}.png` //let url = `${Potree.settings.urls.prefix1}/testdata/${Potree.settings.number}/data/${this.pointcloud.sceneCode}/imagemap/${this.originID}_temp.png` let texture = texLoader.load( url, ()=>{ this[type+'Tex'] = texture this.dispatchEvent({type:'loaded_'+type, loaded:true}) this.images360.dispatchEvent({type:'loaded_'+type, pano:this}) this[type+'Loading'] = false },null,(e)=>{//error console.error('load '+type+'Img失败, 数据集sceneCode'+ this.pointcloud.sceneCode, this.id ) this['has_'+type] = false this['loadFailed_'+type] = false this.dispatchEvent({type:'loaded_'+type }) }); texture.wrapS = THREE.RepeatWrapping; texture.flipY = false texture.magFilter = THREE.LinearFilter texture.minFilter = THREE.LinearFilter texture.generateMipmaps = false } */ loadTypeImg(type) { if (!this['has_' + type] || this[type + 'Tex'] || this[type + 'Loading']) return; var url = this.pointcloud.typesUrl + "".concat(this.originID, "_").concat(type, ".png"); //let url = `${Potree.settings.urls.prefix1}/testdata/${this.pointcloud.sceneCode}/data/${this.pointcloud.sceneCode}/imagemap/${this.originID}_temp.png` var range = { min: Infinity, max: -Infinity, minPixel: null, maxPixel: null }; var pixels; var getRangeFun = (type == 'ir' || type == 'temp') && function (uint16Value, index, pixelCount) { if (type == 'ir') { pixels || (pixels = new Uint16Array(pixelCount)); pixels[index] = uint16Value; } if (uint16Value != 0 && uint16Value < range.min) { range.min = uint16Value; range.minPixel_ = index; } if (uint16Value != 0 && uint16Value > range.max) { range.max = uint16Value; range.maxPixel_ = index; } }; var texture = Potree.Common.load16bitPngTex(url, () => { this[type + 'Tex'] = texture; if (getRangeFun) { if (type == 'ir') { var p = [range.minPixel_, range.maxPixel_].map(index => { var row = Math.floor(index / texture.image.width); var col = index % texture.image.width; return { x: col, y: row }; }); range.minPixel = p[0]; range.maxPixel = p[1]; var pixelCount = texture.image.width * texture.image.height; var stopMemberCount = 50; ['min', 'max'].forEach(name => { var _groups2$; var value = range[name]; var groups = []; var isNeigh = (A, B) => { return (Math.abs(A.x - B.x) <= 1 || Math.abs(A.x - B.x) == texture.image.width - 1) && Math.abs(A.y - B.y) <= 1; }; for (var i = 0; i < pixelCount; i++) { if (Math.abs(value - pixels[i]) == 0) { var row = Math.floor(i / texture.image.width); var col = i % texture.image.width; var data = { x: col, y: row, value: pixels[i] }; Potree.Common.pushToGroupAuto([data], groups, isNeigh); if (groups.some(e => e.length > stopMemberCount && e.some(e => e == range[name]))) { break; } } } var groups2 = groups.filter(e => e.length > 1); if (groups2.length < 3) groups2 = groups; groups2.forEach(group => { //x的因边界不好写,就只判断y尽量接近中间且范围不大不小即可 group.sort((a, b) => { return a.y - b.y; }); var minY = group[0].y; var maxY = group[group.length - 1].y; var centerY = (minY + maxY) / 2; var rectRatio = (maxY - minY) * 1; //除非竖的一列,否则一般y范围越大越好 Math.abs(2 - Potree.math.getBaseLog(maxY - minY, group.length)) //范围圆润度, 最好个数是y范围的平方 group.score = group.length * 0.2 + rectRatio - Math.abs(texture.image.height / 2 - centerY) * 0.5; //y尽量接近中间 group.center = group[Math.floor(group.length / 2)]; //忽略是否横向的中间 }); groups2.sort((b, a) => { return a.score - b.score; }); range[name + 'Pixel'] = (_groups2$ = groups2[0]) === null || _groups2$ === void 0 ? void 0 : _groups2$.center; //range[name+'PixelGroup'] = groups2 console.log('groups2', this.id, name, groups2); }); } range.min /= 10, range.max /= 10; texture.image.range = range; viewer.gatherTempRange(type, range); } this.dispatchEvent({ type: 'loaded_' + type, loaded: true }); this.images360.dispatchEvent({ type: 'loaded_' + type, pano: this }); this[type + 'Loading'] = false; }, e => { console.error('load ' + type + 'Img失败, 数据集sceneCode' + this.pointcloud.sceneCode, e, this.id); this['has_' + type] = false; this['loadFailed_' + type] = false; this.dispatchEvent({ type: 'loaded_' + type }); }, getRangeFun); texture.wrapS = RepeatWrapping; texture.flipY = false; texture.magFilter = LinearFilter; texture.minFilter = LinearFilter; texture.generateMipmaps = false; } build() { { // orientation //add //var quaternion = new THREE.Quaternion().multiplyQuaternions(this.quaternion, rot901);//改 为球目全 //quaternion.premultiply(rot90) this.panoMatrix = new Matrix4().makeRotationFromQuaternion(this.quaternion); this.oriPanoMatrix = this.panoMatrix.clone(); if (this.quaternion2) this.oriPanoMatrix2 = new Matrix4().makeRotationFromQuaternion(this.quaternion2); //console.log(this.quaternion) //this.quaternion = quaternion } var marker = new Mesh(planeGeo, this.getMarkerMat()); //new Sprite({mat:this.getMarkerMat(), dontFixOrient:true }) marker.name = 'marker_' + this.id; marker.up.set(0, 0, 1); this.addEventListener('changeMarkerTex', e => { marker.material.map = markerTex[e.name]; }); this.marker = marker; marker.pano = this; this.images360.node.add(marker); Potree.settings.isTest && this.addLabel(); marker.addEventListener('mouseover', this.hoverOn.bind(this)); marker.addEventListener('mouseleave', this.hoverOff.bind(this)); } transformByPointcloud() { this.ceilZ = null; //need reset var position = this.originPosition.clone().applyMatrix4(this.pointcloud.transformMatrix); //也可以用datasetPosTransform算 var floorPosition = this.originFloorPosition.clone().applyMatrix4(this.pointcloud.transformMatrix); this.setPosition(position, floorPosition); this.panoMatrix = new Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix); //this.panoMatrix2 = Potree.Utils.datasetRotTransform({fromDataset:true, pointcloud:this.pointcloud, matrix:this.oriPanoMatrix, getMatrix:true}) //和上一行结果一样 //quaternion也变下 if (this.oriPanoMatrix2) { this.panoMatrix2 = new Matrix4().multiplyMatrices(this.pointcloud.rotateMatrix, this.oriPanoMatrix2); //供DepthImageSampler使用 this.panoMatrix2Inverse = this.panoMatrix2.clone().invert(); } this.dispatchEvent('rePos'); } setPosition(position, floorPosition) { this.position = position; this.floorPosition = floorPosition; //this.mesh.position.copy(this.position) this.marker.position.copy(this.floorPosition); this.marker.lookAt(position); //融合页面marker可能跟随模型倾斜 var upVec = new Vector3().subVectors(position, floorPosition).normalize().multiplyScalar(0.04 * this.pointcloud.scale.x); this.marker.position.add(upVec); //this.marker.position.z+=0.04//会被点云遮住 if (this.label) { if (Potree.settings.editType == 'pano') { this.label.position.copy(this.position); } else { this.label.position.copy(this.floorPosition); } this.label.position.z += 0.14; this.label.updatePose(); } } /* getRealPos(){//当整体移动以后 return this.position.clone().applyMatrix4(viewer.scene.scene.matrix) } */ getMarkerMat() { if (!markerTex) { markerTex = { default: texLoader$3.load(Potree.resourcePath + '/textures/marker.png'), ring: texLoader$3.load(Potree.resourcePath + '/textures/marker2.png') }; markerTex.default.anisotropy = 4; // 各向异性过滤 .防止倾斜模糊 markerTex.ring.anisotropy = 4; //有可能被点云遮住吗。 } var mat = new DepthBasicMaterial({ opacity: markerOpacitys.default, side: DoubleSide, map: markerTex.default, transparent: true, clipDistance: 2, occlusionDistance: 1, //不能设置太短,因为过渡时深度不准确 useDepth: !!(Potree.settings.useDepthTex && this.pointcloud.hasDepthTex || Potree.settings.modelSkybox && this.pointcloud.is4dkkModel), autoDepthTest: true //改为DepthBasicMaterial是因为原Basic的材质在有深度图时过渡会先隐藏后出现。 注:没有深度图时全景模式的marker无法遮挡 }); mat.mapTransparent = true; return mat; } hoverOn() { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //console.log("hoverOn " + this.id ) transitions.start(lerp.property(this.marker.material, "opacity", markerOpacitys.hovered, () => { viewer.dispatchEvent('content_changed'); }), this.marker.visible ? 250 : 0); if (!e.byMap) this.dispatchEvent({ type: 'hoverOn', byMainView: true }); if (!e.byImages360) this.images360.dispatchEvent({ type: 'markerHover', hovered: true, pano: this }); } hoverOff() { var e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //console.log("hoverOff " + this.id ) transitions.start(lerp.property(this.marker.material, "opacity", markerOpacitys.default, () => { viewer.dispatchEvent('content_changed'); }), this.marker.visible ? 250 : 0); if (!e.byMap) this.dispatchEvent({ type: 'hoverOff', byMainView: true }); if (!e.byImages360) this.images360.dispatchEvent({ type: 'markerHover', hovered: false, pano: this }); } setZoomed(zoomed) { this.zoomed = zoomed; Potree.settings.displayMode == 'showPanos' && this.updateSkyboxForZoomLevel(); //放大后换成zoomTarget贴图 viewer.dispatchEvent({ type: 'panoSetZoom', zoomed }); } enter() { this.entered = true; this.setZoomed(!1); viewer.dispatchEvent({ type: PanoramaEvents.Enter, oldPano: old, newPano: this }); old = this; //console.log("enter pano "+ this.id) } exit() { this.clearWaitDeferreds(); this.minimumTiledPanoLoaded = !1; this.tiledPanoRenderTarget = null; this.setZoomed(!1); this.images360.panoRenderer.deactivateTiledPano(this); this.highestPartialTileRenderOpCompleted = 0; this.highestFullTileRenderOpCompleted = 0; this.depthTex && this.depthTex.dispose(); //贴图不使用后先dispose,下次到该点时会自动还原 this.entered = false; //add //console.log("exit pano "+ this.id) viewer.dispatchEvent({ type: PanoramaEvents.Exit, pano: this }); } updateSkyboxForZoomLevel() { if (this.minimumTiledPanoLoaded) { this.images360.updateProjectedPanos(); } } getSkyboxTexture() { if (this.minimumTiledPanoLoaded) { if (this.zoomed && this.images360.qualityManager.maxRenderTargetSize > this.images360.qualityManager.maxNavPanoSize) //change 如果放大后和不放大都是2k就不用这个 { return this.images360.panoRenderer.zoomRenderTarget.texture; } else { this.tiledPanoRenderTarget.texture.mapping = UVMapping; //add return this.tiledPanoRenderTarget.texture; } } else { return null; } } isLoaded(e) { if (e && "string" == typeof e) console.error("Wrong panoSize given to Panorama.isLoaded(); a tiled pano uses PanoSizeClass"); return !!this.minimumTiledPanoLoaded && (!e || this.highestFullTileRenderOpCompleted >= e); //改:原本是:this.highestPartialTileRenderOpCompleted >= e, 希望这代表全部加载完 } getWaitDeferred(size) { //获取不同size的tile贴图的promiss var t = this.resolutionPromise[this.id]; t || (t = {}, this.resolutionPromise[this.id] = t); var i = t[size]; return i || (i = { deferred: $.Deferred(), active: !1 }, t[size] = i), i; } clearWaitDeferreds() { var e = this.resolutionPromise[this.id]; e || (e = {}, this.resolutionPromise[this.id] = e); for (var t in e) if (e.hasOwnProperty(t)) { var i = e[t]; i.active = !1, i.deferred = $.Deferred(); } } resetWaitDeferred(e) { var t = this.getWaitDeferred(e); t.active = !1; t.deferred = $.Deferred(); } onTileRendered(ev) { ev.id === this.id && this.dispatchEvent({ type: PanoramaEvents.TileLoaded, size: ev.panoSize, index: ev.tileIndex, count: ev.totalTiles }); } onPanoRendered(ev) { if (ev.id === this.id) { this.minimumTiledPanoLoaded = !0; this.updateSkyboxForZoomLevel(); //更新贴图 setProjected ev.panoSize > this.highestPartialTileRenderOpCompleted && (this.highestPartialTileRenderOpCompleted = ev.panoSize); //应该是更新最高获取到的Partial size ev.updateFullComplete && ev.panoSize > this.highestFullTileRenderOpCompleted && (this.highestFullTileRenderOpCompleted = ev.panoSize); //应该是更新最高获取到的Full size //this.dispatchEvent("load", ev.panoSize); viewer.ifAllLoaded(this); this.dispatchEvent({ type: PanoramaEvents.LoadComplete, size: ev.panoSize, count: ev.totalTiles }); } } onTileRenderFail(ev) { ev.id === this.id && this.dispatchEvent({ type: PanoramaEvents.LoadFailed }); } onUploadAttemptedForAllTiles(ev) { if (ev.id === this.id) { var n = this.images360.qualityManager.getPanoSize(PanoSizeClass.BASE); if (ev.panoSize === n && this.shouldRedrawOnBaseLoaded) //shouldRedrawOnBaseLoaded一直是false。在4dkk里只有初始点在quickstart后变为true。 { this.shouldRedrawOnBaseLoaded = !1; this.panoRenderer.resetRenderStatus(this.id, !0, !1); this.panoRenderer.renderPanoTiles(this.id, null, !0, !0); } } } addLabel() { this.removeTextLabel(); this.label = new TextSprite$2(Object.assign({}, labelProp, { text: this.id + "(" + this.originID + ")" }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`} ); this.images360.node.add(this.label); this.floorPosition && this.label.position.copy(this.floorPosition); } addLabel2() { if (this.label2) return; this.label2 = new TextSprite$2(Object.assign({}, labelProp2, { text: /* this.originID */parseInt(this.id) + 1 }) //{text: `id:${this.id}, dataset:${this.pointcloud.name}, 4dkkId:${this.originID}`} ); //this.images360.node.add(this.label2); this.marker.add(this.label2); //this.floorPosition && this.label2.position.copy(this.floorPosition) var s = 0.2; this.label2.scale.set(s, s, s); Potree.Utils.updateVisible(this.label2, 'notDisplay', false); Potree.Utils.updateVisible(this.label2, 'panoVisi', this.visible); //Potree.Utils.setObjectLayers(this.label2, 'bothMapAndScene') } removeTextLabel() { if (this.label) { this.label.parent.remove(this.label); } } dispose() { var i = viewer.images360.panos.indexOf(this); if (i == -1) return; viewer.images360.panos.splice(i, 1); i = this.pointcloud.panos.indexOf(this); this.pointcloud.panos.splice(i, 1); this.marker.parent.remove(this.marker); this.removeTextLabel(); if (this.depthTex) this.depthTex.dispose(); this.dispatchEvent('dispose'); //删除tile贴图、depthTex等以后再写 } getCeilHeight() { //天花板高度值 (假设不存在depth为0的点,所有为0的要么是在盲区,要么是无穷远。) if (this.ceilZ == void 0) { //const depthTiming = Potree.timeCollect.depthSampler.median //pc firefox达到4. chrome为0.01 //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算) var rotMat = new Matrix4().makeRotationX((Potree.config.depthTexUVyLimit + 0.01) * Math.PI); // 角度不能小于天花板中空的半径 var dir0 = new Vector3(0, 0, 1).applyMatrix4(rotMat); var dirs = [dir0, dir0.clone().applyMatrix4(new Matrix4().makeRotationZ(Math.PI * 2 / 3)), dir0.clone().applyMatrix4(new Matrix4().makeRotationZ(-Math.PI * 2 / 3))]; /* if(depthTiming < 1){ let rotMat1 = new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3); dirs.push(dirs[0].clone().applyMatrix4(rotMat1)) } if(depthTiming < 0.3){ let rotMat2 = new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3); dirs.push(dirs[0].clone().applyMatrix4(rotMat2)); } */ var zs = dirs.map(dir_ => { var dir = dir_.clone().applyMatrix4(this.panoMatrix2); //pano不一定是垂直的, 需要把之前的dirInPano先转成真实的dir,防止超出角度限制 var intersect = viewer.images360.getIntersect(this, dir); var z = intersect ? intersect.location.z : Infinity; /* this.position.z+skyHeight */ //没有intersect代表可能是天空 return z; }); zs.sort((a, b) => { return b - a; }); //得最大值 (不用中位数的原因:在屋檐处,如果仅有一个intersect是天空,因到了室外所以也用天空高度) this.ceilZ = zs[0]; var min = this.position.z + 1; // 防止意外太低 this.ceilZ = Math.max(min, this.ceilZ); //console.log(this.id, 'ceilZ:', this.ceilZ ) } return this.ceilZ; } } ; Panorama.prototype.loadTiledPano = function () { //var downloads = [] , t = []; var downloaded = {}, eventAdded = {}, latestPartialRequest = {}; //每个pano对应一组这些 return function (size, dirs, fov, o, a, download) { var dir = dirs.datasetsLocal.find(e => e.datasetId == this.pointcloud.dataset_id).direction; //var dir = dirs null !== o && void 0 !== o || (o = !0), null !== a && void 0 !== a || (a = !0); var l = this.getWaitDeferred(size), c = l.deferred, h = null, u = null; fov && ("number" == typeof fov ? h = fov : (h = fov.hFov, u = fov.vFov)); if (!this.isLoaded(size)) { //console.log('loadTiledPano', this.id, size, fov) if (!l.active) { l.active = !0; var name = this.id + ":" + size; downloaded[name] = downloaded[name] || []; /* this.downloaded = downloaded this.latestPartialRequest = latestPartialRequest */ latestPartialRequest[name] = null; if (fov) { var tileArr = []; //add var d = TileUtils.matchingTilesInDirection(this, size, dir, h, u, tileArr); latestPartialRequest[name] = tileArr; downloaded[name].forEach(e => { var item = latestPartialRequest[name].find(a => e.faceTileIndex == a.faceTileIndex && e.face == a.face); if (item) { item.loaded = true; } }); if (!latestPartialRequest[name].some(e => !e.loaded)) { //所需要的全部加载成功 //let total = TileUtils.getTileCountForSize(size) //this.onPanoRendered(this.id, size, total, !0); c.resolve(size /* , total */); this.resetWaitDeferred(size); //console.log('该部分早已经加载好了'+size, this.id) latestPartialRequest[name] = null; } //console.log("Loading partial pano: " + this.id + " with " + d + " tiles") } if (!eventAdded[this.id]) { eventAdded[this.id] = !0; this.addEventListener(PanoramaEvents.LoadComplete, function (ev /* e, t */) { //本次任务全部加载完毕 //console.warn('点位(可能部分)下载完成 ', 'id:'+this.id, 'size:'+ev.size ) var i = this.getWaitDeferred(ev.size).deferred; //"pending"为还未完成 i && "pending" === i.state() && this.highestPartialTileRenderOpCompleted >= ev.size && (i.resolve(ev.size, ev.count), this.resetWaitDeferred(ev.size)); //恢复active为false }.bind(this)); this.addEventListener(PanoramaEvents.LoadFailed, function (ev) { var t = this.getWaitDeferred(e).deferred; t && "pending" === t.state() && this.highestPartialTileRenderOpCompleted >= ev.t && (t.reject(ev.t), this.resetWaitDeferred(ev.t)); //恢复active为false }.bind(this)); this.addEventListener(PanoramaEvents.TileLoaded, function (ev /* t, i, n */) { //每张加载完时 //console.log('tileLoaded', 'id:'+this.id, 'size:'+ev.size, 'tileIndex:'+ev.index ) var tileIndex = ev.index; var total = ev.count; var size = ev.size; var name = this.id + ":" + size; downloaded[name] = downloaded[name] || []; //不是所有的加载都是从loadTiledPano获取的所以会有未定义的情况 var { faceTileIndex, face } = TileUtils.getTileLocation(size, tileIndex, {}); downloaded[name].push({ faceTileIndex, face }); var r = this.getWaitDeferred(size).deferred; if (r && "pending" === r.state()) { r.notify(size, tileIndex, total); if (latestPartialRequest[name]) { var item = latestPartialRequest[name].find(e => e.faceTileIndex == faceTileIndex && e.face == face); item && (item.loaded = true); if (!latestPartialRequest[name].some(e => !e.loaded)) { //所需要的局部tiles全部加载成功 this.onPanoRendered(this.id, size, total, !0); //onPanoRendered还会触发 PanoramaEvents.LoadComplete r.resolve(size, total); this.resetWaitDeferred(size); //console.log('该部分加载好了'+size, this.id) latestPartialRequest[name] = null; } } } viewer.dispatchEvent('content_changed'); /* var r = this.getWaitDeferred(ev.size).deferred; if (r && "pending" === r.state()) { r.notify(ev.size, ev.index, ev.count); var o = downloads[this.id + ":" + ev.size]; if(o){//如果有规定下载哪些tile,只需要下载这些tile则LoadComplete o.tileCount++ if(o.tileCount === o.targetTileCount){//达到下载目标数 this.onPanoRendered(this.id, ev.size, ev.count, !0); r.resolve(ev.size, ev.count); this.resetWaitDeferred(ev.size) } } } */ }.bind(this)); } } this.images360.tileDownloader.clearForceQueue(); this.images360.tileDownloader.forceQueueTilesForPano(this, size, dir, h, u, download); this.tiledPanoRenderTarget = this.images360.panoRenderer.activateTiledPano(this, this.images360.qualityManager.getMaxNavPanoSize(), o); this.images360.panoRenderer.renderPanoTiles(this.id, dirs, a); //将512的先贴上 } else { //console.log('早已经全加载好了' +size, this.id) c.resolve(size); } return c.promise(); }; }(); var { ModelManagerEvents, PanoSizeClass: PanoSizeClass$1 } = Potree.defines; class QualityManager { constructor(e, t, i) { this.maxNavPanoSize = -1; this.maxZoomPanoSize = -1; this.devicePixelDensity = e; this.deviceScreenSize = t; this.clientBandwidth = i; this.panoSizeClassMap = {}; this.useHighResolutionPanos = !0; //是否能够使用2k及以上图 this.useUltraHighResolutionPanos = !1; this.modelHasUltraHighPanos = !1; this.qualityManager = this; this.maxRenderTargetSize = browser.isMobile() ? 2048 : 4096; //add this.maxRenderTargetSize = Math.min(viewer.renderer.capabilities.maxCubemapSize, this.maxRenderTargetSize); //部分系统老浏览器只能2048 this.init(); } init(e) { //var metadata = store.getters['scene/metadata'] ;//有时候请求不到 //if(metadata.sceneSource == 11 || metadata.sceneScheme == 12){ /* if(config.tileClass == '1k'){ this.useHighResolutionPanos = false //xzw add 只加载1k } */ this.buildPanoSizeClassMap(this.devicePixelDensity, this.deviceScreenSize, this.clientBandwidth); this.ultraHighSize = this.getPanoSize(PanoSizeClass$1.ULTRAHIGH); this.highSize = this.getPanoSize(PanoSizeClass$1.HIGH); this.standardSize = this.getPanoSize(PanoSizeClass$1.STANDARD); this.baseSize = this.getPanoSize(PanoSizeClass$1.BASE); config$1.tiling.maxZoomPanoQuality && this.ultraHighSize <= config$1.tiling.maxZoomPanoQuality && (config$1.tiling.allowUltraHighResolution = !0); this.highQualityThreshold = browser.valueFromHash("threshold2k", config$1.windowHeightHighQualityThreshold); this.updateMaximums(); //e.on(ModelManagerEvents.ActiveModelChanged, this.onModelChanged.bind(this)); } updateFromModel(e) { //this.updateHighResolutionSettings(e) this.updateUltraHighResolutionSettings(e); } /* updateHighResolutionSettings(e) { this.useHighResolutionPanos = !0 this.updateMaximums() } */ updateUltraHighResolutionSettings(e) { if (config$1.tiling.allowUltraHighResolution && this.modelHasUltraHighPanos) { this.useUltraHighResolutionPanos = !0; } else { this.useUltraHighResolutionPanos = !1; } this.updateMaximums(); } enableUltraHighQualityMode() { this.modelHasUltraHighPanos = !0; this.updateUltraHighResolutionSettings(null); } ultraHighQualityModeEnabled() { return this.modelHasUltraHighPanos; } onModelChanged(e) { this.updateFromModel(e.model), this.updateMaximums(); } updateMaximums() { this.maxNavPanoSize = config$1.tiling.maxNavPanoQuality || this.detectMaxNavPanoSize(), this.maxZoomPanoSize = config$1.tiling.maxZoomPanoQuality || this.detectMaxZoomPanoSize(), this.maxZoomPanoSize < this.maxNavPanoSize && (this.maxNavPanoSize = this.maxZoomPanoSize); } buildPanoSizeClassMap() { this.panoSizeClassMap[PanoSizeClass$1.BASE] = 512, this.panoSizeClassMap[PanoSizeClass$1.STANDARD] = 1024, this.panoSizeClassMap[PanoSizeClass$1.HIGH] = 2048, this.panoSizeClassMap[PanoSizeClass$1.ULTRAHIGH] = 4096; } getPanoSize(e) { return this.panoSizeClassMap[e]; } getMaxPossiblePanoSize() { return this.getPanoSize(PanoSizeClass$1.ULTRAHIGH); } getMaxPanoSize() { return this.maxZoomPanoSize; } getMaxNavPanoSize() { return this.maxNavPanoSize; } getMaxZoomPanoSize() { return this.maxZoomPanoSize; } detectMaxNavPanoSizeClass() { //return this.useHighResolutionPanos ? browser.isMobile() ? PanoSizeClass.STANDARD : window.innerHeight < this.highQualityThreshold ? PanoSizeClass.STANDARD : PanoSizeClass.HIGH : PanoSizeClass.STANDARD /* if(config.name == 'decor'){ return PanoSizeClass.STANDARD } return PanoSizeClass.HIGH */ switch (Potree.settings.navTileClass) { case '1k': return PanoSizeClass$1.STANDARD; break; case '2k': default: return PanoSizeClass$1.HIGH; } } detectMaxNavPanoSize() { var e = this.detectMaxNavPanoSizeClass(); return this.getPanoSize(e); } detectMaxZoomPanoSize() { //获取当前zoomRenderTarget应下载的最高级别 //若是有三个级别,每次只需要加载到当前的zoomLevel;而两级时因为有zoomed来判断是使用基本贴图还是zoomRenderTarget,所以只需要返回最大的即可 if (this.zoomLevelResolution) { //有三个级别 if (this.zoomLevelResolution == '4k' && this.useUltraHighResolutionPanos) { return this.getPanoSize(PanoSizeClass$1.ULTRAHIGH); } else if (this.zoomLevelResolution == '1k' || !this.useHighResolutionPanos) { return this.getPanoSize(PanoSizeClass$1.STANDARD); } else { return this.getPanoSize(PanoSizeClass$1.HIGH); } } else { if (this.useHighResolutionPanos) { /* if (browser.isMobile()) {//手机版如果要2k的将这里去掉 if (settings.tiling.mobileHighQualityOverride) { return this.getPanoSize(PanoSizeClass.HIGH); } else { return this.getPanoSize(PanoSizeClass.STANDARD); } } else */ if (this.useUltraHighResolutionPanos) { return this.getPanoSize(PanoSizeClass$1.ULTRAHIGH); } else { return this.getPanoSize(PanoSizeClass$1.HIGH); } } else { return this.getPanoSize(PanoSizeClass$1.STANDARD); } } } } var { DownloadStatus } = Potree.defines; var h = Object.freeze({ None: 0, DirectionalFOV: 1 }); var u = function () { var e = function e(t, i) { var n = e._panoSpaceDir, r = e._fovThreshold, o = e._fovThresholdNarrow, a = Math.max(Math.min(n.dot(t.direction), 1), -1), s = Math.max(Math.min(n.dot(i.direction), 1), -1); return t._dot = a, i._dot = s, a >= r && s < r ? -1 : a < r && s >= r ? 1 : a >= o && s < o ? -1 : a < o && s >= o ? 1 : t.panoSize > i.panoSize ? 1 : i.panoSize > t.panoSize ? -1 : -(a - s); }; return e._panoSpaceDir = new Vector3(), e._fovThreshold = -1, e._fovThresholdNarrow = -1, e; }(); class TilePrioritizer { //优先级处理序列 constructor(e, t, i, o, a) { this.qualityManager = e; this.maxNavQuality = this.qualityManager.getMaxNavPanoSize(); this.maxZoomQuality = this.qualityManager.getMaxZoomPanoSize(); this.baseSize = t; this.standardSize = i; this.highSize = o; this.ultraHighSize = a; this.priorityCriteria = new TilePrioritizer.PriorityCriteria(null, new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector3(0, 0, -1)); } updateCriteria(e, t, i, n) { //由player更新 this.priorityCriteria.pano = e, this.priorityCriteria.cameraPosition.copy(t), //this.priorityCriteria.cameraDir.copy(i), this.priorityCriteria.cameraDirs = i; this.priorityCriteria.upcomingPanos = n, this.maxNavQuality = this.qualityManager.getMaxNavPanoSize(), this.maxZoomQuality = this.qualityManager.getMaxZoomPanoSize(); } canDownloadSize(e) { return this.maxNavQuality >= e || this.maxZoomQuality >= e && this.zoomingActive; } /* populateNeighborPanos(e, t, i) { i = i || [], i.length = 0; var n = t.getNeighbours(e); for (var r in n) if (n.hasOwnProperty(r)) { var o = t.get(r); if(!o){ console.log(1) } i.push(o) } return i } */ getNearPanos(panos) { if (!this.priorityCriteria.pano || viewer.mainViewport.view.isFlying()) return; var cameraDirLocals = this.priorityCriteria.cameraDirs.vectorForward; this.populateScoredPanos(panos, [], cameraDirLocals, Infinity, true); } filterDepthTex(panos) { // 下载depthTex if (!Potree.settings.useDepthTex) return; viewer.addTimeMark('filterDepthTex', 'start'); var dlCount = 1; //window.slowDown ? 0 : (browser.isMobile() ? 1 : Common.getBestCountFPS('loadDepTexCount', false, 1, 2, 57, 62 ) ) var loadingCount = panos.filter(p => p.depthTexLoading).length; if (viewer.mapViewer) { var mapLayer = viewer.mapViewer.mapLayer; loadingCount += mapLayer.loadingInProgress * 1.5 + mapLayer.waitQueue.length; } if (loadingCount < dlCount) { //console.log('dlCount-loadingCount', dlCount-loadingCount) (this.nearPanos || panos).filter(p => !p.depthTex).slice(0, dlCount - loadingCount).forEach(p => p.loadDepthImg()); } viewer.addTimeMark('filterDepthTex', 'end'); } populateScoredPanos( /* e, */t, i, dirs, count, dontFilterDir) { i = i || [], i.length = 0; viewer.addTimeMark('populateScoredPanos', 'start'); var s = [Images360.filters.not(this.priorityCriteria.pano)], l = [Images360.scoreFunctions.distanceSquaredNew(this.priorityCriteria.pano), Images360.scoreFunctions.direction(this.priorityCriteria.pano.position, dirs)]; /* if(!dontFilterDir){//太耗时了,尤其是点多时,直接去掉,反正有count限制。如果必须的话,使用direction中得到的值 s.push(Images360.filters.inPanoDirection(this.priorityCriteria.pano.position, dirs, TilePrioritizer.DIRECTION_SCORE_STRICTNESS) ) } */ var c = Common.sortByScore(t, s, l); //900个点的时候populateScoredPanos已经耗时1.8 viewer.addTimeMark('populateScoredPanos', 'end'); this.nearPanos = c.map(e => e.item); //xzw add 用于获得邻近点位序列 if (c) for (var h = 0; h < c.length && h < count; h++) { var u = c[h].item; i.push(u); } return i; } queueTilesForPanos(e, t, i, n, r) { for (var o = 0, a = 0; a < t.length; a++) { var s = t[a], l = this.queueTilesForPano(e, i, s, n); if (o += l > 0 ? 1 : 0, r && o >= r) break; } return o; } /* queueTilesInDirectionForPanos(e, t, i, n, r, o, a, s) {//没用到 for (var l = 0, c = 0; c < i.length; c++) { var h = i[c], u = this.queueTilesInDirectionForPano(e, t, h, n, o, a); if (l += u > 0 ? 1 : 0, s && l >= s) break } return l } */ canIncludeDescriptor(e) { return e.status !== DownloadStatus.Downloading && e.status !== DownloadStatus.Downloaded; } canIncludePano(e, t) { return !e.isLoaded(t); } getFOVDotThreshold(e) { return Math.cos(MathUtils.degToRad(e / 2)); } setZoomingActive(e) { e !== this.zoomingActive && (this.zoomingActive = e); } } TilePrioritizer.PriorityCriteria = function (e, t, i, n, o) { this.pano = e, this.cameraPosition = new Vector3().copy(t), //this.cameraDir = (new THREE.Vector3).copy(i), this.cameraDirs = [], // this.panoSpaceDir = new Vector3().copy(n), this.upcomingPanos = o, this.copy = function (e) { this.pano = e.pano, this.cameraPosition.copy(e.cameraPosition), //this.cameraDir.copy(e.cameraDir), this.cameraDirs = e.cameraDirs; this.panoSpaceDir.copy(e.panoSpaceDir), this.upcomingPanos = o; }, this.zoomingActive = !1; }; TilePrioritizer.DIRECTIONAL_FOV = 180; TilePrioritizer.DIRECTIONAL_FOV_NARROW = 120; TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER = 6; TilePrioritizer.MAX_SCORED_PANOS_TOADD = 2; TilePrioritizer.MAX_UPCOMING_PANOS_TOADD = 3; TilePrioritizer.DIRECTION_SCORE_STRICTNESS = .75; TilePrioritizer.appendQueue = function (e, t) { if (e && t) for (var i = 0; i < t.length; i++) { e.push(t[i]); //console.log(t[i]) } }; TilePrioritizer.sortPanoTiles = function (descriptors, pano, dir) { if (dir.datasetsLocal) dir = dir.datasetsLocal.find(e => e.datasetId == pano.pointcloud.dataset_id).direction; //add u._panoSpaceDir.copy(dir); TileUtils.getRelativeDirection(pano.quaternion4dkk, u._panoSpaceDir); //应该是将dir根据quaternion转化下 u._fovThresholdNarrow = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV_NARROW); u._fovThreshold = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV); descriptors.sort(u); }; TilePrioritizer.insertSortedPanoTile = function (e, t, pano, dir) { if (dir.datasetsLocal) dir = dir.datasetsLocal.find(e => e.datasetId == pano.pointcloud.dataset_id).direction; //add u._panoSpaceDir.copy(dir), TileUtils.getRelativeDirection(pano.quaternion4dkk, u._panoSpaceDir), u._fovThresholdNarrow = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV_NARROW), u._fovThreshold = math.getFOVDotThreshold(TilePrioritizer.DIRECTIONAL_FOV); for (var o = -1, a = 0; a < e.length; a++) { var s = u(t, e[a]); if (s <= 0) { o = a; break; } } if (o === -1) e[e.length] = t;else { for (var h = e.length; h > o; h--) e[h] = e[h - 1]; e[o] = t; } }; TilePrioritizer.prototype.filterAndPrioritize = function () { //挑选出优先加载的 pano和tile (有点复杂,没看很懂) var e = [], t = [], i = []; return function (queue, panos, tileDownloader) { //this.populateNeighborPanos(this.priorityCriteria.pano, panos, e); /* let cameraDirLocals = this.priorityCriteria.cameraDirs.map(e=>{ //add var dataset = viewer.scene.pointclouds.find(u=>u.dataset_id == e.datasetId) var matrix = new THREE.Matrix4().copy(dataset.rotateMatrix) var direction = math.convertVector.YupToZup(e.direction) return { datasetId:e.datasetId, direction: direction.clone().applyMatrix4(matrix) } }) */ var cameraDirLocals = this.priorityCriteria.cameraDirs.vectorForward; //获得视野范围内的邻近点位序列t this.populateScoredPanos( /* this.priorityCriteria.pano, */panos, t, cameraDirLocals, TilePrioritizer.MAX_SCORED_PANOS_TOCONSIDER); //t.filter(p=>!p.depthTex).slice(0, Potree.config.depTexDlCount).forEach(p=>p.loadDepthImg()) //add var s = this.baseSize //512 , l = this.standardSize //1024 , c = this.highSize //2048 , h = this.ultraHighSize; //4096 this.queueTilesForPano(queue, tileDownloader, this.priorityCriteria.pano, s); //把当前pano的512下载了 if (this.priorityCriteria.upcomingPanos) { // 添加即将走到的点(之前用于导览路线)512 tiles this.queueTilesForPanos(queue, this.priorityCriteria.upcomingPanos, tileDownloader, s, TilePrioritizer.MAX_UPCOMING_PANOS_TOADD); } i.length = 0; //把当前pano角度范围内的tile按照分辨率从低到高加入队列 if (this.canDownloadSize(l)) { //1024如果在限制范围内的话 this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, l, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV_NARROW); } TilePrioritizer.sortPanoTiles(i, this.priorityCriteria.pano, this.priorityCriteria.cameraDirs); //排序 TilePrioritizer.appendQueue(queue, i); //添加邻近点t 512的tiles this.queueTilesForPanos(queue, t, tileDownloader, s, TilePrioritizer.MAX_SCORED_PANOS_TOADD); i.length = 0; //NARROW : if (this.canDownloadSize(c)) { //2048 this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, c, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV_NARROW); } if (this.canDownloadSize(h) && this.priorityCriteria.pano.pointcloud.tileRes != '2k') { //4096 this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, h, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV_NARROW); } TilePrioritizer.sortPanoTiles(i, this.priorityCriteria.pano, this.priorityCriteria.cameraDirs); //排序 TilePrioritizer.appendQueue(queue, i); i.length = 0; if (this.canDownloadSize(l)) { //1024 this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, l, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV); } if (this.canDownloadSize(c)) { //2048 this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, c, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV); } if (this.canDownloadSize(h) && this.priorityCriteria.pano.pointcloud.tileRes != '2k') { //4096 this.queueTilesInDirectionForPano(i, tileDownloader, this.priorityCriteria.pano, h, this.priorityCriteria.cameraPosition, this.priorityCriteria.cameraDirs, TilePrioritizer.DIRECTIONAL_FOV); } TilePrioritizer.sortPanoTiles(i, this.priorityCriteria.pano, this.priorityCriteria.cameraDirs); //排序 TilePrioritizer.appendQueue(queue, i); this.queueTilesForPanos(queue, e, tileDownloader, s); // 如果前面有populateNeighborPanos的话,这步就是加neibour }; }(); TilePrioritizer.prototype.queueTilesInDirectionForPano = function () { var e = { filter: h.DirectionalFOV, direction: new Vector3(), fov: 60 }, t = new Vector3(); return function (i, n, pano, o, a, dirs, c) { var dir = dirs.datasetsLocal.find(e => e.datasetId == pano.pointcloud.dataset_id).direction; //add //var dir = dirs t.copy(dir); TileUtils.getRelativeDirection(pano.quaternion4dkk, t); e.direction.copy(t); e.fov = c; return this.filterAndQueueTileDownloadDescriptors(i, n, pano, o, e); }; }(); TilePrioritizer.prototype.filterAndQueueTileDownloadDescriptors = function () { var e = []; return function (t, i, n, r, o) { var a = i.getTileDownloadDescriptors(n, r); e.length = 0, this.filterTileDownloadDescriptors(n, a, e, o); for (var s = 0, l = 0; l < e.length; l++) { var c = e[l]; if (c) { t.push(c); s++; } } return s; }; }(); TilePrioritizer.prototype.filterTileDownloadDescriptors = function () { new Vector3(); return function (e, t, i, n) { var r, o; switch (n.filter) { case h.DirectionalFOV: for (r = 0; r < t.length; r++) o = t[r], TileUtils.isTileWithinFOV(o.panoSize, o.tileSize, o.face, o.tileX, o.tileY, n.direction, n.fov) && i.push(o); break; default: for (r = 0; r < t.length; r++) o = t[r], i.push(o); } for (r = 0; r < i.length; r++) o = i[r], this.canIncludeDescriptor(o) || (i[r] = null); }; }(); TilePrioritizer.prototype.queueTilesForPano = function () { var e = { filter: h.None }; return function (t, i, n, r) { return this.filterAndQueueTileDownloadDescriptors(t, i, n, r, e); }; }(); /* TilePrioritizer.prototype.queueTilesForPanosInDirection = function () { //没用到 var e = new THREE.Vector3; return function (t, i, n, r, o, a, s, l) { for (var h = 0, u = 0; u < n.length; u++) { var d = n[u]; e.copy(d.position), e.sub(o), e.normalize(); var p = Math.max(Math.min(a.dot(e), 1), -1), f = c.getFOVDotThreshold(s); if (p >= f) { var g = this.queueTilesInDirectionForPano(t, i, d, r, o, a, s); if (h += g > 0 ? 1 : 0, l && h >= l) break } } return h } }() */ //import { i18n } from "@/lang/index" // 媒体名称 /* export const mediaTypes = { image: i18n.t("common.photo"), video: i18n.t("common.video"), audio: i18n.t("common.voice"), } */ // 媒体扩展类型 var mediaMimes = { image: ["jpg", "png", "jpeg", "bmp", "gif"], audio: ["mp3", "aac", "ogg", "wav" /* , "m4a" */], video: ["mp4", "mov", "quicktime", "webm" /* "rmvb", "wmv" */] //ios:mov }; // 媒体大小显示(MB) var mediaMaxSize = { image: 10, video: 20, audio: 5 }; /** * 获取媒体扩展类型 * @param {Stirng} filename 文件名称 */ var getMime = filename => { if (!filename || filename.indexOf(".") === -1) { return ""; } return filename.split(".").pop().toLowerCase(); }; /** * 在路径中获取文件名 * @param {*} path */ var getFilename = path => { var segment = (path || "").split("/"); return segment[segment.length - 1]; }; /** * 检测媒体文件是否超过预设限制 * @param {String} type 媒体类型 * @param {Number} size 文件大小 */ var checkSizeLimit = (type, size) => { size = size / 1024 / 1024; return size <= mediaMaxSize[type]; }; var checkSizeLimitFree = (size, limit) => { size = size / 1024 / 1024; return size <= limit; }; /** * 检测媒体类型 * @param {String} type 媒体类型 * @param {String} filename 文件名称 */ var checkMediaMime = (type, filename) => { var mime = getMime(filename); var find = mediaMimes[type]; if (!find) { return false; } return find.indexOf(mime) !== -1; }; var checkMediaMimeByAccept = (accept, filename) => { var mime = getMime(filename); var type = accept; if (type && type.indexOf("jpg") == -1 && type.indexOf("jpeg") != -1) { type += ",image/jpg"; } return (type || "").indexOf(mime) != -1; }; var base64ToBlob = base64 => { var arr = base64.split(","), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new Blob([u8arr], { type: mime }); }; var base64ToDataURL = base64 => { return window.URL.createObjectURL(base64ToBlob(base64)); }; var blobToBase64 = function blobToBase64(blob) { return new Promise(resolve => { var reader = new FileReader(); reader.onload = function () { resolve(reader.result); }; reader.readAsDataURL(blob); }); }; /* * @Author: Rindy * @Date: 2019-08-06 16:25:08 * @LastEditors: Rindy * @LastEditTime: 2021-08-27 12:33:49 * @Description: Request */ //import { $alert, $confirm, $loginTips } from "@/components/shared/message" //import { $waiting } from "@/components/shared/loading" //import { checkLogin } from "@/api" //import { LoginDetector } from "@/core/starter" //import { i18n } from "@/lang" //import { password } from "@/utils/string" //import store from "../Store" // 空函数 var noop = function noop() {}; // 请求回调队列 var postQueue = []; /** * @property {number} NEXT - 继续执行 * @property {number} SUCCESS - 成功 * @property {number} EXCEPTION - 异常错误 * @property {number} FAILURE_CODE_3001 - 缺少必要参数 * @property {number} FAILURE_CODE_3002 - 访问异常 * @property {number} FAILURE_CODE_3003 - 非法访问 * @property {number} FAILURE_CODE_3004 - 用户未登录 * @property {number} FAILURE_CODE_3005 - 验证码已过期 * @property {number} FAILURE_CODE_3006 - 验证码错误 * @property {number} FAILURE_CODE_3007 - 昵称已存在 * @property {number} FAILURE_CODE_3008 - 该手机已被注册 * @property {number} FAILURE_CODE_3009 - 两次输入的密码不一致 * @property {number} FAILURE_CODE_3010 - 昵称长度错误 * @property {number} FAILURE_CODE_3011 - 密码长度错误 * @property {number} FAILURE_CODE_3012 - 昵称包含敏感词 * @property {number} FAILURE_CODE_3013 - 手机号码格式错误 * @property {number} FAILURE_CODE_3014 - 账号或密码不正确 * @property {number} FAILURE_CODE_3015 - 用户不存在 * @property {number} FAILURE_CODE_3016 - 您没有权限,请联系管理员 * @property {number} FAILURE_CODE_3017 - 空文件 * @property {number} FAILURE_CODE_3018 - 需要上传或使用的文件不存在 * @property {number} FAILURE_CODE_5010 - 找不到该场景对应的相机 * @property {number} FAILURE_CODE_5012 - 数据不正常 * @property {number} FAILURE_CODE_5014 - 无权操作该场景 * @property {number} FAILURE_CODE_5005 - 场景不存在 */ var statusCode = { NEXT: -999, SUCCESS: 0, EXCEPTION: -1, FAILURE_CODE_3001: 3001, FAILURE_CODE_3002: 3002, FAILURE_CODE_3003: 3003, FAILURE_CODE_3004: 3004, FAILURE_CODE_3005: 3005, FAILURE_CODE_3006: 3006, FAILURE_CODE_3007: 3007, FAILURE_CODE_3008: 3008, FAILURE_CODE_3009: 3009, FAILURE_CODE_3010: 3010, FAILURE_CODE_3011: 3011, FAILURE_CODE_3012: 3012, FAILURE_CODE_3013: 3013, FAILURE_CODE_3014: 3014, FAILURE_CODE_3015: 3015, FAILURE_CODE_3016: 3016, FAILURE_CODE_3017: 3017, FAILURE_CODE_3018: 3018, FAILURE_CODE_5010: 5010, FAILURE_CODE_5012: 5012, FAILURE_CODE_5014: 5014, FAILURE_CODE_5005: 5005 }; /** * 获取Token * @function */ function getToken() { var urlToken = browser.urlHasValue("token", true); if (urlToken) { // 设置token共享给用户中心 localStorage.setItem("token", urlToken); } return urlToken || localStorage.getItem("token") || ""; } /** * 根据状态码的结果处理后续操作 * @function * @param {number} code - 状态码 * @param {function} callback - 回调 */ function statusCodesHandler(code, callback) { if (code == statusCode.EXCEPTION) { return $alert({ content: i18n.t("tips.exception") }); } if (code == statusCode.FAILURE_CODE_3002 || code == statusCode.FAILURE_CODE_3003 || code == statusCode.FAILURE_CODE_3004) { callback(code); return showLoginTips(); } if (code == statusCode.FAILURE_CODE_3001) { callback(code); return $alert({ content: i18n.t("tips.params_notfound") }); } if (code == statusCode.FAILURE_CODE_3017) { callback(code); return $alert({ content: i18n.t("tips.file_notfound") }); } if (code == statusCode.FAILURE_CODE_5005) { /* if (!config.isEdit) { return (location.href = config.pages.NotFound) } */ callback(code); return $alert({ content: i18n.t("tips.scene_notfound") }); } if (code == statusCode.FAILURE_CODE_5010) { callback(code); return $alert({ content: i18n.t("tips.camera_notfound") }); } if (code == statusCode.FAILURE_CODE_5012) { callback(code); return $alert({ content: i18n.t("tips.data_error") }); } if (code == statusCode.FAILURE_CODE_5014) { callback(code); return $alert({ content: i18n.t("tips.auth_deny") }); } return statusCode.NEXT; } $.ajaxSetup({ headers: {}, beforeSend: function beforeSend(xhr) { var token = getToken(); if (token) { xhr.setRequestHeader("token", token); } else if (!token && this.url.indexOf("isLogin") != -1) { showLoginTips(); } /* if (config.oem == "localshow") { // 本地版本兼容当前目录 if (this.url.indexOf("http") == -1 && this.url.indexOf("/") == 0) { this.url = this.url.substr(1) } } */ // if(this.url.indexOf('http')==-1 && this.url.indexOf('/') !=0){ // this.url = '/'+this.url // } }, error: function error(xhr, status, _error) { // 出错时默认的处理函数 if (this.url.indexOf("/scene.json") != -1 && xhr.status == 404) { return $alert({ content: i18n.t("tips.scene_notfound") }); } else if (this.type === "POST") { return $alert({ content: i18n.t("tips.network_error") }); } }, success: function success(result) {}, complete: function complete() { // Post类型请求无论成功或失败都关闭等待提示 if (this.type === "POST") { http.__loading && $waiting.hide(); } http.__loading = true; } }); /** * @namespace http * @type {Object} */ var http = { statusCode, __loading: true, __request(xhr, method, url, data, done, fail) { if (typeof done != "function") { done = noop; } if (typeof fail != "function") { fail = noop; } xhr.done(result => { if (typeof result.code !== "undefined") { var flag = statusCodesHandler(result.code, function (code) { // 需要登录的状态 if (code == statusCode.FAILURE_CODE_3001 || code == statusCode.FAILURE_CODE_3002 || code == statusCode.FAILURE_CODE_3003 || code == statusCode.FAILURE_CODE_3004) { if (url.indexOf("isLogin") == -1 && url.indexOf("openSceneBykey") == -1) { postQueue.push(function () { http[method](url, data, done, fail); }); } } fail(); }); if (flag === statusCode.NEXT) { done(result, result.code == 0); } } else { done(result); } }); xhr.fail(fail); xhr.always(() => xhr = null); return xhr; }, /** * Get请求 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ get(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; if (/\.json/.test(url)) { // json文件格式自动调用getJson方法 return this.getJson(url, data, done, fail); } return this.__request($.get(url, data), "get", url, data, done, fail); }, /** * Get Blob请求 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ getText(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; return this.__request($.ajax({ url: url, dataType: "text" }), "getText", url, data, done, fail); }, /** * GetJson请求 读取json文件数据 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ getJson(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; return this.__request($.getJSON(url, data), "get", url, data, done, fail); }, /** * Get Blob请求 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ getBlob(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; return this.__request($.ajax({ url: url, dataType: "blob" }), "getBlob", url, data, done, fail); }, /** * Get Arraybuffer请求 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ getArraybuffer(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; return this.__request($.ajax({ url: url, dataType: "arraybuffer" }), "getArraybuffer", url, data, done, fail); }, /** * Post 请求 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ post(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; if (url.indexOf("isLogin") == -1) { http.__loading && $waiting.show(); } return this.__request($.post(url, data), "post", url, data, done, fail); }, /** * PostJson 请求 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ postJson(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; http.__loading && $waiting.show(); return this.__request($.ajax({ type: "POST", url: url, contentType: "application/json", data: JSON.stringify(data) }), "postJson", url, data, done, fail); }, /** * Post 表单 支持文件上传 * @param {String} url 请求地址 * @param {FormData?} formData 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ postForm(url, formData, done, fail, onProgress) { if (typeof onProgress === "function") { return this.__request($.ajax({ type: "POST", url: url, processData: false, contentType: false, data: formData, xhr: function xhr() { var xhr = new XMLHttpRequest(); xhr.upload.addEventListener("progress", function (e) { onProgress(e.loaded / e.total * 100 + "%"); }); return xhr; } }), "postForm", url, formData, done, fail); } else { http.__loading && $waiting.show(); return this.__request($.ajax({ type: "POST", url: url, processData: false, contentType: false, data: formData }), "postForm", url, formData, done, fail); } }, /** * 加载图片 * @param {String} url 请求地址 * @param {Number?} retry 重试次数,默认为3 */ loadImage(url) { var retry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3; var def = $.Deferred(); var img = new Image(); /* if (process.env.VUE_APP_REGION == "AWS" && url.indexOf("x-oss-process=image") != -1) { var arr = url.split("?") url = arr[0] + encodeURIComponent("?" + arr[1].replace(/\//g, "@")) } */ var load = () => { console.warn("Retrying load image: " + url); this.loadImage(url, retry - 1).done(def.resolve.bind(def)).progress(def.notify.bind(def)).fail(def.reject.bind(def)); }; img.onerror = function () { retry > 0 ? setTimeout(() => load(), 1e3) : def.reject("[".concat(url, "]\u52A0\u8F7D\u5931\u8D25")); }; img.onload = function () { def.resolve(img); }; img.crossOrigin = "anonymous"; img.src = url; return def; }, /** * 上传文件 * @param {String} url 请求地址 * @param {Object?} data 请求参数 * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ uploadFile(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; var onProgress = arguments.length > 4 ? arguments[4] : undefined; var form = new FormData(); // if (file.needTransfer) { //ie和苹果都不支持dataURLtoFile得传送,所以只能用blob // form.append("file", common.dataURLtoBlob(file.file), file.name || file.file.name); // } else { // form.append("file", file.file, file.name || file.file.name); // } for (var key in data) { if (key == "file") { form.append("file", data[key], data.filename || data[key].name); } else if (key != "filename") { form.append(key, data[key]); } } return this.postForm(url, form, done, fail, onProgress); }, /** * 上传文件 * @param {String} url 请求地址 * @param {Object?} data 请求参数 {file:'base64 string',filename:'image.jpg',...} * @param {Function?} done 成功回调 * @param {Function?} fail 失败回调 */ uploadBlobFile(url) { var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var done = arguments.length > 2 ? arguments[2] : undefined; var fail = arguments.length > 3 ? arguments[3] : undefined; var form = new FormData(); for (var key in data) { if (key === "file") { form.append("file", base64ToBlob(data.file), data.filename); } else if (key != "filename") { form.append(key, data[key]); } } return this.postForm(url, form, done, fail); } }; var { TileDownloaderEvents, DownloadStatus: DownloadStatus$1 } = Potree.defines; window.downloaded = {}; window.startdownloads = []; class TileDownloader extends EventDispatcher { constructor() { super(); this.panos = null; this.retryMinimumTime = 1e4; this.panoLoadCallbacks = {}; this.downloadDescriptors = {}; this.priorityQueue = []; this.forceQueue = []; this.activeDownloads = []; this.tilePrioritizer = null; this.refreshInterval = null; this.processPriorityQueue = !1; this.concurrentDownloads = 6; //e.concurrentDownloads || 1; this.downloadTestResults = {}; this.freeze = Object.freeze({ Testing: 1, Success: 2, Fail: 3 }); this.tilesCount = 0; //add 加载好的tile数目 /* viewer.addEventListener('pageVisible', (e)=>{//不可见时不refreshUpdateInterval //console.log('visibilitychange:', state) Potree.Utils.updateVisible(this, 'pageVisible', e.v) this.judgeStart() }) this.visible = true //add 借用Potree.Utils.updateVisible来判断是否start if(Potree.settings.useDepthTex){ this.judgeStart() //开始下载depthTex }else{ Potree.Utils.updateVisible(this,'showPanos', false ) //默认visible = false } */ } setPanoData(e, t /* , i */) { this.panos = e, this.imagePanos = t; // this.panoGroupId = i } start() { this.downloadCubeTex = true; /* if(!Potree.settings.useDepthTex){ Potree.Utils.updateVisible(this,'showPanos', true ) this.judgeStart() }else{ //this.refreshInterval || this.judgeStart() } */ } stop() { this.downloadCubeTex = false; /* if(!Potree.settings.useDepthTex){ Potree.Utils.updateVisible(this,'showPanos', false ) this.judgeStart() } */ } /* judgeStart(){//add if(this.visible){ //console.log('judgeStart true') this.started = true //this.refreshUpdateInterval(0) }else{ //console.log('judgeStart false') this.started = false //window.clearTimeout(this.refreshInterval) } } */ /* refreshUpdateInterval(e) { e || (e = 0), this.refreshInterval = window.setTimeout(function() { var e = this.update(); e ? this.refreshUpdateInterval(TileDownloader.ACTIVE_REFRESH_DELAY) : this.refreshUpdateInterval(TileDownloader.IDLE_REFRESH_DELAY) } .bind(this), e) } */ update() { if (!this.panos) return; var time = Potree.Common.getBestCountFPS('processPriorityQueue', false, 700, 66); if (this.downloadCubeTex) { //可以下载贴图 var e = this.forceQueue.length > 0; this.processQueueForDownloading(this.forceQueue); if (this.processPriorityQueue) { Potree.Common.intervalTool.isWaiting('processPriorityQueue', () => { //延时update,防止崩溃 , 未到时间就拦截(第一次直接执行) this.downloadCubeTex && this.queuePrioritizedTilesForPanos(this.panos); //这句比较耗时 降四倍时大概1-2毫秒 }, time); this.priorityQueue.length > 0 && (e = !0); this.processQueueForDownloading(this.priorityQueue); } //return e } else if (viewer.scene.pointclouds.some(e => e.hasDepthTex)) { //不加载贴图也要获得nearPanos viewer.lastFrameChanged && Potree.Common.intervalTool.isWaiting('processPriorityQueue2', () => { this.tilePrioritizer.getNearPanos(this.panos); }, time); } var time2 = 1000; //Potree.Common.getBestCountFPS('filterDepthTex', false, 5000, 88, 2, 63 ) Potree.Common.intervalTool.isWaiting('filterDepthTex', () => { this.tilePrioritizer.filterDepthTex(this.panos); //下载深度图 }, time2); } queuePrioritizedTilesForPanos(e) { viewer.addTimeMark('queuePrioritizedTilesForPanos', 'start'); this.tilePrioritizer && (this.clearQueue(this.priorityQueue), this.tilePrioritizer.filterAndPrioritize(this.priorityQueue, e, this), this.clearFromQueue(this.priorityQueue, DownloadStatus$1.None, !0), //去除state为DownloadStatus.None的(可能是去除已经在下载的) this.setStatusOrRemoveForAllDescriptors(this.priorityQueue, DownloadStatus$1.Queued)); viewer.addTimeMark('queuePrioritizedTilesForPanos', 'end'); } clearQueue(e) { //停止下载并清空 this.setStatusForAllDescriptors(e, DownloadStatus$1.None), e.length = 0; } clearForceQueue() { this.clearQueue(this.forceQueue); } clearFromQueue(e, t, i) { for (var n = 0; n < e.length; n++) { var r = e[n]; r && (t === r.status && !i || t !== r.status && i) && (e[n] = null); } } setStatusForAllDescriptors(e, t) { for (var i = 0; i < e.length; i++) { var n = e[i]; n && (n.status = t); } } setStatusOrRemoveForAllDescriptors(e, t) { for (var i = 0; i < e.length; i++) { var n = e[i]; n && (n.status !== t ? n.status = t : e[i] = null); } } getTileDownloadDescriptors(pano, size) { //获取该pano的该size的全部的tile的descriptor var i = this.getAllTileDownloadDescriptorsForPano(pano), n = i[size]; return n || (n = this.buildDownloadDescriptorArray(size), //创建的全部是空的 i[size] = n, this.initTileDownloadDescriptors(n, pano, size)), //绑定到该pano size n; } getAllTileDownloadDescriptorsForPano(pano) { //新建空Descriptors var t = this.downloadDescriptors[pano.id]; return t || (t = {}, this.downloadDescriptors[pano.id] = t), t; } processQueueForDownloading(e, t) { //执行下载任务 this.cleanupActiveDownloads(); if (e.length) { var concurrentDownloads = Potree.Common.getBestCount('concurrentDownloads', 1, 6, 1.8, 13 /* ,true */); if (this.activeDownloads.length < concurrentDownloads || t) { var i = t ? e.length : concurrentDownloads - this.activeDownloads.length; for (var n = 0, r = 0; n < i && e.length > 0; r++) { var o = e.shift(); if (o) { //add 为了防止1024的在512前下载完,这里强行等待512下载完毕再开始下载 if (o.panoSize > 512 && !this.isPanoDownloaded(o.pano, 512)) { //console.log('512的还没下载好呢!') e.push(o); break; //一般512的都是连续下载的,所以后面就都不是512了直接中断 } this.startDownload(o); n++; } } } } } testDownload(panoSize, tileSize, callback) { var n = this.downloadTestResults[panoSize]; if (n) return void (n === this.freeze.Success ? callback(!0) : n === this.freeze.Fail && callback(!1)); this.downloadTestResults[panoSize] = this.freeze.Testing; var r = this.panos[0], o = this.getTileUrl({ pano: r, panoSize, tileSize, tileIndex: 0 } /* r.id, panoSize, tileSize, 0 */), a = function (t) { this.downloadTestResults[panoSize] = this.freeze.Success, callback(!0); }.bind(this), s = function () { this.downloadTestResults[panoSize] = this.freeze.Fail, callback(!1); }.bind(this); this.loadImage(o, 0, a, s); } startDownload(e) { //开始下载啦 startdownloads.push(e); e.local2SrcFailed = this.local2SrcFailed; e.status = DownloadStatus$1.Downloading; var t = this.getTileUrl(e /* e.pano.id, e.panoSize, e.tileSize, e.tileIndex, e.pano.alignmentType */); //xzw add alignmentType if (!t) return; this.activeDownloads.push(e); this.loadImage(t, TileDownloader.DOWNLOAD_RETRIES, this.downloadComplete.bind(this, e), this.downloadFailed.bind(this, e)); } downloadFailed(e, t) { //add if (Potree.settings.isLocal2 && !e.local2SrcFailed) { //为了兼容旧的数据src,如果新src没加载成功,就加载旧的 e.local2SrcFailed = this.local2SrcFailed = true; //this.startDownload(e)//重新下载 var t = this.getTileUrl(e); this.loadImage(t, TileDownloader.DOWNLOAD_RETRIES, this.downloadComplete.bind(this, e), this.downloadFailed.bind(this, e)); } } downloadComplete(e, t) { //下载成功时 //if (e.panoGroupId === this.panoGroupId) { var i = this.getPanoLoadCallbacks(e.pano, e.panoSize); e.status = DownloadStatus$1.Downloaded, i && i.onProgress && i.onProgress(e.pano, e.panoSize); var n = { panoId: e.pano.id, image: t, tileSize: e.tileSize, panoSize: e.panoSize, tileIndex: e.tileIndex, faceTileIndex: e.faceTileIndex, totalTiles: e.totalTiles, face: e.face, tileX: e.tileX, tileY: e.tileY, direction: e.direction }; downloaded[e.pano.id] || (downloaded[e.pano.id] = { 512: [], 1024: [], 2048: [] }); downloaded[e.pano.id][e.panoSize] || (downloaded[e.pano.id][e.panoSize] = []); downloaded[e.pano.id][e.panoSize].push(e); if (e.panoSize != 512 && downloaded[e.pano.id][512].length < 6) { console.warn('没下完'); } this.tilesCount++; //add e.image = t, this.dispatchEvent({ type: TileDownloaderEvents.TileDownloadSuccess, desc: n }); this.isPanoDownloaded(e.pano, e.panoSize) && (n = { panoId: e.pano.id, tileSize: e.tileSize, panoSize: e.panoSize }, this.dispatchEvent({ type: TileDownloaderEvents.PanoDownloadComplete, desc: n }), i && i.onLoad && i.onLoad(e.pano, e.panoSize)); //} } isPanoDownloaded(e, t) { var i = this.getTileDownloadDescriptors(e, t); if (i.length <= 0) return !1; for (var n = 0; n < i.length; n++) { var r = i[n]; if (r.status !== DownloadStatus$1.Downloaded) return !1; } return !0; } setPanoLoadCallbacks(e, t, i, n, r) { var o = e.id + ":" + this.qualityManager.getPanoSize(t); this.panoLoadCallbacks[o] = { onLoad: i, onFail: n, onProgress: r }; } getPanoLoadCallbacks(e, t) { var i = e.id + ":" + t; return this.panoLoadCallbacks[i]; } buildDownloadDescriptorArray(e) { for (var t = TileUtils.getTileCountForSize(e), i = [], n = 0; n < t; n++) { var r = this.buildDownloadDescriptor(); i.push(r); } return i; } buildDownloadDescriptor() { //Descriptor! var e = { panoGroupId: null, pano: null, panoSize: -1, tileSize: -1, tileIndex: -1, totalTiles: -1, faceTileIndex: -1, status: DownloadStatus$1.None, url: null, image: null, direction: new Vector3(), //该tile在cube中的方向 face: -1, cubeFace: -1, tileX: -1, tileY: -1 }; return e; } initTileDownloadDescriptors(e, t, i) { for (var n = 0; n < e.length; n++) { var r = e[n]; this.initTileDownloadDescriptor(r, t, i, n); } } initTileDownloadDescriptor(desc, pano, size, index) { var r = size >= TileUtils.TILE_SIZE ? TileUtils.TILE_SIZE : size; desc.face = TileUtils.getFaceForTile(size, index); //根据顺序得到的face的index desc.cubeFace = TileUtils.mapFaceToCubemapFace(desc.face); //为了贴图而转化的face index //desc.panoGroupId = this.panoGroupId;//就是场景号 desc.pano = pano; desc.panoSize = size; desc.tileSize = r; //瓦片图size 512 desc.tileIndex = index; desc.totalTiles = TileUtils.getTileCountForSize(size); desc.status = DownloadStatus$1.None; desc.image = null; TileUtils.getTileLocation(desc.panoSize, desc.tileIndex, desc); //得到该tile在这个face中的具体位置(tileX等) TileUtils.getTileVector(desc.panoSize, desc.tileSize, desc.cubeFace, desc.tileX, desc.tileY, TileUtils.LocationOnTile.Center, 0, desc.direction); } getTiles(d, sceneNum, useV4url, pointcloud) { var v3OrV4Str = useV4url ? "/scene_view_data/".concat(sceneNum, "/images/") : "/images/images".concat(sceneNum, "/"); var prefix = Potree.settings.urls.getPrefix(3, pointcloud); //Potree.settings.urls.panoOssRoot || pointcloud.prop?.raw?.panoOssRoot || Potree.settings.urls.prefix3 if (Potree.settings.isLocal && !this.local2SrcFailed && pointcloud.datasetData.mapping && !Potree.settings.isLocal2) { //非离线包的话加mapping return "".concat(prefix, "/").concat(pointcloud.datasetData.mapping).concat(v3OrV4Str).concat(d); } return "".concat(prefix).concat(v3OrV4Str).concat(d); /* if(Potree.settings.isLocal && !this.local2SrcFailed || useV4url){//新的地址 scene_view_data/场景码/images/tiles if(pointcloud.datasetData.mapping && !Potree.settings.isLocal2){//非离线包的话加mapping return `${Potree.settings.urls.prefix3}/${pointcloud.datasetData.mapping}/scene_view_data/${sceneNum}/images/${d}` }else{ return `${Potree.settings.urls.prefix3}/scene_view_data/${sceneNum}/images/${d}` } } return `${Potree.settings.urls.prefix3}/images/images${sceneNum}/${d}` */ } loadImage(e, t, i, n) { //自己修改了ajax,把getImage改成了loadImg http.loadImage(e, t).then(function (e) { i(e); }).fail(n); } } TileDownloader.prototype.forceQueueTilesForPano = function () { //根据条件开始加载tile var e = [], t = []; return function (pano, size, dir, hFov, vFov, download) { e.length = 0; for (var u = this.getTileDownloadDescriptors(pano, size), d = 0; d < u.length; d++) { var p = u[d]; p.status !== DownloadStatus$1.None && p.status !== DownloadStatus$1.Queued || e.push(p); } //挑出没下载的开始下载 if (dir && e.length > 0) { TilePrioritizer.sortPanoTiles(e, pano, dir); //按最佳方向排序e t.length = 0; TileUtils.matchingTilesInDirection(pano, size, dir, hFov, vFov, t); //得到在符合视野标准的集合t for (var f = 0, g = function g(e) { return e.face === m.face && e.faceTileIndex === m.faceTileIndex; }; f < e.length;) { //过滤掉不符合角度要求的 var m = e[f], v = t.findIndex(g); v < 0 ? e.splice(f, 1) : f++; } } for (var A = 0; A < e.length; A++) { this.forceQueue.push(e[A]); //装载 } /* if(e.length){ console.log(e) } */ this.setStatusForAllDescriptors(this.forceQueue, DownloadStatus$1.ForceQueued); this.clearFromQueue(this.priorityQueue, DownloadStatus$1.ForceQueued, !1); download && this.processQueueForDownloading(this.forceQueue, !0); }; }(); TileDownloader.prototype.cleanupActiveDownloads = function () { var e = []; return function () { e.length = 0; for (var t = 0; t < this.activeDownloads.length; t++) { var i = this.activeDownloads[t]; i.status !== DownloadStatus$1.Downloaded && i.status !== DownloadStatus$1.Failed && e.push(i); } this.activeDownloads.length = 0, this.activeDownloads.push.apply(this.activeDownloads, e); }; }(); TileDownloader.prototype.getTileUrl = function () { var e = { 256: "256", 512: "512", 1024: "1k", 2048: "2k", 4096: "4k" }, t = { face: -1, faceTileIndex: -1, tileX: -1, tileY: -1 }; return function () { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var id = o.pano.originID, //////// panoSize = o.panoSize, tileSize = o.tileSize, tileIndex = o.tileIndex, sceneCode = o.pano.pointcloud.sceneCode, useV4url = Potree.settings.useV4url && (!o.pano.pointcloud.datasetData || o.pano.pointcloud.datasetData.sceneVersion == 'V4'); //v4的全景图等路径不一样 var resolution = o.pano.pointcloud.tileRes || '4k'; var metadata = { sceneScheme: 10 }; TileUtils.getTileLocation(panoSize, tileIndex, t); var s = Math.floor(panoSize / tileSize), l = s * s, h = Math.floor(tileIndex / l), u = "", d = '', g = ''; if (Potree.settings.tileOriginUrl) { //原始规则 //1 === config.tiling.customCompression && (u = "_" + config.tiling["q" + e[panoSize]]); //1 === o.tiling.customCompression && (u = "_" + o.tiling["q" + e[n]]); d = "tiles/" + id + "/" + e[panoSize] + u + "_face" + h + "_" + t.tileX + "_" + t.tileY + ".jpg"; d = this.getTiles(d, sceneCode, useV4url, o.pano.pointcloud); g = "?"; } else { //阿里云oss的规则 if (metadata.sceneScheme == 10) d = 'tiles/' + resolution + '/' + id + '_skybox' + h + '.jpg?x-oss-process='; if (e[panoSize] == '512') { d += 'image/resize,h_512'; } else { //4k的图,移动端是1k,pc端是2k,放大才是4k if (e[panoSize] == '1k' || e[panoSize] == '2k') { //https://4dkk.4dage.com/images/imagesx4iqYDG3/tiles/4k/122_skybox0.jpg?x-oss-process=image/resize,m_lfit,w_1024/crop,w_512,h_512,x_511,y_0 d += 'image/resize,m_lfit,w_' + panoSize + '/crop,w_512,h_512,'; } else { d = 'tiles/' + resolution + '/' + id + '_skybox' + h + '.jpg?x-oss-process=image/crop,w_512,h_512,'; } //起始位置 if (t.tileX == 0) { d += 'x_0,'; } else { d += 'x_' + 512 * t.tileX /* - 1 */ + ','; } if (t.tileY == 0) { d += 'y_0'; } else { d += 'y_' + 512 * t.tileY /* - 1 */; } } d = this.getTiles(d, sceneCode, useV4url, o.pano.pointcloud); g = "&"; } //Potree.settings.panoVersion = 4 d += g + (Potree.settings.panoVersion ? 'version=' + Potree.settings.panoVersion : 'time=' + o.pano.pointcloud.timeStamp); //加后缀 return d; /* 4dkk getViewImagesURL(url) { if (url.indexOf('&_=') !== -1) { return url } let version = url.indexOf('/panorama/') !== -1 ? this.linkVersion : this.imageVersion if (url.indexOf('?') !== -1) { return this.$app.config.resource + `scene_view_data/${this.num}/images/${url}&_=${version}` } return this.$app.config.resource + `scene_view_data/${this.num}/images/${url}?_=${version}` } /** */ }; }(); TileDownloader.tilegen = true; TileDownloader.IDLE_REFRESH_DELAY = 500; TileDownloader.ACTIVE_REFRESH_DELAY = 16; TileDownloader.DOWNLOAD_RETRIES = 4; function Node$1(e, t) { this.tree = e, //所属树(TileTree) this.parent = t, this.children = [], this.id = ++u$1; } function o(e, t, i, r, a, s, l, h) { if (e) { l = l || TileTree.TraversalType.PreOrder; var u = r * c + i; if (l === TileTree.TraversalType.PreOrder && (a && a(e, t, u, i, r), s && s.push(e)), e.children && 0 !== e.children.length) { for (var d = r * c, p = i * c, f = 0; f < c; f++) for (var g = 0; g < c; g++) o(e.children[g * c + f], t + 1, p + f, d + g, a, s, l, h); l === TileTree.TraversalType.PostOrder && (a && a(e, t, u, i, r), s && s.push(e)); } } } function Plant(seed) { seed.root = Branch(seed, null, 0); } function Branch(seed, parent, level) { if (level > seed.levels) return null; var node = new Node$1(seed, parent); seed.allNodes.push(node); for (var o = 0; o < h$1; o++) node.children[o] = Branch(seed, node, level + 1); return node; } function l(parent, t, level, n, r) { if (!parent) return null; if (0 === level) return parent; if (!parent.children || 0 === parent.children.length) return null; var o = Math.pow(c, level), a = o / c, s = n % a, h = r % a, u = Math.floor(r / a), d = Math.floor(n / a), p = u * c + d, f = parent.children[p]; return l(f, t + 1, level - 1, s, h); } /* cube每个面都有一个分层树 用于代表瓦片图的细分? 树4096的分为三层,每层有4个子节点。(最后一层的四个子节点都是null) */ var c = 2, h$1 = c * c; //4个子节点 var u$1 = 0; class TileTree { constructor(e, t) { this.levels = t, this.tileSize = e, this.root = null, this.allNodes = [], Plant(this); } getSubNode(e, t, i) { (!t || e < this.tileSize) && (t = 0), (!i || e < this.tileSize) && (i = 0), e < this.tileSize && (e = this.tileSize); var level = TileTree.getLevelCountForSize(this.tileSize, e), o = l(this.root, 0, level, t, i); return o; } breadthFirst(e) { //广度优先搜索 e = e || {}; var t = !!e.nullLevelEnd, i = e.maxLevel, n = e.minLevel, r = e.callback, o = e.saveVisited, a = [], s = {}, l = 0, c = 0; for (a.push(this.root), a.push(s); a.length > 0 && !(i && l > i);) { var h = a.shift(); if (h === s) (!n || l >= n) && (r && t && r(null), o && t && o.push(null)), a.length > 0 && a.push(s), l++, c = 0;else { if (h.children) for (var u = 0; u < h.children.length; u++) { var d = h.children[u]; d && a.push(h.children[u]); } var p = this.getFaceIndexFromNode(h); (!n || l >= n) && (r && r(h, l, p), o && o.push(h)), c++; } } } getFaceIndexFromNode(e) { if (!e) return -1; for (var t = 1, i = e, n = 0, r = 0;;) { var o = i.parent; if (!o) break; for (var a = -1, s = 0; s < o.children.length; s++) o.children[s] === i && (a = s); var l = a % c, h = Math.floor(a / c); n = l * t + n, r = h * t + r, t *= c, i = o; } return r * t + n; } depthFirst(e, t, i) { o(this.root, 0, 0, 0, e, t, i, this.tileSize); } } TileTree.TraversalType = Object.freeze({ PreOrder: 0, PostOrder: 1 }); TileTree.getLevelCountForSize = function (tileSize, size) { //512->0 2024->1 var i = 0; for (size < tileSize && (size = tileSize);;) { if (size /= c, size < tileSize) break; i++; } return i; }; TileTree.getSizeForLevel = function (e, t) { return Math.pow(c, t) * e; }; var { PanoSizeClass: PanoSizeClass$2, PanoRendererEvents: PanoRendererEvents$1, Vectors, SceneRendererEvents, TileDownloaderEvents: TileDownloaderEvents$1, GLCubeFaces: GLCubeFaces$2 } = Potree.defines; function createDescriptor() { var e = { renderTarget: null, inUse: !1, size: -1, pano: null }; return e; } /* function upload() { if (!this.uploadIntervalCancelled) { if (this.overlayTilesLoaded || !this.usingTileOverlay) { b = !0 let maxNonBaseUploadsPerFrame = viewer.mainViewport.view.isFlying() ? 1 : this.maxNonBaseUploadsPerFrame //原先2。这是每帧uploadTile非512的瓦片tex的数量。之前的2太卡了,降为1。(检测卡顿方法:在一个pano点旋转至所有2048的tile都加载完,然后之后到这个点看看卡不卡。因为该点tiles都下载完了所以会在飞过来时陆续都加载,所以容易卡) this.updateUploadQueue(maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame) let time = viewer.mainViewport.view.isFlying() ? 60 : w //add 飞行有时候会卡,增长间隔 this.peekNextFromUploadQueue() ? this.refreshUploadInterval(time) : this.uploadInterval = null //定时下一次更新 } else { this.refreshUploadInterval(this.uploadIntervalDelay) } } } */ var b = !1, w = config$1.tiling.uploadIntervalDelay, _ = config$1.tiling.initialIntervalDelay, T = config$1.tiling.maxNonBaseUploadsPerFrame, //2 size>512的每次只upload这么多个 x$1 = config$1.tiling.maxBaseUploadsPerFrame, //6 S = { Base: 0, Remaining: 1 }; /* , M = []; */ class PanoRenderer extends EventDispatcher { constructor(viewer, tileDownloader, qualityManager) { super(); this.tileDirectory = {}; this.activeRenderTargetDescriptors = {}; this.activePanos = []; this.panoLODDescriptors = {}; this.panoDescriptors = {}; this.tileTrees = {}; this.forceQueue = []; this.uploadQueues = {}; this.uploadInterval = null; this.uploadIntervalCancelled = true; //!1; this.usingTileOverlay = !1; this.overlayTilesLoaded = !1; this.overlayTileBase = null; this.overlayTilesBasic = {}; this.overlayTilesEnhanced = {}; this.zoomRenderTarget = null; //用于缩放的rendertarget this.zoomPano = null; this.zoomingActive = !1; this.zoomPanoId = null; this.zoomPanoRenderingDisabled = !1; this.direction = []; //new THREE.Vector3; this.maxBaseUploadsPerFrame = x$1; this.maxNonBaseUploadsPerFrame = T; this.M = []; //move M to here 似乎列表里会有两个 this.viewer = viewer; this.tileDownloader = tileDownloader; this.qualityManager = qualityManager; this.initTime = performance.now(); this.bindEvents(); } getActivePanoTextures(e) { e = e || []; for (var t = 0; t < M.length; t++) { var i = M[t]; i.renderTarget && i.renderTarget.texture && e.push(i.renderTarget.texture); } } hasQueuedTiles() { var e = this.peekNextFromUploadQueue(); return null !== e && void 0 !== e; } getActiveRenderTargetDescriptor(e) { return this.activeRenderTargetDescriptors[e]; } setActiveRenderTargetDescriptor(e, t) { this.activeRenderTargetDescriptors[e] = t; } bindEvents() { this.tileDownloader.addEventListener(TileDownloaderEvents$1.TileDownloadSuccess, this.onTileDownloaded.bind(this)); } enableUltraHighQualityMode(e) { if (config$1.tileClass == "2k" || config$1.tileClass == "1k") return this.enableHighQuality(e); //xzw add 濡傛灉鏈€澶氬彧瑕?k鐨勮瘽 if (!this.qualityManager.ultraHighQualityModeEnabled()) { var t = this.qualityManager.getPanoSize(PanoSizeClass$2.ULTRAHIGH); this.tileDownloader.testDownload(t, TileUtils.TILE_SIZE, function (t) { t && (this.qualityManager.enableUltraHighQualityMode(), this.setupZoomRenderTarget(), e()); }.bind(this)); } } activateTiledPano(pano, size, i) { i && this.clearAllQueuedUploads(); for (var n = 0; n < TileUtils.FACES_PER_PANO; n++) this.initTileTree(pano.id, n, this.qualityManager.getMaxPossiblePanoSize()); //得到this.tileTrees[pano.id],arr[6] this.linkAllTilesAndNodes(pano); var r = this.getActiveRenderTargetDescriptor(pano.id), l = size; l > this.qualityManager.getMaxNavPanoSize() && (l = this.qualityManager.getMaxNavPanoSize()); if (!r || l !== r.size) { r && this.deactiveDescripor(r.renderTarget); r = this.activeDescripor(l); if (!r) { var ren = this.initTiledPano(l, Potree.settings.disableAATarget ? false : !browser.isMobile()); //有的老设备如果抗锯齿打开场景时要多花五秒 r = this.initDescriptor(ren.width); r.renderTarget = ren; } r.pano = pano; this.resetPanoDescriptor(pano.id); this.resetPanoLODDescriptors(pano.id); this.resetRenderStatus(pano.id, !0, !0); } this.setActiveRenderTargetDescriptor(pano.id, r); var h = i ? 0 : 1; this.updateActivePanos(pano, h); //console.log(`index:${this.viewer.index} ${r.renderTarget.texture.id} ${pano.id}`) return r.renderTarget; } deactivateTiledPano(pano) { var t = this.getActiveRenderTargetDescriptor(pano.id); if (this.isRenderTargetDescriptorValid(t)) { this.deactiveDescripor(t.renderTarget); this.setActiveRenderTargetDescriptor(pano.id, null); } var i = this.getUploadQueueForPano(pano.id); this.clearUploadQueue(i); this.updateActivePanos(); viewer.cancelLoad(pano); //add } getActivePanoCount() { return this.activePanos.length; } resetRenderStatus(e, t, i, n) { var r = null; n && (r = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, n) + 1); for (var o = function o(e, n, r, _o) { i && (n.tile.zoomUploaded = !1), t && (n.tile.uploaded = !1); }, a = 0; a < TileUtils.FACES_PER_PANO; a++) { var s = this.getTileTree(e, a); s.breadthFirst({ callback: o.bind(this, a), minLevel: r }); } } copyBaseRenderStatusToZoomed(e) { for (var t = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, this.qualityManager.getMaxNavPanoSize()), i = function i(e, t, _i, n) { t.tile.zoomUploaded = t.tile.uploaded, t.zoomCovered = t.covered; }, n = 0; n < TileUtils.FACES_PER_PANO; n++) { var r = this.getTileTree(e, n); r.breadthFirst({ callback: i.bind(this, n), maxLevel: t }); } } isRenderTargetDescriptorValid(e) { return e && e.renderTarget; } isPanoActive(e) { var t = this.getActiveRenderTargetDescriptor(e); return this.isRenderTargetDescriptorValid(t); } isPanoZoomed(e) { return this.zoomingActive && this.zoomPanoId === e; } initTileTree(e, t, i) { var n = this.tileTrees[e]; n || (n = [], this.tileTrees[e] = n); var r = n[t]; if (!r) { var o = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, i); r = new TileTree(TileUtils.TILE_SIZE, o), n[t] = r; } } getTileTree(e, t) { var i = this.tileTrees[e]; if (!i) console.error("PanoRenderer.getTileTree() -> Tree array not yet initialized!"); var n = i[t]; if (!n) console.error("PanoRenderer.getTileTree() -> Tree not yet initialized!"); return n; } /* * 创建tile的renderTarget, 包括pano.tiledPanoRenderTarget和zoomRenderTarget * @param {number} size 当前的panoSize,每个面的分辨率 */ initTiledPano(size, ifNormalFilter) { //创建 RenderTargetCube var renderer = this.viewer.renderer; var renderTarget, texture; renderTarget = new WebGLCubeRenderTarget(size, { //THREE.WebGLRenderTargetCube(size, size, { stencilBuffer: !1 }); texture = new CubeTexture([]); texture.image = [null, null, null, null, null, null]; texture.flipY = !0, texture.format = RGBAFormat; ifNormalFilter ? (texture.generateMipmaps = !0, texture.magFilter = LinearFilter, texture.minFilter = LinearMipMapLinearFilter) : (texture.generateMipmaps = !1, texture.magFilter = LinearFilter, //LinearFilter更清晰,但锯齿噪点严重,相当于锐化,其实失真了。对于条纹状的画面移动镜头时锯齿明显 眩晕 texture.minFilter = LinearFilter); //平时还是直接用LinearMipMapLinearFilter,其实并非不清晰,只是没有加锐化,像加了层柔光和抗锯齿,观感更好。放大后使用LinearFilter // 如果抗锯齿的话,采用mipmap,会增加一倍的存储消耗。原版本都是不抗锯齿的。但是抗锯齿效果更柔和 renderTarget.texture = texture; //居然漏了一句,2022.10.9补 renderer.setRenderTarget(renderTarget); renderer.setRenderTarget(null); var o = renderer.properties.get(texture); o.__image__webglTextureCube = o.__webglTexture; //window.tex[r.id] = {texture:r, index:this.viewer.index } return renderTarget; } getUploadQueueForPano(e) { var t = this.uploadQueues[e]; return t || (t = [], this.uploadQueues[e] = t), t; } isTileUploaded(e) { return this.isPanoZoomed(e.panoId) ? e.zoomUploaded : e.uploaded; } setUploaded(e, t) { this.isPanoZoomed(e.panoId) ? e.zoomUploaded = t : e.uploaded = t; } queueTileUpload(e, t, i) { var n = this.getActiveRenderTargetDescriptor(e.panoId); if (this.isRenderTargetDescriptorValid(n) && e.downloaded && !this.isTileUploaded(e) && (!e.uploadQueued || i) && (!(e.panoSize > this.qualityManager.getMaxNavPanoSize()) || this.zoomingActive && viewer.images360.getPano(e.panoId).tileRes != '2k')) { var r = this.getUploadQueueForPano(e.panoId); //console.log(window.sceneName, 'queueTileUpload: ', e.panoId, e.tileIndex, i) if (i) { //console.log('512下载好了直接uploadTile') this.uploadTile(e, !1); //提交后该tile直接loaded了,无需等待 } else { if (this.shoulPushToFrontOfQueue(e)) { //如果是512的优先 this.forceQueue.push(e); } else { if (t && this.direction) { TilePrioritizer.insertSortedPanoTile(r, e, n.pano, this.direction); } else r.push(e); } e.uploadQueued = !0; this.uploadInterval || this.uploadIntervalCancelled || this.refreshUploadInterval(0); } } } shoulPushToFrontOfQueue(e) { return 0 === TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, e.panoSize); } getTopUploadQueue() { for (var e = null, t = null, i = S.Base; i <= S.Remaining; i++) for (var n = 0; n < this.activePanos.length; n++) if (e = this.activePanos[n], t = this.getUploadQueueForPano(e.id), t.length > 0) switch (i) { case S.Base: if (0 === t[0].level) return t; break; case S.Remaining: return t; } return null; } peekNextFromUploadQueue() { if (this.forceQueue.length > 0) return this.forceQueue[0]; var e = this.getTopUploadQueue(); return e && e.length > 0 ? e[0] : null; } clearAllQueuedUploads() { this.clearAllUploadQueues(null, 0); } clearAllQueuedUploadsForPano(e) { this.clearAllUploadQueues(e, 0); } clearAllUploadQueues(e, t) { if (e) this.clearUploadQueue(this.getUploadQueueForPano(e), t), this.clearUploadQueue(this.forceQueue, t, e);else { for (var i = 0; i < this.activePanos.length; i++) { var n = this.activePanos[i]; this.clearUploadQueue(this.getUploadQueueForPano(n.id), t); } this.clearUploadQueue(this.forceQueue, t); } } clearUploadQueue(e, t, i) { void 0 !== t && null !== t || (t = 0); for (var n = 0; n < e.length;) { var r = e[n]; (!i || i && i === r.tile.panoId) && r.level >= t ? (r.uploadQueued = !1, e.splice(n, 1)) : n++; } //若报错, r.tile.panoId改为 r.panoId } updateUploadQueue(maxNPF, maxPF /* e, t */) { //参数是 maxNonBaseUploadsPerFrame and maxBaseUploadsPerFrame, 优先上传512 //maxNPF || (maxNPF = 1); for (var i = 0, n = 0;;) { //let old = this.forceQueue.slice(0) if (n >= maxPF || i >= maxNPF) break; var r = this.getNextFromUploadQueue(); if (!r) break; //r.panoSize <2048 && console.log('panoId', r.panoId, 'panoSize', r.panoSize , old) 0 !== r.level ? i++ : n++; if (!(r.panoSize > this.qualityManager.getMaxNavPanoSize()) || this.zoomingActive) { var o = this.getActiveRenderTargetDescriptor(r.panoId); this.isRenderTargetDescriptorValid(o) && this.uploadTile(r, r.forceUpload); } } } updateDirection(e) { if (e = e || this.direction) { this.direction = e; for (var t = 0; t < this.activePanos.length; t++) { var i = this.activePanos[t], n = this.getUploadQueueForPano(i.id); TilePrioritizer.sortPanoTiles(n, i, this.direction); } } } anyUploaded(e) { if (!e) return !1; if (e.tile && this.isTileUploaded(e.tile)) return !0; if (e.children) for (var t = 0; t < e.children.length; t++) { var i = e.children[t]; if (this.anyUploaded(i)) return !0; } return !1; } setNodeCovered(e, t) { this.isPanoZoomed(e.tile.panoId) ? e.zoomCovered = t : e.covered = t; } isNodeCovered(e) { return !!e && (this.isPanoZoomed(e.tile.panoId) ? e.zoomCovered : e.covered); } addCoverageForNode(e) { if (this.setNodeCovered(e, !0), e.parent && e.covered) { var t = e.parent; this.nodeSubcovered(t) && this.addCoverageForNode(t, !0); } } calcFullCoverage(e) { var t = !1; if (e.children) for (var i = 0; i < e.children.length; i++) { var n = e.children[i]; t = t || this.calcFullCoverage(n); } e.covered = e.tile.uploaded || t; } nodeSubcovered(e) { if (!e.children) return !1; for (var t = 0; t < e.children.length; t++) if (!e.children[t] || !this.isNodeCovered(e.children[t])) return !1; return !0; } resetPanoDescriptor(e) { this.getPanoDescriptor(e); } getPanoDescriptor(e) { var t = this.panoDescriptors[e]; return t || (t = {}, this.panoDescriptors[e] = t), t; } resetPanoLODDescriptors(e) { var t = this.getPanoLODDescriptors(e); for (var i in t) if (t.hasOwnProperty(i)) { var n = t[i]; n.uploadCount = 0, n.uploadAttempts = 0; n.uploaded = []; } } getPanoLODDescriptor(e, t) { var i = this.getPanoLODDescriptors(e), n = i[t]; return n || (n = { uploadCount: 0, uploadAttempts: 0, uploaded: [] //add }, i[t] = n), n; } getPanoLODDescriptors(e) { var t = this.panoLODDescriptors[e]; return t || (t = {}, this.panoLODDescriptors[e] = t), t; } onTileDownloaded(o) { var e = o.desc; var t = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, e.panoSize), i = this.getTileDirectoryEntry(e.panoId, e.face, t, e.faceTileIndex); i.downloaded = !0; i.image = e.image; i.panoSize = e.panoSize; i.tileX = e.tileX; i.tileY = e.tileY; i.totalTiles = e.totalTiles; i.tileIndex = e.tileIndex; i.faceTileIndex = e.faceTileIndex; i.face = e.face; i.cubeFace = TileUtils.mapFaceToCubemapFace(e.face); i.panoId = e.panoId; i.tileSize = e.tileSize; i.direction = new Vector3().copy(e.direction); i.node = null; i.level = TileTree.getLevelCountForSize(TileUtils.TILE_SIZE, i.panoSize); if (this.isPanoActive(i.panoId)) { var n = this.getTileTree(i.panoId, i.face); var r = n.getSubNode(i.panoSize, i.tileX, i.tileY); this.linkTileAndNode(i, r); this.queueTileUpload(i, !0); } } getTileDirectoryEntry(panoId, t, i, n) { var r = this.tileDirectory[panoId]; r || (r = {}, this.tileDirectory[panoId] = r); var o = 16384 * t + 1024 * i + n, //t:4096级别 a = r[o]; return a || (a = { downloaded: !1, uploaded: !1, zoomUploaded: !1 }, r[o] = a), a._key = panoId + ":" + t + ":" + i + ":" + n, //panoId : face : level : faceTileIndex a._tileKey = o, a; } setZoomingActive(active, pano, i) { //设置当前正在zoom的pano this.zoomPanoRenderingDisabled || active === this.zoomingActive && this.zoomPanoId === pano.id || (this.zoomingActive = active, this.zoomPanoId = pano.id, this.zoomingActive && (this.zoomPanoId !== pano.id || i) && this.updateZoomedPanoFromBase(pano)); } updateZoomedPanoFromBase(pano) { //因更换pano所以将pano的rendertarget渲染到panoRenderer的zoomRenderTarget上 if (!this.zoomPanoRenderingDisabled) { var t = this.getActiveRenderTargetDescriptor(pano.id); if (t && t.renderTarget) { if (this.zoomRenderTarget) { var i = Math.min(this.qualityManager.maxRenderTargetSize, this.qualityManager.getMaxZoomPanoSize()), //change n = t.renderTarget, r = t.size; this.copyCubeMap(n.texture, this.zoomRenderTarget, r, r, i, i); } this.copyBaseRenderStatusToZoomed(pano.id); } } } add(e) { this.M.push(e); } initDescriptor(size) { var t = createDescriptor(); t.inUse = !0; t.size = size; this.add(t); return t; } activeDescripor(e) { for (var t = 0; t < this.M.length; t++) { var i = this.M[t]; if (!i.inUse && i.size === e) { i.inUse = !0; return i; } } return null; } deactiveDescripor(e) { for (var t = 0; t < this.M.length; t++) { var i = this.M[t]; if (i.renderTarget === e) { i.inUse = !1; return !0; } } return !1; } enableHighQuality(e) { //xzw add 如果最多只要2k图的话enableUltraHighQualityMode替换成这个 if (!this.qualityManager.highQualityModeStarted) { this.setupZoomRenderTarget(); e(); //this.qualityManager.updateHighResolutionSettings(e)////////? this.qualityManager.highQualityModeStarted = true; } } linkTileAndNode(e, t) { t.tile = e, e.node = t; } linkAllTilesAndNodes(e) { var t = function t(_t, i, n, r, o) { var a = this.getTileDirectoryEntry(e.id, i, r, o); this.linkTileAndNode(a, n); }; for (var i = 0; i < TileUtils.FACES_PER_PANO; i++) { var n = this.getTileTree(e.id, i); n.breadthFirst({ callback: t.bind(this, n, i) }); } } //--------------sceneRenderer initSizedTexture2D(e, t, i) { var renderer = this.viewer.renderer, o = renderer.getContext(), a = renderer.state, s = new Texture(null); s.flipY = !1, i !== !0 && (i = !1), s.generateMipmaps = i; var l = renderer.paramThreeToGL(s.format), c = renderer.paramThreeToGL(s.type), h = renderer.properties.get(s), u = o.createTexture(); a.bindTexture(o.TEXTURE_2D, u), o.pixelStorei(o.UNPACK_FLIP_Y_WEBGL, s.flipY), o.texImage2D(o.TEXTURE_2D, 0, l, e, e, 0, l, c, null), s.wrapS = t, s.wrapT = t; var d = renderer.paramThreeToGL(t); return o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_S, d), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_WRAP_T, d), i ? (s.magFilter = LinearFilter, s.minFilter = LinearMipMapLinearFilter, o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MAG_FILTER, o.LINEAR), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MIN_FILTER, o.LINEAR_MIPMAP_NEAREST), o.generateMipmap(o.TEXTURE_2D)) : (s.magFilter = LinearFilter, s.minFilter = LinearFilter, o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MAG_FILTER, o.LINEAR), o.texParameteri(o.TEXTURE_2D, o.TEXTURE_MIN_FILTER, o.LINEAR)), a.bindTexture(o.TEXTURE_2D, null), h.__webglTexture = u, s; } deallocateCubeTexture(e) { var t = this.viewer.renderer, i = t.getContext(), renderer = t.properties.get(e); i.deleteTexture(renderer.__image__webglTextureCube); } uploadTexture2D(img, tex, startX, startY, width, height) { var renderer = this.viewer.renderer, gl = renderer.getContext(), webglState = renderer.state, c = renderer.properties.get(tex); webglState.bindTexture(gl.TEXTURE_2D, c.__webglTexture), gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, tex.flipY), gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, tex.premultiplyAlpha), gl.pixelStorei(gl.UNPACK_ALIGNMENT, tex.unpackAlignment), gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, renderer.paramThreeToGL(tex.wrapS)), gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, renderer.paramThreeToGL(tex.wrapT)), gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, renderer.paramThreeToGL(tex.magFilter)), gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, renderer.paramThreeToGL(tex.minFilter)), gl.texSubImage2D(gl.TEXTURE_2D, 0, startX, startY, gl.RGBA, gl.UNSIGNED_BYTE, img), tex.generateMipmaps && gl.generateMipmap(gl.TEXTURE_2D), webglState.bindTexture(gl.TEXTURE_2D, null); } getCubeOrientationForCubeFace(e, t) { switch (e) { case GLCubeFaces$2.GL_TEXTURE_CUBE_MAP_POSITIVE_X: t.set(0, -Math.PI / 2, 0); break; case GLCubeFaces$2.GL_TEXTURE_CUBE_MAP_NEGATIVE_X: t.set(0, Math.PI / 2, 0); break; case GLCubeFaces$2.GL_TEXTURE_CUBE_MAP_POSITIVE_Y: t.set(Math.PI / 2, Math.PI, 0); break; case GLCubeFaces$2.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: t.set(-Math.PI / 2, Math.PI, 0); break; case GLCubeFaces$2.GL_TEXTURE_CUBE_MAP_POSITIVE_Z: t.set(0, -Math.PI, 0); break; case GLCubeFaces$2.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: t.set(0, 0, 0); } } //dispose所有cubeRenderTarget disposeIdelTargets() { var disposeCount = 0; for (var t = 0; t < this.M.length; t++) { var i = this.M[t]; if (!i.inUse) { disposeCount++; i.renderTarget.dispose(); } } //console.log('disposeIdelTargets ', disposeCount, 'of', this.M.length) } } PanoRenderer.prototype.setupZoomRenderTarget = function () { var targets = {}; return function () { if (this.qualityManager.maxRenderTargetSize == 2048 && this.qualityManager.getMaxNavPanoSize() == 2048) return; //不使用zoomTarget 直接用pano的tiledPanoRenderTarget,防崩溃 if (this.qualityManager.getMaxZoomPanoSize() >= this.qualityManager.getMaxNavPanoSize()) { //部分手机2k时copyCubeMap会重载 , 所以如果没有超出当前分辨率,就不使用zoomRenderTarget。但在微信依旧会重载,只是优化了些,safari几乎不会。 if (this.zoomRenderTarget && this.zoomRenderTarget.width === this.qualityManager.getMaxZoomPanoSize()) return; var e = this.zoomRenderTarget; var size = this.qualityManager.getMaxZoomPanoSize(); if (size > this.qualityManager.maxRenderTargetSize) { return; } if (targets[size]) { this.zoomRenderTarget = targets[size]; } else { this.zoomRenderTarget = this.initTiledPano(size, false); //放大后不使用抗锯齿,否则消 targets[size] = this.zoomRenderTarget; } if (e) { //将旧的zoomRenderTarget渲染到新zoomRenderTarget上 var t = e.width, i = this.zoomRenderTarget.width; this.copyCubeMap(e.texture, this.zoomRenderTarget, t, t, i, i), e.texture.dispose(), e.texture.loaded = !1, e.texture.version = 0, this.deallocateCubeTexture(e.texture), e.texture = null; } this.zoomPanoRenderingDisabled = !1; } else this.zoomPanoRenderingDisabled = !0; }; }(); PanoRenderer.prototype.updateActivePanos = function () { var e = []; return function (t, i) { e.length = 0; for (var n = 0; n < this.activePanos.length; n++) { t && e.length === i && e.push(t); var r = this.activePanos[n], o = this.getActiveRenderTargetDescriptor(r.id); t && r.id === t.id || !this.isRenderTargetDescriptorValid(o) || e.push(r); } t && i >= e.length && e.push(t), this.activePanos.length = 0, this.activePanos.push.apply(this.activePanos, e); }; }(); PanoRenderer.prototype.renderPanoTiles = function () { var e = []; return function (panoId, i, n, r) { this.zoomRenderTarget && this.zoomRenderTarget.width === this.qualityManager.getMaxZoomPanoSize() || this.zoomPanoRenderingDisabled || this.setupZoomRenderTarget(), //如果ZoomRenderTarget大小需要变动就重新创建 i = i || this.direction || Vectors.FORWARD; // [{1:Vectors.FORWARD}]? var o = this.getActiveRenderTargetDescriptor(panoId); if (!this.isRenderTargetDescriptorValid(o)) console.error("PanoRenderer.renderPanoTiles() -> Cannot render to a pano that is not activated."); for (var a = 0; a < TileUtils.FACES_PER_PANO; a++) { var s = this.getTileTree(panoId, a); e.length = 0; s.breadthFirst({ //获取该面所有node 85个 = 1+4+16+64 saveVisited: e }); for (var l = 0; l < e.length; l++) { var c = e[l]; this.queueTileUpload(c.tile, !1, r || 0 === l && n); // 第0个是512的,会直接uploadTile } } this.updateDirection(i); }; }(); PanoRenderer.prototype.getNextFromUploadQueue = function () { var e = function e(_e) { var t = _e.shift(); return t.uploadQueued = !1, t; }; return function () { if (this.forceQueue.length > 0) return e(this.forceQueue); var t = this.getTopUploadQueue(); return t && t.length > 0 ? e(t) : null; }; }(); /* PanoRenderer.prototype.refreshUploadInterval = function () { var e = null; return function (t) { this.uploadIntervalCancelled || (e || (e = upload.bind(this)), null !== t && void 0 !== t || (t = w), b || (t = _), this.uploadInterval = window.setTimeout(e, t), this.uploadIntervalDelay = t) } }() PanoRenderer.prototype.update = function () { var e = performance.now(), t = 0; return function () { this.uploadIntervalCancelled = !0; window.clearTimeout(this.uploadInterval); this.uploadInterval = null; var i = performance.now() - e; //!(i > w || 0 === t) || !this.overlayTilesLoaded && this.usingTileOverlay || (this.updateUploadQueue(this.maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame), //e = performance.now()), if (!(i > w || 0 === t) || !this.overlayTilesLoaded && this.usingTileOverlay) {} else { this.updateUploadQueue(this.maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame); e = performance.now(); } t++; } }() */ PanoRenderer.prototype.update = function () { //this.uploadIntervalCancelled = true ; //不使用setTimeout,而是在sceneRenderer每帧都update if (viewer.images360.latestRequestMode == 'showPanos') { var maxNonBaseUploadsPerFrame = viewer.lastFrameChanged ? Common.getBestCount('maxStandard', 0, 2, 1, 10 /* ,true */) : 2; // 这是每帧uploadTile非512的瓦片tex的数量。 手机在前进时可能会总是0,直到到漫游点后为2 var maxBaseUploadsPerFrame = viewer.lastFrameChanged ? Common.getBestCount('maxBase', 1, 6, 1, 14 /* ,true */) : 6; //this.maxBaseUploadsPerFrame //原先6. 但持续前进过程中会请求加载下一个漫游图,一次加6张会卡的。 this.updateUploadQueue(maxNonBaseUploadsPerFrame, maxBaseUploadsPerFrame); //注:静止时看不出卡顿所以全速加载 } }; /* function upload() { if (!this.uploadIntervalCancelled) { if (this.overlayTilesLoaded || !this.usingTileOverlay) { b = !0 let maxNonBaseUploadsPerFrame = viewer.mainViewport.view.isFlying() ? 1 : this.maxNonBaseUploadsPerFrame //原先2。这是每帧uploadTile非512的瓦片tex的数量。之前的2太卡了,降为1。(检测卡顿方法:在一个pano点旋转至所有2048的tile都加载完,然后之后到这个点看看卡不卡。因为该点tiles都下载完了所以会在飞过来时陆续都加载,所以容易卡) this.updateUploadQueue(maxNonBaseUploadsPerFrame, this.maxBaseUploadsPerFrame) let time = viewer.mainViewport.view.isFlying() ? 60 : w //add 飞行有时候会卡,增长间隔 this.peekNextFromUploadQueue() ? this.refreshUploadInterval(time) : this.uploadInterval = null //定时下一次更新 } else { this.refreshUploadInterval(this.uploadIntervalDelay) } } } */ PanoRenderer.prototype.uploadTile = function () { //重写 var collection = {}, overlayStyle = config$1.tiling.overlayStyle; var failHistory = {}; return function (info, n) { var id = info.panoId, img = info.image, tileSize = info.tileSize, panoSize = info.panoSize, tileIndex = info.tileIndex, totalTiles = info.totalTiles, tileX = info.tileX, tileY = info.tileY, p = !0, g = !1, ignore = false, //add LodDescripor = (this.getPanoDescriptor(id), this.getPanoLODDescriptor(id, panoSize)), activeDescripor = this.getActiveRenderTargetDescriptor(id), renderTarget = activeDescripor.renderTarget, size = activeDescripor.size; //当前要渲染的面的分辨率,也就是MaxNavPanoSize if (this.isPanoZoomed(id) && this.zoomRenderTarget) { renderTarget = this.zoomRenderTarget; size = this.zoomRenderTarget.width; //this.qualityManager.getMaxZoomPanoSize(); //放大后可能2048或4096 } //console.log(window.sceneName, 'uploadTile ', id, tileIndex) var done = () => { if (!LodDescripor.uploaded.includes(tileIndex)) { //已经upload过(本来这时候直接返回,但发现缩放后这不会归零,导致清晰度不更新,所以还是redraw且emit吧) //console.log('try to reupload and return',tileIndex) LodDescripor.uploaded.push(tileIndex); LodDescripor.uploadCount++; } this.dispatchEvent({ type: PanoRendererEvents$1.TileRenderSuccess, id, panoSize, tileIndex, totalTiles }); LodDescripor.uploadCount === totalTiles && this.dispatchEvent({ type: PanoRendererEvents$1.PanoRenderComplete, id, panoSize, totalTiles, updateFullComplete: true }); this.setUploaded(info, !0); this.addCoverageForNode(info.node); }; { //已经uploadTile过了不再uploadTile if (!this.isRenderTargetDescriptorValid(activeDescripor)) { p = !1; g = !1; } if (!n) { this.anyUploaded(info.node) && (p = !1, g = !0, ignore = true); //包括子集也uploadTile了 this.isTileUploaded(info) && (p = !1, g = !1, ignore = true); //当前tile uploadTile了 } } if (p) { /*if(failHistory[id+':'+ panoSize+ ':' +tileIndex]){ console.log('uploadTile retry',id, panoSize, tileIndex) } console.log('uploadTile 成功', id, panoSize, tileIndex) */ var C = tileX * tileSize, I = tileY * tileSize, E = tileSize / panoSize * size, // tile在renderTarget上渲染出的宽度 b = C / panoSize * size, // tile在renderTarget上渲染的startX w = I / panoSize * size; // tile在renderTarget上渲染的startY if (panoSize > this.qualityManager.maxRenderTargetSize) { //4096 改 /* var tex = this.initSizedTexture2D(tileSize, THREE.ClampToEdgeWrapping); var loaded = this.viewer.images360.isHighMapLoaded(info.cubeFace, tileX,tileY) */ this.viewer.images360.getHighImage(img, info.cubeFace, tileX, tileY); } else { collection[tileSize] || (collection[tileSize] = this.initSizedTexture2D(tileSize, ClampToEdgeWrapping)); var tex = collection[tileSize]; this.uploadTexture2D(img, tex, 0, 0, tileSize, tileSize); //只替换tex对应的img,不新建 if (panoSize > this.qualityManager.maxRenderTargetSize) { loaded || this.viewer.images360.updateHighMap(tex, info.cubeFace, tileX, tileY); } else { if (1 === overlayStyle || 2 === overlayStyle) { var T = 1 === overlayStyle ? this.overlayTilesBasic : this.overlayTilesEnhanced; this.renderToCubeMap(tex, renderTarget, tileSize, tileSize, 0, 0, tileSize, tileSize, b, w, E, E, info.cubeFace); this.renderToCubeMap(T[panoSize], renderTarget, tileSize, tileSize, 0, 0, tileSize, tileSize, b, w, E, E, info.cubeFace, NormalBlending, !0, .5); } else { this.renderToCubeMap(tex, renderTarget, tileSize, tileSize, 0, 0, tileSize, tileSize, b, w, E, E, info.cubeFace); } } } done(); } else if (ignore) { //console.log('finish because anyUploaded',id,panoSize,tileIndex) done(); //改: 如果因为这部分更高清的贴图已加载所以才不绘制的话,直接完成 } else { //console.log('uploadTile 失败', id, panoSize, tileIndex) if (panoSize == 512) { //console.log("!!!!!!!!!!!!!") } failHistory[id + ':' + panoSize + ':' + tileIndex] = true; this.setUploaded(info, !1); } info.uploadAttempted || (LodDescripor.uploadAttempts++, this.dispatchEvent({ type: PanoRendererEvents$1.TileUploadAttempted, id, panoSize, tileIndex, totalTiles })), info.uploadAttempted = !0; LodDescripor.uploadAttempts === totalTiles && this.dispatchEvent({ type: PanoRendererEvents$1.UploadAttemptedForAllTiles, id, panoSize, totalTiles }); return g; }; }(); /* 注:tileY的方向同UV,从下到上 renderToCubeMap里的画布or镜头的xy范围是-0.5到0.5 */ PanoRenderer.prototype.renderToCubeMap = function () { var inited = !1, scene = null, camera = null, material = null, geo = null, plane = null, l = 1; return function (texture, renderTarget, tileWidth, tileHeight, startXinTile, startYinTile, widthinTile, heightinTile, startX, startY, width, height, cubeFace, E, b, w) { var renderer = this.viewer.renderer; inited || (camera = new OrthographicCamera(l / -2, l / 2, l / 2, l / -2, -200, 200), camera.position.z = 150, scene = new Scene(), scene.add(camera), material = new BasicMaterial({ depthWrite: !1, depthTest: !1, side: 0 }), geo = new PlaneBufferGeometry(l, l), plane = new Mesh(geo, material), plane.position.z = 0, scene.add(plane), inited = !0); var uv = geo.getAttribute("uv"); uv.setDynamic(!0), //setUsage uv.needsUpdate = !0; var uvArr = uv.array, S = startXinTile / tileWidth, //uv这几个值基本是固定的startXinTile:0,startYinTile:0,widthinTile:512,widthinTile:512,tileWidth:512,tileHeight:512 也就是说uv不会变、每张tile的有效范围是100% M = startYinTile / tileHeight, R = widthinTile / tileWidth, P = heightinTile / tileHeight; uvArr[0] = S, uvArr[1] = M + P, uvArr[2] = S + R, uvArr[3] = M + P, uvArr[4] = S, uvArr[5] = M, uvArr[6] = S + R, uvArr[7] = M; //修改posistion,使该plane只占据需要绘制的部分。类似拼图。 //startX startY width height 都是在画布上的大小,比如画布大小为2048*2048,此tile为16分之一,tileX是1,tileY是1,则startX=2048/4,startY=2048/4 var pos = geo.getAttribute("position"); pos.setDynamic(!0), pos.needsUpdate = !0; var posArr = pos.array, D = startX / renderTarget.width - l / 2 // 起始x , N = startY / renderTarget.height - l / 2 // 起始y , B = width / renderTarget.width // 宽 , F = height / renderTarget.height; // 高 posArr[0] = D, posArr[1] = N + F, posArr[3] = D + B, posArr[4] = N + F, posArr[6] = D, posArr[7] = N, posArr[9] = D + B, posArr[10] = N; //renderer.properties.get(scene); material.map = texture; //material.uniforms.map.value = texture; material.blending = E || NoBlending, material.transparent = !!b; void 0 !== w && null !== w || (w = 1), material.uniforms.opacity.value = w, // material.needUpdate = !0 //renderTarget.activeCubeFace = cubeFace, //0-5 应该是指定渲染h中的面 失效 /* renderer.setScissorTest(!0) //指定绘制区域,类似遮罩(相对于屏幕) renderer.setScissor(startX,startY,width,height) //加上这个会不会快一些,尤其是spherical //指定绘制视口位置和大小(相对于屏幕) */ renderTarget.viewport.set(0, 0, renderTarget.width, renderTarget.height); var V = renderer.autoClear; var oldTarget = renderer.getRenderTarget(); renderer.autoClear = !1; renderer.setRenderTarget(renderTarget, cubeFace); renderer.render(scene, camera /* , renderTarget, !1 */); renderer.setRenderTarget(oldTarget); renderer.autoClear = V; //renderer.setScissorTest(!1) /* this.renderer.render(scene, camera, this.planeTargets[cubeFace], !1),//针对有的场景app第一个点图加载不成功的问题 console.log(`图index ${cubeFace} , ${startX}, ${startY}, ${width}, ${height}`) this.targetList[cubeFace] || (this.targetList[cubeFace] = []) this.targetList[cubeFace].push([startX,startY,width,height])*/ }; }(); /* * 将texture渲染到zoomRenderTarget上(目的是复制贴图到zoomRenderTarget) */ PanoRenderer.prototype.copyCubeMap = function () { //将texture渲染到zoomRenderTarget上 var inited = !1, scene = null, camera = null, material = null, geo = null, mesh = null, testCube = null, //add c = new Euler(); return function (texture, renderTarget, tWidth, tHeight, rWidth, rHeight, m, v, A) { if (rWidth > this.qualityManager.maxRenderTargetSize) return; //add if (!inited) { var w = 2; camera = new OrthographicCamera(w / -2, w / 2, w / 2, w / -2, 0, 200), camera.position.set(0, 0, 0), scene = new Scene(), scene.add(camera); material = new ShaderMaterial({ uniforms: { tDiffuse: { type: "t", value: null }, alpha: { type: "f", value: 1 } }, vertexShader: Shaders['copyCubeMap.vs'], fragmentShader: Shaders['copyCubeMap.fs'], depthWrite: !1, depthTest: !1, side: DoubleSide }); geo = new BoxGeometry(w, w, w), mesh = new Mesh(geo, material), scene.add(mesh); inited = !0; /* testCube = mesh.clone(); viewer.scene.scene.add(testCube); Potree.Utils.setObjectLayers(testCube, 'sceneObjects') */ } var autoClear = this.viewer.renderer.autoClear; var oldTarget = this.viewer.renderer.getRenderTarget(); this.viewer.renderer.autoClear = false; material.uniforms.tDiffuse.value = texture; material.blending = m || NoBlending; material.transparent = !!v; void 0 !== A && null !== A || (A = 1); material.uniforms.alpha.value = A; material.needUpdate = !0; for (var C = 0; C < 6; C++) { this.getCubeOrientationForCubeFace(C, c); mesh.rotation.copy(c); mesh.matrixWorldNeedsUpdate = !0; mesh.updateMatrixWorld(); renderTarget.viewport.set(0, 0, rWidth, rHeight); this.viewer.renderer.setRenderTarget(renderTarget, C); //renderTarget.activeCubeFace = C//失效 this.viewer.renderer.render(scene, camera); } //console.warn('copyCubeMap' + rWidth) this.viewer.renderer.autoClear = autoClear; this.viewer.renderer.setRenderTarget(oldTarget); }; }(); var imgCount = {}; window.imgCount = imgCount; class DepthImageSampler extends EventDispatcher { constructor() { super(); var canvas = document.createElement("canvas"); this.canvas = canvas; this.context = canvas.getContext("2d", { willReadFrequently: true }); this.imgDatas = []; /* document.getElementsByTagName('body')[0].appendChild(canvas); canvas.style.position = 'fixed'; canvas.style.width = '1024px'; canvas.style.top = canvas.style.left = 0 canvas.style['z-index'] = 100 */ this.maxDataCount = browser.isMobile() ? 12 : 20; //手机会崩溃. 平均每张图为8M数据量(以200个点的园区为例,加载时间久一些后,总内存=700 + 每张图的8M * maxDataCount) this.maxNeighCount = browser.isMobile() ? 9 : 14; //包含在maxDataCount内的nearPanos最大个数.至少比maxDataCount少3个,留出空位给最近更新的pano this.nearPanos = []; } updateNearPanos(panos) { this.nearPanos = panos.slice(0, this.maxNeighCount); } changeImg(img, pano) { imgCount[pano.id] = (imgCount[pano.id] || 0) + 1; this.pano = pano; var item = this.imgDatas.find(p => p.pano == pano); if ( /* this.img == img || */item) { //最新的在末尾,所以换到末尾 var index = this.imgDatas.indexOf(item); this.imgDatas.splice(index, 1); this.imgDatas.push(item); //console.log('重复使用',item.pano.id) return; } Potree.timeCollect.depthSamChangeImg.start = true; //不过在刚启动时得到的用时较大 try { viewer.addTimeMark('depthSamChangeImg', 'start'); this.canvas.width = img.width; this.canvas.height = img.height; this.context.drawImage(img, 0, 0); var o = this.context.getImageData(0, 0, img.width, img.height); var data = o && o.data; //getImageData 1px时 : pc chrome 耗时0.01毫秒左右(排除第一次的50) , 但firefox: 4。但换贴图之后就多达5甚至几十 //console.warn('changeImg', pano.id, !!data ) //this.img = img if (!data) { console.error('getImageData 得不到?!why!'); return false; } if (this.imgDatas.length >= this.maxDataCount) { var old = this.imgDatas.find(e => !this.nearPanos.includes(e.pano)); //console.log('推出',old.pano.id) this.imgDatas.splice(this.imgDatas.indexOf(old), 1); //推出使用时间最早的一个非nearPano } this.imgDatas.push({ pano, data }); this.dispatchEvent({ type: 'changeImg', pano }); viewer.addTimeMark('depthSamChangeImg', 'end'); //耗时chrome 25ms,firefox: 38ms, iphoneX:33ms } catch (e) { console.error(e); //内存不足 Failed to execute 'getImageData' on 'CanvasRenderingContext2D': Out of memory at ImageData creation return false; } /* pano.depthData = {} for(let h=0; h0) pano.depthData[h+'|'+w] = depth } } */ } clearTexData() { this.imgDatas.length = 0; } getDepth(UVx, UVy, useNeighIfZero) { //根据图片像素获取深度值 var x = Math.round(UVx * (this.canvas.width - 1)), y = Math.round(UVy * (this.canvas.height - 1)); var imgData = this.imgDatas.find(p => p.pano == this.pano); var get = (x, y) => { if (!(x < 0 || y < 0 || x >= this.canvas.width || y >= this.canvas.height)) { var blockIndex = this.canvas.width * y + x; var color = imgData.data.slice(blockIndex * 4, (blockIndex + 1) * 4); return color[1] + color[0] / 256; //为什么不是除以255见聊天记录.(验证过比点云的远一点点) } }; /* viewer.addTimeMark('depthSampler','start') var r = this.context.getImageData(x, y, 1, 1).data; //pc chrome 耗时0.01毫秒左右(排除第一次的50) , 但firefox: 4。但换贴图之后就多达5甚至几十 viewer.addTimeMark('depthSampler','end') */ var depth = get(x, y); if (!depth && useNeighIfZero) { //遇到过有的相隔很远且有阻挡的两个漫游点间居然depth为0,没有阻挡?但是周围点有四个非零。所以为了修正会飞到很远的点加个识别周围像素的depth 。 2024.3.19 var results = []; var d = 0, sum = 0, count = 0, maxResDis = 1, padding = 2; //间隔 while (d++ < maxResDis) { for (var i = -d; i <= d; i++) { for (var j = -d; j <= d; j++) { if (i == -d || i == d || j == -d || j == d) { //外围 var depth1 = get(x + i * padding, y + j * padding); if (depth1 != 0) { results.push(depth1); } } } } } if (results.length) { depth = results.reduce((w, c) => { return w + c; }, 0) / results.length; //console.error('useNeighIfZero', results, depth) } //console.log('useNeighIfZero', results, depth) } return { depth, x, y }; } sample(intersect, currentPano, onlyPos, useNeighIfZero) { //通过和skybox的intersect得到真实的intersect的位置 if (!intersect) return; var location = new Vector3(); var normal; currentPano = currentPano || viewer.images360.currentPano; if (currentPano != this.currentPano || !this.imgDatas.find(p => p.pano == currentPano)) { if (!currentPano.depthTex /* || !currentPano.depthTex.image */) return; //未加载 if (this.changeImg(currentPano.depthTex.image, currentPano) === false) { console.log('失败', currentPano.id); return false; //失败 } this.currentPano = currentPano; } var origin = currentPano.position; var dir = intersect.dir || new Vector3().subVectors(intersect.point, origin).normalize(); //var uv = intersect.uv //let dirInPano = math.getNormalDir(dir, currentPano)//转化为考虑漫游点旋转的方向 var dirInPano = dir.clone().applyMatrix4(currentPano.panoMatrix2Inverse).normalize(); //转化为考虑漫游点旋转的方向 var uv = math.getUVfromDir(dirInPano); //转化为uv var { depth, x, y } = this.getDepth(uv.x, uv.y, useNeighIfZero); depth *= currentPano.pointcloud.scale.x; //viewer.addTimeMark(markName,'end') if (!depth) { if (this.ignoreTopAndBtmHole) { return null; } var margin = 0.1; if (uv.y > 1 - Potree.config.depthTexUVyLimit) { //漫游点底部识别不到的区域,给一个地板高度 depth = (currentPano.floorPosition.z - origin.z - margin) / dir.z; location.copy(dir).multiplyScalar(depth).add(origin); var _normal = new Vector3(0, 0, 1); return { location, normal: _normal, distance: depth, uv }; } else if (uv.y < Potree.config.depthTexUVyLimit) { var ceilZ = currentPano.getCeilHeight(); if (ceilZ == Infinity) return !1;else { depth = (ceilZ - origin.z - margin) / dir.z; location.copy(dir).multiplyScalar(depth).add(origin); var _normal2 = new Vector3(0, 0, -1); return { location, normal: _normal2, distance: depth, uv }; } } //console.log('无穷远') return !1; //应该是天空或模型外 , 因为很少有漫游点的地方还拍不到地板 } //console.log('depth', depth, dirInPano.clone().multiplyScalar(depth)) location.copy(dir).multiplyScalar(depth).add(origin); if (!onlyPos) { var pL = this.getNearbyPoint(origin, uv, -1, 0), pR = this.getNearbyPoint(origin, uv, 1, 0), pB = this.getNearbyPoint(origin, uv, 0, -1), pT = this.getNearbyPoint(origin, uv, 0, 1); normal = this.planeFit(dir, location, pL, pR, pB, pT); } /* if(normal.x != normal.x ){ console.log('NAN', normal) var pL = this.getNearbyPoint(origin, uv, -1, 0) , pR = this.getNearbyPoint(origin, uv, 1, 0) , pB = this.getNearbyPoint(origin, uv, 0, -1) , pT = this.getNearbyPoint(origin, uv, 0, 1); } */ //console.log(location, normal, distance) return { location, normal, distance: depth, x, y, uv }; } getNearbyPoint(origin, uv, x, y) { //获取附近的若干像素距离的点 var uv2 = uv.clone(); uv2.x += x / (this.canvas.width - 1); uv2.x = this.clampUV(uv2.x); uv2.y += y / (this.canvas.height - 1); uv2.y = this.clampUV(uv2.y); /* if(uv2.x < 0 || uv2.y < 0 || uv2.x > 1 || uv2.y > 1){ console.log('will nan') } */ var dir = math.getDirFromUV(uv2); //从uv获取到方向 dir.applyMatrix4(viewer.images360.currentPano.panoMatrix2); var { depth } = this.getDepth(uv2.x, uv2.y); /* if(Math.abs(depth - this.mainDepth) > 0.3){ console.log('Math.abs(depth - this.mainDepth) > 0.3') } */ //let dir = new THREE.Vector3().subVectors(intersect.point, origin).normalize() var position = new Vector3().copy(dir).multiplyScalar(depth).add(origin); //console.log('getNearbyPoint', uv2, depth, dir, position ) return position; } clampUV(v) { return (v + 1) % 1; // 使输出在 0-1 } planeFit(dir, position, pL, pR, pB, pT) { //求平均法线 var normal = new Vector3(); var plane = new Plane(); function addNormal(p1, p2) { //根据临接的四个点,分别求法线,然后法线相加能得到平均法线 if (!p1 || !p2) return; plane.setFromCoplanarPoints(position, p1, p2); //console.log('normalSub', plane.normal) normal.addScaledVector(plane.normal, dir.dot(plane.normal) < 0 ? 1 : -1); //根据面的朝向判断加还是减 } addNormal(pL, pB); addNormal(pL, pT); addNormal(pR, pB); addNormal(pR, pT); if (0 !== normal.x || 0 !== normal.y || 0 !== normal.z) { normal.normalize(); //console.log(normal) return normal; } /* 四个面拼成一个菱形 */ } } var { PanoSizeClass: PanoSizeClass$3, Vectors: Vectors$1, GLCubeFaces: GLCubeFaces$3, PanoramaEvents: PanoramaEvents$1 } = Potree.defines; var rot90$1 = new Quaternion().setFromAxisAngle(new Vector3(0, 0, 1), Math.PI / 2); //使用的是刚好适合全景图的,给cube贴图需要转90° var raycaster = new Raycaster(); //let currentlyHovered = null; var texLoader$4 = new TextureLoader(); var addLine = function addLine(origin, dir, len) { var color = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : '#fff'; var line1 = LineDraw.createLine([origin, origin.clone().add(dir.clone().multiplyScalar(len || 1))], { color }); //console.log(origin.toArray(), dir.toArray()) viewer.scene.scene.add(line1); return line1; }; var tileArr = []; var previousView = { controls: null, position: null, target: null }; var HighMapCubeWidth = 1; var directionFactor = 400; //原先10,几乎只往距离近的走了;设置太大容易略过近处漫游点走向下坡,因为鼠标一般在地面,下坡的漫游点更有利 var getID = function () { var id = 0; return function () { return id++; }; }(); class Images360 extends EventDispatcher { constructor(viewer) { super(); this.viewer = viewer; this.panos = []; this.neighbourMap = {}; this.disMap = {}; this.node = new Object3D(); this.node.name = 'ImagesNode'; this.cubePanos = []; this.fastTranMaskPass = new FastTranPass(viewer.renderer); { this.cube = new Mesh(new BoxBufferGeometry(1, 1, 1, 1), new ModelTextureMaterial()); Potree.Utils.updateVisible(this.cube, 'showSkybox', false); this.cube.layers.set(Potree.config.renderLayers.skybox); this.cube.name = 'skyboxCube'; viewer.scene.scene.add(this.cube); if (Potree.settings.mergeType2 && Potree.settings.modelSkybox) { this.materialInside = new ModelTextureMaterial(); this.materialInside.dontChangeDepth = true; //chunk本身深度是对的,不用深度图,否则过渡会有很多裂痕 this.cube.material.defines.depth_background = ''; //skybox的深度改得向后一些, 避免盖住chunk,造成坑坑洼洼 this.addEventListener('endChangeMode', e => { viewer.objs.children.forEach(e => { if (e.panos) this.changeModelMat(e); }); //干脆全部换,就不容易错 }); } } if (Potree.settings.testCube) { this.cubeTest = this.cube.clone(); this.cubeTest.material = new MeshBasicMaterial({ wireframe: true, color: '#FF3377', transparent: true, opacity: 0.7, depthWrite: false, depthTest: false }); viewer.scene.scene.add(this.cubeTest); this.cubeTest.visible = true; } this.flying_ = false; this.currentPano = null; this.mouseLastMoveTime = Date.now(); this.scrollZoomSpeed = 0.06; this.zoomLevel = 1; this.tileDownloader = new TileDownloader(); this.qualityManager = new QualityManager(); this.panoRenderer = new PanoRenderer(viewer, this.tileDownloader, this.qualityManager); this.basePanoSize = this.qualityManager.getPanoSize(PanoSizeClass$3.BASE); this.standardPanoSize = this.qualityManager.getPanoSize(PanoSizeClass$3.STANDARD); this.highPanoSize = this.qualityManager.getPanoSize(PanoSizeClass$3.HIGH); this.ultraHighPanoSize = this.qualityManager.getPanoSize(PanoSizeClass$3.ULTRAHIGH); this.tileDownloader.processPriorityQueue = !1; this.tileDownloader.tilePrioritizer = new TilePrioritizer(this.qualityManager, this.basePanoSize, this.standardPanoSize, this.highPanoSize, this.ultraHighPanoSize); { //高分辨率cube 放大 this.addHighMapCube(); viewer.addEventListener(PanoramaEvents$1.Enter, e => { this.setHighMap(e.newPano); }); viewer.addEventListener('panoSetZoom', e => { if (Potree.settings.displayMode == 'showPanos') { e.zoomed ? this.showHighMap() : this.hideHighMap(); //add } }); } this.depthSampler = new DepthImageSampler(); viewer.fpControls.addEventListener('dollyStopCauseUnable', e => { if ( /* e.hoverViewport != viewer.mainViewport || */!Potree.settings.zoom.enabled) return; if (e.scale != void 0) { //触屏 this.zoomBy(e.scale, e.pointer); } else { //滚轮 var zoom; if (e.delta > 0) { zoom = 1 + this.scrollZoomSpeed; } else { zoom = 1 - this.scrollZoomSpeed; } e.delta != 0 && this.zoomBy(zoom); } }); var click = e => { //不用"mouseup" 是因为 mouseup有drag object时也会触发 if (e.clickElement || Potree.settings.unableNavigate || this.flying || !e.isTouch && e.button != MOUSE.LEFT || e.drag && e.drag.object //拖拽结束时不算 /* || Potree.settings.editType == 'pano' && viewer.modules.PanoEditor.entered */ //|| Potree.settings.editType == 'merge' && !e.intersectPoint || viewer.inputHandler.hoveredElements[0] && viewer.inputHandler.hoveredElements[0].isModel && e.intersectPoint.distance > viewer.inputHandler.hoveredElements[0].distance || Potree.settings.editType == 'merge' && !Potree.settings.mergeType2 //|| Potree.settings.mergeType2 && Potree.settings.displayMode == 'showPointCloud' ) return; if (Potree.settings.editType != 'pano' && Potree.settings.editType != 'merge') { var _viewer$mapViewer; if (e.hoverViewport == ((_viewer$mapViewer = viewer.mapViewer) === null || _viewer$mapViewer === void 0 ? void 0 : _viewer$mapViewer.viewports[0])) { return viewer.mapViewer.dispatchEvent(e /* {type:'global_click',e } */); } else if (e.hoverViewport != viewer.mainViewport) { //如数据集校准其他viewport return; } } if (!Potree.settings.dblToFocusPoint /* && this.currentPano */) { //双击不会focus点云 或者 已经focusPano了 this.flyToPanoClosestToMouse(); } }; viewer.addEventListener('global_click', click); viewer.addEventListener("global_mousemove", e => { if (!Potree.settings.unableNavigate && Potree.settings.ifShowMarker && e.hoverViewport == viewer.mainViewport) { //如果不显示marker,就在点击时再更新 this.updateClosestPano(e.intersect); } }); this.addEventListener('markerHover', e => { this.updateClosestPano(e.pano, e.hovered); }); if (!Potree.settings.isOfficial) { this.domRoot = viewer.renderer.domElement.parentElement; var elUnfocus = $(""); elUnfocus.css({ position: "absolute", right: '25%', bottom: '20px', zIndex: "10000", fontSize: '1em', color: "black", display: 'none', background: 'rgba(255,255,255,0.8)' }); elUnfocus.on("click", () => this.unfocus()); this.elUnfocus = elUnfocus; this.domRoot.appendChild(elUnfocus[0]); if (Potree.settings.editType != 'merge') { /* let elHide = $("") elHide.css({ position : "absolute", right : '40%', bottom: '20px', zIndex: "10000", fontSize:'1em' ,color:"black", width : '100px', background:'rgba(255,255,255,0.8)', }) this.domRoot.appendChild(elHide[0]); elHide.on("click", (e) => { let visi = Potree.Utils.getObjVisiByReason(viewer.scene.pointclouds[0], 'force') viewer.scene.pointclouds.forEach(e=>{ Potree.Utils.updateVisible(e, 'force', !visi) }) elHide.val(!visi ? "隐藏点云" : "显示点云") }); */ viewer.addEventListener('allLoaded', () => { var interval = setInterval(() => { var _viewer$scene$pointcl, _viewer$scene$pointcl2; var attrs = ((_viewer$scene$pointcl = viewer.scene.pointclouds[0].root.geometryNode) === null || _viewer$scene$pointcl === void 0 ? void 0 : _viewer$scene$pointcl.geometry.attributes) || ((_viewer$scene$pointcl2 = viewer.scene.pointclouds[0].root.geometry) === null || _viewer$scene$pointcl2 === void 0 ? void 0 : _viewer$scene$pointcl2.attributes); if (!attrs) return; if (attrs.ir) { var btnHot = $(''); $('body').append(btnHot); var onHot = false; btnHot.on('click', () => { onHot = !onHot; Potree.settings.showHotIr = onHot; btnHot.text((onHot ? '关闭' : '打开') + '热成像'); }); } if (attrs.temp) { var btn = $(''); $('body').append(btn); var on = false; btn.on('click', () => { on = !on; Potree.settings.showHotTemp = on; btn.text((on ? '关闭' : '打开') + '火调温度'); }); } if (attrs.classification) { var btnSeg = $(''); $('body').append(btnSeg); var onSeg = false; btnSeg.on('click', () => { onSeg = !onSeg; Potree.settings.showClass = onSeg; btnSeg.text((onSeg ? '关闭' : '打开') + '分类'); }); } clearInterval(interval); }, 100); }, { once: true }); } var elDisplayModel = $(""); elDisplayModel.css({ position: "absolute", right: '65%', bottom: '20px', zIndex: "10000", fontSize: '1em', color: "black", width: '100px', background: 'rgba(255,255,255,0.8)' }); this.domRoot.appendChild(elDisplayModel[0]); elDisplayModel.on("click", e => { if (Potree.settings.displayMode == 'showPointCloud' && this.panos.length == 0) return; Potree.settings.displayMode = Potree.settings.displayMode == 'showPointCloud' ? 'showPanos' : 'showPointCloud'; }); this.elDisplayModel = elDisplayModel; if (viewer.mapViewer) { var mapStyleBtn = $(""); mapStyleBtn.css({ position: "absolute", right: '50%', bottom: '20px', zIndex: "10000", fontSize: '1em', color: "black", width: '100px', background: 'rgba(255,255,255,0.8)' }); this.domRoot.appendChild(mapStyleBtn[0]); var map = viewer.mapViewer.mapLayer.maps.find(e => e.name == 'map'); mapStyleBtn.on("click", e => { map.switchStyle(map.style == 'satellite' ? 'standard' : 'satellite'); mapStyleBtn.val(map.style == 'satellite' ? '卫星' : '普通'); }); } } { //切换模式 var displayMode = ''; this.latestRequestMode = ''; //因为可能延迟,所以记录下每次的请求模式,延迟后判断这个 Object.defineProperty(Potree.settings, "displayMode", { get: function get() { return displayMode; }, set: mode => { this.latestRequestMode = mode; console.warn('Request setMode: ' + mode); this.dispatchEvent({ type: 'requestMode', mode }); var config2; var config = Potree.config.displayMode[mode]; if (this.isAtPano() && !this.latestToPano) { config2 = config.atPano; } else { config2 = config.transition; } var pano = this.currentPano; var changeTileDownload = () => { if (config2.showSkybox || config2.showPoint && config2.pointUsePanoTex) { this.tileDownloader.start(); this.currentPano && this.currentPano.enter(); } else { this.tileDownloader.stop(); this.currentPano && this.currentPano.exit(); this.nextPano && viewer.cancelLoad(this.nextPano); } }; if (mode != displayMode) { var camera = viewer.scene.getActiveCamera(); if (mode == 'showPanos' && viewer.mainViewport.view.isFlying()) { //飞完才能切换全景 var f = () => { if (this.latestRequestMode == mode) { //如果ui还是停在这个模式的话 Potree.settings.displayMode = mode; } viewer.mainViewport.view.removeEventListener('flyingDone', f); viewer.mainViewport.view.removeEventListener('flyCancel', f); }; viewer.mainViewport.view.addEventListener('flyingDone', f); //once viewer.mainViewport.view.addEventListener('flyCancel', f); return; } if (this.isAtPano() && !this.latestToPano) { config2 = config.atPano; } else { config2 = config.transition; if (mode == 'showPanos') { //自动飞入一个pano //要改成飞进最近的。。。 if (this.panos.length == 0) return; //this.modeChanging = true //主要是因为到全景图不会立刻成功 var wait = e => { console.log('flyToPanoDone'); this.removeEventListener('flyToPanoDone', wait); setTimeout(() => { if (this.latestRequestMode == mode) { Potree.settings.displayMode = mode; //被打断,重新执行 } }, e.makeIt ? 1 : 50); }; this.addEventListener('flyToPanoDone', wait); //等待飞行完毕。flyToPano的callback可能不执行所以换这个。但也可能被cancel this.flyToPano({ pano: this.findNearestPano() //dealDoneWhenCancel:true, /* callback: ()=>{ setTimeout(()=>{ //防止循环,所以延迟 if(this.latestRequestMode == mode ){ Potree.settings.displayMode = mode } },1) } */ }); return; } else {} } changeTileDownload(); if (config2.showSkybox || config2.pointUsePanoTex) { var _wait = () => { //console.log('waitdone') //if(e.pano && e.pano != this.currentPano)return //loadedDepthImg setTimeout(() => { if (this.latestRequestMode == mode) { Potree.settings.displayMode = mode; } }, 1); }; //this.updateDepthTex() if (this.checkAndWaitForPanoLoad(pano, this.basePanoSize, _wait)) { //console.log('等待贴图加载2', this.currentPano.id) return; } var types = { 'seg': 'showClass', 'ir': 'showHotIr', 'temp': 'showHotTemp' }; for (var type in types) { if (Potree.settings[types[type]] && pano['has_' + type] && !pano[type + 'Tex']) { pano.loadTypeImg(type); return pano.addEventListener('loaded_' + type, _wait, { once: true }); } } } viewer.scene.pointclouds.forEach(e => { Potree.Utils.updateVisible(e, 'displayMode', config2.showPoint, 2); }); if (config2.pointUsePanoTex) { viewer.scene.pointclouds.forEach(e => { e.material.setProjectedPanos(this.currentPano, this.currentPano, 1); }); } else { viewer.scene.pointclouds.forEach(e => { e.material.stopProjectedPanos(); }); } Potree.Utils.updateVisible(this.cube, 'showSkybox', config2.showSkybox); // this.cube.visible = config2.showSkybox //this.cube.visible = config.atPano.showSkybox if (this.cube.visible) { //this.cube.material.setProjectedPanos(this.currentPano, this.currentPano, 1) this.setProjectedPanos({ progress: 1, ifSkybox: true, ifPointcloud: false, easeInOutRatio: 0, pano0: this.currentPano, pano1: this.currentPano }); } else { this.smoothZoomTo(1); this.resetHighMap(); this.hideHighMap(); } /* viewer.dispatchEvent({ type: "enableChangePos", canLeavePano : config.canLeavePano , viewport: }) */ //viewer.mainViewport.unableChangePos = !config.canLeavePano displayMode = mode; if (mode == 'showPanos') { camera.far = viewer.farWhenShowPano; //修改far Potree.settings.pointDensity = 'panorama'; if (Potree.config.displayMode.showPanos.transition.pointUsePanoTex) { viewer.scene.pointclouds.forEach(e => { e.material.pointSizeType = 'FIXED'; }); } this.updateCube(this.currentPano); //viewer.setControls(viewer.fpControls) } else { if (camera.limitFar) camera.far = Potree.settings.cameraFar; //修改far Potree.settings.pointDensity = Potree.settings.UserPointDensity; //Potree.sdk && Potree.sdk.scene.changePointOpacity() if (Potree.config.displayMode.showPanos.transition.pointUsePanoTex) { viewer.scene.pointclouds.forEach(e => { e.material.pointSizeType = Potree.config.material.pointSizeType; }); } //Potree.settings.editType == 'merge' && viewer.setControls(viewer.orbitControls) } camera.updateProjectionMatrix(); if (this.elDisplayModel) { this.elDisplayModel.val(mode == 'showPointCloud' ? ">>全景" : '>>点云'); } /* this.panos.forEach(e=>{ Potree.Utils.updateVisible(e, 'modeIsShowPanos', mode == 'showPanos', 1, mode == 'showPanos' ? 'add':'cancel') // }) */ this.dispatchEvent({ type: 'endChangeMode', mode }); console.log('setModeSuccess: ' + mode); } else { changeTileDownload(); //this.dispatchEvent({type:'endChangeMode',mode}) } } }); Potree.settings.displayMode = 'showPointCloud'; } // 切换模式 end { var showHotTemp = null; //火调温度 /* --目前仅需点云模式 */ Object.defineProperty(Potree.settings, "showHotTemp", { get: function get() { return showHotTemp; }, set: show => { if (showHotTemp == show) return; showHotTemp = show; viewer.switchHotType(); var pano = this.currentPano; if (Potree.settings.displayMode == 'showPanos' && pano.has_temp) { var change = () => { this.cube.material.updateTempEnable(); }; if (show && !pano.tempTex) { pano.loadTypeImg('temp'); pano.addEventListener('loaded_temp', change, { once: true }); } else { change(); } } } }); } { //优先级最低 var showHotIr = null; Object.defineProperty(Potree.settings, "showHotIr", { get: function get() { return showHotIr; }, set: show => { if (showHotIr == show) return; showHotIr = show; viewer.switchHotType(); var pano = this.currentPano; if (Potree.settings.displayMode == 'showPanos' && pano.has_ir) { var change = () => { this.cube.material.updateTempEnable(); }; if (show && !pano.irTex) { pano.loadTypeImg('ir'); pano.addEventListener('loaded_ir', change, { once: true }); } else { change(); } } } }); } { //优先级最高 var showClass = null; Object.defineProperty(Potree.settings, "showClass", { get: function get() { return showClass; }, set: show => { if (showClass == show) return; showClass = show; var pano = this.currentPano; viewer.scene.pointclouds.forEach(e => e.updateAttrAuto()); if (Potree.settings.displayMode == 'showPanos' && pano.has_seg) { var change = () => { this.cube.material.updateClassEnable(); }; if (show && !pano.segTex) { pano.loadTypeImg('seg'); pano.addEventListener('loaded_seg', change, { once: true }); } else { change(); } } } }); } { // var currentPano = null; Object.defineProperty(this, "currentPano", { get: function get() { return currentPano; }, set: function set(e) { if (e != currentPano) { //console.log('set currentPano ', e.id) currentPano && currentPano.exit(); e && e.enter(); currentPano = e; } } }); } { //是否显示marker var ifShowMarker = true; Object.defineProperty(Potree.settings, "ifShowMarker", { get: function get() { return ifShowMarker; }, set: show => { show = !!show; if (show != ifShowMarker) { this.panos.forEach(pano => { Potree.Utils.updateVisible(pano, 'ifShowMarker', show, 1); }); //this.emit('markersDisplayChange', show) ifShowMarker = show; viewer.dispatchEvent('showMarkerChanged'); viewer.dispatchEvent('content_changed'); } } }); } viewer.addEventListener("update", () => { this.update(viewer); }); //viewer.inputHandler.addInputListener(this); var keys = { FORWARD: ['W'.charCodeAt(0), 38], BACKWARD: ['S'.charCodeAt(0), 40], LEFT: ['A'.charCodeAt(0), 37], RIGHT: ['D'.charCodeAt(0), 39] }; viewer.inputHandler.addEventListener('keydown', e => { if (Potree.settings.displayMode == 'showPanos') { for (var i in keys) { if (keys[i].some(a => a == e.keyCode)) { switch (i) { case 'FORWARD': this.flyLocalDirection(Vectors$1.FORWARD.clone()); break; case 'BACKWARD': this.flyLocalDirection(Vectors$1.BACK.clone()); break; case 'LEFT': this.flyLocalDirection(Vectors$1.LEFT.clone()); break; case 'RIGHT': this.flyLocalDirection(Vectors$1.RIGHT.clone()); break; } break; } } } }); } changeModelMat(model /* , isCurModel */) { if (model !== null && model !== void 0 && model.is4dkkModel) { if (model.fileType == '3dTiles') { viewer.setAllTilesets(model, child => { child.runtime.limit2lowestDepth(Potree.settings.displayMode == 'showPanos'); child.runtime.getTileset().tiles.forEach(e => { this.judgeModelMat(e.tileContent /* , isCurModel */); }); }); } else { this.judgeModelMat(model /* , isCurModel */); } } } judgeModelMat(object /* , isCurModel */) { if (!(Potree.settings.mergeType2 && Potree.settings.modelSkybox)) return; object.traverse(mesh => { if (mesh.material) { if (!mesh.materialOutside) { mesh.materialOutside = mesh.material; } //mesh.material = Potree.settings.displayMode == 'showPanos' && (this.nextPano?.pointcloud == object || this.currentPano.pointcloud == object || isCurModel) ? this.materialInside : mesh.materialOutside mesh.material = Potree.settings.displayMode == 'showPanos' ? this.materialInside : mesh.materialOutside; Potree.Utils.setObjectLayers(mesh, Potree.settings.displayMode == 'showPanos' ? 'skybox' : 'model'); //为了渲染到rtEDL } }); } updateDepthTex(pano) { if (this.currentPano != pano || !pano.depthTex) return; //this.depthSampler.changeImg(pano.depthTex.image); //pick sampler要飞到了才能切换图,而skybox贴图是随着全景图切换而切换的 this.cube.material.updateDepthTex(pano); //确保一下 } findNearestPano(pos) { var panos = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.panos; pos = pos ? new Vector3().copy(pos) : this.position; var result = Common.sortByScore(panos, [Images360.filters.isEnabled()], [e => -e.position.distanceTo(pos) / e.pointcloud.scale.x]); var pano = result[0] && result[0].item; return pano; } /* set flying(v){//正在飞向pano this.flying_ = !!v //console.log('this.flying_ ', !!v ) //this.emit('flying', this.flying_) let config = Potree.config.displayMode[Potree.settings.displayMode] viewer.mainViewport.unableChangePos = !config.canLeavePano || !!v } get flying(){ return this.flying_ } */ flyLocalDirection(dir) { var direction = this.getDirection(dir), option1 = 1 === dir.y ? .4 : .75, option2 = 1 === Math.abs(dir.x); return this.flyDirection(direction, option1, option2, true); } getDirection(e) { if (!e) { return viewer.scene.view.direction; } else { return e = e ? e : new Vector3().copy(Vectors$1.FORWARD), e.applyQuaternion(viewer.mainViewport.camera.quaternion); } } get position() { return this.viewer.scene.view.position.clone(); } isAtPano() { var precision = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1e-6; //是否在某个漫游点上 if (precision) { return this.currentPano && math.closeTo(viewer.scene.view.position, this.currentPano.position, precision); } return this.currentPano && viewer.scene.view.position.equals(this.currentPano.position); } updateProjectedPanos() { //更新材质贴图 //console.warn('updateProjectedPanos') this.projectedPano0 && this.projectedPano1 && this.setProjectedPanos({ pano0: this.projectedPano0, pano1: this.projectedPano1 }); } setProjectedPanos() { var o = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //设置cube和点云的材质贴图 this.cube.material.setProjectedPanos(o.pano0, o.pano1, o.progress); this.materialInside && this.materialInside.setProjectedPanos(o.pano0, o.pano1, o.progress); if (o.ifPointcloud) { viewer.scene.pointclouds.forEach(e => { e.material.setProjectedPanos(o.pano0, o.pano1, o.progress, o.easeInOutRatio); }); } //console.warn('setProjectedPanos ', o.pano0.id , o.pano1.id) this.projectedPano0 = o.pano0; this.projectedPano1 = o.pano1; } get latestToPano() { return this._latestToPano; } set latestToPano(toPano) { this._latestToPano = toPano; //console.warn('latestToPano',toPano) } cancelFlyToPano(toPano) { //取消当前已有的飞行准备,前提是相机还未移动 //2024.11.13删除isFlying的判断,因为在done中延时1之后居然isFlying有概率还是true bugID=47743 if ( /* viewer.mainViewport.view.isFlying() || */toPano && this.latestToPano != toPano) { return; } //Potree.Log('cancelFlyToPano', this.latestToPano && this.latestToPano.pano.id) this.nextPano = null; this.latestToPano = null; } flyToPano(toPano) { //飞向漫游点 if (!toPano) return; if (typeof toPano == 'number') toPano = this.panos[toPano]; if (toPano instanceof Panorama) { toPano = { pano: toPano }; } var done = (makeIt, disturb) => { //console.log('flyToPano done ', toPano.pano.id, makeIt, disturb ) if (makeIt || disturb) { // disturb已经开始飞行但中途取消 toPano.callback && toPano.callback(makeIt); //this.flying = false this.cancelFlyToPano(toPano); this.updateClosestPano(this.closestPano, false); //飞行结束后取消点击漫游点时得到的closestPano } else {} this.dispatchEvent({ type: 'flyToPanoDone', makeIt, disturb }); this.fastTranMaskPass.stop(); toPano.deferred && toPano.deferred.resolve(makeIt); //测量线截图时发现,resolve需要写在flying=false 后才行。 }; if (!toPano.pano.enabled) return done(false, true); //Potree.Log('hope flyToPano: '+toPano.pano.id, toPano.pano.position.toArray() ) if (!toPano.canCancelLast && this.latestToPano && this.latestToPano != toPano && ( //还在飞 this.latestToPano.pano != this.currentPano || !this.isAtPano())) { //如果旧的toPano只在pano旋转镜头,就直接取消旧的,继续执行 return done(false); } if (this.currentPano == toPano.pano && this.isAtPano() && !toPano.target && !toPano.quaternion) { //已在该pano this.dispatchEvent({ type: 'flyToPano', toPano }); return done(true); } //Potree.Log('flyToPano: '+toPano.pano.id, toPano.pano.position.toArray() /* this.latestToPano && this.latestToPano.pano.id */ ) var target = toPano.target; var config = Potree.config.displayMode[Potree.settings.displayMode]; var pano = toPano.pano; var dis = pano.position.distanceTo(this.position); this.nextPano = pano; this.latestToPano = toPano; //this.flying = true //防止新的请求 //Potree.Log('flyToPano:'+pano.id + ' , duration:'+toPano.duration, null, 12) { //不飞的话是否不要执行这段? var wait = e => { //console.log('wait done', pano.id) if ( /* e.pano && */this.latestToPano && pano != this.latestToPano.pano) return; //loadedDepthImg if (this.latestToPano != toPano) return Potree.Log('已经取消', pano.id); //如果取消了 setTimeout(() => { if (this.latestToPano != toPano) return; this.flyToPano(toPano); }, 1); }; if (!pano.depthTex && pano.pointcloud.hasDepthTex) { //点云模式也要加载depthTex,因获取neighbour需要用到 console.log('等待加载depthtex', pano.id); pano.addEventListener('loadedDepthImg', wait, { once: true }); return pano.loadDepthImg(); } if (config.atPano.showSkybox || config.atPano.pointUsePanoTex) { var types = { 'seg': 'showClass', 'ir': 'showHotIr', 'temp': 'showHotTemp' }; for (var type in types) { if (Potree.settings[types[type]] && pano['has_' + type] && !pano[type + 'Tex']) { pano.loadTypeImg(type); return pano.addEventListener('loaded_' + type, wait, { once: true }); } } var a = this.updateCube(this.currentPano, toPano.pano); if (a == 'useBound') { toPano.useBound = true; } if (this.checkAndWaitForPanoLoad(pano, toPano.basePanoSize || this.basePanoSize, wait)) { return; } } } config = Potree.config.displayMode[Potree.settings.displayMode]; //可能换了 var pointcloudVisi = config.atPano.showPoint; //viewer.scene.pointclouds[0].visible Potree.Utils.updateVisible(this.cube, 'showSkybox', config.atPano.showSkybox); // this.cube.visible = config.atPano.showSkybox //console.log('开始飞1') if (config.transition.showPoint) { viewer.scene.pointclouds.forEach(e => { Potree.Utils.updateVisible(e, 'displayMode', true); }); } var endPosition = pano.position.clone(); var T = Potree.config.transitionsTime; var maxDis = 7; var duration = toPano.duration == void 0 ? Math.min(dis, maxDis) * T.flytimeDistanceMultiplier + T.flyMinTime : toPano.duration; if (config.transition.showSkybox || config.transition.pointUsePanoTex) { if (Potree.settings.fastTran) { this.fastTranMaskPass.start(); //截图当前画面 viewer.scene.view.position.copy(endPosition); } this.setProjectedPanos({ progress: 0, ifSkybox: this.cube.visible, ifPointcloud: config.transition.pointUsePanoTex, easeInOutRatio: pointcloudVisi ? 0.3 : 0, pano0: this.currentPano, pano1: pano }); } if (toPano.useBound) { duration = Math.min(1500, duration); toPano.easeName = 'easeInOutQuad'; } else { if (endPosition.equals(this.position)) toPano.easeName = 'easeOutSine'; } { toPano.easeName = toPano.easeName || 'linearTween'; toPano.duration = duration; this.beforeFlyToPano(toPano); } /* let fly = ()=>{ this.dispatchEvent({type:'flyToPano', toPano}) viewer.scene.view.setView({position:endPosition, target, quaternion:toPano.quaternion , duration, callback:()=>{ if(!config.atPano.pointUsePanoTex){ viewer.scene.pointclouds.forEach(e=>{ e.material.stopProjectedPanos() }) } this.currentPano = pano; this.nextPano = null; if(Potree.settings.displayMode == 'showPanos'){ viewer.scene.pointclouds.forEach(e=>{ Potree.Utils.updateVisible(e, 'displayMode',pointcloudVisi) }) } done(true); this.updateDepthTex(this.currentPano) }, onUpdate:(progress)=>{ //console.log('uniforms progress',progress) this.cube.material.uniforms.progress.value = progress viewer.scene.pointclouds.forEach(e=>{ e.material.uniforms.progress.value = progress }) }, cancelFun:()=>{ done(false, true) }, Easing:easeName }) //duration > 0 && (this.flying = true) //再写一遍 防止cancel其他项目导致flying为false } */ var _onUpdate = progress => { this.cube.material.uniforms.progress.value = progress; this.materialInside && (this.materialInside.uniforms.progress.value = progress); viewer.scene.pointclouds.forEach(e => { e.material.uniforms.progress.value = progress; }); }; var fly = () => { var startProgress = toPano.progress = toPano.progress || 0; var loadNextProgress = MathUtils.clamp(1 - 2.5 / dis, 0.3, 0.8); //console.log(loadNextProgress, (1-loadNextProgress) * dis ) this.dispatchEvent({ type: 'flyToPano', toPano }); viewer.scene.view.setView({ position: endPosition, target, quaternion: toPano.quaternion, duration: toPano.duration, onUpdate: (progress_, delta) => { var progress = startProgress + progress_ * (1 - startProgress); var currentSpeed; if (progress_ != 1 && progress_ != 0) { // 1的时候不准,往往偏小, 0的时候速度为0,也不记录 currentSpeed = (progress - toPano.progress) * dis / delta; //记录下当前速度,当变为匀速时可以过渡到flySpeed } else { currentSpeed = toPano.currentSpeed || 0; } toPano.currentSpeed = currentSpeed; toPano.progress = progress; //console.log('progress_', progress_, 'delta',delta , 'progress', progress/*, 'currentSpeed', currentSpeed, */ ) if (progress > loadNextProgress && toPano.easeName == 'linearTween' && currentSpeed) { // 减速. 如果仅旋转就不停止 //console.log('减速', /* currentSpeed , */progress_ ) toPano.easeName = 'easeOutSine'; var restDis = (1 - progress) * dis; toPano.duration = Math.PI / 2 * restDis / currentSpeed; // 这样能保证初始速度为currentSpeed viewer.scene.view.cancelFlying('all', false); //为了防止执行cancelFun先主动cancel toPano.flyCount = 2; fly(toPano); } _onUpdate(progress); }, callback: () => { if (!config.atPano.pointUsePanoTex) { viewer.scene.pointclouds.forEach(e => { e.material.stopProjectedPanos(); }); } this.lastPano = this.currentPano; //记录,调试 this.currentPano = pano; this.nextPano = null; if (Potree.settings.displayMode == 'showPanos') { viewer.scene.pointclouds.forEach(e => { Potree.Utils.updateVisible(e, 'displayMode', pointcloudVisi); }); } done(true); this.updateDepthTex(this.currentPano); //删除dispose的depthTex }, cancelFun: () => { done(false, true); }, Easing: toPano.easeName, ignoreFirstFrame: toPano.flyCount != 2 //变换transition时不停一帧 }); }; if (Potree.settings.displayMode == 'showPanos') { setTimeout(fly, 40); //更新geo后缓冲 } else { fly(); } //console.log('flyToPano:', toPano.pano.id) } beforeFlyToPano(toPano) { if (this.currentPano != toPano.pano) { if (Potree.settings.displayMode == 'showPanos') { this.resetHighMap(); } this.smoothZoomTo(toPano.zoomLevel || 1, toPano.duration / 2); } } /* updateCube(pano0, pano1){ if(Potree.settings.displayMode != 'showPanos')return if(!viewer.scene.pointclouds.some(e=>!e.hasDepthTex)) return this.updateCube2(pano0, pano1) //都hasDepthTex的话 let useBound = (bound, size)=>{ size = size || bound.getSize(new THREE.Vector3) let center = bound.getCenter(new THREE.Vector3) size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth)) this.cube.scale.copy(size) this.cube.position.copy(center) } let getPanoBound = (pano)=>{//因漫游点可能在点云外部,如室外平地,所以需要union进漫游点 let panoBound = new THREE.Box3 panoBound.expandByPoint(pano.position) panoBound.expandByVector(new THREE.Vector3(10,10,10));//give pano a margin return pano.pointcloud.bound.clone().union(panoBound) } let getDis = (bound1, bound2)=>{ //获取bound1边界到bound2边界距离 if(bound1.intersectsBox(bound2))return 0 let center1 = bound1.getCenter(new THREE.Vector3) let center2 = bound2.getCenter(new THREE.Vector3) let dis = center1.distanceTo(center2) let dis1 = bound1.distanceToPoint(center2) let dis2 = bound2.distanceToPoint(center1) return dis1 + dis2 - dis } if(pano1){//过渡 if(pano0.pointcloud == pano1.pointcloud){//同一个数据集内的过渡 useBound(getPanoBound(pano0)) }else{//非同一个数据集内的过渡 let bound = getPanoBound(pano0).union(getPanoBound(pano1)) if(getDis(pano0.pointcloud.bound, pano1.pointcloud.bound) < 100){ useBound(bound) }else{//如果两个数据集boundingbox距离大于这个距离,扩大一下,防止精度问题导致失真//对很远的数据集似乎没有什么用 let size = bound.getSize(new THREE.Vector3) let max = Math.max(size.x, size.y, size.z) size.set(max,max,max) useBound(pano0.pointcloud.bound.clone().union(pano1.pointcloud.bound), size) } } }else{ useBound(getPanoBound(pano0)) //假定一个数据集不会太大,否则会造成失真,且bump时移动距离看起来很小。或者可以考虑用固定大小 } } */ getIntersect(pano, dir, origin) { if (pano && pano.pointcloud.hasDepthTex) { return this.depthSampler.sample({ dir }, pano, true); } else { origin = origin || pano.position; return viewer.inputHandler.getIntersect({ viewport: viewer.inputHandler.hoverViewport, onlyGetIntersect: true, usePointcloud: true, point: origin.clone().add(dir), cameraPos: origin }); } } /* updateCube(pano0, pano1){//增加细分的版本,且垂直方向上也分多个 if(Potree.settings.displayMode != 'showPanos')return console.log('updateCube',pano0.id, pano1&&pano1.id) let f = (bound, size)=>{ size = size || bound.getSize(new THREE.Vector3) let center = bound.getCenter(new THREE.Vector3) size.max(new THREE.Vector3(HighMapCubeWidth,HighMapCubeWidth,HighMapCubeWidth)) this.cube.scale.copy(size) this.cube.position.copy(center) } this.cube.geometry.dispose(); if(pano1){//过渡 let count1, count2; let panoIndex = 0 let add = (pano, dir )=>{ let getPI = function(index, panoIndex_){ if(panoIndex_ == void 0) panoIndex_ = panoIndex return (count1 * count2 + 2 ) * panoIndex_ + index + 2 } let minZ, maxZ minZ = pano.floorPosition.z {//天花板高度值 //用三个间隔120度散开,和中心垂直线成一定夹角的三个向量去求 最高高度 (不求平均的原因:万一是0不好算) let rotMat = new THREE.Matrix4().makeRotationX(THREE.Math.degToRad(40))// 角度不能小于天花板中空的半径,大概就是0.2*Math.PI=36度 let dir1 = new THREE.Vector3(0,0,1).applyMatrix4(rotMat) let rotMat1 = new THREE.Matrix4().makeRotationZ(Math.PI*2 / 3); let rotMat2 = new THREE.Matrix4().makeRotationZ(-Math.PI*2 / 3); let dir2 = dir1.clone().applyMatrix4(rotMat1) let dir3 = dir1.clone().applyMatrix4(rotMat2) let zs = [dir1,dir2,dir3].map(dir_=>{ let intersect = this.depthSampler.sample( {dir: dir_}, pano, true ) let z = intersect ? intersect.location.z : pano.position.z return z }) zs.sort((a,b)=>{return b-a});//得最大值 maxZ = zs[0] let min = pano.position.z + 1 if(maxZ < pano.position.z + 0.04){//户外 maxZ = pano.position.z + 50 } maxZ = Math.max(min,maxZ) console.log(pano.id, 'maxZ:',maxZ ) console.log(pano.id, 'minZ:',minZ ) } [maxZ, minZ ].forEach(z=>{ posArr.push(pano.position.clone().setZ(z)) }) const angle = Math.PI/8 let getDir = (angle_)=>{ let rotMat = new THREE.Matrix4().makeRotationZ(angle_) return dir.clone().applyMatrix4(rotMat) } let dirs = [ getDir(angle*4), getDir(angle*3), getDir(angle*2), getDir(angle), dir.clone(), getDir(-angle) , getDir(-angle*2), getDir(-angle*3), getDir(-angle*4) ]; // 夹角总和180度 count1 = dirs.length dirs.forEach((dir, index)=>{//获取在这个方向上和墙体intersect的最远距离,使用中、上、下三个方向 let dirs_ = [ dir.clone().setZ(Math.tan(THREE.Math.degToRad(30))).normalize(), dir.clone().setZ(Math.tan(THREE.Math.degToRad(15))).normalize(), dir.clone(), // 水平方向 dir.clone().setZ(-Math.tan(THREE.Math.degToRad(15))).normalize(), dir.clone().setZ(-Math.tan(THREE.Math.degToRad(30))).normalize(), ]; count2 = dirs_.length dirs_.forEach((dir_, i) =>{ let intersect = this.depthSampler.sample( {dir: dir_}, pano, true ) let location if(!intersect){ //超级远 let distance = 100 location = dir_.clone().multiplyScalar(distance).add(pano.position) intersect = {distance,location} } //intersect.distance location = intersect.location.clone() let shouldZ// = THREE.Math.clamp(intersect.location.z, minZ, maxZ); let needShrink = false if(i == 0){//最上方的点高度直接等于天花板 shouldZ = maxZ if(location.zminZ)location.z = minZ else needShrink = true } if(needShrink ){//需要在保持dir_不变的情况下修改z let len = (shouldZ - pano.position.z) / (intersect.location.z - pano.position.z) * intersect.distance location = dir_.clone().multiplyScalar(len).add(pano.position) } posArr.push(location) if(index!=0 && i!=0){//加入一块四方面 let curIndex = getPI(count2 * index + i); faceArr.push([curIndex-1, curIndex-count2-1, curIndex-count2], [curIndex-1, curIndex, curIndex-count2]) } }) if(index!=0){ let top = -2; let btm = -1 faceArr.push([getPI(count2*(index-1) ), getPI(count2*index ), getPI(top) ])//天花板扇形 faceArr.push([getPI(count2*index-1), getPI(count2*(index+1)-1), getPI(btm) ] )//地板扇形 }else{ //加入和另一个pano连接的其中一个侧边 let panoIndex2 = (panoIndex + 1) % 2; for(let i=1;i 2 && arguments[2] !== undefined ? arguments[2] : {}; //是否之间没有遮挡(在加载visibles之前,自己算) 最好pano0是currentPano if (!pano0 || !pano1) return; if (!viewer.scene.pointclouds.some(e => e.hasDepthTex) || Potree.settings.editType == 'pano') return true; //点云编辑页设置exist true但不恢复的话,得不到depthTex。 则Neighbours很难算。所以现在先全部可走。 var margin = 0.1; var map0 = this.neighbourMap[pano0.id]; //主 var map1 = this.neighbourMap[pano1.id]; //副 /* if(map0[pano1.id] != void 0){ ifNeighbour = map0[pano1.id] } if(!ifNeighbour && map1[pano0.id] != void 0){ ifNeighbour = map1[pano0.id] } */ var ifNeighbour = map1[pano0.id] == void 0 ? map0[pano1.id] : map0[pano1.id] && map1[pano0.id]; //如果map1没计算就只要返回map0 var dis = pano0.position.distanceTo(pano1.position); if (math.closeTo(dis, 0)) { //两个点相同 ifNeighbour = map1[pano0.id] = map0[pano1.id] = true; return true; } if (dontCompute) return ifNeighbour; var logSids = [/* '1566756240057044992|4','1566756240057044992|5' */]; var ifLog = pano0.sid == logSids[0] && pano1.sid == logSids[1] || pano0.sid == logSids[1] && pano1.sid == logSids[0]; if (ifLog) { console.log(2); } var ifSheltered = (mainPano, subPano) => { //该点前方近处是否都被遮挡 。 为了防止杂点干扰,多个方向探测 var vec = new Vector3().subVectors(subPano.position, mainPano.position).normalize(); var getDir = (angle_, upDown) => { //基于vec的方向 向左向右旋转 //if(upDown) vec.clone().applyAxisAngle(new THREE.Vector3(1, 0, 0), upDown); return vec.clone().applyAxisAngle(new Vector3(0, 0, 1), angle_); }; var minScore = 0.25; var angles = [20, 16, 12.5, 10, 8, 6, 4.5, 3, 1, -1, -3, -4.5, -6, -8, -10, -12.5, -16, -20]; //水平方向角度 3之间有较大概率杂点, 3以外一部分有较大概率有空隙所以紧凑点 var wellCount = 0; var seccess = []; for (var i = 0; i < angles.length; i++) { var rad = MathUtils.degToRad(angles[i]); var dir = getDir(rad); var color = void 0; var intersectPoint = viewer.images360.depthSampler.sample({ dir }, mainPano, true); if (!intersectPoint || intersectPoint.distance * Math.cos(Math.abs(rad)) /* + margin */ > dis) { //投影在vec方向的长度不超过漫游点间距 wellCount++; seccess.push(angles[i]); color = new Color('#0f8'); } ifLog && addLine(mainPano.position, dir, dis, color); } //console.log('ifSheltered') if (ifLog) { console.log('ifSheltered seccess', seccess, 'id:', mainPano.originID, subPano.originID); } var score = wellCount / angles.length; if (score >= minScore) { //合格个数 return score; } }; /* 识别一定角度范围内是否有遮挡时 几种奇葩的情况: 1 在墙的两边的但是墙是倾斜的漫游点: A / / / B 从B出发和BA成20度的射线和墙面的交点会远于A,导致识别为无遮挡。 所以还是改为识别范围内多个深度图像素, 合格像素个数占比 至少要大于50%,因为半边墙壁是50% 如SG-9UsbysDufBw&formal&test 的 6、9点。 而SS-GTmFBp1JN8k&formal&test的 2、3两点是人物遮挡 2 可以通行,但是右边是墙,右边的都检测失败,左侧的又有一些杂点,导致成功dir个数很低。 所以minRatio 要降低于0.5 */ //三个方向 : position0到position1, position0到floorPosition1, position1到floorPosition0。 只要有一个满足ifNeighbour就为true。 不过为了不使sampler总换图,先只考虑从主pano到副pano的方向 var getNeighbour = (mainPano, subPano) => { var dirPoints = [[subPano.position, mainPano.position]]; //注: 点A能到B不代表点B能到A,因为拍摄时物体会移动,或点位相对位置不对,无论是否是意外遮挡都且记下来,反正最后只要有一方可行就算相邻。 if (new Vector2().subVectors(subPano.position, mainPano.position).length() > 1e-4) { //xy都不同 if (dis < 25) { //在远处去掉对floorPosition的判断 dirPoints.push([subPano.floorPosition.clone().add(new Vector3(0, 0, 0.1)), mainPano.position]); } if (dis < 15) { //为了防止楼梯拐角、杂点遮挡。 尽量只在穿墙时不可通行 dirPoints.push([subPano.position.clone().add(new Vector3(0, 0, 0.8)), mainPano.position]); var normal = math.getNormal2d({ p1: subPano.position, p2: mainPano.position }).multiplyScalar(0.3); //左右方向 dirPoints.push([subPano.position.clone().add(new Vector3(normal.x, normal.y, 0.1)), mainPano.position]); dirPoints.push([subPano.position.clone().add(new Vector3(-normal.x, -normal.y, 0.1)), mainPano.position]); } } else { console.warn('两个点位的xy几乎相同', mainPano.id, subPano.id); } //console.warn('getNeighbour', mainPano.id,subPano.id) for (var i = 0; i < dirPoints.length; i++) { var dir = new Vector3().subVectors(dirPoints[i][0], dirPoints[i][1]).normalize(); var intersectPoint = viewer.images360.depthSampler.sample({ dir }, mainPano, true, true); if (!intersectPoint || intersectPoint.distance + margin > dirPoints[i][0].distanceTo(dirPoints[i][1])) { //if(i>2)console.log('haha',i,'id:',mainPano.id,subPano.id) return true; } } }; //if(!ifNeighbour && (map0[pano1.id] == void 0 || computeTwoDir && !map0[pano1.id] && map1[pano0.id] == void 0 )) {//主方向为空且不为邻居 if (pano0.pointcloud.hasDepthTex || pano1.pointcloud.hasDepthTex) { if (computeTwoDir ? map0[pano1.id] == void 0 || map1[pano0.id] == void 0 : map0[pano1.id] == void 0 && map1[pano0.id] !== false) { //2024.3.20改为必须两个方向都为true才行。 因为如果两个点分别在两个数据集相距很远,A确定有墙无法到B,B朝A看 没有B所在的数据集点云遮挡,因深度图不会包含别的数据集的点,故深度图探测到A处为无遮挡,就会导致会飞到遥远的A。 即使在同一个数据集,因为点云可以编辑位置,导致AB相对位置错误,也要避免这种情况可以互相乱走。 缺点是更容易被杂点阻挡,所以需要ifShelter降低阈值。 if (map0[pano1.id] == void 0 && pano0.depthTex) { var is = getNeighbour(pano0, pano1); map0[pano1.id] = !!is; } if (computeTwoDir && map0[pano1.id] && pano1.depthTex && map1[pano0.id] == void 0) { //computeTwoDir : 允许反向计算,即可以读取另一张的深度图 。 如果map0为false,map1就不用计算,只需要等待计算ifShelter var _is = getNeighbour(pano1, pano0); map1[pano0.id] = !!_is; } if (map0[pano1.id] === false || map1[pano0.id] === false) { if (dis < 25) { //再检查下 只要两方都没有被完全遮挡就算可通行 针对杂点较多的情况 if (pano0.depthTex && pano1.depthTex) { ifNeighbour = false; var a = ifSheltered(pano0, pano1); if (a) { var b = ifSheltered(pano1, pano0); if (b && a + b >= 0.6) { ifNeighbour = true; } } map0[pano1.id] = map1[pano0.id] = ifNeighbour; } } else { //标记为无需再计算, 因已经失败。 当map0和map1都不为void 0后就无法再计算了。 if (map0[pano1.id] === void 0) map0[pano1.id] = false; if (map1[pano0.id] === void 0) map1[pano0.id] = false; } } ifNeighbour = map1[pano0.id] == void 0 ? map0[pano1.id] : map0[pano1.id] && map1[pano0.id]; //如果map1没计算就只要返回map0 if (ifLog) { console.log('isNeighbour', pano0.id + "(" + pano0.originID + ")", pano1.id + "(" + pano1.originID + ")", map0[pano1.id], map1[pano0.id]); } } } else if (!onlyUseTex && !ifNeighbour) { //使用点云判断(有深度贴图时不会执行到这) /* let inDirection = ()=>{ let dir = new THREE.Vector3().subVectors(pano1.position,pano0.position).normalize() let dis = pano1.position.distanceTo(pano0.position) let fov = THREE.Math.degToRad(viewer.mainViewport.camera.fov) let hfov = cameraLight.getHFOVForCamera(viewer.mainViewport.camera , true ); let hov = Math.min(fov, hfov) let max = Math.cos(THREE.Math.degToRad(10)) let min = Math.cos(THREE.Math.degToRad(40)) if(this.getDirection().dot(dir) > THREE.Math.clamp(Math.cos(hov/2 ) * dis / 10, min, max )){//距离越远要求和视线角度越接近 return true } } if(computeDirFirst){//先计算方向,防止重复计算ifBlockedByIntersect if(inDirection()){ ifNeighbour = !viewer.inputHandler.ifBlockedByIntersect({point:pano1.position, margin, cameraPos:pano0.position}) } }else{ ifNeighbour = !viewer.inputHandler.ifBlockedByIntersect({point:pano1.position, margin, cameraPos:pano0.position}) if(ifNeighbour && !inDirection()){ ifNeighbour = undefined //不确定 } } map0[pano1.id] = map1[pano0.id] = ifNeighbour ? 'byCloud' : ifNeighbour//写简单点 */ map0[pano1.id] = map1[pano0.id] = true; //全部可通行 } if (map0[pano1.id] && map1[pano0.id]) { //要获得双向需要depthTex都加载好,可能要很久时间 pano0.neighbours.includes(pano1) || pano0.neighbours.push(pano1); pano1.neighbours.includes(pano0) || pano1.neighbours.push(pano0); } return ifNeighbour; } bump(direction) { //撞墙弹回效果 if (!this.bumping && !this.latestToPano) { var _this$currentPano; var distance = (Potree.settings.displayMode == 'showPanos' ? 0.15 : 0.12) * (((_this$currentPano = this.currentPano) === null || _this$currentPano === void 0 ? void 0 : _this$currentPano.pointcloud.scale.x) || 1); //感觉点云模式比全景模式更明显,所以降低 var currentPos = this.position.clone(); var endPosition = new Vector3().addVectors(this.position, direction.clone().multiplyScalar(distance)); var duration = 150; viewer.scene.view.setView({ position: endPosition, duration, callback: () => { viewer.scene.view.setView({ position: currentPos, duration: duration * 5, callback: () => { this.bumping = false; //this.dispatchEvent('cameraMoveDone') }, Easing: 'easeInOutSine', cancelFun: () => { this.bumping = false; } }); this.bumping = true; }, cancelFun: () => { this.bumping = false; }, Easing: 'easeInOutSine' }); this.bumping = true; } //备注:将4dkk中的‘前后方向变化fov、左右方向移动镜头’ 都改为移动镜头。 因为这里无法判断左右离壁距离。 } flyToPanoClosestToMouse() { /* if (Date.now() - this.mouseLastMoveTime > 50) { //this.intersect = this.getMouseIntersect(); this.intersect && this.updateClosestPano(this.intersect); } */ if (!Potree.settings.ifShowMarker) { //不显示marker的时候mousemove没更新鼠标最近点所以更新 this.updateClosestPano(viewer.inputHandler.intersect); } //console.log('flyToPanoClosestToMouse',this.closestPano) if (this.closestPano) { var pano = this.closestPano; return this.flyToPano({ pano, easeName: this.isAtPano() ? 'linearTween' : 'easeInOutQuad' }); } var direction = this.viewer.inputHandler.getMouseDirection().direction; this.flyDirection(direction); } flyDirection(direction, option1, option2, byKey) { if (viewer.mainViewport.view.isFlying()) { // closestPanoInDirection函数耗时长,飞行时运行会卡顿(如果以后加无缝过渡再说) return; } var deferred = $.Deferred(); //this.history.invalidate(); var panoSet = this.closestPanoInDirection(direction, option1, option2, byKey); if (panoSet) { this.flyToPano({ pano: panoSet, callback: deferred.resolve.bind(deferred, !0) }); } else { //如果离数据集较远,转动也很难找到点云,就飞到就近点: if (Potree.settings.displayMode == 'showPointcloud') { var po = viewer.scene.pointclouds.find(e => e.visibleNodes.some(a => a.getLevel() > Math.ceil(e.maxLevel * 0.15))); //虽然当点云在前方很远的地方也可能符合 if (!po) { //无可见点云 //console.log('no visi cloud') if (!this.panos.length) { //如果场景中没有漫游点,如SG-t-XPf1k9pv3Zg 点击后回到可见区域 if (viewer.scene.pointclouds.length == 0) return; var map = new Map(); var clouds = viewer.scene.pointclouds.filter(e => e.root.geometryNode); clouds.forEach(e => map.set(e, e.bound.distanceToPoint(this.position))); clouds.sort((a, b) => map.get(a) - map.get(b)); viewer.flyToDataset({ focusOnPoint: true, pointcloud: clouds[0], duration: 500 }); //飞最近的一个点云 return deferred.promise(); } this.flyToPano({ pano: this.findNearestPano(), duration: 500, callback: deferred.resolve.bind(deferred, !0) }); return deferred.promise(); } } this.bump(direction); deferred.resolve(!1); } return deferred.promise(); } closestPanoInDirection(direction, option1, option2, byKey) { return this.rankedPanoInDirection(0, direction, option1, option2, byKey); } rankedPanoInDirection(t, direction, option1, option2, byKey) { var _this$currentPano2; //此direction为mouseDirection,是否需要加上相机角度的权重 var startTime = Date.now(); var panoSet = { pano: null, candidates: [] //缓存顺序--如果需要打印的话 }; t || (t = 0); option1 = void 0 !== option1 ? option1 : 0.6; //.75; 2024.3.22改小,也就是可以走的角度范围增加,主要针对像山野那种很大的场景,不知道可以走哪,容易因角度范围内无近点而飞到远处的情况。但不能太小,总是横着走路 var o = option2 ? "angle" : "direction"; var floor = viewer.modules.SiteModel.currentFloor; var entity = viewer.modules.SiteModel.inEntity; var scaleFactor = Math.pow(((_this$currentPano2 = this.currentPano) === null || _this$currentPano2 === void 0 ? void 0 : _this$currentPano2.pointcloud.scale.x) || 1, 2); var changeTexCount = 0, maxWaitDur = 300; //maxSamplerChangeTex = THREE.Math.clamp( maxWaitDur / Potree.timeCollect.depthSampler.median, 2, 10) //计算换贴图最大数目 var request = [ //必要条件 Images360.filters.not(this.currentPano), Images360.filters.isEnabled(), //Images360.filters.inFloorDirection( this.position, direction, option1 ), //原先用inPanoDirection,但容易穿楼层,当mouse较低或较高 //因不束缚纵向所以可能往相反反向 pano => { // 不会再changeTex了 var isNeighbour = this.isNeighbour(this.currentPano, pano, { onlyUseTex: true }); if (isNeighbour || pano.noNeighbour && Images360.scoreFunctions.distanceSquaredNew(this.currentPano)(pano) > -200 /* disSquareMap.get(pano) < 200 */) { //在靠近孤立点时可以通行。但是不好把握这个距离,太远的话很多地方都会不小心到孤立点,太近的话可能永远到不了。 return true; } }]; if (byKey) { request.push(Images360.filters.inPanoDirection(this.position, direction, option1)); } else { request.push(Images360.filters.inPanoDirection(this.position, this.getDirection(), option1 /* , true */)); //垂直方向上再稍微限制一下, 要接近视线方向,避免点击前方时因无路而到下一楼。但不能太高,否则楼梯上稍微朝下点击都到不了上方。之所以使用视线方向是因为镜头方向比鼠标方向目的性更强。 } var list = [ //决胜项目 /* (pano)=>{ return -disSquareMap.get(pano) }, */ Images360.scoreFunctions.distanceSquaredNew(this.currentPano), Images360.scoreFunctions[o](this.position, direction, true), pano => { var neighbour = this.isNeighbour(this.currentPano, pano, { dontCompute: true, isNeighbour: true }); //不计算的 return neighbour ? directionFactor : 0; } /* (pano)=>{//尽量不穿越地板到下一层 let dis = getHeightDis(pano) return -dis * directionFactor * 0.1; }, */ /* (pano)=>{ //尽量在一个建筑内行走,这样不易穿墙 let score = 0 if(entity ){ if(!entity.panos.includes(pano) ) score -= directionFactor * 0.01; //不能设置太高,否则会走不进大房间的小房间中,因为容易穿过小房间的一个门到另一个门外 if(!floor.panos.includes(pano)) {//避免穿楼层 let heightDiff = Math.abs(pano.position.z - this.position.z); if(heightDiff > 2){ score -= directionFactor * 0.1 * heightDiff; } } } return score } */]; if (!byKey && viewer.inputHandler.intersect && this.currentPano) { //方便上下楼, 考虑panos之间的角度差 var pos1 = this.currentPano.floorPosition; var vec1 = new Vector3().subVectors(viewer.inputHandler.intersect.location, pos1).normalize(); //应该只有atPano时才会执行到这吧? list.push(function (pano) { var pos2 = pano.floorPosition; var vec2 = pos2.clone().sub(pos1).normalize(); return vec2.dot(vec1) * directionFactor * 4; }); } this.findRankedByScore(t, request, list, panoSet); //console.log( 'costTime:',Date.now() - startTime) return panoSet.pano; } findRankedByScore(e, t, i, n) { n && (n.candidates = null, //candidates 缓存顺序--如果需要打印的话 n.pano = null), e || (e = 0); var r = Common.sortByScore(this.panos, t, i); //console.log('findRankedByScore', r && r.map(u=>u.item.id + '| ' + math.toPrecision(u.score,4) + " | " + math.toPrecision(u.scores,4))) return !r || 0 === r.length || e >= r.length ? null : (n && (n.candidates = r, n.pano = r[e].item), r[e].item); } updateClosestPano(intersect, state) { //hover到的pano 大多数时候是null var pano; if (intersect instanceof Panorama) { //漫游模式 pano = state ? intersect : null; } else { if (this.isAtPano() || this.bumping) { return; } else { var filterFuncs = []; intersect = intersect && intersect.location; if (!intersect) return; var sortFuncs = Potree.settings.editType != 'pano' ? [Images360.sortFunctions.floorDisSquaredToPoint(intersect)] : [Images360.sortFunctions.disSquaredToPoint(intersect)]; pano = Common.find(this.panos, filterFuncs, sortFuncs); } } if (pano != this.closestPano) { pano && (this.isPanoHover = !0); this.closestPanoChanging(this.closestPano, pano); // 高亮marker //console.log('closestPano '+ (pano ? pano.id : 'null' )) this.closestPano = pano; } else { this.isPanoHover = !1; } } closestPanoChanging(oldPano, newPano) { if (!Potree.settings.ifShowMarker) return; oldPano && oldPano.hoverOff({ byImages360: true }); newPano && newPano.hoverOn({ byImages360: true }); } getTileDirection() { //根据不同dataset的来存储 var vectorForward = viewer.scene.view.direction.clone(); var vectorForwards = viewer.scene.pointclouds.concat(viewer.objs.children.filter(e => e.panos)).map(e => { var inv = new Matrix4().copy(e.rotateMatrix).invert(); //乘上dataset的旋转的反转 var direction = vectorForward.clone().applyMatrix4(inv); return { datasetId: e.dataset_id, direction: math.convertVector.ZupToYup(direction) }; }); //return vectorForwards[0].direction return { datasetsLocal: vectorForwards, vectorForward }; } update() { //if(this.tileDownloader.started){ var vectorForwards = this.getTileDirection(); //vectorForwards = vectorForwards[0].direction this.updateTileDownloader(tileArr, vectorForwards); this.updatePanoRenderer(vectorForwards); //} this.updateZoomPano(); } updateTileDownloader(t, vectorForward) { var i = this.nextPano || this.currentPano; if (i) { this.tileDownloader.tilePrioritizer.updateCriteria(i, viewer.scene.view.position.clone(), vectorForward, t.length > 0 ? t : null), this.tileDownloader.processPriorityQueue = !0; } } updatePanoRenderer(vectorForward) { var i = this.nextPano || this.currentPano; if (i) { if (this.panoRenderer.hasQueuedTiles() && i) { this.panoRenderer.updateDirection(vectorForward); } } } //等待部分加载完 checkAndWaitForTiledPanoLoad(pano, basePanoSize, callback1, callback2, progressCallback, iswait, isclear, l) { //console.log('checkAndWaitForTiledPanoLoad',pano.id) if (!pano) { console.error("Player.checkAndWaitForTiledPanoLoad() -> Cannot load texture for null pano."); } var vectorForward = this.getTileDirection(); if (!pano.isLoaded(basePanoSize)) { iswait && viewer.waitForLoad(pano, function () { //发送loading return pano.isLoaded(basePanoSize); }); /* var fov = {//test for direction 预加载的边缘有一丢丢不准确,尤其在相机倾斜时(4dkk也是)。 hFov: cameraLight.getHFOVForCamera(viewer.scene.getActiveCamera() ), vFov: viewer.scene.getActiveCamera().fov }//原先是null,不要求方向 */ var fov = null; //若不为null的话,因为可能可见范围的tile下载过了从而无法触发下载,然后得不到下载成功的消息,怎么办 pano.loadTiledPano( /* 1024 */basePanoSize, vectorForward, fov, isclear, l, null).done(function (e, t) { callback1 && callback1(e, t); }.bind(this)).fail(function (msg) { callback2 && callback2(msg); }.bind(this)).progress(function (e, t, i) { progressCallback && progressCallback(e, t, i); }.bind(this)); return !0; } } fitPanoTowardPoint(o) { //寻找最适合的点位 var point = o.point, //相机最佳位置 target = o.target || o.point, //实际要看的位置 require = o.require || [], rank = o.rank || [], force = o.force, getAll = o.getAll, bestDistance = o.bestDistance || 0, sameFloor = o.sameFloor, maxDis = o.maxDis, dir = o.dir; var camera = viewer.scene.getActiveCamera(); if (target && !dir) { dir = new Vector3().subVectors(target, point).normalize(); } var atFloor = sameFloor && viewer.modules.SiteModel.pointInWhichEntity(point, 'floor'); //if(o.floor)require.push(Panorama.filters.atFloor(o.floor)) var checkIntersect = o.checkIntersect; var base = Math.max(300, viewer.bound.boundSize.length() * 3); if (o.boundSphere) { //只接受boundSphere var aspect = 1; //size.x / size.y var dis; if (camera.aspect > aspect) { //视野更宽则用bound的纵向来决定 dis = /* size.y */o.boundSphere.radius /* / 2 */ / MathUtils.degToRad(camera.fov / 2); } else { var hfov = cameraLight.getHFOVForCamera(camera, true); dis = /* size.x */o.boundSphere.radius /* / 2 */ / (hfov / 2); } bestDistance = dis; //*0.8 } var disSquareMap = new Map(); var bestDisSquared = bestDistance * bestDistance; var maxDisSquared = maxDis && maxDis * maxDis; this.panos.forEach(pano => { var dis2 = pano.position.distanceToSquared(target); //距离目标点 disSquareMap.set(pano, dis2); }); var panos = this.panos.sort((p1, p2) => { return disSquareMap.get(p1) - disSquareMap.get(p2); }); if (maxDisSquared) { //热点超过最大距离不可见的 var panos2 = [], _pano, i = 0; while (_pano = panos[i], disSquareMap.get(_pano) < maxDisSquared) { panos2.push(_pano); i++; } if (panos2.length == 0) return { pano: _pano, msg: 'tooFar' }; //全部都大于maxDis, 就返回最近的 panos = panos2; } rank.push(pano => { var dis1 = Math.abs(pano.position.distanceToSquared(point) - bestDisSquared); //距离最佳位置 disSquareMap.set(pano, dis1); if (!target) { return -dis1; } else { var dis2 = disSquareMap.get(pano); if (o.gotoBestView) { //忽略和当前视线的角度 return -(dis1 + dis2 * 0.3); } var vec2 = new Vector3().subVectors(target, pano.position).normalize(); var cos = dir.dot(vec2); //let result = (- dis1 - Math.pow(dis2 , 1.5)) / (cos + 2) // cos+2是为了调整到1-3, var result = (dis1 + dis2 * 0.3) * (-1 + cos * 0.9); //尽量贴近最佳位置的角度, 或贴近相机原来的角度 。尽量靠近最佳观测点,并且优先选择靠近目标点的位置.(注意cos的乘数不能太接近1,否则容易只考虑角度) //Potree.Log(pano.id, dis1, dis2, cos, result,{font:{toFixed:2,fontSize:10}}) return result; } //注:热点最好加上法线信息,这样可以多加一个限制,尽量顺着热点像展示的方向。 } /* (pano)=>{ let score = 0 if(pano.depthTex && checkIntersect){ let intersect = !!viewer.ifPointBlockedByIntersect(target, pano.id, true) //viewer.inputHandler.ifBlockedByIntersect({point:target, margin:0.1, cameraPos:pano}) if(intersect){ score = 0 }else { score = base * 2 } }else{ score = base * 1.5 //没加载好的话,不管了 , 几乎当做无遮挡,否则容易到不了最近点 } return score } */); var g = Common.sortByScore(panos, require, rank); // console.log(g) var pano = g && g.length > 0 && g[0].item; if (pano && checkIntersect) { var intersect = !!viewer.ifPointBlockedByIntersect(target, pano.id, true); if (intersect) { return { pano, msg: 'sheltered' }; } } //if(getAll)return g; return pano; //注:深度图有的位置会不准确,以至于会算出错误的遮挡、选择错误的pano,解决办法只能是移动下热点到更好的位置 } //---------------scroll zoom ------------------------------------------ /* zoomIn = function() { //放大 this.zoomBy(1 + this.zoomSpeed); } zoomOut = function() {//缩小 this.zoomBy(1 - this.zoomSpeed); } */ zoomBy(e, pointer) { //以倍数 this.zoomTo(this.zoomLevel * e, pointer); } zoomTo(zoomLevel, pointer) { //缩放到某绝对zoomLevel var zoom = Potree.settings.zoom; if (zoom.enabled) { zoomLevel = MathUtils.clamp(zoomLevel, zoom.min, zoom.max); //console.log(zoomLevel) if (zoomLevel == this.zoomLevel) return; this.zoomLevel = zoomLevel; if (Potree.settings.panoZoomByPointer) { //定点缩放:使当前鼠标所在的位置缩放后不变 var view = viewer.scene.view; var originDir = viewer.scene.view.direction; var oldPointerDir = viewer.inputHandler.getMouseDirection(pointer).direction; viewer.setFOV(Potree.config.view.fov * (1 / this.zoomLevel)); var newPointerDir = viewer.inputHandler.getMouseDirection(pointer).direction; view.direction = oldPointerDir; //获取一下鼠标所在位置的yaw 和 pitch var oldPitch = view.pitch, oldYaw = view.yaw; view.direction = newPointerDir; var newPitch = view.pitch, newYaw = view.yaw; view.direction = originDir; //还原 viewer.scene.view.pitch -= newPitch - oldPitch; viewer.scene.view.yaw -= newYaw - oldYaw; } else { viewer.setFOV(Potree.config.view.fov * (1 / this.zoomLevel)); } } } zoomFovTo(fov) { //通过fov来算zoomLevel var zoomLevel = Potree.config.view.fov /* this.baseFov */ / fov; this.zoomTo(zoomLevel); } smoothZoomTo(aimLevel) { var dur = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; var currentLevel = this.zoomLevel; if (currentLevel == aimLevel) return; var fun = progress => { //progress > 1 && (progress = 1) var level = currentLevel * (1 - progress) + aimLevel * progress; this.zoomTo(level, !0); }; transitions.start(fun, dur, null, null, 0, easing['easeInOutQuad']); } updateZoomPano() { if (!this.panoRenderer.zoomPanoRenderingDisabled && Potree.settings.displayMode == 'showPanos') { var currentPano = this.currentPano; if (currentPano) { var levelThreshold1 = Potree.settings.navTileClass == '1k' ? 1.3 : 1.7, levelThreshold2 = 2; var t = this.zoomLevel > levelThreshold1, n = !(this.flying && this.nextPano && this.nextPano !== this.currentPano), r = t && n; this.tileDownloader.tilePrioritizer.setZoomingActive(r); this.panoRenderer.setZoomingActive(r, currentPano, !0); var o = (pano, ifZoom) => { this.panoRenderer.resetRenderStatus(pano.id, !1, !0, this.qualityManager.getMaxNavPanoSize()); this.panoRenderer.clearAllQueuedUploadsForPano(pano.id); this.panoRenderer.renderPanoTiles(pano.id, null, !1, !1); pano.setZoomed(ifZoom); }; if (currentPano.pointcloud.tileRes == '2k') { //融合页面 return currentPano.zoomed && o(currentPano, !1); } if (r && (!currentPano.zoomed || this.qualityManager.zoomLevelResolution && this.qualityManager.zoomLevelResolution != '4k')) { //needZoom currentPano.zoomed || o(currentPano, !0); if (Potree.settings.navTileClass == '1k' && Potree.settings.tileClass != '1k' && this.zoomLevel < levelThreshold2) { this.panoRenderer.enableHighQuality(function () { //开启2k if (Potree.settings.tileClass != '4k') o(currentPano, !0); }.bind(this)); } else { this.panoRenderer.enableUltraHighQualityMode(function () { //开启4k getMaxZoomPanoSize this.qualityManager.useUltraHighResolutionPanos && (Potree.settings.zoom.max = Potree.config.ultraHighQualityMaxZoom); o(currentPano, !0); }.bind(this)); } } else { !t && currentPano.zoomed && o(currentPano, !1); } if (r && Potree.settings.navTileClass == '1k' && Potree.settings.tileClass == '4k') { //目前只有手机端navTileClass == '1k' (分三个梯度) var change = zoomedFlag => { this.qualityManager.updateMaximums(); //更新maxZoomPanoSize this.panoRenderer.setupZoomRenderTarget(); //更新renderTarget //currentPano.setZoomed(t);//更新uniforms贴图 }; this.qualityManager.zoomLevelResolution = this.zoomLevel >= levelThreshold2 ? '4k' : this.zoomLevel > levelThreshold1 ? '2k' : '1k'; if (this.oldZoomLevel < levelThreshold2 && this.zoomLevel >= levelThreshold2) { //1k/2k-4k change(); o(currentPano, t); } else if (this.oldZoomLevel <= levelThreshold1 && this.zoomLevel > levelThreshold1) { //1k-2k change(); } else if (this.oldZoomLevel > levelThreshold2 && this.zoomLevel <= levelThreshold2) { //4k-2k/1k change(); o(currentPano, t); } else if (this.oldZoomLevel > levelThreshold1 && this.zoomLevel <= levelThreshold1) { //2k-1k change(); } this.oldZoomLevel = this.zoomLevel; } } } } //-------------------- addHighMapCube() { //创建8*8的tile cube 主要因手机版崩溃 要在电脑端测试得设置maxRenderTargetSize if (Potree.settings.tileClass == '4k' && this.qualityManager.maxRenderTargetSize == 2048) { var geo = new PlaneGeometry(1, 1, 1, 1); var cube = new Object3D(); cube.tiles = []; for (var cubeIndex = 0; cubeIndex < 6; cubeIndex++) { var face = new Object3D(); for (var i = 0; i < 8; i++) { for (var j = 0; j < 8; j++) { var tile = new Mesh(geo, new MeshBasicMaterial({ //之后有必要的话像4dkk一样换成ModelTextureMaterial,加深度图 //side:THREE.DoubleSide transparent: true, opacity: 0.4, depthTest: false })); tile.position.set(i - 3.5, j - 3.5, -4); if (Potree.settings.isTest) { var colorHue = Math.random(); tile.material.color = new Color().setHSL(colorHue, 0.6, 0.7); /* tile.scoreLabel = new TextSprite( { backgroundColor: { r: 0, g: 0, b: 0, a: 0 }, textColor: { r: 0, g: 0, b: 0, a: 1 }, borderRadius: 15, renderOrder: 50, fontsize : 13, text: '' , dontFixOrient:true }) tile.scoreLabel.sprite.material.side = 2; tile.scoreLabel.rotation.x = Math.PI tile.add(tile.scoreLabel) */ } Potree.Utils.updateVisible(tile, 'show', false); tile.tileX = i; tile.tileY = j; tile.cubeFace = cubeIndex; //tile.renderOrder = RenderOrder.highTileCube cube.tiles.push(tile); face.add(tile); } } switch (cubeIndex) { case GLCubeFaces$3.GL_TEXTURE_CUBE_MAP_POSITIVE_X: face.rotation.set(0, Math.PI / 2, 0); break; case GLCubeFaces$3.GL_TEXTURE_CUBE_MAP_NEGATIVE_X: face.rotation.set(0, -Math.PI / 2, 0); break; case GLCubeFaces$3.GL_TEXTURE_CUBE_MAP_POSITIVE_Y: var rot1 = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI); var rot2 = new Quaternion().setFromAxisAngle(new Vector3(1, 0, 0), Math.PI / 2); face.quaternion.copy(rot1).multiply(rot2); //face.rotation.set(Math.PI/2,0,0); break; case GLCubeFaces$3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: //face.rotation.set(-Math.PI/2,0,0); var rot1 = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI); var rot2 = new Quaternion().setFromAxisAngle(new Vector3(1, 0, 0), -Math.PI / 2); face.quaternion.copy(rot1).multiply(rot2); break; case GLCubeFaces$3.GL_TEXTURE_CUBE_MAP_POSITIVE_Z: face.rotation.set(0, Math.PI, 0); break; case GLCubeFaces$3.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: face.rotation.set(0, 0, 0); } face.scale.set(1, -1, 1); face.cubeFace = cubeIndex; cube.add(face); } cube.name = 'highMapCube'; this.highMapCube = cube; viewer.scene.scene.add(cube); { var s = 0.1; cube.scale.set(s, s, s); } //注:由于原本的mesh上加了深度贴图,可能距离镜头比这里的近。凡是在cube以内的部分都会挡住cube导致模糊。但是应该不常见吧(另外到天空的边缘也是很近)。姑且depthTest=false this.highMapCube.visible = false; Potree.Utils.setObjectLayers(this.highMapCube, 'sceneObjects' /* 'skybox' */); //因它的深度是错误的,故不在skybox层渲染,影响edlRT, 而在renderOverlay时渲染覆盖。 //console.warn('addHighMapCube') viewer.addEventListener('update', () => { if (this.highMapCube.visibleTiles) { this.updateTiles(); //逐步将visibleTiles加载完 } }); viewer.addEventListener('camera_changed', e => { if (e.viewport == viewer.mainViewport) { //重新获取visibleTiles Common.intervalTool.isWaiting('update4kTiles', () => { var vectorForward = this.getDirection(); this.updateTiles(vectorForward); }, 500); } }); } } isHighMapLoaded(cubeFace, tileX, tileY) { var tile = this.highMapCube.children[cubeFace].children[tileX * 8 + tileY]; return !!tile.material.map; } updateTiles(direction) { if (!this.highMapCube || !this.highMapCube.visible) return; if (this.highMapCube.tiles.filter(e => e.image).length <= 10) return; //加载的太少了 //performance.mark('updateTiles-start') if (direction) { var camera = viewer.mainViewport.camera; var hfov = cameraLight.getHFOVForCamera(camera, true) / 2; var vfov = MathUtils.degToRad(camera.fov) / 2; /* let hcos = Math.cos(hfov / 2) let vcos = Math.cos(vfov / 2) */ var list = this.highMapCube.tiles; list.forEach(e => { //屏幕外的不显示 var pos = e.getWorldPosition(new Vector3()); var dir = new Vector3().subVectors(pos, this.highMapCube.position).normalize(); var hcos_ = dir.clone().setZ(direction.z).normalize().dot(direction); //在direction的斜面上水平角度差 var hRad = Math.acos(hcos_); var vRad = -200; if ( /* hRad > hfov + 0.08 */hRad / hfov > 1.5) { e.score = -100; } else { vRad = Math.abs(Math.acos(dir.z) - Math.acos(direction.z)); if ( /* vRad > vfov + 0.08 */vRad / vfov > 1.5) { e.score = -100; } else { e.score = -(hRad / hfov + vRad / vfov); } } e.scores = hRad.toFixed(3) + ', ' + vRad.toFixed(3); if (e.score == -100) { this.resetTile(e); } }); this.highMapCube.visibleTiles = list.filter(e => e.score > -100); //list.forEach(e=>e.scoreLabel.setText(e.scores)) } var needRecover = this.highMapCube.visibleTiles.filter(e => !e.material.map); if (needRecover.length) { var maxCount = Common.getBestCount('4kmaxTileRecover', 0, 2, 1.5, 6, false, 2); var count = 0; //console.log(maxCount) needRecover.forEach((e, i) => { //只更新若干个,因为太耗时了, 其余的等下帧更新 if (count >= maxCount) return; var r = this.recoverTile(e); if (r) count++; }); } } getHighImage(image, cubeFace, tileX, tileY) { var tile = this.highMapCube.children[cubeFace].children[tileX * 8 + tileY]; tile.image = image; //先记录下来 } updateHighMap(image, cubeFace, tileX, tileY) { //console.warn('updateHighMap') var tile = this.highMapCube.children[cubeFace].children[tileX * 8 + tileY]; if (image) tile.image = image; //先记录下来 if (tile.material.map) return; if (this.highMapCube.visibleTiles && !this.highMapCube.visibleTiles.includes(tile) /* this.highMapCube.texLoadedCount >= this.getMaxTileCount() */) { return; } //简易创建贴图 /* var tex = this.$app.core.get('SceneRenderer').initSizedTexture2D(512, THREE.ClampToEdgeWrapping) //var loaded = this.$app.core.get('Player').model.isHighMapLoaded(tile.cubeFace, tile.tileX, tile.tileY) this.$app.core.get('SceneRenderer').uploadTexture2D(image, tex, 0, 0, 512, 512) //只替换tex对应的img,不新建 */ var tex = new Texture(); tex.image = image; tex.flipY = false; tex.wrapS = tex.wrapT = ClampToEdgeWrapping; tex.generateMipmaps = false; tex.minFilter = LinearFilter; tex.needsUpdate = true; tile.material.map = tex; tile.material.opacity = 1; tile.material.transparent = false; Potree.Utils.updateVisible(tile, 'show', true); tile.material.needsUpdate = true; //发现每次开始放大但还未放大到4k时也会把之前加载过的4k加载 } recoverTile(tile) { if (tile.material.map) return; if (tile.image) { this.updateHighMap(tile.image, tile.cubeFace, tile.tileX, tile.tileY); return true; } else { //console.log('no image') } } resetTile(tile, kill) { if (kill) { //完全消灭 tile.image = null; } var map = tile.material.map; if (map) { map.dispose(); //这句执行了以后,h.__webglTexture一直就是undefined map.loaded = !1; map.version = 0; //保底再执行一下这个,类似app.sceneRenderer.deallocateCubeTexture(tile.material.map) var h = viewer.renderer.properties.get(map); //console.log('__webglTexture',!!h.__webglTexture) viewer.renderer.getContext().deleteTexture(h.__webglTexture); tile.material.map = null; tile.material.needsUpdate = true; Potree.Utils.updateVisible(tile, 'show', false); //this.highMapCube.texLoadedCount -- //console.log('resetTile'/* , tile.cubeFace, tile.tileX, tile.tileY */) } } resetHighMap() { if (!this.highMapCube) return; this.highMapCube.children.forEach(e => e.children.forEach(tile => { this.resetTile(tile, true); })); //this.highMapCube.texLoadedCount = 0 this.highMapCube.visibleTiles = null; this.hideHighMap(); //console.log('resetHighMap') } setHighMap(pano) { if (!this.highMapCube) return; this.highMapCube.position.copy(pano.position); //可以利用第0个pano查看,其 rotation4dkk是(_x: 0, _y: -1.5707963267948966, _z: 0 )而手动旋转至(_x:1.5707963, _y: -1.57079, _z: 0)时才正确,说明要在4dkk的旋转基础上,绕x轴转90度,(也就是转成navvis坐标系), 然后得到YupToZup的函数写法的 this.highMapCube.quaternion.copy(math.convertQuaternion.YupToZup(pano.quaternion4dkk)); //乘上数据集整体的旋转: var modelRotQua = new Quaternion().setFromRotationMatrix(pano.pointcloud.rotateMatrix); this.highMapCube.quaternion.premultiply(modelRotQua); } showHighMap() { if (!this.highMapCube) return; //console.warn('showHighMap') this.highMapCube.visible = true; } hideHighMap() { if (!this.highMapCube) return; //console.warn('hideHighMap') this.highMapCube.visible = false; } //缩小后继续显示cube呢还是不显示? 不显示的话,就要把cube上的复制到renderTarget上……会不会又崩溃,or没加载的显示??? addPanoData(data, pointcloud) { //加载漫游点 //data[0].file_id = '00019' if (data.data) data = data.data; //if(data.length == 0)console.error(pointcloud.dataset_id + ' 没有漫游点') //data = data.sort(function(a,b){return a.id-b.id}) data.forEach(info => { //if(Potree.fileServer){ info.id = getID(); //把info的id的一长串数字改简单点 info.pointcloud = pointcloud; //} var pano = new Panorama(info, this); pano.addEventListener('dispose', e => { if (this.closestPano == pano) this.closestPano = null; if (this.currentPano == pano && Potree.settings.displayMode == 'showPanos') { Potree.settings.displayMode = 'showPointCloud'; } }); this.panos.push(pano); this.neighbourMap[info.id] = {}; pano.label && Potree.Utils.setObjectLayers(pano.label, 'bothMapAndScene'); pano.label2 && Potree.Utils.setObjectLayers(pano.label2, 'bothMapAndScene'); if (Potree.settings.editType == 'pano') { Potree.settings.datasetsPanos[pointcloud.dataset_id].panos.push(pano); } }); } loadDone() { this.tileDownloader.setPanoData(this.panos, []); this.updatePanoBound(); if (viewer.scene.pointclouds.some(e => e.panos.length == 0)) { //console.warn('存在数据集没有pano'); viewer.hasNoPanoDataset = true; } } updatePanoBound() { var minSize = new Vector3(1, 1, 1); this.bound = math.getBoundByPoints(this.panos.map(e => e.position), minSize); Potree.settings.editType == 'pano' || viewer.scene.pointclouds.forEach(pointcloud => pointcloud.getPanosBound()); } getPano(value) { var typeName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'id'; //默认找的是id,也可以是sid、uuid return this.panos.find(p => p[typeName] == value); } } ; //判断当前点是否加载了全景图 Images360.prototype.checkAndWaitForPanoLoad = function () { var loadingPanos = {}, LoadedTimePanos = {}, loadedCallback = {}, //add maxTime = 5e3; /* var withinTime = function() { for (var panoId in loadingPanos) if (loadingPanos.hasOwnProperty(panoId) && loadingPanos[panoId]) { var differTime = performance.now() - LoadedTimePanos[panoId]; if (differTime < maxTime) return !0 } return !1 } */ // withinTime 改为只判断当前请求的点。原始代码的callback是针对任意pano的,所以遍历所有漫游点,只要有在加载的就返回,但已不满足需求(4dkk)。2024:9 发现bim分屏有问题,然后发现4dkk已经改过这,遂搬过来 var withinTime = function withinTime(pano) { //5秒之内还在加载的话,直接返回仍在加载状态,否则重新判断 if (loadingPanos.hasOwnProperty(pano.id) && loadingPanos[pano.id]) { //还在加载 var differTime = performance.now() - LoadedTimePanos[pano.id]; if (differTime < maxTime) return !0; } }; return function (pano, basePanoSize, doneFun1, doneFun2, progressCallback, iswait, isclear, p) { loadedCallback[pano.id] = doneFun1; //add 因为有可能之前请求的没加doneFun1, 如果加载好就执行最新的doneFun1 if (withinTime(pano)) { //距离上次请求时间很近 //console.log('withinTime',window.name) return !0; //这里感觉应该是!1 } var changeMode = e => { if (e.mode == 'showPointCloud') { console.warn('切到点云模式了,就删除loadedCallback记录', pano.id); //否则再次转为showPanos会被withinTime阻拦 delete loadingPanos[pano.id]; viewer.cancelLoad(pano); loadedCallback[pano.id] && loadedCallback[pano.id](); //可以飞行了 doneFun2 && doneFun2(); this.removeEventListener('requestMode', changeMode); } }; this.addEventListener('requestMode', changeMode); var callback1 = (param1, param2) => { setTimeout(() => { loadingPanos[pano.id] = !1; loadedCallback[pano.id] && loadedCallback[pano.id](param1, param2); this.removeEventListener('requestMode', changeMode); }, 1); }; var callback2 = param => { //没有看到有传doneFun2的 setTimeout(() => { loadingPanos[pano.id] = !1; doneFun2 && doneFun2(param); }, 1); }; try { null !== iswait && void 0 !== iswait || (iswait = !0); loadingPanos[pano.id] = this.checkAndWaitForTiledPanoLoad(pano, basePanoSize, callback1, callback2, progressCallback, iswait, isclear, p); //true代表没加载好 if (loadingPanos[pano.id]) { LoadedTimePanos[pano.id] = performance.now(); } return loadingPanos[pano.id]; } catch (msg) { loadingPanos[pano.id] = !1; LoadedTimePanos[pano.id] = performance.now() - maxTime; throw msg; } }; }(); //加载全景图的流程:请求下载当前点的最低分辨率图(不过有可能已经下载了),并且请求uploadTile渲染当前漫游点,若tile都upload完(先6个)会发送消息,LoadComplete 从而回调成功。 Images360.prototype.getNeighbours = function () { //逐渐自动获取neighbours。 200个点差不多在半分钟内算完 var lastIndex, inited; //标记上次查询到哪,防止重新sortByScore var startTime = 0, lastTime = 0; return function (interacted) { var _this = this; if (Potree.settings.editType == 'pano') return; if (!this.currentPano || viewer.mainViewport.view.isFlying() || viewer.lastFrameChanged || viewer.inputHandler.drag //拖拽时不更新,否则移动端卡 || !viewer.scene.pointclouds.some(e => e.hasDepthTex) //目前无深度图的话不判断可行,全部可通行 ) { return lastIndex = 0; } var nearPanos = this.tileDownloader.tilePrioritizer.nearPanos; if (!nearPanos) return; if (Date.now() - lastTime < 50) return; //不要每帧都算 startTime = lastTime = Date.now(); var panos = [this.currentPano, ...nearPanos]; this.depthSampler.updateNearPanos(panos); var maxWaitDur = browser.isMobile() ? 2 : 4; var changeCount = 0, getCount = 0, maxChangeTex = 1; var changeTexCount = () => { changeCount++; }; var median = Potree.timeCollect.depthSamChangeImg.median; var loopOutCount = 0; var ifOverTime = () => { var is = changeCount >= maxChangeTex || changeCount * median + getCount * 0.01 + 0.0004 * panos.length * loopOutCount > maxWaitDur; //不换贴图也要一丢丢计算时间 /* if(is){ console.log('OverTime, changeCount', changeCount) } */ return is; }; this.depthSampler.addEventListener('changeImg', changeTexCount); var maxChangePanoCount = Math.min(panos.length, this.depthSampler.maxDataCount); //在lastIndex清空前最多可以有的mainPano的数量。 限制数量,否则一直changeTex很卡 outer: for (var i = lastIndex, j = panos.length; i < maxChangePanoCount /* j */; i++) { var pano = panos[i]; var others = panos.slice(i + 1, j); lastIndex = i; if (!pano.pointcloud.hasDepthTex && i > 0) break; //点云的情况下最好不改相机位置,因为其他位置点云还没加载完,所以不判断当前点以外的漫游点 viewer.addTimeMark('sortByScore', 'start'); var g = Common.sortByScore(others, [], [Images360.scoreFunctions.distanceSquaredNew(pano) //900个点时至少耗时0.3 ]); loopOutCount++; viewer.addTimeMark('sortByScore', 'end'); var _loop = function _loop() { var item = g[a]; if (item.item == pano) return 0; // continue if (_this.neighbourMap[pano.id][item.item.id] != void 0 && _this.neighbourMap[item.item.id][pano.id] != void 0 /* this.isNeighbour(pano,item.item,{dontCompute:true}) != void 0 */) return 0; // continue //确定结果了就不需要算 //console.log('check isNeighbour', pano.id, item.item.id) var byCloud = !pano.pointcloud.hasDepthTex; //const computeTwoDir = e.pano == item.item); //一定数量内允许双向计算,也就是允许changeTex. 这样一来,每到一个新的pano最多只有开头一秒卡一点,不然一直changeTex很卡 var result = _this.isNeighbour(pano, item.item, { onlyUseTex: !byCloud, computeDirFirst: true, computeTwoDir }); //计算 if (result != void 0) { //计算了 (byTex时其实并不一定) //console.log('提前计算neighbor', pano.id, item.item.id) _this.dispatchEvent({ type: 'getNeighbourAuto', panos: [pano, item.item] }); if (byCloud) { //只计算一次 //console.log('提前计算neighbor byCloud', result, pano.id, item.item.id) return 1; // break outer } getCount++; if (ifOverTime()) { return 1; // break outer } } }, _ret; for (var a = 0, b = g.length; a < b; a++) { _ret = _loop(); if (_ret === 0) continue; if (_ret === 1) break outer; } lastIndex = i + 1; //这轮结束 if (ifOverTime()) { break outer; } } /* if(changeCount){ console.log( 'changeCount', changeCount) } */ this.depthSampler.removeEventListener('changeImg', changeTexCount); if (!inited) { inited = true; this.addEventListener('loadedDepthImg', e => { //console.log('loadedDepthImg',e.pano) var computeTwoDir = this.depthSampler.nearPanos.includes(e.pano) || this.depthSampler.imgDatas.some(a => e.pano == a.pano); if (computeTwoDir) { lastIndex = 0; //主要针对刚打开场景第一个点有杂点时单向不成功,要立即进一步双向计算ifShelter } }); } var costTime = Date.now() - startTime; //控制好时间,否则转动时卡(主要是开始拖拽时延迟). 尽量在转完一圈使附近disMap都算完后costTime和maxWaitDur接近 //costTime > maxWaitDur && changeCount == 0 && console.log( 'costTime:',costTime) //console.log('getCount',getCount) }; }(); Images360.filters = { inPanoDirection: function inPanoDirection(pos, dir, i, log) { //pano在mouse的方向上 return function (pano) { var o = pano.position.clone().sub(pos).normalize(); if (o.dot(dir) > i) return true; var r = pano.floorPosition.clone().sub(pos).normalize(); if (r.dot(dir) > i) return true; }; }, inFloorDirection: function inFloorDirection(pos, dir, min, log) { //pano在mouse的水平方向上 return function (pano) { var vec = new Vector2().subVectors(pano.floorPosition, pos).normalize(); var dir_ = new Vector2().copy(dir).normalize(); log && console.log('dire', pano.id, vec.dot(dir_)); return vec.dot(dir_) > min; /* var i = pano.floorPosition.clone().sub(pos).setZ(0).normalize();//改成在xz方向上,否则点击墙面不会移动 return i.dot(dir.clone().setZ(0)) > min */ }; }, isNotBehindNormal: function isNotBehindNormal(e, t) { var i = new Vector3(); return t = t.clone(), function (n) { var r = i.copy(n.position).sub(e).normalize(); return r.dot(t) > 0; }; }, isCloseEnoughTo: function isCloseEnoughTo(e, t) { return function (i) { //因为marker可能比地面高,所以识别范围要比marker看起来更近一些。(因为投影到地板的位置比marker更近) return e.distanceTo(i.floorPosition) < t; //许钟文 }; }, not: function not(e) { return function (t) { return t !== e; }; }, isEnabled: function isEnabled() { return function (t) { return t.enabled; }; }, isVisible: function isVisible() { return function (t) { return t.visible; }; } }; Images360.scoreFunctions = { direction: function direction(curPos, dir, ifLog) { return function (pano) { var pos1 = /* pano.floorPosition */pano.position; //旧:改为权重放在marker上,这样对有斜坡的更准确,如上楼, 但这样近距离的pano角度就会向下了,以致于走不到 var n = pos1.clone().sub(curPos).normalize(); //ifLog && console.log('direction', pano.id, n.dot(dir) * directionFactor ) return n.dot(dir) * directionFactor; }; }, distance: function distance(pos1) { var r = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var ifLog = arguments.length > 2 ? arguments[2] : undefined; if (pos1.position) pos1 = pos1.position; return function (pano) { //许钟文 改 var pos2 = pano.position.clone(); //ifLog && console.log('distance', pano.id, pos1.distance(pos2) * -1 ) return pos1.distanceTo(pos2) * -1 * r; }; }, distanceSquared: function distanceSquared(pos1) { var r = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; if (pos1.position) pos1 = pos1.position; return function (pano) { //许钟文 改 var pos2 = pano.position.clone(); return pos1.distanceToSquared(pos2) * -1 * r; }; }, distanceSquaredNew: function distanceSquaredNew(mainPano) { var r = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; var disMap = viewer.images360.disMap; return function (pano) { //许钟文 改 if (!disMap[mainPano.id]) disMap[mainPano.id] = {}; if (!disMap[pano.id]) disMap[pano.id] = {}; var dis; if (disMap[mainPano.id][pano.id] != void 0) dis = disMap[mainPano.id][pano.id];else if (disMap[pano.id][mainPano.id]) dis = disMap[pano.id][mainPano.id];else { dis = Images360.scoreFunctions.distanceSquared(mainPano, 1)(pano); disMap[mainPano.id][pano.id] = dis; } return dis * r; }; }, angle: function angle(e, t) { return function (i) { var n = i.position.clone().sub(e).normalize(); return n.angleTo(t) * Potree.config.navigation.angleFactor; }; } }; Images360.sortFunctions = { //排序函数,涉及到两个item相减 floorDisSquaredToPoint: function floorDisSquaredToPoint(e) { return function (t, i) { return t.floorPosition.distanceToSquared(e) - i.floorPosition.distanceToSquared(e); }; }, disSquaredToPoint: function disSquaredToPoint(e) { return function (t, i) { return t.position.distanceToSquared(e) - i.position.distanceToSquared(e); }; } }; Images360.prototype.updateCube = function () { //增加细分的版本,且垂直方向上取中位数 侧边多条 var minDis = 0.2; //pano和墙距离不能小于相机的near var height = 1; //准确计算的话要分别计算两个pano的地面、天花板之间的俯仰角,和pano之间的还不一样。这里统一假设一个比较小的高度,因高度越大 maxSinAlpha 越大 var minTanBeta = minDis / height; /* (pano0.position.z - pano0.floorPosition.z) */ var minBeta = Math.atan(minTanBeta); var maxSinAlpha = Math.cos(minBeta); // 注:beta = Math/2 - alpha var skyHeight = 50; return function (pano0, pano1) { if (Potree.settings.displayMode != 'showPanos' || pano0 == pano1 || this.cubePanos.some(e => e.pano == pano0 && e.matrix.equals(pano0.pointcloud.matrix)) && (!pano1 || this.cubePanos.some(e => e.pano == pano1 && e.matrix.equals(pano1.pointcloud.matrix)))) return; this.cubePanos = [{ pano: pano0, matrix: pano0.pointcloud.matrix.clone() }, { pano: pano1, matrix: pano1 && pano1.pointcloud.matrix.clone() }]; viewer.addTimeMark('updateCube', 'start'); //console.log('updateCube',pano0.id, pano1&&pano1.id) var useBound = (bound, size) => { size = size || bound.getSize(new Vector3()); var center = bound.getCenter(new Vector3()); size.max(new Vector3(HighMapCubeWidth, HighMapCubeWidth, HighMapCubeWidth)); this.cube.geometry = new BoxBufferGeometry(1, 1, 1, 1); this.cube.scale.copy(size); this.cube.position.copy(center); if (Potree.settings.testCube) { this.cubeTest.geometry = this.cube.geometry; this.cubeTest.scale.copy(size); this.cubeTest.position.copy(center); } return 'useBound'; }; var getPanoBound = (pano, dis) => { //因漫游点可能在点云外部,如室外平地,所以需要union进漫游点 var panoBound = new Box3(); panoBound.expandByPoint(pano.position); var margin = dis ? Math.max(dis * 0.1, 10) : 10; panoBound.expandByVector(new Vector3(margin, margin, margin)); //give pano a margin 。 随着bound扩大,margin要相应扩大,否则当距离很远时看起来就像没有 return pano.pointcloud.bound.clone().union(panoBound); }; this.cube.geometry.dispose(); if (pano1) { //过渡 var dontAddSides; var dis = pano0.position.distanceTo(pano1.position); if (dis == 0) return; var sinAlpha = Math.abs(pano0.position.z - pano1.position.z) / dis; //俯仰角的sin,随角度增大而增大 0-1 var score = (1 + sinAlpha * 20) * dis; //score越大创建的mesh越不适合 var isNeighbour = this.isNeighbour(pano0, pano1); //console.log(pano0.id, pano1.id, maxSinAlpha.toFixed(2), sinAlpha.toFixed(2), score.toFixed(2), isNeighbour) if (sinAlpha > maxSinAlpha || !pano0.pointcloud.hasDepthTex || !pano1.pointcloud.hasDepthTex || (isNeighbour ? score > 100 : score > 50)) { var bound = getPanoBound(pano0, dis).union(getPanoBound(pano1, dis)); var size = bound.getSize(new Vector3()); var max = Math.max(size.x, size.y, size.z); size.set(max, max, max); //距离太远的数据集,过渡会畸变。所以扩大skybox,且为立方体 return useBound(bound, size); } else if (isNeighbour ? score > 70 : score > 15) { dontAddSides = true; //pano间有阻挡时得到的side点可能使通道变窄,所以去掉。 } //俯仰角增大时可能不在同一楼层,算出来的mesh不太好,所以更倾向直接使用cube,或去除side。 var half = browser.isMobile() ? 4 : 8; //自行输入 (点云计算的慢,还不准) var count1 = 2 * half; //偶数个 每个pano向 外dir 个数 //奇数个的好处:在窄空间内能探测到最远距离,坏处是前方有尖角。偶数个的坏处就是可能检测距离太近。 var getDir = (angle_, vec) => { //旋转获得水平向量 var rotMat = new Matrix4().makeRotationZ(angle_); return vec.clone().applyMatrix4(rotMat); }; var getFar = (dir, pano, origin, height) => { //获取在这个方向上和墙体intersect的距离 //在垂直方向上分出多个方向,取一个最可能的接近真实的距离 var maxH = 40, minH = 2, minR = 0.5, maxR = 2; height = height == void 0 ? Math.min(skyHeight, pano.ceilZ - pano.floorPosition.z) : height; //let r = height (maxH - minH)* 0.14 // 高度越小,角度越小 //let r = minR + ( maxR - minR) * THREE.Math.clamp((height - minH) / (maxH - minH),0,1) //THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound); var r = math.linearClamp(height, [minH, maxH], [minR, maxR]); var getZ = deg => { deg *= r; deg = MathUtils.clamp(deg, 1, 80); return Math.tan(MathUtils.degToRad(deg)); }; var dirs_; //注意:角度太大会碰到天花板或地板,越远越容易碰到, 在地下停车场就会伸展不开。 更多取接近0度的,否则围墙矮一点就会取到远处。 dirs_ = [15, 8, 3, -1, -5]; dirs_ = dirs_.map(deg => dir.clone().setZ(getZ(deg)).normalize()); var max = 50; var count2 = dirs_.length; var disArr = dirs_.map((dir_, i) => { var intersect = this.getIntersect(pano, dir_, origin); var projectLen = intersect && intersect.distance ? dir_.dot(dir) * intersect.distance : max; //得到project在dir的长度 return projectLen; //得水平距离 }); //console.log(pano ? pano.id : 'side','disArr', disArr.slice(0)) disArr.sort((a, b) => { return b - a; }); //从大到小 //console.log(pano ? pano.id : 'side','disArr', disArr) var dis = disArr[Math.floor(count2 / 2 - 0.5)]; //对半、取前(中位数) return dis; }; var sideCount = [0, 0]; var addPos = (pano, vec) => { //添加这个pano这一侧向外半圆的顶点 //添加pano位置对应的最高点最低点: var minZ, maxZ; minZ = pano.floorPosition.z; maxZ = pano.getCeilHeight(); if (maxZ == Infinity) maxZ = skyHeight + pano.position.z; //maxZ = Math.max(skyHeight, maxZ) [maxZ, minZ].forEach(z => { posArr.push(pano.position.clone().setZ(z)); }); //在画面上线条从左往右数 var angle = Math.PI / (count1 - 1); var dirs = []; //平分这半边180度 for (var i = 0; i < count1; i++) { dirs.push(getDir(Math.PI / 2 - i * angle, vec)); //正的在左边 } var dirs2 = dirs.map(dir => { return { dir, dis: getFar(dir, pano) }; }); //剔除那些突然间离相机很近的dir。有可能是拍摄的人、或者杆子、树 /* dirs2.forEach((e,i)=>{ console.log(i, e.dis) let smallThanBefore = ()=>{ return dirs2[i-1].dis / e.dis > maxRatio } let smallThanAfter = ()=>{ return dirs2[i+1].dis / e.dis > maxRatio } if(i>0 && i { start += 1; //不包含start和end var count = end - start; var dis = 0; for (var m = start; m < end; m++) { //得平均数 dis += dirs2[m].dis; } dis /= count; var angle = Math.PI / (count1 - 1) * count / 2; var width = dis * Math.tan(angle); //得到block的半宽度 return width; }; var changeDis = (start, end) => { //不包含start start += 1; //不包含start和end for (var m = start; m < end; m++) { dirs2[m].disB = dirs2[end].dis * 0.8; //console.log('changeDis', m, dirs2[m].disB) } }; var start = -1; for (var _i = 0; _i < count1; _i++) { //遍历时将左边dis比之小很多且宽度较小的改大 //console.log(i, dirs2[i].dis) var j = _i - 1; var ratios = 0; while (j > start && dirs2[_i].dis / dirs2[j].dis > maxRatio1) { ratios += dirs2[_i].dis / dirs2[j].dis; j--; } var count = _i - j - 1; ratios /= count; if (count > 0 && computeWidth(j, _i) < minWidth * ratios / maxRatio2) { //怎么感觉好像改成了视觉宽度小于某个值即可,那直接用count好了? changeDis(j, _i); start = _i; //在此之前的修改过,之后不用再判断 } /* if(count > 0 && (count == 1 || computeWidth(j,i) { var dir = e.dir.clone().multiplyScalar(e.disB || e.dis); [maxZ, minZ].forEach(z => { posArr.push(pano.position.clone().setZ(z).add(dir)); //获取到外墙点 }); }); }; var addSide = () => { //两个漫游点间两边各加一些侧线 //中点处的 var top0 = pano0.ceilZ == Infinity ? pano0.position.z + skyHeight : pano0.ceilZ; var top1 = pano1.ceilZ == Infinity ? pano1.position.z + skyHeight : pano1.ceilZ; var midMaxZ = (top0 + top1) / 2; var midMinZ = (pano0.floorPosition.z + pano1.floorPosition.z) / 2; var mid = new Vector3().addVectors(pano0.position, pano1.position).multiplyScalar(0.5); if (!dontAddSides) { if (pano0.pointcloud.hasDepthTex && pano0.pointcloud.hasDepthTex) { var panos = [pano0, pano1]; var vecs = [vec.clone().negate(), vec]; var axis = [[-1, 1], [1, -1]]; var dis2d = new Vector2().subVectors(pano0.position, pano1.position).length(); //水平上的距离 var maxDis = 50, _minDis = 0.5, minR = 0.2, maxR = 1.2; //let r = maxR - ( maxR - minR) * THREE.Math.clamp((dis2d - minDis) / (maxDis - minDis),0,1) //dis2d越大,角度要越小 //THREE.Math.smoothstep(currentDis, op.nearBound, op.farBound); var r = math.linearClamp(dis2d, [_minDis, maxDis], [maxR, minR]); //console.log('dis2d',dis2d,'r',r) var angles = (browser.isMobile() /* || dis2d<4 */ ? [60] : [50, 70] /* [35,65] */).map(deg => { //正的在左边 尽量能够平分中间这段墙体。 (角度为从中心向外) var angle = MathUtils.clamp(deg * r, 5, 80); //console.log('angle',angle) return MathUtils.degToRad(angle); }); axis.forEach((axis_, index0) => { var disToSides = []; var accordingPano = index0 == 0 ? pano0 : pano1; //根据离该点在vec方向上的距离顺序来存顶点 panos.forEach((pano, index) => { var dirs = angles.map(angle => getDir(axis_[index] * angle, vecs[index])); //一侧的若干角度 dirs.forEach((dir_, i) => { var dis1 = getFar(dir_, pano); var disToPano2d = dis1 * Math.cos(angles[i]); if (disToPano2d < dis2d) { //超过的话都到另一半pano的半圆了,不计入。(角度越小越容易超过) var disToSide = dis1 * Math.sin(angles[i]); if (pano != accordingPano) { disToPano2d = dis2d - disToPano2d; //反一下 } dir_.multiplyScalar(dis1); disToSides.push({ disToSide, disToPano2d, pano, dir_ }); } else { //console.log('超过',index0,index,i) } }); }); if (disToSides.length) { //disToSides.sort((a,b)=>{return b-a});//从大到小 //由距离accordingPano的近到远: disToSides.sort((a, b) => { return a.disToPano2d - b.disToPano2d; }); //console.log('disToSides', index0, disToSides) if (disToSides.length == 1 && disToSides[0].disToSide < 0.5) { disToSides = []; //如果太近直接去除 } disToSides.forEach(e => { //求z var ratio = e.disToPano2d / dis2d; var r = accordingPano == pano0 ? 1 - ratio : ratio; var sideMaxZ_ = top0 * r + top1 * (1 - r); var sideMinZ_ = pano0.floorPosition.z * r + pano1.floorPosition.z * (1 - r); [sideMaxZ_, sideMinZ_].forEach(z => { posArr.push(e.pano.position.clone().setZ(z).add(e.dir_)); //是直接使用最长dis的那个intersect点好还是mid }); }); } sideCount[index0] = disToSides.length; //记录侧边个数 }); } else { //这段针对点云时,仅测试才会执行到 sideCount = [1, 1]; var sideDirs = [getDir(Math.PI / 2, vec), getDir(-Math.PI / 2, vec)]; sideDirs.forEach((dir_, index) => { var dis = getFar(dir_, null, mid, midMaxZ - midMinZ); //直接从中点求两侧的距离 dir_.multiplyScalar( /* Math.max( */dis /* , sideDis[index]) */); [midMaxZ, midMinZ].forEach(z => { posArr.push(mid.clone().setZ(z).add(dir_)); }); }); } } //中心: [midMaxZ, midMinZ].forEach(z => { posArr.push(mid.clone().setZ(z)); }); }; //positions存放顺序:pano的每边的 zMax和zMin 、count1个dir的点 ;侧边的点 ;连接处顶底的中点 var addFaces = () => { var getPI = function getPI(index, posType, panoIndex) { //获取顶点序号 return 2 + (count1 * 2 + 2) * panoIndex + index * 2 + (posType == 'top' ? 0 : 1); }; var getSidePI = function getSidePI(index, posType, panoIndex) { //获取侧边顶点序号 if (panoIndex == 1) index += sideCount[0]; return getPI(index, posType, 2) - 2; }; var getPanoPI = function getPanoPI(posType, panoIndex) { //获取pano处对应的点序号 return getPI(-1, posType, panoIndex); }; var topCenter = posArr.length - 2; //最后添加的两个中心点 var btmCenter = posArr.length - 1; for (var i = 0; i < 2; i++) { for (var index = 1; index < count1; index++) { //pano外侧半圆围墙 faceArr.push([getPI(index, 'top', i), getPI(index - 1, 'btm', i), getPI(index - 1, 'top', i)], //加入一块四方面 [getPI(index, 'top', i), getPI(index, 'btm', i), getPI(index - 1, 'btm', i)]); faceArr.push([getPI(index, 'top', i), getPI(index - 1, 'top', i), getPanoPI('top', i)]); //天花板扇形 faceArr.push([getPI(index, 'btm', i), getPI(index - 1, 'btm', i), getPanoPI('btm', i)]); //地板扇形 } var j = (i + 1) % 2; //另一个pano //侧边 for (var u = 0; u <= sideCount[i]; u++) { //侧边每个四方的左上右上左下右下四个点 var pLT = u == 0 ? getPI(0, 'top', i) : getSidePI(u - 1, 'top', i); var pRT = u == sideCount[i] ? getPI(count1 - 1, 'top', j) : getSidePI(u, 'top', i); var pLB = u == 0 ? getPI(0, 'btm', i) : getSidePI(u - 1, 'btm', i); var pRB = u == sideCount[i] ? getPI(count1 - 1, 'btm', j) : getSidePI(u, 'btm', i); faceArr.push([pLT, pLB, pRB], [pLT, pRB, pRT]); //外侧四方面 faceArr.push([pLT, topCenter, pRT], [pLB, btmCenter, pRB]); //顶部和底部到整体中心的扇形(由于点的顺序是根据和两个pano的距离,因此到中心的夹角并没按顺序排列,所以可能会重叠) if (i == 0) { //只需要算一次 //顶部和底部中心与两个pano边构成的三角形 if (u == 0) { faceArr.push([pLT, getPI(count1 - 1, 'top', i), topCenter], [pLB, getPI(count1 - 1, 'btm', i), btmCenter]); } //不能用else 因为当length==0时u==0也==sideCount[i],此时就是要两个面 if (u == sideCount[i]) { faceArr.push([pRT, getPI(0, 'top', j), topCenter], [pRB, getPI(0, 'btm', j), btmCenter]); } } } } }; var posArr = []; var faceArr = []; //两点连线的水平向量 var vec = new Vector3().subVectors(pano0.position, pano1.position).setZ(0).normalize(); /* let sideDis0 = */ addPos(pano0, vec); /* let sideDis1 = */ addPos(pano1, vec.clone().negate()); addSide(); addFaces(); //MeshDraw.updateGeometry(this.cube.geometry, posArr, faceArr) var geo = MeshDraw.createGeometry(posArr, faceArr); this.cube.geometry = geo; this.cube.scale.set(1, 1, 1); this.cube.position.set(0, 0, 0); if (Potree.settings.testCube) { this.cubeTest.geometry = geo; this.cubeTest.scale.set(1, 1, 1); this.cubeTest.position.set(0, 0, 0); } } else { useBound(getPanoBound(pano0)); } viewer.addTimeMark('updateCube', 'end'); return true; /* 关于卡顿: 即使使用cube,若scale设置为只容纳两个pano,也会卡顿。但也是有的卡有的不卡。 若按照原先的复杂geo,一般在平直的街道上行走流畅,经过拐弯或者复杂区域较卡。减小geo复杂度没有什么作用。 注: 修改skybox,若不准的话,会遮住其他mesh,比如marker。尤其在没有深度贴图时。 耗时: chrome: 20+ ,但是firefox:500+ 咋回事 ? iphonex safari: 40+ */ }; }(); /* viewer.images360.flyToPano(viewer.images360.getPano('1765955963253690368|70','sid')) viewer.images360.flyToPano(viewer.images360.lastPano) */ /** * @author alteredq / http://alteredqualia.com/ * * Full-screen textured quad shader */ var CopyShader = { uniforms: { "tDiffuse": { value: null }, "opacity": { value: 1.0 } }, vertexShader: ["varying vec2 vUv;", "void main() {", "vUv = uv;", "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", "}"].join("\n"), fragmentShader: ["uniform float opacity;", "uniform sampler2D tDiffuse;", "varying vec2 vUv;", "void main() {", "vec4 texel = texture2D( tDiffuse, vUv );", //如果开启premultipliedAlpha用这个,否则用注释的 "gl_FragColor = opacity * texel;", //"gl_FragColor = texture2D( tDiffuse, vUv );", //"gl_FragColor.a *= opacity;", "}"].join("\n") }; /** * @author alteredq / http://alteredqualia.com/ */ var Pass = function Pass() { // if set to true, the pass is processed by the composer this.enabled = true; // if set to true, the pass indicates to swap read and write buffer after rendering this.needsSwap = true; // if set to true, the pass clears its buffer before rendering this.clear = false; // if set to true, the result of the pass is rendered to screen this.renderToScreen = false; }; Object.assign(Pass.prototype, { setSize: function setSize(width, height) {}, render: function render(renderer, writeBuffer, readBuffer, delta, maskActive) { console.error('THREE.Pass: .render() must be implemented in derived pass.'); } }); var ShaderPass = function ShaderPass(shader, textureID) { Pass.call(this); this.textureID = textureID !== undefined ? textureID : "tDiffuse"; if (shader instanceof ShaderMaterial) { this.uniforms = shader.uniforms; this.material = shader; } else if (shader) { this.uniforms = UniformsUtils.clone(shader.uniforms); this.material = new ShaderMaterial({ defines: Object.assign({}, shader.defines), uniforms: this.uniforms, vertexShader: shader.vertexShader, fragmentShader: shader.fragmentShader, transparent: true //add }); } this.camera = new OrthographicCamera(-1, 1, 1, -1, 0, 1); this.scene = new Scene(); this.quad = new Mesh(new PlaneBufferGeometry(2, 2), null); this.quad.frustumCulled = false; // Avoid getting clipped this.scene.add(this.quad); }; ShaderPass.prototype = Object.assign(Object.create(Pass.prototype), { constructor: ShaderPass, render: function render(scene, camera, viewports, renderer, writeBuffer, readBuffer, delta, maskActive) { var oldTarget = renderer.getRenderTarget(); /* if(this.readTarget){ //add readBuffer = oldTarget } */ if (this.uniforms[this.textureID]) { this.uniforms[this.textureID].value = readBuffer.texture; } this.quad.material = this.material; if (this.renderToScreen) { renderer.render(this.scene, this.camera); } else { renderer.setRenderTarget(writeBuffer); if (this.clear) renderer.clear(); renderer.render(this.scene, this.camera); renderer.setRenderTarget(oldTarget); } } }); // Copied from three.js: WebGLRenderer.js function paramThreeToGL$1(_gl, 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$1) 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$1) 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; } ; var attributeLocations$1 = { "position": { name: "position", location: 0 }, "color": { name: "color", location: 1 }, "rgba": { name: "color", location: 1 }, "intensity": { name: "intensity", location: 2 }, "classification": { name: "classification", location: 3 }, "returnNumber": { name: "returnNumber", location: 4 }, "return number": { name: "returnNumber", location: 4 }, "returns": { name: "returnNumber", location: 4 }, "numberOfReturns": { name: "numberOfReturns", location: 5 }, "number of returns": { name: "numberOfReturns", location: 5 }, "pointSourceID": { name: "pointSourceID", location: 6 }, "source id": { name: "pointSourceID", location: 6 }, "point source id": { name: "pointSourceID", location: 6 }, "indices": { name: "indices", location: 7 }, "normal": { name: "normal", location: 8 }, "spacing": { name: "spacing", location: 9 }, "gps-time": { name: "gpsTime", location: 10 }, "aExtra": { name: "aExtra", location: 11 }, //add: ir: { name: "ir", location: 12 }, temp: { name: "temp", location: 13 } }; class Shader$1 { constructor(gl, name, vsSource, fsSource) { this.gl = gl; this.name = name; this.vsSource = vsSource; this.fsSource = fsSource; this.cache = new Map(); this.vs = null; this.fs = null; this.program = null; this.uniformLocations = {}; this.attributeLocations = {}; this.uniformBlockIndices = {}; this.uniformBlocks = {}; this.uniforms = {}; this.update(vsSource, fsSource); } update(vsSource, fsSource) { this.vsSource = vsSource; this.fsSource = fsSource; this.linkProgram(); } compileShader(shader, source) { var gl = this.gl; gl.shaderSource(shader, source); gl.compileShader(shader); var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS); if (!success) { var info = gl.getShaderInfoLog(shader); var numberedSource = source.split("\n").map((a, i) => "".concat(i + 1).padEnd(5) + a).join("\n"); throw "could not compile shader ".concat(this.name, ": ").concat(info, ", \n").concat(numberedSource); } } linkProgram() { var tStart = performance.now(); var gl = this.gl; this.uniformLocations = {}; this.attributeLocations = {}; this.uniforms = {}; gl.useProgram(null); var cached = this.cache.get("".concat(this.vsSource, ", ").concat(this.fsSource)); if (cached) { this.program = cached.program; this.vs = cached.vs; this.fs = cached.fs; this.attributeLocations = cached.attributeLocations; this.uniformLocations = cached.uniformLocations; this.uniformBlocks = cached.uniformBlocks; this.uniforms = cached.uniforms; return; } else { this.vs = gl.createShader(gl.VERTEX_SHADER); this.fs = gl.createShader(gl.FRAGMENT_SHADER); this.program = gl.createProgram(); if (!gl.isProgram(this.program)) { //创建失败 开启多个页面可能会,原因是webglcontextlost //console.error('创建program失败'); viewer.dispatchEvent('webglError', { msg: 'potreeRenderer创建program失败' }); console.log(this.vs); console.log(this.fs); return; } for (var name of Object.keys(attributeLocations$1)) { var location = attributeLocations$1[name].location; var glslName = attributeLocations$1[name].name; gl.bindAttribLocation(this.program, location, glslName); } this.compileShader(this.vs, this.vsSource); this.compileShader(this.fs, this.fsSource); var program = this.program; gl.attachShader(program, this.vs); gl.attachShader(program, this.fs); gl.linkProgram(program); gl.detachShader(program, this.vs); gl.detachShader(program, this.fs); // 检测当前程序链接状态 var success = gl.getProgramParameter(program, gl.LINK_STATUS); if (!success) { var info = gl.getProgramInfoLog(program); throw "could not link program ".concat(this.name, ": ").concat(info); } { // attribute locations var numAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); for (var i = 0; i < numAttributes; i++) { var attribute = gl.getActiveAttrib(program, i); var _location = gl.getAttribLocation(program, attribute.name); this.attributeLocations[attribute.name] = _location; } } { // uniform locations var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); for (var _i = 0; _i < numUniforms; _i++) { var uniform = gl.getActiveUniform(program, _i); var _location2 = gl.getUniformLocation(program, uniform.name); this.uniformLocations[uniform.name] = _location2; this.uniforms[uniform.name] = { location: _location2, value: null }; } } // uniform blocks if (typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext) { //WebGL2RenderingContext在mac的safari14以下是没有定义的 var numBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS); for (var _i2 = 0; _i2 < numBlocks; _i2++) { var blockName = gl.getActiveUniformBlockName(program, _i2); var blockIndex = gl.getUniformBlockIndex(program, blockName); this.uniformBlockIndices[blockName] = blockIndex; gl.uniformBlockBinding(program, blockIndex, blockIndex); var dataSize = gl.getActiveUniformBlockParameter(program, blockIndex, gl.UNIFORM_BLOCK_DATA_SIZE); var uBuffer = gl.createBuffer(); gl.bindBuffer(gl.UNIFORM_BUFFER, uBuffer); gl.bufferData(gl.UNIFORM_BUFFER, dataSize, gl.DYNAMIC_READ); gl.bindBufferBase(gl.UNIFORM_BUFFER, blockIndex, uBuffer); gl.bindBuffer(gl.UNIFORM_BUFFER, null); this.uniformBlocks[blockName] = { name: blockName, index: blockIndex, dataSize: dataSize, buffer: uBuffer }; } } var _cached = { program: this.program, vs: this.vs, fs: this.fs, attributeLocations: this.attributeLocations, uniformLocations: this.uniformLocations, uniforms: this.uniforms, uniformBlocks: this.uniformBlocks }; this.cache.set("".concat(this.vsSource, ", ").concat(this.fsSource), _cached); } var tEnd = performance.now(); var duration = tEnd - tStart; //console.log(`shader compile duration: ${duration.toFixed(3)}`); } setUniformMatrix4(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } var tmp = new Float32Array(value.elements); gl.uniformMatrix4fv(location, false, tmp); } setUniform1f(name, value) { var gl = this.gl; var uniform = this.uniforms[name]; if (uniform === undefined) { return; } if (uniform.value === value) { return; } uniform.value = value; gl.uniform1f(uniform.location, value); } setUniformBoolean(name, value) { var gl = this.gl; var uniform = this.uniforms[name]; if (uniform === undefined) { return; } if (uniform.value === value) { return; } uniform.value = value; gl.uniform1i(uniform.location, value); } setUniformTexture(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform1i(location, value); } setUniform2f(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform2f(location, value[0], value[1]); } setUniform3f(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform3f(location, value[0], value[1], value[2]); } setUniform(name, value) { if (value.constructor === Matrix4) { this.setUniformMatrix4(name, value); } else if (typeof value === "number") { this.setUniform1f(name, value); } else if (typeof value === "boolean") { this.setUniformBoolean(name, value); } else if (value instanceof WebGLTexture$1) { this.setUniformTexture(name, value); } else if (value instanceof Array) { if (value.length === 2) { this.setUniform2f(name, value); } else if (value.length === 3) { this.setUniform3f(name, value); } } else { console.error("unhandled uniform type: ", name, value); } } setUniform1i(name, value) { var gl = this.gl; var location = this.uniformLocations[name]; if (location == null) { return; } gl.uniform1i(location, value); } } ; class WebGLTexture$1 { constructor(gl, texture, threeRenderer) { this.gl = gl; this.texture = texture; if (texture.image && !(texture.image instanceof Image) && !texture.isCanvasTexture && !texture.isDataTexture) { //renderTarget的texture在创建renderTarget时已经初始化过 见setupRenderTarget this.id = threeRenderer.properties.get(texture).__webglTexture || gl.createTexture(); this.isFromRenderTarget = true; } else { this.id = gl.createTexture(); } this.target = gl.TEXTURE_2D; this.version = -1; this.update(texture); } update() { if (!this.texture.image) { this.version = this.texture.version; return; } //if(this.isFromRenderTarget)return //没找到怎么update。 在three.js里的uploadTexture没找到。 这里会报错,可能是 state.texImage2D( 3553, 0, glInternalFormat, image.width, image.height, 0, glFormat, glType, null ),类似depthTex的写法 var gl = this.gl; var texture = this.texture; if (this.version === texture.version) { return; } this.target = gl.TEXTURE_2D; gl.bindTexture(this.target, this.id); var level = 0; var internalFormat = paramThreeToGL$1(gl, texture.format); var width = texture.image.width; var height = texture.image.height; var border = 0; var srcFormat = internalFormat; var srcType = paramThreeToGL$1(gl, texture.type); var data; 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); if (texture instanceof DataTexture) { data = texture.image.data; gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL$1(gl, texture.magFilter)); gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL$1(gl, texture.minFilter)); gl.texImage2D(this.target, level, internalFormat, width, height, border, srcFormat, srcType, data); } else if (texture instanceof CanvasTexture || texture instanceof Texture) { data = texture.image; gl.texParameteri(this.target, gl.TEXTURE_WRAP_S, paramThreeToGL$1(gl, texture.wrapS)); gl.texParameteri(this.target, gl.TEXTURE_WRAP_T, paramThreeToGL$1(gl, texture.wrapT)); gl.texParameteri(this.target, gl.TEXTURE_MAG_FILTER, paramThreeToGL$1(gl, texture.magFilter)); gl.texParameteri(this.target, gl.TEXTURE_MIN_FILTER, paramThreeToGL$1(gl, texture.minFilter)); if (this.isFromRenderTarget) { //咋写? //gl.texImage2D( 3553, 0, internalFormat, width, height, 0, srcFormat, srcType, null ) //console.log('isFromRenderTarget') } else { gl.texImage2D(this.target, level, internalFormat, internalFormat, srcType, data); //这个参数怎么跟我查的不一样呢 } if (texture instanceof Texture) { gl.generateMipmap(gl.TEXTURE_2D); } } gl.bindTexture(this.target, null); this.version = texture.version; } } ; class WebGLBuffer$1 { constructor() { this.numElements = 0; this.vao = null; this.vbos = new Map(); } } ; class Renderer$1 { constructor(threeRenderer) { this.threeRenderer = threeRenderer; this.gl = this.threeRenderer.getContext(); this.buffers = new Map(); this.shaders = new Map(); this.textures = new Map(); this.glTypeMapping = new Map(); this.glTypeMapping.set(Float32Array, this.gl.FLOAT); this.glTypeMapping.set(Uint8Array, this.gl.UNSIGNED_BYTE); this.glTypeMapping.set(Uint16Array, this.gl.UNSIGNED_SHORT); //貌似不能用,uint只能是Uint32Array this.toggle = 0; } deleteBuffer(geometry) { var gl = this.gl; var webglBuffer = this.buffers.get(geometry); if (webglBuffer != null) { for (var attributeName in geometry.attributes) { gl.deleteBuffer(webglBuffer.vbos.get(attributeName).handle); } this.buffers.delete(geometry); } } createBuffer(geometry) { var gl = this.gl; var webglBuffer = new WebGLBuffer$1(); webglBuffer.vao = gl.createVertexArray(); webglBuffer.numElements = geometry.attributes.position.count; gl.bindVertexArray(webglBuffer.vao); for (var attributeName in geometry.attributes) { var bufferAttribute = geometry.attributes[attributeName]; var vbo = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW); var normalized = bufferAttribute.normalized; var type = this.glTypeMapping.get(bufferAttribute.array.constructor); if (attributeLocations$1[attributeName] === undefined) { //attributeLocation = attributeLocations["aExtra"]; } else { var attributeLocation = attributeLocations$1[attributeName].location; gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } webglBuffer.vbos.set(attributeName, { handle: vbo, name: attributeName, count: bufferAttribute.count, itemSize: bufferAttribute.itemSize, type: geometry.attributes.position.array.constructor, version: 0 }); } gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); var disposeHandler = event => { this.deleteBuffer(geometry); geometry.removeEventListener("dispose", disposeHandler); }; geometry.addEventListener("dispose", disposeHandler); return webglBuffer; } updateBuffer(geometry) { var gl = this.gl; var webglBuffer = this.buffers.get(geometry); gl.bindVertexArray(webglBuffer.vao); for (var attributeName in geometry.attributes) { var bufferAttribute = geometry.attributes[attributeName]; var normalized = bufferAttribute.normalized; var type = this.glTypeMapping.get(bufferAttribute.array.constructor); var vbo = null; if (!webglBuffer.vbos.has(attributeName)) { vbo = gl.createBuffer(); webglBuffer.vbos.set(attributeName, { handle: vbo, name: attributeName, count: bufferAttribute.count, itemSize: bufferAttribute.itemSize, type: geometry.attributes.position.array.constructor, version: bufferAttribute.version }); } else { vbo = webglBuffer.vbos.get(attributeName).handle; webglBuffer.vbos.get(attributeName).version = bufferAttribute.version; } gl.bindBuffer(gl.ARRAY_BUFFER, vbo); gl.bufferData(gl.ARRAY_BUFFER, bufferAttribute.array, gl.STATIC_DRAW); if (attributeLocations$1[attributeName] === undefined) { //attributeLocation = attributeLocations["aExtra"]; } else { var attributeLocation = attributeLocations$1[attributeName].location; gl.vertexAttribPointer(attributeLocation, bufferAttribute.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } } gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); } traverse(scene) { var octrees = []; var stack = [scene]; while (stack.length > 0) { var node = stack.pop(); if (node instanceof PointCloudTree) { octrees.push(node); continue; } var visibleChildren = node.children.filter(c => c.visible); stack.push(...visibleChildren); } var result = { octrees: octrees }; return result; } renderNodes(octree, nodes, visibilityTextureData, camera, target, shader, params) { var _this = this; viewer.addTimeMark('renderNodes', 'start'); var gl = this.gl; var material = params.material ? params.material : octree.material; var shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps; var view = camera.matrixWorldInverse; if (params.viewOverride) { view = params.viewOverride; } var worldView = new Matrix4(); var mat4holder = new Float32Array(16); var i = 0; //---------从renderOctree搬到这---- shader.setUniform1f("size", material.usePanoMap ? Potree.config.material.absolutePanoramaSize * Math.min(window.devicePixelRatio, 2) : material.size); //usePanoMap时控制在不大不小的范围内感觉较好,考虑到有的点云稀疏,用大一点的点 shader.setUniform1f("uOpacity", material.usePanoMap ? 1 : material.opacity); shader.setUniform3f("uColor", material.color.toArray()); shader.setUniform2f("temperRange", material.uniforms.temperRange.value.toArray()); var currentTextureBindingPoint = params.currentTextureBindingPoint; if (material.pointSizeType >= 0 /* && window.needvisibilityTexture */) { if (material.pointSizeType === PointSizeType.ADAPTIVE || material.activeAttributeName === "level of detail") { var vnNodes = params.vnTextureNodes != null ? params.vnTextureNodes : nodes; visibilityTextureData = octree.computeVisibilityTextureData(vnNodes, camera); var vnt = material.visibleNodesTexture; var data = vnt.image.data; data.set(visibilityTextureData.data); vnt.needsUpdate = true; var webGLTexture = this.textures.get(vnt); webGLTexture.update(); //不加这个会闪烁 var vnWebGLTexture = this.textures.get(material.visibleNodesTexture); //不知道为什么这段从renderOctree中移过来,会崩溃。暂时不移动了 if (vnWebGLTexture) { shader.setUniform1i("visibleNodes", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(vnWebGLTexture.target, vnWebGLTexture.id); currentTextureBindingPoint++; } } } /* if (material.pointSizeType >= 0) { if (material.pointSizeType === PointSizeType.ADAPTIVE || material.activeAttributeName === "level of detail") { let vnNodes = (params.vnTextureNodes != null) ? params.vnTextureNodes : nodes; visibilityTextureData = octree.computeVisibilityTextureData(vnNodes, camera); const vnt = material.visibleNodesTexture; const data = vnt.image.data; data.set(visibilityTextureData.data); vnt.needsUpdate = true; } } */ var transparent = false; if (params.transparent !== undefined) { transparent = params.transparent && material.opacity < 1; } else { transparent = material.usePanoMap ? false : material.useFilterByNormal || material.opacity < 1; //add useFilterByNormal } if (transparent) { gl.enable(gl.BLEND); if (params.notAdditiveBlending) { gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA); //NormalBlending //gl.disable(gl.DEPTH_TEST); gl.depthMask(true); //old gl.enable(gl.DEPTH_TEST); gl.depthMask(false); //gl.enable(gl.DEPTH_TEST); gl.depthMask(true); //gl.disable(gl.DEPTH_TEST); /* 几种选择: 1 都开启: 当opacity很小时发黑且遮住其他透明物体 2 关闭depthTest开启depthWrite 自己深度错乱,前排挡不住后排, 自己将后排的晚渲染可以改善,见disSqToCamZ_。 但前排不透明遮不住它!之前没发现 3 开启depthTest关闭depthWrite 除了2的缺点,还挡不住后排的半透明物体(因为后渲染),或被先渲染的前排透明物体完全遮住。(写入深度值就会遮住后方。总之透明的要后渲染而且按距离排序,但mesh和点云排序是不可能了) 采用这项 4 关闭depthTest关闭depthWrite 连不透明物都遮不住它,后排透明物能遮住它 */ } else { gl.blendFunc(gl.SRC_ALPHA, gl.ONE); //AdditiveBlending 原本 gl.disable(gl.DEPTH_TEST); gl.depthMask(false); } } else { gl.disable(gl.BLEND); gl.depthMask(true); gl.enable(gl.DEPTH_TEST); } if (params.blendFunc !== undefined) { gl.enable(gl.BLEND); gl.blendFunc(...params.blendFunc); } if (params.depthTest !== undefined) { if (params.depthTest === true) { gl.enable(gl.DEPTH_TEST); } else { gl.disable(gl.DEPTH_TEST); } } if (params.depthWrite !== undefined) { if (params.depthWrite === true) { gl.depthMask(true); } else { gl.depthMask(false); } } //--------------------------------- var _loop = function _loop() { if (exports.debug.allowedNodes !== undefined) { if (!exports.debug.allowedNodes.includes(node.name)) { return 1; // continue } } var world = node.sceneNode.matrixWorld; worldView.multiplyMatrices(view, world); if (visibilityTextureData) { var vnStart = visibilityTextureData.offsets.get(node); //console.log('vnStart',vnStart) shader.setUniform1f("uVNStart", vnStart); } var level = node.getLevel(); if (node.debug) { shader.setUniform("uDebug", true); } else { shader.setUniform("uDebug", false); } // let isLeaf = false; // if(node instanceof PointCloudOctreeNode){ // isLeaf = Object.keys(node.children).length === 0; // }else if(node instanceof PointCloudArena4DNode){ // isLeaf = node.geometryNode.isLeaf; // } // shader.setUniform("uIsLeafNode", isLeaf); // let isLeaf = node.children.filter(n => n != null).length === 0; // if(!isLeaf){ // continue; // } // TODO consider passing matrices in an array to avoid uniformMatrix4fv overhead var lModel = shader.uniformLocations["modelMatrix"]; if (lModel) { mat4holder.set(world.elements); gl.uniformMatrix4fv(lModel, false, mat4holder); } var lModelView = shader.uniformLocations["modelViewMatrix"]; //mat4holder.set(worldView.elements); // faster then set in chrome 63 for (var j = 0; j < 16; j++) { mat4holder[j] = worldView.elements[j]; } gl.uniformMatrix4fv(lModelView, false, mat4holder); { // Clip Polygons if (material.clipPolygons && material.clipPolygons.length > 0) { var clipPolygonVCount = []; var worldViewProjMatrices = []; for (var clipPolygon of material.clipPolygons) { var _view = clipPolygon.viewMatrix; var proj = clipPolygon.projMatrix; var worldViewProj = proj.clone().multiply(_view).multiply(world); clipPolygonVCount.push(clipPolygon.markers.length); worldViewProjMatrices.push(worldViewProj); } var flattenedMatrices = [].concat(...worldViewProjMatrices.map(m => m.elements)); var flattenedVertices = new Array(8 * 3 * material.clipPolygons.length); for (var _i3 = 0; _i3 < material.clipPolygons.length; _i3++) { var _clipPolygon = material.clipPolygons[_i3]; for (var _j = 0; _j < _clipPolygon.markers.length; _j++) { flattenedVertices[_i3 * 24 + (_j * 3 + 0)] = _clipPolygon.markers[_j].position.x; flattenedVertices[_i3 * 24 + (_j * 3 + 1)] = _clipPolygon.markers[_j].position.y; flattenedVertices[_i3 * 24 + (_j * 3 + 2)] = _clipPolygon.markers[_j].position.z; } } var lClipPolygonVCount = shader.uniformLocations["uClipPolygonVCount[0]"]; gl.uniform1iv(lClipPolygonVCount, clipPolygonVCount); var lClipPolygonVP = shader.uniformLocations["uClipPolygonWVP[0]"]; gl.uniformMatrix4fv(lClipPolygonVP, false, flattenedMatrices); var lClipPolygons = shader.uniformLocations["uClipPolygonVertices[0]"]; gl.uniform3fv(lClipPolygons, flattenedVertices); } } //shader.setUniformMatrix4("modelMatrix", world); //shader.setUniformMatrix4("modelViewMatrix", worldView); shader.setUniform1f("uLevel", level); shader.setUniform1f("levelPercent", octree.nodeMaxLevel ? level / octree.nodeMaxLevel : 0.5); //xzw add shader.setUniform1f("uNodeSpacing", node.geometryNode.estimatedSpacing); shader.setUniform1f("uPCIndex", i); // uBBSize if (shadowMaps.length > 0) { var lShadowMap = shader.uniformLocations["uShadowMap[0]"]; shader.setUniform3f("uShadowColor", material.uniforms.uShadowColor.value); var bindingStart = 5; var bindingPoints = new Array(shadowMaps.length).fill(bindingStart).map((a, i) => a + i); gl.uniform1iv(lShadowMap, bindingPoints); for (var _i4 = 0; _i4 < shadowMaps.length; _i4++) { var shadowMap = shadowMaps[_i4]; var bindingPoint = bindingPoints[_i4]; var glTexture = _this.threeRenderer.properties.get(shadowMap.target.texture).__webglTexture; gl.activeTexture(gl["TEXTURE".concat(bindingPoint)]); gl.bindTexture(gl.TEXTURE_2D, glTexture); } { var worldViewMatrices = shadowMaps.map(sm => sm.camera.matrixWorldInverse).map(view => new Matrix4().multiplyMatrices(view, world)); var _flattenedMatrices = [].concat(...worldViewMatrices.map(c => c.elements)); var lWorldView = shader.uniformLocations["uShadowWorldView[0]"]; gl.uniformMatrix4fv(lWorldView, false, _flattenedMatrices); } { var _flattenedMatrices2 = [].concat(...shadowMaps.map(sm => sm.camera.projectionMatrix.elements)); var lProj = shader.uniformLocations["uShadowProj[0]"]; gl.uniformMatrix4fv(lProj, false, _flattenedMatrices2); } } var geometry = node.geometryNode.geometry; if (geometry.attributes["gps-time"]) { var bufferAttribute = geometry.attributes["gps-time"]; var attGPS = octree.getAttribute("gps-time"); var initialRange = attGPS.initialRange; var initialRangeSize = initialRange[1] - initialRange[0]; var globalRange = attGPS.range; var globalRangeSize = globalRange[1] - globalRange[0]; var scale = initialRangeSize / globalRangeSize; var offset = -(globalRange[0] - initialRange[0]) / initialRangeSize; scale = Number.isNaN(scale) ? 1 : scale; offset = Number.isNaN(offset) ? 0 : offset; shader.setUniform1f("uGpsScale", scale); shader.setUniform1f("uGpsOffset", offset); //shader.setUniform2f("uFilterGPSTimeClipRange", [-Infinity, Infinity]); var uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value; // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0] // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1] // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]); var normalizedClipRange = [(uFilterGPSTimeClipRange[0] - globalRange[0]) / globalRangeSize, (uFilterGPSTimeClipRange[1] - globalRange[0]) / globalRangeSize]; shader.setUniform2f("uFilterGPSTimeClipRange", normalizedClipRange); // // ranges in full gps coordinate system // const globalRange = attGPS.range; // const bufferRange = bufferAttribute.potree.range; // // ranges in [0, 1] // // normalizedGlobalRange = [0, 1] // // normalizedBufferRange: norm buffer within norm global range e.g. [0.2, 0.8] // const globalWidth = globalRange[1] - globalRange[0]; // const normalizedBufferRange = [ // (bufferRange[0] - globalRange[0]) / globalWidth, // (bufferRange[1] - globalRange[0]) / globalWidth, // ]; // shader.setUniform2f("uNormalizedGpsBufferRange", normalizedBufferRange); // let uFilterGPSTimeClipRange = material.uniforms.uFilterGPSTimeClipRange.value; // let gpsCliPRangeMin = uFilterGPSTimeClipRange[0] // let gpsCliPRangeMax = uFilterGPSTimeClipRange[1] // shader.setUniform2f("uFilterGPSTimeClipRange", [gpsCliPRangeMin, gpsCliPRangeMax]); // shader.setUniform1f("uGpsScale", bufferAttribute.potree.scale); // shader.setUniform1f("uGpsOffset", bufferAttribute.potree.offset); } { var uFilterReturnNumberRange = material.uniforms.uFilterReturnNumberRange.value; var uFilterNumberOfReturnsRange = material.uniforms.uFilterNumberOfReturnsRange.value; var uFilterPointSourceIDClipRange = material.uniforms.uFilterPointSourceIDClipRange.value; shader.setUniform2f("uFilterReturnNumberRange", uFilterReturnNumberRange); shader.setUniform2f("uFilterNumberOfReturnsRange", uFilterNumberOfReturnsRange); shader.setUniform2f("uFilterPointSourceIDClipRange", uFilterPointSourceIDClipRange); } var webglBuffer = null; if (!_this.buffers.has(geometry)) { webglBuffer = _this.createBuffer(geometry); _this.buffers.set(geometry, webglBuffer); } else { webglBuffer = _this.buffers.get(geometry); for (var attributeName in geometry.attributes) { var attribute = geometry.attributes[attributeName]; if (attribute.version > webglBuffer.vbos.get(attributeName).version) { _this.updateBuffer(geometry); } } } gl.bindVertexArray(webglBuffer.vao); var isExtraAttribute = attributeLocations$1[material.activeAttributeName] === undefined && Object.keys(geometry.attributes).includes(material.activeAttributeName); if (isExtraAttribute) { var attributeLocation = attributeLocations$1["aExtra"].location; for (var _attributeName in geometry.attributes) { var _bufferAttribute = geometry.attributes[_attributeName]; var _vbo = webglBuffer.vbos.get(_attributeName); gl.bindBuffer(gl.ARRAY_BUFFER, _vbo.handle); gl.disableVertexAttribArray(attributeLocation); } var attName = material.activeAttributeName; var _bufferAttribute2 = geometry.attributes[attName]; var vbo = webglBuffer.vbos.get(attName); if (_bufferAttribute2 !== undefined && vbo !== undefined) { var type = _this.glTypeMapping.get(_bufferAttribute2.array.constructor); var normalized = _bufferAttribute2.normalized; gl.bindBuffer(gl.ARRAY_BUFFER, vbo.handle); gl.vertexAttribPointer(attributeLocation, _bufferAttribute2.itemSize, type, normalized, 0, 0); gl.enableVertexAttribArray(attributeLocation); } { var attExtra = octree.pcoGeometry.pointAttributes.attributes.find(a => a.name === attName); var range = material.getRange(attName); if (!range) { range = attExtra.range; } if (!range) { range = [0, 1]; } var _initialRange = attExtra.initialRange; var _initialRangeSize = _initialRange[1] - _initialRange[0]; var _globalRange = range; var _globalRangeSize = _globalRange[1] - _globalRange[0]; var _scale = _initialRangeSize / _globalRangeSize; var _offset = -(_globalRange[0] - _initialRange[0]) / _initialRangeSize; _scale = Number.isNaN(_scale) ? 1 : _scale; _offset = Number.isNaN(_offset) ? 0 : _offset; shader.setUniform1f("uExtraScale", _scale); shader.setUniform1f("uExtraOffset", _offset); } } else { for (var _attributeName2 in geometry.attributes) { var _bufferAttribute3 = geometry.attributes[_attributeName2]; var _vbo2 = webglBuffer.vbos.get(_attributeName2); if (attributeLocations$1[_attributeName2] !== undefined) { var _attributeLocation = attributeLocations$1[_attributeName2].location; var _type = _this.glTypeMapping.get(_bufferAttribute3.array.constructor); var _normalized = _bufferAttribute3.normalized; gl.bindBuffer(gl.ARRAY_BUFFER, _vbo2.handle); gl.vertexAttribPointer(_attributeLocation, _bufferAttribute3.itemSize, _type, _normalized, 0, 0); gl.enableVertexAttribArray(_attributeLocation); } } } var numPoints = webglBuffer.numElements; gl.drawArrays(gl.POINTS, 0, numPoints); //gl.drawArrays(gl.TRIANGLES, 0, numPoints); i++; }; for (var node of nodes) { if (_loop()) continue; } gl.bindVertexArray(null); viewer.addTimeMark('renderNodes', 'end'); } renderOctree(octrees, nodes, camera, target) { var params = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; viewer.addTimeMark('renderOctree', 'start'); var octree; if (octrees instanceof Array) { octree = octrees[0]; } else { octree = octrees; octrees = [octree]; } var gl = this.gl; var material = params.material ? params.material : octree.material; var shadowMaps = params.shadowMaps == null ? [] : params.shadowMaps; var view = camera.matrixWorldInverse; var viewInv = camera.matrixWorld; if (params.viewOverride) { view = params.viewOverride; viewInv = view.clone().invert(); } var proj = camera.projectionMatrix; var projInv = proj.clone().invert(); //let worldView = new THREE.Matrix4(); var shader = null; var visibilityTextureData = null; var currentTextureBindingPoint = 0; /* if (material.pointSizeType >= 0) {//最好搬到renderNodes if (material.pointSizeType === PointSizeType.ADAPTIVE || material.activeAttributeName === "level of detail") { let vnNodes = (params.vnTextureNodes != null) ? params.vnTextureNodes : nodes; visibilityTextureData = octree.computeVisibilityTextureData(vnNodes, camera); const vnt = material.visibleNodesTexture; const data = vnt.image.data; data.set(visibilityTextureData.data); vnt.needsUpdate = true; } } */ { // UPDATE SHADER AND TEXTURES if (!this.shaders.has(material)) { var [vs, fs] = [material.vertexShader, material.fragmentShader]; var _shader = new Shader$1(gl, "pointcloud", vs, fs); this.shaders.set(material, _shader); } shader = this.shaders.get(material); if (material.shaderNeedsUpdate) { var [_vs, _fs] = [material.vertexShader, material.fragmentShader]; var numSnapshots = material.snapEnabled ? material.numSnapshots : 0; var num_in_clipboxes = material.clipBoxes_in && material.clipBoxes_in.length ? material.clipBoxes_in.length : 0; var num_out_clipboxes = material.clipBoxes_out && material.clipBoxes_out.length ? material.clipBoxes_out.length : 0; var num_highlightBox = material.highlightBoxes && material.highlightBoxes.length ? material.highlightBoxes.length : 0; var numClipSpheres = params.clipSpheres && params.clipSpheres.length ? params.clipSpheres.length : 0; var numClipPolygons = material.clipPolygons && material.clipPolygons.length ? material.clipPolygons.length : 0; var defines = ["#define num_shadowmaps ".concat(shadowMaps.length), "#define num_snapshots ".concat(numSnapshots), "#define num_in_clipboxes ".concat(num_in_clipboxes), //改 "#define num_out_clipboxes ".concat(num_out_clipboxes), //改 "#define num_highlightBox ".concat(num_highlightBox), //改 "#define num_clipspheres ".concat(numClipSpheres), "#define num_prism ".concat(material.prisms.pointsCount ? material.prisms.length : 0), //土方量数 如果num_prism>0,prismPointCountSum为0 会报错 :array size must be greater than zero "#define prismPointCountSum ".concat(material.prisms.pointsCount), //点总个数 "#define prism_maxPointsCount ".concat(material.prisms.maxPointsCount) //单个prism最大点个数 (如果define也能传递个数数组,就不用再uniform里传了,呜 ) ]; //add:----------- if (material.bigClipInBox) { //裁剪下载 defines.push('#define bigClipInBox'); } if (material.usePanoMap) { defines.push("#define usePanoMap"); } if (material.useFilterByNormal) { defines.push("#define use_filter_by_normal"); //Potree.settings.editType == 'pano' ? defines.push("#define attenuated_opacity2") : defines.push("#define attenuated_opacity"); } if (material.uniforms.baseHeightAreaMap.value) { //根据模型高亮土方 defines.push('#define showBaseHeight'); } //--------------- if (octree.pcoGeometry.root.isLoaded()) { var attributes = octree.pcoGeometry.root.geometry.attributes; if (attributes["gps-time"]) { defines.push("#define clip_gps_enabled"); } if (attributes["return number"]) { defines.push("#define clip_return_number_enabled"); } if (attributes["number of returns"]) { defines.push("#define clip_number_of_returns_enabled"); } if (attributes["source id"] || attributes["point source id"]) { defines.push("#define clip_point_source_id_enabled"); } } var definesString = defines.join("\n"); var vsVersionIndex = _vs.indexOf("#version "); var fsVersionIndex = _fs.indexOf("#version "); if (vsVersionIndex >= 0) { _vs = _vs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { _vs = "".concat(definesString, "\n").concat(_vs); } if (fsVersionIndex >= 0) { _fs = _fs.replace(/(#version .*)/, "$1\n".concat(definesString)); } else { _fs = "".concat(definesString, "\n").concat(_fs); } shader.update(_vs, _fs); material.shaderNeedsUpdate = false; } for (var uniformName of Object.keys(material.uniforms)) { var uniform = material.uniforms[uniformName]; if (uniform.type == "t") { var texture = uniform.value; if (!texture) { continue; } //add if (uniformName == 'pano0Map' || uniformName == 'pano1Map') { //属于cubeTex,另外设置 continue; } /* if(texture.image && !(texture.image instanceof Image) && !(texture instanceof THREE.CanvasTexture)){ //renderTarget的texture在创建renderTarget时已经初始化过 见setupRenderTarget continue } */ if (!this.textures.has(texture) || texture.needsRebuild) { var webglTexture = new WebGLTexture$1(gl, texture, this.threeRenderer); this.textures.set(texture, webglTexture); delete texture.needsRebuild; //renderTarget在resize后会触发dispose, 然后 _gl.deleteTexture( textureProperties.__webglTexture )所以需要重新建立 } var webGLTexture = this.textures.get(texture); webGLTexture.update(); } } } gl.useProgram(shader.program); /* let transparent = false; if(params.transparent !== undefined){ transparent = params.transparent && material.opacity < 1; }else{ transparent = material.usePanoMap ? false : (material.useFilterByNormal || material.opacity < 1); //add useFilterByNormal } if (transparent){ gl.enable(gl.BLEND); if(params.notAdditiveBlending){ gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA ); //NormalBlending gl.enable(gl.DEPTH_TEST); gl.depthMask(true); //如果不开启depthWrite,深度会错乱。 }else{ gl.blendFunc(gl.SRC_ALPHA, gl.ONE); //AdditiveBlending 原本 gl.disable(gl.DEPTH_TEST); gl.depthMask(false); } } else { gl.disable(gl.BLEND); gl.depthMask(true); gl.enable(gl.DEPTH_TEST); } if(params.blendFunc !== undefined){ gl.enable(gl.BLEND); gl.blendFunc(...params.blendFunc); } if(params.depthTest !== undefined){ if(params.depthTest === true){ gl.enable(gl.DEPTH_TEST); }else{ gl.disable(gl.DEPTH_TEST); } } if(params.depthWrite !== undefined){ if(params.depthWrite === true){ gl.depthMask(true); }else{ gl.depthMask(false); } } */ { // UPDATE UNIFORMS shader.setUniformMatrix4("projectionMatrix", proj); shader.setUniformMatrix4("viewMatrix", view); shader.setUniformMatrix4("uViewInv", viewInv); shader.setUniformMatrix4("uProjInv", projInv); /* let screenWidth = target ? target.width : material.screenWidth; let screenHeight = target ? target.height : material.screenHeight; shader.setUniform1f("uScreenWidth", screenWidth); shader.setUniform1f("uScreenHeight", screenHeight); */ shader.setUniform2f('resolution', material.resolution.toArray()); shader.setUniform1f("fov", Math.PI * camera.fov / 180); shader.setUniform1f("near", camera.near); shader.setUniform1f("far", camera.far); if (camera instanceof OrthographicCamera) { shader.setUniform("uUseOrthographicCamera", true); shader.setUniform("uOrthoWidth", (camera.right - camera.left) / camera.zoom); //改 shader.setUniform("uOrthoHeight", camera.top - camera.bottom); } else { shader.setUniform("uUseOrthographicCamera", false); } /* if(material.clipBoxes.length + material.clipPolygons.length === 0){//改 shader.setUniform1i("clipTask", ClipTask.NONE); }else{ shader.setUniform1i("clipTask", material.clipTask); } shader.setUniform1i("clipMethod", material.clipMethod);*/ //改 if (material.clipBoxes_in && material.clipBoxes_in.length > 0) { //let flattenedMatrices = [].concat(...material.clipBoxes.map(c => c.inverse.elements)); var lClipBoxes = shader.uniformLocations["clipBoxes_in[0]"]; gl.uniformMatrix4fv(lClipBoxes, false, material.uniforms.clipBoxes_in.value); } if (material.clipBoxes_out && material.clipBoxes_out.length > 0) { //add var lClipBoxes2 = shader.uniformLocations["clipBoxes_out[0]"]; gl.uniformMatrix4fv(lClipBoxes2, false, material.uniforms.clipBoxes_out.value); } if (material.highlightBoxes && material.highlightBoxes.length > 0) { //add var boxes_highlight = shader.uniformLocations["boxes_highlight[0]"]; gl.uniformMatrix4fv(boxes_highlight, false, material.uniforms.boxes_highlight.value); } if (material.bigClipInBox) { //add shader.setUniformMatrix4("clipBoxBig_in", material.uniforms.clipBoxBig_in.value); } if (material.uniforms.baseHeightAreaMap.value) { //根据模型高亮土方 var baseHeightBoundZ = shader.uniformLocations["baseHeightBoundZ"]; gl.uniform2f(baseHeightBoundZ, ...material.uniforms.baseHeightBoundZ.value.toArray()); var baseHeightBoundXY = shader.uniformLocations["baseHeightBoundXY"]; gl.uniform4f(baseHeightBoundXY, ...material.uniforms.baseHeightBoundXY.value.toArray()); } if (material.prisms.length) { var prismList = shader.uniformLocations["prismList[0]"]; gl.uniformMatrix3fv(prismList, false, material.uniforms.prismList.value); var prismPoints = shader.uniformLocations["prismPoints[0]"]; gl.uniform2fv(prismPoints, material.uniforms.prismPoints.value); } // TODO CLIPSPHERES if (params.clipSpheres && params.clipSpheres.length > 0) { var clipSpheres = params.clipSpheres; var matrices = []; for (var clipSphere of clipSpheres) { //let mScale = new THREE.Matrix4().makeScale(...clipSphere.scale.toArray()); //let mTranslate = new THREE.Matrix4().makeTranslation(...clipSphere.position.toArray()); //let clipToWorld = new THREE.Matrix4().multiplyMatrices(mTranslate, mScale); var clipToWorld = clipSphere.matrixWorld; var viewToWorld = camera.matrixWorld; var worldToClip = clipToWorld.clone().invert(); var viewToClip = new Matrix4().multiplyMatrices(worldToClip, viewToWorld); matrices.push(viewToClip); } var flattenedMatrices = [].concat(...matrices.map(matrix => matrix.elements)); var lClipSpheres = shader.uniformLocations["uClipSpheres[0]"]; gl.uniformMatrix4fv(lClipSpheres, false, flattenedMatrices); //const lClipSpheres = shader.uniformLocations["uClipSpheres[0]"]; //gl.uniformMatrix4fv(lClipSpheres, false, material.uniforms.clipSpheres.value); } shader.setUniform1f("orthoMaxSize", material.uniforms.orthoMaxSize.value); shader.setUniform1f("maxSize", material.uniforms.maxSize.value); shader.setUniform1f("minSize", material.uniforms.minSize.value); // uniform float uPCIndex shader.setUniform1f("uOctreeSpacing", material.spacing); shader.setUniform("uOctreeSize", material.uniforms.octreeSize.value); shader.setUniform2f("elevationRange", material.elevationRange); shader.setUniform2f("intensityRange", material.intensityRange); shader.setUniform3f("uIntensity_gbc", [material.intensityGamma, material.intensityBrightness, material.intensityContrast]); shader.setUniform3f("uRGB_gbc", [material.rgbGamma, material.rgbBrightness, material.rgbContrast]); shader.setUniform1f("uTransition", material.transition); shader.setUniform1f("wRGB", material.weightRGB); shader.setUniform1f("wIntensity", material.weightIntensity); shader.setUniform1f("wElevation", material.weightElevation); shader.setUniform1f("wClassification", material.weightClassification); shader.setUniform1f("wReturnNumber", material.weightReturnNumber); shader.setUniform1f("wSourceID", material.weightSourceID); shader.setUniform("backfaceCulling", material.uniforms.backfaceCulling.value); //========================== //gl.TEXTURE_CUBE_MAP: 34067 //gl.TEXTURE0=33984 , vnWebGLTexture.target=gl.TEXTURE_2D = 3353 /* let vnWebGLTexture = this.textures.get(material.visibleNodesTexture);//最好搬到renderNodes if(vnWebGLTexture){ shader.setUniform1i("visibleNodes", currentTextureBindingPoint); //为何之前写的是"visibleNodesTexture",但和"visibleNodes"效果相同?可shader里只有"visibleNodes" gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(vnWebGLTexture.target, vnWebGLTexture.id); currentTextureBindingPoint++; } */ var gradientTexture = this.textures.get(material.gradientTexture); shader.setUniform1i("gradient", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(gradientTexture.target, gradientTexture.id); currentTextureBindingPoint++; var repeat = material.elevationGradientRepeat; if (repeat === ElevationGradientRepeat.REPEAT) { gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.REPEAT); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.REPEAT); } else if (repeat === ElevationGradientRepeat.MIRRORED_REPEAT) { gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.MIRRORED_REPEAT); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.MIRRORED_REPEAT); } else { gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gradientTexture.target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } currentTextureBindingPoint++; var classificationTexture = this.textures.get(material.classificationTexture); shader.setUniform1i("classificationLUT", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(classificationTexture.target, classificationTexture.id); currentTextureBindingPoint++; /* let matcapTexture = this.textures.get(material.matcapTexture); shader.setUniform1i("matcapTextureUniform", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(matcapTexture.target, matcapTexture.id); currentTextureBindingPoint++; */ var baseHeightAreaMap = material.uniforms.baseHeightAreaMap.value; if (baseHeightAreaMap) { //根据模型高亮土方 var map = this.textures.get(baseHeightAreaMap); shader.setUniform1i("baseHeightAreaMap", currentTextureBindingPoint); gl.activeTexture(gl.TEXTURE0 + currentTextureBindingPoint); gl.bindTexture(map.target, map.id); currentTextureBindingPoint++; } if (material.snapEnabled === true) { { var lSnapshot = shader.uniformLocations["uSnapshot[0]"]; var lSnapshotDepth = shader.uniformLocations["uSnapshotDepth[0]"]; var bindingStart = currentTextureBindingPoint; var lSnapshotBindingPoints = new Array(5).fill(bindingStart).map((a, i) => a + i); var lSnapshotDepthBindingPoints = new Array(5).fill(1 + Math.max(...lSnapshotBindingPoints)).map((a, i) => a + i); currentTextureBindingPoint = 1 + Math.max(...lSnapshotDepthBindingPoints); gl.uniform1iv(lSnapshot, lSnapshotBindingPoints); gl.uniform1iv(lSnapshotDepth, lSnapshotDepthBindingPoints); for (var i = 0; i < 5; i++) { var _texture = material.uniforms["uSnapshot"].value[i]; var textureDepth = material.uniforms["uSnapshotDepth"].value[i]; if (!_texture) { break; } var snapTexture = this.threeRenderer.properties.get(_texture).__webglTexture; var snapTextureDepth = this.threeRenderer.properties.get(textureDepth).__webglTexture; var bindingPoint = lSnapshotBindingPoints[i]; var depthBindingPoint = lSnapshotDepthBindingPoints[i]; gl.activeTexture(gl["TEXTURE".concat(bindingPoint)]); gl.bindTexture(gl.TEXTURE_2D, snapTexture); gl.activeTexture(gl["TEXTURE".concat(depthBindingPoint)]); gl.bindTexture(gl.TEXTURE_2D, snapTextureDepth); } } { var _flattenedMatrices3 = [].concat(...material.uniforms.uSnapView.value.map(c => c.elements)); var lSnapView = shader.uniformLocations["uSnapView[0]"]; gl.uniformMatrix4fv(lSnapView, false, _flattenedMatrices3); } { var _flattenedMatrices4 = [].concat(...material.uniforms.uSnapProj.value.map(c => c.elements)); var lSnapProj = shader.uniformLocations["uSnapProj[0]"]; gl.uniformMatrix4fv(lSnapProj, false, _flattenedMatrices4); } { var _flattenedMatrices5 = [].concat(...material.uniforms.uSnapProjInv.value.map(c => c.elements)); var lSnapProjInv = shader.uniformLocations["uSnapProjInv[0]"]; gl.uniformMatrix4fv(lSnapProjInv, false, _flattenedMatrices5); } { var _flattenedMatrices6 = [].concat(...material.uniforms.uSnapViewInv.value.map(c => c.elements)); var lSnapViewInv = shader.uniformLocations["uSnapViewInv[0]"]; gl.uniformMatrix4fv(lSnapViewInv, false, _flattenedMatrices6); } } //=============add=========== if (material.usePanoMap) { //为什么pointsize失效 shader.setUniform1f("progress", material.uniforms.progress.value); shader.setUniform1f("easeInOutRatio", material.uniforms.easeInOutRatio.value); shader.setUniform3f("pano0Position", material.uniforms.pano0Position.value.toArray()); shader.setUniform3f("pano1Position", material.uniforms.pano1Position.value.toArray()); shader.setUniform('pano0Matrix', material.uniforms.pano0Matrix.value); shader.setUniform('pano1Matrix', material.uniforms.pano1Matrix.value); var pano0Map = material.uniforms.pano0Map.value; if (pano0Map) { this.threeRenderer._textures.safeSetTextureCube(pano0Map, ++currentTextureBindingPoint); shader.setUniform1i('pano0Map', currentTextureBindingPoint); } var pano1Map = material.uniforms.pano1Map.value; if (pano1Map) { this.threeRenderer._textures.safeSetTextureCube(pano1Map, ++currentTextureBindingPoint); shader.setUniform1i('pano1Map', currentTextureBindingPoint); } //注: three.js我添加了个 _textures, safeSetTextureCube里主要就是activeTexture和bindTexture } } viewer.addTimeMark('renderOctree', 'end'); params.currentTextureBindingPoint = ++currentTextureBindingPoint; octrees.forEach(octree => { this.renderNodes(octree, nodes || octree.visibleNodes, visibilityTextureData, camera, target, shader, params); }); gl.activeTexture(gl.TEXTURE2); gl.bindTexture(gl.TEXTURE_2D, null); gl.activeTexture(gl.TEXTURE0); //gl.bindTexture(gl.TEXTURE_2D, null); //add //add 恢复为不透明(否则renderToCubeMap时的贴图会被渲染成高亮的颜色) gl.disable(gl.BLEND); gl.depthMask(true); gl.enable(gl.DEPTH_TEST); //DEPTH_TEST等需要恢复吗 } render(scene, camera) { var target = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; var params = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; var gl = this.gl; // PREPARE if (target != null) { this.threeRenderer.setRenderTarget(target); } //camera.updateProjectionMatrix(); // camera.matrixWorldInverse.invert(camera.matrixWorld); var traversalResult = this.traverse(scene); //排序 if (params.notAdditiveBlending) { //add traversalResult.octrees.forEach(tree => { if (tree.material.opacity == 1) { tree._z = Infinity; //不透明的先渲染 //2024.10发现没用,会透过不透明的看到后面透明的orz } else { var center = tree.boundCenter ? tree.boundCenter.clone() : tree.boundingBox.getCenter(tree.boundCenter).applyMatrix4(tree.matrixWorld); center.project(camera); tree._z = center.z; } }); traversalResult.octrees.sort((tree1, tree2) => { return tree2._z - tree1._z; //降序 (-1 朝外)。 离屏幕近的后渲染 }); } // RENDER var mat = params.material || traversalResult.octrees[0].material; var cloudSameMat = Potree.settings.cloudSameMat && !traversalResult.octrees.some(e => e.material.activeAttributeName != mat.activeAttributeName); //activeAttributeName都一样才行 if (cloudSameMat && viewer.scene.volumes.length == 0 && mat.pointSizeType != PointSizeType.ADAPTIVE && mat.activeAttributeName != "level of detail") { this.renderOctree(traversalResult.octrees, null, camera, target, params); //所有点云除了个别属性需要在shader中更新,其他都使用第一个点云的材质 } else for (var octree of traversalResult.octrees) { for (var _octree of traversalResult.octrees) { this.renderOctree(_octree, _octree.visibleNodes, camera, target, params); } } //if (octree material.pointSizeType === PointSizeType.ADAPTIVE || material.activeAttributeName === "level of detail") { // CLEANUP gl.activeTexture(gl.TEXTURE1); gl.bindTexture(gl.TEXTURE_2D, null); gl.bindBuffer(gl.ARRAY_BUFFER, null); gl.bindVertexArray(null); this.threeRenderer.resetState(); } } ; /* 中东的链接http://indoor.popsmart.cn:8094/zdoblh-yz/?vlon=5.14&vlat=-0.13&fov=100.0&pc=true&lon=121.61136592&lat=29.87855579&z=16.577 geometry: 有的attributes: 属性是: classification: color: indices: normal: position: 最好有个spacing */ /* const mapHeight = -1000;//要比点云低。最低 const cameraHeight = 1000; //最高 */ var panosHeight = config$1.map.mapHeight + 100; //要比点云低 (marker) var cursorHeight = 0; //比地图高就行 var routeLayerHeight = config$1.map.mapHeight + 105; var texLoader$5 = new TextureLoader(); var planeGeo$1 = new PlaneBufferGeometry(1, 1); var markerSize = 1; var initCameraFeildWidth = 50; var panoMarkerMats; class MapViewer extends ViewerBase { constructor(dom) { super(dom, { clearColor: Potree.config.mapBG, name: 'mapViewer', antialias: true }); this.visible = true; this.initScene(); this.needRender_ = false; this.mapLayer = new MapLayer(this, this.viewports[0]); this.scene.add(this.mapLayer.sceneGroup); this.mapLayer.sceneGroup.position.setZ(Potree.config.map.mapHeight); this.mapRatio = 0.5; this.splitDir = 'leftRight'; this.renderMeasure = false; //因context的preserveDrawingBuffer为false之后,canvas渲染多个viewport时会自动clear,所以若不渲染就会是空的。所以没有变化时就直接拷贝buffer好了。 this.copyPass = new ShaderPass(CopyShader); this.copyBuffer = new WebGLRenderTarget(100, 100, { minFilter: LinearFilter, magFilter: LinearFilter, format: RGBAFormat, stencilBuffer: false }); viewer.addEventListener("camera_changed", e => { var needUpdateCursor; if (e.viewport == viewer.mainViewport) { needUpdateCursor = true; } else if (e.viewport == this.viewports[0]) { //attachedToViewer needUpdateCursor = true; this.mapChanged = true; this.updateWhenAtViewer(); e.changeInfo.projectionChanged && this.setViewLimit(); } needUpdateCursor && this.updateCursor(); }); this.addEventListener("camera_changed", e => { e.changeInfo.projectionChanged && this.setViewLimit(); }); //viewer.addEventListener("global_mousemove", this.updateWhenAtViewer.bind(this)) //鼠标移动时reticule也动,所以直接就needRender /* viewer.reticule.addEventListener('update',(e)=>{ if(this.attachedToViewer)this.needRender = true }) */ viewer.scene.addEventListener("360_images_added", this.addPanos.bind(this)); viewer.addEventListener("loadPointCloudDone", this.initProjection.bind(this)); this.addEventListener('global_click', e => { if (!e.isTouch && e.button != MOUSE.LEFT) return; this.updateClosestPano(e.intersect); }); this.addEventListener('add', e => { //添加其他mesh this.scene.add(e.object); if (e.name == 'route') { e.object.position.z = routeLayerHeight; } Potree.Utils.setObjectLayers(e.object, 'mapObjects'); }); viewer.addEventListener('allLoaded', () => { this.setViewLimit('standard'); }); if (!Potree.settings.isOfficial) { var domRoot = viewer.renderer.domElement.parentElement; var elAttach = $(""); elAttach.css({ position: "absolute", right: '80%', bottom: '20px', zIndex: "10000", fontSize: '1em', color: "black", background: 'rgba(255,255,255,0.8)' }); var state = false; elAttach.on("click", () => { state = !state; this.attachToMainViewer(state, 'measure'); elAttach.val(state ? 'map分离' : 'map绑定'); }); domRoot.appendChild(elAttach[0]); } } get needRender() { return this.needRender_; } set needRender(n) { this.needRender_ = n; n && (this.viewports[0].needRender = true); //使attachedToViewer时在renderDefault中可渲染 } get mapChanged() { return this.mapChanged_; } set mapChanged(c) { //镜头移动、地图内容改变都会为true this.mapChanged_ = c; c && (this.needRender = true); } waitLoadDone(callback) { //确保加载完后执行 var timer; //等待一段时间看有没有新加载的tile,如果超过这个时间没有就不等了,算加载结束 var pauseCountDown = () => { //重新等待加载结束,中断倒计时 clearTimeout(timer); //console.log('pauseCountDown') }; var freshCountDown = () => { //刷新倒计时 //console.log('freshCountDown') clearTimeout(timer); timer = setTimeout(() => { this.mapLayer.removeEventListener('loadDone', freshCountDown); this.mapLayer.removeEventListener('startLoad', pauseCountDown); callback(); }, document.hidden ? 5000 : 500); }; this.mapLayer.addEventListener('loadDone', freshCountDown); this.mapLayer.addEventListener('startLoad', pauseCountDown); if (this.mapLayer.loadingInProgress == 0) { freshCountDown(); } } addListener(images360) { images360.addEventListener('flyToPano', e => { var toPano = e.toPano; if (toPano.dontMoveMap) return; /* transitions.start(lerp.vector(this.view.position, toPano.pano.position.clone().setZ(cameraHeight), (pos, progress)=>{ }), toPano.duration, null, 0, easing[toPano.easeName] ); */ var boundSize; // = new THREE.Vector2(10,10) this.moveTo(toPano.pano.position.clone().setZ(Potree.config.map.cameraHeight), boundSize, toPano.duration, null, toPano.easeName); }); } initProjection() { this.started = true; this.mapLayer.initProjection(); } initScene() { var w = initCameraFeildWidth; var width = this.renderArea.clientWidth; var height = this.renderArea.clientHeight; //let aspect = width / height this.camera = new OrthographicCamera(-width / 2, width / 2, height / 2, -height / 2 /* -w/2, w/2, w/2/aspect, -w/2/aspect */, 0.01, 10000); this.camera.zoom = width / w; //zoom越大视野越小 //this.camera.position.set(0,0,10); this.camera.up.set(0, 0, 1); //this.camera.lookAt(new THREE.Vector3()) //this.camera.updateMatrixWorld() this.view = new ExtendView(); this.view.position.set(0, 0, Potree.config.map.cameraHeight); this.view.lookAt(0, 0, 0); var viewport = new Viewport(this.view, this.camera, { left: 0, bottom: 0, width: 1, height: 1, name: 'mapViewport' }); viewport.axis = ["x", "y"]; viewport.axisSign = [1, 1]; viewport.noPointcloud = true; //不要渲染点云 viewport.render = this.render.bind(this); //标志给mainView渲染 //viewport.unableDepth = true //depthBasicMaterial等在此viewport中不开启depth viewport.addEventListener('resize', () => { this.copyBuffer.setSize(viewport.resolution2.x, viewport.resolution2.y); //this.cloudBuffer && this.cloudBuffer.setSize(viewport.resolution2.x, viewport.resolution2.y) }); this.viewports = [viewport]; this.controls = new FirstPersonControls(this, this.viewports[0]); this.controls.setEnable(true); this.scene = new Scene(); this.inputHandler = new InputHandler(this, this.scene); this.inputHandler.name = 'mapInputHandler'; //this.inputHandler.addInputListener(this.controls); this.inputHandler.registerInteractiveScene(this.scene); //interactiveScenes this.viewports[0].interactiveScenes = this.inputHandler.interactiveScenes; //供viewer的inputHandler使用 var cursor = new Mesh(planeGeo$1, new MeshBasicMaterial({ transparent: true, opacity: 0.9, depthTest: false, //防止透明冲突 map: texLoader$5.load(Potree.resourcePath + '/textures/pic_location128.png') })); cursor.position.set(0, 0, cursorHeight); this.cursor = cursor; this.scene.add(cursor); Potree.Utils.setObjectLayers(this.scene, 'mapObjects'); } setViewLimit(state) { //设置边界,当编辑空间模型等时要解禁 if (!state) state = this.limitBoundState; if (!state) return; this.limitBoundState = state; var setting = Potree.config.OrthoCameraLimit[state]; if (setting) { this.camera.zoomLimit = $.extend({}, setting.zoom); var lonlatCenter = viewer.transform.lonlatToLocal.inverse([0, 0]); var minY = viewer.transform.lonlatToLocal.forward([lonlatCenter[0], -90 + setting.latPad])[1]; //屏幕边界的bound var maxY = viewer.transform.lonlatToLocal.forward([lonlatCenter[0], 90 - setting.latPad])[1]; /*this.view.limitBound = new THREE.Box3( new THREE.Vector3(setting.xBound[0], minY, Potree.config.map.cameraHeight), new THREE.Vector3(setting.xBound[1], maxY, 1 / 0) ) */ var halfHeight = this.viewports[0].resolution.y / 2 / this.camera.zoom; //屏幕所占高度的一半 var halfWidth = this.viewports[0].resolution.x / 2 / this.camera.zoom; this.view.limitBound = new Box3(new Vector3(setting.xBound[0] + halfWidth, minY + halfHeight, Potree.config.map.cameraHeight), new Vector3(setting.xBound[1] - halfWidth, maxY - halfHeight, 1 / 0)); } else { this.view.limitBound = null; this.camera.zoomLimit = null; } } updateCursor() { //console.log('pos', viewer.mainViewport.camera.position.toArray() ) var scale = math.getScaleForConstantSize({ //规定下最小最大像素 minSize: 80, maxSize: 200, nearBound: initCameraFeildWidth * 0.1, farBound: initCameraFeildWidth * 2, camera: this.camera, position: this.cursor.getWorldPosition(new Vector3()), resolution: this.viewports[0].resolution //2 }); this.cursor.scale.set(scale, scale, scale); //当地图缩放时 this.cursor.position.copy(viewer.mainViewport.camera.position).setZ(cursorHeight); //当场景镜头旋转移动时 this.cursor.rotation.z = viewer.mainViewport.view.yaw; this.needRender = true; } addPanos(e) { var panosGroup = new Object3D(); panosGroup.name = 'markers'; panoMarkerMats = { default: new MeshBasicMaterial({ transparent: true, opacity: 0.5, map: texLoader$5.load(Potree.resourcePath + '/textures/map_marker.png') }), selected: new MeshBasicMaterial({ transparent: true, opacity: 1, map: texLoader$5.load(Potree.resourcePath + '/textures/map_marker.png') }) }; e.images.panos.forEach(pano => { pano.mapMarker = new Mesh(planeGeo$1, panoMarkerMats.default); pano.mapMarker.position.copy(pano.position).setZ(0); pano.mapMarker.scale.set(markerSize, markerSize, markerSize); pano.mapMarker.name = 'mapMarker'; panosGroup.add(pano.mapMarker); var mouseover = e => { if (!e.byMap) { pano.mapMarker.material = panoMarkerMats.selected; if (!e.byMainView) pano.dispatchEvent({ type: "hoverOn", byMap: true }); this.needRender = true; } }; var mouseleave = e => { if (!e.byMap) { pano.mapMarker.material = panoMarkerMats.default; if (!e.byMainView) pano.dispatchEvent({ type: "hoverOff", byMap: true }); this.needRender = true; } }; pano.mapMarker.addEventListener('mouseover', mouseover); pano.mapMarker.addEventListener('mouseleave', mouseleave); pano.addEventListener('hoverOn', mouseover); pano.addEventListener('hoverOff', mouseleave); var onclick = e => { viewer.images360.flyToPano(pano); }; pano.mapMarker.addEventListener('click', onclick); pano.addEventListener('isVisible', e => { //是否显示该点的mesh(不显示也能走) //console.log('panoMarker isVisible', pano.id, e.visible) Potree.Utils.updateVisible(pano.mapMarker, 'panoVisible', e.visible); this.needRender = true; }); pano.addEventListener('rePos', e => { pano.mapMarker.position.copy(pano.position).setZ(0); }); }); this.scene.add(panosGroup); panosGroup.position.z = panosHeight; this.panosGroup = panosGroup; Potree.Utils.setObjectLayers(panosGroup, 'mapObjects'); /* e.images.on('markersDisplayChange', (show)=>{ panosGroup.visible = show this.needRender = true }) */ //------- //this.fitPanosToViewport() this.initFitView(); } updateClosestPano(intersect) { if (viewer.images360.flying) return; intersect = intersect && intersect.orthoIntersect; if (!intersect) return; intersect = intersect.clone().setZ(0); var minDis = 20; //距离鼠标不能太远 var filterFuncs = [Images360.filters.isEnabled(), Images360.filters.isVisible(), //只走显示的点,否则会走到别的层 pano => { return pano.position.clone().setZ(0).distanceTo(intersect) < minDis; }]; var pano = Common.find(viewer.images360.panos, filterFuncs, [Images360.sortFunctions.floorDisSquaredToPoint(intersect)]); if (pano && pano != viewer.images360.currentPano) { viewer.images360.flyToPano(pano); } } fitPanosToViewport() { //使所有漫游点占满viewport //var w = viewer.bound.boundSize.x; var boundSize = viewer.images360.bound.size.clone().multiplyScalar(1.1); boundSize.max(new Vector3(4, 4, 4)); var endPosition = viewer.images360.bound.center.clone(); this.moveTo(endPosition, boundSize, 0); } fitToPointcloud(pointcloud) { var duration = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 400; var boundSize = pointcloud.bound.getSize(new Vector3()); /* .multiplyScalar(1.1); */ boundSize.max(new Vector3(4, 4, 4)); var endPosition = pointcloud.bound.getCenter(new Vector3()); this.moveTo(endPosition, boundSize, duration); //给点duration去变化 否则地图放大后所占的还是很小 } initFitView() { var dis = 5, px = 70; //地图上px像素长度代表的距离为dis //px是手动缩放到5m后发现是这个长度 var zoom = px / dis; this.camera.zoom = zoom; this.moveTo(viewer.images360.position /* viewer.images360.bound.center */); this.camera.updateProjectionMatrix(); } fitToDatasets(datasets) { var bound = new Box3(); datasets.forEach(e => bound.union(e.bound)); var center = bound.getCenter(new Vector3()); var size = bound.getSize(new Vector3()); this.moveTo(center, size, 200); //给duration是为了顺应视口大小改变,缓冲 } moveTo(endPosition, boundSize) { var duration = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; var margin = arguments.length > 3 ? arguments[3] : undefined; var easeName = arguments.length > 4 ? arguments[4] : undefined; var callback = arguments.length > 5 ? arguments[5] : undefined; //前两个参数有xy即可 var z = Math.max(Potree.config.map.cameraHeight, (endPosition.z || 0) + ((boundSize === null || boundSize === void 0 ? void 0 : boundSize.z) || 0) / 2 + 1); endPosition = new Vector3(endPosition.x, endPosition.y, z); this.view.moveOrthoCamera(this.viewports[0], { endPosition, boundSize, margin, callback }, duration, easeName); /* let endZoom, startZoom = this.camera.zoom //修改相机为bound中心,这样能看到全部(宽度范围内) this.view.setView({ position:endPosition, duration, callback:()=>{//done }, onUpdate:(progress)=>{ if(boundSize){ let aspect = boundSize.x / boundSize.y let w, h; if(this.camera.aspect > aspect){//视野更宽则用bound的纵向来决定 h = boundSize.y //w = h * this.camera.aspect endZoom = this.viewports[0].resolution.y / h }else{ w = boundSize.x; //h = w / this.camera.aspect endZoom = this.viewports[0].resolution.x / w } //onUpdate时更新endzoom是因为画布大小可能更改 this.camera.zoom = endZoom * progress + startZoom * (1 - progress) this.camera.updateProjectionMatrix() } }, Easing:easeName }) */ } updateWhenAtViewer(e) { //两个触发来源: 1 camera_changed时 2 mapLayer.needUpdate时。 render在viewer中执行 if (this.attachedToViewer) { if (this.started) this.mapLayer.update(); this.needRender = true; } } update(delta, areaSize) { if (!this.visible && !this.attachedToViewer) return; if (this.attachedToViewer) { if (this.mapLayer.needUpdate) { this.updateWhenAtViewer(); } return; } this.updateScreenSize(); this.controls.update(delta); this.view.applyToCamera(this.camera); var changed = this.cameraChanged(); if (this.started && (changed || this.mapLayer.needUpdate)) this.mapLayer.update(); if (changed /*|| || this.needRender */) { /* this.dispatchEvent({ type: "camera_changed", camera: this.camera, viewport : this.viewports[0] }) */ this.mapChanged = true; this.needRender = true; this.updateCursor(); //更改大小 } this.render(); } attachToMainViewer(state, desc) { var mapRatio = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0.5; var options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; //转移到viewer中。测量时展示or截图需要 if (!Potree.settings.isOfficial) this.renderArea.style.display = state ? 'none' : 'block'; if (state) { this.enabledOld = this.enabled; this.enabled = true; if (mapRatio != 'dontSet') { this.changeSplitScreenDir(options.dir, mapRatio); if (this.attachedToViewer) { //this.fitPanosToViewport() viewer.updateScreenSize({ forceUpdateSize: true }); return; } viewer.viewports = [viewer.mainViewport, viewer.mapViewer.viewports[0]]; //因为mainViewer的相机变化会触发map的变化,所以先渲染mainViewer } if (desc == 'measure') this.inputHandler.registerInteractiveScene(viewer.scene.overlayScene /* viewer.measuringTool.scene */); //虽然用的是viewer的inputHandler,但借用了this.inputHandler的interactiveScenes else if (desc == 'split4Screens') { this.inputHandler.registerInteractiveScene(viewer.scene.scene); } } else { if (!this.attachedToViewer) return; viewer.mainViewport.left = 0; viewer.mainViewport.bottom = 0; viewer.mainViewport.width = 1; viewer.mainViewport.height = 1; this.viewports[0].width = 1; this.viewports[0].bottom = 0; this.viewports[0].height = 1; this.viewports[0].left = 0; this.renderMeasure || this.inputHandler.unregisterInteractiveScene(viewer.scene.overlayScene /* viewer.measuringTool.scene */); this.inputHandler.unregisterInteractiveScene(viewer.scene.scene); viewer.viewports = [viewer.mainViewport]; this.updateScreenSize({ forceUpdateSize: true }); //更新相机projectionMatrix } //if(desc == 'measure')this.renderMeasure = state this.attachedToViewer = state; viewer.updateScreenSize({ forceUpdateSize: true }); //mapRatio != 'dontSet' && !options.dontFit && this.moveTo(...)//要写在updateScreenSize后面,因为要根据视图大小来fit if (options.moveToCurrentPos) { var boundSize = new Vector2(10, 10); var duration = 1000; this.moveTo(viewer.images360.position.clone(), boundSize, duration); } this.needRender = true; } setDrawMeasure(draw) { this.renderMeasure = !!draw; if (draw) { this.inputHandler.registerInteractiveScene(viewer.scene.overlayScene /* viewer.measuringTool.scene */); } else { this.inputHandler.unregisterInteractiveScene(viewer.scene.overlayScene /* viewer.measuringTool.scene */); } } changeSplitScreenDir(dir, mapRatio) { //左右 | 上下 //if(!dir || dir == this.dir)return if (dir) this.splitDir = dir; this.updateSplitSize(mapRatio); /* if(this.attachedToViewer){ //如果已经分屏了,中途修改方向的话…… this.updateSplitSize() } */ } updateSplitSize(mapRatio) { if (mapRatio != void 0) this.mapRatio = mapRatio; //console.log(this.mapRatio) if (this.splitDir == 'leftRight') { //地图在左方 viewer.mainViewport.left = this.mapRatio; viewer.mainViewport.width = 1 - this.mapRatio; this.viewports[0].width = this.mapRatio; viewer.mainViewport.bottom = this.viewports[0].bottom = 0; viewer.mainViewport.height = this.viewports[0].height = 1; } else if (this.splitDir == 'upDown') { //地图在下方 viewer.mainViewport.bottom = this.mapRatio; viewer.mainViewport.height = 1 - this.mapRatio; this.viewports[0].height = this.mapRatio; viewer.mainViewport.left = this.viewports[0].left = 0; viewer.mainViewport.width = this.viewports[0].width = 1; } if (this.attachedToViewer) { viewer.updateScreenSize({ forceUpdateSize: true }); } } render1() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //viewer的preserveDrawingBuffer为false时的版本 var needCopy, waitCopy; if (!this.visible && !this.attachedToViewer || !this.needRender && !params.force) { if (this.attachedToViewer) { needCopy = true; } else { return; } } waitCopy = this.attachedToViewer && this.needRender && !params.force; //是否写入到copyBuffer。双屏时,若needRender就拷贝到copyBuffer中,双屏时就直接使用copyBuffer。 四屏时因渲染点云会每帧都渲染,所以不需要缓存。 var renderer = params.renderer || this.renderer; if (waitCopy) { this.copyBuffer.setSize(params.viewport.resolution2.x, params.viewport.resolution2.y); renderer.setRenderTarget(this.copyBuffer); } else if (params.target) { renderer.setRenderTarget(params.target); } /* if(params.resize){ this.emitResizeMsg(new THREE.Vector2(params.width,params.height, viewport:params.viewport)) } */ params.clear ? params.clear() : renderer.clear(); if (!needCopy || waitCopy) { //重绘 viewer.dispatchEvent({ type: "render.begin", viewer: this, viewport: this.viewports[0], params }); Potree.Utils.setCameraLayers(this.camera, ['map', 'mapObjects', 'bothMapAndScene']); if (this.mapGradientBG) { //渲染背景 viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg); renderer.render(viewer.scene.scene, viewer.scene.cameraBG); } renderer.render(this.scene, this.camera); renderer.render(viewer.scene.scene, this.camera); //测量线等 //params.renderOverlay && params.renderOverlay( $.extend({}, params, { isMap:true })) renderer.setRenderTarget(params.target || null); } if (needCopy || waitCopy) { //使用缓存 ----当viewer的preserveDrawingBuffer为false的话,使用buffer this.copyPass.render(null, null, null, renderer, params.target || null, this.copyBuffer); } this.needRender = false; return true; } clear(params) { if (this.transparentBG) { this.renderer.setClearColor(0x000000, 0); } else { this.renderer.setClearColor(Potree.config.mapBG, 1); } (params.renderer || this.renderer).clear(); } //拆成两次渲染,一个地图一个其他物体,且地图渲染后保存在buffer中,只有当地图变化后才重渲染。 render() { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!this.visible && !this.attachedToViewer || !this.needRender && !params.force) { //注意:mapViewer.needRender的权重高于它的viewport的needRender,也就是说,当attachedToViewer时,viewer即使needRender, mapViewer也不一定会渲染。 return; } viewer.addTimeMark('mapRender', 'start'); var renderer = params.renderer || this.renderer; if (this.mapChanged) { //渲染地图背景 renderer.setRenderTarget(this.copyBuffer); params.clear ? params.clear(params) : this.clear(params); if (this.mapGradientBG) { //渲染背景 viewer.scene.cameraBG.layers.set(Potree.config.renderLayers.bg); renderer.render(viewer.scene.scene, viewer.scene.cameraBG); } Potree.Utils.setCameraLayers(this.camera, ['map']); renderer.render(this.scene, this.camera); params.renderBG && params.renderBG(this.viewports[0]); this.mapChanged = false; renderer.setRenderTarget(params.target || null); } params.clear ? params.clear(params) : this.clear(params); this.copyPass.render(null, null, null, renderer, params.target || null, this.copyBuffer); //拷贝地图背景 renderer.clearDepth(); //防止地图遮挡其他物体 //绘制其他物体 var layers = ['mapObjects', 'bothMapAndScene', 'light']; Potree.settings.showObjectsOnMap && layers.push('model'); Potree.Utils.setCameraLayers(this.camera, layers); viewer.dispatchEvent({ type: "render.begin", viewer: this, viewport: this.viewports[0], params }); this.attachedToViewer || this.renderCloud || renderer.render(viewer.scene.scene, this.camera); //类同renderOverlay renderer.render(this.scene, this.camera); if (!this.attachedToViewer && this.renderMeasure) { //在未attach到主页面时也要渲染测量线 viewer.dispatchEvent({ type: "render.pass.perspective_overlay", camera: this.camera, viewport: this.viewports[0], renderer }); } renderer.setRenderTarget(null); this.needRender = false; viewer.addTimeMark('mapRender', 'end'); return true; } renderOverlay() { //偶尔需要绘制测量线 viewer.renderOverlay({ camera: this.camera, viewport: this.viewports[0], renderer: this.renderer }); } setRenderCloud() { var _this = this; var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; //使利用viewer的渲染来渲染点云 (如果点云不旋转其实也可以贴floorplan……) this.renderPointcloud = true; this.viewports[0].noPointcloud = false; this.viewports[0].background = 'none'; //防止renderBG绘制skybox var oldMapRender = this.render, oldMapClear = this.clear; var pRenderer = new Renderer$1(this.renderer); //必须重新创建一个点云渲染器,否则和旧的webgl冲突 this.render = function () { var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; if (!_this.visible && !_this.attachedToViewer || !_this.needRender && !params.force) { //注意:mapViewer.needRender的权重高于它的viewport的needRender,也就是说,当attachedToViewer时,viewer即使needRender, mapViewer也不一定会渲染。 return; } if (!options.renderMeasure) { viewer.scene.measurements.forEach(e => Potree.Utils.updateVisible(e, 'renderCloudAtMap', false)); } viewer.renderDefault({ viewports: _this.viewports, camera: _this.camera, renderer: _this.renderer, render: oldMapRender, clear: oldMapClear, pRenderer }); viewer.scene.measurements.forEach(e => Potree.Utils.updateVisible(e, 'renderCloudAtMap', true)); }; } //本来想用mainViewer渲染点云在target上再贴过来的,但是失败了,可能因为不同的renderer不能互通。 /* setRenderCloud(){ this.renderCloud = true this.cloudBuffer = new THREE.WebGLRenderTarget( 300, 200 , { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, //stencilBuffer: false, }); } */ } /* 渲染顺序: 地图 背景Overlay 地图scene的物体,如cursor、 marker 点云(如果有) overlay,两层:第一层:viewer的scene中bothMapAndScene的如reticule. 第二层:如测量线(attachToMainViewer时才渲染) */ //本地调试地图白屏是因为代码自动更新了 但没刷新 class U { static toVector3(v, offset) { return new Vector3().fromArray(v, offset || 0); } static toBox3(b) { return new Box3(U.toVector3(b), U.toVector3(b, 3)); } static findDim(schema, name) { var dim = schema.find(dim => dim.name == name); if (!dim) throw new Error('Failed to find ' + name + ' in schema'); return dim; } static sphereFrom(b) { return b.getBoundingSphere(new Sphere()); } } ; class PointCloudEptGeometry { constructor(url, info) { var version = info.version; var schema = info.schema; var bounds = info.bounds; var boundsConforming = info.boundsConforming; var xyz = [U.findDim(schema, 'X'), U.findDim(schema, 'Y'), U.findDim(schema, 'Z')]; var scale = xyz.map(d => d.scale || 1); var offset = xyz.map(d => d.offset || 0); this.eptScale = U.toVector3(scale); this.eptOffset = U.toVector3(offset); this.url = url; this.info = info; this.type = 'ept'; this.schema = schema; this.span = info.span || info.ticks; this.boundingBox = U.toBox3(bounds); this.tightBoundingBox = U.toBox3(boundsConforming); this.offset = U.toVector3([0, 0, 0]); this.boundingSphere = U.sphereFrom(this.boundingBox); this.tightBoundingSphere = U.sphereFrom(this.tightBoundingBox); this.version = new Potree.Version('1.7'); this.projection = null; this.fallbackProjection = null; if (info.srs && info.srs.horizontal) { this.projection = info.srs.authority + ':' + info.srs.horizontal; } if (info.srs.wkt) { if (!this.projection) this.projection = info.srs.wkt;else this.fallbackProjection = info.srs.wkt; } { // TODO [mschuetz]: named projections that proj4 can't handle seem to cause problems. // remove them for now try { proj4(this.projection); } catch (e) { this.projection = null; } } { var attributes = new PointAttributes(); attributes.add(PointAttribute.POSITION_CARTESIAN); attributes.add(new PointAttribute("rgba", PointAttributeTypes.DATA_TYPE_UINT8, 4)); attributes.add(new PointAttribute("intensity", PointAttributeTypes.DATA_TYPE_UINT16, 1)); attributes.add(new PointAttribute("classification", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("gps-time", PointAttributeTypes.DATA_TYPE_DOUBLE, 1)); attributes.add(new PointAttribute("returnNumber", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("number of returns", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("return number", PointAttributeTypes.DATA_TYPE_UINT8, 1)); attributes.add(new PointAttribute("source id", PointAttributeTypes.DATA_TYPE_UINT16, 1)); this.pointAttributes = attributes; } this.spacing = (this.boundingBox.max.x - this.boundingBox.min.x) / this.span; var hierarchyType = info.hierarchyType || 'json'; var dataType = info.dataType; if (dataType == 'laszip') { this.loader = new Potree.EptLaszipLoader(); } else if (dataType == 'binary') { this.loader = new Potree.EptBinaryLoader(); } else if (dataType == 'zstandard') { this.loader = new Potree.EptZstandardLoader(); } else { throw new Error('Could not read data type: ' + dataType); } } } ; class EptKey { constructor(ept, b, d, x, y, z) { this.ept = ept; this.b = b; this.d = d; this.x = x || 0; this.y = y || 0; this.z = z || 0; } name() { return this.d + '-' + this.x + '-' + this.y + '-' + this.z; } step(a, b, c) { var min = this.b.min.clone(); var max = this.b.max.clone(); var dst = new Vector3().subVectors(max, min); if (a) min.x += dst.x / 2;else max.x -= dst.x / 2; if (b) min.y += dst.y / 2;else max.y -= dst.y / 2; if (c) min.z += dst.z / 2;else max.z -= dst.z / 2; return new Potree.EptKey(this.ept, new Box3(min, max), this.d + 1, this.x * 2 + a, this.y * 2 + b, this.z * 2 + c); } children() { var result = []; for (var a = 0; a < 2; ++a) { for (var b = 0; b < 2; ++b) { for (var c = 0; c < 2; ++c) { var add = this.step(a, b, c).name(); if (!result.includes(add)) result = result.concat(add); } } } return result; } } class PointCloudEptGeometryNode extends PointCloudTreeNode { constructor(ept, b, d, x, y, z) { super(); this.ept = ept; this.key = new Potree.EptKey(this.ept, b || this.ept.boundingBox, d || 0, x, y, z); this.id = PointCloudEptGeometryNode.IDCount++; this.geometry = null; this.boundingBox = this.key.b; this.tightBoundingBox = this.boundingBox; this.spacing = this.ept.spacing / Math.pow(2, this.key.d); this.boundingSphere = U.sphereFrom(this.boundingBox); // These are set during hierarchy loading. this.hasChildren = false; this.children = {}; this.numPoints = -1; this.level = this.key.d; this.loaded = false; this.loading = false; this.oneTimeDisposeHandlers = []; var k = this.key; this.name = this.toPotreeName(k.d, k.x, k.y, k.z); this.index = parseInt(this.name.charAt(this.name.length - 1)); } isGeometryNode() { return true; } getLevel() { return this.level; } isTreeNode() { return false; } isLoaded() { return this.loaded; } getBoundingSphere() { return this.boundingSphere; } getBoundingBox() { return this.boundingBox; } url() { return this.ept.url + 'ept-data/' + this.filename(); } getNumPoints() { return this.numPoints; } filename() { return this.key.name(); } getChildren() { var children = []; for (var i = 0; i < 8; i++) { if (this.children[i]) { children.push(this.children[i]); } } return children; } addChild(child) { this.children[child.index] = child; child.parent = this; } load() { if (this.loaded || this.loading) return; if (Potree.numNodesLoading >= Potree.maxNodesLoading) return; this.loading = true; ++Potree.numNodesLoading; if (this.numPoints == -1) this.loadHierarchy(); this.loadPoints(); } loadPoints() { this.ept.loader.load(this); } async loadHierarchy() { var nodes = {}; nodes[this.filename()] = this; this.hasChildren = false; var eptHierarchyFile = "".concat(this.ept.url, "ept-hierarchy/").concat(this.filename(), ".json"); var response = await fetch(eptHierarchyFile); var hier = await response.json(); // Since we want to traverse top-down, and 10 comes // lexicographically before 9 (for example), do a deep sort. var keys = Object.keys(hier).sort((a, b) => { var [da, xa, ya, za] = a.split('-').map(n => parseInt(n, 10)); var [db, xb, yb, zb] = b.split('-').map(n => parseInt(n, 10)); if (da < db) return -1; if (da > db) return 1; if (xa < xb) return -1; if (xa > xb) return 1; if (ya < yb) return -1; if (ya > yb) return 1; if (za < zb) return -1; if (za > zb) return 1; return 0; }); keys.forEach(v => { var [d, x, y, z] = v.split('-').map(n => parseInt(n, 10)); var a = x & 1, b = y & 1, c = z & 1; var parentName = d - 1 + '-' + (x >> 1) + '-' + (y >> 1) + '-' + (z >> 1); var parentNode = nodes[parentName]; if (!parentNode) return; parentNode.hasChildren = true; var key = parentNode.key.step(a, b, c); var node = new Potree.PointCloudEptGeometryNode(this.ept, key.b, key.d, key.x, key.y, key.z); node.level = d; node.numPoints = hier[v]; parentNode.addChild(node); nodes[key.name()] = node; }); } doneLoading(bufferGeometry, tightBoundingBox, np, mean) { bufferGeometry.boundingBox = this.boundingBox; this.geometry = bufferGeometry; this.tightBoundingBox = tightBoundingBox; this.numPoints = np; this.mean = mean; this.loaded = true; this.loading = false; --Potree.numNodesLoading; } toPotreeName(d, x, y, z) { var name = 'r'; for (var i = 0; i < d; ++i) { var shift = d - i - 1; var mask = 1 << shift; var step = 0; if (x & mask) step += 4; if (y & mask) step += 2; if (z & mask) step += 1; name += step; } return name; } dispose() { if (this.geometry && this.parent != null) { this.geometry.dispose(); this.geometry = null; this.loaded = false; // this.dispatchEvent( { type: 'dispose' } ); for (var i = 0; i < this.oneTimeDisposeHandlers.length; i++) { var handler = this.oneTimeDisposeHandlers[i]; handler(); } this.oneTimeDisposeHandlers = []; } } } PointCloudEptGeometryNode.IDCount = 0; class ExtendPointCloudOctree extends PointCloudOctree { constructor(geometry, material) { material = material || new ExtendPointCloudMaterial(); super(geometry, material); //xzw move from material 。 adaptive_point_size才使用 /* this.visibleNodesTexture = Utils.generateDataTexture(2048, 1, new THREE.Color(0xffffff)); this.visibleNodesTexture.minFilter = THREE.NearestFilter; this.visibleNodesTexture.magFilter = THREE.NearestFilter; */ this.boundingBox = this.pcoGeometry.tightBoundingBox; //this.pcoGeometry.boundingBox; //boundingBox是正方体,所以换掉 this.boundingSphere = this.boundingBox.getBoundingSphere(new Sphere()); this.nodeMaxLevel = 0; //add if (!this.pcoGeometry.loader.version.equalOrHigher('1.5')) { //las 一级一级增长的,但是testNodeMaxLevel时需要大于0 this.nodeMaxLevel = 1; //add } this.maxLevel_ = Infinity; this.temp = { sizeFitToLevel: {}, opacity: {} }; //add //add this.panos = []; this.matrixAutoUpdate = false; //最好禁止updateMatrix 直接使用matrixWorld this.orientationUser = 0; this.translateUser = new Vector3(); this.rotateMatrix = new Matrix4(); this.transformMatrix = new Matrix4(); // 数据集的变化矩阵 this.transformInvMatrix = new Matrix4(); /* this.transformMatrix2 = new THREE.Matrix4;// 数据集的变化矩阵 this.transformInvMatrix2 = new THREE.Matrix4; */ this.rotateInvMatrix = new Matrix4(); this.material.spacing = this.pcoGeometry.spacing; //初始化一下 以便于设置pointsize this.nodeMaxLevelPredict = this.predictNodeMaxLevel(); //预测maxNodeLevel this.testMaxNodeCount = this.testMaxNodeCount2 = 0; this._visible = true; this.pcoGeometry.addEventListener('updateNodeMaxLevel', this.updateNodeMaxLevel.bind(this)); this.isPointcloud = true; //add } /* 注释:node的level从最大的box 0开始。 且加载任意一个node必定也会加载它的所有祖先。(如visibleNodes中有一个level为4,则一定有3,2,1,0) visibleNodes就是所有可见的node,比如: 如果相机在0这个位置朝下,这时候的visibleNodes中只有一个level为0的node; 而如果朝上看,上方的几个node如果在视野中占据足够大的位置的话,就会加载。 如果相机在2这个位置朝上,这时候的visibleNodes中所包含的level为: 0,1,2 ________________ | | | | |__2| | | | 1 | 1 | |_______|_______| | | | | | 0 | |_______________| 查看box可在potree中开启 */ get maxLevel() { return this.maxLevel_; } set maxLevel(level) { if (level != this.maxLevel_) { this.maxLevel_ = level; this.dispatchEvent('maxLevelChanged'); } } updateNodeMaxLevel(e) { //目前点云包含node的最高level var level = Math.max(e.level, this.nodeMaxLevel); if (level != this.nodeMaxLevel) { this.nodeMaxLevel = level; //viewer.dispatchEvent({type:'updateNodeMaxLevel', pointcloud: this, nodeMaxLevel:level}) Potree.settings.isOfficial || console.log('updateNodeMaxLevel ' + this.dataset_id + " : " + this.nodeMaxLevel); setTimeout(() => { this.setPointLevel(); //重新计算 延迟是因为testNodeMax会变回旧的 }, 1); if (!Potree.settings.sizeFitToLevel) { this.changePointSize(); } } } //注:在没有加载到真实的 nodeMaxLevel之前,点云会显示得偏大 //panoEdit时比预测值小很多? testMaxNodeLevel(camera) { //手动使maxLevel达到最高,从而迫使updateNodeMaxLevel。 因为Potree.settings.pointDensity 不为 'high'时,maxLevel不是所加载的最高,就很容易加载不出下一个层级,导致无法知道nodeMaxLevel if (this.testMaxNodeLevelDone) return; //if(this.nodeMaxLevel > this.nodeMaxLevelPredict.min )return if (this.nodeMaxLevel == 0) return true; if (camera.type == "OrthographicCamera" || this.testMaxNodeCount < 50) { if (!Potree.Utils.isInsideFrustum(this.bound, camera)) { return true; } } else if (!viewer.atDatasets.includes(this)) return true; //否则老远就count++ var levels = this.visibleNodes.map(e => e.getLevel()); var actMaxLevel = Math.max.apply(null, levels); //实际加载到的最高的node level if (actMaxLevel < this.maxLevel) return true; // 还没加载到能加载到的最高。 但在细节设置较低时,排除作用微弱。 //尝试加载出更高级的level var old = this.maxLevel; this.maxLevel = 12; //var visibleNodes1 = this.visibleNodes.map(e=>e.getLevel()) //console.log('visibleNodes1',visibleNodes1) Potree.updatePointClouds([this], viewer.scene.getActiveCamera(), viewer.mainViewport.resolution); //不在camera可视范围内还是加载不出来。即使临时修改位置 var visibleNodes2 = this.visibleNodes.map(e => e.getLevel()); //console.log('visibleNodes2',visibleNodes2) this.maxLevel = old; Potree.updatePointClouds([this], viewer.scene.getActiveCamera(), viewer.mainViewport.resolution); this.testMaxNodeCount++; viewer.testMaxNodeCount++; //console.log('testMaxNodeCount', this.dataset_id, this.testMaxNodeCount, 'nodeMaxLevel', this.nodeMaxLevel ) if (this.testMaxNodeCount == Potree.config.testNodeCount1) { //差不多等当前所在数据集nodeMaxLevel加载出来 this.changePointSize(); //重新更新一下大小。因之前用的是nodeMaxLevelPredict (防止刚开始因nodeMaxLevel没涨完,导致过大的点云突然出现 } //每个到hierarchyStepSize的结点的后代的最高level都不一样, 所以还是要等待,理论上只有全部加载完才能确定 if (this.testMaxNodeCount > 100) { console.log('testMaxNodeLevel次数超出,强制结束:', this.dataset_id, this.nodeMaxLevel, this.nodeMaxLevelPredict.min); this.testMaxNodeLevelDone = 'moreThanMaxCount'; return; //在可以看见点云的情况下,超时,有可能是预测的max是错的 } if (this.nodeMaxLevel < this.nodeMaxLevelPredict.min) return true; //仍需要继续testMaxNodeLevel this.testMaxNodeCount2++; // 已经> this.nodeMaxLevelPredict.min 后,开始计数。因为min可能低于真实nodeMaxLevel所以要再试几次 if (this.testMaxNodeCount2 < 50) return true; //再试几次 ( 主要是细节调得低时需要多测几次才加载到 this.testMaxNodeLevelDone = true; } updateMatrixWorld(force) { //add super.updateMatrixWorld(force); this.matrixWorldInverse = this.matrixWorld.clone().invert(); } setPointLevel() { var pointDensity = Potree.settings.pointDensity; var config = Potree.config.pointDensity[pointDensity]; if (!config) return; /* if(this.testingMaxLevel){ this.maxLevel = 12;//先加载到最大的直到测试完毕。由于5个level为一组来加载,所以如果写4最高能加载到5,如果写5最高能加载到下一个级别的最高也就是10 //console.log('maxLevel: '+e.maxLevel + ' testingMaxLevel中 ' ) }else{ */ var percent = config.percentByUser && Potree.settings.UserDensityPercent != void 0 ? Potree.settings.UserDensityPercent : config.maxLevelPercent; this.maxLevel = Math.round(percent * this.nodeMaxLevel); //console.log('maxLevel: '+e.maxLevel + ', density : '+Potree.settings.pointDensity, ", percent :"+percent); if (Potree.settings.sizeFitToLevel) { this.changePointSize(); } this.changePointOpacity(); //} viewer.dealBeforeRender || viewer.dispatchEvent('content_changed'); } //预测可能的nodeMaxLevel: predictNodeMaxLevel() { //预测maxNodeLevel。 可能只适用于我们相机拍的点云 var spacing = { min: 0.005, max: 0.014 }; //最小节的两点间的距离 ,获得方法:spacing / Math.pow(2, nodeMaxLevel)。 目前观测的我们自己拍的这个数值的范围大概是这样 var min = Math.log2(this.material.spacing / spacing.max); //有见过最大是0.01368 var max = Math.log2(this.material.spacing / spacing.min); //大部分是 0.006 //console.log('predictNodeMaxLevel:', this.name , min, max ) //只对深时相机,其他相机不准 return { min, max }; } getHighestNodeSpacing() { return this.material.spacing / Math.pow(2, this.nodeMaxLevel); //前提是这个nodeMaxLevel是准确的 } updateMaterial(material, visibleNodes, camera, renderer, resolution) { //改 material.fov = camera.fov * (Math.PI / 180); /* material.screenWidth = renderer.domElement.clientWidth; material.screenHeight = renderer.domElement.clientHeight; */ material.resolution = resolution; material.spacing = this.pcoGeometry.spacing; // * Math.max(this.scale.x, this.scale.y, this.scale.z); material.near = camera.near; material.far = camera.far; material.uniforms.octreeSize.value = this.pcoGeometry.boundingBox.getSize(new Vector3()).x; } pick(viewer, viewport, camera, ray) { var params = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; //改 var renderer = viewer.renderer; var pRenderer = viewer.pRenderer; viewer.addTimeMark('pick', 'start'); var getVal = (a, b) => a != void 0 ? a : b; var pickWindowSize = params.pickWindowSize; //拾取像素边长,越小越精准,但点云稀疏的话可能容易出现识别不到的情况。 另外左下侧会有缝隙无法识别到,缝隙大小和这个值有关//突然发现pickWindowSize在一百以内的变化对pick费时影响甚微,1和100差1毫秒不到,但400时会多4毫秒 if (pickWindowSize == void 0) { if (Potree.settings.displayMode == 'showPanos') { pickWindowSize = 50; } else { var r0 = this.nodeMaxLevel > 0 ? this.maxLevel / this.nodeMaxLevel : 0.5; pickWindowSize = MathUtils.clamp(Math.round((1.1 - r0) * 80), 15, 100); } } if (camera.type == 'OrthographicCamera') { var cameraDir = new Vector3(0, 0, -1).applyQuaternion(camera.quaternion); pickWindowSize *= 4; //pointsize比较大时截取太小会没多少点可以选 } var pickOutsideClipRegion = getVal(params.pickOutsideClipRegion, false); var size = params.resolution || (viewport ? viewport.resolution : renderer.getSize(new Vector2())); var width = Math.ceil(getVal(params.width, size.width)); //renderTarget大小。影响识别精度 var height = Math.ceil(getVal(params.height, size.height)); var screenshot = () => { if (window.testScreen) { pickMaterial.activeAttributeName = "rgba"; pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget); var dataUrl = Potree.Utils.renderTargetToDataUrl(pickState.renderTarget, width, height, renderer); Potree.Common.downloadFile(dataUrl, 'screenshot.png'); window.testScreen = 0; pickMaterial.activeAttributeName = "indices"; //indices renderer.clear(true, true, true); } }; var pointSizeType = getVal(params.pointSizeType, this.material.pointSizeType); var pointSize = getVal(params.pointSize, this.material.size); var nodes = this.nodesOnRay(this.visibleNodes, ray); if (window.testScreen) { console.log('nodes.length', this.visibleNodes.length, nodes); } if (nodes.length === 0) { //console.log('nodes.length === 0') return null; } //console.log('nodes.length != 0', this.name) if (!this.pickState) { var scene = new Scene(); var material = new ExtendPointCloudMaterial(); material.activeAttributeName = "indices"; //indices var renderTarget = new WebGLRenderTarget(1, 1, { minFilter: LinearFilter, magFilter: NearestFilter, format: RGBAFormat }); this.pickState = { renderTarget: renderTarget, material: material, scene: scene }; } ; var pickState = this.pickState; var pickMaterial = pickState.material; { // update pick material pickMaterial.pointSizeType = pointSizeType; //pickMaterial.shape = this.material.shape; pickMaterial.shape = Potree.PointShape.PARABOLOID; pickMaterial.uniforms.uFilterReturnNumberRange.value = this.material.uniforms.uFilterReturnNumberRange.value; pickMaterial.uniforms.uFilterNumberOfReturnsRange.value = this.material.uniforms.uFilterNumberOfReturnsRange.value; pickMaterial.uniforms.uFilterGPSTimeClipRange.value = this.material.uniforms.uFilterGPSTimeClipRange.value; pickMaterial.uniforms.uFilterPointSourceIDClipRange.value = this.material.uniforms.uFilterPointSourceIDClipRange.value; pickMaterial.size = pointSize; pickMaterial.uniforms.minSize.value = this.material.uniforms.minSize.value; pickMaterial.uniforms.maxSize.value = this.material.uniforms.maxSize.value; pickMaterial.classification = this.material.classification; pickMaterial.recomputeClassification(); //pickClipped判断转移到上一层函数 var { bigClipInBox, clipBoxes_in, clipBoxes_out } = this.material; pickMaterial.setClipBoxes(bigClipInBox, clipBoxes_in, clipBoxes_out, []); this.updateMaterial(pickMaterial, nodes, camera, renderer, new Vector2(width, height)); } pickState.renderTarget.setSize(width, height); //仅绘制屏幕大小的,而不乘以devicePixelRatio var pixelPos = new Vector2(params.x, params.y); var gl = renderer.getContext(); //规定渲染范围,只渲染一小块 /* renderer.setScissorTest(true); gl.enable(gl.SCISSOR_TEST); gl.scissor( parseInt(pixelPos.x - (pickWindowSize - 1) / 2), parseInt(pixelPos.y - (pickWindowSize - 1) / 2), parseInt(pickWindowSize), parseInt(pickWindowSize)); */ //---这段没用 pickState.renderTarget.scissor.set(parseInt(pixelPos.x - (pickWindowSize - 1) / 2), parseInt(pixelPos.y - (pickWindowSize - 1) / 2), parseInt(pickWindowSize), parseInt(pickWindowSize)); pickState.renderTarget.scissorTest = true; renderer.state.buffers.depth.setTest(pickMaterial.depthTest); renderer.state.buffers.depth.setMask(pickMaterial.depthWrite); renderer.state.setBlending(NoBlending); { // RENDER renderer.setRenderTarget(pickState.renderTarget); //gl.clearColor(0, 0, 0, 0); renderer.setClearColor(0x000000, 0); var tmp = this.material; this.material = pickMaterial; screenshot(); pRenderer.renderOctree(this, nodes, camera, pickState.renderTarget); //只绘制ray pick到的部分nodes this.material = tmp; } var clamp = (number, min, max) => Math.min(Math.max(min, number), max); var x = parseInt(clamp(pixelPos.x - (pickWindowSize - 1) / 2, 0, width)); var y = parseInt(clamp(pixelPos.y - (pickWindowSize - 1) / 2, 0, height)); /* let w = parseInt(Math.min(x + pickWindowSize, width) - x); let h = parseInt(Math.min(y + pickWindowSize, height) - y); */ var pixelCount = pickWindowSize * pickWindowSize; //w * h; var buffer = new Uint8Array(4 * pixelCount); //w 0) { //最后选取的是最靠近鼠标的那个pick if (distance < hits[0].distanceToCenter) { hits[0] = hit; } } else { hits.push(hit); } if (distance < rSquare) hits2.push(hit); //add } } } } if (!params.all && Potree.settings.pickFrontPointRatio) { if (hits2.length) { //add hits = hits2; } } // { // DEBUG: show panel with pick image // let img = Utils.pixelsArrayToImage(buffer, w, h); // let screenshot = img.src; // if(!this.debugDIV){ // this.debugDIV = $(` //
`); // $(document.body).append(this.debugDIV); // } // this.debugDIV.empty(); // this.debugDIV.append($(``)); // //$(this.debugWindow.document).append($(``)); // //this.debugWindow.document.write(''); // } for (var _hit of hits) { var point = {}; if (!nodes[_hit.pcIndex]) { return null; } var node = nodes[_hit.pcIndex]; var pc = node.sceneNode; var geometry = node.geometryNode.geometry; var _loop = function _loop() { var attribute = geometry.attributes[attributeName]; if (attributeName === 'position') { var _x = attribute.array[3 * _hit.pIndex + 0]; var _y = attribute.array[3 * _hit.pIndex + 1]; var z = attribute.array[3 * _hit.pIndex + 2]; var position = new Vector3(_x, _y, z); position.applyMatrix4(pc.matrixWorld); point[attributeName] = position; //add if (!params.all && Potree.settings.pickFrontPointRatio) { if (camera.type == 'OrthographicCamera') { var vec = new Vector3().subVectors(position, camera.position); _hit.disSquare = vec.projectOnVector(cameraDir).length(); //只考虑到相机的垂直距离 } else { _hit.disSquare = camera.position.distanceTo(position); } } } else if (attributeName === 'indices') {} else { var values = attribute.array.slice(attribute.itemSize * _hit.pIndex, attribute.itemSize * (_hit.pIndex + 1)); if (attribute.potree) { var { scale, offset: _offset } = attribute.potree; values = values.map(v => v / scale + _offset); } point[attributeName] = values; //debugger; //if (values.itemSize === 1) { // point[attribute.name] = values.array[hit.pIndex]; //} else { // let value = []; // for (let j = 0; j < values.itemSize; j++) { // value.push(values.array[values.itemSize * hit.pIndex + j]); // } // point[attribute.name] = value; //} } }; for (var attributeName in geometry.attributes) { _loop(); } _hit.point = point; } viewer.addTimeMark('pick', 'end'); if (params.all) { return hits.map(hit => hit.point); } else { if (hits.length === 0) { return null; } else { if (Potree.settings.pickFrontPointRatio == 0) { //如果不需要选偏离镜头近的,就要离中心近的即可 //console.log(hits[0], hits2, pixelPos) return hits[0].point; } //为了防止透过点云缝隙,选到后排的点云,将选取的位置离相机的距离考虑进去。倾向选择离相机近、且离鼠标位置近的点。(否则按照原方案只选离鼠标位置最近的,可能从高楼不小心走到下层,导航选点也是) var sorted1 = hits.sort((a, b) => a.disSquare - b.disSquare).slice(); var nearest = sorted1[0]; //return nearest.point; //直接用最近点 在点云稀疏时不太跟手,如地面上,最近点往往在鼠标下方 hits.forEach(hit => { var disDiff = hit.disSquare - nearest.disSquare; //和最近点的偏差 hit.disDiff = disDiff; hit.score = -hit.distanceToCenter - disDiff * Potree.settings.pickFrontPointRatio; }); var sorted2 = hits.sort((a, b) => b.score - a.score); //console.log(sorted2[0].point.position.z ) return sorted2[0].point; } } } // 设置点大小 changePointSize(num, sizeFitToLevel) { var size, nodeMaxLevel; //console.error('changePointSize',num) var dontRender = viewer.dealBeforeRender; if (this.material.pointSizeType != PointSizeType.ATTENUATED) { num && (size = num / Potree.config.material.realPointSize / 1.3); } else { var num_ = num; if (num_ == void 0) { num_ = this.temp.pointSize; } else { this.temp.pointSize = num_; } num_ = num_ / (Potree.config.material.realPointSize / Potree.config.material.pointSize); //兼容 num_ *= this.scale.x; //for mergeEditor //num_ = Math.pow(num_, 1.05) * 5 nodeMaxLevel = this.testMaxNodeCount >= Potree.config.testNodeCount1 ? this.nodeMaxLevel : Math.max(this.nodeMaxLevel, this.nodeMaxLevelPredict.max); //防止刚开始因nodeMaxLevel没涨完,导致过大的点云突然出现 if (sizeFitToLevel || Potree.settings.sizeFitToLevel) { //按照点云质量来调整的版本: 近似将pointSizeType换成ADAPTIVE var str = this.temp.pointSize + ':' + this.maxLevel + ':' + nodeMaxLevel; var value = this.temp.sizeFitToLevel[str]; //储存。防止每次渲染(反复切换density)都要算。 if (value) { size = value *= this.scale.x; } else { if (this.maxLevel == Infinity) return; var base = this.material.spacing / Math.pow(2, this.maxLevel); //点云大小在level为0时设置为spacing,每长一级,大小就除以2. (不同场景还是会有偏差) var r = nodeMaxLevel > 0 ? this.maxLevel / nodeMaxLevel : 0.5; //越大,越精细,需要越缩小 base *= nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(r, 3 * r + 0.3)) : 0.1; //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙(但是ortho是不用缩小的,如果能分开判断就好了) //base *= nodeMaxLevel > 0 ? Math.max(0.1, Math.pow(this.maxLevel / nodeMaxLevel, 1.1)) : 0.1 //低质量的缩小点,因为视觉上看太大了。navvis是不铺满的,我们也留一点缝隙(但是ortho是不用缩小的,如果能分开判断就好了) size = base * 20 * num_; /* * window.devicePixelRatio */ //在t-8BCqxQAr93 会议室 和 t-e2Kb2iU 隧道 两个场景里调节,因为它们的spacing相差较大,观察会议室墙壁的龟裂程度 this.temp.sizeFitToLevel[str] = size; } } else { var _base = 0.015; //2024.8.21 又要求需要不同场景点云大小相同,所以只能固定值了 (正式环境这两个场景 SG-5yxFuWhEMlg SG-XZNyvCDk0jz) //let base = this.material.spacing / Math.pow(2, nodeMaxLevel) //在保证最高级别大小刚好的情况下,不改变level降低后的大小 //base的数值理论上应该是右侧算出来的,但发现有的场景nodeMaxLevel和nodeMaxLevelPredict差别较大的点云显示也过大,而直接换成固定值反而可以适应所有场景。该固定值来源于 getHighestNodeSpacing 最小值,修改了下。(会不会是我们的相机其实该值是固定的,根据该值算出的spacing才是有误差的? 如果换了相机是否要改值?) //2022-12-21又换回非固定值。因为有的场景如SS-t-t01myDqnfE的两个数据集密集程度差别很大,应该将稀疏点云的大小设置的大些。 但是这样的缺点是两个数据集因相接处有大有小无法融合。 size = _base * 20 * num_; } //不同数据集点云最高级别时的间距不同,但不知道如何计算, 级别越高点云越密,spacing越大初始级别越密。 但实际并非2的level次方,底数可能1-2之间 } //console.log('changePointSize:' + this.dataset_id + ' , num: ' + (num && num.toPrecision(3)) + ' , size: ' + size.toPrecision(3), 'nodeMaxLevel', nodeMaxLevel.toPrecision(3), 'testMaxNodeCount',this.testMaxNodeCount /* this.material.spacing */) if (size) { if (Potree.settings.sortCloudMat) { //被废弃,不给material分组了 this.size = size; this.material.size = size; } else { this.material.size = size; } } dontRender || viewer.dispatchEvent('content_changed'); } // 设置点透明度 changePointOpacity(num, canMoreThanOne) { //num:0-1 navvis用的是亮度 if (num == void 0) { num = this.temp.pointOpacity; } else { this.temp.pointOpacity = num; } var dontRender = viewer.dealBeforeRender; //在执行beforeRender时更改的话不要发送content_changed 尤其分屏 if (Potree.settings.notAdditiveBlending) { return this.material.opacity = num; } var opacity; if (num == 1) { opacity = 1; } else { var str = (Potree.settings.sizeFitToLevel ? 'sizeFit:' : '') + (canMoreThanOne ? 'canMoreThanOne:' : '') + this.temp.pointOpacity + ':' + this.maxLevel + ':' + this.nodeMaxLevel; var value = this.temp.opacity[str]; //储存。防止每次渲染(反复切换density)都要算。 if (value) { opacity = value; } else { if (Potree.settings.sizeFitToLevel) { //按照点云质量来调整的版本: var base = this.material.spacing / Math.pow(1.7, this.maxLevel); //随着level提高,点云重叠几率增多 var minBase = this.material.spacing / Math.pow(1.7, this.nodeMaxLevel); var ratio = Math.min(1 / base, 1 / minBase / 3); //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。 opacity = base * ratio * num; if (!canMoreThanOne) { opacity = MathUtils.clamp(opacity, 0, 0.999); //到1就不透明了(可能出现一段一样) } } else { var _base2 = this.material.spacing / Math.pow(2, this.maxLevel); var _minBase = this.material.spacing / Math.pow(2, this.nodeMaxLevel); //console.log(1 / base, 1 / minBase / 6) var _ratio = Math.min(1 / _base2, 1 / _minBase / 3); //ratio为一个能使opacity不大于1 的 乘量,minBase要除以一个数,该数调越大减弱效果越强。level越低opacity和面板越接,level越高效果越弱,以减免过度重叠后的亮度。 opacity = _base2 * _ratio * num; if (!canMoreThanOne) { opacity = MathUtils.clamp(opacity, 0, 0.999); //到1就不透明了(可能出现一段一样) } } this.temp.opacity[str] = opacity; } //缺点:防止颜色过亮主要是相机离远时,当在漫游点处由于离点云太近,可能会导致高质量点云看起来很暗。 } //console.log('changePointOpacity ' + this.dataset_id + ', num : ' + num + ' , opacity : ' + this.material.opacity) //检查是否做到了低质量时num==opacity,中质量opacity稍小于num,高质量更小 if (Potree.settings.sortCloudMat) { this.opacity = opacity; this.material.opacity = opacity; } else { this.material.opacity = opacity; } dontRender || viewer.dispatchEvent('content_changed'); } updateBound() { var boundingBox_ = this.pcoGeometry.tightBoundingBox.clone().applyMatrix4(this.matrixWorld); //tightBoundingBox是点云原始的bound,但经过(绕z)旋转后bound有所改变,比之前体积更大。 this.bound = boundingBox_; this.bound2 = this.getBoundWithPanos(); } getBoundWithPanos() { //确保panoBound在内的bound var bound = this.bound.clone(); this.panos.forEach(pano => { var panoBound = new Box3(); panoBound.expandByPoint(pano.position); panoBound.expandByVector(new Vector3(1, 1, 1)); //give pano a margin bound.union(panoBound); }); return bound; } getPanosBound() { //仅由所有pano构成的bound if (this.panos.length > 0) { var minSize = new Vector3(1, 1, 1); this.panosBound = math.getBoundByPoints(this.panos.map(e => e.position), minSize); } else { this.panosBound = null; } } getUnrotBoundPoint(type) { //获取没有旋转的tightBounding的水平四个点 //如果alighment支持任意轴旋转,水平面上看到的可能就是六边形了,失去意义,到时候不能用这个。也可以若只绕z旋转, 使用tightBoundingBox,否则bound var bound = this.pcoGeometry.tightBoundingBox; if (type == 'all') { return [new Vector3(bound.min.x, bound.min.y, bound.min.z), new Vector3(bound.max.x, bound.min.y, bound.min.z), new Vector3(bound.max.x, bound.max.y, bound.min.z), new Vector3(bound.min.x, bound.max.y, bound.min.z), new Vector3(bound.min.x, bound.min.y, bound.max.z), new Vector3(bound.max.x, bound.min.y, bound.max.z), new Vector3(bound.max.x, bound.max.y, bound.max.z), new Vector3(bound.min.x, bound.max.y, bound.max.z)].map(e => e.applyMatrix4(this.matrixWorld)); } else return [new Vector3(bound.min.x, bound.min.y, 0), new Vector3(bound.max.x, bound.min.y, 0), new Vector3(bound.max.x, bound.max.y, 0), new Vector3(bound.min.x, bound.max.y, 0)].map(e => e.applyMatrix4(this.matrixWorld)); } getVolume() { /* var points = this.getUnrotBoundPoint() -----在只绕z轴旋转时这么写也行 var area = Math.abs(math.getArea(points)) return area * (this.bound.max.z - this.bound.min.z) */ var bound = this.pcoGeometry.tightBoundingBox.clone(); var size = bound.getSize(new Vector3()); return size.x * size.y * size.z; } ifContainsPoint(pos) { //pos是否坐落于tightBound内 /* if(!this.bound || !this.bound.containsPoint(pos))return ---这样写也行 var points = this.getUnrotBoundPoint() return math.isPointInArea(points, null, pos) */ //要把tightBoundingBox想象成一个volumeBox var box = this.pcoGeometry.tightBoundingBox; var center = box.getCenter(new Vector3()); var size = box.getSize(new Vector3()); var boxMatrix = new Matrix4().setPosition(center.x, center.y, center.z); boxMatrix.scale(size); boxMatrix.premultiply(this.matrixWorld); return Potree.Utils.isIntersectBox(pos, boxMatrix); } intersectBox(boxWorldMatrix) { var boxM = boxWorldMatrix.clone().premultiply(this.matrixWorld.clone().invert()); //box乘上点云逆矩阵 (因为点云的忽略掉其matrixWorld, 为了保持相对位置不变,box要左乘matrixWorld的逆)(因第一个参数bound不好变形,第二个参数box可以) return Potree.Utils.isIntersectBox(this.pcoGeometry.tightBoundingBox, boxM); } updateAttrAuto() { var _this$root$geometryNo, _this$root$geometry; //add var attributes = ((_this$root$geometryNo = this.root.geometryNode) === null || _this$root$geometryNo === void 0 ? void 0 : _this$root$geometryNo.geometry.attributes) || ((_this$root$geometry = this.root.geometry) === null || _this$root$geometry === void 0 ? void 0 : _this$root$geometry.attributes); if (!attributes) { console.log('updateAttrAuto unready, delay'); return setTimeout(() => { this.updateAttrAuto(); }, 300); } this.material.activeAttributeName = Potree.settings.showClass && attributes.classification ? 'classification' : Potree.settings.showHotTemp && attributes.temp ? 'temp' : Potree.settings.showHotIr && attributes.ir ? 'ir' : Potree.settings.cloudAttributeName || 'rgba'; } } class ProfileData { constructor(profile) { this.profile = profile; this.segments = []; this.boundingBox = new Box3(); for (var i = 0; i < profile.points.length - 1; i++) { var start = profile.points[i]; var end = profile.points[i + 1]; var startGround = new Vector3(start.x, start.y, 0); var endGround = new Vector3(end.x, end.y, 0); var center = new Vector3().addVectors(endGround, startGround).multiplyScalar(0.5); var length = startGround.distanceTo(endGround); var side = new Vector3().subVectors(endGround, startGround).normalize(); var up = new Vector3(0, 0, 1); var forward = new Vector3().crossVectors(side, up).normalize(); var N = forward; var cutPlane = new Plane().setFromNormalAndCoplanarPoint(N, startGround); var halfPlane = new Plane().setFromNormalAndCoplanarPoint(side, center); var segment = { start: start, end: end, cutPlane: cutPlane, halfPlane: halfPlane, length: length, points: new Points$1() }; this.segments.push(segment); } } size() { var size = 0; for (var segment of this.segments) { size += segment.points.numPoints; } return size; } } ; class ProfileRequest { constructor(pointcloud, profile, maxDepth, callback) { this.pointcloud = pointcloud; this.profile = profile; this.maxDepth = maxDepth || Number.MAX_VALUE; this.callback = callback; this.temporaryResult = new ProfileData(this.profile); this.pointsServed = 0; this.highestLevelServed = 0; this.priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; }); this.initialize(); } initialize() { this.priorityQueue.push({ node: this.pointcloud.pcoGeometry.root, weight: Infinity }); } // traverse the node and add intersecting descendants to queue traverse(node) { var stack = []; for (var i = 0; i < 8; i++) { var child = node.children[i]; if (child && this.pointcloud.nodeIntersectsProfile(child, this.profile)) { stack.push(child); } } while (stack.length > 0) { var _node = stack.pop(); var weight = _node.boundingSphere.radius; this.priorityQueue.push({ node: _node, weight: weight }); // add children that intersect the cutting plane if (_node.level < this.maxDepth) { for (var _i = 0; _i < 8; _i++) { var _child = _node.children[_i]; if (_child && this.pointcloud.nodeIntersectsProfile(_child, this.profile)) { stack.push(_child); } } } } } update() { if (!this.updateGeneratorInstance) { this.updateGeneratorInstance = this.updateGenerator(); } var result = this.updateGeneratorInstance.next(); if (result.done) { this.updateGeneratorInstance = null; } } *updateGenerator() { // load nodes in queue // if hierarchy expands, also load nodes from expanded hierarchy // once loaded, add data to this.points and remove node from queue // only evaluate 1-50 nodes per frame to maintain responsiveness var start = performance.now(); var maxNodesPerUpdate = 1; var intersectedNodes = []; for (var i = 0; i < Math.min(maxNodesPerUpdate, this.priorityQueue.size()); i++) { var element = this.priorityQueue.pop(); var node = element.node; if (node.level > this.maxDepth) { continue; } if (node.loaded) { // add points to result intersectedNodes.push(node); exports.lru.touch(node); this.highestLevelServed = Math.max(node.getLevel(), this.highestLevelServed); var geom = node.pcoGeometry; var hierarchyStepSize = geom ? geom.hierarchyStepSize : 1; var doTraverse = node.getLevel() === 0 || node.level % hierarchyStepSize === 0 && node.hasChildren; if (doTraverse) { this.traverse(node); } } else { node.load(); this.priorityQueue.push(element); } } if (intersectedNodes.length > 0) { for (var done of this.getPointsInsideProfile(intersectedNodes, this.temporaryResult)) { if (!done) { //console.log("updateGenerator yields"); yield false; } } if (this.temporaryResult.size() > 100) { this.pointsServed += this.temporaryResult.size(); this.callback.onProgress({ request: this, points: this.temporaryResult }); this.temporaryResult = new ProfileData(this.profile); } } if (this.priorityQueue.size() === 0) { // we're done! inform callback and remove from pending requests if (this.temporaryResult.size() > 0) { this.pointsServed += this.temporaryResult.size(); this.callback.onProgress({ request: this, points: this.temporaryResult }); this.temporaryResult = new ProfileData(this.profile); } this.callback.onFinish({ request: this }); var index = this.pointcloud.profileRequests.indexOf(this); if (index >= 0) { this.pointcloud.profileRequests.splice(index, 1); } } yield true; } *getAccepted(numPoints, node, matrix, segment, segmentDir, points, totalMileage) { var checkpoint = performance.now(); var accepted = new Uint32Array(numPoints); var mileage = new Float64Array(numPoints); var acceptedPositions = new Float32Array(numPoints * 3); var numAccepted = 0; var pos = new Vector3(); var svp = new Vector3(); var view = new Float32Array(node.geometry.attributes.position.array); for (var i = 0; i < numPoints; i++) { pos.set(view[i * 3 + 0], view[i * 3 + 1], view[i * 3 + 2]); pos.applyMatrix4(matrix); var distance = Math.abs(segment.cutPlane.distanceToPoint(pos)); var centerDistance = Math.abs(segment.halfPlane.distanceToPoint(pos)); if (distance < this.profile.width / 2 && centerDistance < segment.length / 2) { svp.subVectors(pos, segment.start); var localMileage = segmentDir.dot(svp); accepted[numAccepted] = i; mileage[numAccepted] = localMileage + totalMileage; points.boundingBox.expandByPoint(pos); pos.sub(this.pointcloud.position); acceptedPositions[3 * numAccepted + 0] = pos.x; acceptedPositions[3 * numAccepted + 1] = pos.y; acceptedPositions[3 * numAccepted + 2] = pos.z; numAccepted++; } if (i % 1000 === 0) { var duration = performance.now() - checkpoint; if (duration > 4) { //console.log(`getAccepted yield after ${duration}ms`); yield false; checkpoint = performance.now(); } } } accepted = accepted.subarray(0, numAccepted); mileage = mileage.subarray(0, numAccepted); acceptedPositions = acceptedPositions.subarray(0, numAccepted * 3); //let end = performance.now(); //let duration = end - start; //console.log("accepted duration ", duration) //console.log(`getAccepted finished`); yield [accepted, mileage, acceptedPositions]; } *getPointsInsideProfile(nodes, target) { var checkpoint = performance.now(); var totalMileage = 0; var pointsProcessed = 0; for (var segment of target.segments) { for (var node of nodes) { var numPoints = node.numPoints; var geometry = node.geometry; if (!numPoints) { continue; } { // skip if current node doesn't intersect current segment var bbWorld = node.boundingBox.clone().applyMatrix4(this.pointcloud.matrixWorld); var bsWorld = bbWorld.getBoundingSphere(new Sphere()); var start = new Vector3(segment.start.x, segment.start.y, bsWorld.center.z); var end = new Vector3(segment.end.x, segment.end.y, bsWorld.center.z); var closest = new Line3(start, end).closestPointToPoint(bsWorld.center, true, new Vector3()); var distance = closest.distanceTo(bsWorld.center); var intersects = distance < bsWorld.radius + target.profile.width; if (!intersects) { continue; } } //{// DEBUG // console.log(node.name); // let boxHelper = new Potree.Box3Helper(node.getBoundingBox()); // boxHelper.matrixAutoUpdate = false; // boxHelper.matrix.copy(viewer.scene.pointclouds[0].matrixWorld); // viewer.scene.scene.add(boxHelper); //} var sv = new Vector3().subVectors(segment.end, segment.start).setZ(0); var segmentDir = sv.clone().normalize(); var points = new Points$1(); var nodeMatrix = new Matrix4().makeTranslation(...node.boundingBox.min.toArray()); var matrix = new Matrix4().multiplyMatrices(this.pointcloud.matrixWorld, nodeMatrix); pointsProcessed = pointsProcessed + numPoints; var accepted = null; var mileage = null; var acceptedPositions = null; for (var result of this.getAccepted(numPoints, node, matrix, segment, segmentDir, points, totalMileage)) { if (!result) { var _duration = performance.now() - checkpoint; //console.log(`getPointsInsideProfile yield after ${duration}ms`); yield false; checkpoint = performance.now(); } else { [accepted, mileage, acceptedPositions] = result; } } var duration = performance.now() - checkpoint; if (duration > 4) { //console.log(`getPointsInsideProfile yield after ${duration}ms`); yield false; checkpoint = performance.now(); } points.data.position = acceptedPositions; var relevantAttributes = Object.keys(geometry.attributes).filter(a => !["position", "indices"].includes(a)); for (var attributeName of relevantAttributes) { var attribute = geometry.attributes[attributeName]; var numElements = attribute.array.length / numPoints; if (numElements !== parseInt(numElements)) { debugger; } var Type = attribute.array.constructor; var filteredBuffer = new Type(numElements * accepted.length); var source = attribute.array; var _target = filteredBuffer; for (var i = 0; i < accepted.length; i++) { var index = accepted[i]; var _start = index * numElements; var _end = _start + numElements; var sub = source.subarray(_start, _end); _target.set(sub, i * numElements); } points.data[attributeName] = filteredBuffer; } points.data['mileage'] = mileage; points.numPoints = accepted.length; segment.points.add(points); } totalMileage += segment.length; } for (var _segment of target.segments) { target.boundingBox.union(_segment.points.boundingBox); } //console.log(`getPointsInsideProfile finished`); yield true; } finishLevelThenCancel() { if (this.cancelRequested) { return; } this.maxDepth = this.highestLevelServed; this.cancelRequested = true; //console.log(`maxDepth: ${this.maxDepth}`); } cancel() { this.callback.onCancel(); this.priorityQueue = new BinaryHeap(function (x) { return 1 / x.weight; }); var index = this.pointcloud.profileRequests.indexOf(this); if (index >= 0) { this.pointcloud.profileRequests.splice(index, 1); } } } function _OverloadYield(e, d) { this.v = e, this.k = d; } function _applyDecoratedDescriptor(i, e, r, n, l) { var a = {}; return Object.keys(n).forEach(function (i) { a[i] = n[i]; }), a.enumerable = !!a.enumerable, a.configurable = !!a.configurable, ("value" in a || a.initializer) && (a.writable = !0), a = r.slice().reverse().reduce(function (r, n) { return n(i, e, r) || r; }, a), l && void 0 !== a.initializer && (a.value = a.initializer ? a.initializer.call(l) : void 0, a.initializer = void 0), void 0 === a.initializer ? (Object.defineProperty(i, e, a), null) : a; } function _applyDecs2311(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 != (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); } function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); } function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } function _asyncGeneratorDelegate(t) { var e = {}, n = !1; function pump(e, r) { return n = !0, r = new Promise(function (n) { n(t[e](r)); }), { done: !1, value: new _OverloadYield(r, 1) }; } return e["undefined" != typeof Symbol && Symbol.iterator || "@@iterator"] = function () { return this; }, e.next = function (t) { return n ? (n = !1, t) : pump("next", t); }, "function" == typeof t.throw && (e.throw = function (t) { if (n) throw n = !1, t; return pump("throw", t); }), "function" == typeof t.return && (e.return = function (t) { return n ? (n = !1, t) : pump("return", t); }), e; } function _asyncIterator(r) { var n, t, o, e = 2; for ("undefined" != typeof Symbol && (t = Symbol.asyncIterator, o = Symbol.iterator); e--;) { if (t && null != (n = r[t])) return n.call(r); if (o && null != (n = r[o])) return new AsyncFromSyncIterator(n.call(r)); t = "@@asyncIterator", o = "@@iterator"; } throw new TypeError("Object is not async iterable"); } function AsyncFromSyncIterator(r) { function AsyncFromSyncIteratorContinuation(r) { if (Object(r) !== r) return Promise.reject(new TypeError(r + " is not an object.")); var n = r.done; return Promise.resolve(r.value).then(function (r) { return { value: r, done: n }; }); } return AsyncFromSyncIterator = function (r) { this.s = r, this.n = r.next; }, AsyncFromSyncIterator.prototype = { s: null, n: null, next: function () { return AsyncFromSyncIteratorContinuation(this.n.apply(this.s, arguments)); }, return: function (r) { var n = this.s.return; return void 0 === n ? Promise.resolve({ value: r, done: !0 }) : AsyncFromSyncIteratorContinuation(n.apply(this.s, arguments)); }, throw: function (r) { var n = this.s.return; return void 0 === n ? Promise.reject(r) : AsyncFromSyncIteratorContinuation(n.apply(this.s, arguments)); } }, new AsyncFromSyncIterator(r); } function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } function _awaitAsyncGenerator(e) { return new _OverloadYield(e, 0); } function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); } function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } function _classNameTDZError(e) { throw new ReferenceError('Class "' + e + '" cannot be referenced in computed property keys.'); } function _classPrivateFieldGet2(s, a) { return s.get(_assertClassBrand(s, a)); } function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); } function _classPrivateFieldLooseBase(e, t) { if (!{}.hasOwnProperty.call(e, t)) throw new TypeError("attempted to use private field on non-instance"); return e; } var id = 0; function _classPrivateFieldLooseKey(e) { return "__private_" + id++ + "_" + e; } function _classPrivateFieldSet2(s, a, r) { return s.set(_assertClassBrand(s, a), r), r; } function _classPrivateGetter(s, r, a) { return a(_assertClassBrand(s, r)); } function _classPrivateMethodInitSpec(e, a) { _checkPrivateRedeclaration(e, a), a.add(e); } function _classPrivateSetter(s, r, a, t) { return r(_assertClassBrand(s, a), t), t; } function _classStaticPrivateMethodGet(s, a, t) { return _assertClassBrand(a, s), t; } function _construct(t, e, r) { if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); var o = [null]; o.push.apply(o, e); var p = new (t.bind.apply(t, o))(); return r && _setPrototypeOf(p, r.prototype), p; } function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var n = 0, F = function () {}; return { s: F, n: function () { return n >= r.length ? { done: !0 } : { done: !1, value: r[n++] }; }, e: function (r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function () { t = t.call(r); }, n: function () { var r = t.next(); return a = r.done, r; }, e: function (r) { u = !0, o = r; }, f: function () { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _createForOfIteratorHelperLoose(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (t) return (t = t.call(r)).next.bind(t); if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var o = 0; return function () { return o >= r.length ? { done: !0 } : { done: !1, value: r[o++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _createSuper(t) { var r = _isNativeReflectConstruct(); return function () { var e, o = _getPrototypeOf(t); if (r) { var s = _getPrototypeOf(this).constructor; e = Reflect.construct(o, arguments, s); } else e = o.apply(this, arguments); return _possibleConstructorReturn(this, e); }; } function _decorate(e, r, t, i) { var o = _getDecoratorsApi(); if (i) for (var n = 0; n < i.length; n++) o = i[n](o); var s = r(function (e) { o.initializeInstanceElements(e, a.elements); }, t), a = o.decorateClass(_coalesceClassElements(s.d.map(_createElementDescriptor)), e); return o.initializeClassElements(s.F, a.elements), o.runClassFinishers(s.F, a.finishers); } function _getDecoratorsApi() { _getDecoratorsApi = function () { return e; }; var e = { elementsDefinitionOrder: [["method"], ["field"]], initializeInstanceElements: function (e, r) { ["method", "field"].forEach(function (t) { r.forEach(function (r) { r.kind === t && "own" === r.placement && this.defineClassElement(e, r); }, this); }, this); }, initializeClassElements: function (e, r) { var t = e.prototype; ["method", "field"].forEach(function (i) { r.forEach(function (r) { var o = r.placement; if (r.kind === i && ("static" === o || "prototype" === o)) { var n = "static" === o ? e : t; this.defineClassElement(n, r); } }, this); }, this); }, defineClassElement: function (e, r) { var t = r.descriptor; if ("field" === r.kind) { var i = r.initializer; t = { enumerable: t.enumerable, writable: t.writable, configurable: t.configurable, value: void 0 === i ? void 0 : i.call(e) }; } Object.defineProperty(e, r.key, t); }, decorateClass: function (e, r) { var t = [], i = [], o = { static: [], prototype: [], own: [] }; if (e.forEach(function (e) { this.addElementPlacement(e, o); }, this), e.forEach(function (e) { if (!_hasDecorators(e)) return t.push(e); var r = this.decorateElement(e, o); t.push(r.element), t.push.apply(t, r.extras), i.push.apply(i, r.finishers); }, this), !r) return { elements: t, finishers: i }; var n = this.decorateConstructor(t, r); return i.push.apply(i, n.finishers), n.finishers = i, n; }, addElementPlacement: function (e, r, t) { var i = r[e.placement]; if (!t && -1 !== i.indexOf(e.key)) throw new TypeError("Duplicated element (" + e.key + ")"); i.push(e.key); }, decorateElement: function (e, r) { for (var t = [], i = [], o = e.decorators, n = o.length - 1; n >= 0; n--) { var s = r[e.placement]; s.splice(s.indexOf(e.key), 1); var a = this.fromElementDescriptor(e), l = this.toElementFinisherExtras((0, o[n])(a) || a); e = l.element, this.addElementPlacement(e, r), l.finisher && i.push(l.finisher); var c = l.extras; if (c) { for (var p = 0; p < c.length; p++) this.addElementPlacement(c[p], r); t.push.apply(t, c); } } return { element: e, finishers: i, extras: t }; }, decorateConstructor: function (e, r) { for (var t = [], i = r.length - 1; i >= 0; i--) { var o = this.fromClassDescriptor(e), n = this.toClassDescriptor((0, r[i])(o) || o); if (void 0 !== n.finisher && t.push(n.finisher), void 0 !== n.elements) { e = n.elements; for (var s = 0; s < e.length - 1; s++) for (var a = s + 1; a < e.length; a++) if (e[s].key === e[a].key && e[s].placement === e[a].placement) throw new TypeError("Duplicated element (" + e[s].key + ")"); } } return { elements: e, finishers: t }; }, fromElementDescriptor: function (e) { var r = { kind: e.kind, key: e.key, placement: e.placement, descriptor: e.descriptor }; return Object.defineProperty(r, Symbol.toStringTag, { value: "Descriptor", configurable: !0 }), "field" === e.kind && (r.initializer = e.initializer), r; }, toElementDescriptors: function (e) { if (void 0 !== e) return _toArray(e).map(function (e) { var r = this.toElementDescriptor(e); return this.disallowProperty(e, "finisher", "An element descriptor"), this.disallowProperty(e, "extras", "An element descriptor"), r; }, this); }, toElementDescriptor: function (e) { var r = e.kind + ""; if ("method" !== r && "field" !== r) throw new TypeError('An element descriptor\'s .kind property must be either "method" or "field", but a decorator created an element descriptor with .kind "' + r + '"'); var t = _toPropertyKey(e.key), i = e.placement + ""; if ("static" !== i && "prototype" !== i && "own" !== i) throw new TypeError('An element descriptor\'s .placement property must be one of "static", "prototype" or "own", but a decorator created an element descriptor with .placement "' + i + '"'); var o = e.descriptor; this.disallowProperty(e, "elements", "An element descriptor"); var n = { kind: r, key: t, placement: i, descriptor: Object.assign({}, o) }; return "field" !== r ? this.disallowProperty(e, "initializer", "A method descriptor") : (this.disallowProperty(o, "get", "The property descriptor of a field descriptor"), this.disallowProperty(o, "set", "The property descriptor of a field descriptor"), this.disallowProperty(o, "value", "The property descriptor of a field descriptor"), n.initializer = e.initializer), n; }, toElementFinisherExtras: function (e) { return { element: this.toElementDescriptor(e), finisher: _optionalCallableProperty(e, "finisher"), extras: this.toElementDescriptors(e.extras) }; }, fromClassDescriptor: function (e) { var r = { kind: "class", elements: e.map(this.fromElementDescriptor, this) }; return Object.defineProperty(r, Symbol.toStringTag, { value: "Descriptor", configurable: !0 }), r; }, toClassDescriptor: function (e) { var r = e.kind + ""; if ("class" !== r) throw new TypeError('A class descriptor\'s .kind property must be "class", but a decorator created a class descriptor with .kind "' + r + '"'); this.disallowProperty(e, "key", "A class descriptor"), this.disallowProperty(e, "placement", "A class descriptor"), this.disallowProperty(e, "descriptor", "A class descriptor"), this.disallowProperty(e, "initializer", "A class descriptor"), this.disallowProperty(e, "extras", "A class descriptor"); var t = _optionalCallableProperty(e, "finisher"); return { elements: this.toElementDescriptors(e.elements), finisher: t }; }, runClassFinishers: function (e, r) { for (var t = 0; t < r.length; t++) { var i = (0, r[t])(e); if (void 0 !== i) { if ("function" != typeof i) throw new TypeError("Finishers must return a constructor."); e = i; } } return e; }, disallowProperty: function (e, r, t) { if (void 0 !== e[r]) throw new TypeError(t + " can't have a ." + r + " property."); } }; return e; } function _createElementDescriptor(e) { var r, t = _toPropertyKey(e.key); "method" === e.kind ? r = { value: e.value, writable: !0, configurable: !0, enumerable: !1 } : "get" === e.kind ? r = { get: e.value, configurable: !0, enumerable: !1 } : "set" === e.kind ? r = { set: e.value, configurable: !0, enumerable: !1 } : "field" === e.kind && (r = { configurable: !0, writable: !0, enumerable: !0 }); var i = { kind: "field" === e.kind ? "field" : "method", key: t, placement: e.static ? "static" : "field" === e.kind ? "own" : "prototype", descriptor: r }; return e.decorators && (i.decorators = e.decorators), "field" === e.kind && (i.initializer = e.value), i; } function _coalesceGetterSetter(e, r) { void 0 !== e.descriptor.get ? r.descriptor.get = e.descriptor.get : r.descriptor.set = e.descriptor.set; } function _coalesceClassElements(e) { for (var r = [], isSameElement = function (e) { return "method" === e.kind && e.key === o.key && e.placement === o.placement; }, t = 0; t < e.length; t++) { var i, o = e[t]; if ("method" === o.kind && (i = r.find(isSameElement))) { if (_isDataDescriptor(o.descriptor) || _isDataDescriptor(i.descriptor)) { if (_hasDecorators(o) || _hasDecorators(i)) throw new ReferenceError("Duplicated methods (" + o.key + ") can't be decorated."); i.descriptor = o.descriptor; } else { if (_hasDecorators(o)) { if (_hasDecorators(i)) throw new ReferenceError("Decorators can't be placed on different accessors with for the same property (" + o.key + ")."); i.decorators = o.decorators; } _coalesceGetterSetter(o, i); } } else r.push(o); } return r; } function _hasDecorators(e) { return e.decorators && e.decorators.length; } function _isDataDescriptor(e) { return void 0 !== e && !(void 0 === e.value && void 0 === e.writable); } function _optionalCallableProperty(e, r) { var t = e[r]; if (void 0 !== t && "function" != typeof t) throw new TypeError("Expected '" + r + "' to be a function"); return t; } function _defaults(e, r) { for (var t = Object.getOwnPropertyNames(r), o = 0; o < t.length; o++) { var n = t[o], a = Object.getOwnPropertyDescriptor(r, n); a && a.configurable && void 0 === e[n] && Object.defineProperty(e, n, a); } return e; } function _defineAccessor(e, r, n, t) { var c = { configurable: !0, enumerable: !0 }; return c[e] = t, Object.defineProperty(r, n, c); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function _get() { return _get = "undefined" != typeof Reflect && Reflect.get ? Reflect.get.bind() : function (e, t, r) { var p = _superPropBase(e, t); if (p) { var n = Object.getOwnPropertyDescriptor(p, t); return n.get ? n.get.call(arguments.length < 3 ? e : r) : n.value; } }, _get.apply(null, arguments); } function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } function _identity(t) { return t; } function _importDeferProxy(e) { var t = null, constValue = function (e) { return function () { return e; }; }, proxy = function (r) { return function (n, o, f) { return null === t && (t = e()), r(t, o, f); }; }; return new Proxy({}, { defineProperty: constValue(!1), deleteProperty: constValue(!1), get: proxy(Reflect.get), getOwnPropertyDescriptor: proxy(Reflect.getOwnPropertyDescriptor), getPrototypeOf: constValue(null), isExtensible: constValue(!1), has: proxy(Reflect.has), ownKeys: proxy(Reflect.ownKeys), preventExtensions: constValue(!0), set: constValue(!1), setPrototypeOf: constValue(!1) }); } function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } function _inheritsLoose(t, o) { t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); } function _initializerDefineProperty(e, i, r, l) { r && Object.defineProperty(e, i, { enumerable: r.enumerable, configurable: r.configurable, writable: r.writable, value: r.initializer ? r.initializer.call(l) : void 0 }); } function _initializerWarningHelper(r, e) { throw Error("Decorating class property failed. Please ensure that transform-class-properties is enabled and runs after the decorators transform."); } function _instanceof(n, e) { return null != e && "undefined" != typeof Symbol && e[Symbol.hasInstance] ? !!e[Symbol.hasInstance](n) : n instanceof e; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _isNativeFunction(t) { try { return -1 !== Function.toString.call(t).indexOf("[native code]"); } catch (n) { return "function" == typeof t; } } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function () { return !!t; })(); } function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } var REACT_ELEMENT_TYPE; function _jsx(e, r, E, l) { REACT_ELEMENT_TYPE || (REACT_ELEMENT_TYPE = "function" == typeof Symbol && Symbol.for && Symbol.for("react.element") || 60103); var o = e && e.defaultProps, n = arguments.length - 3; if (r || 0 === n || (r = { children: void 0 }), 1 === n) r.children = l;else if (n > 1) { for (var t = Array(n), f = 0; f < n; f++) t[f] = arguments[f + 3]; r.children = t; } if (r && o) for (var i in o) void 0 === r[i] && (r[i] = o[i]);else r || (r = o || {}); return { $$typeof: REACT_ELEMENT_TYPE, type: e, key: void 0 === E ? null : "" + E, ref: null, props: r, _owner: null }; } function _maybeArrayLike(r, a, e) { if (a && !Array.isArray(a) && "number" == typeof a.length) { var y = a.length; return _arrayLikeToArray(a, void 0 !== e && e < y ? e : y); } return r(a, e); } function _newArrowCheck(n, r) { if (n !== r) throw new TypeError("Cannot instantiate an arrow function"); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _nullishReceiverError(r) { throw new TypeError("Cannot set property of null or undefined."); } function _objectDestructuringEmpty(t) { if (null == t) throw new TypeError("Cannot destructure " + t); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _objectWithoutProperties(e, t) { if (null == e) return {}; var o, r, i = _objectWithoutPropertiesLoose(e, t); if (Object.getOwnPropertySymbols) { var s = Object.getOwnPropertySymbols(e); for (r = 0; r < s.length; r++) o = s[r], t.includes(o) || {}.propertyIsEnumerable.call(e, o) && (i[o] = e[o]); } return i; } function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t = {}; for (var n in r) if ({}.hasOwnProperty.call(r, n)) { if (e.includes(n)) continue; t[n] = r[n]; } return t; } function _possibleConstructorReturn(t, e) { if (e && ("object" == typeof e || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } function _readOnlyError(r) { throw new TypeError('"' + r + '" is read-only'); } function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function () { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function (t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == typeof h && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function (t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator.return && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(typeof e + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function (e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function () { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function (e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function (t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function (t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function (t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, catch: function (t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function (e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; } function set(e, r, t, o) { return set = "undefined" != typeof Reflect && Reflect.set ? Reflect.set : function (e, r, t, o) { var f, i = _superPropBase(e, r); if (i) { if ((f = Object.getOwnPropertyDescriptor(i, r)).set) return f.set.call(o, t), !0; if (!f.writable) return !1; } if (f = Object.getOwnPropertyDescriptor(o, r)) { if (!f.writable) return !1; f.value = t, Object.defineProperty(o, r, f); } else _defineProperty(o, r, t); return !0; }, set(e, r, t, o); } function _set(e, r, t, o, f) { if (!set(e, r, t, o || e) && f) throw new TypeError("failed to set property"); return t; } function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } function _skipFirstGeneratorNext(t) { return function () { var r = t.apply(this, arguments); return r.next(), r; }; } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _superPropBase(t, o) { for (; !{}.hasOwnProperty.call(t, o) && null !== (t = _getPrototypeOf(t));); return t; } function _superPropGet(t, e, r, o) { var p = _get(_getPrototypeOf(1 & o ? t.prototype : t), e, r); return 2 & o ? function (t) { return p.apply(r, t); } : p; } function _superPropSet(t, e, o, r, p, f) { return _set(_getPrototypeOf(f ? t.prototype : t), e, o, r, p); } function _taggedTemplateLiteral(e, t) { return t || (t = e.slice(0)), Object.freeze(Object.defineProperties(e, { raw: { value: Object.freeze(t) } })); } function _taggedTemplateLiteralLoose(e, t) { return t || (t = e.slice(0)), e.raw = t, e; } function _tdz(e) { throw new ReferenceError(e + " is not defined - temporal dead zone"); } function _temporalRef(r, e) { return r === _temporalUndefined ? _tdz(e) : r; } function _temporalUndefined() {} function _toArray(r) { return _arrayWithHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableRest(); } function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toSetter(t, e, n) { e || (e = []); var r = e.length++; return Object.defineProperty({}, "_", { set: function (o) { e[r] = o, t.apply(n, e); } }); } function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _usingCtx() { var r = "function" == typeof SuppressedError ? SuppressedError : function (r, e) { var n = Error(); return n.name = "SuppressedError", n.error = r, n.suppressed = e, n; }, e = {}, n = []; function using(r, e) { if (null != e) { if (Object(e) !== e) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined."); if (r) var o = e[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]; if (void 0 === o && (o = e[Symbol.dispose || Symbol.for("Symbol.dispose")], r)) var t = o; if ("function" != typeof o) throw new TypeError("Object is not disposable."); t && (o = function () { try { t.call(e); } catch (r) { return Promise.reject(r); } }), n.push({ v: e, d: o, a: r }); } else r && n.push({ d: e, a: r }); return e; } return { e: e, u: using.bind(null, !1), a: using.bind(null, !0), d: function () { var o, t = this.e, s = 0; function next() { for (; o = n.pop();) try { if (!o.a && 1 === s) return s = 0, n.push(o), Promise.resolve().then(next); if (o.d) { var r = o.d.call(o.v); if (o.a) return s |= 2, Promise.resolve(r).then(next, err); } else s |= 1; } catch (r) { return err(r); } if (1 === s) return t !== e ? Promise.reject(t) : Promise.resolve(); if (t !== e) throw t; } function err(n) { return t = t !== e ? new r(n, t) : n, next(); } return next(); } }; } function _wrapAsyncGenerator(e) { return function () { return new AsyncGenerator(e.apply(this, arguments)); }; } function AsyncGenerator(e) { var r, t; function resume(r, t) { try { var n = e[r](t), o = n.value, u = o instanceof _OverloadYield; Promise.resolve(u ? o.v : o).then(function (t) { if (u) { var i = "return" === r ? "return" : "next"; if (!o.k || t.done) return resume(i, t); t = e[i](t).value; } settle(n.done ? "return" : "normal", t); }, function (e) { resume("throw", e); }); } catch (e) { settle("throw", e); } } function settle(e, n) { switch (e) { case "return": r.resolve({ value: n, done: !0 }); break; case "throw": r.reject(n); break; default: r.resolve({ value: n, done: !1 }); } (r = r.next) ? resume(r.key, r.arg) : t = null; } this._invoke = function (e, n) { return new Promise(function (o, u) { var i = { key: e, arg: n, resolve: o, reject: u, next: null }; t ? t = t.next = i : (r = t = i, resume(e, n)); }); }, "function" != typeof e.return && (this.return = void 0); } AsyncGenerator.prototype["function" == typeof Symbol && Symbol.asyncIterator || "@@asyncIterator"] = function () { return this; }, AsyncGenerator.prototype.next = function (e) { return this._invoke("next", e); }, AsyncGenerator.prototype.throw = function (e) { return this._invoke("throw", e); }, AsyncGenerator.prototype.return = function (e) { return this._invoke("return", e); }; function _wrapNativeSuper(t) { var r = "function" == typeof Map ? new Map() : void 0; return _wrapNativeSuper = function (t) { if (null === t || !_isNativeFunction(t)) return t; if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function"); if (void 0 !== r) { if (r.has(t)) return r.get(t); r.set(t, Wrapper); } function Wrapper() { return _construct(t, arguments, _getPrototypeOf(this).constructor); } return Wrapper.prototype = Object.create(t.prototype, { constructor: { value: Wrapper, enumerable: !1, writable: !0, configurable: !0 } }), _setPrototypeOf(Wrapper, t); }, _wrapNativeSuper(t); } function _wrapRegExp() { _wrapRegExp = function (e, r) { return new BabelRegExp(e, void 0, r); }; var e = RegExp.prototype, r = new WeakMap(); function BabelRegExp(e, t, p) { var o = RegExp(e, t); return r.set(o, p || r.get(e)), _setPrototypeOf(o, BabelRegExp.prototype); } function buildGroups(e, t) { var p = r.get(t); return Object.keys(p).reduce(function (r, t) { var o = p[t]; if ("number" == typeof o) r[t] = e[o];else { for (var i = 0; void 0 === e[o[i]] && i + 1 < o.length;) i++; r[t] = e[o[i]]; } return r; }, Object.create(null)); } return _inherits(BabelRegExp, RegExp), BabelRegExp.prototype.exec = function (r) { var t = e.exec.call(this, r); if (t) { t.groups = buildGroups(t, this); var p = t.indices; p && (p.groups = buildGroups(p, this)); } return t; }, BabelRegExp.prototype[Symbol.replace] = function (t, p) { if ("string" == typeof p) { var o = r.get(this); return e[Symbol.replace].call(this, t, p.replace(/\$<([^>]+)>/g, function (e, r) { var t = o[r]; return "$" + (Array.isArray(t) ? t.join("$") : t); })); } if ("function" == typeof p) { var i = this; return e[Symbol.replace].call(this, t, function () { var e = arguments; return "object" != typeof e[e.length - 1] && (e = [].slice.call(e)).push(buildGroups(e, i)), p.apply(this, e); }); } return e[Symbol.replace].call(this, t, p); }, _wrapRegExp.apply(this, arguments); } function _writeOnlyError(r) { throw new TypeError('"' + r + '" is write-only'); } function _AwaitValue(t) { this.wrapped = t; } function old_createMetadataMethodsForProperty(e, t, a, r) { return { getMetadata: function (o) { old_assertNotFinished(r, "getMetadata"), old_assertMetadataKey(o); var i = e[o]; if (void 0 !== i) if (1 === t) { var n = i.public; if (void 0 !== n) return n[a]; } else if (2 === t) { var l = i.private; if (void 0 !== l) return l.get(a); } else if (Object.hasOwnProperty.call(i, "constructor")) return i.constructor; }, setMetadata: function (o, i) { old_assertNotFinished(r, "setMetadata"), old_assertMetadataKey(o); var n = e[o]; if (void 0 === n && (n = e[o] = {}), 1 === t) { var l = n.public; void 0 === l && (l = n.public = {}), l[a] = i; } else if (2 === t) { var s = n.priv; void 0 === s && (s = n.private = new Map()), s.set(a, i); } else n.constructor = i; } }; } function old_convertMetadataMapToFinal(e, t) { var a = e[Symbol.metadata || Symbol.for("Symbol.metadata")], r = Object.getOwnPropertySymbols(t); if (0 !== r.length) { for (var o = 0; o < r.length; o++) { var i = r[o], n = t[i], l = a ? a[i] : null, s = n.public, c = l ? l.public : null; s && c && Object.setPrototypeOf(s, c); var d = n.private; if (d) { var u = Array.from(d.values()), f = l ? l.private : null; f && (u = u.concat(f)), n.private = u; } l && Object.setPrototypeOf(n, l); } a && Object.setPrototypeOf(t, a), e[Symbol.metadata || Symbol.for("Symbol.metadata")] = t; } } function old_createAddInitializerMethod(e, t) { return function (a) { old_assertNotFinished(t, "addInitializer"), old_assertCallable(a, "An initializer"), e.push(a); }; } function old_memberDec(e, t, a, r, o, i, n, l, s) { var c; switch (i) { case 1: c = "accessor"; break; case 2: c = "method"; break; case 3: c = "getter"; break; case 4: c = "setter"; break; default: c = "field"; } var d, u, f = { kind: c, name: l ? "#" + t : _toPropertyKey(t), isStatic: n, isPrivate: l }, p = { v: !1 }; if (0 !== i && (f.addInitializer = old_createAddInitializerMethod(o, p)), l) { d = 2, u = Symbol(t); var v = {}; 0 === i ? (v.get = a.get, v.set = a.set) : 2 === i ? v.get = function () { return a.value; } : (1 !== i && 3 !== i || (v.get = function () { return a.get.call(this); }), 1 !== i && 4 !== i || (v.set = function (e) { a.set.call(this, e); })), f.access = v; } else d = 1, u = t; try { return e(s, Object.assign(f, old_createMetadataMethodsForProperty(r, d, u, p))); } finally { p.v = !0; } } function old_assertNotFinished(e, t) { if (e.v) throw Error("attempted to call " + t + " after decoration was finished"); } function old_assertMetadataKey(e) { if ("symbol" != typeof e) throw new TypeError("Metadata keys must be symbols, received: " + e); } function old_assertCallable(e, t) { if ("function" != typeof e) throw new TypeError(t + " must be a function"); } function old_assertValidReturnValue(e, t) { var a = typeof t; if (1 === e) { if ("object" !== a || null === t) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); void 0 !== t.get && old_assertCallable(t.get, "accessor.get"), void 0 !== t.set && old_assertCallable(t.set, "accessor.set"), void 0 !== t.init && old_assertCallable(t.init, "accessor.init"), void 0 !== t.initializer && old_assertCallable(t.initializer, "accessor.initializer"); } else if ("function" !== a) throw new TypeError((0 === e ? "field" : 10 === e ? "class" : "method") + " decorators must return a function or void 0"); } function old_getInit(e) { var t; return null == (t = e.init) && (t = e.initializer) && void 0 !== console && console.warn(".initializer has been renamed to .init as of March 2022"), t; } function old_applyMemberDec(e, t, a, r, o, i, n, l, s) { var c, d, u, f, p, v, y, h = a[0]; if (n ? (0 === o || 1 === o ? (c = { get: a[3], set: a[4] }, u = "get") : 3 === o ? (c = { get: a[3] }, u = "get") : 4 === o ? (c = { set: a[3] }, u = "set") : c = { value: a[3] }, 0 !== o && (1 === o && _setFunctionName(a[4], "#" + r, "set"), _setFunctionName(a[3], "#" + r, u))) : 0 !== o && (c = Object.getOwnPropertyDescriptor(t, r)), 1 === o ? f = { get: c.get, set: c.set } : 2 === o ? f = c.value : 3 === o ? f = c.get : 4 === o && (f = c.set), "function" == typeof h) void 0 !== (p = old_memberDec(h, r, c, l, s, o, i, n, f)) && (old_assertValidReturnValue(o, p), 0 === o ? d = p : 1 === o ? (d = old_getInit(p), v = p.get || f.get, y = p.set || f.set, f = { get: v, set: y }) : f = p);else for (var m = h.length - 1; m >= 0; m--) { var b; void 0 !== (p = old_memberDec(h[m], r, c, l, s, o, i, n, f)) && (old_assertValidReturnValue(o, p), 0 === o ? b = p : 1 === o ? (b = old_getInit(p), v = p.get || f.get, y = p.set || f.set, f = { get: v, set: y }) : f = p, void 0 !== b && (void 0 === d ? d = b : "function" == typeof d ? d = [d, b] : d.push(b))); } if (0 === o || 1 === o) { if (void 0 === d) d = function (e, t) { return t; };else if ("function" != typeof d) { var g = d; d = function (e, t) { for (var a = t, r = 0; r < g.length; r++) a = g[r].call(e, a); return a; }; } else { var _ = d; d = function (e, t) { return _.call(e, t); }; } e.push(d); } 0 !== o && (1 === o ? (c.get = f.get, c.set = f.set) : 2 === o ? c.value = f : 3 === o ? c.get = f : 4 === o && (c.set = f), n ? 1 === o ? (e.push(function (e, t) { return f.get.call(e, t); }), e.push(function (e, t) { return f.set.call(e, t); })) : 2 === o ? e.push(f) : e.push(function (e, t) { return f.call(e, t); }) : Object.defineProperty(t, r, c)); } function old_applyMemberDecs(e, t, a, r, o) { for (var i, n, l = new Map(), s = new Map(), c = 0; c < o.length; c++) { var d = o[c]; if (Array.isArray(d)) { var u, f, p, v = d[1], y = d[2], h = d.length > 3, m = v >= 5; if (m ? (u = t, f = r, 0 != (v -= 5) && (p = n = n || [])) : (u = t.prototype, f = a, 0 !== v && (p = i = i || [])), 0 !== v && !h) { var b = m ? s : l, g = b.get(y) || 0; if (!0 === g || 3 === g && 4 !== v || 4 === g && 3 !== v) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + y); !g && v > 2 ? b.set(y, v) : b.set(y, !0); } old_applyMemberDec(e, u, d, y, v, m, h, f, p); } } old_pushInitializers(e, i), old_pushInitializers(e, n); } function old_pushInitializers(e, t) { t && e.push(function (e) { for (var a = 0; a < t.length; a++) t[a].call(e); return e; }); } function old_applyClassDecs(e, t, a, r) { if (r.length > 0) { for (var o = [], i = t, n = t.name, l = r.length - 1; l >= 0; l--) { var s = { v: !1 }; try { var c = Object.assign({ kind: "class", name: n, addInitializer: old_createAddInitializerMethod(o, s) }, old_createMetadataMethodsForProperty(a, 0, n, s)), d = r[l](i, c); } finally { s.v = !0; } void 0 !== d && (old_assertValidReturnValue(10, d), i = d); } e.push(i, function () { for (var e = 0; e < o.length; e++) o[e].call(i); }); } } function _applyDecs(e, t, a) { var r = [], o = {}, i = {}; return old_applyMemberDecs(r, e, i, o, t), old_convertMetadataMapToFinal(e.prototype, i), old_applyClassDecs(r, e, o, a), old_convertMetadataMapToFinal(e, o), r; } function applyDecs2203Factory() { function createAddInitializerMethod(e, t) { return function (r) { !function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); }(t), assertCallable(r, "An initializer"), e.push(r); }; } function memberDec(e, t, r, a, n, i, s, o) { var c; switch (n) { case 1: c = "accessor"; break; case 2: c = "method"; break; case 3: c = "getter"; break; case 4: c = "setter"; break; default: c = "field"; } var l, u, f = { kind: c, name: s ? "#" + t : t, static: i, private: s }, p = { v: !1 }; 0 !== n && (f.addInitializer = createAddInitializerMethod(a, p)), 0 === n ? s ? (l = r.get, u = r.set) : (l = function () { return this[t]; }, u = function (e) { this[t] = e; }) : 2 === n ? l = function () { return r.value; } : (1 !== n && 3 !== n || (l = function () { return r.get.call(this); }), 1 !== n && 4 !== n || (u = function (e) { r.set.call(this, e); })), f.access = l && u ? { get: l, set: u } : l ? { get: l } : { set: u }; try { return e(o, f); } finally { p.v = !0; } } function assertCallable(e, t) { if ("function" != typeof e) throw new TypeError(t + " must be a function"); } function assertValidReturnValue(e, t) { var r = typeof t; if (1 === e) { if ("object" !== r || null === t) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); void 0 !== t.get && assertCallable(t.get, "accessor.get"), void 0 !== t.set && assertCallable(t.set, "accessor.set"), void 0 !== t.init && assertCallable(t.init, "accessor.init"); } else if ("function" !== r) throw new TypeError((0 === e ? "field" : 10 === e ? "class" : "method") + " decorators must return a function or void 0"); } function applyMemberDec(e, t, r, a, n, i, s, o) { var c, l, u, f, p, d, h = r[0]; if (s ? c = 0 === n || 1 === n ? { get: r[3], set: r[4] } : 3 === n ? { get: r[3] } : 4 === n ? { set: r[3] } : { value: r[3] } : 0 !== n && (c = Object.getOwnPropertyDescriptor(t, a)), 1 === n ? u = { get: c.get, set: c.set } : 2 === n ? u = c.value : 3 === n ? u = c.get : 4 === n && (u = c.set), "function" == typeof h) void 0 !== (f = memberDec(h, a, c, o, n, i, s, u)) && (assertValidReturnValue(n, f), 0 === n ? l = f : 1 === n ? (l = f.init, p = f.get || u.get, d = f.set || u.set, u = { get: p, set: d }) : u = f);else for (var v = h.length - 1; v >= 0; v--) { var g; void 0 !== (f = memberDec(h[v], a, c, o, n, i, s, u)) && (assertValidReturnValue(n, f), 0 === n ? g = f : 1 === n ? (g = f.init, p = f.get || u.get, d = f.set || u.set, u = { get: p, set: d }) : u = f, void 0 !== g && (void 0 === l ? l = g : "function" == typeof l ? l = [l, g] : l.push(g))); } if (0 === n || 1 === n) { if (void 0 === l) l = function (e, t) { return t; };else if ("function" != typeof l) { var y = l; l = function (e, t) { for (var r = t, a = 0; a < y.length; a++) r = y[a].call(e, r); return r; }; } else { var m = l; l = function (e, t) { return m.call(e, t); }; } e.push(l); } 0 !== n && (1 === n ? (c.get = u.get, c.set = u.set) : 2 === n ? c.value = u : 3 === n ? c.get = u : 4 === n && (c.set = u), s ? 1 === n ? (e.push(function (e, t) { return u.get.call(e, t); }), e.push(function (e, t) { return u.set.call(e, t); })) : 2 === n ? e.push(u) : e.push(function (e, t) { return u.call(e, t); }) : Object.defineProperty(t, a, c)); } function pushInitializers(e, t) { t && e.push(function (e) { for (var r = 0; r < t.length; r++) t[r].call(e); return e; }); } return function (e, t, r) { var a = []; return function (e, t, r) { for (var a, n, i = new Map(), s = new Map(), o = 0; o < r.length; o++) { var c = r[o]; if (Array.isArray(c)) { var l, u, f = c[1], p = c[2], d = c.length > 3, h = f >= 5; if (h ? (l = t, 0 != (f -= 5) && (u = n = n || [])) : (l = t.prototype, 0 !== f && (u = a = a || [])), 0 !== f && !d) { var v = h ? s : i, g = v.get(p) || 0; if (!0 === g || 3 === g && 4 !== f || 4 === g && 3 !== f) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + p); !g && f > 2 ? v.set(p, f) : v.set(p, !0); } applyMemberDec(e, l, c, p, f, h, d, u); } } pushInitializers(e, a), pushInitializers(e, n); }(a, e, t), function (e, t, r) { if (r.length > 0) { for (var a = [], n = t, i = t.name, s = r.length - 1; s >= 0; s--) { var o = { v: !1 }; try { var c = r[s](n, { kind: "class", name: i, addInitializer: createAddInitializerMethod(a, o) }); } finally { o.v = !0; } void 0 !== c && (assertValidReturnValue(10, c), n = c); } e.push(n, function () { for (var e = 0; e < a.length; e++) a[e].call(n); }); } }(a, e, r), a; }; } var applyDecs2203Impl; function _applyDecs2203(e, t, r) { return (applyDecs2203Impl = applyDecs2203Impl || applyDecs2203Factory())(e, t, r); } function applyDecs2203RFactory() { function createAddInitializerMethod(e, t) { return function (r) { !function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); }(t), assertCallable(r, "An initializer"), e.push(r); }; } function memberDec(e, t, r, n, a, i, o, s) { var c; switch (a) { case 1: c = "accessor"; break; case 2: c = "method"; break; case 3: c = "getter"; break; case 4: c = "setter"; break; default: c = "field"; } var l, u, f = { kind: c, name: o ? "#" + t : _toPropertyKey(t), static: i, private: o }, p = { v: !1 }; 0 !== a && (f.addInitializer = createAddInitializerMethod(n, p)), 0 === a ? o ? (l = r.get, u = r.set) : (l = function () { return this[t]; }, u = function (e) { this[t] = e; }) : 2 === a ? l = function () { return r.value; } : (1 !== a && 3 !== a || (l = function () { return r.get.call(this); }), 1 !== a && 4 !== a || (u = function (e) { r.set.call(this, e); })), f.access = l && u ? { get: l, set: u } : l ? { get: l } : { set: u }; try { return e(s, f); } finally { p.v = !0; } } function assertCallable(e, t) { if ("function" != typeof e) throw new TypeError(t + " must be a function"); } function assertValidReturnValue(e, t) { var r = typeof t; if (1 === e) { if ("object" !== r || null === t) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); void 0 !== t.get && assertCallable(t.get, "accessor.get"), void 0 !== t.set && assertCallable(t.set, "accessor.set"), void 0 !== t.init && assertCallable(t.init, "accessor.init"); } else if ("function" !== r) throw new TypeError((0 === e ? "field" : 10 === e ? "class" : "method") + " decorators must return a function or void 0"); } function applyMemberDec(e, t, r, n, a, i, o, s) { var c, l, u, f, p, d, h, v = r[0]; if (o ? (0 === a || 1 === a ? (c = { get: r[3], set: r[4] }, u = "get") : 3 === a ? (c = { get: r[3] }, u = "get") : 4 === a ? (c = { set: r[3] }, u = "set") : c = { value: r[3] }, 0 !== a && (1 === a && _setFunctionName(r[4], "#" + n, "set"), _setFunctionName(r[3], "#" + n, u))) : 0 !== a && (c = Object.getOwnPropertyDescriptor(t, n)), 1 === a ? f = { get: c.get, set: c.set } : 2 === a ? f = c.value : 3 === a ? f = c.get : 4 === a && (f = c.set), "function" == typeof v) void 0 !== (p = memberDec(v, n, c, s, a, i, o, f)) && (assertValidReturnValue(a, p), 0 === a ? l = p : 1 === a ? (l = p.init, d = p.get || f.get, h = p.set || f.set, f = { get: d, set: h }) : f = p);else for (var g = v.length - 1; g >= 0; g--) { var y; void 0 !== (p = memberDec(v[g], n, c, s, a, i, o, f)) && (assertValidReturnValue(a, p), 0 === a ? y = p : 1 === a ? (y = p.init, d = p.get || f.get, h = p.set || f.set, f = { get: d, set: h }) : f = p, void 0 !== y && (void 0 === l ? l = y : "function" == typeof l ? l = [l, y] : l.push(y))); } if (0 === a || 1 === a) { if (void 0 === l) l = function (e, t) { return t; };else if ("function" != typeof l) { var m = l; l = function (e, t) { for (var r = t, n = 0; n < m.length; n++) r = m[n].call(e, r); return r; }; } else { var b = l; l = function (e, t) { return b.call(e, t); }; } e.push(l); } 0 !== a && (1 === a ? (c.get = f.get, c.set = f.set) : 2 === a ? c.value = f : 3 === a ? c.get = f : 4 === a && (c.set = f), o ? 1 === a ? (e.push(function (e, t) { return f.get.call(e, t); }), e.push(function (e, t) { return f.set.call(e, t); })) : 2 === a ? e.push(f) : e.push(function (e, t) { return f.call(e, t); }) : Object.defineProperty(t, n, c)); } function applyMemberDecs(e, t) { for (var r, n, a = [], i = new Map(), o = new Map(), s = 0; s < t.length; s++) { var c = t[s]; if (Array.isArray(c)) { var l, u, f = c[1], p = c[2], d = c.length > 3, h = f >= 5; if (h ? (l = e, 0 != (f -= 5) && (u = n = n || [])) : (l = e.prototype, 0 !== f && (u = r = r || [])), 0 !== f && !d) { var v = h ? o : i, g = v.get(p) || 0; if (!0 === g || 3 === g && 4 !== f || 4 === g && 3 !== f) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + p); !g && f > 2 ? v.set(p, f) : v.set(p, !0); } applyMemberDec(a, l, c, p, f, h, d, u); } } return pushInitializers(a, r), pushInitializers(a, n), a; } function pushInitializers(e, t) { t && e.push(function (e) { for (var r = 0; r < t.length; r++) t[r].call(e); return e; }); } return function (e, t, r) { return { e: applyMemberDecs(e, t), get c() { return function (e, t) { if (t.length > 0) { for (var r = [], n = e, a = e.name, i = t.length - 1; i >= 0; i--) { var o = { v: !1 }; try { var s = t[i](n, { kind: "class", name: a, addInitializer: createAddInitializerMethod(r, o) }); } finally { o.v = !0; } void 0 !== s && (assertValidReturnValue(10, s), n = s); } return [n, function () { for (var e = 0; e < r.length; e++) r[e].call(n); }]; } }(e, r); } }; }; } function _applyDecs2203R(e, t, r) { return (_applyDecs2203R = applyDecs2203RFactory())(e, t, r); } function applyDecs2301Factory() { function createAddInitializerMethod(e, t) { return function (r) { !function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); }(t), assertCallable(r, "An initializer"), e.push(r); }; } function assertInstanceIfPrivate(e, t) { if (!e(t)) throw new TypeError("Attempted to access private element on non-instance"); } function memberDec(e, t, r, n, a, i, s, o, c) { var u; switch (a) { case 1: u = "accessor"; break; case 2: u = "method"; break; case 3: u = "getter"; break; case 4: u = "setter"; break; default: u = "field"; } var l, f, p = { kind: u, name: s ? "#" + t : _toPropertyKey(t), static: i, private: s }, d = { v: !1 }; if (0 !== a && (p.addInitializer = createAddInitializerMethod(n, d)), s || 0 !== a && 2 !== a) { if (2 === a) l = function (e) { return assertInstanceIfPrivate(c, e), r.value; };else { var h = 0 === a || 1 === a; (h || 3 === a) && (l = s ? function (e) { return assertInstanceIfPrivate(c, e), r.get.call(e); } : function (e) { return r.get.call(e); }), (h || 4 === a) && (f = s ? function (e, t) { assertInstanceIfPrivate(c, e), r.set.call(e, t); } : function (e, t) { r.set.call(e, t); }); } } else l = function (e) { return e[t]; }, 0 === a && (f = function (e, r) { e[t] = r; }); var v = s ? c.bind() : function (e) { return t in e; }; p.access = l && f ? { get: l, set: f, has: v } : l ? { get: l, has: v } : { set: f, has: v }; try { return e(o, p); } finally { d.v = !0; } } function assertCallable(e, t) { if ("function" != typeof e) throw new TypeError(t + " must be a function"); } function assertValidReturnValue(e, t) { var r = typeof t; if (1 === e) { if ("object" !== r || null === t) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); void 0 !== t.get && assertCallable(t.get, "accessor.get"), void 0 !== t.set && assertCallable(t.set, "accessor.set"), void 0 !== t.init && assertCallable(t.init, "accessor.init"); } else if ("function" !== r) throw new TypeError((0 === e ? "field" : 10 === e ? "class" : "method") + " decorators must return a function or void 0"); } function curryThis2(e) { return function (t) { e(this, t); }; } function applyMemberDec(e, t, r, n, a, i, s, o, c) { var u, l, f, p, d, h, v, y, g = r[0]; if (s ? (0 === a || 1 === a ? (u = { get: (d = r[3], function () { return d(this); }), set: curryThis2(r[4]) }, f = "get") : 3 === a ? (u = { get: r[3] }, f = "get") : 4 === a ? (u = { set: r[3] }, f = "set") : u = { value: r[3] }, 0 !== a && (1 === a && _setFunctionName(u.set, "#" + n, "set"), _setFunctionName(u[f || "value"], "#" + n, f))) : 0 !== a && (u = Object.getOwnPropertyDescriptor(t, n)), 1 === a ? p = { get: u.get, set: u.set } : 2 === a ? p = u.value : 3 === a ? p = u.get : 4 === a && (p = u.set), "function" == typeof g) void 0 !== (h = memberDec(g, n, u, o, a, i, s, p, c)) && (assertValidReturnValue(a, h), 0 === a ? l = h : 1 === a ? (l = h.init, v = h.get || p.get, y = h.set || p.set, p = { get: v, set: y }) : p = h);else for (var m = g.length - 1; m >= 0; m--) { var b; void 0 !== (h = memberDec(g[m], n, u, o, a, i, s, p, c)) && (assertValidReturnValue(a, h), 0 === a ? b = h : 1 === a ? (b = h.init, v = h.get || p.get, y = h.set || p.set, p = { get: v, set: y }) : p = h, void 0 !== b && (void 0 === l ? l = b : "function" == typeof l ? l = [l, b] : l.push(b))); } if (0 === a || 1 === a) { if (void 0 === l) l = function (e, t) { return t; };else if ("function" != typeof l) { var I = l; l = function (e, t) { for (var r = t, n = 0; n < I.length; n++) r = I[n].call(e, r); return r; }; } else { var w = l; l = function (e, t) { return w.call(e, t); }; } e.push(l); } 0 !== a && (1 === a ? (u.get = p.get, u.set = p.set) : 2 === a ? u.value = p : 3 === a ? u.get = p : 4 === a && (u.set = p), s ? 1 === a ? (e.push(function (e, t) { return p.get.call(e, t); }), e.push(function (e, t) { return p.set.call(e, t); })) : 2 === a ? e.push(p) : e.push(function (e, t) { return p.call(e, t); }) : Object.defineProperty(t, n, u)); } function applyMemberDecs(e, t, r) { for (var n, a, i, s = [], o = new Map(), c = new Map(), u = 0; u < t.length; u++) { var l = t[u]; if (Array.isArray(l)) { var f, p, d = l[1], h = l[2], v = l.length > 3, y = d >= 5, g = r; if (y ? (f = e, 0 != (d -= 5) && (p = a = a || []), v && !i && (i = function (t) { return _checkInRHS(t) === e; }), g = i) : (f = e.prototype, 0 !== d && (p = n = n || [])), 0 !== d && !v) { var m = y ? c : o, b = m.get(h) || 0; if (!0 === b || 3 === b && 4 !== d || 4 === b && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); !b && d > 2 ? m.set(h, d) : m.set(h, !0); } applyMemberDec(s, f, l, h, d, y, v, p, g); } } return pushInitializers(s, n), pushInitializers(s, a), s; } function pushInitializers(e, t) { t && e.push(function (e) { for (var r = 0; r < t.length; r++) t[r].call(e); return e; }); } return function (e, t, r, n) { return { e: applyMemberDecs(e, t, n), get c() { return function (e, t) { if (t.length > 0) { for (var r = [], n = e, a = e.name, i = t.length - 1; i >= 0; i--) { var s = { v: !1 }; try { var o = t[i](n, { kind: "class", name: a, addInitializer: createAddInitializerMethod(r, s) }); } finally { s.v = !0; } void 0 !== o && (assertValidReturnValue(10, o), n = o); } return [n, function () { for (var e = 0; e < r.length; e++) r[e].call(n); }]; } }(e, r); } }; }; } function _applyDecs2301(e, t, r, n) { return (_applyDecs2301 = applyDecs2301Factory())(e, t, r, n); } function _applyDecs2305(e, t, r, n, o, a) { function i(e, t, r) { return function (n, o) { return r && r(n), e[t].call(n, o); }; } function c(e, t) { for (var r = 0; r < e.length; r++) e[r].call(t); return t; } function s(e, t, r, n) { if ("function" != typeof e && (n || void 0 !== e)) throw new TypeError(t + " must " + (r || "be") + " a function" + (n ? "" : " or undefined")); return e; } function applyDec(e, t, r, n, o, a, c, u, l, f, p, d, h) { function m(e) { if (!h(e)) throw new TypeError("Attempted to access private element on non-instance"); } var y, v = t[0], g = t[3], b = !u; if (!b) { r || Array.isArray(v) || (v = [v]); var w = {}, S = [], A = 3 === o ? "get" : 4 === o || d ? "set" : "value"; f ? (p || d ? w = { get: _setFunctionName(function () { return g(this); }, n, "get"), set: function (e) { t[4](this, e); } } : w[A] = g, p || _setFunctionName(w[A], n, 2 === o ? "" : A)) : p || (w = Object.getOwnPropertyDescriptor(e, n)); } for (var P = e, j = v.length - 1; j >= 0; j -= r ? 2 : 1) { var D = v[j], E = r ? v[j - 1] : void 0, I = {}, O = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: n, metadata: a, addInitializer: function (e, t) { if (e.v) throw Error("attempted to call addInitializer after decoration was finished"); s(t, "An initializer", "be", !0), c.push(t); }.bind(null, I) }; try { if (b) (y = s(D.call(E, P, O), "class decorators", "return")) && (P = y);else { var k, F; O.static = l, O.private = f, f ? 2 === o ? k = function (e) { return m(e), w.value; } : (o < 4 && (k = i(w, "get", m)), 3 !== o && (F = i(w, "set", m))) : (k = function (e) { return e[n]; }, (o < 2 || 4 === o) && (F = function (e, t) { e[n] = t; })); var N = O.access = { has: f ? h.bind() : function (e) { return n in e; } }; if (k && (N.get = k), F && (N.set = F), P = D.call(E, d ? { get: w.get, set: w.set } : w[A], O), d) { if ("object" == typeof P && P) (y = s(P.get, "accessor.get")) && (w.get = y), (y = s(P.set, "accessor.set")) && (w.set = y), (y = s(P.init, "accessor.init")) && S.push(y);else if (void 0 !== P) throw new TypeError("accessor decorators must return an object with get, set, or init properties or void 0"); } else s(P, (p ? "field" : "method") + " decorators", "return") && (p ? S.push(P) : w[A] = P); } } finally { I.v = !0; } } return (p || d) && u.push(function (e, t) { for (var r = S.length - 1; r >= 0; r--) t = S[r].call(e, t); return t; }), p || b || (f ? d ? u.push(i(w, "get"), i(w, "set")) : u.push(2 === o ? w[A] : i.call.bind(w[A])) : Object.defineProperty(e, n, w)), P; } function u(e, t) { return Object.defineProperty(e, Symbol.metadata || Symbol.for("Symbol.metadata"), { configurable: !0, enumerable: !0, value: t }); } if (arguments.length >= 6) var l = a[Symbol.metadata || Symbol.for("Symbol.metadata")]; var f = Object.create(null == l ? null : l), p = function (e, t, r, n) { var o, a, i = [], s = function (t) { return _checkInRHS(t) === e; }, u = new Map(); function l(e) { e && i.push(c.bind(null, e)); } for (var f = 0; f < t.length; f++) { var p = t[f]; if (Array.isArray(p)) { var d = p[1], h = p[2], m = p.length > 3, y = 16 & d, v = !!(8 & d), g = 0 == (d &= 7), b = h + "/" + v; if (!g && !m) { var w = u.get(b); if (!0 === w || 3 === w && 4 !== d || 4 === w && 3 !== d) throw Error("Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: " + h); u.set(b, !(d > 2) || d); } applyDec(v ? e : e.prototype, p, y, m ? "#" + h : _toPropertyKey(h), d, n, v ? a = a || [] : o = o || [], i, v, m, g, 1 === d, v && m ? s : r); } } return l(o), l(a), i; }(e, t, o, f); return r.length || u(e, f), { e: p, get c() { var t = []; return r.length && [u(applyDec(e, [r], n, e.name, 5, f, t), f), c.bind(null, t, e)]; } }; } function _classApplyDescriptorDestructureSet(e, t) { if (t.set) return "__destrObj" in t || (t.__destrObj = { set value(r) { t.set.call(e, r); } }), t.__destrObj; if (!t.writable) throw new TypeError("attempted to set read only private field"); return t; } function _classApplyDescriptorGet(e, t) { return t.get ? t.get.call(e) : t.value; } function _classApplyDescriptorSet(e, t, l) { if (t.set) t.set.call(e, l);else { if (!t.writable) throw new TypeError("attempted to set read only private field"); t.value = l; } } function _classCheckPrivateStaticAccess(s, a, r) { return _assertClassBrand(a, s, r); } function _classCheckPrivateStaticFieldDescriptor(t, e) { if (void 0 === t) throw new TypeError("attempted to " + e + " private static field before its declaration"); } function _classExtractFieldDescriptor(e, t) { return _classPrivateFieldGet2(t, e); } function _classPrivateFieldDestructureSet(e, t) { var r = _classPrivateFieldGet2(t, e); return _classApplyDescriptorDestructureSet(e, r); } function _classPrivateFieldGet(e, t) { var r = _classPrivateFieldGet2(t, e); return _classApplyDescriptorGet(e, r); } function _classPrivateFieldSet(e, t, r) { var s = _classPrivateFieldGet2(t, e); return _classApplyDescriptorSet(e, s, r), r; } function _classPrivateMethodGet(s, a, r) { return _assertClassBrand(a, s), r; } function _classPrivateMethodSet() { throw new TypeError("attempted to reassign private method"); } function _classStaticPrivateFieldDestructureSet(t, r, s) { return _assertClassBrand(r, t), _classCheckPrivateStaticFieldDescriptor(s, "set"), _classApplyDescriptorDestructureSet(t, s); } function _classStaticPrivateFieldSpecGet(t, s, r) { return _assertClassBrand(s, t), _classCheckPrivateStaticFieldDescriptor(r, "get"), _classApplyDescriptorGet(t, r); } function _classStaticPrivateFieldSpecSet(s, t, r, e) { return _assertClassBrand(t, s), _classCheckPrivateStaticFieldDescriptor(r, "set"), _classApplyDescriptorSet(s, r, e), e; } function _classStaticPrivateMethodSet() { throw new TypeError("attempted to set read only static private field"); } function _defineEnumerableProperties(e, r) { for (var t in r) { var n = r[t]; n.configurable = n.enumerable = !0, "value" in n && (n.writable = !0), Object.defineProperty(e, t, n); } if (Object.getOwnPropertySymbols) for (var a = Object.getOwnPropertySymbols(r), b = 0; b < a.length; b++) { var i = a[b]; (n = r[i]).configurable = n.enumerable = !0, "value" in n && (n.writable = !0), Object.defineProperty(e, i, n); } return e; } function dispose_SuppressedError(r, e) { return "undefined" != typeof SuppressedError ? dispose_SuppressedError = SuppressedError : (dispose_SuppressedError = function (r, e) { this.suppressed = e, this.error = r, this.stack = Error().stack; }, dispose_SuppressedError.prototype = Object.create(Error.prototype, { constructor: { value: dispose_SuppressedError, writable: !0, configurable: !0 } })), new dispose_SuppressedError(r, e); } function _dispose(r, e, s) { function next() { for (; r.length > 0;) try { var o = r.pop(), p = o.d.call(o.v); if (o.a) return Promise.resolve(p).then(next, err); } catch (r) { return err(r); } if (s) throw e; } function err(r) { return e = s ? new dispose_SuppressedError(e, r) : r, s = !0, next(); } return next(); } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? Object(arguments[r]) : {}, o = Object.keys(t); "function" == typeof Object.getOwnPropertySymbols && o.push.apply(o, Object.getOwnPropertySymbols(t).filter(function (e) { return Object.getOwnPropertyDescriptor(t, e).enumerable; })), o.forEach(function (r) { _defineProperty(e, r, t[r]); }); } return e; } function _using(o, n, e) { if (null == n) return n; if (Object(n) !== n) throw new TypeError("using declarations can only be used with objects, functions, null, or undefined."); if (e) var r = n[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")]; if (null == r && (r = n[Symbol.dispose || Symbol.for("Symbol.dispose")]), "function" != typeof r) throw new TypeError("Property [Symbol.dispose] is not a function."); return o.push({ v: n, d: r, a: e }), n; } //import SorterWasm from './sorter.wasm'; //wasm加载报错: (Note that you need plugins to import files that are not JavaScript) //origin //const SorterWasm = "AGFzbQEAAAAADwhkeWxpbmsuMAEEAAAAAAEhA2AAAGAWf39/f39/f39/f39/f39/f39/f39/fwBgAAF/AhIBA2VudgZtZW1vcnkCAwCAgAQDBAMAAQIHOQMRX193YXNtX2NhbGxfY3RvcnMAAAtzb3J0SW5kZXhlcwABE2Vtc2NyaXB0ZW5fdGxzX2luaXQAAgqSBgMDAAELhgYDAXwCewJ9An8gCyoCCLv9FCALKgIYu/0iAf0MAAAAAABAj0AAAAAAAECPQP3yASIX/SEBIhaZRAAAAAAAAOBBYwRAIBaqDAELQYCAgIB4CyEMAn8gF/0hACIWmUQAAAAAAADgQWMEQCAWqgwBC0GAgICAeAshDQJ/IAsqAii7RAAAAAAAQI9AoiIWmUQAAAAAAADgQWMEQCAWqgwBC0GAgICAeAshCwJAIBEgEGsiAyARSSIORQ0AIA39ESAM/RwBIAv9HAIhGEH4////ByEMQYiAgIB4IQ0gAyEQA0AgCSAQQQJ0aiABIBBBBHRq/QAAACAY/bUBIhf9GwAgF/0bAWogF/0bAmoiCzYCACALIAwgCyAMSBshDCALIA0gCyANShshDSAQQQFqIhAgEUcNAAsgDkUNACAPQQFrsyANsiAMspOVIRkgAyELA0ACfyAZIAkgC0ECdGoiASgCACAMa7KUIhqLQwAAAE9dBEAgGqgMAQtBgICAgHgLIRAgASAQNgIAIAogEEECdGoiASABKAIAQQFqNgIAIAtBAWoiCyARRw0ACwsgD0ECTwRAIAooAgAhEEEBIQsDQCAKIAtBAnRqIgEgASgCACAQaiIQNgIAIAtBAWoiCyAPRw0ACwsgAyARSARAIBEhEANAIAIgESAKIAkgEEEBayIQQQJ0IgFqKAIAQQJ0aiIPKAIAIhJrIgxBDGwiC2ogACAQQQR0aioCADgCACACIAtBBGoiDWogACABQQFyIhNBAnRqKgIAOAIAIAIgC0EIaiIOaiAAIAFBAnIiFEECdGoqAgA4AgAgBSAMQQJ0aiIMIAEgBGoiAS0AADoAACAMIAQgE2otAAA6AAEgDCAEIBRqLQAAOgACIAwgAS0AAzoAAyAHIAtqIAYgEEEYbGoiASoCADgCACAHIA1qIAEqAgQ4AgAgByAOaiABKgIIOAIAIAggC2ogASoCDDgCACAIIA1qIAEqAhA4AgAgCCAOaiABKgIUOAIAIA8gEkEBazYCACADIBBIDQALCwsEAEEACw=="; //增加了adaptivesize floatCenter仍是4个 var SorterWasm = "AGFzbQEAAAAADwhkeWxpbmsuMAEEAAAAAAEkA2AAAGAZf39/f39/f39/f39/f39/f39/f39/f39/fwBgAAF/AhIBA2VudgZtZW1vcnkCAwCAgAQDBAMAAQIHOQMRX193YXNtX2NhbGxfY3RvcnMAAAtzb3J0SW5kZXhlcwABE2Vtc2NyaXB0ZW5fdGxzX2luaXQAAgq7BgMDAAELrwYDAXwCewJ9An8gDioCCLv9FCAOKgIYu/0iAf0MAAAAAABAj0AAAAAAAECPQP3yASIa/SEBIhmZRAAAAAAAAOBBYwRAIBmqDAELQYCAgIB4CyEPAn8gGv0hACIZmUQAAAAAAADgQWMEQCAZqgwBC0GAgICAeAshEAJ/IA4qAii7RAAAAAAAQI9AoiIZmUQAAAAAAADgQWMEQCAZqgwBC0GAgICAeAshDgJAIBQgE2siAyAUSSIRRQ0AIBD9ESAP/RwBIA79HAIhG0H4////ByEPQYiAgIB4IRAgAyETA0AgDCATQQJ0aiABIBNBBHRq/QAAACAb/bUBIhr9GwAgGv0bAWogGv0bAmoiDjYCACAOIA8gDiAPSBshDyAOIBAgDiAQShshECATQQFqIhMgFEcNAAsgEUUNACASQQFrsyAQsiAPspOVIRwgAyEOA0ACfyAcIAwgDkECdGoiASgCACAPa7KUIh2LQwAAAE9dBEAgHagMAQtBgICAgHgLIRMgASATNgIAIA0gE0ECdGoiASABKAIAQQFqNgIAIA5BAWoiDiAURw0ACwsgEkECTwRAIA0oAgAhE0EBIQ4DQCANIA5BAnRqIgEgASgCACATaiITNgIAIA5BAWoiDiASRw0ACwsgAyAUSARAIBQhEwNAIAIgFCANIAwgE0EBayITQQJ0IgFqKAIAQQJ0aiIVKAIAIhZrIhBBDGwiDmogACATQQR0aioCADgCACACIA5BBGoiEWogACABQQFyIhdBAnRqKgIAOAIAIAIgDkEIaiISaiAAIAFBAnIiGEECdGoqAgA4AgAgBSAQQQJ0aiIPIAEgBGoiAS0AADoAACAPIAQgF2otAAA6AAEgDyAEIBhqLQAAOgACIA8gAS0AAzoAAyAHIA5qIAYgE0EYbGoiASoCADgCACAHIBFqIAEqAgQ4AgAgByASaiABKgIIOAIAIAggDmogASoCDDgCACAIIBFqIAEqAhA4AgAgCCASaiABKgIUOAIAIAkEQCAKIBBBA3RqIgEgCyATQQN0aiIOKAIANgIAIAEgDigCBDYCBAsgFSAWQQFrNgIAIAMgE0gNAAsLCwQAQQAL"; //floatCenter改为3个: var SorterWasm = "AGFzbQEAAAAADwhkeWxpbmsuMAEEAAAAAAEkA2AAAGAZf39/f39/f39/f39/f39/f39/f39/f39/fwBgAAF/AhIBA2VudgZtZW1vcnkCAwCAgAQDBAMAAQIHOQMRX193YXNtX2NhbGxfY3RvcnMAAAtzb3J0SW5kZXhlcwABE2Vtc2NyaXB0ZW5fdGxzX2luaXQAAgqhBgMDAAELlQYDAXwCewJ9An8gDioCCLv9FCAOKgIYu/0iAf0MAAAAAABAj0AAAAAAAECPQP3yASIa/SEBIhmZRAAAAAAAAOBBYwRAIBmqDAELQYCAgIB4CyEPAn8gGv0hACIZmUQAAAAAAADgQWMEQCAZqgwBC0GAgICAeAshEAJ/IA4qAii7RAAAAAAAQI9AoiIZmUQAAAAAAADgQWMEQCAZqgwBC0GAgICAeAshDgJAIBQgE2siAyAUSSIRRQ0AIBD9ESAP/RwBIA79HAIhG0H4////ByEPQYiAgIB4IRAgAyETA0AgDCATQQJ0aiABIBNBBHRq/QAAACAb/bUBIhr9GwAgGv0bAWogGv0bAmoiDjYCACAOIA8gDiAPSBshDyAOIBAgDiAQShshECATQQFqIhMgFEcNAAsgEUUNACASQQFrsyAQsiAPspOVIRwgAyEOA0ACfyAcIAwgDkECdGoiASgCACAPa7KUIh2LQwAAAE9dBEAgHagMAQtBgICAgHgLIRMgASATNgIAIA0gE0ECdGoiASABKAIAQQFqNgIAIA5BAWoiDiAURw0ACwsgEkECTwRAIA0oAgAhE0EBIQ4DQCANIA5BAnRqIgEgASgCACATaiITNgIAIA5BAWoiDiASRw0ACwsgAyAUSARAIBQhEwNAIAIgFCANIAwgE0EBayITQQJ0Ig9qKAIAQQJ0aiIVKAIAIhZrIhBBDGwiDmogACATQQxsaiIBKgIAOAIAIAIgDkEEaiIRaiABKgIEOAIAIAIgDkEIaiISaiABKgIIOAIAIAUgEEECdGoiASAEIA9qIg8tAAA6AAAgASAPLQABOgABIAEgDy0AAjoAAiABIA8tAAM6AAMgByAOaiAGIBNBGGxqIgEqAgA4AgAgByARaiABKgIEOAIAIAcgEmogASoCCDgCACAIIA5qIAEqAgw4AgAgCCARaiABKgIQOAIAIAggEmogASoCFDgCACAJBEAgCiAQQQN0aiIBIAsgE0EDdGoiDigCADYCACABIA4oAgQ2AgQLIBUgFkEBazYCACADIBNIDQALCwsEAEEACw=="; //把wasm文件替换后,build,guassian-splats那个js里找 //sortIndexes 按照深度排序 function sortWorker(self) { var wasmInstance; var wasmMemory; var useSharedMemory; var integerBasedSort; var dynamicMode; var splatCount; var indexesToSortOffset; //存储首地址 var sortedIndexesOffset = 0; var transformIndexesOffset; var transformsOffset; var precomputedDistancesOffset; var mappedDistancesOffset; var frequenciesOffset; var centersFloatOffset, centersIntOffset, sortedCentersOffset; // var sortedNodeOffset = 0, nodeOffset = 0; var colorsOffset, sortedColorsOffset, covsOffset, sortedCovs1Offset, sortedCovs2Offset; var modelViewProjOffset; var countsZero; var sortedIndexesOut; var dataInited; var Constants; var memoryRequiredOut; var sortedBufferIndex = 0; var splatEnableSwapOut, adaptiveSize, sortWorkerDatas; /* const uintToFloat = (function(){ const int32View = new Int32Array(1); const floatView = new Float32Array(int32View.buffer); return function(f) { int32View[0] = f; return floatView[0]; }; })(); */ function sort(splatSortCount, splatRenderCount, modelViewProj, usePrecomputedDistances, /* copyIndexesToSort, */copyPrecomputedDistances /* , copyTransforms */) { var sortStartTime = performance.now(); if (!dataInited) { return self.postMessage({ 'sortDone': true, 'splatSortCount': splatSortCount, 'splatRenderCount': splatRenderCount, 'sortTime': 0 }); } //console.log('startSort') if (!useSharedMemory) { var destView = new Float32Array(wasmMemory, centersFloatOffset, splatCount * 3); // copy to sorted destView.set(sortWorkerDatas.toSort.centersFloat); destView = new Uint8Array(wasmMemory, colorsOffset, splatCount * 4); destView.set(sortWorkerDatas.toSort.colors); destView = new Float32Array(wasmMemory, covsOffset, splatCount * 6); destView.set(sortWorkerDatas.toSort.covs); if (integerBasedSort) { var _destView = new Int32Array(wasmMemory, centersIntOffset, splatCount * 4); // copy to sorted _destView.set(sortWorkerDatas.toSort.centersInt); } /* const indexesToSort = new Uint32Array(wasmMemory, indexesToSortOffset, copyIndexesToSort.byteLength / Potree.defines.gs3d.BytesPerInt); indexesToSort.set(copyIndexesToSort); */ /* const transforms = new Float32Array(wasmMemory, transformsOffset, copyTransforms.byteLength / Constants.BytesPerFloat); transforms.set(copyTransforms); */ if (usePrecomputedDistances) { var precomputedDistances; if (integerBasedSort) { //可以提高速度,但可能溢出。默认是true precomputedDistances = new Int32Array(wasmMemory, precomputedDistancesOffset, copyPrecomputedDistances.byteLength / Constants.BytesPerInt); } else { precomputedDistances = new Float32Array(wasmMemory, precomputedDistancesOffset, copyPrecomputedDistances.byteLength / Constants.BytesPerFloat); } precomputedDistances.set(copyPrecomputedDistances); } } if (!countsZero) countsZero = new Uint32Array(Constants.DepthMapRange); new Float32Array(wasmMemory, modelViewProjOffset, 16).set(modelViewProj); //由于新数组已经与 WebAssembly 内存关联,实际上是将 modelViewProj 的数据复制到了 WebAssembly 内存的指定位置。 new Uint32Array(wasmMemory, frequenciesOffset, Constants.DepthMapRange).set(countsZero); var sortStartTime1 = performance.now(); var swapBufferOffset = splatEnableSwapOut && sortedBufferIndex == 1 ? memoryRequiredOut : 0; wasmInstance.exports.sortIndexes(centersFloatOffset, centersIntOffset, sortedCentersOffset + swapBufferOffset, precomputedDistancesOffset, colorsOffset, sortedColorsOffset + swapBufferOffset, covsOffset, sortedCovs1Offset + swapBufferOffset, sortedCovs2Offset + swapBufferOffset, adaptiveSize, sortedNodeOffset, nodeOffset, //// mappedDistancesOffset, frequenciesOffset, modelViewProjOffset, sortedIndexesOffset, transformIndexesOffset, transformsOffset, Constants.DepthMapRange, splatSortCount, splatRenderCount, splatCount, usePrecomputedDistances, integerBasedSort, dynamicMode); var sortMessage = { 'sortDone': true, 'splatSortCount': splatSortCount, 'splatRenderCount': splatRenderCount, 'sortTime': 0, sortedBufferIndex /* datas */ }; splatEnableSwapOut && (sortedBufferIndex = sortedBufferIndex == 1 ? 0 : 1); var sortEndTime = performance.now(); sortMessage.sortTime = sortEndTime - sortStartTime; if (!useSharedMemory) { var _destView2 = new Float32Array(wasmMemory, sortedCentersOffset, splatCount * 3); // copy to sorted sortWorkerDatas.sorted.centers.set(_destView2); _destView2 = new Uint8Array(wasmMemory, sortedColorsOffset, splatCount * 4); sortWorkerDatas.sorted.colors.set(_destView2); _destView2 = new Float32Array(wasmMemory, sortedCovs1Offset, splatCount * 3); sortWorkerDatas.sorted.covs1.set(_destView2); _destView2 = new Float32Array(wasmMemory, sortedCovs2Offset, splatCount * 3); sortWorkerDatas.sorted.covs2.set(_destView2); //若需将数据传回主线程,需再次转移。 sortMessage.toSort = { centersFloat: sortWorkerDatas.toSort.centersFloat.buffer, colors: sortWorkerDatas.toSort.colors.buffer, covs: sortWorkerDatas.toSort.covs.buffer }; integerBasedSort && (sortMessage.toSort.centersInt = sortWorkerDatas.toSort.centersInt.buffer); sortMessage.sorted = { centers: sortWorkerDatas.sorted.centers.buffer, colors: sortWorkerDatas.sorted.colors.buffer, covs1: sortWorkerDatas.sorted.covs1.buffer, covs2: sortWorkerDatas.sorted.covs2.buffer }; var TransferArr = [sortWorkerDatas.toSort.centersFloat.buffer, sortWorkerDatas.toSort.colors.buffer, sortWorkerDatas.toSort.covs.buffer, sortWorkerDatas.sorted.centers.buffer, sortWorkerDatas.sorted.colors.buffer, sortWorkerDatas.sorted.covs1.buffer, sortWorkerDatas.sorted.covs2.buffer]; TransferArr && TransferArr.push(sortWorkerDatas.toSort.centersInt.buffer); self.postMessage(sortMessage, TransferArr); /* const sortedIndexes = new Uint32Array(wasmMemory, sortedIndexesOffset, splatRenderCount); if (!sortedIndexesOut || sortedIndexesOut.length < splatRenderCount) { sortedIndexesOut = new Uint32Array(splatRenderCount); } sortedIndexesOut.set(sortedIndexes); sortMessage.sortedIndexes = sortedIndexesOut; */ } else { self.postMessage(sortMessage); } } self.onmessage = e => { if (e.data.sort) { //请求sort var renderCount = e.data.splatRenderCount || 0; var sortCount = e.data.splatSortCount || 0; var usePrecomputedDistances = e.data.usePrecomputedDistances; var copyIndexesToSort; var copyPrecomputedDistances; //let copyTransforms; if (!useSharedMemory) { // 恢复 TypedArray sortWorkerDatas.sorted.centers = new Float32Array(e.data.sorted.centers); sortWorkerDatas.sorted.covs1 = new Float32Array(e.data.sorted.covs1); sortWorkerDatas.sorted.covs2 = new Float32Array(e.data.sorted.covs2); sortWorkerDatas.sorted.colors = new Uint8Array(e.data.sorted.colors); sortWorkerDatas.toSort.centersFloat = new Float32Array(e.data.toSort.centersFloat); sortWorkerDatas.toSort.colors = new Uint8Array(e.data.toSort.colors); sortWorkerDatas.toSort.covs = new Float32Array(e.data.toSort.covs); if (integerBasedSort) { sortWorkerDatas.toSort.centersInt = new Int32Array(e.data.toSort.centersInt); } //copyTransforms = e.data.transforms; if (usePrecomputedDistances) copyPrecomputedDistances = e.data.precomputedDistances; } sort(sortCount, renderCount, e.data.modelViewProj, usePrecomputedDistances, copyPrecomputedDistances /* , copyTransforms */); } else if (e.data.init) { //初始化 分配空间 // Yep, this is super hacky and gross :( Constants = e.data.init.Constants; splatCount = e.data.init.splatCount; useSharedMemory = e.data.init.useSharedMemory; integerBasedSort = e.data.init.integerBasedSort; dynamicMode = e.data.init.dynamicMode; splatEnableSwapOut = e.data.init.splatEnableSwapOut; adaptiveSize = e.data.init.adaptiveSize; //const CENTERS_BYTES_PER_ENTRY = integerBasedSort ? (Constants.BytesPerInt * 4) : (Constants.BytesPerFloat * 4); var CENTERSInt_BYTES_PER_ENTRY = Constants.BytesPerInt * 4; var CENTERSFloat_BYTES_PER_ENTRY = Constants.BytesPerFloat * 3; var sorterWasmBytes = new Uint8Array(e.data.init.sorterWasmBytes); var matrixSize = 16 * Constants.BytesPerFloat; var memoryRequiredForIndexesToSort = splatCount * Constants.BytesPerInt; var memoryRequiredForCenters = splatCount * ((integerBasedSort ? CENTERSInt_BYTES_PER_ENTRY : 0) + CENTERSFloat_BYTES_PER_ENTRY); var memoryRequiredForCentersOut = splatCount * CENTERSFloat_BYTES_PER_ENTRY; //add:------ var memoryRequiredForColors = splatCount * 1 * 4; //4 byte var memoryRequiredForCovsHalf = splatCount * Constants.BytesPerFloat * 3; var memoryRequiredForModelViewProjectionMatrix = matrixSize; //const memoryRequiredForPrecomputedDistances = integerBasedSort ? (splatCount * Constants.BytesPerInt) : (splatCount * Constants.BytesPerFloat); var memoryRequiredForMappedDistances = splatCount * Constants.BytesPerInt; var memoryRequiredForIntermediateSortBuffers = Constants.DepthMapRange * Constants.BytesPerInt * 2; var memoryRequiredforTransformIndexes = dynamicMode ? splatCount * Constants.BytesPerInt : 0; var memoryRequiredforTransforms = dynamicMode ? Constants.MaxScenes * matrixSize : 0; var extraMemory = Constants.MemoryPageSize * 4 /* 32 */; var memoryRequiredforNode = splatCount * 8; // [uint16,uint16] var totalRequiredMemory = memoryRequiredForCenters + //memoryRequiredForCenters * (integerBasedSort ? 2 : 1) + //change !! memoryRequiredForCentersOut + memoryRequiredForColors * 2 + //add !! memoryRequiredForCovsHalf * 4 + //add !! memoryRequiredForModelViewProjectionMatrix + memoryRequiredForMappedDistances + memoryRequiredForIntermediateSortBuffers + memoryRequiredforTransformIndexes + memoryRequiredforTransforms + extraMemory; //输出要双buffer if (splatEnableSwapOut) { memoryRequiredOut = memoryRequiredForCentersOut + memoryRequiredForColors + memoryRequiredForCovsHalf * 2; totalRequiredMemory += memoryRequiredOut; } if (adaptiveSize) { totalRequiredMemory += 2 * memoryRequiredforNode; } var totalPagesRequired = Math.floor(totalRequiredMemory / Constants.MemoryPageSize) + 1; var sorterWasmImport = { module: {}, env: { memory: new WebAssembly.Memory({ initial: totalPagesRequired * e.data.init.initialPagesRatio, //2, //创建一个初始大小为 .. 页(每页64KiB)的内存实例 maximum: totalPagesRequired * 4, shared: true }) } }; WebAssembly.compile(sorterWasmBytes).then(wasmModule => { return WebAssembly.instantiate(wasmModule, sorterWasmImport); }).then(instance => { wasmInstance = instance; centersFloatOffset = 0; /* if(integerBasedSort){ centersIntOffset = centersFloatOffset + memoryRequiredForCenters } colorsOffset = (integerBasedSort ? centersIntOffset : centersFloatOffset) + memoryRequiredForCenters */ if (integerBasedSort) { centersIntOffset = centersFloatOffset + splatCount * CENTERSFloat_BYTES_PER_ENTRY; } colorsOffset = centersFloatOffset + memoryRequiredForCenters; covsOffset = colorsOffset + memoryRequiredForColors; sortedCentersOffset = covsOffset + memoryRequiredForCovsHalf * 2; sortedColorsOffset = sortedCentersOffset + memoryRequiredForCentersOut; sortedCovs1Offset = sortedColorsOffset + memoryRequiredForColors; sortedCovs2Offset = sortedCovs1Offset + memoryRequiredForCovsHalf; modelViewProjOffset = sortedCovs2Offset + memoryRequiredForCovsHalf; if (splatEnableSwapOut) { modelViewProjOffset += memoryRequiredOut; } mappedDistancesOffset = modelViewProjOffset + memoryRequiredForModelViewProjectionMatrix; frequenciesOffset = mappedDistancesOffset + memoryRequiredForMappedDistances; transformIndexesOffset = frequenciesOffset + memoryRequiredForIntermediateSortBuffers; transformsOffset = transformIndexesOffset + memoryRequiredforTransformIndexes; if (adaptiveSize) { sortedNodeOffset = transformsOffset + memoryRequiredforTransforms; nodeOffset = sortedNodeOffset + memoryRequiredforNode; } //注意:每个offset = 前一个offset + 前一个所需大小 wasmMemory = sorterWasmImport.env.memory.buffer; //SharedArrayBuffer //self.postMessage({msg:'worker useSharedMemory'}); if (useSharedMemory) { try { self.postMessage({ 'sortSetupPhase1Complete': true, 'sortedIndexesOffset': sortedIndexesOffset, 'precomputedDistancesOffset': precomputedDistancesOffset, 'transformsOffset': transformsOffset, centersFloatOffset, centersIntOffset, sortedCentersOffset, colorsOffset, sortedColorsOffset, covsOffset, sortedCovs1Offset, sortedCovs2Offset, sortedNodeOffset, nodeOffset, memoryRequiredOut, wasmMemory }); } catch (e) { self.postMessage({ msg: { 'msg': 'error', e, name: e.name, message: e.message, crossOriginIsolated: self.crossOriginIsolated //or window.SharedArrayBuffer存在 } }); //如果设备不支持传wasmMemory,会报错: DataCloneError: Failed to execute 'postMessage' on 'DedicatedWorkerGlobalScope': SharedArrayBuffer transfer requires self.crossOriginIsolated. //没有配置环境或ip没用https 或安卓非chrome } } else { sortWorkerDatas = { //接收 sorted: {}, toSort: {} }; self.postMessage({ 'sortSetupPhase1Complete': true }); } }); } else if (e.data.dataInited) { dataInited = true; } }; } function createSortWorker(useSharedMemory, splatCount, integerBasedSort, dynamicMode, adaptiveSize, splatEnableSwapOut) { var worker = new Worker(URL.createObjectURL(new Blob(['(', sortWorker.toString(), ')(self)'], { type: 'application/javascript' }))); var sorterWasmBinaryString = atob(SorterWasm); //二进制代码 写死了 var sorterWasmBytes = new Uint8Array(sorterWasmBinaryString.length); for (var i = 0; i < sorterWasmBinaryString.length; i++) { sorterWasmBytes[i] = sorterWasmBinaryString.charCodeAt(i); } worker.postMessage({ 'init': { 'sorterWasmBytes': sorterWasmBytes.buffer, 'splatCount': splatCount, 'useSharedMemory': useSharedMemory, 'integerBasedSort': integerBasedSort, 'dynamicMode': dynamicMode, splatEnableSwapOut, initialPagesRatio: Potree.browser.isMobile ? 1.1 : 1.5, /* 1.3, */ adaptiveSize, // Super hacky 'Constants': { 'BytesPerFloat': Potree.defines.gs3d.BytesPerFloat, 'BytesPerInt': Potree.defines.gs3d.BytesPerInt, 'DepthMapRange': Potree.defines.gs3d.DepthMapRange, 'MemoryPageSize': Potree.defines.gs3d.MemoryPageSize, 'MaxScenes': Potree.defines.gs3d.MaxScenes } } }); return worker; } /* 相关函数见 setupSortWorker updateSplatSort updateRenderIndexes addSplatBuffers 不share buffer 的话似乎不可行 */ var adaptive_size_vert = "\n#if defined adaptive_size\n\n\n \n float Round(float number){\n return floor(number + 0.5);\n }\n int numberOfOnes(int number, int index){\n int numOnes = 0;\n int tmp = 128;\n for(int i = 7; i >= 0; i--){\n \n if(number >= tmp){\n number = number - tmp;\n\n if(i <= index){\n numOnes++;\n }\n }\n \n tmp = tmp / 2;\n }\n\n return numOnes;\n }\n\n bool isBitSet(int number, int index){\n\n // weird multi else if due to lack of proper array, int and bitwise support in WebGL 1.0\n int powi = 1;\n if(index == 0){\n powi = 1;\n }else if(index == 1){\n powi = 2;\n }else if(index == 2){\n powi = 4;\n }else if(index == 3){\n powi = 8;\n }else if(index == 4){\n powi = 16;\n }else if(index == 5){\n powi = 32;\n }else if(index == 6){\n powi = 64;\n }else if(index == 7){\n powi = 128;\n }else{\n return false;\n }\n\n int ndp = number / powi;\n\n return mod(float(ndp), 2.0) != 0.0;\n }\n \n \n float getLOD(){//////copy from pointcloud Adaptive\n \n float level = float(nodeLevelVNS.x);\n float depth = level; \n int iOffset = nodeLevelVNS.y; //VNStart\n vec3 offset = vec3(0.0, 0.0, 0.0); \n \n for(float i = 0.0; i <= 30.0; i++){\n float nodeSizeAtLevel = uOctreeSize / pow(2.0, i + level + 0.0);\n \n vec3 index3d = (center-offset) / nodeSizeAtLevel;\n index3d = floor(index3d + 0.5);\n int index = int(Round(4.0 * index3d.x + 2.0 * index3d.y + index3d.z));\n \n vec4 value = texture2D(visibleNodesTex, vec2(float(iOffset) / 2048.0, 0.0));\n int mask = int(Round(value.r * 255.0));\n\n if(isBitSet(mask, index)){\n // there are more visible child nodes at this position\n int advanceG = int(Round(value.g * 255.0)) * 256;\n int advanceB = int(Round(value.b * 255.0));\n int advanceChild = numberOfOnes(mask, index - 1);\n int advance = advanceG + advanceB + advanceChild;\n\n iOffset = iOffset + advance;\n \n depth++;\n }else{\n // no more visible child nodes at this position\n //return value.a * 255.0;\n\n float lodOffset = (255.0 * value.a) / 10.0 - 10.0;\n\n return depth + lodOffset;\n }\n \n offset = offset + (vec3(1.0, 1.0, 1.0) * nodeSizeAtLevel * 0.5) * index3d;\n }\n \n return depth;\n }\n\n float getPointSizeAttenuation(){\n return pow(2.0, getLOD());\n }\n \n \n \n \n #endif\n"; //从 GaussianSplats3D 里摘过来,用于显示webcloud型的lod 3dgs var sortCount = 0; var dummyGeometry = new BufferGeometry(); var dummyMaterial = new MeshBasicMaterial(); //只允许存在至多一个Splat class Splat extends Mesh { constructor() { var { adaptiveSize, splatEnableSwapOut } = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; super(dummyGeometry, dummyMaterial); _defineProperty(this, "gatherSceneNodesForSort", function () { //将要渲染的点的index写入sortWorkerIndexesToSort的buffer return function () { var gatherAllNodes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; var time0 = performance.now(); var CENTERSInt_BYTES_PER_ENTRY = Potree.defines.gs3d.BytesPerInt * 4; var CENTERSFloat_BYTES_PER_ENTRY = Potree.defines.gs3d.BytesPerFloat * 3; var COLORS_BYTES_PER_ENTRY = 1 * 4; //4 byte var COVS_BYTES_PER_ENTRY = Potree.defines.gs3d.BytesPerFloat * 6; var NODES_BYTES_PER_ENTRY = 4 * 2; var currentByteOffset = { centers: 0, centersInt: 0, colors: 0, covs: 0, nodes: 0 }; var { centersFloat, centersInt, colors, covs, nodes } = this.sortWorkerDatas.toSort; var shareBuffer = centersFloat.buffer; //大伙buffer都是同一个 this.currentNodes = Potree.visibleNodes.slice(); var i = 0, nodeCount = Potree.visibleNodes.length, splatCount = 0; if (this.adaptiveSize) { //这里若有多个pointclouds,需要改下吗,似乎可以不用改 this.visibilityTextureData = this.pointclouds[0].computeVisibilityTextureData(this.currentNodes, viewer.mainViewport.camera); } while (splatCount < Potree.config.maxSplatCount && i < nodeCount) { var numPoints = Potree.visibleNodes[i].geometryNode.numPoints; if (numPoints + splatCount > Potree.config.maxSplatCount) break; splatCount += numPoints; var c0 = numPoints * 3, c1 = numPoints * 4, c2 = numPoints * 6, c3 = numPoints * 2; var geo = Potree.visibleNodes[i].geometryNode.geometry; var destView = this.useSharedMemory ? new Float32Array(shareBuffer, centersFloat.byteOffset + currentByteOffset.centers, c0) : new Float32Array(centersFloat.buffer, currentByteOffset.centers, c0); destView.set(geo.attributes.centersFloat.array /* .subarray(0,c0) */); if (this.integerBasedSort) { destView = this.useSharedMemory ? new Int32Array(shareBuffer, centersInt.byteOffset + currentByteOffset.centersInt, c1) : new Int32Array(centersInt.buffer, currentByteOffset.centersInt, c1); destView.set(geo.attributes.centersInt.array /* .subarray(0,c1) */); } destView = this.useSharedMemory ? new Uint8Array(shareBuffer, colors.byteOffset + currentByteOffset.colors, c1) : new Uint8Array(colors.buffer, currentByteOffset.colors, c1); destView.set(geo.attributes.rgba.array /* .subarray(0,c1) */); destView = this.useSharedMemory ? new Float32Array(shareBuffer, covs.byteOffset + currentByteOffset.covs, c2) : new Float32Array(covs.buffer, currentByteOffset.covs, c2); destView.set(geo.attributes.covs.array /* .subarray(0,c2) */); if (this.adaptiveSize) { //效果不好,暂只支持useSharedMemory时开启 destView = new Int32Array(shareBuffer, nodes.byteOffset + currentByteOffset.nodes, c3); var vnStart = this.visibilityTextureData.offsets.get(Potree.visibleNodes[i]); var level = Potree.visibleNodes[i].getLevel(); for (var n = 0; n < numPoints; n += 1) { destView[n * 2] = level; destView[n * 2 + 1] = vnStart; } } currentByteOffset.centersInt += numPoints * CENTERSInt_BYTES_PER_ENTRY; currentByteOffset.centers += numPoints * CENTERSFloat_BYTES_PER_ENTRY; currentByteOffset.colors += numPoints * COLORS_BYTES_PER_ENTRY; currentByteOffset.covs += numPoints * COVS_BYTES_PER_ENTRY; this.adaptiveSize && (currentByteOffset.nodes += numPoints * NODES_BYTES_PER_ENTRY); i++; } this.sortWorker.postMessage({ 'dataInited': true }); return { 'splatRenderCount': splatCount }; }; }()); _defineProperty(this, "updateSort", function () { var mvpMatrix = new Matrix4(); var cameraPositionArray = []; var lastSortViewDir = new Vector3(0, 0, -1); var sortViewDir = new Vector3(0, 0, -1); var lastSortViewPos = new Vector3(); var sortViewOffset = new Vector3(); var queuedSorts = []; var lastTime = 0; var partialSorts = [{ 'angleThreshold': 0.55, 'sortFractions': [0.125, 0.33333, 0.75] }, { 'angleThreshold': 0.65, 'sortFractions': [0.33333, 0.66667] }, { 'angleThreshold': 0.8, 'sortFractions': [0.5] }]; return async function () { var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; // Potree.visibleNodes || console.log('updateSort') // this.sortWorkerDatas || console.log('updateSort') if (this.sortRunning || this.unableSort || !this.sortWorkerDatas || !Potree.visibleNodes || !this.visible) return; var camera = viewer.mainViewport.camera; var timeNow = Date.now(); var angleDiff = 0; var positionDiff = 0; var needsRefreshForRotation = false; var needsRefreshForPosition = false; if (Potree.math.closeTo(Potree.visibleNodes.length, this.currentNodes.length, 10) && timeNow - lastTime < 1000) { //显示的nodes没太大变化的话,可能先不sort sortViewDir.set(0, 0, -1).applyQuaternion(camera.quaternion); angleDiff = sortViewDir.dot(lastSortViewDir); positionDiff = sortViewOffset.copy(camera.position).sub(lastSortViewPos).length(); if (!force) { if (!this.sortNeededForSceneChange && !this.dynamicMode && queuedSorts.length === 0) { if (angleDiff <= 0.99) needsRefreshForRotation = true; if (positionDiff >= 1.0) needsRefreshForPosition = true; if (!needsRefreshForRotation && !needsRefreshForPosition) return setTimeout(this.updateSort.bind(this), 600); //console.log('no need refresh', Potree.visibleNodes.length, this.currentNodes.length); } } } this.sortRunning = true; var { splatRenderCount } = this.gatherSceneNodesForSort(); this.splatRenderCount = splatRenderCount; mvpMatrix.copy(camera.matrixWorld).invert(); mvpMatrix.premultiply(camera.projectionMatrix); mvpMatrix.multiply(this.matrixWorld); /* if (this.gpuAcceleratedSort && (queuedSorts.length <= 1 || queuedSorts.length % 2 === 0)) { await this.splatMesh.computeDistancesOnGPU(mvpMatrix, this.sortWorkerPrecomputedDistances); } */ if (this.dynamicMode) { queuedSorts.push(this.splatRenderCount); } else { if (queuedSorts.length === 0) { for (var partialSort of partialSorts) { if (angleDiff < partialSort.angleThreshold) { for (var sortFraction of partialSort.sortFractions) { queuedSorts.push(Math.floor(this.splatRenderCount * sortFraction)); } break; } } queuedSorts.push(this.splatRenderCount); } } if (queuedSorts.length > 1) { //console.log(queuedSorts) queuedSorts.length = 0; } //let sortCount = Math.min(queuedSorts.shift(), this.splatRenderCount); //console.log('updateSort') var sortCount = this.splatRenderCount; cameraPositionArray[0] = camera.position.x; cameraPositionArray[1] = camera.position.y; cameraPositionArray[2] = camera.position.z; var sortMessage = { sort: true, 'modelViewProj': mvpMatrix.elements, 'cameraPosition': cameraPositionArray, 'splatRenderCount': this.splatRenderCount, 'splatSortCount': sortCount, 'usePrecomputedDistances': this.gpuAcceleratedSort //也就是不用worker算而用gpu,见 computeDistancesOnGPU }; if (!this.useSharedMemory) { //通过 Transferable 对象转移所有权:无拷贝. 将内存控制权从主线程转移到 Worker,主线程中原 TypedArray 会变为不可用(长度变为 0)。 //把所有盆都交给worker,装满数据再回来 -,- sortMessage.toSort = { centersFloat: this.sortWorkerDatas.toSort.centersFloat.buffer, // worker.postMessage({ buffer: array.buffer }, [array.buffer]); colors: this.sortWorkerDatas.toSort.colors.buffer, covs: this.sortWorkerDatas.toSort.covs.buffer }; this.integerBasedSort && (sortMessage.toSort.centersInt = this.sortWorkerDatas.toSort.centersInt.buffer); sortMessage.sorted = { centers: this.sortWorkerDatas.sorted[0].centers.buffer, colors: this.sortWorkerDatas.sorted[0].colors.buffer, covs1: this.sortWorkerDatas.sorted[0].covs1.buffer, covs2: this.sortWorkerDatas.sorted[0].covs2.buffer }; } //如果要直接swapBuffer,内存占用太多还是算了吧 /* if (this.splatMesh.dynamicMode) { this.splatMesh.fillTransformsArray(this.sortWorkerTransforms); } if (!this.sharedMemoryForWorkers) { //sortMessage.indexesToSort = this.sortWorkerIndexesToSort; sortMessage.transforms = this.sortWorkerTransforms; if (this.gpuAcceleratedSort) { sortMessage.precomputedDistances = this.sortWorkerPrecomputedDistances; } }*/ this.sortPromise = new Promise(resolve => { this.sortPromiseResolver = resolve; }); if (this.useSharedMemory) { this.sortWorker.postMessage(sortMessage); } else { var TransferArr = [this.sortWorkerDatas.toSort.centersFloat.buffer, this.sortWorkerDatas.toSort.colors.buffer, this.sortWorkerDatas.toSort.covs.buffer, this.sortWorkerDatas.sorted[0].centers.buffer, this.sortWorkerDatas.sorted[0].colors.buffer, this.sortWorkerDatas.sorted[0].covs1.buffer, this.sortWorkerDatas.sorted[0].covs2.buffer]; TransferArr && TransferArr.push(this.sortWorkerDatas.toSort.centersInt.buffer); this.sortWorker.postMessage(sortMessage, TransferArr); } //if (queuedSorts.length === 0) { lastSortViewPos.copy(camera.position); lastSortViewDir.copy(sortViewDir); //} this.sortNeededForSceneChange = false; lastTime = timeNow; }; }()); this.adaptiveSize = adaptiveSize; this.geometry = this.buildGeometry(); this.material = this.buildMaterial(); this.frustumCulled = false; //禁止视锥体剔除 this.focalAdjustment = 1; this.integerBasedSort = true; this.currentNodes = []; Potree.Utils.setObjectLayers(this, 'model'); this.pointclouds = []; var maxSplatCount = Potree.config.maxSplatCount; var PosArrayType = this.integerBasedSort ? Int32Array : Float32Array; adaptiveSize && this.switchSizeType(true); //初始化后不支持更改 this.useSharedMemory = Potree.browser.urlHasValue('NoShareBuffer') ? false : !!self.crossOriginIsolated; if (!self.crossOriginIsolated) { alert('sharedMemory unsupported!'); } document.title = this.useSharedMemory ? '3dgs-shareBuffer' : '3dgs-NoShareBuffer'; if (!this.useSharedMemory) { Potree.pointBudget = Potree.config.maxSplatCount = Potree.browser.isMobile() ? 1000000 : 2000000; } if (this.useSharedMemory) splatEnableSwapOut = false; //暂不支持,否则要创建太多内存 this.sortWorker = createSortWorker(this.useSharedMemory, maxSplatCount, true, false, adaptiveSize, splatEnableSwapOut); this.sortWorker.onmessage = e => { if (e.data.sortDone) { this.sortRunning = false; var sortWorkerDatas = this.sortWorkerDatas; if (!this.useSharedMemory) { //恢复数据主权 sortWorkerDatas.sorted[0].centers = new Float32Array(e.data.sorted.centers); sortWorkerDatas.sorted[0].covs1 = new Float32Array(e.data.sorted.covs1); sortWorkerDatas.sorted[0].covs2 = new Float32Array(e.data.sorted.covs2); sortWorkerDatas.sorted[0].colors = new Uint8Array(e.data.sorted.colors); sortWorkerDatas.toSort.centersFloat = new Float32Array(e.data.toSort.centersFloat); sortWorkerDatas.toSort.colors = new Uint8Array(e.data.toSort.colors); sortWorkerDatas.toSort.covs = new Float32Array(e.data.toSort.covs); if (this.integerBasedSort) { sortWorkerDatas.toSort.centersInt = new Int32Array(e.data.toSort.centersInt); } } var { centers, colors, covs1, covs2, nodes } = sortWorkerDatas.sorted[e.data.sortedBufferIndex]; this.updateRenderIndexes(centers, colors, covs1, covs2, nodes, e.data.splatRenderCount); //this.lastSortTime = e.data.sortTime;//sort耗时 this.sortPromiseResolver(); this.sortPromiseResolver = null; viewer.dispatchEvent('content_changed'); //this.forceRenderNextFrame(); sortCount++; } else if (e.data.sortCanceled) { this.sortRunning = false; } else if (e.data.sortSetupPhase1Complete) { //sortWorker init 完毕 console.log('Sorting web worker WASM setup complete.'); var CENTERS_BYTES_PER_ENTRY = this.integerBasedSort ? Potree.defines.gs3d.BytesPerInt * 4 : Potree.defines.gs3d.BytesPerFloat * 4; if (this.useSharedMemory) { this.sortWorkerDatas = { sorted: [{ centers: new Float32Array(e.data.wasmMemory, e.data.sortedCentersOffset, maxSplatCount * 3), colors: new Uint8Array(e.data.wasmMemory, e.data.sortedColorsOffset, maxSplatCount * 4), covs1: new Float32Array(e.data.wasmMemory, e.data.sortedCovs1Offset, maxSplatCount * 3), covs2: new Float32Array(e.data.wasmMemory, e.data.sortedCovs2Offset, maxSplatCount * 3) }], toSort: { centersFloat: new Float32Array(e.data.wasmMemory, e.data.centersFloatOffset, maxSplatCount * 3), colors: new Uint8Array(e.data.wasmMemory, e.data.colorsOffset, maxSplatCount * 4), //所有类型参数均为:buffer, byteOffset, length, 第三个参数是item个数,而非字节数 covs: new Float32Array(e.data.wasmMemory, e.data.covsOffset, maxSplatCount * 6) } }; if (this.integerBasedSort) { this.sortWorkerDatas.toSort.centersInt = new Int32Array(e.data.wasmMemory, e.data.centersIntOffset, maxSplatCount * 4); } } else { this.sortWorkerDatas = { sorted: [{ centers: new Float32Array(maxSplatCount * 3), colors: new Uint8Array(maxSplatCount * 4), covs1: new Float32Array(maxSplatCount * 3), covs2: new Float32Array(maxSplatCount * 3) }], toSort: { centersFloat: new Float32Array(maxSplatCount * 3), colors: new Uint8Array(maxSplatCount * 4), //所有类型参数均为:buffer, byteOffset, length, 第三个参数是item个数,而非字节数 covs: new Float32Array(maxSplatCount * 6) } }; if (this.integerBasedSort) { this.sortWorkerDatas.toSort.centersInt = new Int32Array(maxSplatCount * 4); } } if (this.adaptiveSize) { this.sortWorkerDatas.sorted[0].nodes = new Int32Array(e.data.wasmMemory, e.data.sortedNodeOffset, maxSplatCount * 2); this.sortWorkerDatas.toSort.nodes = new Int32Array(e.data.wasmMemory, e.data.nodeOffset, maxSplatCount * 2); } if (this.splatEnableSwapOut) { this.sortWorkerDatas.sorted.push({ centers: new Float32Array(e.data.wasmMemory, e.data.sortedCentersOffset + e.data.memoryRequiredOut, maxSplatCount * 3), colors: new Uint8Array(e.data.wasmMemory, e.data.sortedColorsOffset + e.data.memoryRequiredOut, maxSplatCount * 4), covs1: new Float32Array(e.data.wasmMemory, e.data.sortedCovs1Offset + e.data.memoryRequiredOut, maxSplatCount * 3), covs2: new Float32Array(e.data.wasmMemory, e.data.sortedCovs2Offset + e.data.memoryRequiredOut, maxSplatCount * 3) }); } //if gpuAcceleratedSort and dont shareMemory //this.sortWorkerPrecomputedDistances = new PosArrayType(e.data.wasmMemory, e.data.precomputedDistancesOffset, maxSplatCount); //this.sortWorkerTransforms = new Float32Array(e.data.wasmMemory, e.data.transformsOffset, Potree.defines.gs3d.MaxScenes * 16); //this.sortWorker.maxSplatCount = maxSplatCount;//总点数 console.log('Sorting web worker ready.'); this.updateSort(); } else { console.log(e.data.msg); } }; viewer.addEventListener('camera_changed', e => { var camera = e.viewport.camera; var pos = camera.position; if (e.viewport.name == 'MainView') { this.updateSort(); if (e.changeInfo.projectionChanged) { this.updateMaterial(); } } }); viewer.addEventListener('pointcloud_changed', e => { this.updateSort(); }); this.updateMaterial(); } addPointcloud(pointcloud) { //添加点云,数据先借助点云加载,再转过来 var index = this.pointclouds.indexOf(pointcloud); index == -1 && this.pointclouds.push(pointcloud); pointcloud.isSplat = true; pointcloud.matrixAutoUpdate = true; //this.update() } transformCallBack() { //其点云要和这一模一样 this.pointclouds.forEach(e => { e.position.copy(this.position); e.rotation.copy(this.rotation); }); } removePointcloud(pointcloud) { //删除 var index = this.pointclouds.indexOf(pointcloud); index > -1 && this.pointclouds.splice(index, 1); //this.update() pointcloud.isSplat = false; } buildGeometry() { var baseGeometry = new BufferGeometry(); baseGeometry.setIndex([0, 1, 2, 0, 2, 3]); // Vertices for the instanced quad var positionsArray = new Float32Array(4 * 3); var positions = new BufferAttribute(positionsArray, 3); baseGeometry.setAttribute('position', positions); positions.setXYZ(0, -1.0, -1.0, 0.0); positions.setXYZ(1, -1.0, 1.0, 0.0); positions.setXYZ(2, 1.0, 1.0, 0.0); positions.setXYZ(3, 1.0, -1.0, 0.0); positions.needsUpdate = true; var geometry = new InstancedBufferGeometry().copy(baseGeometry); /* //给点加序号 const splatIndexArray = new Uint32Array(Potree.config.maxSplatCount); const splatIndexes = new THREE.InstancedBufferAttribute(splatIndexArray, 1, false); splatIndexes.setUsage(THREE.DynamicDrawUsage); geometry.setAttribute('splatIndex', splatIndexes); for(let i=0;i clip || clipCenter.y < -clip || clipCenter.y > clip) {\n gl_Position = vec4(0.0, 0.0, 2.0, 1.0);\n return;\n }\n\n vPosition = position.xy;\n vColor = color;\n \n //debug \u6D4B\u8BD5sort\u662F\u5426\u6B63\u786E. \u7531\u8FDC\u53CA\u8FD1\u7531\u9ED1\u53D8\u767D\n //float r = float(splatIndex) / float(splatCount) ;\n //vColor = vec4(r,r,r,1.0); \n\n mat3 Vrk = mat3( // Construct the 3D covariance matrix\n covRow1.x, covRow1.y, covRow1.z,\n covRow1.y, covRow2.x, covRow2.y,\n covRow1.z, covRow2.y, covRow2.z\n );\n \n //\u7FFB\u8BD1\u7ED3\u679C\uFF1A\u6784\u5EFA\u6295\u5F71\u77E9\u9635\u4EFF\u5C04\u8FD1\u4F3C\u7684\u96C5\u53EF\u6BD4\u77E9\u9635\u3002\u5B83\u5C06\u88AB\u7528\u6765\u53D8\u63623D\u534F\u65B9\u5DEE\u77E9\u9635\uFF0C\u800C\u4E0D\u662F\u76F4\u63A5\u4F7F\u7528\u5B9E\u9645\u7684\u6295\u5F71\u77E9\u9635\uFF0C\u56E0\u4E3A\u4F7F\u7528\u5B9E\u9645\u6295\u5F71\u77E9\u9635\u8FDB\u884C\u53D8\u6362\u9700\u8981\u5305\u542B\u975E\u7EBF\u6027\u6210\u5206\uFF08\u900F\u89C6\u9664\u6CD5\uFF09\uFF0C\u8FD9\u4F1A\u5BFC\u81F4\u975E\u9AD8\u65AF\u7684\u7ED3\u679C\u3002\u8FD9\u91CC\u5047\u8BBE\u5F53\u524D\u7684\u6295\u5F71\u4E3A\u900F\u89C6\u6295\u5F71\u3002\n \n mat3 J;\n if (orthographicMode == 1) {\n J = transpose(mat3(orthoZoom, 0.0, 0.0,\n 0.0, orthoZoom, 0.0,\n 0.0, 0.0, 0.0));\n } else {\n float s = 1.0 / (viewCenter.z * viewCenter.z);\n J = mat3(\n focal.x / viewCenter.z, 0., -(focal.x * viewCenter.x) * s,\n 0., focal.y / viewCenter.z, -(focal.y * viewCenter.y) * s,\n 0., 0., 0.\n );\n }\n \n // Concatenate the projection approximation with the model-view transformation\n mat3 W = transpose(mat3(transformModelViewMatrix));\n mat3 T = W * J;\n\n // Transform the 3D covariance matrix (Vrk) to compute the 2D covariance matrix\n mat3 cov2Dm = transpose(T) * Vrk * T;\n \n \n cov2Dm[0][0] += 0.3;\n cov2Dm[1][1] += 0.3;\n float compensation = 1.0;\n \n \n \n vColor.a *= compensation;\n\n if (vColor.a < minAlpha) return;\n\n // We are interested in the upper-left 2x2 portion of the projected 3D covariance matrix because\n // we only care about the X and Y values. We want the X-diagonal, cov2Dm[0][0],\n // the Y-diagonal, cov2Dm[1][1], and the correlation between the two cov2Dm[0][1]. We don't\n // need cov2Dm[1][0] because it is a symetric matrix.\n vec3 cov2Dv = vec3(cov2Dm[0][0], cov2Dm[0][1], cov2Dm[1][1]);\n\n vec3 ndcCenter = clipCenter.xyz / clipCenter.w;\n\n float a = cov2Dv.x;\n float d = cov2Dv.z;\n float b = cov2Dv.y;\n float D = a * d - b * b;\n float trace = a + d; //\u8FFD\u8E2A\n float traceOver2 = 0.5 * trace;\n float term2 = sqrt(max(0.1f, traceOver2 * traceOver2 - D));\n float eigenValue1 = traceOver2 + term2; //\u7279\u5F81\u503CL1\n float eigenValue2 = traceOver2 - term2; //\u7279\u5F81\u503CL1 //max( traceOver2 - term2, 0.25 ); \n\n if (pointCloudModeEnabled == 1) {// each splat is rendered as a filled circle\n eigenValue1 = eigenValue2 = 0.2;\n }\n\n if (eigenValue2 <= 0.0 ) return;\n \n\n\n //add-----------\n float splatScale_ = splatScale;\n \n #if defined adaptive_size \n //splatScale_ /= getPointSizeAttenuation();\n float level = float(nodeLevelVNS.x);\n float maxLevel = 5.;\n float ratio1 = pow( ").concat(Potree.browser.urlHasValue('ratio1', true) || 1.4, ", /* max(0., */maxLevel - level/* ) */);\n splatScale_ *= ratio1;\n //vColor.a /= ratio1;\n #endif \n \n ////-----------\n\n\n\n\n vec2 eigenVector1 = normalize(vec2(b, eigenValue1 - a));\n // since the eigen vectors are orthogonal, we derive the second one from the first \u7531\u4E8E\u672C\u5F81\u5411\u91CF\u662F\u6B63\u4EA4\u7684\uFF0C\u56E0\u6B64\u6211\u4EEC\u4ECE\u7B2C\u4E00\u4E2A\u5BFC\u51FA\u7B2C\u4E8C\u4E2A\n vec2 eigenVector2 = vec2(eigenVector1.y, -eigenVector1.x);\n\n // We use sqrt(8) standard deviations\u504F\u5DEE instead of 3 to eliminate\u6D88\u9664 more of the splat with a very low opacity.\n vec2 basisVector1 = eigenVector1 * splatScale_ * min(sqrt8 * sqrt(eigenValue1), ").concat(parseInt(maxScreenSpaceSplatSize), ".0);\n vec2 basisVector2 = eigenVector2 * splatScale_ * min(sqrt8 * sqrt(eigenValue2), ").concat(parseInt(maxScreenSpaceSplatSize), ".0);\n\n\n//vec2 basisVector1 = min( sqrt( eigenValue1 * 2. ), 512. ) * eigenVector1 * splatScale_ ;\n//vec2 basisVector2 = min( sqrt( eigenValue2 * 2. ), 512. ) * eigenVector2 * splatScale_ ;\n\n vec2 ndcOffset = vec2(vPosition.x * basisVector1 + vPosition.y * basisVector2) / viewport * 2.0 * inverseFocalAdjustment;\n\n vec4 quadPos = vec4(ndcCenter.xy + ndcOffset, ndcCenter.z , 1.0);\n gl_Position = quadPos;\n\n // Scale the position data we send to the fragment shader\n vPosition *= sqrt8;\n }\n "); var fs = "\n precision highp float;\n #include \n // uniform lowp sampler2D gtable;\n uniform vec3 debugColor;\n\n varying vec4 vColor;\n varying vec2 vUv;\n\n varying vec2 vPosition;\n\n void main () {\n \n \n float A = dot(vPosition, vPosition);\n if (A > 8.0) discard; //position\u7684\u8303\u56F4\u534A\u5F84\u4E3A1\u3002\u6307\u4E00\u4E2Arectangle\u9762\u4E2D\u7684\u8303\u56F4\u3002\u692D\u5706\u5916\u7684\u5B8C\u5168\u900F\u660E\n vec3 color = vColor.rgb;\n \n \n float opacity = exp(-0.5 * A) * vColor.a; \n \n //vec2 uv = vPosition / sqrt(2); \n //float opacity = min( vColor.a, 1. ) * texture( gtable, vec2( 0.5 * A, color.a - 1. ) ).r; //\u5468\u56F4\u7684\u900F\u660E\u5EA6\u964D\u4F4E \u8FB9\u7F18\u5904\u6700\u9AD8\u900F\u660E\u5EA6\u4E3AMath.pow(Math.E, -4)= 0.0183 \n \n gl_FragColor = vec4(color.rgb /* * opacity */, opacity); \n \n \n }\n "; var visibleNodesTex = Potree.Utils.generateDataTexture(2048, 1, new Color(0xffffff)); visibleNodesTex.minFilter = NearestFilter; visibleNodesTex.magFilter = NearestFilter; var uniforms = { 'orthographicMode': { 'type': 'i', 'value': 0 }, 'focal': { 'type': 'v2', 'value': new Vector2() }, 'orthoZoom': { 'type': 'f', 'value': 1.0 }, 'inverseFocalAdjustment': { 'type': 'f', 'value': 1.0 }, 'viewport': { 'type': 'v2', 'value': new Vector2() }, visibleNodesTex: { 'type': 't', 'value': visibleNodesTex }, /* 'basisViewport': { 'type': 'v2', 'value': new THREE.Vector2() }, */ 'splatScale': { 'type': 'f', 'value': 1 //this.adaptiveSize ? 32 : 1 }, 'pointCloudModeEnabled': { 'type': 'i', 'value': 0 }, uOctreeSize: { 'type': 'f', value: 0 }, splatCount: { type: 'i', value: 0 }, gtable: { 'type': 't', value: this.createAlphaTex() } }; var material = new ShaderMaterial({ uniforms: uniforms, vertexShader: vs, fragmentShader: fs, transparent: true, alphaTest: 1.0, blending: NormalBlending, /* blending: THREE.CustomBlending, //需要预乘alpha blendSrc: THREE.OneFactor, // 源因子 = ONE blendDst: THREE.OneMinusSrcAlphaFactor, // 目标因子 = ONE_MINUS_SRC_ALPHA blendEquation: THREE.AddEquation, // 混合方程(默认加法) */ depthTest: false, depthWrite: true, side: DoubleSide }); return material; } update() { this.updateMesh(); } createAlphaTex() { //splat每个点的贴图,规定alpha走势 var A = function (A) { var g = atob(A), I = new Uint8Array(g.length); for (var _A = 0; _A < g.length; _A++) I[_A] = g.charCodeAt(_A); return I; }("8da+qZWEdWdbUUc/NzErJiIeGhcUEhAODAsJCAcGBgX03MWwnYx8bmFWTEM7NC4pJCAcGBUTEA4NCwoJBwcGBfbgy7ekk4N1aFxRSEA4MSsmIh0aFxQRDw0MCgkIBwYF9+TRvauaintuYldNRDw1LikkHxsYFRIQDgwKCQgHBgX56NbEsqGRgnVoXFJIQDgxKyYhHRkWExEODAsJCAcGBfrs3Mu5qZmKfG9jWE5FPDUuKSMfGxcUEQ8NCwoIBwYF++/g0MCwoJGDdWldU0lBOTIrJiEcGRUSEA0LCggHBgX88uXXx7iomYt9cGRZT0Y9NS8pIx4aFxMRDgwKCQcGBf306dzNvq+gkoR3al9USkE5MislIBwYFBEPDAoJBwYF/fft4dPFt6iajH5xZVpQRj02LygjHhkWEg8NCwkIBgX++PDl2cy+sKKUhnlsYVZLQjoyKyUgGxcTEA4LCQgGBf768+ne0sW3qZuOgHNnW1FHPjYuKCIdGBQRDgwKCAYF/vv17ePYzL+xpJaIe25iV0xDOjIrJB8aFhIPDAoIBwX//Pfw6N7SxrmsnpCDdmldUkg+Ni4nIRwXExANCggHBf/9+fPs49nNwbSnmYt+cWRZTkM6MiokHhkUEA0LCQcF//369vDo39TIvK+hlIZ5bF9UST82LiYgGhYRDgsJBwX//vz48+zk2tDEt6qdj4F0Z1tPRDoxKSIcFxMPDAkHBf/+/fr18Ong18zAs6aYinxvYlZKPzYtJR4ZFBAMCQcF///9+/jz7ebd08i8r6KUhnhqXVFFOzEoIRsVEQ0KBwX///78+vbx6+Pa0MW4q52PgXNlWEtANSwkHRcSDgoIBv///v37+PTv6OHXzcG1p5mLfG5gUkY7MCcfGRMOCwgG/////vz69/Pt5t7Vyr6xo5WGd2haTUA1KyIbFA8LCAb////+/fz59vHs5dzTyLuun5GBcmNURzovJR0WEAwIBv/////+/fv49fDr49vRxbmrnIx8bV1OQTQpIBgSDAkG//////7+/Pr49PDq4tnPw7anmIh3Z1dIOi4jGhMNCQb///////79/Pr39O/p4djNwbOklINyYVBBMycdFQ4JBv////////79/Pr39O/o4dfMv7GhkH5sWkk6LCAXDwoG/////////v79/Pr39O/o4dfLvq6ei3llU0IyJBkRCwb//////////v79/Pr39O/p4dfLvKyah3JeSzkpHRMLBv///////////v79/Pr49PDp4dfKu6qWgWxWQjAhFQwH//////////////7+/fv59fHq4tfKuqeSe2RNOCYYDQf///////////////7+/fz59vLs49jKuaWOdFtCLRsPBw=="); /* , g = this.gl , I = g.createTexture(); const B = g.TEXTURE_2D; return g.bindTexture(B, I), g.pixelStorei(g.UNPACK_ALIGNMENT, 1), g.texImage2D(B, 0, g.R8, 32, 32, 0, g.RED, g.UNSIGNED_BYTE, A), this.ig(B, g.LINEAR), g.bindTexture(B, null), I */ } switchAttrUpdateType(state) { this.splatAttrSameMemory = state; if (state == 1) { this.geometry.attributes.center.array = this.attrOrigin.center; this.geometry.attributes.color.array = this.attrOrigin.color; this.geometry.attributes.covRow1.array = this.attrOrigin.covRow1; this.geometry.attributes.covRow2.array = this.attrOrigin.covRow2; } } updateRenderIndexes(globalCenters, globalColors, globalCovs1, globalCovs2, nodes, renderSplatCount) { var geometry = this.geometry; var start = performance.now(); if (!this.splatEnableSwapOut || this.splatAttrSameMemory) { geometry.attributes.center.set(globalCenters); geometry.attributes.center.needsUpdate = true; geometry.attributes.color.set(globalColors); geometry.attributes.color.needsUpdate = true; geometry.attributes.covRow1.set(globalCovs1); geometry.attributes.covRow1.needsUpdate = true; geometry.attributes.covRow2.set(globalCovs2); geometry.attributes.covRow2.needsUpdate = true; if (this.adaptiveSize) { geometry.attributes.nodeLevelVNS.set(nodes); geometry.attributes.nodeLevelVNS.needsUpdate = true; } } else { //直接赋值速度快,需要双buffer否则会被擦写导致黑影。 splatEnableSwapOut要在sortworker init时就确定 geometry.attributes.center.array = globalCenters; geometry.attributes.center.needsUpdate = true; //不知为啥2025来再看并不会更流畅,可能这里时间减少了但gpu换buffer了需要时间吧? 另外流畅度更多取决于非sort时的,手机换buffer吃不消。 geometry.attributes.color.array = globalColors; geometry.attributes.color.needsUpdate = true; geometry.attributes.covRow1.array = globalCovs1; geometry.attributes.covRow1.needsUpdate = true; geometry.attributes.covRow2.array = globalCovs2; geometry.attributes.covRow2.needsUpdate = true; } if (renderSplatCount > 0) geometry.instanceCount = renderSplatCount; //console.log('updateRenderIndexes time:', performance.now() - start) //this.material.uniforms.splatCount.value = renderSplatCount if (this.adaptiveSize) { var vnt = this.material.uniforms.visibleNodesTex.value; var data = vnt.image.data; data.set(this.visibilityTextureData.data); vnt.needsUpdate = true; } } updateMaterial() { if (!this.pointclouds.length) return; var camera = viewer.mainViewport.camera; //this.material.uniforms.viewport.value.copy(viewer.mainViewport.resolution2); var viewport = viewer.mainViewport.resolution2; var focalMultiplier = camera.isOrthographicCamera ? 1.0 / window.devicePixelRatio : 1.0; var focalAdjustment = this.focalAdjustment * focalMultiplier; var focalLengthX = camera.projectionMatrix.elements[0] * 0.5 * viewport.x; var focalLengthY = camera.projectionMatrix.elements[5] * 0.5 * viewport.y; var inverseFocalAdjustment = 1.0 / focalAdjustment; this.material.uniforms.viewport.value.copy(viewport); this.material.uniforms.focal.value.set(focalLengthX * focalAdjustment, focalLengthY * focalAdjustment); this.material.uniforms.orthographicMode.value = camera.isOrthographicCamera ? 1 : 0; this.material.uniforms.orthoZoom.value = camera.zoom || 1.0; this.material.uniforms.inverseFocalAdjustment.value = inverseFocalAdjustment; if (this.dynamicMode) { for (var i = 0; i < this.scenes.length; i++) { this.material.uniforms.transforms.value[i].copy(this.getScene(i).transform); } } this.material.uniforms.uOctreeSize.value = this.pointclouds[0].pcoGeometry.boundingBox.getSize(new Vector3()).x; this.material.uniformsNeedUpdate = true; } switchSizeType(ifAdaptive) { Potree.Utils.addOrRemoveDefine(this.material, 'adaptive_size', ifAdaptive ? 'add' : 'remove'); } } class npyjs { constructor(opts) { var _opts$convertFloat; if (opts && !('convertFloat16' in opts)) { console.warn(["npyjs constructor now accepts {convertFloat16?: boolean}.", "For usage, go to https://github.com/jhuapl-boss/npyjs."].join(" ")); } this.convertFloat16 = (_opts$convertFloat = opts === null || opts === void 0 ? void 0 : opts.convertFloat16) !== null && _opts$convertFloat !== void 0 ? _opts$convertFloat : true; this.dtypes = { "> 15 & 0x1; var exponent = float16 >> 10 & 0x1f; var fraction = float16 & 0x3ff; // Handle special cases if (exponent === 0) { if (fraction === 0) { // Zero return sign ? -0 : 0; } // Denormalized number return (sign ? -1 : 1) * Math.pow(2, -14) * (fraction / 0x400); } else if (exponent === 0x1f) { if (fraction === 0) { // Infinity return sign ? -Infinity : Infinity; } // NaN return NaN; } // Normalized number return (sign ? -1 : 1) * Math.pow(2, exponent - 15) * (1 + fraction / 0x400); } parse(arrayBufferContents) { // const version = arrayBufferContents.slice(6, 8); // Uint8-encoded var headerLength = new DataView(arrayBufferContents.slice(8, 10)).getUint8(0); var offsetBytes = 10 + headerLength; var hcontents = new TextDecoder("utf-8").decode(new Uint8Array(arrayBufferContents.slice(10, 10 + headerLength))); var header = JSON.parse(hcontents.toLowerCase() // True -> true .replace(/'/g, '"').replace("(", "[").replace(/,*\),*/g, "]")); var shape = header.shape; var dtype = this.dtypes[header.descr]; if (!dtype) { console.error("Unsupported dtype: ".concat(header.descr)); return null; } var nums = new dtype.arrayConstructor(arrayBufferContents, offsetBytes); // Convert float16 to float32 if converter exists var data = dtype.converter ? dtype.converter.call(this, nums) : nums; return { dtype: dtype.name, data: data, shape, fortranOrder: header.fortran_order }; } async load(filename, callback, fetchArgs) { /* Loads an array from a stream of bytes. */ fetchArgs = fetchArgs || {}; var arrayBuf; // If filename is ArrayBuffer if (filename instanceof ArrayBuffer) { arrayBuf = filename; } // If filename is a file path else { var resp = await fetch$1(filename, _objectSpread2({}, fetchArgs)); arrayBuf = await resp.arrayBuffer(); } var result = this.parse(arrayBuf); if (callback) { return callback(result); } return result; } } var npyjs$1 = /*#__PURE__*/Object.freeze({ __proto__: null, 'default': npyjs }); /*! JSZip v3.10.1 - A JavaScript class for generating and reading zip files (c) 2009-2016 Stuart Knightley Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown. JSZip uses the library pako released under the MIT license : https://github.com/nodeca/pako/blob/main/LICENSE */ (function (f) { if (typeof exports === "object" && typeof module !== "undefined") { module.exports = f(); } else if (typeof define === "function" && define.amd) { define([], f); } else { var g; if (typeof window !== "undefined") { g = window; } else if (typeof global !== "undefined") { g = global; } else if (typeof self !== "undefined") { g = self; } else { g = this; } g.JSZip = f(); } })(function () { var define, module, exports; return function e(t, n, r) { function s(o, u) { if (!n[o]) { if (!t[o]) { var a = typeof require == "function" && require; if (!u && a) return a(o, !0); if (i) return i(o, !0); var f = new Error("Cannot find module '" + o + "'"); throw f.code = "MODULE_NOT_FOUND", f; } var l = n[o] = { exports: {} }; t[o][0].call(l.exports, function (e) { var n = t[o][1][e]; return s(n ? n : e); }, l, l.exports, e, t, n, r); } return n[o].exports; } var i = typeof require == "function" && require; for (var o = 0; o < r.length; o++) s(r[o]); return s; }({ 1: [function (require, module, exports) { "use strict"; var utils = require("./utils"); var support = require("./support"); // private property var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // public method for encoding exports.encode = function (input) { var output = []; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; var i = 0, len = input.length, remainingBytes = len; var isArray = utils.getTypeOf(input) !== "string"; while (i < input.length) { remainingBytes = len - i; if (!isArray) { chr1 = input.charCodeAt(i++); chr2 = i < len ? input.charCodeAt(i++) : 0; chr3 = i < len ? input.charCodeAt(i++) : 0; } else { chr1 = input[i++]; chr2 = i < len ? input[i++] : 0; chr3 = i < len ? input[i++] : 0; } enc1 = chr1 >> 2; enc2 = (chr1 & 3) << 4 | chr2 >> 4; enc3 = remainingBytes > 1 ? (chr2 & 15) << 2 | chr3 >> 6 : 64; enc4 = remainingBytes > 2 ? chr3 & 63 : 64; output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4)); } return output.join(""); }; // public method for decoding exports.decode = function (input) { var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0, resultIndex = 0; var dataUrlPrefix = "data:"; if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) { // This is a common error: people give a data url // (data:image/png;base64,iVBOR...) with a {base64: true} and // wonders why things don't work. // We can detect that the string input looks like a data url but we // *can't* be sure it is one: removing everything up to the comma would // be too dangerous. throw new Error("Invalid base64 input, it looks like a data url."); } input = input.replace(/[^A-Za-z0-9+/=]/g, ""); var totalLength = input.length * 3 / 4; if (input.charAt(input.length - 1) === _keyStr.charAt(64)) { totalLength--; } if (input.charAt(input.length - 2) === _keyStr.charAt(64)) { totalLength--; } if (totalLength % 1 !== 0) { // totalLength is not an integer, the length does not match a valid // base64 content. That can happen if: // - the input is not a base64 content // - the input is *almost* a base64 content, with a extra chars at the // beginning or at the end // - the input uses a base64 variant (base64url for example) throw new Error("Invalid base64 input, bad content length."); } var output; if (support.uint8array) { output = new Uint8Array(totalLength | 0); } else { output = new Array(totalLength | 0); } while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = enc1 << 2 | enc2 >> 4; chr2 = (enc2 & 15) << 4 | enc3 >> 2; chr3 = (enc3 & 3) << 6 | enc4; output[resultIndex++] = chr1; if (enc3 !== 64) { output[resultIndex++] = chr2; } if (enc4 !== 64) { output[resultIndex++] = chr3; } } return output; }; }, { "./support": 30, "./utils": 32 }], 2: [function (require, module, exports) { "use strict"; var external = require("./external"); var DataWorker = require("./stream/DataWorker"); var Crc32Probe = require("./stream/Crc32Probe"); var DataLengthProbe = require("./stream/DataLengthProbe"); /** * Represent a compressed object, with everything needed to decompress it. * @constructor * @param {number} compressedSize the size of the data compressed. * @param {number} uncompressedSize the size of the data after decompression. * @param {number} crc32 the crc32 of the decompressed file. * @param {object} compression the type of compression, see lib/compressions.js. * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data. */ function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) { this.compressedSize = compressedSize; this.uncompressedSize = uncompressedSize; this.crc32 = crc32; this.compression = compression; this.compressedContent = data; } CompressedObject.prototype = { /** * Create a worker to get the uncompressed content. * @return {GenericWorker} the worker. */ getContentWorker: function getContentWorker() { var worker = new DataWorker(external.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new DataLengthProbe("data_length")); var that = this; worker.on("end", function () { if (this.streamInfo["data_length"] !== that.uncompressedSize) { throw new Error("Bug : uncompressed data size mismatch"); } }); return worker; }, /** * Create a worker to get the compressed content. * @return {GenericWorker} the worker. */ getCompressedWorker: function getCompressedWorker() { return new DataWorker(external.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize", this.compressedSize).withStreamInfo("uncompressedSize", this.uncompressedSize).withStreamInfo("crc32", this.crc32).withStreamInfo("compression", this.compression); } }; /** * Chain the given worker with other workers to compress the content with the * given compression. * @param {GenericWorker} uncompressedWorker the worker to pipe. * @param {Object} compression the compression object. * @param {Object} compressionOptions the options to use when compressing. * @return {GenericWorker} the new worker compressing the content. */ CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) { return uncompressedWorker.pipe(new Crc32Probe()).pipe(new DataLengthProbe("uncompressedSize")).pipe(compression.compressWorker(compressionOptions)).pipe(new DataLengthProbe("compressedSize")).withStreamInfo("compression", compression); }; module.exports = CompressedObject; }, { "./external": 6, "./stream/Crc32Probe": 25, "./stream/DataLengthProbe": 26, "./stream/DataWorker": 27 }], 3: [function (require, module, exports) { "use strict"; var GenericWorker = require("./stream/GenericWorker"); exports.STORE = { magic: "\x00\x00", compressWorker: function compressWorker() { return new GenericWorker("STORE compression"); }, uncompressWorker: function uncompressWorker() { return new GenericWorker("STORE decompression"); } }; exports.DEFLATE = require("./flate"); }, { "./flate": 7, "./stream/GenericWorker": 28 }], 4: [function (require, module, exports) { "use strict"; var utils = require("./utils"); /** * The following functions come from pako, from pako/lib/zlib/crc32.js * released under the MIT license, see pako https://github.com/nodeca/pako/ */ // Use ordinary array, since untyped makes no boost here function makeTable() { var 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. var crcTable = makeTable(); function crc32(crc, buf, len, pos) { var t = crcTable, end = pos + len; crc = crc ^ -1; for (var i = pos; i < end; i++) { crc = crc >>> 8 ^ t[(crc ^ buf[i]) & 0xFF]; } return crc ^ -1; // >>> 0; } // That's all for the pako functions. /** * Compute the crc32 of a string. * This is almost the same as the function crc32, but for strings. Using the * same function for the two use cases leads to horrible performances. * @param {Number} crc the starting value of the crc. * @param {String} str the string to use. * @param {Number} len the length of the string. * @param {Number} pos the starting position for the crc32 computation. * @return {Number} the computed crc32. */ function crc32str(crc, str, len, pos) { var t = crcTable, end = pos + len; crc = crc ^ -1; for (var i = pos; i < end; i++) { crc = crc >>> 8 ^ t[(crc ^ str.charCodeAt(i)) & 0xFF]; } return crc ^ -1; // >>> 0; } module.exports = function crc32wrapper(input, crc) { if (typeof input === "undefined" || !input.length) { return 0; } var isArray = utils.getTypeOf(input) !== "string"; if (isArray) { return crc32(crc | 0, input, input.length, 0); } else { return crc32str(crc | 0, input, input.length, 0); } }; }, { "./utils": 32 }], 5: [function (require, module, exports) { "use strict"; exports.base64 = false; exports.binary = false; exports.dir = false; exports.createFolders = true; exports.date = null; exports.compression = null; exports.compressionOptions = null; exports.comment = null; exports.unixPermissions = null; exports.dosPermissions = null; }, {}], 6: [function (require, module, exports) { "use strict"; // load the global object first: // - it should be better integrated in the system (unhandledRejection in node) // - the environment may have a custom Promise implementation (see zone.js) var ES6Promise = null; if (typeof Promise !== "undefined") { ES6Promise = Promise; } else { ES6Promise = require("lie"); } /** * Let the user use/change some implementations. */ module.exports = { Promise: ES6Promise }; }, { "lie": 37 }], 7: [function (require, module, exports) { "use strict"; var USE_TYPEDARRAY = typeof Uint8Array !== "undefined" && typeof Uint16Array !== "undefined" && typeof Uint32Array !== "undefined"; var pako = require("pako"); var utils = require("./utils"); var GenericWorker = require("./stream/GenericWorker"); var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array"; exports.magic = "\x08\x00"; /** * Create a worker that uses pako to inflate/deflate. * @constructor * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate". * @param {Object} options the options to use when (de)compressing. */ function FlateWorker(action, options) { GenericWorker.call(this, "FlateWorker/" + action); this._pako = null; this._pakoAction = action; this._pakoOptions = options; // the `meta` object from the last chunk received // this allow this worker to pass around metadata this.meta = {}; } utils.inherits(FlateWorker, GenericWorker); /** * @see GenericWorker.processChunk */ FlateWorker.prototype.processChunk = function (chunk) { this.meta = chunk.meta; if (this._pako === null) { this._createPako(); } this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false); }; /** * @see GenericWorker.flush */ FlateWorker.prototype.flush = function () { GenericWorker.prototype.flush.call(this); if (this._pako === null) { this._createPako(); } this._pako.push([], true); }; /** * @see GenericWorker.cleanUp */ FlateWorker.prototype.cleanUp = function () { GenericWorker.prototype.cleanUp.call(this); this._pako = null; }; /** * Create the _pako object. * TODO: lazy-loading this object isn't the best solution but it's the * quickest. The best solution is to lazy-load the worker list. See also the * issue #446. */ FlateWorker.prototype._createPako = function () { this._pako = new pako[this._pakoAction]({ raw: true, level: this._pakoOptions.level || -1 // default compression }); var self = this; this._pako.onData = function (data) { self.push({ data: data, meta: self.meta }); }; }; exports.compressWorker = function (compressionOptions) { return new FlateWorker("Deflate", compressionOptions); }; exports.uncompressWorker = function () { return new FlateWorker("Inflate", {}); }; }, { "./stream/GenericWorker": 28, "./utils": 32, "pako": 38 }], 8: [function (require, module, exports) { "use strict"; var utils = require("../utils"); var GenericWorker = require("../stream/GenericWorker"); var utf8 = require("../utf8"); var crc32 = require("../crc32"); var signature = require("../signature"); /** * Transform an integer into a string in hexadecimal. * @private * @param {number} dec the number to convert. * @param {number} bytes the number of bytes to generate. * @returns {string} the result. */ var decToHex = function decToHex(dec, bytes) { var hex = "", i; for (i = 0; i < bytes; i++) { hex += String.fromCharCode(dec & 0xff); dec = dec >>> 8; } return hex; }; /** * Generate the UNIX part of the external file attributes. * @param {Object} unixPermissions the unix permissions or null. * @param {Boolean} isDir true if the entry is a directory, false otherwise. * @return {Number} a 32 bit integer. * * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute : * * TTTTsstrwxrwxrwx0000000000ADVSHR * ^^^^____________________________ file type, see zipinfo.c (UNX_*) * ^^^_________________________ setuid, setgid, sticky * ^^^^^^^^^________________ permissions * ^^^^^^^^^^______ not used ? * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only */ var generateUnixExternalFileAttr = function generateUnixExternalFileAttr(unixPermissions, isDir) { var result = unixPermissions; if (!unixPermissions) { // I can't use octal values in strict mode, hence the hexa. // 040775 => 0x41fd // 0100664 => 0x81b4 result = isDir ? 0x41fd : 0x81b4; } return (result & 0xFFFF) << 16; }; /** * Generate the DOS part of the external file attributes. * @param {Object} dosPermissions the dos permissions or null. * @param {Boolean} isDir true if the entry is a directory, false otherwise. * @return {Number} a 32 bit integer. * * Bit 0 Read-Only * Bit 1 Hidden * Bit 2 System * Bit 3 Volume Label * Bit 4 Directory * Bit 5 Archive */ var generateDosExternalFileAttr = function generateDosExternalFileAttr(dosPermissions) { // the dir flag is already set for compatibility return (dosPermissions || 0) & 0x3F; }; /** * Generate the various parts used in the construction of the final zip file. * @param {Object} streamInfo the hash with information about the compressed file. * @param {Boolean} streamedContent is the content streamed ? * @param {Boolean} streamingEnded is the stream finished ? * @param {number} offset the current offset from the start of the zip file. * @param {String} platform let's pretend we are this platform (change platform dependents fields) * @param {Function} encodeFileName the function to encode the file name / comment. * @return {Object} the zip parts. */ var generateZipParts = function generateZipParts(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) { var file = streamInfo["file"], compression = streamInfo["compression"], useCustomEncoding = encodeFileName !== utf8.utf8encode, encodedFileName = utils.transformTo("string", encodeFileName(file.name)), utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)), comment = file.comment, encodedComment = utils.transformTo("string", encodeFileName(comment)), utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)), useUTF8ForFileName = utfEncodedFileName.length !== file.name.length, useUTF8ForComment = utfEncodedComment.length !== comment.length, dosTime, dosDate, extraFields = "", unicodePathExtraField = "", unicodeCommentExtraField = "", dir = file.dir, date = file.date; var dataInfo = { crc32: 0, compressedSize: 0, uncompressedSize: 0 }; // if the content is streamed, the sizes/crc32 are only available AFTER // the end of the stream. if (!streamedContent || streamingEnded) { dataInfo.crc32 = streamInfo["crc32"]; dataInfo.compressedSize = streamInfo["compressedSize"]; dataInfo.uncompressedSize = streamInfo["uncompressedSize"]; } var bitflag = 0; if (streamedContent) { // Bit 3: the sizes/crc32 are set to zero in the local header. // The correct values are put in the data descriptor immediately // following the compressed data. bitflag |= 0x0008; } if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) { // Bit 11: Language encoding flag (EFS). bitflag |= 0x0800; } var extFileAttr = 0; var versionMadeBy = 0; if (dir) { // dos or unix, we set the dos dir flag extFileAttr |= 0x00010; } if (platform === "UNIX") { versionMadeBy = 0x031E; // UNIX, version 3.0 extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir); } else { // DOS or other, fallback to DOS versionMadeBy = 0x0014; // DOS, version 2.0 extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir); } // date // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html dosTime = date.getUTCHours(); dosTime = dosTime << 6; dosTime = dosTime | date.getUTCMinutes(); dosTime = dosTime << 5; dosTime = dosTime | date.getUTCSeconds() / 2; dosDate = date.getUTCFullYear() - 1980; dosDate = dosDate << 4; dosDate = dosDate | date.getUTCMonth() + 1; dosDate = dosDate << 5; dosDate = dosDate | date.getUTCDate(); if (useUTF8ForFileName) { // set the unicode path extra field. unzip needs at least one extra // field to correctly handle unicode path, so using the path is as good // as any other information. This could improve the situation with // other archive managers too. // This field is usually used without the utf8 flag, with a non // unicode path in the header (winrar, winzip). This helps (a bit) // with the messy Windows' default compressed folders feature but // breaks on p7zip which doesn't seek the unicode path extra field. // So for now, UTF-8 everywhere ! unicodePathExtraField = // Version decToHex(1, 1) + // NameCRC32 decToHex(crc32(encodedFileName), 4) + // UnicodeName utfEncodedFileName; extraFields += // Info-ZIP Unicode Path Extra Field "\x75\x70" + // size decToHex(unicodePathExtraField.length, 2) + // content unicodePathExtraField; } if (useUTF8ForComment) { unicodeCommentExtraField = // Version decToHex(1, 1) + // CommentCRC32 decToHex(crc32(encodedComment), 4) + // UnicodeName utfEncodedComment; extraFields += // Info-ZIP Unicode Path Extra Field "\x75\x63" + // size decToHex(unicodeCommentExtraField.length, 2) + // content unicodeCommentExtraField; } var header = ""; // version needed to extract header += "\x0A\x00"; // general purpose bit flag header += decToHex(bitflag, 2); // compression method header += compression.magic; // last mod file time header += decToHex(dosTime, 2); // last mod file date header += decToHex(dosDate, 2); // crc-32 header += decToHex(dataInfo.crc32, 4); // compressed size header += decToHex(dataInfo.compressedSize, 4); // uncompressed size header += decToHex(dataInfo.uncompressedSize, 4); // file name length header += decToHex(encodedFileName.length, 2); // extra field length header += decToHex(extraFields.length, 2); var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields; var dirRecord = signature.CENTRAL_FILE_HEADER + // version made by (00: DOS) decToHex(versionMadeBy, 2) + // file header (common to file and central directory) header + // file comment length decToHex(encodedComment.length, 2) + // disk number start "\x00\x00" + // internal file attributes TODO "\x00\x00" + // external file attributes decToHex(extFileAttr, 4) + // relative offset of local header decToHex(offset, 4) + // file name encodedFileName + // extra field extraFields + // file comment encodedComment; return { fileRecord: fileRecord, dirRecord: dirRecord }; }; /** * Generate the EOCD record. * @param {Number} entriesCount the number of entries in the zip file. * @param {Number} centralDirLength the length (in bytes) of the central dir. * @param {Number} localDirLength the length (in bytes) of the local dir. * @param {String} comment the zip file comment as a binary string. * @param {Function} encodeFileName the function to encode the comment. * @return {String} the EOCD record. */ var generateCentralDirectoryEnd = function generateCentralDirectoryEnd(entriesCount, centralDirLength, localDirLength, comment, encodeFileName) { var dirEnd = ""; var encodedComment = utils.transformTo("string", encodeFileName(comment)); // end of central dir signature dirEnd = signature.CENTRAL_DIRECTORY_END + // number of this disk "\x00\x00" + // number of the disk with the start of the central directory "\x00\x00" + // total number of entries in the central directory on this disk decToHex(entriesCount, 2) + // total number of entries in the central directory decToHex(entriesCount, 2) + // size of the central directory 4 bytes decToHex(centralDirLength, 4) + // offset of start of central directory with respect to the starting disk number decToHex(localDirLength, 4) + // .ZIP file comment length decToHex(encodedComment.length, 2) + // .ZIP file comment encodedComment; return dirEnd; }; /** * Generate data descriptors for a file entry. * @param {Object} streamInfo the hash generated by a worker, containing information * on the file entry. * @return {String} the data descriptors. */ var generateDataDescriptors = function generateDataDescriptors(streamInfo) { var descriptor = ""; descriptor = signature.DATA_DESCRIPTOR + // crc-32 4 bytes decToHex(streamInfo["crc32"], 4) + // compressed size 4 bytes decToHex(streamInfo["compressedSize"], 4) + // uncompressed size 4 bytes decToHex(streamInfo["uncompressedSize"], 4); return descriptor; }; /** * A worker to concatenate other workers to create a zip file. * @param {Boolean} streamFiles `true` to stream the content of the files, * `false` to accumulate it. * @param {String} comment the comment to use. * @param {String} platform the platform to use, "UNIX" or "DOS". * @param {Function} encodeFileName the function to encode file names and comments. */ function ZipFileWorker(streamFiles, comment, platform, encodeFileName) { GenericWorker.call(this, "ZipFileWorker"); // The number of bytes written so far. This doesn't count accumulated chunks. this.bytesWritten = 0; // The comment of the zip file this.zipComment = comment; // The platform "generating" the zip file. this.zipPlatform = platform; // the function to encode file names and comments. this.encodeFileName = encodeFileName; // Should we stream the content of the files ? this.streamFiles = streamFiles; // If `streamFiles` is false, we will need to accumulate the content of the // files to calculate sizes / crc32 (and write them *before* the content). // This boolean indicates if we are accumulating chunks (it will change a lot // during the lifetime of this worker). this.accumulate = false; // The buffer receiving chunks when accumulating content. this.contentBuffer = []; // The list of generated directory records. this.dirRecords = []; // The offset (in bytes) from the beginning of the zip file for the current source. this.currentSourceOffset = 0; // The total number of entries in this zip file. this.entriesCount = 0; // the name of the file currently being added, null when handling the end of the zip file. // Used for the emitted metadata. this.currentFile = null; this._sources = []; } utils.inherits(ZipFileWorker, GenericWorker); /** * @see GenericWorker.push */ ZipFileWorker.prototype.push = function (chunk) { var currentFilePercent = chunk.meta.percent || 0; var entriesCount = this.entriesCount; var remainingFiles = this._sources.length; if (this.accumulate) { this.contentBuffer.push(chunk); } else { this.bytesWritten += chunk.data.length; GenericWorker.prototype.push.call(this, { data: chunk.data, meta: { currentFile: this.currentFile, percent: entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100 } }); } }; /** * The worker started a new source (an other worker). * @param {Object} streamInfo the streamInfo object from the new source. */ ZipFileWorker.prototype.openedSource = function (streamInfo) { this.currentSourceOffset = this.bytesWritten; this.currentFile = streamInfo["file"].name; var streamedContent = this.streamFiles && !streamInfo["file"].dir; // don't stream folders (because they don't have any content) if (streamedContent) { var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); this.push({ data: record.fileRecord, meta: { percent: 0 } }); } else { // we need to wait for the whole file before pushing anything this.accumulate = true; } }; /** * The worker finished a source (an other worker). * @param {Object} streamInfo the streamInfo object from the finished source. */ ZipFileWorker.prototype.closedSource = function (streamInfo) { this.accumulate = false; var streamedContent = this.streamFiles && !streamInfo["file"].dir; var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); this.dirRecords.push(record.dirRecord); if (streamedContent) { // after the streamed file, we put data descriptors this.push({ data: generateDataDescriptors(streamInfo), meta: { percent: 100 } }); } else { // the content wasn't streamed, we need to push everything now // first the file record, then the content this.push({ data: record.fileRecord, meta: { percent: 0 } }); while (this.contentBuffer.length) { this.push(this.contentBuffer.shift()); } } this.currentFile = null; }; /** * @see GenericWorker.flush */ ZipFileWorker.prototype.flush = function () { var localDirLength = this.bytesWritten; for (var i = 0; i < this.dirRecords.length; i++) { this.push({ data: this.dirRecords[i], meta: { percent: 100 } }); } var centralDirLength = this.bytesWritten - localDirLength; var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName); this.push({ data: dirEnd, meta: { percent: 100 } }); }; /** * Prepare the next source to be read. */ ZipFileWorker.prototype.prepareNextSource = function () { this.previous = this._sources.shift(); this.openedSource(this.previous.streamInfo); if (this.isPaused) { this.previous.pause(); } else { this.previous.resume(); } }; /** * @see GenericWorker.registerPrevious */ ZipFileWorker.prototype.registerPrevious = function (previous) { this._sources.push(previous); var self = this; previous.on("data", function (chunk) { self.processChunk(chunk); }); previous.on("end", function () { self.closedSource(self.previous.streamInfo); if (self._sources.length) { self.prepareNextSource(); } else { self.end(); } }); previous.on("error", function (e) { self.error(e); }); return this; }; /** * @see GenericWorker.resume */ ZipFileWorker.prototype.resume = function () { if (!GenericWorker.prototype.resume.call(this)) { return false; } if (!this.previous && this._sources.length) { this.prepareNextSource(); return true; } if (!this.previous && !this._sources.length && !this.generatedError) { this.end(); return true; } }; /** * @see GenericWorker.error */ ZipFileWorker.prototype.error = function (e) { var sources = this._sources; if (!GenericWorker.prototype.error.call(this, e)) { return false; } for (var i = 0; i < sources.length; i++) { try { sources[i].error(e); } catch (e) { // the `error` exploded, nothing to do } } return true; }; /** * @see GenericWorker.lock */ ZipFileWorker.prototype.lock = function () { GenericWorker.prototype.lock.call(this); var sources = this._sources; for (var i = 0; i < sources.length; i++) { sources[i].lock(); } }; module.exports = ZipFileWorker; }, { "../crc32": 4, "../signature": 23, "../stream/GenericWorker": 28, "../utf8": 31, "../utils": 32 }], 9: [function (require, module, exports) { "use strict"; var compressions = require("../compressions"); var ZipFileWorker = require("./ZipFileWorker"); /** * Find the compression to use. * @param {String} fileCompression the compression defined at the file level, if any. * @param {String} zipCompression the compression defined at the load() level. * @return {Object} the compression object to use. */ var getCompression = function getCompression(fileCompression, zipCompression) { var compressionName = fileCompression || zipCompression; var compression = compressions[compressionName]; if (!compression) { throw new Error(compressionName + " is not a valid compression method !"); } return compression; }; /** * Create a worker to generate a zip file. * @param {JSZip} zip the JSZip instance at the right root level. * @param {Object} options to generate the zip file. * @param {String} comment the comment to use. */ exports.generateWorker = function (zip, options, comment) { var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName); var entriesCount = 0; try { zip.forEach(function (relativePath, file) { entriesCount++; var compression = getCompression(file.options.compression, options.compression); var compressionOptions = file.options.compressionOptions || options.compressionOptions || {}; var dir = file.dir, date = file.date; file._compressWorker(compression, compressionOptions).withStreamInfo("file", { name: relativePath, dir: dir, date: date, comment: file.comment || "", unixPermissions: file.unixPermissions, dosPermissions: file.dosPermissions }).pipe(zipFileWorker); }); zipFileWorker.entriesCount = entriesCount; } catch (e) { zipFileWorker.error(e); } return zipFileWorker; }; }, { "../compressions": 3, "./ZipFileWorker": 8 }], 10: [function (require, module, exports) { "use strict"; /** * Representation a of zip file in js * @constructor */ function JSZip() { // if this constructor is used without `new`, it adds `new` before itself: if (!(this instanceof JSZip)) { return new JSZip(); } if (arguments.length) { throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide."); } // object containing the files : // { // "folder/" : {...}, // "folder/data.txt" : {...} // } // NOTE: we use a null prototype because we do not // want filenames like "toString" coming from a zip file // to overwrite methods and attributes in a normal Object. this.files = Object.create(null); this.comment = null; // Where we are in the hierarchy this.root = ""; this.clone = function () { var newObj = new JSZip(); for (var i in this) { if (typeof this[i] !== "function") { newObj[i] = this[i]; } } return newObj; }; } JSZip.prototype = require("./object"); JSZip.prototype.loadAsync = require("./load"); JSZip.support = require("./support"); JSZip.defaults = require("./defaults"); // TODO find a better way to handle this version, // a require('package.json').version doesn't work with webpack, see #327 JSZip.version = "3.10.1"; JSZip.loadAsync = function (content, options) { return new JSZip().loadAsync(content, options); }; JSZip.external = require("./external"); module.exports = JSZip; }, { "./defaults": 5, "./external": 6, "./load": 11, "./object": 15, "./support": 30 }], 11: [function (require, module, exports) { "use strict"; var utils = require("./utils"); var external = require("./external"); var utf8 = require("./utf8"); var ZipEntries = require("./zipEntries"); var Crc32Probe = require("./stream/Crc32Probe"); var nodejsUtils = require("./nodejsUtils"); /** * Check the CRC32 of an entry. * @param {ZipEntry} zipEntry the zip entry to check. * @return {Promise} the result. */ function checkEntryCRC32(zipEntry) { return new external.Promise(function (resolve, reject) { var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe()); worker.on("error", function (e) { reject(e); }).on("end", function () { if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) { reject(new Error("Corrupted zip : CRC32 mismatch")); } else { resolve(); } }).resume(); }); } module.exports = function (data, options) { var zip = this; options = utils.extend(options || {}, { base64: false, checkCRC32: false, optimizedBinaryString: false, createFolders: false, decodeFileName: utf8.utf8decode }); if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file.")); } return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64).then(function (data) { var zipEntries = new ZipEntries(options); zipEntries.load(data); return zipEntries; }).then(function checkCRC32(zipEntries) { var promises = [external.Promise.resolve(zipEntries)]; var files = zipEntries.files; if (options.checkCRC32) { for (var i = 0; i < files.length; i++) { promises.push(checkEntryCRC32(files[i])); } } return external.Promise.all(promises); }).then(function addFiles(results) { var zipEntries = results.shift(); var files = zipEntries.files; for (var i = 0; i < files.length; i++) { var input = files[i]; var unsafeName = input.fileNameStr; var safeName = utils.resolve(input.fileNameStr); zip.file(safeName, input.decompressed, { binary: true, optimizedBinaryString: true, date: input.date, dir: input.dir, comment: input.fileCommentStr.length ? input.fileCommentStr : null, unixPermissions: input.unixPermissions, dosPermissions: input.dosPermissions, createFolders: options.createFolders }); if (!input.dir) { zip.file(safeName).unsafeOriginalName = unsafeName; } } if (zipEntries.zipComment.length) { zip.comment = zipEntries.zipComment; } return zip; }); }; }, { "./external": 6, "./nodejsUtils": 14, "./stream/Crc32Probe": 25, "./utf8": 31, "./utils": 32, "./zipEntries": 33 }], 12: [function (require, module, exports) { "use strict"; var utils = require("../utils"); var GenericWorker = require("../stream/GenericWorker"); /** * A worker that use a nodejs stream as source. * @constructor * @param {String} filename the name of the file entry for this stream. * @param {Readable} stream the nodejs stream. */ function NodejsStreamInputAdapter(filename, stream) { GenericWorker.call(this, "Nodejs stream input adapter for " + filename); this._upstreamEnded = false; this._bindStream(stream); } utils.inherits(NodejsStreamInputAdapter, GenericWorker); /** * Prepare the stream and bind the callbacks on it. * Do this ASAP on node 0.10 ! A lazy binding doesn't always work. * @param {Stream} stream the nodejs stream to use. */ NodejsStreamInputAdapter.prototype._bindStream = function (stream) { var self = this; this._stream = stream; stream.pause(); stream.on("data", function (chunk) { self.push({ data: chunk, meta: { percent: 0 } }); }).on("error", function (e) { if (self.isPaused) { this.generatedError = e; } else { self.error(e); } }).on("end", function () { if (self.isPaused) { self._upstreamEnded = true; } else { self.end(); } }); }; NodejsStreamInputAdapter.prototype.pause = function () { if (!GenericWorker.prototype.pause.call(this)) { return false; } this._stream.pause(); return true; }; NodejsStreamInputAdapter.prototype.resume = function () { if (!GenericWorker.prototype.resume.call(this)) { return false; } if (this._upstreamEnded) { this.end(); } else { this._stream.resume(); } return true; }; module.exports = NodejsStreamInputAdapter; }, { "../stream/GenericWorker": 28, "../utils": 32 }], 13: [function (require, module, exports) { "use strict"; var Readable = require("readable-stream").Readable; var utils = require("../utils"); utils.inherits(NodejsStreamOutputAdapter, Readable); /** * A nodejs stream using a worker as source. * @see the SourceWrapper in http://nodejs.org/api/stream.html * @constructor * @param {StreamHelper} helper the helper wrapping the worker * @param {Object} options the nodejs stream options * @param {Function} updateCb the update callback. */ function NodejsStreamOutputAdapter(helper, options, updateCb) { Readable.call(this, options); this._helper = helper; var self = this; helper.on("data", function (data, meta) { if (!self.push(data)) { self._helper.pause(); } if (updateCb) { updateCb(meta); } }).on("error", function (e) { self.emit("error", e); }).on("end", function () { self.push(null); }); } NodejsStreamOutputAdapter.prototype._read = function () { this._helper.resume(); }; module.exports = NodejsStreamOutputAdapter; }, { "../utils": 32, "readable-stream": 16 }], 14: [function (require, module, exports) { "use strict"; module.exports = { /** * True if this is running in Nodejs, will be undefined in a browser. * In a browser, browserify won't include this file and the whole module * will be resolved an empty object. */ isNode: typeof Buffer !== "undefined", /** * Create a new nodejs Buffer from an existing content. * @param {Object} data the data to pass to the constructor. * @param {String} encoding the encoding to use. * @return {Buffer} a new Buffer. */ newBufferFrom: function newBufferFrom(data, encoding) { if (Buffer.from && Buffer.from !== Uint8Array.from) { return Buffer.from(data, encoding); } else { if (typeof data === "number") { // Safeguard for old Node.js versions. On newer versions, // Buffer.from(number) / Buffer(number, encoding) already throw. throw new Error("The \"data\" argument must not be a number"); } return new Buffer(data, encoding); } }, /** * Create a new nodejs Buffer with the specified size. * @param {Integer} size the size of the buffer. * @return {Buffer} a new Buffer. */ allocBuffer: function allocBuffer(size) { if (Buffer.alloc) { return Buffer.alloc(size); } else { var buf = new Buffer(size); buf.fill(0); return buf; } }, /** * Find out if an object is a Buffer. * @param {Object} b the object to test. * @return {Boolean} true if the object is a Buffer, false otherwise. */ isBuffer: function isBuffer(b) { return Buffer.isBuffer(b); }, isStream: function isStream(obj) { return obj && typeof obj.on === "function" && typeof obj.pause === "function" && typeof obj.resume === "function"; } }; }, {}], 15: [function (require, module, exports) { "use strict"; var utf8 = require("./utf8"); var utils = require("./utils"); var GenericWorker = require("./stream/GenericWorker"); var StreamHelper = require("./stream/StreamHelper"); var defaults = require("./defaults"); var CompressedObject = require("./compressedObject"); var ZipObject = require("./zipObject"); var generate = require("./generate"); var nodejsUtils = require("./nodejsUtils"); var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter"); /** * Add a file in the current folder. * @private * @param {string} name the name of the file * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file * @param {Object} originalOptions the options of the file * @return {Object} the new file. */ var fileAdd = function fileAdd(name, data, originalOptions) { // be sure sub folders exist var dataType = utils.getTypeOf(data), parent; /* * Correct options. */ var o = utils.extend(originalOptions || {}, defaults); o.date = o.date || new Date(); if (o.compression !== null) { o.compression = o.compression.toUpperCase(); } if (typeof o.unixPermissions === "string") { o.unixPermissions = parseInt(o.unixPermissions, 8); } // UNX_IFDIR 0040000 see zipinfo.c if (o.unixPermissions && o.unixPermissions & 0x4000) { o.dir = true; } // Bit 4 Directory if (o.dosPermissions && o.dosPermissions & 0x0010) { o.dir = true; } if (o.dir) { name = forceTrailingSlash(name); } if (o.createFolders && (parent = parentFolder(name))) { folderAdd.call(this, parent, true); } var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false; if (!originalOptions || typeof originalOptions.binary === "undefined") { o.binary = !isUnicodeString; } var isCompressedEmpty = data instanceof CompressedObject && data.uncompressedSize === 0; if (isCompressedEmpty || o.dir || !data || data.length === 0) { o.base64 = false; o.binary = true; data = ""; o.compression = "STORE"; dataType = "string"; } /* * Convert content to fit. */ var zipObjectContent = null; if (data instanceof CompressedObject || data instanceof GenericWorker) { zipObjectContent = data; } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { zipObjectContent = new NodejsStreamInputAdapter(name, data); } else { zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64); } var object = new ZipObject(name, zipObjectContent, o); this.files[name] = object; /* TODO: we can't throw an exception because we have async promises (we can have a promise of a Date() for example) but returning a promise is useless because file(name, data) returns the JSZip object for chaining. Should we break that to allow the user to catch the error ? return external.Promise.resolve(zipObjectContent) .then(function () { return object; }); */ }; /** * Find the parent folder of the path. * @private * @param {string} path the path to use * @return {string} the parent folder, or "" */ var parentFolder = function parentFolder(path) { if (path.slice(-1) === "/") { path = path.substring(0, path.length - 1); } var lastSlash = path.lastIndexOf("/"); return lastSlash > 0 ? path.substring(0, lastSlash) : ""; }; /** * Returns the path with a slash at the end. * @private * @param {String} path the path to check. * @return {String} the path with a trailing slash. */ var forceTrailingSlash = function forceTrailingSlash(path) { // Check the name ends with a / if (path.slice(-1) !== "/") { path += "/"; // IE doesn't like substr(-1) } return path; }; /** * Add a (sub) folder in the current folder. * @private * @param {string} name the folder's name * @param {boolean=} [createFolders] If true, automatically create sub * folders. Defaults to false. * @return {Object} the new folder. */ var folderAdd = function folderAdd(name, createFolders) { createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders; name = forceTrailingSlash(name); // Does this folder already exist? if (!this.files[name]) { fileAdd.call(this, name, null, { dir: true, createFolders: createFolders }); } return this.files[name]; }; /** * Cross-window, cross-Node-context regular expression detection * @param {Object} object Anything * @return {Boolean} true if the object is a regular expression, * false otherwise */ function isRegExp(object) { return Object.prototype.toString.call(object) === "[object RegExp]"; } // return the actual prototype of JSZip var out = { /** * @see loadAsync */ load: function load() { throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); }, /** * Call a callback function for each entry at this folder level. * @param {Function} cb the callback function: * function (relativePath, file) {...} * It takes 2 arguments : the relative path and the file. */ forEach: function forEach(cb) { var filename, relativePath, file; // ignore warning about unwanted properties because this.files is a null prototype object /* eslint-disable-next-line guard-for-in */ for (filename in this.files) { file = this.files[filename]; relativePath = filename.slice(this.root.length, filename.length); if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn... } } }, /** * Filter nested files/folders with the specified function. * @param {Function} search the predicate to use : * function (relativePath, file) {...} * It takes 2 arguments : the relative path and the file. * @return {Array} An array of matching elements. */ filter: function filter(search) { var result = []; this.forEach(function (relativePath, entry) { if (search(relativePath, entry)) { // the file matches the function result.push(entry); } }); return result; }, /** * Add a file to the zip file, or search a file. * @param {string|RegExp} name The name of the file to add (if data is defined), * the name of the file to find (if no data) or a regex to match files. * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded * @param {Object} o File options * @return {JSZip|Object|Array} this JSZip object (when adding a file), * a file (when searching by string) or an array of files (when searching by regex). */ file: function file(name, data, o) { if (arguments.length === 1) { if (isRegExp(name)) { var regexp = name; return this.filter(function (relativePath, file) { return !file.dir && regexp.test(relativePath); }); } else { // text var obj = this.files[this.root + name]; if (obj && !obj.dir) { return obj; } else { return null; } } } else { // more than one argument : we have data ! name = this.root + name; fileAdd.call(this, name, data, o); } return this; }, /** * Add a directory to the zip file, or search. * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders. * @return {JSZip} an object with the new directory as the root, or an array containing matching folders. */ folder: function folder(arg) { if (!arg) { return this; } if (isRegExp(arg)) { return this.filter(function (relativePath, file) { return file.dir && arg.test(relativePath); }); } // else, name is a new folder var name = this.root + arg; var newFolder = folderAdd.call(this, name); // Allow chaining by returning a new object with this folder as the root var ret = this.clone(); ret.root = newFolder.name; return ret; }, /** * Delete a file, or a directory and all sub-files, from the zip * @param {string} name the name of the file to delete * @return {JSZip} this JSZip object */ remove: function remove(name) { name = this.root + name; var file = this.files[name]; if (!file) { // Look for any folders if (name.slice(-1) !== "/") { name += "/"; } file = this.files[name]; } if (file && !file.dir) { // file delete this.files[name]; } else { // maybe a folder, delete recursively var kids = this.filter(function (relativePath, file) { return file.name.slice(0, name.length) === name; }); for (var i = 0; i < kids.length; i++) { delete this.files[kids[i].name]; } } return this; }, /** * @deprecated This method has been removed in JSZip 3.0, please check the upgrade guide. */ generate: function generate() { throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); }, /** * Generate the complete zip file as an internal stream. * @param {Object} options the options to generate the zip file : * - compression, "STORE" by default. * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. * @return {StreamHelper} the streamed zip file. */ generateInternalStream: function generateInternalStream(options) { var worker, opts = {}; try { opts = utils.extend(options || {}, { streamFiles: false, compression: "STORE", compressionOptions: null, type: "", platform: "DOS", comment: null, mimeType: "application/zip", encodeFileName: utf8.utf8encode }); opts.type = opts.type.toLowerCase(); opts.compression = opts.compression.toUpperCase(); // "binarystring" is preferred but the internals use "string". if (opts.type === "binarystring") { opts.type = "string"; } if (!opts.type) { throw new Error("No output type specified."); } utils.checkSupport(opts.type); // accept nodejs `process.platform` if (opts.platform === "darwin" || opts.platform === "freebsd" || opts.platform === "linux" || opts.platform === "sunos") { opts.platform = "UNIX"; } if (opts.platform === "win32") { opts.platform = "DOS"; } var comment = opts.comment || this.comment || ""; worker = generate.generateWorker(this, opts, comment); } catch (e) { worker = new GenericWorker("error"); worker.error(e); } return new StreamHelper(worker, opts.type || "string", opts.mimeType); }, /** * Generate the complete zip file asynchronously. * @see generateInternalStream */ generateAsync: function generateAsync(options, onUpdate) { return this.generateInternalStream(options).accumulate(onUpdate); }, /** * Generate the complete zip file asynchronously. * @see generateInternalStream */ generateNodeStream: function generateNodeStream(options, onUpdate) { options = options || {}; if (!options.type) { options.type = "nodebuffer"; } return this.generateInternalStream(options).toNodejsStream(onUpdate); } }; module.exports = out; }, { "./compressedObject": 2, "./defaults": 5, "./generate": 9, "./nodejs/NodejsStreamInputAdapter": 12, "./nodejsUtils": 14, "./stream/GenericWorker": 28, "./stream/StreamHelper": 29, "./utf8": 31, "./utils": 32, "./zipObject": 35 }], 16: [function (require, module, exports) { "use strict"; /* * This file is used by module bundlers (browserify/webpack/etc) when * including a stream implementation. We use "readable-stream" to get a * consistent behavior between nodejs versions but bundlers often have a shim * for "stream". Using this shim greatly improve the compatibility and greatly * reduce the final size of the bundle (only one stream implementation, not * two). */ module.exports = require("stream"); }, { "stream": undefined }], 17: [function (require, module, exports) { "use strict"; var DataReader = require("./DataReader"); var utils = require("../utils"); function ArrayReader(data) { DataReader.call(this, data); for (var i = 0; i < this.data.length; i++) { data[i] = data[i] & 0xFF; } } utils.inherits(ArrayReader, DataReader); /** * @see DataReader.byteAt */ ArrayReader.prototype.byteAt = function (i) { return this.data[this.zero + i]; }; /** * @see DataReader.lastIndexOfSignature */ ArrayReader.prototype.lastIndexOfSignature = function (sig) { var sig0 = sig.charCodeAt(0), sig1 = sig.charCodeAt(1), sig2 = sig.charCodeAt(2), sig3 = sig.charCodeAt(3); for (var i = this.length - 4; i >= 0; --i) { if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) { return i - this.zero; } } return -1; }; /** * @see DataReader.readAndCheckSignature */ ArrayReader.prototype.readAndCheckSignature = function (sig) { var sig0 = sig.charCodeAt(0), sig1 = sig.charCodeAt(1), sig2 = sig.charCodeAt(2), sig3 = sig.charCodeAt(3), data = this.readData(4); return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3]; }; /** * @see DataReader.readData */ ArrayReader.prototype.readData = function (size) { this.checkOffset(size); if (size === 0) { return []; } var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); this.index += size; return result; }; module.exports = ArrayReader; }, { "../utils": 32, "./DataReader": 18 }], 18: [function (require, module, exports) { "use strict"; var utils = require("../utils"); function DataReader(data) { this.data = data; // type : see implementation this.length = data.length; this.index = 0; this.zero = 0; } DataReader.prototype = { /** * Check that the offset will not go too far. * @param {string} offset the additional offset to check. * @throws {Error} an Error if the offset is out of bounds. */ checkOffset: function checkOffset(offset) { this.checkIndex(this.index + offset); }, /** * Check that the specified index will not be too far. * @param {string} newIndex the index to check. * @throws {Error} an Error if the index is out of bounds. */ checkIndex: function checkIndex(newIndex) { if (this.length < this.zero + newIndex || newIndex < 0) { throw new Error("End of data reached (data length = " + this.length + ", asked index = " + newIndex + "). Corrupted zip ?"); } }, /** * Change the index. * @param {number} newIndex The new index. * @throws {Error} if the new index is out of the data. */ setIndex: function setIndex(newIndex) { this.checkIndex(newIndex); this.index = newIndex; }, /** * Skip the next n bytes. * @param {number} n the number of bytes to skip. * @throws {Error} if the new index is out of the data. */ skip: function skip(n) { this.setIndex(this.index + n); }, /** * Get the byte at the specified index. * @param {number} i the index to use. * @return {number} a byte. */ byteAt: function byteAt() { // see implementations }, /** * Get the next number with a given byte size. * @param {number} size the number of bytes to read. * @return {number} the corresponding number. */ readInt: function readInt(size) { var result = 0, i; this.checkOffset(size); for (i = this.index + size - 1; i >= this.index; i--) { result = (result << 8) + this.byteAt(i); } this.index += size; return result; }, /** * Get the next string with a given byte size. * @param {number} size the number of bytes to read. * @return {string} the corresponding string. */ readString: function readString(size) { return utils.transformTo("string", this.readData(size)); }, /** * Get raw data without conversion, bytes. * @param {number} size the number of bytes to read. * @return {Object} the raw data, implementation specific. */ readData: function readData() { // see implementations }, /** * Find the last occurrence of a zip signature (4 bytes). * @param {string} sig the signature to find. * @return {number} the index of the last occurrence, -1 if not found. */ lastIndexOfSignature: function lastIndexOfSignature() { // see implementations }, /** * Read the signature (4 bytes) at the current position and compare it with sig. * @param {string} sig the expected signature * @return {boolean} true if the signature matches, false otherwise. */ readAndCheckSignature: function readAndCheckSignature() { // see implementations }, /** * Get the next date. * @return {Date} the date. */ readDate: function readDate() { var dostime = this.readInt(4); return new Date(Date.UTC((dostime >> 25 & 0x7f) + 1980, // year (dostime >> 21 & 0x0f) - 1, // month dostime >> 16 & 0x1f, // day dostime >> 11 & 0x1f, // hour dostime >> 5 & 0x3f, // minute (dostime & 0x1f) << 1)); // second } }; module.exports = DataReader; }, { "../utils": 32 }], 19: [function (require, module, exports) { "use strict"; var Uint8ArrayReader = require("./Uint8ArrayReader"); var utils = require("../utils"); function NodeBufferReader(data) { Uint8ArrayReader.call(this, data); } utils.inherits(NodeBufferReader, Uint8ArrayReader); /** * @see DataReader.readData */ NodeBufferReader.prototype.readData = function (size) { this.checkOffset(size); var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); this.index += size; return result; }; module.exports = NodeBufferReader; }, { "../utils": 32, "./Uint8ArrayReader": 21 }], 20: [function (require, module, exports) { "use strict"; var DataReader = require("./DataReader"); var utils = require("../utils"); function StringReader(data) { DataReader.call(this, data); } utils.inherits(StringReader, DataReader); /** * @see DataReader.byteAt */ StringReader.prototype.byteAt = function (i) { return this.data.charCodeAt(this.zero + i); }; /** * @see DataReader.lastIndexOfSignature */ StringReader.prototype.lastIndexOfSignature = function (sig) { return this.data.lastIndexOf(sig) - this.zero; }; /** * @see DataReader.readAndCheckSignature */ StringReader.prototype.readAndCheckSignature = function (sig) { var data = this.readData(4); return sig === data; }; /** * @see DataReader.readData */ StringReader.prototype.readData = function (size) { this.checkOffset(size); // this will work because the constructor applied the "& 0xff" mask. var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); this.index += size; return result; }; module.exports = StringReader; }, { "../utils": 32, "./DataReader": 18 }], 21: [function (require, module, exports) { "use strict"; var ArrayReader = require("./ArrayReader"); var utils = require("../utils"); function Uint8ArrayReader(data) { ArrayReader.call(this, data); } utils.inherits(Uint8ArrayReader, ArrayReader); /** * @see DataReader.readData */ Uint8ArrayReader.prototype.readData = function (size) { this.checkOffset(size); if (size === 0) { // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of []. return new Uint8Array(0); } var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size); this.index += size; return result; }; module.exports = Uint8ArrayReader; }, { "../utils": 32, "./ArrayReader": 17 }], 22: [function (require, module, exports) { "use strict"; var utils = require("../utils"); var support = require("../support"); var ArrayReader = require("./ArrayReader"); var StringReader = require("./StringReader"); var NodeBufferReader = require("./NodeBufferReader"); var Uint8ArrayReader = require("./Uint8ArrayReader"); /** * Create a reader adapted to the data. * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read. * @return {DataReader} the data reader. */ module.exports = function (data) { var type = utils.getTypeOf(data); utils.checkSupport(type); if (type === "string" && !support.uint8array) { return new StringReader(data); } if (type === "nodebuffer") { return new NodeBufferReader(data); } if (support.uint8array) { return new Uint8ArrayReader(utils.transformTo("uint8array", data)); } return new ArrayReader(utils.transformTo("array", data)); }; }, { "../support": 30, "../utils": 32, "./ArrayReader": 17, "./NodeBufferReader": 19, "./StringReader": 20, "./Uint8ArrayReader": 21 }], 23: [function (require, module, exports) { "use strict"; exports.LOCAL_FILE_HEADER = "PK\x03\x04"; exports.CENTRAL_FILE_HEADER = "PK\x01\x02"; exports.CENTRAL_DIRECTORY_END = "PK\x05\x06"; exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07"; exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06"; exports.DATA_DESCRIPTOR = "PK\x07\x08"; }, {}], 24: [function (require, module, exports) { "use strict"; var GenericWorker = require("./GenericWorker"); var utils = require("../utils"); /** * A worker which convert chunks to a specified type. * @constructor * @param {String} destType the destination type. */ function ConvertWorker(destType) { GenericWorker.call(this, "ConvertWorker to " + destType); this.destType = destType; } utils.inherits(ConvertWorker, GenericWorker); /** * @see GenericWorker.processChunk */ ConvertWorker.prototype.processChunk = function (chunk) { this.push({ data: utils.transformTo(this.destType, chunk.data), meta: chunk.meta }); }; module.exports = ConvertWorker; }, { "../utils": 32, "./GenericWorker": 28 }], 25: [function (require, module, exports) { "use strict"; var GenericWorker = require("./GenericWorker"); var crc32 = require("../crc32"); var utils = require("../utils"); /** * A worker which calculate the crc32 of the data flowing through. * @constructor */ function Crc32Probe() { GenericWorker.call(this, "Crc32Probe"); this.withStreamInfo("crc32", 0); } utils.inherits(Crc32Probe, GenericWorker); /** * @see GenericWorker.processChunk */ Crc32Probe.prototype.processChunk = function (chunk) { this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0); this.push(chunk); }; module.exports = Crc32Probe; }, { "../crc32": 4, "../utils": 32, "./GenericWorker": 28 }], 26: [function (require, module, exports) { "use strict"; var utils = require("../utils"); var GenericWorker = require("./GenericWorker"); /** * A worker which calculate the total length of the data flowing through. * @constructor * @param {String} propName the name used to expose the length */ function DataLengthProbe(propName) { GenericWorker.call(this, "DataLengthProbe for " + propName); this.propName = propName; this.withStreamInfo(propName, 0); } utils.inherits(DataLengthProbe, GenericWorker); /** * @see GenericWorker.processChunk */ DataLengthProbe.prototype.processChunk = function (chunk) { if (chunk) { var length = this.streamInfo[this.propName] || 0; this.streamInfo[this.propName] = length + chunk.data.length; } GenericWorker.prototype.processChunk.call(this, chunk); }; module.exports = DataLengthProbe; }, { "../utils": 32, "./GenericWorker": 28 }], 27: [function (require, module, exports) { "use strict"; var utils = require("../utils"); var GenericWorker = require("./GenericWorker"); // the size of the generated chunks // TODO expose this as a public variable var DEFAULT_BLOCK_SIZE = 16 * 1024; /** * A worker that reads a content and emits chunks. * @constructor * @param {Promise} dataP the promise of the data to split */ function DataWorker(dataP) { GenericWorker.call(this, "DataWorker"); var self = this; this.dataIsReady = false; this.index = 0; this.max = 0; this.data = null; this.type = ""; this._tickScheduled = false; dataP.then(function (data) { self.dataIsReady = true; self.data = data; self.max = data && data.length || 0; self.type = utils.getTypeOf(data); if (!self.isPaused) { self._tickAndRepeat(); } }, function (e) { self.error(e); }); } utils.inherits(DataWorker, GenericWorker); /** * @see GenericWorker.cleanUp */ DataWorker.prototype.cleanUp = function () { GenericWorker.prototype.cleanUp.call(this); this.data = null; }; /** * @see GenericWorker.resume */ DataWorker.prototype.resume = function () { if (!GenericWorker.prototype.resume.call(this)) { return false; } if (!this._tickScheduled && this.dataIsReady) { this._tickScheduled = true; utils.delay(this._tickAndRepeat, [], this); } return true; }; /** * Trigger a tick a schedule an other call to this function. */ DataWorker.prototype._tickAndRepeat = function () { this._tickScheduled = false; if (this.isPaused || this.isFinished) { return; } this._tick(); if (!this.isFinished) { utils.delay(this._tickAndRepeat, [], this); this._tickScheduled = true; } }; /** * Read and push a chunk. */ DataWorker.prototype._tick = function () { if (this.isPaused || this.isFinished) { return false; } var size = DEFAULT_BLOCK_SIZE; var data = null, nextIndex = Math.min(this.max, this.index + size); if (this.index >= this.max) { // EOF return this.end(); } else { switch (this.type) { case "string": data = this.data.substring(this.index, nextIndex); break; case "uint8array": data = this.data.subarray(this.index, nextIndex); break; case "array": case "nodebuffer": data = this.data.slice(this.index, nextIndex); break; } this.index = nextIndex; return this.push({ data: data, meta: { percent: this.max ? this.index / this.max * 100 : 0 } }); } }; module.exports = DataWorker; }, { "../utils": 32, "./GenericWorker": 28 }], 28: [function (require, module, exports) { "use strict"; /** * A worker that does nothing but passing chunks to the next one. This is like * a nodejs stream but with some differences. On the good side : * - it works on IE 6-9 without any issue / polyfill * - it weights less than the full dependencies bundled with browserify * - it forwards errors (no need to declare an error handler EVERYWHERE) * * A chunk is an object with 2 attributes : `meta` and `data`. The former is an * object containing anything (`percent` for example), see each worker for more * details. The latter is the real data (String, Uint8Array, etc). * * @constructor * @param {String} name the name of the stream (mainly used for debugging purposes) */ function GenericWorker(name) { // the name of the worker this.name = name || "default"; // an object containing metadata about the workers chain this.streamInfo = {}; // an error which happened when the worker was paused this.generatedError = null; // an object containing metadata to be merged by this worker into the general metadata this.extraStreamInfo = {}; // true if the stream is paused (and should not do anything), false otherwise this.isPaused = true; // true if the stream is finished (and should not do anything), false otherwise this.isFinished = false; // true if the stream is locked to prevent further structure updates (pipe), false otherwise this.isLocked = false; // the event listeners this._listeners = { "data": [], "end": [], "error": [] }; // the previous worker, if any this.previous = null; } GenericWorker.prototype = { /** * Push a chunk to the next workers. * @param {Object} chunk the chunk to push */ push: function push(chunk) { this.emit("data", chunk); }, /** * End the stream. * @return {Boolean} true if this call ended the worker, false otherwise. */ end: function end() { if (this.isFinished) { return false; } this.flush(); try { this.emit("end"); this.cleanUp(); this.isFinished = true; } catch (e) { this.emit("error", e); } return true; }, /** * End the stream with an error. * @param {Error} e the error which caused the premature end. * @return {Boolean} true if this call ended the worker with an error, false otherwise. */ error: function error(e) { if (this.isFinished) { return false; } if (this.isPaused) { this.generatedError = e; } else { this.isFinished = true; this.emit("error", e); // in the workers chain exploded in the middle of the chain, // the error event will go downward but we also need to notify // workers upward that there has been an error. if (this.previous) { this.previous.error(e); } this.cleanUp(); } return true; }, /** * Add a callback on an event. * @param {String} name the name of the event (data, end, error) * @param {Function} listener the function to call when the event is triggered * @return {GenericWorker} the current object for chainability */ on: function on(name, listener) { this._listeners[name].push(listener); return this; }, /** * Clean any references when a worker is ending. */ cleanUp: function cleanUp() { this.streamInfo = this.generatedError = this.extraStreamInfo = null; this._listeners = []; }, /** * Trigger an event. This will call registered callback with the provided arg. * @param {String} name the name of the event (data, end, error) * @param {Object} arg the argument to call the callback with. */ emit: function emit(name, arg) { if (this._listeners[name]) { for (var i = 0; i < this._listeners[name].length; i++) { this._listeners[name][i].call(this, arg); } } }, /** * Chain a worker with an other. * @param {Worker} next the worker receiving events from the current one. * @return {worker} the next worker for chainability */ pipe: function pipe(next) { return next.registerPrevious(this); }, /** * Same as `pipe` in the other direction. * Using an API with `pipe(next)` is very easy. * Implementing the API with the point of view of the next one registering * a source is easier, see the ZipFileWorker. * @param {Worker} previous the previous worker, sending events to this one * @return {Worker} the current worker for chainability */ registerPrevious: function registerPrevious(previous) { if (this.isLocked) { throw new Error("The stream '" + this + "' has already been used."); } // sharing the streamInfo... this.streamInfo = previous.streamInfo; // ... and adding our own bits this.mergeStreamInfo(); this.previous = previous; var self = this; previous.on("data", function (chunk) { self.processChunk(chunk); }); previous.on("end", function () { self.end(); }); previous.on("error", function (e) { self.error(e); }); return this; }, /** * Pause the stream so it doesn't send events anymore. * @return {Boolean} true if this call paused the worker, false otherwise. */ pause: function pause() { if (this.isPaused || this.isFinished) { return false; } this.isPaused = true; if (this.previous) { this.previous.pause(); } return true; }, /** * Resume a paused stream. * @return {Boolean} true if this call resumed the worker, false otherwise. */ resume: function resume() { if (!this.isPaused || this.isFinished) { return false; } this.isPaused = false; // if true, the worker tried to resume but failed var withError = false; if (this.generatedError) { this.error(this.generatedError); withError = true; } if (this.previous) { this.previous.resume(); } return !withError; }, /** * Flush any remaining bytes as the stream is ending. */ flush: function flush() {}, /** * Process a chunk. This is usually the method overridden. * @param {Object} chunk the chunk to process. */ processChunk: function processChunk(chunk) { this.push(chunk); }, /** * Add a key/value to be added in the workers chain streamInfo once activated. * @param {String} key the key to use * @param {Object} value the associated value * @return {Worker} the current worker for chainability */ withStreamInfo: function withStreamInfo(key, value) { this.extraStreamInfo[key] = value; this.mergeStreamInfo(); return this; }, /** * Merge this worker's streamInfo into the chain's streamInfo. */ mergeStreamInfo: function mergeStreamInfo() { for (var key in this.extraStreamInfo) { if (!Object.prototype.hasOwnProperty.call(this.extraStreamInfo, key)) { continue; } this.streamInfo[key] = this.extraStreamInfo[key]; } }, /** * Lock the stream to prevent further updates on the workers chain. * After calling this method, all calls to pipe will fail. */ lock: function lock() { if (this.isLocked) { throw new Error("The stream '" + this + "' has already been used."); } this.isLocked = true; if (this.previous) { this.previous.lock(); } }, /** * * Pretty print the workers chain. */ toString: function toString() { var me = "Worker " + this.name; if (this.previous) { return this.previous + " -> " + me; } else { return me; } } }; module.exports = GenericWorker; }, {}], 29: [function (require, module, exports) { "use strict"; var utils = require("../utils"); var ConvertWorker = require("./ConvertWorker"); var GenericWorker = require("./GenericWorker"); var base64 = require("../base64"); var support = require("../support"); var external = require("../external"); var NodejsStreamOutputAdapter = null; if (support.nodestream) { try { NodejsStreamOutputAdapter = require("../nodejs/NodejsStreamOutputAdapter"); } catch (e) { // ignore } } /** * Apply the final transformation of the data. If the user wants a Blob for * example, it's easier to work with an U8intArray and finally do the * ArrayBuffer/Blob conversion. * @param {String} type the name of the final type * @param {String|Uint8Array|Buffer} content the content to transform * @param {String} mimeType the mime type of the content, if applicable. * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format. */ function transformZipOutput(type, content, mimeType) { switch (type) { case "blob": return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType); case "base64": return base64.encode(content); default: return utils.transformTo(type, content); } } /** * Concatenate an array of data of the given type. * @param {String} type the type of the data in the given array. * @param {Array} dataArray the array containing the data chunks to concatenate * @return {String|Uint8Array|Buffer} the concatenated data * @throws Error if the asked type is unsupported */ function concat(type, dataArray) { var i, index = 0, res = null, totalLength = 0; for (i = 0; i < dataArray.length; i++) { totalLength += dataArray[i].length; } switch (type) { case "string": return dataArray.join(""); case "array": return Array.prototype.concat.apply([], dataArray); case "uint8array": res = new Uint8Array(totalLength); for (i = 0; i < dataArray.length; i++) { res.set(dataArray[i], index); index += dataArray[i].length; } return res; case "nodebuffer": return Buffer.concat(dataArray); default: throw new Error("concat : unsupported type '" + type + "'"); } } /** * Listen a StreamHelper, accumulate its content and concatenate it into a * complete block. * @param {StreamHelper} helper the helper to use. * @param {Function} updateCallback a callback called on each update. Called * with one arg : * - the metadata linked to the update received. * @return Promise the promise for the accumulation. */ function _accumulate(helper, updateCallback) { return new external.Promise(function (resolve, reject) { var dataArray = []; var chunkType = helper._internalType, resultType = helper._outputType, mimeType = helper._mimeType; helper.on("data", function (data, meta) { dataArray.push(data); if (updateCallback) { updateCallback(meta); } }).on("error", function (err) { dataArray = []; reject(err); }).on("end", function () { try { var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType); resolve(result); } catch (e) { reject(e); } dataArray = []; }).resume(); }); } /** * An helper to easily use workers outside of JSZip. * @constructor * @param {Worker} worker the worker to wrap * @param {String} outputType the type of data expected by the use * @param {String} mimeType the mime type of the content, if applicable. */ function StreamHelper(worker, outputType, mimeType) { var internalType = outputType; switch (outputType) { case "blob": case "arraybuffer": internalType = "uint8array"; break; case "base64": internalType = "string"; break; } try { // the type used internally this._internalType = internalType; // the type used to output results this._outputType = outputType; // the mime type this._mimeType = mimeType; utils.checkSupport(internalType); this._worker = worker.pipe(new ConvertWorker(internalType)); // the last workers can be rewired without issues but we need to // prevent any updates on previous workers. worker.lock(); } catch (e) { this._worker = new GenericWorker("error"); this._worker.error(e); } } StreamHelper.prototype = { /** * Listen a StreamHelper, accumulate its content and concatenate it into a * complete block. * @param {Function} updateCb the update callback. * @return Promise the promise for the accumulation. */ accumulate: function accumulate(updateCb) { return _accumulate(this, updateCb); }, /** * Add a listener on an event triggered on a stream. * @param {String} evt the name of the event * @param {Function} fn the listener * @return {StreamHelper} the current helper. */ on: function on(evt, fn) { var self = this; if (evt === "data") { this._worker.on(evt, function (chunk) { fn.call(self, chunk.data, chunk.meta); }); } else { this._worker.on(evt, function () { utils.delay(fn, arguments, self); }); } return this; }, /** * Resume the flow of chunks. * @return {StreamHelper} the current helper. */ resume: function resume() { utils.delay(this._worker.resume, [], this._worker); return this; }, /** * Pause the flow of chunks. * @return {StreamHelper} the current helper. */ pause: function pause() { this._worker.pause(); return this; }, /** * Return a nodejs stream for this helper. * @param {Function} updateCb the update callback. * @return {NodejsStreamOutputAdapter} the nodejs stream. */ toNodejsStream: function toNodejsStream(updateCb) { utils.checkSupport("nodestream"); if (this._outputType !== "nodebuffer") { // an object stream containing blob/arraybuffer/uint8array/string // is strange and I don't know if it would be useful. // I you find this comment and have a good usecase, please open a // bug report ! throw new Error(this._outputType + " is not supported by this method"); } return new NodejsStreamOutputAdapter(this, { objectMode: this._outputType !== "nodebuffer" }, updateCb); } }; module.exports = StreamHelper; }, { "../base64": 1, "../external": 6, "../nodejs/NodejsStreamOutputAdapter": 13, "../support": 30, "../utils": 32, "./ConvertWorker": 24, "./GenericWorker": 28 }], 30: [function (require, module, exports) { "use strict"; exports.base64 = true; exports.array = true; exports.string = true; exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined"; exports.nodebuffer = typeof Buffer !== "undefined"; // contains true if JSZip can read/generate Uint8Array, false otherwise. exports.uint8array = typeof Uint8Array !== "undefined"; if (typeof ArrayBuffer === "undefined") { exports.blob = false; } else { var buffer = new ArrayBuffer(0); try { exports.blob = new Blob([buffer], { type: "application/zip" }).size === 0; } catch (e) { try { var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder; var builder = new Builder(); builder.append(buffer); exports.blob = builder.getBlob("application/zip").size === 0; } catch (e) { exports.blob = false; } } } try { exports.nodestream = !!require("readable-stream").Readable; } catch (e) { exports.nodestream = false; } }, { "readable-stream": 16 }], 31: [function (require, module, exports) { "use strict"; var utils = require("./utils"); var support = require("./support"); var nodejsUtils = require("./nodejsUtils"); var GenericWorker = require("./stream/GenericWorker"); /** * The following functions come from pako, from pako/lib/utils/strings * released under the MIT license, see pako https://github.com/nodeca/pako/ */ // 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 var _utf8len = new Array(256); for (var i = 0; i < 256; i++) { _utf8len[i] = i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1; } _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start // convert string to array (typed, when possible) var string2buf = function string2buf(str) { var 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 if (support.uint8array) { buf = new Uint8Array(buf_len); } else { buf = new Array(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; }; // 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 = function utf8border(buf, max) { var pos; max = max || buf.length; if (max > buf.length) { max = buf.length; } // go back from last position, until start of sequence found pos = max - 1; while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) { pos--; } // Fuckup - 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 vuffer is too small, // return max too. if (pos === 0) { return max; } return pos + _utf8len[buf[pos]] > max ? pos : max; }; // convert array to string var buf2string = function buf2string(buf) { var i, out, c, c_len; var len = buf.length; // Reserve max possible length (2 words per char) // NB: by unknown reasons, Array is significantly faster for // String.fromCharCode.apply than Uint16Array. var utf16buf = new Array(len * 2); for (out = 0, i = 0; i < len;) { c = buf[i++]; // quick process ascii if (c < 0x80) { utf16buf[out++] = c; continue; } 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; } } // shrinkBuf(utf16buf, out) if (utf16buf.length !== out) { if (utf16buf.subarray) { utf16buf = utf16buf.subarray(0, out); } else { utf16buf.length = out; } } // return String.fromCharCode.apply(null, utf16buf); return utils.applyFromCharCode(utf16buf); }; // That's all for the pako functions. /** * Transform a javascript string into an array (typed if possible) of bytes, * UTF-8 encoded. * @param {String} str the string to encode * @return {Array|Uint8Array|Buffer} the UTF-8 encoded string. */ exports.utf8encode = function utf8encode(str) { if (support.nodebuffer) { return nodejsUtils.newBufferFrom(str, "utf-8"); } return string2buf(str); }; /** * Transform a bytes array (or a representation) representing an UTF-8 encoded * string into a javascript string. * @param {Array|Uint8Array|Buffer} buf the data de decode * @return {String} the decoded string. */ exports.utf8decode = function utf8decode(buf) { if (support.nodebuffer) { return utils.transformTo("nodebuffer", buf).toString("utf-8"); } buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf); return buf2string(buf); }; /** * A worker to decode utf8 encoded binary chunks into string chunks. * @constructor */ function Utf8DecodeWorker() { GenericWorker.call(this, "utf-8 decode"); // the last bytes if a chunk didn't end with a complete codepoint. this.leftOver = null; } utils.inherits(Utf8DecodeWorker, GenericWorker); /** * @see GenericWorker.processChunk */ Utf8DecodeWorker.prototype.processChunk = function (chunk) { var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data); // 1st step, re-use what's left of the previous chunk if (this.leftOver && this.leftOver.length) { if (support.uint8array) { var previousData = data; data = new Uint8Array(previousData.length + this.leftOver.length); data.set(this.leftOver, 0); data.set(previousData, this.leftOver.length); } else { data = this.leftOver.concat(data); } this.leftOver = null; } var nextBoundary = utf8border(data); var usableData = data; if (nextBoundary !== data.length) { if (support.uint8array) { usableData = data.subarray(0, nextBoundary); this.leftOver = data.subarray(nextBoundary, data.length); } else { usableData = data.slice(0, nextBoundary); this.leftOver = data.slice(nextBoundary, data.length); } } this.push({ data: exports.utf8decode(usableData), meta: chunk.meta }); }; /** * @see GenericWorker.flush */ Utf8DecodeWorker.prototype.flush = function () { if (this.leftOver && this.leftOver.length) { this.push({ data: exports.utf8decode(this.leftOver), meta: {} }); this.leftOver = null; } }; exports.Utf8DecodeWorker = Utf8DecodeWorker; /** * A worker to endcode string chunks into utf8 encoded binary chunks. * @constructor */ function Utf8EncodeWorker() { GenericWorker.call(this, "utf-8 encode"); } utils.inherits(Utf8EncodeWorker, GenericWorker); /** * @see GenericWorker.processChunk */ Utf8EncodeWorker.prototype.processChunk = function (chunk) { this.push({ data: exports.utf8encode(chunk.data), meta: chunk.meta }); }; exports.Utf8EncodeWorker = Utf8EncodeWorker; }, { "./nodejsUtils": 14, "./stream/GenericWorker": 28, "./support": 30, "./utils": 32 }], 32: [function (require, module, exports) { "use strict"; var support = require("./support"); var base64 = require("./base64"); var nodejsUtils = require("./nodejsUtils"); var external = require("./external"); require("setimmediate"); /** * Convert a string that pass as a "binary string": it should represent a byte * array but may have > 255 char codes. Be sure to take only the first byte * and returns the byte array. * @param {String} str the string to transform. * @return {Array|Uint8Array} the string in a binary format. */ function string2binary(str) { var result = null; if (support.uint8array) { result = new Uint8Array(str.length); } else { result = new Array(str.length); } return stringToArrayLike(str, result); } /** * Create a new blob with the given content and the given type. * @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use * an Uint8Array because the stock browser of android 4 won't accept it (it * will be silently converted to a string, "[object Uint8Array]"). * * Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge: * when a large amount of Array is used to create the Blob, the amount of * memory consumed is nearly 100 times the original data amount. * * @param {String} type the mime type of the blob. * @return {Blob} the created blob. */ exports.newBlob = function (part, type) { exports.checkSupport("blob"); try { // Blob constructor return new Blob([part], { type: type }); } catch (e) { try { // deprecated, browser only, old way var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder; var builder = new Builder(); builder.append(part); return builder.getBlob(type); } catch (e) { // well, fuck ?! throw new Error("Bug : can't construct the Blob."); } } }; /** * The identity function. * @param {Object} input the input. * @return {Object} the same input. */ function identity(input) { return input; } /** * Fill in an array with a string. * @param {String} str the string to use. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated). * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array. */ function stringToArrayLike(str, array) { for (var i = 0; i < str.length; ++i) { array[i] = str.charCodeAt(i) & 0xFF; } return array; } /** * An helper for the function arrayLikeToString. * This contains static information and functions that * can be optimized by the browser JIT compiler. */ var arrayToStringHelper = { /** * Transform an array of int into a string, chunk by chunk. * See the performances notes on arrayLikeToString. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. * @param {String} type the type of the array. * @param {Integer} chunk the chunk size. * @return {String} the resulting string. * @throws Error if the chunk is too big for the stack. */ stringifyByChunk: function stringifyByChunk(array, type, chunk) { var result = [], k = 0, len = array.length; // shortcut if (len <= chunk) { return String.fromCharCode.apply(null, array); } while (k < len) { if (type === "array" || type === "nodebuffer") { result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len)))); } else { result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len)))); } k += chunk; } return result.join(""); }, /** * Call String.fromCharCode on every item in the array. * This is the naive implementation, which generate A LOT of intermediate string. * This should be used when everything else fail. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. * @return {String} the result. */ stringifyByChar: function stringifyByChar(array) { var resultStr = ""; for (var i = 0; i < array.length; i++) { resultStr += String.fromCharCode(array[i]); } return resultStr; }, applyCanBeUsed: { /** * true if the browser accepts to use String.fromCharCode on Uint8Array */ uint8array: function () { try { return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1; } catch (e) { return false; } }(), /** * true if the browser accepts to use String.fromCharCode on nodejs Buffer. */ nodebuffer: function () { try { return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1; } catch (e) { return false; } }() } }; /** * Transform an array-like object to a string. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. * @return {String} the result. */ function arrayLikeToString(array) { // Performances notes : // -------------------- // String.fromCharCode.apply(null, array) is the fastest, see // see http://jsperf.com/converting-a-uint8array-to-a-string/2 // but the stack is limited (and we can get huge arrays !). // // result += String.fromCharCode(array[i]); generate too many strings ! // // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2 // TODO : we now have workers that split the work. Do we still need that ? var chunk = 65536, type = exports.getTypeOf(array), canUseApply = true; if (type === "uint8array") { canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array; } else if (type === "nodebuffer") { canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer; } if (canUseApply) { while (chunk > 1) { try { return arrayToStringHelper.stringifyByChunk(array, type, chunk); } catch (e) { chunk = Math.floor(chunk / 2); } } } // no apply or chunk error : slow and painful algorithm // default browser on android 4.* return arrayToStringHelper.stringifyByChar(array); } exports.applyFromCharCode = arrayLikeToString; /** * Copy the data from an array-like to an other array-like. * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array. * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated. * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array. */ function arrayLikeToArrayLike(arrayFrom, arrayTo) { for (var i = 0; i < arrayFrom.length; i++) { arrayTo[i] = arrayFrom[i]; } return arrayTo; } // a matrix containing functions to transform everything into everything. var transform = {}; // string to ? transform["string"] = { "string": identity, "array": function array(input) { return stringToArrayLike(input, new Array(input.length)); }, "arraybuffer": function arraybuffer(input) { return transform["string"]["uint8array"](input).buffer; }, "uint8array": function uint8array(input) { return stringToArrayLike(input, new Uint8Array(input.length)); }, "nodebuffer": function nodebuffer(input) { return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length)); } }; // array to ? transform["array"] = { "string": arrayLikeToString, "array": identity, "arraybuffer": function arraybuffer(input) { return new Uint8Array(input).buffer; }, "uint8array": function uint8array(input) { return new Uint8Array(input); }, "nodebuffer": function nodebuffer(input) { return nodejsUtils.newBufferFrom(input); } }; // arraybuffer to ? transform["arraybuffer"] = { "string": function string(input) { return arrayLikeToString(new Uint8Array(input)); }, "array": function array(input) { return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength)); }, "arraybuffer": identity, "uint8array": function uint8array(input) { return new Uint8Array(input); }, "nodebuffer": function nodebuffer(input) { return nodejsUtils.newBufferFrom(new Uint8Array(input)); } }; // uint8array to ? transform["uint8array"] = { "string": arrayLikeToString, "array": function array(input) { return arrayLikeToArrayLike(input, new Array(input.length)); }, "arraybuffer": function arraybuffer(input) { return input.buffer; }, "uint8array": identity, "nodebuffer": function nodebuffer(input) { return nodejsUtils.newBufferFrom(input); } }; // nodebuffer to ? transform["nodebuffer"] = { "string": arrayLikeToString, "array": function array(input) { return arrayLikeToArrayLike(input, new Array(input.length)); }, "arraybuffer": function arraybuffer(input) { return transform["nodebuffer"]["uint8array"](input).buffer; }, "uint8array": function uint8array(input) { return arrayLikeToArrayLike(input, new Uint8Array(input.length)); }, "nodebuffer": identity }; /** * Transform an input into any type. * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer. * If no output type is specified, the unmodified input will be returned. * @param {String} outputType the output type. * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert. * @throws {Error} an Error if the browser doesn't support the requested output type. */ exports.transformTo = function (outputType, input) { if (!input) { // undefined, null, etc // an empty string won't harm. input = ""; } if (!outputType) { return input; } exports.checkSupport(outputType); var inputType = exports.getTypeOf(input); var result = transform[inputType][outputType](input); return result; }; /** * Resolve all relative path components, "." and "..", in a path. If these relative components * traverse above the root then the resulting path will only contain the final path component. * * All empty components, e.g. "//", are removed. * @param {string} path A path with / or \ separators * @returns {string} The path with all relative path components resolved. */ exports.resolve = function (path) { var parts = path.split("/"); var result = []; for (var index = 0; index < parts.length; index++) { var part = parts[index]; // Allow the first and last component to be empty for trailing slashes. if (part === "." || part === "" && index !== 0 && index !== parts.length - 1) { continue; } else if (part === "..") { result.pop(); } else { result.push(part); } } return result.join("/"); }; /** * Return the type of the input. * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer. * @param {Object} input the input to identify. * @return {String} the (lowercase) type of the input. */ exports.getTypeOf = function (input) { if (typeof input === "string") { return "string"; } if (Object.prototype.toString.call(input) === "[object Array]") { return "array"; } if (support.nodebuffer && nodejsUtils.isBuffer(input)) { return "nodebuffer"; } if (support.uint8array && input instanceof Uint8Array) { return "uint8array"; } if (support.arraybuffer && input instanceof ArrayBuffer) { return "arraybuffer"; } }; /** * Throw an exception if the type is not supported. * @param {String} type the type to check. * @throws {Error} an Error if the browser doesn't support the requested type. */ exports.checkSupport = function (type) { var supported = support[type.toLowerCase()]; if (!supported) { throw new Error(type + " is not supported by this platform"); } }; exports.MAX_VALUE_16BITS = 65535; exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1 /** * Prettify a string read as binary. * @param {string} str the string to prettify. * @return {string} a pretty string. */ exports.pretty = function (str) { var res = "", code, i; for (i = 0; i < (str || "").length; i++) { code = str.charCodeAt(i); res += "\\x" + (code < 16 ? "0" : "") + code.toString(16).toUpperCase(); } return res; }; /** * Defer the call of a function. * @param {Function} callback the function to call asynchronously. * @param {Array} args the arguments to give to the callback. */ exports.delay = function (callback, args, self) { setImmediate(function () { callback.apply(self || null, args || []); }); }; /** * Extends a prototype with an other, without calling a constructor with * side effects. Inspired by nodejs' `utils.inherits` * @param {Function} ctor the constructor to augment * @param {Function} superCtor the parent constructor to use */ exports.inherits = function (ctor, superCtor) { var Obj = function Obj() {}; Obj.prototype = superCtor.prototype; ctor.prototype = new Obj(); }; /** * Merge the objects passed as parameters into a new one. * @private * @param {...Object} var_args All objects to merge. * @return {Object} a new object with the data of the others. */ exports.extend = function () { var result = {}, i, attr; for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers for (attr in arguments[i]) { if (Object.prototype.hasOwnProperty.call(arguments[i], attr) && typeof result[attr] === "undefined") { result[attr] = arguments[i][attr]; } } } return result; }; /** * Transform arbitrary content into a Promise. * @param {String} name a name for the content being processed. * @param {Object} inputData the content to process. * @param {Boolean} isBinary true if the content is not an unicode string * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character. * @param {Boolean} isBase64 true if the string content is encoded with base64. * @return {Promise} a promise in a format usable by JSZip. */ exports.prepareContent = function (name, inputData, isBinary, isOptimizedBinaryString, isBase64) { // if inputData is already a promise, this flatten it. var promise = external.Promise.resolve(inputData).then(function (data) { var isBlob = support.blob && (data instanceof Blob || ["[object File]", "[object Blob]"].indexOf(Object.prototype.toString.call(data)) !== -1); if (isBlob && typeof FileReader !== "undefined") { return new external.Promise(function (resolve, reject) { var reader = new FileReader(); reader.onload = function (e) { resolve(e.target.result); }; reader.onerror = function (e) { reject(e.target.error); }; reader.readAsArrayBuffer(data); }); } else { return data; } }); return promise.then(function (data) { var dataType = exports.getTypeOf(data); if (!dataType) { return external.Promise.reject(new Error("Can't read the data of '" + name + "'. Is it " + "in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?")); } // special case : it's way easier to work with Uint8Array than with ArrayBuffer if (dataType === "arraybuffer") { data = exports.transformTo("uint8array", data); } else if (dataType === "string") { if (isBase64) { data = base64.decode(data); } else if (isBinary) { // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask if (isOptimizedBinaryString !== true) { // this is a string, not in a base64 format. // Be sure that this is a correct "binary string" data = string2binary(data); } } } return data; }); }; }, { "./base64": 1, "./external": 6, "./nodejsUtils": 14, "./support": 30, "setimmediate": 54 }], 33: [function (require, module, exports) { "use strict"; var readerFor = require("./reader/readerFor"); var utils = require("./utils"); var sig = require("./signature"); var ZipEntry = require("./zipEntry"); var support = require("./support"); // class ZipEntries {{{ /** * All the entries in the zip file. * @constructor * @param {Object} loadOptions Options for loading the stream. */ function ZipEntries(loadOptions) { this.files = []; this.loadOptions = loadOptions; } ZipEntries.prototype = { /** * Check that the reader is on the specified signature. * @param {string} expectedSignature the expected signature. * @throws {Error} if it is an other signature. */ checkSignature: function checkSignature(expectedSignature) { if (!this.reader.readAndCheckSignature(expectedSignature)) { this.reader.index -= 4; var signature = this.reader.readString(4); throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")"); } }, /** * Check if the given signature is at the given index. * @param {number} askedIndex the index to check. * @param {string} expectedSignature the signature to expect. * @return {boolean} true if the signature is here, false otherwise. */ isSignature: function isSignature(askedIndex, expectedSignature) { var currentIndex = this.reader.index; this.reader.setIndex(askedIndex); var signature = this.reader.readString(4); var result = signature === expectedSignature; this.reader.setIndex(currentIndex); return result; }, /** * Read the end of the central directory. */ readBlockEndOfCentral: function readBlockEndOfCentral() { this.diskNumber = this.reader.readInt(2); this.diskWithCentralDirStart = this.reader.readInt(2); this.centralDirRecordsOnThisDisk = this.reader.readInt(2); this.centralDirRecords = this.reader.readInt(2); this.centralDirSize = this.reader.readInt(4); this.centralDirOffset = this.reader.readInt(4); this.zipCommentLength = this.reader.readInt(2); // warning : the encoding depends of the system locale // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded. // On a windows machine, this field is encoded with the localized windows code page. var zipComment = this.reader.readData(this.zipCommentLength); var decodeParamType = support.uint8array ? "uint8array" : "array"; // To get consistent behavior with the generation part, we will assume that // this is utf8 encoded unless specified otherwise. var decodeContent = utils.transformTo(decodeParamType, zipComment); this.zipComment = this.loadOptions.decodeFileName(decodeContent); }, /** * Read the end of the Zip 64 central directory. * Not merged with the method readEndOfCentral : * The end of central can coexist with its Zip64 brother, * I don't want to read the wrong number of bytes ! */ readBlockZip64EndOfCentral: function readBlockZip64EndOfCentral() { this.zip64EndOfCentralSize = this.reader.readInt(8); this.reader.skip(4); // this.versionMadeBy = this.reader.readString(2); // this.versionNeeded = this.reader.readInt(2); this.diskNumber = this.reader.readInt(4); this.diskWithCentralDirStart = this.reader.readInt(4); this.centralDirRecordsOnThisDisk = this.reader.readInt(8); this.centralDirRecords = this.reader.readInt(8); this.centralDirSize = this.reader.readInt(8); this.centralDirOffset = this.reader.readInt(8); this.zip64ExtensibleData = {}; var extraDataSize = this.zip64EndOfCentralSize - 44, index = 0, extraFieldId, extraFieldLength, extraFieldValue; while (index < extraDataSize) { extraFieldId = this.reader.readInt(2); extraFieldLength = this.reader.readInt(4); extraFieldValue = this.reader.readData(extraFieldLength); this.zip64ExtensibleData[extraFieldId] = { id: extraFieldId, length: extraFieldLength, value: extraFieldValue }; } }, /** * Read the end of the Zip 64 central directory locator. */ readBlockZip64EndOfCentralLocator: function readBlockZip64EndOfCentralLocator() { this.diskWithZip64CentralDirStart = this.reader.readInt(4); this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8); this.disksCount = this.reader.readInt(4); if (this.disksCount > 1) { throw new Error("Multi-volumes zip are not supported"); } }, /** * Read the local files, based on the offset read in the central part. */ readLocalFiles: function readLocalFiles() { var i, file; for (i = 0; i < this.files.length; i++) { file = this.files[i]; this.reader.setIndex(file.localHeaderOffset); this.checkSignature(sig.LOCAL_FILE_HEADER); file.readLocalPart(this.reader); file.handleUTF8(); file.processAttributes(); } }, /** * Read the central directory. */ readCentralDir: function readCentralDir() { var file; this.reader.setIndex(this.centralDirOffset); while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) { file = new ZipEntry({ zip64: this.zip64 }, this.loadOptions); file.readCentralPart(this.reader); this.files.push(file); } if (this.centralDirRecords !== this.files.length) { if (this.centralDirRecords !== 0 && this.files.length === 0) { // We expected some records but couldn't find ANY. // This is really suspicious, as if something went wrong. throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length); } else { // We found some records but not all. // Something is wrong but we got something for the user: no error here. // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length); } } }, /** * Read the end of central directory. */ readEndOfCentral: function readEndOfCentral() { var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END); if (offset < 0) { // Check if the content is a truncated zip or complete garbage. // A "LOCAL_FILE_HEADER" is not required at the beginning (auto // extractible zip for example) but it can give a good hint. // If an ajax request was used without responseType, we will also // get unreadable data. var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER); if (isGarbage) { throw new Error("Can't find end of central directory : is this a zip file ? " + "If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html"); } else { throw new Error("Corrupted zip: can't find end of central directory"); } } this.reader.setIndex(offset); var endOfCentralDirOffset = offset; this.checkSignature(sig.CENTRAL_DIRECTORY_END); this.readBlockEndOfCentral(); /* extract from the zip spec : 4) If one of the fields in the end of central directory record is too small to hold required data, the field should be set to -1 (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record should be created. 5) The end of central directory record and the Zip64 end of central directory locator record must reside on the same disk when splitting or spanning an archive. */ if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) { this.zip64 = true; /* Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents all numbers as 64-bit double precision IEEE 754 floating point numbers. So, we have 53bits for integers and bitwise operations treat everything as 32bits. see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5 */ // should look for a zip64 EOCD locator offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); if (offset < 0) { throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator"); } this.reader.setIndex(offset); this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); this.readBlockZip64EndOfCentralLocator(); // now the zip64 EOCD record if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) { // console.warn("ZIP64 end of central directory not where expected."); this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); if (this.relativeOffsetEndOfZip64CentralDir < 0) { throw new Error("Corrupted zip: can't find the ZIP64 end of central directory"); } } this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir); this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); this.readBlockZip64EndOfCentral(); } var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize; if (this.zip64) { expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize; } var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset; if (extraBytes > 0) { // console.warn(extraBytes, "extra bytes at beginning or within zipfile"); if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) { // The offsets seem wrong, but we have something at the specified offset. // So… we keep it. } else { // the offset is wrong, update the "zero" of the reader // this happens if data has been prepended (crx files for example) this.reader.zero = extraBytes; } } else if (extraBytes < 0) { throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes."); } }, prepareReader: function prepareReader(data) { this.reader = readerFor(data); }, /** * Read a zip file and create ZipEntries. * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file. */ load: function load(data) { this.prepareReader(data); this.readEndOfCentral(); this.readCentralDir(); this.readLocalFiles(); } }; // }}} end of ZipEntries module.exports = ZipEntries; }, { "./reader/readerFor": 22, "./signature": 23, "./support": 30, "./utils": 32, "./zipEntry": 34 }], 34: [function (require, module, exports) { "use strict"; var readerFor = require("./reader/readerFor"); var utils = require("./utils"); var CompressedObject = require("./compressedObject"); var crc32fn = require("./crc32"); var utf8 = require("./utf8"); var compressions = require("./compressions"); var support = require("./support"); var MADE_BY_DOS = 0x00; var MADE_BY_UNIX = 0x03; /** * Find a compression registered in JSZip. * @param {string} compressionMethod the method magic to find. * @return {Object|null} the JSZip compression object, null if none found. */ var findCompression = function findCompression(compressionMethod) { for (var method in compressions) { if (!Object.prototype.hasOwnProperty.call(compressions, method)) { continue; } if (compressions[method].magic === compressionMethod) { return compressions[method]; } } return null; }; // class ZipEntry {{{ /** * An entry in the zip file. * @constructor * @param {Object} options Options of the current file. * @param {Object} loadOptions Options for loading the stream. */ function ZipEntry(options, loadOptions) { this.options = options; this.loadOptions = loadOptions; } ZipEntry.prototype = { /** * say if the file is encrypted. * @return {boolean} true if the file is encrypted, false otherwise. */ isEncrypted: function isEncrypted() { // bit 1 is set return (this.bitFlag & 0x0001) === 0x0001; }, /** * say if the file has utf-8 filename/comment. * @return {boolean} true if the filename/comment is in utf-8, false otherwise. */ useUTF8: function useUTF8() { // bit 11 is set return (this.bitFlag & 0x0800) === 0x0800; }, /** * Read the local part of a zip file and add the info in this object. * @param {DataReader} reader the reader to use. */ readLocalPart: function readLocalPart(reader) { var compression, localExtraFieldsLength; // we already know everything from the central dir ! // If the central dir data are false, we are doomed. // On the bright side, the local part is scary : zip64, data descriptors, both, etc. // The less data we get here, the more reliable this should be. // Let's skip the whole header and dash to the data ! reader.skip(22); // in some zip created on windows, the filename stored in the central dir contains \ instead of /. // Strangely, the filename here is OK. // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators... // Search "unzip mismatching "local" filename continuing with "central" filename version" on // the internet. // // I think I see the logic here : the central directory is used to display // content and the local directory is used to extract the files. Mixing / and \ // may be used to display \ to windows users and use / when extracting the files. // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394 this.fileNameLength = reader.readInt(2); localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding. this.fileName = reader.readData(this.fileNameLength); reader.skip(localExtraFieldsLength); if (this.compressedSize === -1 || this.uncompressedSize === -1) { throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)"); } compression = findCompression(this.compressionMethod); if (compression === null) { // no compression found throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")"); } this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize)); }, /** * Read the central part of a zip file and add the info in this object. * @param {DataReader} reader the reader to use. */ readCentralPart: function readCentralPart(reader) { this.versionMadeBy = reader.readInt(2); reader.skip(2); // this.versionNeeded = reader.readInt(2); this.bitFlag = reader.readInt(2); this.compressionMethod = reader.readString(2); this.date = reader.readDate(); this.crc32 = reader.readInt(4); this.compressedSize = reader.readInt(4); this.uncompressedSize = reader.readInt(4); var fileNameLength = reader.readInt(2); this.extraFieldsLength = reader.readInt(2); this.fileCommentLength = reader.readInt(2); this.diskNumberStart = reader.readInt(2); this.internalFileAttributes = reader.readInt(2); this.externalFileAttributes = reader.readInt(4); this.localHeaderOffset = reader.readInt(4); if (this.isEncrypted()) { throw new Error("Encrypted zip are not supported"); } // will be read in the local part, see the comments there reader.skip(fileNameLength); this.readExtraFields(reader); this.parseZIP64ExtraField(reader); this.fileComment = reader.readData(this.fileCommentLength); }, /** * Parse the external file attributes and get the unix/dos permissions. */ processAttributes: function processAttributes() { this.unixPermissions = null; this.dosPermissions = null; var madeBy = this.versionMadeBy >> 8; // Check if we have the DOS directory flag set. // We look for it in the DOS and UNIX permissions // but some unknown platform could set it as a compatibility flag. this.dir = this.externalFileAttributes & 0x0010 ? true : false; if (madeBy === MADE_BY_DOS) { // first 6 bits (0 to 5) this.dosPermissions = this.externalFileAttributes & 0x3F; } if (madeBy === MADE_BY_UNIX) { this.unixPermissions = this.externalFileAttributes >> 16 & 0xFFFF; // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8); } // fail safe : if the name ends with a / it probably means a folder if (!this.dir && this.fileNameStr.slice(-1) === "/") { this.dir = true; } }, /** * Parse the ZIP64 extra field and merge the info in the current ZipEntry. * @param {DataReader} reader the reader to use. */ parseZIP64ExtraField: function parseZIP64ExtraField() { if (!this.extraFields[0x0001]) { return; } // should be something, preparing the extra reader var extraReader = readerFor(this.extraFields[0x0001].value); // I really hope that these 64bits integer can fit in 32 bits integer, because js // won't let us have more. if (this.uncompressedSize === utils.MAX_VALUE_32BITS) { this.uncompressedSize = extraReader.readInt(8); } if (this.compressedSize === utils.MAX_VALUE_32BITS) { this.compressedSize = extraReader.readInt(8); } if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) { this.localHeaderOffset = extraReader.readInt(8); } if (this.diskNumberStart === utils.MAX_VALUE_32BITS) { this.diskNumberStart = extraReader.readInt(4); } }, /** * Read the central part of a zip file and add the info in this object. * @param {DataReader} reader the reader to use. */ readExtraFields: function readExtraFields(reader) { var end = reader.index + this.extraFieldsLength, extraFieldId, extraFieldLength, extraFieldValue; if (!this.extraFields) { this.extraFields = {}; } while (reader.index + 4 < end) { extraFieldId = reader.readInt(2); extraFieldLength = reader.readInt(2); extraFieldValue = reader.readData(extraFieldLength); this.extraFields[extraFieldId] = { id: extraFieldId, length: extraFieldLength, value: extraFieldValue }; } reader.setIndex(end); }, /** * Apply an UTF8 transformation if needed. */ handleUTF8: function handleUTF8() { var decodeParamType = support.uint8array ? "uint8array" : "array"; if (this.useUTF8()) { this.fileNameStr = utf8.utf8decode(this.fileName); this.fileCommentStr = utf8.utf8decode(this.fileComment); } else { var upath = this.findExtraFieldUnicodePath(); if (upath !== null) { this.fileNameStr = upath; } else { // ASCII text or unsupported code page var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName); this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray); } var ucomment = this.findExtraFieldUnicodeComment(); if (ucomment !== null) { this.fileCommentStr = ucomment; } else { // ASCII text or unsupported code page var commentByteArray = utils.transformTo(decodeParamType, this.fileComment); this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray); } } }, /** * Find the unicode path declared in the extra field, if any. * @return {String} the unicode path, null otherwise. */ findExtraFieldUnicodePath: function findExtraFieldUnicodePath() { var upathField = this.extraFields[0x7075]; if (upathField) { var extraReader = readerFor(upathField.value); // wrong version if (extraReader.readInt(1) !== 1) { return null; } // the crc of the filename changed, this field is out of date. if (crc32fn(this.fileName) !== extraReader.readInt(4)) { return null; } return utf8.utf8decode(extraReader.readData(upathField.length - 5)); } return null; }, /** * Find the unicode comment declared in the extra field, if any. * @return {String} the unicode comment, null otherwise. */ findExtraFieldUnicodeComment: function findExtraFieldUnicodeComment() { var ucommentField = this.extraFields[0x6375]; if (ucommentField) { var extraReader = readerFor(ucommentField.value); // wrong version if (extraReader.readInt(1) !== 1) { return null; } // the crc of the comment changed, this field is out of date. if (crc32fn(this.fileComment) !== extraReader.readInt(4)) { return null; } return utf8.utf8decode(extraReader.readData(ucommentField.length - 5)); } return null; } }; module.exports = ZipEntry; }, { "./compressedObject": 2, "./compressions": 3, "./crc32": 4, "./reader/readerFor": 22, "./support": 30, "./utf8": 31, "./utils": 32 }], 35: [function (require, module, exports) { "use strict"; var StreamHelper = require("./stream/StreamHelper"); var DataWorker = require("./stream/DataWorker"); var utf8 = require("./utf8"); var CompressedObject = require("./compressedObject"); var GenericWorker = require("./stream/GenericWorker"); /** * A simple object representing a file in the zip file. * @constructor * @param {string} name the name of the file * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data * @param {Object} options the options of the file */ var ZipObject = function ZipObject(name, data, options) { this.name = name; this.dir = options.dir; this.date = options.date; this.comment = options.comment; this.unixPermissions = options.unixPermissions; this.dosPermissions = options.dosPermissions; this._data = data; this._dataBinary = options.binary; // keep only the compression this.options = { compression: options.compression, compressionOptions: options.compressionOptions }; }; ZipObject.prototype = { /** * Create an internal stream for the content of this object. * @param {String} type the type of each chunk. * @return StreamHelper the stream. */ internalStream: function internalStream(type) { var result = null, outputType = "string"; try { if (!type) { throw new Error("No output type specified."); } outputType = type.toLowerCase(); var askUnicodeString = outputType === "string" || outputType === "text"; if (outputType === "binarystring" || outputType === "text") { outputType = "string"; } result = this._decompressWorker(); var isUnicodeString = !this._dataBinary; if (isUnicodeString && !askUnicodeString) { result = result.pipe(new utf8.Utf8EncodeWorker()); } if (!isUnicodeString && askUnicodeString) { result = result.pipe(new utf8.Utf8DecodeWorker()); } } catch (e) { result = new GenericWorker("error"); result.error(e); } return new StreamHelper(result, outputType, ""); }, /** * Prepare the content in the asked type. * @param {String} type the type of the result. * @param {Function} onUpdate a function to call on each internal update. * @return Promise the promise of the result. */ async: function async(type, onUpdate) { return this.internalStream(type).accumulate(onUpdate); }, /** * Prepare the content as a nodejs stream. * @param {String} type the type of each chunk. * @param {Function} onUpdate a function to call on each internal update. * @return Stream the stream. */ nodeStream: function nodeStream(type, onUpdate) { return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate); }, /** * Return a worker for the compressed content. * @private * @param {Object} compression the compression object to use. * @param {Object} compressionOptions the options to use when compressing. * @return Worker the worker. */ _compressWorker: function _compressWorker(compression, compressionOptions) { if (this._data instanceof CompressedObject && this._data.compression.magic === compression.magic) { return this._data.getCompressedWorker(); } else { var result = this._decompressWorker(); if (!this._dataBinary) { result = result.pipe(new utf8.Utf8EncodeWorker()); } return CompressedObject.createWorkerFrom(result, compression, compressionOptions); } }, /** * Return a worker for the decompressed content. * @private * @return Worker the worker. */ _decompressWorker: function _decompressWorker() { if (this._data instanceof CompressedObject) { return this._data.getContentWorker(); } else if (this._data instanceof GenericWorker) { return this._data; } else { return new DataWorker(this._data); } } }; var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"]; var removedFn = function removedFn() { throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); }; for (var i = 0; i < removedMethods.length; i++) { ZipObject.prototype[removedMethods[i]] = removedFn; } module.exports = ZipObject; }, { "./compressedObject": 2, "./stream/DataWorker": 27, "./stream/GenericWorker": 28, "./stream/StreamHelper": 29, "./utf8": 31 }], 36: [function (require, module, exports) { (function (global) { 'use strict'; var Mutation = global.MutationObserver || global.WebKitMutationObserver; var scheduleDrain; { if (Mutation) { var called = 0; var observer = new Mutation(nextTick); var element = global.document.createTextNode(''); observer.observe(element, { characterData: true }); scheduleDrain = function scheduleDrain() { element.data = called = ++called % 2; }; } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { var channel = new global.MessageChannel(); channel.port1.onmessage = nextTick; scheduleDrain = function scheduleDrain() { channel.port2.postMessage(0); }; } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { scheduleDrain = function scheduleDrain() { // Create a