碧云体育休闲社区AR党建小程序
fuchengshen
2021-11-30 c1034159f2d7ae8218d470061daef8853b30b14f
merge
36个文件已添加
34267 ■■■■■ 已修改文件
miniprogram-9/.eslintrc.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/README.md 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/app.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/app.json 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/app.wxss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/asstes/CSS/icon.wxss 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/components/loading/loading.js 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/components/loading/loading.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/components/loading/loading.wxml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/components/loading/loading.wxss 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/controls/CameraControls.js 1046 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/controls/OrbitControls.js 1183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/controls/TrackballControls.js 712 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/DDSLoader.js 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/EquirectangularToCubeGenerator.js 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/GLTFLoader.js 3284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/MTLLoader.js 547 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/OBJLoader.js 816 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/RGBELoader.js 542 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/loaders/STLLoader.js 409 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/pmrem/PMREMCubeUVPacker.js 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/pmrem/PMREMGenerator.js 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/jsm/utils/SkeletonUtils.js 599 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/libs/three.weapp.js 22892 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/ThreeModel/ThreeModel.js 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/ThreeModel/ThreeModel.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/ThreeModel/ThreeModel.wxml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/ThreeModel/ThreeModel.wxss 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/index/crsClient.js 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/index/index.js 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/index/index.json 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/index/index.wxml 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/pages/index/index.wxss 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/project.config.json 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/sitemap.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/utils/util.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
miniprogram-9/.eslintrc.js
New file
@@ -0,0 +1,31 @@
/*
 * Eslint config file
 * Documentation: https://eslint.org/docs/user-guide/configuring/
 * Install the Eslint extension before using this feature.
 */
module.exports = {
  env: {
    es6: true,
    browser: true,
    node: true,
  },
  ecmaFeatures: {
    modules: true,
  },
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  globals: {
    wx: true,
    App: true,
    Page: true,
    getCurrentPages: true,
    getApp: true,
    Component: true,
    requirePlugin: true,
    requireMiniProgram: true,
  },
  // extends: 'eslint:recommended',
  rules: {},
}
miniprogram-9/README.md
New file
@@ -0,0 +1,4 @@
## by_AR
碧云
miniprogram-9/app.js
New file
@@ -0,0 +1,19 @@
// app.js
App({
  onLaunch() {
    // 展示本地存储能力
    const logs = wx.getStorageSync('logs') || []
    logs.unshift(Date.now())
    wx.setStorageSync('logs', logs)
    // 登录
    wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
      }
    })
  },
  globalData: {
    userInfo: null
  }
})
miniprogram-9/app.json
New file
@@ -0,0 +1,15 @@
{
  "pages": [
    "pages/index/index",
    "pages/logs/logs",
    "pages/ThreeModel/ThreeModel"
  ],
  "window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "Weixin",
    "navigationBarTextStyle": "black"
  },
  "style": "v2",
  "sitemapLocation": "sitemap.json"
}
miniprogram-9/app.wxss
New file
@@ -0,0 +1,11 @@
/**app.wxss**/
@import "./asstes/CSS/icon.wxss";
.container {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 200rpx 0;
  box-sizing: border-box;
}
miniprogram-9/asstes/CSS/icon.wxss
New file
@@ -0,0 +1,38 @@
@font-face {
  font-family: "iconfont"; /* Project id 2927301 */
  src: url('//at.alicdn.com/t/font_2927301_pb0nhfotata.woff2?t=1636528701480') format('woff2'),
       url('//at.alicdn.com/t/font_2927301_pb0nhfotata.woff?t=1636528701480') format('woff'),
       url('//at.alicdn.com/t/font_2927301_pb0nhfotata.ttf?t=1636528701480') format('truetype');
}
.iconfont {
  font-family: "iconfont" !important;
  font-size: 16px;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.icon-guanbi:before {
  content: "\e604";
}
.icon-bofang:before {
  content: "\e87c";
}
.icon-yanzhengmashibie:before {
  content: "\e658";
}
.icon-tubiaoji_tupianshibie:before {
  content: "\e696";
}
.icon-xiangji:before {
  content: "\e616";
}
.icon-view:before {
  content: "\e607";
}
miniprogram-9/components/loading/loading.js
New file
@@ -0,0 +1,7 @@
Component({
    properties: {
        text: {type: String, value: null},
    },
    data: {},
    methods: {},
});
miniprogram-9/components/loading/loading.json
New file
@@ -0,0 +1,5 @@
{
  "component": true,
  "usingComponents": {
  }
}
miniprogram-9/components/loading/loading.wxml
New file
@@ -0,0 +1,11 @@
<view class="container">
    <view class="line"></view>
    <view class="loading">
        <view class="loading-ani"></view>
        <view class="loading-ani"></view>
        <view class="loading-ani"></view>
        <view class="loading-ani"></view>
        <view class="loading-ani"></view>
    </view>
</view>
<view wx:if="{{text}}" class="text">{{text}}</view>
miniprogram-9/components/loading/loading.wxss
New file
@@ -0,0 +1,73 @@
.container {
    width: 400rpx;
    height: 360rpx;
    position: relative;
}
.line {
    position: absolute;
    width: 400rpx;
    height: 12rpx;
    background: linear-gradient(90deg, rgba(243, 152, 0, 0) 0%, rgba(243, 152, 0, 1) 20%, rgba(243, 152, 0, 1) 80%, rgba(243, 152, 0, 0) 100%);
    top: 0;
    bottom: 0;
    margin: auto;
}
.text {
    text-align: center;
    font-size: 32rpx;
    font-weight: 600;
    color: #FFFFFF;
    text-shadow: 0 4rpx 8rpx rgba(0, 0, 0, 0.5);
}
.loading {
    position: absolute;
    width: fit-content;
    height: fit-content;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    margin: auto;
}
.loading-ani {
    width: 12rpx;
    height: 100rpx;
    background: #f39800;
    display: inline-block;
    margin: 0 10rpx;
    animation: loading-ani linear 1s infinite;
}
.loading-ani:nth-child(1) {
    animation-delay: 0s;
}
.loading-ani:nth-child(2) {
    animation-delay: -.15s;
}
.loading-ani:nth-child(3) {
    animation-delay: -.3s;
}
.loading-ani:nth-child(4) {
    animation-delay: -.45s;
}
.loading-ani:nth-child(5) {
    animation-delay: -.6s;
}
@keyframes loading-ani {
    0%, 100%, 60% {
        transform: scaleY(1)
    }
    30% {
        transform: scaleY(3)
    }
}
miniprogram-9/jsm/controls/CameraControls.js
New file
@@ -0,0 +1,1046 @@
( function () {
    var CameraControls = function ( object, domElement ) {
        if ( domElement === undefined ) console.warn( 'THREE.CameraControls: The second parameter "domElement" is now mandatory.' );
        if ( domElement === document ) console.error( 'THREE.CameraControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
        this.object = object;
        this.domElement = domElement; // Set to false to disable this control
        this.enabled = true; // "target" sets the location of focus, where the object orbits around
        this.target = new THREE.Vector3(); // Set to true to enable trackball behavior
        this.trackball = false; // How far you can dolly in and out ( PerspectiveCamera only )
        this.minDistance = 0;
        this.maxDistance = Infinity; // How far you can zoom in and out ( OrthographicCamera only )
        this.minZoom = 0;
        this.maxZoom = Infinity; // How far you can orbit vertically, upper and lower limits.
        // Range is 0 to Math.PI radians.
        this.minPolarAngle = 0; // radians
        this.maxPolarAngle = Math.PI; // radians
        // How far you can orbit horizontally, upper and lower limits.
        // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
        this.minAzimuthAngle = - Infinity; // radians
        this.maxAzimuthAngle = Infinity; // radians
        // Set to true to enable damping (inertia)
        // If damping is enabled, you must call controls.update() in your animation loop
        this.enableDamping = false;
        this.dampingFactor = 0.05; // This option enables dollying in and out; property named as "zoom" for backwards compatibility
        // Set to false to disable zooming
        this.enableZoom = true;
        this.zoomSpeed = 1.0; // Set to false to disable rotating
        this.enableRotate = true;
        this.rotateSpeed = 1.0; // Set to false to disable panning
        this.enablePan = true;
        this.panSpeed = 1.0;
        this.screenSpacePanning = false; // if true, pan in screen-space
        this.keyPanSpeed = 7.0; // pixels moved per arrow key push
        // Set to true to automatically rotate around the target
        // If auto-rotate is enabled, you must call controls.update() in your animation loop
        // auto-rotate is not supported for trackball behavior
        this.autoRotate = false;
        this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
        // Set to false to disable use of the keys
        this.enableKeys = true; // The four arrow keys
        this.keys = {
            LEFT: 37,
            UP: 38,
            RIGHT: 39,
            BOTTOM: 40
        }; // Mouse buttons
        this.mouseButtons = {
            LEFT: THREE.MOUSE.ROTATE,
            MIDDLE: THREE.MOUSE.DOLLY,
            RIGHT: THREE.MOUSE.PAN
        }; // Touch fingers
        this.touches = {
            ONE: THREE.TOUCH.ROTATE,
            TWO: THREE.TOUCH.DOLLY_PAN
        }; // for reset
        this.target0 = this.target.clone();
        this.position0 = this.object.position.clone();
        this.quaternion0 = this.object.quaternion.clone();
        this.zoom0 = this.object.zoom; //
        // public methods
        //
        this.getPolarAngle = function () {
            return spherical.phi;
        };
        this.getAzimuthalAngle = function () {
            return spherical.theta;
        };
        this.saveState = function () {
            scope.target0.copy( scope.target );
            scope.position0.copy( scope.object.position );
            scope.quaternion0.copy( scope.object.quaternion );
            scope.zoom0 = scope.object.zoom;
        };
        this.reset = function () {
            scope.target.copy( scope.target0 );
            scope.object.position.copy( scope.position0 );
            scope.object.quaternion.copy( scope.quaternion0 );
            scope.object.zoom = scope.zoom0;
            scope.object.updateProjectionMatrix();
            scope.dispatchEvent( changeEvent );
            scope.update();
            state = STATE.NONE;
        }; // this method is exposed, but perhaps it would be better if we can make it private...
        this.update = function () {
            var offset = new THREE.Vector3(); // so camera.up is the orbit axis
            var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
            var quatInverse = quat.clone().invert();
            var lastPosition = new THREE.Vector3();
            var lastQuaternion = new THREE.Quaternion();
            var q = new THREE.Quaternion();
            var vec = new THREE.Vector3();
            return function update() {
                var position = scope.object.position;
                offset.copy( position ).sub( scope.target );
                if ( scope.trackball ) {
                    // rotate around screen-space y-axis
                    if ( sphericalDelta.theta ) {
                        vec.set( 0, 1, 0 ).applyQuaternion( scope.object.quaternion );
                        var factor = scope.enableDamping ? scope.dampingFactor : 1;
                        q.setFromAxisAngle( vec, sphericalDelta.theta * factor );
                        scope.object.quaternion.premultiply( q );
                        offset.applyQuaternion( q );
                    } // rotate around screen-space x-axis
                    if ( sphericalDelta.phi ) {
                        vec.set( 1, 0, 0 ).applyQuaternion( scope.object.quaternion );
                        var factor = scope.enableDamping ? scope.dampingFactor : 1;
                        q.setFromAxisAngle( vec, sphericalDelta.phi * factor );
                        scope.object.quaternion.premultiply( q );
                        offset.applyQuaternion( q );
                    }
                    offset.multiplyScalar( scale );
                    offset.clampLength( scope.minDistance, scope.maxDistance );
                } else {
                    // rotate offset to "y-axis-is-up" space
                    offset.applyQuaternion( quat );
                    if ( scope.autoRotate && state === STATE.NONE ) {
                        rotateLeft( getAutoRotationAngle() );
                    }
                    spherical.setFromVector3( offset );
                    if ( scope.enableDamping ) {
                        spherical.theta += sphericalDelta.theta * scope.dampingFactor;
                        spherical.phi += sphericalDelta.phi * scope.dampingFactor;
                    } else {
                        spherical.theta += sphericalDelta.theta;
                        spherical.phi += sphericalDelta.phi;
                    } // restrict theta to be between desired limits
                    spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) ); // restrict phi to be between desired limits
                    spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
                    spherical.makeSafe();
                    spherical.radius *= scale; // restrict radius to be between desired limits
                    spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
                    offset.setFromSpherical( spherical ); // rotate offset back to "camera-up-vector-is-up" space
                    offset.applyQuaternion( quatInverse );
                } // move target to panned location
                if ( scope.enableDamping === true ) {
                    scope.target.addScaledVector( panOffset, scope.dampingFactor );
                } else {
                    scope.target.add( panOffset );
                }
                position.copy( scope.target ).add( offset );
                if ( scope.trackball === false ) {
                    scope.object.lookAt( scope.target );
                }
                if ( scope.enableDamping === true ) {
                    sphericalDelta.theta *= 1 - scope.dampingFactor;
                    sphericalDelta.phi *= 1 - scope.dampingFactor;
                    panOffset.multiplyScalar( 1 - scope.dampingFactor );
                } else {
                    sphericalDelta.set( 0, 0, 0 );
                    panOffset.set( 0, 0, 0 );
                }
                scale = 1; // update condition is:
                // min(camera displacement, camera rotation in radians)^2 > EPS
                // using small-angle approximation cos(x/2) = 1 - x^2 / 8
                if ( zoomChanged || lastPosition.distanceToSquared( scope.object.position ) > EPS || 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
                    scope.dispatchEvent( changeEvent );
                    lastPosition.copy( scope.object.position );
                    lastQuaternion.copy( scope.object.quaternion );
                    zoomChanged = false;
                    return true;
                }
                return false;
            };
        }();
        this.dispose = function () {
            scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
            scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
            scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
            scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
            scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
            scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
            document.removeEventListener( 'mousemove', onMouseMove, false );
            document.removeEventListener( 'mouseup', onMouseUp, false );
            scope.domElement.removeEventListener( 'keydown', onKeyDown, false ); //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
        }; //
        // internals
        //
        var scope = this;
        var changeEvent = {
            type: 'change'
        };
        var startEvent = {
            type: 'start'
        };
        var endEvent = {
            type: 'end'
        };
        var STATE = {
            NONE: - 1,
            ROTATE: 0,
            DOLLY: 1,
            PAN: 2,
            TOUCH_ROTATE: 3,
            TOUCH_PAN: 4,
            TOUCH_DOLLY_PAN: 5,
            TOUCH_DOLLY_ROTATE: 6
        };
        var state = STATE.NONE;
        var EPS = 0.000001; // current position in spherical coordinates
        var spherical = new THREE.Spherical();
        var sphericalDelta = new THREE.Spherical();
        var scale = 1;
        var panOffset = new THREE.Vector3();
        var zoomChanged = false;
        var rotateStart = new THREE.Vector2();
        var rotateEnd = new THREE.Vector2();
        var rotateDelta = new THREE.Vector2();
        var panStart = new THREE.Vector2();
        var panEnd = new THREE.Vector2();
        var panDelta = new THREE.Vector2();
        var dollyStart = new THREE.Vector2();
        var dollyEnd = new THREE.Vector2();
        var dollyDelta = new THREE.Vector2();
        function getAutoRotationAngle() {
            return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
        }
        function getZoomScale() {
            return Math.pow( 0.95, scope.zoomSpeed );
        }
        function rotateLeft( angle ) {
            sphericalDelta.theta -= angle;
        }
        function rotateUp( angle ) {
            sphericalDelta.phi -= angle;
        }
        var panLeft = function () {
            var v = new THREE.Vector3();
            return function panLeft( distance, objectMatrix ) {
                v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
                v.multiplyScalar( - distance );
                panOffset.add( v );
            };
        }();
        var panUp = function () {
            var v = new THREE.Vector3();
            return function panUp( distance, objectMatrix ) {
                if ( scope.screenSpacePanning === true ) {
                    v.setFromMatrixColumn( objectMatrix, 1 );
                } else {
                    v.setFromMatrixColumn( objectMatrix, 0 );
                    v.crossVectors( scope.object.up, v );
                }
                v.multiplyScalar( distance );
                panOffset.add( v );
            };
        }(); // deltaX and deltaY are in pixels; right and down are positive
        var pan = function () {
            var offset = new THREE.Vector3();
            return function pan( deltaX, deltaY ) {
                var element = scope.domElement;
                if ( scope.object.isPerspectiveCamera ) {
                    // perspective
                    var position = scope.object.position;
                    offset.copy( position ).sub( scope.target );
                    var targetDistance = offset.length(); // half of the fov is center to top of screen
                    targetDistance *= Math.tan( scope.object.fov / 2 * Math.PI / 180.0 ); // we use only clientHeight here so aspect ratio does not distort speed
                    panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
                    panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
                } else if ( scope.object.isOrthographicCamera ) {
                    // orthographic
                    panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
                    panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
                } else {
                    // camera neither orthographic nor perspective
                    console.warn( 'WARNING: CameraControls.js encountered an unknown camera type - pan disabled.' );
                    scope.enablePan = false;
                }
            };
        }();
        function dollyIn( dollyScale ) {
            if ( scope.object.isPerspectiveCamera ) {
                scale /= dollyScale;
            } else if ( scope.object.isOrthographicCamera ) {
                scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
                scope.object.updateProjectionMatrix();
                zoomChanged = true;
            } else {
                console.warn( 'WARNING: CameraControls.js encountered an unknown camera type - dolly/zoom disabled.' );
                scope.enableZoom = false;
            }
        }
        function dollyOut( dollyScale ) {
            if ( scope.object.isPerspectiveCamera ) {
                scale *= dollyScale;
            } else if ( scope.object.isOrthographicCamera ) {
                scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
                scope.object.updateProjectionMatrix();
                zoomChanged = true;
            } else {
                console.warn( 'WARNING: CameraControls.js encountered an unknown camera type - dolly/zoom disabled.' );
                scope.enableZoom = false;
            }
        } //
        // event callbacks - update the object state
        //
        function handleMouseDownRotate( event ) {
            rotateStart.set( event.clientX, event.clientY );
        }
        function handleMouseDownDolly( event ) {
            dollyStart.set( event.clientX, event.clientY );
        }
        function handleMouseDownPan( event ) {
            panStart.set( event.clientX, event.clientY );
        }
        function handleMouseMoveRotate( event ) {
            rotateEnd.set( event.clientX, event.clientY );
            rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
            var element = scope.domElement;
            rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
            rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
            rotateStart.copy( rotateEnd );
            scope.update();
        }
        function handleMouseMoveDolly( event ) {
            dollyEnd.set( event.clientX, event.clientY );
            dollyDelta.subVectors( dollyEnd, dollyStart );
            if ( dollyDelta.y > 0 ) {
                dollyIn( getZoomScale() );
            } else if ( dollyDelta.y < 0 ) {
                dollyOut( getZoomScale() );
            }
            dollyStart.copy( dollyEnd );
            scope.update();
        }
        function handleMouseMovePan( event ) {
            panEnd.set( event.clientX, event.clientY );
            panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
            pan( panDelta.x, panDelta.y );
            panStart.copy( panEnd );
            scope.update();
        }
        function handleMouseUp() { // no-op
        }
        function handleMouseWheel( event ) {
            if ( event.deltaY < 0 ) {
                dollyOut( getZoomScale() );
            } else if ( event.deltaY > 0 ) {
                dollyIn( getZoomScale() );
            }
            scope.update();
        }
        function handleKeyDown( event ) {
            var needsUpdate = false;
            switch ( event.keyCode ) {
                case scope.keys.UP:
                    pan( 0, scope.keyPanSpeed );
                    needsUpdate = true;
                    break;
                case scope.keys.BOTTOM:
                    pan( 0, - scope.keyPanSpeed );
                    needsUpdate = true;
                    break;
                case scope.keys.LEFT:
                    pan( scope.keyPanSpeed, 0 );
                    needsUpdate = true;
                    break;
                case scope.keys.RIGHT:
                    pan( - scope.keyPanSpeed, 0 );
                    needsUpdate = true;
                    break;
            }
            if ( needsUpdate ) {
                // prevent the browser from scrolling on cursor keys
                event.preventDefault();
                scope.update();
            }
        }
        function handleTouchStartRotate( event ) {
            if ( event.touches.length == 1 ) {
                rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
            } else {
                var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
                var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
                rotateStart.set( x, y );
            }
        }
        function handleTouchStartPan( event ) {
            if ( event.touches.length == 1 ) {
                panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
            } else {
                var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
                var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
                panStart.set( x, y );
            }
        }
        function handleTouchStartDolly( event ) {
            var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
            var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
            var distance = Math.sqrt( dx * dx + dy * dy );
            dollyStart.set( 0, distance );
        }
        function handleTouchStartDollyPan( event ) {
            if ( scope.enableZoom ) handleTouchStartDolly( event );
            if ( scope.enablePan ) handleTouchStartPan( event );
        }
        function handleTouchStartDollyRotate( event ) {
            if ( scope.enableZoom ) handleTouchStartDolly( event );
            if ( scope.enableRotate ) handleTouchStartRotate( event );
        }
        function handleTouchMoveRotate( event ) {
            if ( event.touches.length == 1 ) {
                rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
            } else {
                var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
                var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
                rotateEnd.set( x, y );
            }
            rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
            var element = scope.domElement;
            rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
            rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
            rotateStart.copy( rotateEnd );
        }
        function handleTouchMovePan( event ) {
            if ( event.touches.length == 1 ) {
                panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
            } else {
                var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
                var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
                panEnd.set( x, y );
            }
            panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
            pan( panDelta.x, panDelta.y );
            panStart.copy( panEnd );
        }
        function handleTouchMoveDolly( event ) {
            var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
            var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
            var distance = Math.sqrt( dx * dx + dy * dy );
            dollyEnd.set( 0, distance );
            dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
            dollyIn( dollyDelta.y );
            dollyStart.copy( dollyEnd );
        }
        function handleTouchMoveDollyPan( event ) {
            if ( scope.enableZoom ) handleTouchMoveDolly( event );
            if ( scope.enablePan ) handleTouchMovePan( event );
        }
        function handleTouchMoveDollyRotate( event ) {
            if ( scope.enableZoom ) handleTouchMoveDolly( event );
            if ( scope.enableRotate ) handleTouchMoveRotate( event );
        }
        function handleTouchEnd() { // no-op
        } //
        // event handlers - FSM: listen for events and reset state
        //
        function onMouseDown( event ) {
            if ( scope.enabled === false ) return; // Prevent the browser from scrolling.
            event.preventDefault(); // Manually set the focus since calling preventDefault above
            // prevents the browser from setting it automatically.
            scope.domElement.focus ? scope.domElement.focus() : window.focus();
            var mouseAction;
            switch ( event.button ) {
                case 0:
                    mouseAction = scope.mouseButtons.LEFT;
                    break;
                case 1:
                    mouseAction = scope.mouseButtons.MIDDLE;
                    break;
                case 2:
                    mouseAction = scope.mouseButtons.RIGHT;
                    break;
                default:
                    mouseAction = - 1;
            }
            switch ( mouseAction ) {
                case THREE.MOUSE.DOLLY:
                    if ( scope.enableZoom === false ) return;
                    handleMouseDownDolly( event );
                    state = STATE.DOLLY;
                    break;
                case THREE.MOUSE.ROTATE:
                    if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
                        if ( scope.enablePan === false ) return;
                        handleMouseDownPan( event );
                        state = STATE.PAN;
                    } else {
                        if ( scope.enableRotate === false ) return;
                        handleMouseDownRotate( event );
                        state = STATE.ROTATE;
                    }
                    break;
                case THREE.MOUSE.PAN:
                    if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
                        if ( scope.enableRotate === false ) return;
                        handleMouseDownRotate( event );
                        state = STATE.ROTATE;
                    } else {
                        if ( scope.enablePan === false ) return;
                        handleMouseDownPan( event );
                        state = STATE.PAN;
                    }
                    break;
                default:
                    state = STATE.NONE;
            }
            if ( state !== STATE.NONE ) {
                document.addEventListener( 'mousemove', onMouseMove, false );
                document.addEventListener( 'mouseup', onMouseUp, false );
                scope.dispatchEvent( startEvent );
            }
        }
        function onMouseMove( event ) {
            if ( scope.enabled === false ) return;
            event.preventDefault();
            switch ( state ) {
                case STATE.ROTATE:
                    if ( scope.enableRotate === false ) return;
                    handleMouseMoveRotate( event );
                    break;
                case STATE.DOLLY:
                    if ( scope.enableZoom === false ) return;
                    handleMouseMoveDolly( event );
                    break;
                case STATE.PAN:
                    if ( scope.enablePan === false ) return;
                    handleMouseMovePan( event );
                    break;
            }
        }
        function onMouseUp( event ) {
            if ( scope.enabled === false ) return;
            handleMouseUp( event );
            document.removeEventListener( 'mousemove', onMouseMove, false );
            document.removeEventListener( 'mouseup', onMouseUp, false );
            scope.dispatchEvent( endEvent );
            state = STATE.NONE;
        }
        function onMouseWheel( event ) {
            if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE && state !== STATE.ROTATE ) return;
            event.preventDefault();
            event.stopPropagation();
            scope.dispatchEvent( startEvent );
            handleMouseWheel( event );
            scope.dispatchEvent( endEvent );
        }
        function onKeyDown( event ) {
            if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
            handleKeyDown( event );
        }
        function onTouchStart( event ) {
            if ( scope.enabled === false ) return;
            event.preventDefault();
            switch ( event.touches.length ) {
                case 1:
                    switch ( scope.touches.ONE ) {
                        case THREE.TOUCH.ROTATE:
                            if ( scope.enableRotate === false ) return;
                            handleTouchStartRotate( event );
                            state = STATE.TOUCH_ROTATE;
                            break;
                        case THREE.TOUCH.PAN:
                            if ( scope.enablePan === false ) return;
                            handleTouchStartPan( event );
                            state = STATE.TOUCH_PAN;
                            break;
                        default:
                            state = STATE.NONE;
                    }
                    break;
                case 2:
                    switch ( scope.touches.TWO ) {
                        case THREE.TOUCH.DOLLY_PAN:
                            if ( scope.enableZoom === false && scope.enablePan === false ) return;
                            handleTouchStartDollyPan( event );
                            state = STATE.TOUCH_DOLLY_PAN;
                            break;
                        case THREE.TOUCH.DOLLY_ROTATE:
                            if ( scope.enableZoom === false && scope.enableRotate === false ) return;
                            handleTouchStartDollyRotate( event );
                            state = STATE.TOUCH_DOLLY_ROTATE;
                            break;
                        default:
                            state = STATE.NONE;
                    }
                    break;
                default:
                    state = STATE.NONE;
            }
            if ( state !== STATE.NONE ) {
                scope.dispatchEvent( startEvent );
            }
        }
        function onTouchMove( event ) {
            if ( scope.enabled === false ) return;
            event.preventDefault();
            event.stopPropagation();
            switch ( state ) {
                case STATE.TOUCH_ROTATE:
                    if ( scope.enableRotate === false ) return;
                    handleTouchMoveRotate( event );
                    scope.update();
                    break;
                case STATE.TOUCH_PAN:
                    if ( scope.enablePan === false ) return;
                    handleTouchMovePan( event );
                    scope.update();
                    break;
                case STATE.TOUCH_DOLLY_PAN:
                    if ( scope.enableZoom === false && scope.enablePan === false ) return;
                    handleTouchMoveDollyPan( event );
                    scope.update();
                    break;
                case STATE.TOUCH_DOLLY_ROTATE:
                    if ( scope.enableZoom === false && scope.enableRotate === false ) return;
                    handleTouchMoveDollyRotate( event );
                    scope.update();
                    break;
                default:
                    state = STATE.NONE;
            }
        }
        function onTouchEnd( event ) {
            if ( scope.enabled === false ) return;
            handleTouchEnd( event );
            scope.dispatchEvent( endEvent );
            state = STATE.NONE;
        }
        function onContextMenu( event ) {
            if ( scope.enabled === false ) return;
            event.preventDefault();
        } //
        scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
        scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
        scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
        scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
        scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
        scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
        scope.domElement.addEventListener( 'keydown', onKeyDown, false ); // make sure element can receive keys.
        if ( scope.domElement.tabIndex === - 1 ) {
            scope.domElement.tabIndex = 0;
        } // force an update at start
        this.object.lookAt( scope.target );
        this.update();
        this.saveState();
    };
    CameraControls.prototype = Object.create( THREE.EventDispatcher.prototype );
    CameraControls.prototype.constructor = CameraControls; // OrbitControls maintains the "up" direction, camera.up (+Y by default).
    //
    //    Orbit - left mouse / touch: one-finger move
    //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
    //    Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
    var OrbitControls = function ( object, domElement ) {
        CameraControls.call( this, object, domElement );
        this.mouseButtons.LEFT = THREE.MOUSE.ROTATE;
        this.mouseButtons.RIGHT = THREE.MOUSE.PAN;
        this.touches.ONE = THREE.TOUCH.ROTATE;
        this.touches.TWO = THREE.TOUCH.DOLLY_PAN;
    };
    OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
    OrbitControls.prototype.constructor = OrbitControls; // MapControls maintains the "up" direction, camera.up (+Y by default)
    //
    //    Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
    //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
    //    Pan - left mouse, or left right + ctrl/meta/shiftKey, or arrow keys / touch: one-finger move
    var MapControls = function ( object, domElement ) {
        CameraControls.call( this, object, domElement );
        this.mouseButtons.LEFT = THREE.MOUSE.PAN;
        this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
        this.touches.ONE = THREE.TOUCH.PAN;
        this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
    };
    MapControls.prototype = Object.create( THREE.EventDispatcher.prototype );
    MapControls.prototype.constructor = MapControls; // TrackballControls allows the camera to rotate over the polls and does not maintain camera.up
    //
    //    Orbit - left mouse / touch: one-finger move
    //    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
    //    Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
    var TrackballControls = function ( object, domElement ) {
        CameraControls.call( this, object, domElement );
        this.trackball = true;
        this.screenSpacePanning = true;
        this.autoRotate = false;
        this.mouseButtons.LEFT = THREE.MOUSE.ROTATE;
        this.mouseButtons.RIGHT = THREE.MOUSE.PAN;
        this.touches.ONE = THREE.TOUCH.ROTATE;
        this.touches.TWO = THREE.TOUCH.DOLLY_PAN;
    };
    TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
    TrackballControls.prototype.constructor = TrackballControls;
    THREE.CameraControls = CameraControls;
    THREE.MapControls = MapControls;
    THREE.OrbitControls = OrbitControls;
    THREE.TrackballControls = TrackballControls;
} )();
miniprogram-9/jsm/controls/OrbitControls.js
New file
@@ -0,0 +1,1183 @@
/**
 * @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
 * @author ScieCode / http://github.com/sciecode
 */
import {
    EventDispatcher,
    MOUSE,
    Quaternion,
    Spherical,
    TOUCH,
    Vector2,
    Vector3,
    global as window
} from "../../libs/three.weapp.js";
// This set of controls performs orbiting, dollying (zooming), and panning.
// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
//
//    Orbit - left mouse / touch: one-finger move
//    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
//    Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
var OrbitControls = function (object, domElement) {
    this.object = object;
    this.domElement = (domElement !== undefined) ? domElement : document;
    // Set to false to disable this control
    this.enabled = true;
    // "target" sets the location of focus, where the object orbits around
    this.target = new Vector3();
    // How far you can dolly in and out ( PerspectiveCamera only )
    this.minDistance = 0;
    this.maxDistance = Infinity;
    // How far you can zoom in and out ( OrthographicCamera only )
    this.minZoom = 0;
    this.maxZoom = Infinity;
    // How far you can orbit vertically, upper and lower limits.
    // Range is 0 to Math.PI radians.
    this.minPolarAngle = 0; // radians
    this.maxPolarAngle = Math.PI; // radians
    // How far you can orbit horizontally, upper and lower limits.
    // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
    this.minAzimuthAngle = - Infinity; // radians
    this.maxAzimuthAngle = Infinity; // radians
    // Set to true to enable damping (inertia)
    // If damping is enabled, you must call controls.update() in your animation loop
    this.enableDamping = false;
    this.dampingFactor = 0.05;
    // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
    // Set to false to disable zooming
    this.enableZoom = true;
    this.zoomSpeed = 1.0;
    // Set to false to disable rotating
    this.enableRotate = true;
    this.rotateSpeed = 1.0;
    // Set to false to disable panning
    this.enablePan = true;
    this.panSpeed = 1.0;
    this.screenSpacePanning = false; // if true, pan in screen-space
    this.keyPanSpeed = 7.0;    // pixels moved per arrow key push
    // Set to true to automatically rotate around the target
    // If auto-rotate is enabled, you must call controls.update() in your animation loop
    this.autoRotate = false;
    this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
    // Set to false to disable use of the keys
    this.enableKeys = true;
    // The four arrow keys
    this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
    // Mouse buttons
    this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.DOLLY, RIGHT: MOUSE.PAN };
    // Touch fingers
    this.touches = { ONE: TOUCH.ROTATE, TWO: TOUCH.DOLLY_PAN };
    // for reset
    this.target0 = this.target.clone();
    this.position0 = this.object.position.clone();
    this.zoom0 = this.object.zoom;
    //
    // public methods
    //
    this.getPolarAngle = function () {
        return spherical.phi;
    };
    this.getAzimuthalAngle = function () {
        return spherical.theta;
    };
    this.saveState = function () {
        scope.target0.copy(scope.target);
        scope.position0.copy(scope.object.position);
        scope.zoom0 = scope.object.zoom;
    };
    this.reset = function () {
        scope.target.copy(scope.target0);
        scope.object.position.copy(scope.position0);
        scope.object.zoom = scope.zoom0;
        scope.object.updateProjectionMatrix();
        scope.dispatchEvent(changeEvent);
        scope.update();
        state = STATE.NONE;
    };
    // this method is exposed, but perhaps it would be better if we can make it private...
    this.update = function () {
        var offset = new Vector3();
        // so camera.up is the orbit axis
        var quat = new Quaternion().setFromUnitVectors(object.up, new Vector3(0, 1, 0));
        var quatInverse = quat.clone().inverse();
        var lastPosition = new Vector3();
        var lastQuaternion = new Quaternion();
        return function update() {
            var position = scope.object.position;
            offset.copy(position).sub(scope.target);
            // rotate offset to "y-axis-is-up" space
            offset.applyQuaternion(quat);
            // angle from z-axis around y-axis
            spherical.setFromVector3(offset);
            if (scope.autoRotate && state === STATE.NONE) {
                rotateLeft(getAutoRotationAngle());
            }
            if (scope.enableDamping) {
                spherical.theta += sphericalDelta.theta * scope.dampingFactor;
                spherical.phi += sphericalDelta.phi * scope.dampingFactor;
            } else {
                spherical.theta += sphericalDelta.theta;
                spherical.phi += sphericalDelta.phi;
            }
            // restrict theta to be between desired limits
            spherical.theta = Math.max(scope.minAzimuthAngle, Math.min(scope.maxAzimuthAngle, spherical.theta));
            // restrict phi to be between desired limits
            spherical.phi = Math.max(scope.minPolarAngle, Math.min(scope.maxPolarAngle, spherical.phi));
            spherical.makeSafe();
            spherical.radius *= scale;
            // restrict radius to be between desired limits
            spherical.radius = Math.max(scope.minDistance, Math.min(scope.maxDistance, spherical.radius));
            // move target to panned location
            if (scope.enableDamping === true) {
                scope.target.addScaledVector(panOffset, scope.dampingFactor);
            } else {
                scope.target.add(panOffset);
            }
            offset.setFromSpherical(spherical);
            // rotate offset back to "camera-up-vector-is-up" space
            offset.applyQuaternion(quatInverse);
            position.copy(scope.target).add(offset);
            scope.object.lookAt(scope.target);
            if (scope.enableDamping === true) {
                sphericalDelta.theta *= (1 - scope.dampingFactor);
                sphericalDelta.phi *= (1 - scope.dampingFactor);
                panOffset.multiplyScalar(1 - scope.dampingFactor);
            } else {
                sphericalDelta.set(0, 0, 0);
                panOffset.set(0, 0, 0);
            }
            scale = 1;
            // update condition is:
            // min(camera displacement, camera rotation in radians)^2 > EPS
            // using small-angle approximation cos(x/2) = 1 - x^2 / 8
            if (zoomChanged ||
                lastPosition.distanceToSquared(scope.object.position) > EPS ||
                8 * (1 - lastQuaternion.dot(scope.object.quaternion)) > EPS) {
                scope.dispatchEvent(changeEvent);
                lastPosition.copy(scope.object.position);
                lastQuaternion.copy(scope.object.quaternion);
                zoomChanged = false;
                return true;
            }
            return false;
        };
    }();
    this.dispose = function () {
        scope.domElement.removeEventListener('contextmenu', onContextMenu, false);
        scope.domElement.removeEventListener('mousedown', onMouseDown, false);
        scope.domElement.removeEventListener('wheel', onMouseWheel, false);
        scope.domElement.removeEventListener('touchstart', onTouchStart, false);
        scope.domElement.removeEventListener('touchend', onTouchEnd, false);
        scope.domElement.removeEventListener('touchmove', onTouchMove, false);
        document.removeEventListener('mousemove', onMouseMove, false);
        document.removeEventListener('mouseup', onMouseUp, false);
        window.removeEventListener('keydown', onKeyDown, false);
        //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
    };
    //
    // internals
    //
    var scope = this;
    var changeEvent = { type: 'change' };
    var startEvent = { type: 'start' };
    var endEvent = { type: 'end' };
    var STATE = {
        NONE: - 1,
        ROTATE: 0,
        DOLLY: 1,
        PAN: 2,
        TOUCH_ROTATE: 3,
        TOUCH_PAN: 4,
        TOUCH_DOLLY_PAN: 5,
        TOUCH_DOLLY_ROTATE: 6
    };
    var state = STATE.NONE;
    var EPS = 0.000001;
    // current position in spherical coordinates
    var spherical = new Spherical();
    var sphericalDelta = new Spherical();
    var scale = 1;
    var panOffset = new Vector3();
    var zoomChanged = false;
    var rotateStart = new Vector2();
    var rotateEnd = new Vector2();
    var rotateDelta = new Vector2();
    var panStart = new Vector2();
    var panEnd = new Vector2();
    var panDelta = new Vector2();
    var dollyStart = new Vector2();
    var dollyEnd = new Vector2();
    var dollyDelta = new Vector2();
    function getAutoRotationAngle() {
        return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
    }
    function getZoomScale() {
        return Math.pow(0.95, scope.zoomSpeed);
    }
    function rotateLeft(angle) {
        sphericalDelta.theta -= angle;
    }
    function rotateUp(angle) {
        sphericalDelta.phi -= angle;
    }
    var panLeft = function () {
        var v = new Vector3();
        return function panLeft(distance, objectMatrix) {
            v.setFromMatrixColumn(objectMatrix, 0); // get X column of objectMatrix
            v.multiplyScalar(- distance);
            panOffset.add(v);
        };
    }();
    var panUp = function () {
        var v = new Vector3();
        return function panUp(distance, objectMatrix) {
            if (scope.screenSpacePanning === true) {
                v.setFromMatrixColumn(objectMatrix, 1);
            } else {
                v.setFromMatrixColumn(objectMatrix, 0);
                v.crossVectors(scope.object.up, v);
            }
            v.multiplyScalar(distance);
            panOffset.add(v);
        };
    }();
    // deltaX and deltaY are in pixels; right and down are positive
    var pan = function () {
        var offset = new Vector3();
        return function pan(deltaX, deltaY) {
            var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
            if (scope.object.isPerspectiveCamera) {
                // perspective
                var position = scope.object.position;
                offset.copy(position).sub(scope.target);
                var targetDistance = offset.length();
                // half of the fov is center to top of screen
                targetDistance *= Math.tan((scope.object.fov / 2) * Math.PI / 180.0);
                // we use only clientHeight here so aspect ratio does not distort speed
                panLeft(2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix);
                panUp(2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix);
            } else if (scope.object.isOrthographicCamera) {
                // orthographic
                panLeft(deltaX * (scope.object.right - scope.object.left) / scope.object.zoom / element.clientWidth, scope.object.matrix);
                panUp(deltaY * (scope.object.top - scope.object.bottom) / scope.object.zoom / element.clientHeight, scope.object.matrix);
            } else {
                // camera neither orthographic nor perspective
                console.warn('WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.');
                scope.enablePan = false;
            }
        };
    }();
    function dollyIn(dollyScale) {
        if (scope.object.isPerspectiveCamera) {
            scale /= dollyScale;
        } else if (scope.object.isOrthographicCamera) {
            scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom * dollyScale));
            scope.object.updateProjectionMatrix();
            zoomChanged = true;
        } else {
            console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.');
            scope.enableZoom = false;
        }
    }
    function dollyOut(dollyScale) {
        if (scope.object.isPerspectiveCamera) {
            scale *= dollyScale;
        } else if (scope.object.isOrthographicCamera) {
            scope.object.zoom = Math.max(scope.minZoom, Math.min(scope.maxZoom, scope.object.zoom / dollyScale));
            scope.object.updateProjectionMatrix();
            zoomChanged = true;
        } else {
            console.warn('WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.');
            scope.enableZoom = false;
        }
    }
    //
    // event callbacks - update the object state
    //
    function handleMouseDownRotate(event) {
        rotateStart.set(event.clientX, event.clientY);
    }
    function handleMouseDownDolly(event) {
        dollyStart.set(event.clientX, event.clientY);
    }
    function handleMouseDownPan(event) {
        panStart.set(event.clientX, event.clientY);
    }
    function handleMouseMoveRotate(event) {
        rotateEnd.set(event.clientX, event.clientY);
        rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed);
        var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
        rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height
        rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight);
        rotateStart.copy(rotateEnd);
        scope.update();
    }
    function handleMouseMoveDolly(event) {
        dollyEnd.set(event.clientX, event.clientY);
        dollyDelta.subVectors(dollyEnd, dollyStart);
        if (dollyDelta.y > 0) {
            dollyIn(getZoomScale());
        } else if (dollyDelta.y < 0) {
            dollyOut(getZoomScale());
        }
        dollyStart.copy(dollyEnd);
        scope.update();
    }
    function handleMouseMovePan(event) {
        panEnd.set(event.clientX, event.clientY);
        panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
        pan(panDelta.x, panDelta.y);
        panStart.copy(panEnd);
        scope.update();
    }
    function handleMouseUp( /*event*/) {
        // no-op
    }
    function handleMouseWheel(event) {
        if (event.deltaY < 0) {
            dollyOut(getZoomScale());
        } else if (event.deltaY > 0) {
            dollyIn(getZoomScale());
        }
        scope.update();
    }
    function handleKeyDown(event) {
        var needsUpdate = false;
        switch (event.keyCode) {
            case scope.keys.UP:
                pan(0, scope.keyPanSpeed);
                needsUpdate = true;
                break;
            case scope.keys.BOTTOM:
                pan(0, - scope.keyPanSpeed);
                needsUpdate = true;
                break;
            case scope.keys.LEFT:
                pan(scope.keyPanSpeed, 0);
                needsUpdate = true;
                break;
            case scope.keys.RIGHT:
                pan(- scope.keyPanSpeed, 0);
                needsUpdate = true;
                break;
        }
        if (needsUpdate) {
            // prevent the browser from scrolling on cursor keys
            event.preventDefault();
            scope.update();
        }
    }
    function handleTouchStartRotate(event) {
        if (event.touches.length == 1) {
            rotateStart.set(event.touches[0].pageX, event.touches[0].pageY);
        } else {
            var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
            var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
            rotateStart.set(x, y);
        }
    }
    function handleTouchStartPan(event) {
        if (event.touches.length == 1) {
            panStart.set(event.touches[0].pageX, event.touches[0].pageY);
        } else {
            var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
            var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
            panStart.set(x, y);
        }
    }
    function handleTouchStartDolly(event) {
        var dx = event.touches[0].pageX - event.touches[1].pageX;
        var dy = event.touches[0].pageY - event.touches[1].pageY;
        var distance = Math.sqrt(dx * dx + dy * dy);
        dollyStart.set(0, distance);
    }
    function handleTouchStartDollyPan(event) {
        if (scope.enableZoom) handleTouchStartDolly(event);
        if (scope.enablePan) handleTouchStartPan(event);
    }
    function handleTouchStartDollyRotate(event) {
        if (scope.enableZoom) handleTouchStartDolly(event);
        if (scope.enableRotate) handleTouchStartRotate(event);
    }
    function handleTouchMoveRotate(event) {
        if (event.touches.length == 1) {
            rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY);
        } else {
            var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
            var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
            rotateEnd.set(x, y);
        }
        rotateDelta.subVectors(rotateEnd, rotateStart).multiplyScalar(scope.rotateSpeed);
        var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
        rotateLeft(2 * Math.PI * rotateDelta.x / element.clientHeight); // yes, height
        rotateUp(2 * Math.PI * rotateDelta.y / element.clientHeight);
        rotateStart.copy(rotateEnd);
    }
    function handleTouchMovePan(event) {
        if (event.touches.length == 1) {
            panEnd.set(event.touches[0].pageX, event.touches[0].pageY);
        } else {
            var x = 0.5 * (event.touches[0].pageX + event.touches[1].pageX);
            var y = 0.5 * (event.touches[0].pageY + event.touches[1].pageY);
            panEnd.set(x, y);
        }
        panDelta.subVectors(panEnd, panStart).multiplyScalar(scope.panSpeed);
        pan(panDelta.x, panDelta.y);
        panStart.copy(panEnd);
    }
    function handleTouchMoveDolly(event) {
        var dx = event.touches[0].pageX - event.touches[1].pageX;
        var dy = event.touches[0].pageY - event.touches[1].pageY;
        var distance = Math.sqrt(dx * dx + dy * dy);
        dollyEnd.set(0, distance);
        dollyDelta.set(0, Math.pow(dollyEnd.y / dollyStart.y, scope.zoomSpeed));
        dollyIn(dollyDelta.y);
        dollyStart.copy(dollyEnd);
    }
    function handleTouchMoveDollyPan(event) {
        if (scope.enableZoom) handleTouchMoveDolly(event);
        if (scope.enablePan) handleTouchMovePan(event);
    }
    function handleTouchMoveDollyRotate(event) {
        if (scope.enableZoom) handleTouchMoveDolly(event);
        if (scope.enableRotate) handleTouchMoveRotate(event);
    }
    function handleTouchEnd( /*event*/) {
        // no-op
    }
    //
    // event handlers - FSM: listen for events and reset state
    //
    function onMouseDown(event) {
        if (scope.enabled === false) return;
        // Prevent the browser from scrolling.
        event.preventDefault();
        // Manually set the focus since calling preventDefault above
        // prevents the browser from setting it automatically.
        scope.domElement.focus ? scope.domElement.focus() : window.focus();
        switch (event.button) {
            case 0:
                switch (scope.mouseButtons.LEFT) {
                    case MOUSE.ROTATE:
                        if (event.ctrlKey || event.metaKey || event.shiftKey) {
                            if (scope.enablePan === false) return;
                            handleMouseDownPan(event);
                            state = STATE.PAN;
                        } else {
                            if (scope.enableRotate === false) return;
                            handleMouseDownRotate(event);
                            state = STATE.ROTATE;
                        }
                        break;
                    case MOUSE.PAN:
                        if (event.ctrlKey || event.metaKey || event.shiftKey) {
                            if (scope.enableRotate === false) return;
                            handleMouseDownRotate(event);
                            state = STATE.ROTATE;
                        } else {
                            if (scope.enablePan === false) return;
                            handleMouseDownPan(event);
                            state = STATE.PAN;
                        }
                        break;
                    default:
                        state = STATE.NONE;
                }
                break;
            case 1:
                switch (scope.mouseButtons.MIDDLE) {
                    case MOUSE.DOLLY:
                        if (scope.enableZoom === false) return;
                        handleMouseDownDolly(event);
                        state = STATE.DOLLY;
                        break;
                    default:
                        state = STATE.NONE;
                }
                break;
            case 2:
                switch (scope.mouseButtons.RIGHT) {
                    case MOUSE.ROTATE:
                        if (scope.enableRotate === false) return;
                        handleMouseDownRotate(event);
                        state = STATE.ROTATE;
                        break;
                    case MOUSE.PAN:
                        if (scope.enablePan === false) return;
                        handleMouseDownPan(event);
                        state = STATE.PAN;
                        break;
                    default:
                        state = STATE.NONE;
                }
                break;
        }
        if (state !== STATE.NONE) {
            document.addEventListener('mousemove', onMouseMove, false);
            document.addEventListener('mouseup', onMouseUp, false);
            scope.dispatchEvent(startEvent);
        }
    }
    function onMouseMove(event) {
        if (scope.enabled === false) return;
        event.preventDefault();
        switch (state) {
            case STATE.ROTATE:
                if (scope.enableRotate === false) return;
                handleMouseMoveRotate(event);
                break;
            case STATE.DOLLY:
                if (scope.enableZoom === false) return;
                handleMouseMoveDolly(event);
                break;
            case STATE.PAN:
                if (scope.enablePan === false) return;
                handleMouseMovePan(event);
                break;
        }
    }
    function onMouseUp(event) {
        if (scope.enabled === false) return;
        handleMouseUp(event);
        document.removeEventListener('mousemove', onMouseMove, false);
        document.removeEventListener('mouseup', onMouseUp, false);
        scope.dispatchEvent(endEvent);
        state = STATE.NONE;
    }
    function onMouseWheel(event) {
        if (scope.enabled === false || scope.enableZoom === false || (state !== STATE.NONE && state !== STATE.ROTATE)) return;
        event.preventDefault();
        event.stopPropagation();
        scope.dispatchEvent(startEvent);
        handleMouseWheel(event);
        scope.dispatchEvent(endEvent);
    }
    function onKeyDown(event) {
        if (scope.enabled === false || scope.enableKeys === false || scope.enablePan === false) return;
        handleKeyDown(event);
    }
    function onTouchStart(event) {
        if (scope.enabled === false) return;
        event.preventDefault();
        switch (event.touches.length) {
            case 1:
                switch (scope.touches.ONE) {
                    case TOUCH.ROTATE:
                        if (scope.enableRotate === false) return;
                        handleTouchStartRotate(event);
                        state = STATE.TOUCH_ROTATE;
                        break;
                    case TOUCH.PAN:
                        if (scope.enablePan === false) return;
                        handleTouchStartPan(event);
                        state = STATE.TOUCH_PAN;
                        break;
                    default:
                        state = STATE.NONE;
                }
                break;
            case 2:
                switch (scope.touches.TWO) {
                    case TOUCH.DOLLY_PAN:
                        if (scope.enableZoom === false && scope.enablePan === false) return;
                        handleTouchStartDollyPan(event);
                        state = STATE.TOUCH_DOLLY_PAN;
                        break;
                    case TOUCH.DOLLY_ROTATE:
                        if (scope.enableZoom === false && scope.enableRotate === false) return;
                        handleTouchStartDollyRotate(event);
                        state = STATE.TOUCH_DOLLY_ROTATE;
                        break;
                    default:
                        state = STATE.NONE;
                }
                break;
            default:
                state = STATE.NONE;
        }
        if (state !== STATE.NONE) {
            scope.dispatchEvent(startEvent);
        }
    }
    function onTouchMove(event) {
        if (scope.enabled === false) return;
        event.preventDefault();
        event.stopPropagation();
        switch (state) {
            case STATE.TOUCH_ROTATE:
                if (scope.enableRotate === false) return;
                handleTouchMoveRotate(event);
                scope.update();
                break;
            case STATE.TOUCH_PAN:
                if (scope.enablePan === false) return;
                handleTouchMovePan(event);
                scope.update();
                break;
            case STATE.TOUCH_DOLLY_PAN:
                if (scope.enableZoom === false && scope.enablePan === false) return;
                handleTouchMoveDollyPan(event);
                scope.update();
                break;
            case STATE.TOUCH_DOLLY_ROTATE:
                if (scope.enableZoom === false && scope.enableRotate === false) return;
                handleTouchMoveDollyRotate(event);
                scope.update();
                break;
            default:
                state = STATE.NONE;
        }
    }
    function onTouchEnd(event) {
        if (scope.enabled === false) return;
        handleTouchEnd(event);
        scope.dispatchEvent(endEvent);
        state = STATE.NONE;
    }
    function onContextMenu(event) {
        if (scope.enabled === false) return;
        event.preventDefault();
    }
    //
    scope.domElement.addEventListener('contextmenu', onContextMenu, false);
    scope.domElement.addEventListener('mousedown', onMouseDown, false);
    scope.domElement.addEventListener('wheel', onMouseWheel, false);
    scope.domElement.addEventListener('touchstart', onTouchStart, false);
    scope.domElement.addEventListener('touchend', onTouchEnd, false);
    scope.domElement.addEventListener('touchmove', onTouchMove, false);
    window.addEventListener('keydown', onKeyDown, false);
    // force an update at start
    this.update();
};
OrbitControls.prototype = Object.create(EventDispatcher.prototype);
OrbitControls.prototype.constructor = OrbitControls;
// This set of controls performs orbiting, dollying (zooming), and panning.
// Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
// This is very similar to OrbitControls, another set of touch behavior
//
//    Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
//    Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
//    Pan - left mouse, or arrow keys / touch: one-finger move
var MapControls = function (object, domElement) {
    OrbitControls.call(this, object, domElement);
    this.mouseButtons.LEFT = MOUSE.PAN;
    this.mouseButtons.RIGHT = MOUSE.ROTATE;
    this.touches.ONE = TOUCH.PAN;
    this.touches.TWO = TOUCH.DOLLY_ROTATE;
};
MapControls.prototype = Object.create(EventDispatcher.prototype);
MapControls.prototype.constructor = MapControls;
export { OrbitControls, MapControls };
miniprogram-9/jsm/controls/TrackballControls.js
New file
@@ -0,0 +1,712 @@
/**
 * @author Eberhard Graether / http://egraether.com/
 * @author Mark Lundin     / http://mark-lundin.com
 * @author Simone Manini / http://daron1337.github.io
 * @author Luca Antiga     / http://lantiga.github.io
 */
export default function (THREE) {
    let {
        EventDispatcher,
        MOUSE,
        Quaternion,
        Vector2,
        Vector3,
        global: window,
    } = THREE;
    let document = window.document;
    var TrackballControls = function (object, domElement) {
        if (domElement === undefined) console.warn('THREE.TrackballControls: The second parameter "domElement" is now mandatory.');
        if (domElement === document) console.error('THREE.TrackballControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.');
        var _this = this;
        var STATE = { NONE: - 1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM_PAN: 4 };
        this.object = object;
        this.domElement = domElement;
        // API
        this.enabled = true;
        this.screen = { left: 0, top: 0, width: 0, height: 0 };
        this.rotateSpeed = 1.0;
        this.zoomSpeed = 1.2;
        this.panSpeed = 0.3;
        this.noRotate = false;
        this.noZoom = false;
        this.noPan = false;
        this.staticMoving = false;
        this.dynamicDampingFactor = 0.2;
        this.minDistance = 0;
        this.maxDistance = Infinity;
        this.keys = [65 /*A*/, 83 /*S*/, 68 /*D*/];
        this.mouseButtons = { LEFT: MOUSE.ROTATE, MIDDLE: MOUSE.ZOOM, RIGHT: MOUSE.PAN };
        // internals
        this.target = new Vector3();
        var EPS = 0.000001;
        var lastPosition = new Vector3();
        var lastZoom = 1;
        var _state = STATE.NONE,
            _keyState = STATE.NONE,
            _eye = new Vector3(),
            _movePrev = new Vector2(),
            _moveCurr = new Vector2(),
            _lastAxis = new Vector3(),
            _lastAngle = 0,
            _zoomStart = new Vector2(),
            _zoomEnd = new Vector2(),
            _touchZoomDistanceStart = 0,
            _touchZoomDistanceEnd = 0,
            _panStart = new Vector2(),
            _panEnd = new Vector2();
        // for reset
        this.target0 = this.target.clone();
        this.position0 = this.object.position.clone();
        this.up0 = this.object.up.clone();
        this.zoom0 = this.object.zoom;
        // events
        var changeEvent = { type: 'change' };
        var startEvent = { type: 'start' };
        var endEvent = { type: 'end' };
        // methods
        this.handleResize = function () {
            var box = this.domElement.getBoundingClientRect();
            // adjustments come from similar code in the jquery offset() function
            // var d = this.domElement.ownerDocument.documentElement;
            var d = document.documentElement;
            this.screen.left = box.left + window.scrollX - d.clientLeft;
            this.screen.top = box.top + window.scrollY - d.clientTop;
            this.screen.width = box.width;
            this.screen.height = box.height;
        };
        var getMouseOnScreen = (function () {
            var vector = new Vector2();
            return function getMouseOnScreen(pageX, pageY) {
                vector.set(
                    (pageX - _this.screen.left) / _this.screen.width,
                    (pageY - _this.screen.top) / _this.screen.height
                );
                return vector;
            };
        }());
        var getMouseOnCircle = (function () {
            var vector = new Vector2();
            return function getMouseOnCircle(pageX, pageY) {
                vector.set(
                    ((pageX - _this.screen.width * 0.5 - _this.screen.left) / (_this.screen.width * 0.5)),
                    ((_this.screen.height + 2 * (_this.screen.top - pageY)) / _this.screen.width) // screen.width intentional
                );
                return vector;
            };
        }());
        this.rotateCamera = (function () {
            var axis = new Vector3(),
                quaternion = new Quaternion(),
                eyeDirection = new Vector3(),
                objectUpDirection = new Vector3(),
                objectSidewaysDirection = new Vector3(),
                moveDirection = new Vector3(),
                angle;
            return function rotateCamera() {
                moveDirection.set(_moveCurr.x - _movePrev.x, _moveCurr.y - _movePrev.y, 0);
                angle = moveDirection.length();
                if (angle) {
                    _eye.copy(_this.object.position).sub(_this.target);
                    eyeDirection.copy(_eye).normalize();
                    objectUpDirection.copy(_this.object.up).normalize();
                    objectSidewaysDirection.crossVectors(objectUpDirection, eyeDirection).normalize();
                    objectUpDirection.setLength(_moveCurr.y - _movePrev.y);
                    objectSidewaysDirection.setLength(_moveCurr.x - _movePrev.x);
                    moveDirection.copy(objectUpDirection.add(objectSidewaysDirection));
                    axis.crossVectors(moveDirection, _eye).normalize();
                    angle *= _this.rotateSpeed;
                    quaternion.setFromAxisAngle(axis, angle);
                    _eye.applyQuaternion(quaternion);
                    _this.object.up.applyQuaternion(quaternion);
                    _lastAxis.copy(axis);
                    _lastAngle = angle;
                } else if (!_this.staticMoving && _lastAngle) {
                    _lastAngle *= Math.sqrt(1.0 - _this.dynamicDampingFactor);
                    _eye.copy(_this.object.position).sub(_this.target);
                    quaternion.setFromAxisAngle(_lastAxis, _lastAngle);
                    _eye.applyQuaternion(quaternion);
                    _this.object.up.applyQuaternion(quaternion);
                }
                _movePrev.copy(_moveCurr);
            };
        }());
        this.zoomCamera = function () {
            var factor;
            if (_state === STATE.TOUCH_ZOOM_PAN) {
                factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
                _touchZoomDistanceStart = _touchZoomDistanceEnd;
                if (_this.object.isPerspectiveCamera) {
                    _eye.multiplyScalar(factor);
                } else if (_this.object.isOrthographicCamera) {
                    _this.object.zoom *= factor;
                    _this.object.updateProjectionMatrix();
                } else {
                    console.warn('THREE.TrackballControls: Unsupported camera type');
                }
            } else {
                factor = 1.0 + (_zoomEnd.y - _zoomStart.y) * _this.zoomSpeed;
                if (factor !== 1.0 && factor > 0.0) {
                    if (_this.object.isPerspectiveCamera) {
                        _eye.multiplyScalar(factor);
                    } else if (_this.object.isOrthographicCamera) {
                        _this.object.zoom /= factor;
                        _this.object.updateProjectionMatrix();
                    } else {
                        console.warn('THREE.TrackballControls: Unsupported camera type');
                    }
                }
                if (_this.staticMoving) {
                    _zoomStart.copy(_zoomEnd);
                } else {
                    _zoomStart.y += (_zoomEnd.y - _zoomStart.y) * this.dynamicDampingFactor;
                }
            }
        };
        this.panCamera = (function () {
            var mouseChange = new Vector2(),
                objectUp = new Vector3(),
                pan = new Vector3();
            return function panCamera() {
                mouseChange.copy(_panEnd).sub(_panStart);
                if (mouseChange.lengthSq()) {
                    if (_this.object.isOrthographicCamera) {
                        var scale_x = (_this.object.right - _this.object.left) / _this.object.zoom / _this.domElement.clientWidth;
                        var scale_y = (_this.object.top - _this.object.bottom) / _this.object.zoom / _this.domElement.clientWidth;
                        mouseChange.x *= scale_x;
                        mouseChange.y *= scale_y;
                    }
                    mouseChange.multiplyScalar(_eye.length() * _this.panSpeed);
                    pan.copy(_eye).cross(_this.object.up).setLength(mouseChange.x);
                    pan.add(objectUp.copy(_this.object.up).setLength(mouseChange.y));
                    _this.object.position.add(pan);
                    _this.target.add(pan);
                    if (_this.staticMoving) {
                        _panStart.copy(_panEnd);
                    } else {
                        _panStart.add(mouseChange.subVectors(_panEnd, _panStart).multiplyScalar(_this.dynamicDampingFactor));
                    }
                }
            };
        }());
        this.checkDistances = function () {
            if (!_this.noZoom || !_this.noPan) {
                if (_eye.lengthSq() > _this.maxDistance * _this.maxDistance) {
                    _this.object.position.addVectors(_this.target, _eye.setLength(_this.maxDistance));
                    _zoomStart.copy(_zoomEnd);
                }
                if (_eye.lengthSq() < _this.minDistance * _this.minDistance) {
                    _this.object.position.addVectors(_this.target, _eye.setLength(_this.minDistance));
                    _zoomStart.copy(_zoomEnd);
                }
            }
        };
        this.update = function () {
            _eye.subVectors(_this.object.position, _this.target);
            if (!_this.noRotate) {
                _this.rotateCamera();
            }
            if (!_this.noZoom) {
                _this.zoomCamera();
            }
            if (!_this.noPan) {
                _this.panCamera();
            }
            _this.object.position.addVectors(_this.target, _eye);
            if (_this.object.isPerspectiveCamera) {
                _this.checkDistances();
                _this.object.lookAt(_this.target);
                if (lastPosition.distanceToSquared(_this.object.position) > EPS) {
                    _this.dispatchEvent(changeEvent);
                    lastPosition.copy(_this.object.position);
                }
            } else if (_this.object.isOrthographicCamera) {
                _this.object.lookAt(_this.target);
                if (lastPosition.distanceToSquared(_this.object.position) > EPS || lastZoom !== _this.object.zoom) {
                    _this.dispatchEvent(changeEvent);
                    lastPosition.copy(_this.object.position);
                    lastZoom = _this.object.zoom;
                }
            } else {
                console.warn('THREE.TrackballControls: Unsupported camera type');
            }
        };
        this.reset = function () {
            _state = STATE.NONE;
            _keyState = STATE.NONE;
            _this.target.copy(_this.target0);
            _this.object.position.copy(_this.position0);
            _this.object.up.copy(_this.up0);
            _this.object.zoom = _this.zoom0;
            _this.object.updateProjectionMatrix();
            _eye.subVectors(_this.object.position, _this.target);
            _this.object.lookAt(_this.target);
            _this.dispatchEvent(changeEvent);
            lastPosition.copy(_this.object.position);
            lastZoom = _this.object.zoom;
        };
        // listeners
        function keydown(event) {
            if (_this.enabled === false) return;
            window.removeEventListener('keydown', keydown);
            if (_keyState !== STATE.NONE) {
                return;
            } else if (event.keyCode === _this.keys[STATE.ROTATE] && !_this.noRotate) {
                _keyState = STATE.ROTATE;
            } else if (event.keyCode === _this.keys[STATE.ZOOM] && !_this.noZoom) {
                _keyState = STATE.ZOOM;
            } else if (event.keyCode === _this.keys[STATE.PAN] && !_this.noPan) {
                _keyState = STATE.PAN;
            }
        }
        function keyup() {
            if (_this.enabled === false) return;
            _keyState = STATE.NONE;
            window.addEventListener('keydown', keydown, false);
        }
        function mousedown(event) {
            if (_this.enabled === false) return;
            event.preventDefault();
            event.stopPropagation();
            if (_state === STATE.NONE) {
                switch (event.button) {
                    case _this.mouseButtons.LEFT:
                        _state = STATE.ROTATE;
                        break;
                    case _this.mouseButtons.MIDDLE:
                        _state = STATE.ZOOM;
                        break;
                    case _this.mouseButtons.RIGHT:
                        _state = STATE.PAN;
                        break;
                    default:
                        _state = STATE.NONE;
                }
            }
            var state = (_keyState !== STATE.NONE) ? _keyState : _state;
            if (state === STATE.ROTATE && !_this.noRotate) {
                _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY));
                _movePrev.copy(_moveCurr);
            } else if (state === STATE.ZOOM && !_this.noZoom) {
                _zoomStart.copy(getMouseOnScreen(event.pageX, event.pageY));
                _zoomEnd.copy(_zoomStart);
            } else if (state === STATE.PAN && !_this.noPan) {
                _panStart.copy(getMouseOnScreen(event.pageX, event.pageY));
                _panEnd.copy(_panStart);
            }
            document.addEventListener('mousemove', mousemove, false);
            document.addEventListener('mouseup', mouseup, false);
            _this.dispatchEvent(startEvent);
        }
        function mousemove(event) {
            if (_this.enabled === false) return;
            event.preventDefault();
            event.stopPropagation();
            var state = (_keyState !== STATE.NONE) ? _keyState : _state;
            if (state === STATE.ROTATE && !_this.noRotate) {
                _movePrev.copy(_moveCurr);
                _moveCurr.copy(getMouseOnCircle(event.pageX, event.pageY));
            } else if (state === STATE.ZOOM && !_this.noZoom) {
                _zoomEnd.copy(getMouseOnScreen(event.pageX, event.pageY));
            } else if (state === STATE.PAN && !_this.noPan) {
                _panEnd.copy(getMouseOnScreen(event.pageX, event.pageY));
            }
        }
        function mouseup(event) {
            if (_this.enabled === false) return;
            event.preventDefault();
            event.stopPropagation();
            _state = STATE.NONE;
            document.removeEventListener('mousemove', mousemove);
            document.removeEventListener('mouseup', mouseup);
            _this.dispatchEvent(endEvent);
        }
        function mousewheel(event) {
            if (_this.enabled === false) return;
            if (_this.noZoom === true) return;
            event.preventDefault();
            event.stopPropagation();
            switch (event.deltaMode) {
                case 2:
                    // Zoom in pages
                    _zoomStart.y -= event.deltaY * 0.025;
                    break;
                case 1:
                    // Zoom in lines
                    _zoomStart.y -= event.deltaY * 0.01;
                    break;
                default:
                    // undefined, 0, assume pixels
                    _zoomStart.y -= event.deltaY * 0.00025;
                    break;
            }
            _this.dispatchEvent(startEvent);
            _this.dispatchEvent(endEvent);
        }
        function touchstart(event) {
            if (_this.enabled === false) return;
            event.preventDefault();
            switch (event.touches.length) {
                case 1:
                    _state = STATE.TOUCH_ROTATE;
                    _moveCurr.copy(getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY));
                    _movePrev.copy(_moveCurr);
                    break;
                default: // 2 or more
                    _state = STATE.TOUCH_ZOOM_PAN;
                    var dx = event.touches[0].pageX - event.touches[1].pageX;
                    var dy = event.touches[0].pageY - event.touches[1].pageY;
                    _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt(dx * dx + dy * dy);
                    var x = (event.touches[0].pageX + event.touches[1].pageX) / 2;
                    var y = (event.touches[0].pageY + event.touches[1].pageY) / 2;
                    _panStart.copy(getMouseOnScreen(x, y));
                    _panEnd.copy(_panStart);
                    break;
            }
            _this.dispatchEvent(startEvent);
        }
        function touchmove(event) {
            if (_this.enabled === false) return;
            event.preventDefault();
            event.stopPropagation();
            switch (event.touches.length) {
                case 1:
                    _movePrev.copy(_moveCurr);
                    _moveCurr.copy(getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY));
                    break;
                default: // 2 or more
                    var dx = event.touches[0].pageX - event.touches[1].pageX;
                    var dy = event.touches[0].pageY - event.touches[1].pageY;
                    _touchZoomDistanceEnd = Math.sqrt(dx * dx + dy * dy);
                    var x = (event.touches[0].pageX + event.touches[1].pageX) / 2;
                    var y = (event.touches[0].pageY + event.touches[1].pageY) / 2;
                    _panEnd.copy(getMouseOnScreen(x, y));
                    break;
            }
        }
        function touchend(event) {
            if (_this.enabled === false) return;
            switch (event.touches.length) {
                case 0:
                    _state = STATE.NONE;
                    break;
                case 1:
                    _state = STATE.TOUCH_ROTATE;
                    _moveCurr.copy(getMouseOnCircle(event.touches[0].pageX, event.touches[0].pageY));
                    _movePrev.copy(_moveCurr);
                    break;
            }
            _this.dispatchEvent(endEvent);
        }
        function contextmenu(event) {
            if (_this.enabled === false) return;
            event.preventDefault();
        }
        this.dispose = function () {
            this.domElement.removeEventListener('contextmenu', contextmenu, false);
            this.domElement.removeEventListener('mousedown', mousedown, false);
            this.domElement.removeEventListener('wheel', mousewheel, false);
            this.domElement.removeEventListener('touchstart', touchstart, false);
            this.domElement.removeEventListener('touchend', touchend, false);
            this.domElement.removeEventListener('touchmove', touchmove, false);
            document.removeEventListener('mousemove', mousemove, false);
            document.removeEventListener('mouseup', mouseup, false);
            window.removeEventListener('keydown', keydown, false);
            window.removeEventListener('keyup', keyup, false);
        };
        this.domElement.addEventListener('contextmenu', contextmenu, false);
        this.domElement.addEventListener('mousedown', mousedown, false);
        this.domElement.addEventListener('wheel', mousewheel, false);
        this.domElement.addEventListener('touchstart', touchstart, false);
        this.domElement.addEventListener('touchend', touchend, false);
        this.domElement.addEventListener('touchmove', touchmove, false);
        window.addEventListener('keydown', keydown, false);
        window.addEventListener('keyup', keyup, false);
        this.handleResize();
        // force an update at start
        this.update();
    };
    TrackballControls.prototype = Object.create(EventDispatcher.prototype);
    TrackballControls.prototype.constructor = TrackballControls;
    return { TrackballControls };
}
miniprogram-9/jsm/loaders/DDSLoader.js
New file
@@ -0,0 +1,285 @@
/**
 * @author mrdoob / http://mrdoob.com/
 */
export default function (THREE) {
    let {
        CompressedTextureLoader,
        RGBAFormat,
        RGBA_S3TC_DXT3_Format,
        RGBA_S3TC_DXT5_Format,
        RGB_ETC1_Format,
        RGB_S3TC_DXT1_Format
    } = THREE;
    var DDSLoader = function (manager) {
        CompressedTextureLoader.call(this, manager);
    };
    DDSLoader.prototype = Object.assign(Object.create(CompressedTextureLoader.prototype), {
        constructor: DDSLoader,
        parse: function (buffer, loadMipmaps) {
            var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 };
            // Adapted from @toji's DDS utils
            // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js
            // All values and structures referenced from:
            // http://msdn.microsoft.com/en-us/library/bb943991.aspx/
            var DDS_MAGIC = 0x20534444;
            var DDSD_CAPS = 0x1,
                DDSD_HEIGHT = 0x2,
                DDSD_WIDTH = 0x4,
                DDSD_PITCH = 0x8,
                DDSD_PIXELFORMAT = 0x1000,
                DDSD_MIPMAPCOUNT = 0x20000,
                DDSD_LINEARSIZE = 0x80000,
                DDSD_DEPTH = 0x800000;
            var DDSCAPS_COMPLEX = 0x8,
                DDSCAPS_MIPMAP = 0x400000,
                DDSCAPS_TEXTURE = 0x1000;
            var DDSCAPS2_CUBEMAP = 0x200,
                DDSCAPS2_CUBEMAP_POSITIVEX = 0x400,
                DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800,
                DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000,
                DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000,
                DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000,
                DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000,
                DDSCAPS2_VOLUME = 0x200000;
            var DDPF_ALPHAPIXELS = 0x1,
                DDPF_ALPHA = 0x2,
                DDPF_FOURCC = 0x4,
                DDPF_RGB = 0x40,
                DDPF_YUV = 0x200,
                DDPF_LUMINANCE = 0x20000;
            function fourCCToInt32(value) {
                return value.charCodeAt(0) +
                    (value.charCodeAt(1) << 8) +
                    (value.charCodeAt(2) << 16) +
                    (value.charCodeAt(3) << 24);
            }
            function int32ToFourCC(value) {
                return String.fromCharCode(
                    value & 0xff,
                    (value >> 8) & 0xff,
                    (value >> 16) & 0xff,
                    (value >> 24) & 0xff
                );
            }
            function loadARGBMip(buffer, dataOffset, width, height) {
                var dataLength = width * height * 4;
                var srcBuffer = new Uint8Array(buffer, dataOffset, dataLength);
                var byteArray = new Uint8Array(dataLength);
                var dst = 0;
                var src = 0;
                for (var y = 0; y < height; y++) {
                    for (var x = 0; x < width; x++) {
                        var b = srcBuffer[src]; src++;
                        var g = srcBuffer[src]; src++;
                        var r = srcBuffer[src]; src++;
                        var a = srcBuffer[src]; src++;
                        byteArray[dst] = r; dst++;    //r
                        byteArray[dst] = g; dst++;    //g
                        byteArray[dst] = b; dst++;    //b
                        byteArray[dst] = a; dst++;    //a
                    }
                }
                return byteArray;
            }
            var FOURCC_DXT1 = fourCCToInt32("DXT1");
            var FOURCC_DXT3 = fourCCToInt32("DXT3");
            var FOURCC_DXT5 = fourCCToInt32("DXT5");
            var FOURCC_ETC1 = fourCCToInt32("ETC1");
            var headerLengthInt = 31; // The header length in 32 bit ints
            // Offsets into the header array
            var off_magic = 0;
            var off_size = 1;
            var off_flags = 2;
            var off_height = 3;
            var off_width = 4;
            var off_mipmapCount = 7;
            var off_pfFlags = 20;
            var off_pfFourCC = 21;
            var off_RGBBitCount = 22;
            var off_RBitMask = 23;
            var off_GBitMask = 24;
            var off_BBitMask = 25;
            var off_ABitMask = 26;
            var off_caps = 27;
            var off_caps2 = 28;
            var off_caps3 = 29;
            var off_caps4 = 30;
            // Parse header
            var header = new Int32Array(buffer, 0, headerLengthInt);
            if (header[off_magic] !== DDS_MAGIC) {
                console.error('THREE.DDSLoader.parse: Invalid magic number in DDS header.');
                return dds;
            }
            if (!header[off_pfFlags] & DDPF_FOURCC) {
                console.error('THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.');
                return dds;
            }
            var blockBytes;
            var fourCC = header[off_pfFourCC];
            var isRGBAUncompressed = false;
            switch (fourCC) {
                case FOURCC_DXT1:
                    blockBytes = 8;
                    dds.format = RGB_S3TC_DXT1_Format;
                    break;
                case FOURCC_DXT3:
                    blockBytes = 16;
                    dds.format = RGBA_S3TC_DXT3_Format;
                    break;
                case FOURCC_DXT5:
                    blockBytes = 16;
                    dds.format = RGBA_S3TC_DXT5_Format;
                    break;
                case FOURCC_ETC1:
                    blockBytes = 8;
                    dds.format = RGB_ETC1_Format;
                    break;
                default:
                    if (header[off_RGBBitCount] === 32
                        && header[off_RBitMask] & 0xff0000
                        && header[off_GBitMask] & 0xff00
                        && header[off_BBitMask] & 0xff
                        && header[off_ABitMask] & 0xff000000) {
                        isRGBAUncompressed = true;
                        blockBytes = 64;
                        dds.format = RGBAFormat;
                    } else {
                        console.error('THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC(fourCC));
                        return dds;
                    }
            }
            dds.mipmapCount = 1;
            if (header[off_flags] & DDSD_MIPMAPCOUNT && loadMipmaps !== false) {
                dds.mipmapCount = Math.max(1, header[off_mipmapCount]);
            }
            var caps2 = header[off_caps2];
            dds.isCubemap = caps2 & DDSCAPS2_CUBEMAP ? true : false;
            if (dds.isCubemap && (
                !(caps2 & DDSCAPS2_CUBEMAP_POSITIVEX) ||
                !(caps2 & DDSCAPS2_CUBEMAP_NEGATIVEX) ||
                !(caps2 & DDSCAPS2_CUBEMAP_POSITIVEY) ||
                !(caps2 & DDSCAPS2_CUBEMAP_NEGATIVEY) ||
                !(caps2 & DDSCAPS2_CUBEMAP_POSITIVEZ) ||
                !(caps2 & DDSCAPS2_CUBEMAP_NEGATIVEZ)
            )) {
                console.error('THREE.DDSLoader.parse: Incomplete cubemap faces');
                return dds;
            }
            dds.width = header[off_width];
            dds.height = header[off_height];
            var dataOffset = header[off_size] + 4;
            // Extract mipmaps buffers
            var faces = dds.isCubemap ? 6 : 1;
            for (var face = 0; face < faces; face++) {
                var width = dds.width;
                var height = dds.height;
                for (var i = 0; i < dds.mipmapCount; i++) {
                    if (isRGBAUncompressed) {
                        var byteArray = loadARGBMip(buffer, dataOffset, width, height);
                        var dataLength = byteArray.length;
                    } else {
                        var dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
                        var byteArray = new Uint8Array(buffer, dataOffset, dataLength);
                    }
                    var mipmap = { "data": byteArray, "width": width, "height": height };
                    dds.mipmaps.push(mipmap);
                    dataOffset += dataLength;
                    width = Math.max(width >> 1, 1);
                    height = Math.max(height >> 1, 1);
                }
            }
            return dds;
        }
    });
    return { DDSLoader };
}
miniprogram-9/jsm/loaders/EquirectangularToCubeGenerator.js
New file
@@ -0,0 +1,252 @@
/**
* @author Richard M. / https://github.com/richardmonette
* @author WestLangley / http://github.com/WestLangley
*/
export default function (THREE) {
let {
    BackSide,
    BoxBufferGeometry,
    CubeCamera,
    Mesh,
    NoBlending,
    PerspectiveCamera,
    Scene,
    ShaderMaterial,
    UniformsUtils,
    WebGLRenderTargetCube
} = THREE;
var CubemapGenerator = function ( renderer ) {
    this.renderer = renderer;
};
CubemapGenerator.prototype.fromEquirectangular = function ( texture, options ) {
    options = options || {};
    var scene = new Scene();
    var shader = {
        uniforms: {
            tEquirect: { value: null },
        },
        vertexShader:
            `
            varying vec3 vWorldDirection;
            //include <common>
            vec3 transformDirection( in vec3 dir, in mat4 matrix ) {
                return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
            }
            void main() {
                vWorldDirection = transformDirection( position, modelMatrix );
                #include <begin_vertex>
                #include <project_vertex>
            }
            `,
        fragmentShader:
            `
            uniform sampler2D tEquirect;
            varying vec3 vWorldDirection;
            //include <common>
            #define RECIPROCAL_PI 0.31830988618
            #define RECIPROCAL_PI2 0.15915494
            void main() {
                vec3 direction = normalize( vWorldDirection );
                vec2 sampleUV;
                sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;
                sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;
                gl_FragColor = texture2D( tEquirect, sampleUV );
            }
            `
    };
    var material = new ShaderMaterial( {
        type: 'CubemapFromEquirect',
        uniforms: UniformsUtils.clone( shader.uniforms ),
        vertexShader: shader.vertexShader,
        fragmentShader: shader.fragmentShader,
        side: BackSide,
        blending: NoBlending
    } );
    material.uniforms.tEquirect.value = texture;
    var mesh = new Mesh( new BoxBufferGeometry( 5, 5, 5 ), material );
    scene.add( mesh );
    var resolution = options.resolution || 512;
    var params = {
        type: texture.type,
        format: texture.format,
        encoding: texture.encoding,
        generateMipmaps: ( options.generateMipmaps !== undefined ) ? options.generateMipmaps : texture.generateMipmaps,
        minFilter: ( options.minFilter !== undefined ) ? options.minFilter : texture.minFilter,
        magFilter: ( options.magFilter !== undefined ) ? options.magFilter : texture.magFilter
    };
    var camera = new CubeCamera( 1, 10, resolution, params );
    camera.update( this.renderer, scene );
    mesh.geometry.dispose();
    mesh.material.dispose();
    return camera.renderTarget;
};
//
var EquirectangularToCubeGenerator = ( function () {
    var camera = new PerspectiveCamera( 90, 1, 0.1, 10 );
    var scene = new Scene();
    var boxMesh = new Mesh( new BoxBufferGeometry( 1, 1, 1 ), getShader() );
    boxMesh.material.side = BackSide;
    scene.add( boxMesh );
    var EquirectangularToCubeGenerator = function ( sourceTexture, options ) {
        options = options || {};
        this.sourceTexture = sourceTexture;
        this.resolution = options.resolution || 512;
        this.views = [
            { t: [ 1, 0, 0 ], u: [ 0, - 1, 0 ] },
            { t: [ - 1, 0, 0 ], u: [ 0, - 1, 0 ] },
            { t: [ 0, 1, 0 ], u: [ 0, 0, 1 ] },
            { t: [ 0, - 1, 0 ], u: [ 0, 0, - 1 ] },
            { t: [ 0, 0, 1 ], u: [ 0, - 1, 0 ] },
            { t: [ 0, 0, - 1 ], u: [ 0, - 1, 0 ] },
        ];
        var params = {
            format: options.format || this.sourceTexture.format,
            magFilter: this.sourceTexture.magFilter,
            minFilter: this.sourceTexture.minFilter,
            type: options.type || this.sourceTexture.type,
            generateMipmaps: this.sourceTexture.generateMipmaps,
            anisotropy: this.sourceTexture.anisotropy,
            encoding: this.sourceTexture.encoding
        };
        this.renderTarget = new WebGLRenderTargetCube( this.resolution, this.resolution, params );
    };
    EquirectangularToCubeGenerator.prototype = {
        constructor: EquirectangularToCubeGenerator,
        update: function ( renderer ) {
            var currentRenderTarget = renderer.getRenderTarget();
            boxMesh.material.uniforms.equirectangularMap.value = this.sourceTexture;
            for ( var i = 0; i < 6; i ++ ) {
                var v = this.views[ i ];
                camera.position.set( 0, 0, 0 );
                camera.up.set( v.u[ 0 ], v.u[ 1 ], v.u[ 2 ] );
                camera.lookAt( v.t[ 0 ], v.t[ 1 ], v.t[ 2 ] );
                renderer.setRenderTarget( this.renderTarget, i );
                renderer.clear();
                renderer.render( scene, camera );
            }
            renderer.setRenderTarget( currentRenderTarget );
            return this.renderTarget.texture;
        },
        dispose: function () {
            this.renderTarget.dispose();
        }
    };
    function getShader() {
        var shaderMaterial = new ShaderMaterial( {
            uniforms: {
                "equirectangularMap": { value: null },
            },
            vertexShader:
                "varying vec3 localPosition;\n\
                \n\
                void main() {\n\
                    localPosition = position;\n\
                    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
                }",
            fragmentShader:
                "#include <common>\n\
                varying vec3 localPosition;\n\
                uniform sampler2D equirectangularMap;\n\
                \n\
                vec2 EquirectangularSampleUV(vec3 v) {\n\
                    vec2 uv = vec2(atan(v.z, v.x), asin(v.y));\n\
                    uv *= vec2(0.1591, 0.3183); // inverse atan\n\
                    uv += 0.5;\n\
                    return uv;\n\
                }\n\
                \n\
                void main() {\n\
                    vec2 uv = EquirectangularSampleUV(normalize(localPosition));\n\
                    gl_FragColor = texture2D(equirectangularMap, uv);\n\
                }",
            blending: NoBlending
        } );
        shaderMaterial.type = 'EquirectangularToCubeGenerator';
        return shaderMaterial;
    }
    return EquirectangularToCubeGenerator;
} )();
return { CubemapGenerator, EquirectangularToCubeGenerator };
}
miniprogram-9/jsm/loaders/GLTFLoader.js
New file
@@ -0,0 +1,3284 @@
/**
 * @author Rich Tibbett / https://github.com/richtr
 * @author mrdoob / http://mrdoob.com/
 * @author Tony Parisi / http://www.tonyparisi.com/
 * @author Takahiro / https://github.com/takahirox
 * @author Don McCurdy / https://www.donmccurdy.com
 */
export default function (THREE) {
    let {
        AnimationClip,
        Bone,
        BufferAttribute,
        BufferGeometry,
        ClampToEdgeWrapping,
        Color,
        DefaultLoadingManager,
        DirectionalLight,
        DoubleSide,
        FileLoader,
        FrontSide,
        Group,
        InterleavedBuffer,
        InterleavedBufferAttribute,
        Interpolant,
        InterpolateDiscrete,
        InterpolateLinear,
        Line,
        LineBasicMaterial,
        LineLoop,
        LineSegments,
        LinearFilter,
        LinearMipmapLinearFilter,
        LinearMipmapNearestFilter,
        Loader,
        LoaderUtils,
        Material,
        Math: _Math,
        Matrix4,
        Mesh,
        MeshBasicMaterial,
        MeshStandardMaterial,
        MirroredRepeatWrapping,
        NearestFilter,
        NearestMipmapLinearFilter,
        NearestMipmapNearestFilter,
        NumberKeyframeTrack,
        Object3D,
        OrthographicCamera,
        PerspectiveCamera,
        PointLight,
        Points,
        PointsMaterial,
        PropertyBinding,
        QuaternionKeyframeTrack,
        RGBAFormat,
        RGBFormat,
        RepeatWrapping,
        Scene,
        ShaderLib,
        ShaderMaterial,
        Skeleton,
        SkinnedMesh,
        SpotLight,
        TextureLoader,
        TriangleFanDrawMode,
        TriangleStripDrawMode,
        UniformsUtils,
        Vector2,
        VectorKeyframeTrack,
        VertexColors,
        sRGBEncoding,
        global: window
    } = THREE;
    function GLTFLoader(manager) {
        this.manager = (manager !== undefined) ? manager : DefaultLoadingManager;
        this.dracoLoader = null;
        this.ddsLoader = null;
    }
    GLTFLoader.prototype = {
        constructor: GLTFLoader,
        crossOrigin: 'anonymous',
        load: function (url, onLoad, onProgress, onError) {
            var scope = this;
            var resourcePath;
            if (this.resourcePath !== undefined) {
                resourcePath = this.resourcePath;
            } else if (this.path !== undefined) {
                resourcePath = this.path;
            } else {
                resourcePath = LoaderUtils.extractUrlBase(url);
            }
            // Tells the LoadingManager to track an extra item, which resolves after
            // the model is fully loaded. This means the count of items loaded will
            // be incorrect, but ensures manager.onLoad() does not fire early.
            scope.manager.itemStart(url);
            var _onError = function (e) {
                if (onError) {
                    onError(e);
                } else {
                    console.error(e);
                }
                scope.manager.itemError(url);
                scope.manager.itemEnd(url);
            };
            var loader = new FileLoader(scope.manager);
            loader.setPath(this.path);
            loader.setResponseType('arraybuffer');
            if (scope.crossOrigin === 'use-credentials') {
                loader.setWithCredentials(true);
            }
            loader.load(url, function (data) {
                try {
                    scope.parse(data, resourcePath, function (gltf) {
                        onLoad(gltf);
                        scope.manager.itemEnd(url);
                    }, _onError);
                } catch (e) {
                    _onError(e);
                }
            }, onProgress, _onError);
        },
        setCrossOrigin: function (value) {
            this.crossOrigin = value;
            return this;
        },
        setPath: function (value) {
            this.path = value;
            return this;
        },
        setResourcePath: function (value) {
            this.resourcePath = value;
            return this;
        },
        setDRACOLoader: function (dracoLoader) {
            this.dracoLoader = dracoLoader;
            return this;
        },
        setDDSLoader: function (ddsLoader) {
            this.ddsLoader = ddsLoader;
            return this;
        },
        parse: function (data, path, onLoad, onError) {
            var content;
            var extensions = {};
            if (typeof data === 'string') {
                content = data;
            } else {
                var magic = LoaderUtils.decodeText(new Uint8Array(data, 0, 4));
                if (magic === BINARY_EXTENSION_HEADER_MAGIC) {
                    try {
                        extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(data);
                    } catch (error) {
                        if (onError) onError(error);
                        return;
                    }
                    content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content;
                } else {
                    content = LoaderUtils.decodeText(new Uint8Array(data));
                }
            }
            var json = JSON.parse(content);
            if (json.asset === undefined || json.asset.version[0] < 2) {
                if (onError) onError(new Error('THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported. Use LegacyGLTFLoader instead.'));
                return;
            }
            if (json.extensionsUsed) {
                for (var i = 0; i < json.extensionsUsed.length; ++i) {
                    var extensionName = json.extensionsUsed[i];
                    var extensionsRequired = json.extensionsRequired || [];
                    switch (extensionName) {
                        case EXTENSIONS.KHR_LIGHTS_PUNCTUAL:
                            extensions[extensionName] = new GLTFLightsExtension(json);
                            break;
                        case EXTENSIONS.KHR_MATERIALS_UNLIT:
                            extensions[extensionName] = new GLTFMaterialsUnlitExtension();
                            break;
                        case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
                            extensions[extensionName] = new GLTFMaterialsPbrSpecularGlossinessExtension();
                            break;
                        case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
                            extensions[extensionName] = new GLTFDracoMeshCompressionExtension(json, this.dracoLoader);
                            break;
                        case EXTENSIONS.MSFT_TEXTURE_DDS:
                            extensions[EXTENSIONS.MSFT_TEXTURE_DDS] = new GLTFTextureDDSExtension(this.ddsLoader);
                            break;
                        case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
                            extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM] = new GLTFTextureTransformExtension();
                            break;
                        default:
                            if (extensionsRequired.indexOf(extensionName) >= 0) {
                                console.warn('THREE.GLTFLoader: Unknown extension "' + extensionName + '".');
                            }
                    }
                }
            }
            var parser = new GLTFParser(json, extensions, {
                path: path || this.resourcePath || '',
                crossOrigin: this.crossOrigin,
                manager: this.manager
            });
            parser.parse(onLoad, onError);
        }
    };
    /* GLTFREGISTRY */
    function GLTFRegistry() {
        var objects = {};
        return {
            get: function (key) {
                return objects[key];
            },
            add: function (key, object) {
                objects[key] = object;
            },
            remove: function (key) {
                delete objects[key];
            },
            removeAll: function () {
                objects = {};
            }
        };
    }
    /*********************************/
    /********** EXTENSIONS ***********/
    /*********************************/
    var EXTENSIONS = {
        KHR_BINARY_GLTF: 'KHR_binary_glTF',
        KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
        KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
        KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
        KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
        KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
        MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
    };
    /**
     * DDS Texture Extension
     *
     * Specification:
     * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
     *
     */
    function GLTFTextureDDSExtension(ddsLoader) {
        if (!ddsLoader) {
            throw new Error('THREE.GLTFLoader: Attempting to load .dds texture without importing DDSLoader');
        }
        this.name = EXTENSIONS.MSFT_TEXTURE_DDS;
        this.ddsLoader = ddsLoader;
    }
    /**
     * Lights Extension
     *
     * Specification: PENDING
     */
    function GLTFLightsExtension(json) {
        this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL;
        var extension = (json.extensions && json.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL]) || {};
        this.lightDefs = extension.lights || [];
    }
    GLTFLightsExtension.prototype.loadLight = function (lightIndex) {
        var lightDef = this.lightDefs[lightIndex];
        var lightNode;
        var color = new Color(0xffffff);
        if (lightDef.color !== undefined) color.fromArray(lightDef.color);
        var range = lightDef.range !== undefined ? lightDef.range : 0;
        switch (lightDef.type) {
            case 'directional':
                lightNode = new DirectionalLight(color);
                lightNode.target.position.set(0, 0, - 1);
                lightNode.add(lightNode.target);
                break;
            case 'point':
                lightNode = new PointLight(color);
                lightNode.distance = range;
                break;
            case 'spot':
                lightNode = new SpotLight(color);
                lightNode.distance = range;
                // Handle spotlight properties.
                lightDef.spot = lightDef.spot || {};
                lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
                lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
                lightNode.angle = lightDef.spot.outerConeAngle;
                lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
                lightNode.target.position.set(0, 0, - 1);
                lightNode.add(lightNode.target);
                break;
            default:
                throw new Error('THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".');
        }
        // Some lights (e.g. spot) default to a position other than the origin. Reset the position
        // here, because node-level parsing will only override position if explicitly specified.
        lightNode.position.set(0, 0, 0);
        lightNode.decay = 2;
        if (lightDef.intensity !== undefined) lightNode.intensity = lightDef.intensity;
        lightNode.name = lightDef.name || ('light_' + lightIndex);
        return Promise.resolve(lightNode);
    };
    /**
     * Unlit Materials Extension (pending)
     *
     * PR: https://github.com/KhronosGroup/glTF/pull/1163
     */
    function GLTFMaterialsUnlitExtension() {
        this.name = EXTENSIONS.KHR_MATERIALS_UNLIT;
    }
    GLTFMaterialsUnlitExtension.prototype.getMaterialType = function () {
        return MeshBasicMaterial;
    };
    GLTFMaterialsUnlitExtension.prototype.extendParams = function (materialParams, materialDef, parser) {
        var pending = [];
        materialParams.color = new Color(1.0, 1.0, 1.0);
        materialParams.opacity = 1.0;
        var metallicRoughness = materialDef.pbrMetallicRoughness;
        if (metallicRoughness) {
            if (Array.isArray(metallicRoughness.baseColorFactor)) {
                var array = metallicRoughness.baseColorFactor;
                materialParams.color.fromArray(array);
                materialParams.opacity = array[3];
            }
            if (metallicRoughness.baseColorTexture !== undefined) {
                pending.push(parser.assignTexture(materialParams, 'map', metallicRoughness.baseColorTexture));
            }
        }
        return Promise.all(pending);
    };
    /* BINARY EXTENSION */
    var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';
    var BINARY_EXTENSION_HEADER_LENGTH = 12;
    var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };
    function GLTFBinaryExtension(data) {
        this.name = EXTENSIONS.KHR_BINARY_GLTF;
        this.content = null;
        this.body = null;
        var headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH);
        this.header = {
            magic: LoaderUtils.decodeText(new Uint8Array(data.slice(0, 4))),
            version: headerView.getUint32(4, true),
            length: headerView.getUint32(8, true)
        };
        if (this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {
            throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.');
        } else if (this.header.version < 2.0) {
            throw new Error('THREE.GLTFLoader: Legacy binary file detected. Use LegacyGLTFLoader instead.');
        }
        var chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH);
        var chunkIndex = 0;
        while (chunkIndex < chunkView.byteLength) {
            var chunkLength = chunkView.getUint32(chunkIndex, true);
            chunkIndex += 4;
            var chunkType = chunkView.getUint32(chunkIndex, true);
            chunkIndex += 4;
            if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {
                var contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength);
                this.content = LoaderUtils.decodeText(contentArray);
            } else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {
                var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
                this.body = data.slice(byteOffset, byteOffset + chunkLength);
            }
            // Clients must ignore chunks with unknown types.
            chunkIndex += chunkLength;
        }
        if (this.content === null) {
            throw new Error('THREE.GLTFLoader: JSON content not found.');
        }
    }
    /**
     * DRACO Mesh Compression Extension
     *
     * Specification: https://github.com/KhronosGroup/glTF/pull/874
     */
    function GLTFDracoMeshCompressionExtension(json, dracoLoader) {
        if (!dracoLoader) {
            throw new Error('THREE.GLTFLoader: No DRACOLoader instance provided.');
        }
        this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
        this.json = json;
        this.dracoLoader = dracoLoader;
    }
    GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function (primitive, parser) {
        var json = this.json;
        var dracoLoader = this.dracoLoader;
        var bufferViewIndex = primitive.extensions[this.name].bufferView;
        var gltfAttributeMap = primitive.extensions[this.name].attributes;
        var threeAttributeMap = {};
        var attributeNormalizedMap = {};
        var attributeTypeMap = {};
        for (var attributeName in gltfAttributeMap) {
            var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase();
            threeAttributeMap[threeAttributeName] = gltfAttributeMap[attributeName];
        }
        for (attributeName in primitive.attributes) {
            var threeAttributeName = ATTRIBUTES[attributeName] || attributeName.toLowerCase();
            if (gltfAttributeMap[attributeName] !== undefined) {
                var accessorDef = json.accessors[primitive.attributes[attributeName]];
                var componentType = WEBGL_COMPONENT_TYPES[accessorDef.componentType];
                attributeTypeMap[threeAttributeName] = componentType;
                attributeNormalizedMap[threeAttributeName] = accessorDef.normalized === true;
            }
        }
        return parser.getDependency('bufferView', bufferViewIndex).then(function (bufferView) {
            return new Promise(function (resolve) {
                dracoLoader.decodeDracoFile(bufferView, function (geometry) {
                    for (var attributeName in geometry.attributes) {
                        var attribute = geometry.attributes[attributeName];
                        var normalized = attributeNormalizedMap[attributeName];
                        if (normalized !== undefined) attribute.normalized = normalized;
                    }
                    resolve(geometry);
                }, threeAttributeMap, attributeTypeMap);
            });
        });
    };
    /**
     * Texture Transform Extension
     *
     * Specification:
     */
    function GLTFTextureTransformExtension() {
        this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;
    }
    GLTFTextureTransformExtension.prototype.extendTexture = function (texture, transform) {
        texture = texture.clone();
        if (transform.offset !== undefined) {
            texture.offset.fromArray(transform.offset);
        }
        if (transform.rotation !== undefined) {
            texture.rotation = transform.rotation;
        }
        if (transform.scale !== undefined) {
            texture.repeat.fromArray(transform.scale);
        }
        if (transform.texCoord !== undefined) {
            console.warn('THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.');
        }
        texture.needsUpdate = true;
        return texture;
    };
    /**
     * Specular-Glossiness Extension
     *
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
     */
    function GLTFMaterialsPbrSpecularGlossinessExtension() {
        return {
            name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,
            specularGlossinessParams: [
                'color',
                'map',
                'lightMap',
                'lightMapIntensity',
                'aoMap',
                'aoMapIntensity',
                'emissive',
                'emissiveIntensity',
                'emissiveMap',
                'bumpMap',
                'bumpScale',
                'normalMap',
                'displacementMap',
                'displacementScale',
                'displacementBias',
                'specularMap',
                'specular',
                'glossinessMap',
                'glossiness',
                'alphaMap',
                'envMap',
                'envMapIntensity',
                'refractionRatio',
            ],
            getMaterialType: function () {
                return ShaderMaterial;
            },
            extendParams: function (materialParams, materialDef, parser) {
                var pbrSpecularGlossiness = materialDef.extensions[this.name];
                var shader = ShaderLib['standard'];
                var uniforms = UniformsUtils.clone(shader.uniforms);
                var specularMapParsFragmentChunk = [
                    '#ifdef USE_SPECULARMAP',
                    '    uniform sampler2D specularMap;',
                    '#endif'
                ].join('\n');
                var glossinessMapParsFragmentChunk = [
                    '#ifdef USE_GLOSSINESSMAP',
                    '    uniform sampler2D glossinessMap;',
                    '#endif'
                ].join('\n');
                var specularMapFragmentChunk = [
                    'vec3 specularFactor = specular;',
                    '#ifdef USE_SPECULARMAP',
                    '    vec4 texelSpecular = texture2D( specularMap, vUv );',
                    '    texelSpecular = sRGBToLinear( texelSpecular );',
                    '    // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
                    '    specularFactor *= texelSpecular.rgb;',
                    '#endif'
                ].join('\n');
                var glossinessMapFragmentChunk = [
                    'float glossinessFactor = glossiness;',
                    '#ifdef USE_GLOSSINESSMAP',
                    '    vec4 texelGlossiness = texture2D( glossinessMap, vUv );',
                    '    // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture',
                    '    glossinessFactor *= texelGlossiness.a;',
                    '#endif'
                ].join('\n');
                var lightPhysicalFragmentChunk = [
                    'PhysicalMaterial material;',
                    'material.diffuseColor = diffuseColor.rgb;',
                    'material.specularRoughness = clamp( 1.0 - glossinessFactor, 0.04, 1.0 );',
                    'material.specularColor = specularFactor.rgb;',
                ].join('\n');
                var fragmentShader = shader.fragmentShader
                    .replace('uniform float roughness;', 'uniform vec3 specular;')
                    .replace('uniform float metalness;', 'uniform float glossiness;')
                    .replace('#include <roughnessmap_pars_fragment>', specularMapParsFragmentChunk)
                    .replace('#include <metalnessmap_pars_fragment>', glossinessMapParsFragmentChunk)
                    .replace('#include <roughnessmap_fragment>', specularMapFragmentChunk)
                    .replace('#include <metalnessmap_fragment>', glossinessMapFragmentChunk)
                    .replace('#include <lights_physical_fragment>', lightPhysicalFragmentChunk);
                delete uniforms.roughness;
                delete uniforms.metalness;
                delete uniforms.roughnessMap;
                delete uniforms.metalnessMap;
                uniforms.specular = { value: new Color().setHex(0x111111) };
                uniforms.glossiness = { value: 0.5 };
                uniforms.specularMap = { value: null };
                uniforms.glossinessMap = { value: null };
                materialParams.vertexShader = shader.vertexShader;
                materialParams.fragmentShader = fragmentShader;
                materialParams.uniforms = uniforms;
                materialParams.defines = { 'STANDARD': '' }
                materialParams.color = new Color(1.0, 1.0, 1.0);
                materialParams.opacity = 1.0;
                var pending = [];
                if (Array.isArray(pbrSpecularGlossiness.diffuseFactor)) {
                    var array = pbrSpecularGlossiness.diffuseFactor;
                    materialParams.color.fromArray(array);
                    materialParams.opacity = array[3];
                }
                if (pbrSpecularGlossiness.diffuseTexture !== undefined) {
                    pending.push(parser.assignTexture(materialParams, 'map', pbrSpecularGlossiness.diffuseTexture));
                }
                materialParams.emissive = new Color(0.0, 0.0, 0.0);
                materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
                materialParams.specular = new Color(1.0, 1.0, 1.0);
                if (Array.isArray(pbrSpecularGlossiness.specularFactor)) {
                    materialParams.specular.fromArray(pbrSpecularGlossiness.specularFactor);
                }
                if (pbrSpecularGlossiness.specularGlossinessTexture !== undefined) {
                    var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture;
                    pending.push(parser.assignTexture(materialParams, 'glossinessMap', specGlossMapDef));
                    pending.push(parser.assignTexture(materialParams, 'specularMap', specGlossMapDef));
                }
                return Promise.all(pending);
            },
            createMaterial: function (params) {
                // setup material properties based on MeshStandardMaterial for Specular-Glossiness
                var material = new ShaderMaterial({
                    defines: params.defines,
                    vertexShader: params.vertexShader,
                    fragmentShader: params.fragmentShader,
                    uniforms: params.uniforms,
                    fog: true,
                    lights: true,
                    opacity: params.opacity,
                    transparent: params.transparent
                });
                material.isGLTFSpecularGlossinessMaterial = true;
                material.color = params.color;
                material.map = params.map === undefined ? null : params.map;
                material.lightMap = null;
                material.lightMapIntensity = 1.0;
                material.aoMap = params.aoMap === undefined ? null : params.aoMap;
                material.aoMapIntensity = 1.0;
                material.emissive = params.emissive;
                material.emissiveIntensity = 1.0;
                material.emissiveMap = params.emissiveMap === undefined ? null : params.emissiveMap;
                material.bumpMap = params.bumpMap === undefined ? null : params.bumpMap;
                material.bumpScale = 1;
                material.normalMap = params.normalMap === undefined ? null : params.normalMap;
                if (params.normalScale) material.normalScale = params.normalScale;
                material.displacementMap = null;
                material.displacementScale = 1;
                material.displacementBias = 0;
                material.specularMap = params.specularMap === undefined ? null : params.specularMap;
                material.specular = params.specular;
                material.glossinessMap = params.glossinessMap === undefined ? null : params.glossinessMap;
                material.glossiness = params.glossiness;
                material.alphaMap = null;
                material.envMap = params.envMap === undefined ? null : params.envMap;
                material.envMapIntensity = 1.0;
                material.refractionRatio = 0.98;
                material.extensions.derivatives = true;
                return material;
            },
            /**
             * Clones a GLTFSpecularGlossinessMaterial instance. The ShaderMaterial.copy() method can
             * copy only properties it knows about or inherits, and misses many properties that would
             * normally be defined by MeshStandardMaterial.
             *
             * This method allows GLTFSpecularGlossinessMaterials to be cloned in the process of
             * loading a glTF model, but cloning later (e.g. by the user) would require these changes
             * AND also updating `.onBeforeRender` on the parent mesh.
             *
             * @param  {ShaderMaterial} source
             * @return {ShaderMaterial}
             */
            cloneMaterial: function (source) {
                var target = source.clone();
                target.isGLTFSpecularGlossinessMaterial = true;
                var params = this.specularGlossinessParams;
                for (var i = 0, il = params.length; i < il; i++) {
                    var value = source[params[i]];
                    target[params[i]] = (value && value.isColor) ? value.clone() : value;
                }
                return target;
            },
            // Here's based on refreshUniformsCommon() and refreshUniformsStandard() in WebGLRenderer.
            refreshUniforms: function (renderer, scene, camera, geometry, material) {
                if (material.isGLTFSpecularGlossinessMaterial !== true) {
                    return;
                }
                var uniforms = material.uniforms;
                var defines = material.defines;
                uniforms.opacity.value = material.opacity;
                uniforms.diffuse.value.copy(material.color);
                uniforms.emissive.value.copy(material.emissive).multiplyScalar(material.emissiveIntensity);
                uniforms.map.value = material.map;
                uniforms.specularMap.value = material.specularMap;
                uniforms.alphaMap.value = material.alphaMap;
                uniforms.lightMap.value = material.lightMap;
                uniforms.lightMapIntensity.value = material.lightMapIntensity;
                uniforms.aoMap.value = material.aoMap;
                uniforms.aoMapIntensity.value = material.aoMapIntensity;
                // uv repeat and offset setting priorities
                // 1. color map
                // 2. specular map
                // 3. normal map
                // 4. bump map
                // 5. alpha map
                // 6. emissive map
                var uvScaleMap;
                if (material.map) {
                    uvScaleMap = material.map;
                } else if (material.specularMap) {
                    uvScaleMap = material.specularMap;
                } else if (material.displacementMap) {
                    uvScaleMap = material.displacementMap;
                } else if (material.normalMap) {
                    uvScaleMap = material.normalMap;
                } else if (material.bumpMap) {
                    uvScaleMap = material.bumpMap;
                } else if (material.glossinessMap) {
                    uvScaleMap = material.glossinessMap;
                } else if (material.alphaMap) {
                    uvScaleMap = material.alphaMap;
                } else if (material.emissiveMap) {
                    uvScaleMap = material.emissiveMap;
                }
                if (uvScaleMap !== undefined) {
                    // backwards compatibility
                    if (uvScaleMap.isWebGLRenderTarget) {
                        uvScaleMap = uvScaleMap.texture;
                    }
                    if (uvScaleMap.matrixAutoUpdate === true) {
                        uvScaleMap.updateMatrix();
                    }
                    uniforms.uvTransform.value.copy(uvScaleMap.matrix);
                }
                if (material.envMap) {
                    uniforms.envMap.value = material.envMap;
                    uniforms.envMapIntensity.value = material.envMapIntensity;
                    // don't flip CubeTexture envMaps, flip everything else:
                    //  WebGLRenderTargetCube will be flipped for backwards compatibility
                    //  WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture
                    // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future
                    uniforms.flipEnvMap.value = material.envMap.isCubeTexture ? - 1 : 1;
                    uniforms.reflectivity.value = material.reflectivity;
                    uniforms.refractionRatio.value = material.refractionRatio;
                    uniforms.maxMipLevel.value = renderer.properties.get(material.envMap).__maxMipLevel;
                }
                uniforms.specular.value.copy(material.specular);
                uniforms.glossiness.value = material.glossiness;
                uniforms.glossinessMap.value = material.glossinessMap;
                uniforms.emissiveMap.value = material.emissiveMap;
                uniforms.bumpMap.value = material.bumpMap;
                uniforms.normalMap.value = material.normalMap;
                uniforms.displacementMap.value = material.displacementMap;
                uniforms.displacementScale.value = material.displacementScale;
                uniforms.displacementBias.value = material.displacementBias;
                if (uniforms.glossinessMap.value !== null && defines.USE_GLOSSINESSMAP === undefined) {
                    defines.USE_GLOSSINESSMAP = '';
                    // set USE_ROUGHNESSMAP to enable vUv
                    defines.USE_ROUGHNESSMAP = '';
                }
                if (uniforms.glossinessMap.value === null && defines.USE_GLOSSINESSMAP !== undefined) {
                    delete defines.USE_GLOSSINESSMAP;
                    delete defines.USE_ROUGHNESSMAP;
                }
            }
        };
    }
    /*********************************/
    /********** INTERPOLATION ********/
    /*********************************/
    // Spline Interpolation
    // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
    function GLTFCubicSplineInterpolant(parameterPositions, sampleValues, sampleSize, resultBuffer) {
        Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer);
    }
    GLTFCubicSplineInterpolant.prototype = Object.create(Interpolant.prototype);
    GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant;
    GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function (index) {
        // Copies a sample value to the result buffer. See description of glTF
        // CUBICSPLINE values layout in interpolate_() function below.
        var result = this.resultBuffer,
            values = this.sampleValues,
            valueSize = this.valueSize,
            offset = index * valueSize * 3 + valueSize;
        for (var i = 0; i !== valueSize; i++) {
            result[i] = values[offset + i];
        }
        return result;
    };
    GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
    GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
    GLTFCubicSplineInterpolant.prototype.interpolate_ = function (i1, t0, t, t1) {
        var result = this.resultBuffer;
        var values = this.sampleValues;
        var stride = this.valueSize;
        var stride2 = stride * 2;
        var stride3 = stride * 3;
        var td = t1 - t0;
        var p = (t - t0) / td;
        var pp = p * p;
        var ppp = pp * p;
        var offset1 = i1 * stride3;
        var offset0 = offset1 - stride3;
        var s2 = - 2 * ppp + 3 * pp;
        var s3 = ppp - pp;
        var s0 = 1 - s2;
        var s1 = s3 - pp + p;
        // Layout of keyframe output values for CUBICSPLINE animations:
        //   [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
        for (var i = 0; i !== stride; i++) {
            var p0 = values[offset0 + i + stride]; // splineVertex_k
            var m0 = values[offset0 + i + stride2] * td; // outTangent_k * (t_k+1 - t_k)
            var p1 = values[offset1 + i + stride]; // splineVertex_k+1
            var m1 = values[offset1 + i] * td; // inTangent_k+1 * (t_k+1 - t_k)
            result[i] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1;
        }
        return result;
    };
    /*********************************/
    /********** INTERNALS ************/
    /*********************************/
    /* CONSTANTS */
    var WEBGL_CONSTANTS = {
        FLOAT: 5126,
        //FLOAT_MAT2: 35674,
        FLOAT_MAT3: 35675,
        FLOAT_MAT4: 35676,
        FLOAT_VEC2: 35664,
        FLOAT_VEC3: 35665,
        FLOAT_VEC4: 35666,
        LINEAR: 9729,
        REPEAT: 10497,
        SAMPLER_2D: 35678,
        POINTS: 0,
        LINES: 1,
        LINE_LOOP: 2,
        LINE_STRIP: 3,
        TRIANGLES: 4,
        TRIANGLE_STRIP: 5,
        TRIANGLE_FAN: 6,
        UNSIGNED_BYTE: 5121,
        UNSIGNED_SHORT: 5123
    };
    var WEBGL_COMPONENT_TYPES = {
        5120: Int8Array,
        5121: Uint8Array,
        5122: Int16Array,
        5123: Uint16Array,
        5125: Uint32Array,
        5126: Float32Array
    };
    var WEBGL_FILTERS = {
        9728: NearestFilter,
        9729: LinearFilter,
        9984: NearestMipmapNearestFilter,
        9985: LinearMipmapNearestFilter,
        9986: NearestMipmapLinearFilter,
        9987: LinearMipmapLinearFilter
    };
    var WEBGL_WRAPPINGS = {
        33071: ClampToEdgeWrapping,
        33648: MirroredRepeatWrapping,
        10497: RepeatWrapping
    };
    var WEBGL_TYPE_SIZES = {
        'SCALAR': 1,
        'VEC2': 2,
        'VEC3': 3,
        'VEC4': 4,
        'MAT2': 4,
        'MAT3': 9,
        'MAT4': 16
    };
    var ATTRIBUTES = {
        POSITION: 'position',
        NORMAL: 'normal',
        TANGENT: 'tangent',
        TEXCOORD_0: 'uv',
        TEXCOORD_1: 'uv2',
        COLOR_0: 'color',
        WEIGHTS_0: 'skinWeight',
        JOINTS_0: 'skinIndex',
    };
    var PATH_PROPERTIES = {
        scale: 'scale',
        translation: 'position',
        rotation: 'quaternion',
        weights: 'morphTargetInfluences'
    };
    var INTERPOLATION = {
        CUBICSPLINE: undefined, // We use a custom interpolant (GLTFCubicSplineInterpolation) for CUBICSPLINE tracks. Each
        // keyframe track will be initialized with a default interpolation type, then modified.
        LINEAR: InterpolateLinear,
        STEP: InterpolateDiscrete
    };
    var ALPHA_MODES = {
        OPAQUE: 'OPAQUE',
        MASK: 'MASK',
        BLEND: 'BLEND'
    };
    var MIME_TYPE_FORMATS = {
        'image/png': RGBAFormat,
        'image/jpeg': RGBFormat
    };
    /* UTILITY FUNCTIONS */
    function resolveURL(url, path) {
        // Invalid URL
        if (typeof url !== 'string' || url === '') return '';
        // Host Relative URL
        if (/^https?:\/\//i.test(path) && /^\//.test(url)) {
            path = path.replace(/(^https?:\/\/[^\/]+).*/i, '$1');
        }
        // Absolute URL http://,https://,//
        if (/^(https?:)?\/\//i.test(url)) return url;
        // Data URI
        if (/^data:.*,.*$/i.test(url)) return url;
        // Blob URL
        if (/^blob:.*$/i.test(url)) return url;
        // Relative URL
        return path + url;
    }
    var defaultMaterial;
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
     */
    function createDefaultMaterial() {
        defaultMaterial = defaultMaterial || new MeshStandardMaterial({
            color: 0xFFFFFF,
            emissive: 0x000000,
            metalness: 1,
            roughness: 1,
            transparent: false,
            depthTest: true,
            side: FrontSide
        });
        return defaultMaterial;
    }
    function addUnknownExtensionsToUserData(knownExtensions, object, objectDef) {
        // Add unknown glTF extensions to an object's userData.
        for (var name in objectDef.extensions) {
            if (knownExtensions[name] === undefined) {
                object.userData.gltfExtensions = object.userData.gltfExtensions || {};
                object.userData.gltfExtensions[name] = objectDef.extensions[name];
            }
        }
    }
    /**
     * @param {Object3D|Material|BufferGeometry} object
     * @param {GLTF.definition} gltfDef
     */
    function assignExtrasToUserData(object, gltfDef) {
        if (gltfDef.extras !== undefined) {
            if (typeof gltfDef.extras === 'object') {
                Object.assign(object.userData, gltfDef.extras);
            } else {
                console.warn('THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras);
            }
        }
    }
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
     *
     * @param {BufferGeometry} geometry
     * @param {Array<GLTF.Target>} targets
     * @param {GLTFParser} parser
     * @return {Promise<BufferGeometry>}
     */
    function addMorphTargets(geometry, targets, parser) {
        var hasMorphPosition = false;
        var hasMorphNormal = false;
        for (var i = 0, il = targets.length; i < il; i++) {
            var target = targets[i];
            if (target.POSITION !== undefined) hasMorphPosition = true;
            if (target.NORMAL !== undefined) hasMorphNormal = true;
            if (hasMorphPosition && hasMorphNormal) break;
        }
        if (!hasMorphPosition && !hasMorphNormal) return Promise.resolve(geometry);
        var pendingPositionAccessors = [];
        var pendingNormalAccessors = [];
        for (var i = 0, il = targets.length; i < il; i++) {
            var target = targets[i];
            if (hasMorphPosition) {
                var pendingAccessor = target.POSITION !== undefined
                    ? parser.getDependency('accessor', target.POSITION)
                    : geometry.attributes.position;
                pendingPositionAccessors.push(pendingAccessor);
            }
            if (hasMorphNormal) {
                var pendingAccessor = target.NORMAL !== undefined
                    ? parser.getDependency('accessor', target.NORMAL)
                    : geometry.attributes.normal;
                pendingNormalAccessors.push(pendingAccessor);
            }
        }
        return Promise.all([
            Promise.all(pendingPositionAccessors),
            Promise.all(pendingNormalAccessors)
        ]).then(function (accessors) {
            var morphPositions = accessors[0];
            var morphNormals = accessors[1];
            // Clone morph target accessors before modifying them.
            for (var i = 0, il = morphPositions.length; i < il; i++) {
                if (geometry.attributes.position === morphPositions[i]) continue;
                morphPositions[i] = cloneBufferAttribute(morphPositions[i]);
            }
            for (var i = 0, il = morphNormals.length; i < il; i++) {
                if (geometry.attributes.normal === morphNormals[i]) continue;
                morphNormals[i] = cloneBufferAttribute(morphNormals[i]);
            }
            for (var i = 0, il = targets.length; i < il; i++) {
                var target = targets[i];
                var attributeName = 'morphTarget' + i;
                if (hasMorphPosition) {
                    // Three.js morph position is absolute value. The formula is
                    //   basePosition
                    //     + weight0 * ( morphPosition0 - basePosition )
                    //     + weight1 * ( morphPosition1 - basePosition )
                    //     ...
                    // while the glTF one is relative
                    //   basePosition
                    //     + weight0 * glTFmorphPosition0
                    //     + weight1 * glTFmorphPosition1
                    //     ...
                    // then we need to convert from relative to absolute here.
                    if (target.POSITION !== undefined) {
                        var positionAttribute = morphPositions[i];
                        positionAttribute.name = attributeName;
                        var position = geometry.attributes.position;
                        for (var j = 0, jl = positionAttribute.count; j < jl; j++) {
                            positionAttribute.setXYZ(
                                j,
                                positionAttribute.getX(j) + position.getX(j),
                                positionAttribute.getY(j) + position.getY(j),
                                positionAttribute.getZ(j) + position.getZ(j)
                            );
                        }
                    }
                }
                if (hasMorphNormal) {
                    // see target.POSITION's comment
                    if (target.NORMAL !== undefined) {
                        var normalAttribute = morphNormals[i];
                        normalAttribute.name = attributeName;
                        var normal = geometry.attributes.normal;
                        for (var j = 0, jl = normalAttribute.count; j < jl; j++) {
                            normalAttribute.setXYZ(
                                j,
                                normalAttribute.getX(j) + normal.getX(j),
                                normalAttribute.getY(j) + normal.getY(j),
                                normalAttribute.getZ(j) + normal.getZ(j)
                            );
                        }
                    }
                }
            }
            if (hasMorphPosition) geometry.morphAttributes.position = morphPositions;
            if (hasMorphNormal) geometry.morphAttributes.normal = morphNormals;
            return geometry;
        });
    }
    /**
     * @param {Mesh} mesh
     * @param {GLTF.Mesh} meshDef
     */
    function updateMorphTargets(mesh, meshDef) {
        mesh.updateMorphTargets();
        if (meshDef.weights !== undefined) {
            for (var i = 0, il = meshDef.weights.length; i < il; i++) {
                mesh.morphTargetInfluences[i] = meshDef.weights[i];
            }
        }
        // .extras has user-defined data, so check that .extras.targetNames is an array.
        if (meshDef.extras && Array.isArray(meshDef.extras.targetNames)) {
            var targetNames = meshDef.extras.targetNames;
            if (mesh.morphTargetInfluences.length === targetNames.length) {
                mesh.morphTargetDictionary = {};
                for (var i = 0, il = targetNames.length; i < il; i++) {
                    mesh.morphTargetDictionary[targetNames[i]] = i;
                }
            } else {
                console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.');
            }
        }
    }
    function createPrimitiveKey(primitiveDef) {
        var dracoExtension = primitiveDef.extensions && primitiveDef.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION];
        var geometryKey;
        if (dracoExtension) {
            geometryKey = 'draco:' + dracoExtension.bufferView
                + ':' + dracoExtension.indices
                + ':' + createAttributesKey(dracoExtension.attributes);
        } else {
            geometryKey = primitiveDef.indices + ':' + createAttributesKey(primitiveDef.attributes) + ':' + primitiveDef.mode;
        }
        return geometryKey;
    }
    function createAttributesKey(attributes) {
        var attributesKey = '';
        var keys = Object.keys(attributes).sort();
        for (var i = 0, il = keys.length; i < il; i++) {
            attributesKey += keys[i] + ':' + attributes[keys[i]] + ';';
        }
        return attributesKey;
    }
    function cloneBufferAttribute(attribute) {
        if (attribute.isInterleavedBufferAttribute) {
            var count = attribute.count;
            var itemSize = attribute.itemSize;
            var array = attribute.array.slice(0, count * itemSize);
            for (var i = 0, j = 0; i < count; ++i) {
                array[j++] = attribute.getX(i);
                if (itemSize >= 2) array[j++] = attribute.getY(i);
                if (itemSize >= 3) array[j++] = attribute.getZ(i);
                if (itemSize >= 4) array[j++] = attribute.getW(i);
            }
            return new BufferAttribute(array, itemSize, attribute.normalized);
        }
        return attribute.clone();
    }
    /* GLTF PARSER */
    function GLTFParser(json, extensions, options) {
        this.json = json || {};
        this.extensions = extensions || {};
        this.options = options || {};
        // loader object cache
        this.cache = new GLTFRegistry();
        // BufferGeometry caching
        this.primitiveCache = {};
        this.textureLoader = new TextureLoader(this.options.manager);
        this.textureLoader.setCrossOrigin(this.options.crossOrigin);
        this.fileLoader = new FileLoader(this.options.manager);
        this.fileLoader.setResponseType('arraybuffer');
        if (this.options.crossOrigin === 'use-credentials') {
            this.fileLoader.setWithCredentials(true);
        }
    }
    GLTFParser.prototype.parse = function (onLoad, onError) {
        var parser = this;
        var json = this.json;
        var extensions = this.extensions;
        // Clear the loader cache
        this.cache.removeAll();
        // Mark the special nodes/meshes in json for efficient parse
        this.markDefs();
        Promise.all([
            this.getDependencies('scene'),
            this.getDependencies('animation'),
            this.getDependencies('camera'),
        ]).then(function (dependencies) {
            var result = {
                scene: dependencies[0][json.scene || 0],
                scenes: dependencies[0],
                animations: dependencies[1],
                cameras: dependencies[2],
                asset: json.asset,
                parser: parser,
                userData: {}
            };
            addUnknownExtensionsToUserData(extensions, result, json);
            assignExtrasToUserData(result, json);
            onLoad(result);
        }).catch(onError);
    };
    /**
     * Marks the special nodes/meshes in json for efficient parse.
     */
    GLTFParser.prototype.markDefs = function () {
        var nodeDefs = this.json.nodes || [];
        var skinDefs = this.json.skins || [];
        var meshDefs = this.json.meshes || [];
        var meshReferences = {};
        var meshUses = {};
        // Nothing in the node definition indicates whether it is a Bone or an
        // Object3D. Use the skins' joint references to mark bones.
        for (var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex++) {
            var joints = skinDefs[skinIndex].joints;
            for (var i = 0, il = joints.length; i < il; i++) {
                nodeDefs[joints[i]].isBone = true;
            }
        }
        // Meshes can (and should) be reused by multiple nodes in a glTF asset. To
        // avoid having more than one Mesh with the same name, count
        // references and rename instances below.
        //
        // Example: CesiumMilkTruck sample model reuses "Wheel" meshes.
        for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) {
            var nodeDef = nodeDefs[nodeIndex];
            if (nodeDef.mesh !== undefined) {
                if (meshReferences[nodeDef.mesh] === undefined) {
                    meshReferences[nodeDef.mesh] = meshUses[nodeDef.mesh] = 0;
                }
                meshReferences[nodeDef.mesh]++;
                // Nothing in the mesh definition indicates whether it is
                // a SkinnedMesh or Mesh. Use the node's mesh reference
                // to mark SkinnedMesh if node has skin.
                if (nodeDef.skin !== undefined) {
                    meshDefs[nodeDef.mesh].isSkinnedMesh = true;
                }
            }
        }
        this.json.meshReferences = meshReferences;
        this.json.meshUses = meshUses;
    };
    /**
     * Requests the specified dependency asynchronously, with caching.
     * @param {string} type
     * @param {number} index
     * @return {Promise<Object3D|Material|THREE.Texture|AnimationClip|ArrayBuffer|Object>}
     */
    GLTFParser.prototype.getDependency = function (type, index) {
        var cacheKey = type + ':' + index;
        var dependency = this.cache.get(cacheKey);
        if (!dependency) {
            switch (type) {
                case 'scene':
                    dependency = this.loadScene(index);
                    break;
                case 'node':
                    dependency = this.loadNode(index);
                    break;
                case 'mesh':
                    dependency = this.loadMesh(index);
                    break;
                case 'accessor':
                    dependency = this.loadAccessor(index);
                    break;
                case 'bufferView':
                    dependency = this.loadBufferView(index);
                    break;
                case 'buffer':
                    dependency = this.loadBuffer(index);
                    break;
                case 'material':
                    dependency = this.loadMaterial(index);
                    break;
                case 'texture':
                    dependency = this.loadTexture(index);
                    break;
                case 'skin':
                    dependency = this.loadSkin(index);
                    break;
                case 'animation':
                    dependency = this.loadAnimation(index);
                    break;
                case 'camera':
                    dependency = this.loadCamera(index);
                    break;
                case 'light':
                    dependency = this.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].loadLight(index);
                    break;
                default:
                    throw new Error('Unknown type: ' + type);
            }
            this.cache.add(cacheKey, dependency);
        }
        return dependency;
    };
    /**
     * Requests all dependencies of the specified type asynchronously, with caching.
     * @param {string} type
     * @return {Promise<Array<Object>>}
     */
    GLTFParser.prototype.getDependencies = function (type) {
        var dependencies = this.cache.get(type);
        if (!dependencies) {
            var parser = this;
            var defs = this.json[type + (type === 'mesh' ? 'es' : 's')] || [];
            dependencies = Promise.all(defs.map(function (def, index) {
                return parser.getDependency(type, index);
            }));
            this.cache.add(type, dependencies);
        }
        return dependencies;
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
     * @param {number} bufferIndex
     * @return {Promise<ArrayBuffer>}
     */
    GLTFParser.prototype.loadBuffer = function (bufferIndex) {
        var bufferDef = this.json.buffers[bufferIndex];
        var loader = this.fileLoader;
        if (bufferDef.type && bufferDef.type !== 'arraybuffer') {
            throw new Error('THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.');
        }
        // If present, GLB container is required to be the first buffer.
        if (bufferDef.uri === undefined && bufferIndex === 0) {
            return Promise.resolve(this.extensions[EXTENSIONS.KHR_BINARY_GLTF].body);
        }
        var options = this.options;
        return new Promise(function (resolve, reject) {
            loader.load(resolveURL(bufferDef.uri, options.path), resolve, undefined, function () {
                reject(new Error('THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".'));
            });
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
     * @param {number} bufferViewIndex
     * @return {Promise<ArrayBuffer>}
     */
    GLTFParser.prototype.loadBufferView = function (bufferViewIndex) {
        var bufferViewDef = this.json.bufferViews[bufferViewIndex];
        return this.getDependency('buffer', bufferViewDef.buffer).then(function (buffer) {
            var byteLength = bufferViewDef.byteLength || 0;
            var byteOffset = bufferViewDef.byteOffset || 0;
            return buffer.slice(byteOffset, byteOffset + byteLength);
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
     * @param {number} accessorIndex
     * @return {Promise<BufferAttribute|InterleavedBufferAttribute>}
     */
    GLTFParser.prototype.loadAccessor = function (accessorIndex) {
        var parser = this;
        var json = this.json;
        var accessorDef = this.json.accessors[accessorIndex];
        if (accessorDef.bufferView === undefined && accessorDef.sparse === undefined) {
            // Ignore empty accessors, which may be used to declare runtime
            // information about attributes coming from another source (e.g. Draco
            // compression extension).
            return Promise.resolve(null);
        }
        var pendingBufferViews = [];
        if (accessorDef.bufferView !== undefined) {
            pendingBufferViews.push(this.getDependency('bufferView', accessorDef.bufferView));
        } else {
            pendingBufferViews.push(null);
        }
        if (accessorDef.sparse !== undefined) {
            pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.indices.bufferView));
            pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.values.bufferView));
        }
        return Promise.all(pendingBufferViews).then(function (bufferViews) {
            var bufferView = bufferViews[0];
            var itemSize = WEBGL_TYPE_SIZES[accessorDef.type];
            var TypedArray = WEBGL_COMPONENT_TYPES[accessorDef.componentType];
            // For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
            var elementBytes = TypedArray.BYTES_PER_ELEMENT;
            var itemBytes = elementBytes * itemSize;
            var byteOffset = accessorDef.byteOffset || 0;
            var byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[accessorDef.bufferView].byteStride : undefined;
            var normalized = accessorDef.normalized === true;
            var array, bufferAttribute;
            // The buffer is not interleaved if the stride is the item size in bytes.
            if (byteStride && byteStride !== itemBytes) {
                // Each "slice" of the buffer, as defined by 'count' elements of 'byteStride' bytes, gets its own InterleavedBuffer
                // This makes sure that IBA.count reflects accessor.count properly
                var ibSlice = Math.floor(byteOffset / byteStride);
                var ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType + ':' + ibSlice + ':' + accessorDef.count;
                var ib = parser.cache.get(ibCacheKey);
                if (!ib) {
                    array = new TypedArray(bufferView, ibSlice * byteStride, accessorDef.count * byteStride / elementBytes);
                    // Integer parameters to IB/IBA are in array elements, not bytes.
                    ib = new InterleavedBuffer(array, byteStride / elementBytes);
                    parser.cache.add(ibCacheKey, ib);
                }
                bufferAttribute = new InterleavedBufferAttribute(ib, itemSize, (byteOffset % byteStride) / elementBytes, normalized);
            } else {
                if (bufferView === null) {
                    array = new TypedArray(accessorDef.count * itemSize);
                } else {
                    array = new TypedArray(bufferView, byteOffset, accessorDef.count * itemSize);
                }
                bufferAttribute = new BufferAttribute(array, itemSize, normalized);
            }
            // https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
            if (accessorDef.sparse !== undefined) {
                var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR;
                var TypedArrayIndices = WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType];
                var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0;
                var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0;
                var sparseIndices = new TypedArrayIndices(bufferViews[1], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices);
                var sparseValues = new TypedArray(bufferViews[2], byteOffsetValues, accessorDef.sparse.count * itemSize);
                if (bufferView !== null) {
                    // Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
                    bufferAttribute.setArray(bufferAttribute.array.slice());
                }
                for (var i = 0, il = sparseIndices.length; i < il; i++) {
                    var index = sparseIndices[i];
                    bufferAttribute.setX(index, sparseValues[i * itemSize]);
                    if (itemSize >= 2) bufferAttribute.setY(index, sparseValues[i * itemSize + 1]);
                    if (itemSize >= 3) bufferAttribute.setZ(index, sparseValues[i * itemSize + 2]);
                    if (itemSize >= 4) bufferAttribute.setW(index, sparseValues[i * itemSize + 3]);
                    if (itemSize >= 5) throw new Error('THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.');
                }
            }
            return bufferAttribute;
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#textures
     * @param {number} textureIndex
     * @return {Promise<THREE.Texture>}
     */
    GLTFParser.prototype.loadTexture = function (textureIndex) {
        var parser = this;
        var json = this.json;
        var options = this.options;
        var textureLoader = this.textureLoader;
        var URL = window.URL || window.webkitURL;
        var textureDef = json.textures[textureIndex];
        var textureExtensions = textureDef.extensions || {};
        var source;
        if (textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS]) {
            source = json.images[textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS].source];
        } else {
            source = json.images[textureDef.source];
        }
        var sourceURI = source.uri;
        var isObjectURL = false;
        if (source.bufferView !== undefined) {
            // Load binary image data from bufferView, if provided.
            sourceURI = parser.getDependency('bufferView', source.bufferView).then(function (bufferView) {
                if (window.arrayBufferToBase64 != undefined) {
                    var base64 = window.arrayBufferToBase64(bufferView)
                    var base64String = `data:${source.mimeType};base64,${base64}`
                    return base64String
                }
                isObjectURL = true;
                var blob = new Blob([bufferView], { type: source.mimeType });
                sourceURI = URL.createObjectURL(blob);
                return sourceURI;
            });
        }
        return Promise.resolve(sourceURI).then(function (sourceURI) {
            // Load Texture resource.
            var loader = Loader.Handlers.get(sourceURI);
            if (!loader) {
                loader = textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS]
                    ? parser.extensions[EXTENSIONS.MSFT_TEXTURE_DDS].ddsLoader
                    : textureLoader;
            }
            return new Promise(function (resolve, reject) {
                loader.load(resolveURL(sourceURI, options.path), resolve, undefined, reject);
            });
        }).then(function (texture) {
            // Clean up resources and configure Texture.
            if (window.arrayBufferToBase64 == undefined) {
                if (isObjectURL === true) {
                    URL.revokeObjectURL(sourceURI);
                }
            }
            texture.flipY = false;
            if (textureDef.name !== undefined) texture.name = textureDef.name;
            // Ignore unknown mime types, like DDS files.
            if (source.mimeType in MIME_TYPE_FORMATS) {
                texture.format = MIME_TYPE_FORMATS[source.mimeType];
            }
            var samplers = json.samplers || {};
            var sampler = samplers[textureDef.sampler] || {};
            texture.magFilter = WEBGL_FILTERS[sampler.magFilter] || LinearFilter;
            texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || LinearMipmapLinearFilter;
            texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || RepeatWrapping;
            texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || RepeatWrapping;
            return texture;
        });
    };
    /**
     * Asynchronously assigns a texture to the given material parameters.
     * @param {Object} materialParams
     * @param {string} mapName
     * @param {Object} mapDef
     * @return {Promise}
     */
    GLTFParser.prototype.assignTexture = function (materialParams, mapName, mapDef) {
        var parser = this;
        return this.getDependency('texture', mapDef.index).then(function (texture) {
            if (!texture.isCompressedTexture) {
                switch (mapName) {
                    case 'aoMap':
                    case 'emissiveMap':
                    case 'metalnessMap':
                    case 'normalMap':
                    case 'roughnessMap':
                        texture.format = RGBFormat;
                        break;
                }
            }
            if (parser.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM]) {
                var transform = mapDef.extensions !== undefined ? mapDef.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM] : undefined;
                if (transform) {
                    texture = parser.extensions[EXTENSIONS.KHR_TEXTURE_TRANSFORM].extendTexture(texture, transform);
                }
            }
            materialParams[mapName] = texture;
        });
    };
    /**
     * Assigns final material to a Mesh, Line, or Points instance. The instance
     * already has a material (generated from the glTF material options alone)
     * but reuse of the same glTF material may require multiple threejs materials
     * to accomodate different primitive types, defines, etc. New materials will
     * be created if necessary, and reused from a cache.
     * @param  {Object3D} mesh Mesh, Line, or Points instance.
     */
    GLTFParser.prototype.assignFinalMaterial = function (mesh) {
        var geometry = mesh.geometry;
        var material = mesh.material;
        var extensions = this.extensions;
        var useVertexTangents = geometry.attributes.tangent !== undefined;
        var useVertexColors = geometry.attributes.color !== undefined;
        var useFlatShading = geometry.attributes.normal === undefined;
        var useSkinning = mesh.isSkinnedMesh === true;
        var useMorphTargets = Object.keys(geometry.morphAttributes).length > 0;
        var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
        if (mesh.isPoints) {
            var cacheKey = 'PointsMaterial:' + material.uuid;
            var pointsMaterial = this.cache.get(cacheKey);
            if (!pointsMaterial) {
                pointsMaterial = new PointsMaterial();
                Material.prototype.copy.call(pointsMaterial, material);
                pointsMaterial.color.copy(material.color);
                pointsMaterial.map = material.map;
                pointsMaterial.lights = false; // PointsMaterial doesn't support lights yet
                pointsMaterial.sizeAttenuation = false; // glTF spec says points should be 1px
                this.cache.add(cacheKey, pointsMaterial);
            }
            material = pointsMaterial;
        } else if (mesh.isLine) {
            var cacheKey = 'LineBasicMaterial:' + material.uuid;
            var lineMaterial = this.cache.get(cacheKey);
            if (!lineMaterial) {
                lineMaterial = new LineBasicMaterial();
                Material.prototype.copy.call(lineMaterial, material);
                lineMaterial.color.copy(material.color);
                lineMaterial.lights = false; // LineBasicMaterial doesn't support lights yet
                this.cache.add(cacheKey, lineMaterial);
            }
            material = lineMaterial;
        }
        // Clone the material if it will be modified
        if (useVertexTangents || useVertexColors || useFlatShading || useSkinning || useMorphTargets) {
            var cacheKey = 'ClonedMaterial:' + material.uuid + ':';
            if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:';
            if (useSkinning) cacheKey += 'skinning:';
            if (useVertexTangents) cacheKey += 'vertex-tangents:';
            if (useVertexColors) cacheKey += 'vertex-colors:';
            if (useFlatShading) cacheKey += 'flat-shading:';
            if (useMorphTargets) cacheKey += 'morph-targets:';
            if (useMorphNormals) cacheKey += 'morph-normals:';
            var cachedMaterial = this.cache.get(cacheKey);
            if (!cachedMaterial) {
                cachedMaterial = material.isGLTFSpecularGlossinessMaterial
                    ? extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].cloneMaterial(material)
                    : material.clone();
                if (useSkinning) cachedMaterial.skinning = true;
                if (useVertexTangents) cachedMaterial.vertexTangents = true;
                if (useVertexColors) cachedMaterial.vertexColors = VertexColors;
                if (useFlatShading) cachedMaterial.flatShading = true;
                if (useMorphTargets) cachedMaterial.morphTargets = true;
                if (useMorphNormals) cachedMaterial.morphNormals = true;
                this.cache.add(cacheKey, cachedMaterial);
            }
            material = cachedMaterial;
        }
        // workarounds for mesh and geometry
        if (material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined) {
            console.log('THREE.GLTFLoader: Duplicating UVs to support aoMap.');
            geometry.addAttribute('uv2', new BufferAttribute(geometry.attributes.uv.array, 2));
        }
        if (material.isGLTFSpecularGlossinessMaterial) {
            // for GLTFSpecularGlossinessMaterial(ShaderMaterial) uniforms runtime update
            mesh.onBeforeRender = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].refreshUniforms;
        }
        mesh.material = material;
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
     * @param {number} materialIndex
     * @return {Promise<Material>}
     */
    GLTFParser.prototype.loadMaterial = function (materialIndex) {
        var parser = this;
        var json = this.json;
        var extensions = this.extensions;
        var materialDef = json.materials[materialIndex];
        var materialType;
        var materialParams = {};
        var materialExtensions = materialDef.extensions || {};
        var pending = [];
        if (materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]) {
            var sgExtension = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS];
            materialType = sgExtension.getMaterialType();
            pending.push(sgExtension.extendParams(materialParams, materialDef, parser));
        } else if (materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT]) {
            var kmuExtension = extensions[EXTENSIONS.KHR_MATERIALS_UNLIT];
            materialType = kmuExtension.getMaterialType();
            pending.push(kmuExtension.extendParams(materialParams, materialDef, parser));
        } else {
            // Specification:
            // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material
            materialType = MeshStandardMaterial;
            var metallicRoughness = materialDef.pbrMetallicRoughness || {};
            materialParams.color = new Color(1.0, 1.0, 1.0);
            materialParams.opacity = 1.0;
            if (Array.isArray(metallicRoughness.baseColorFactor)) {
                var array = metallicRoughness.baseColorFactor;
                materialParams.color.fromArray(array);
                materialParams.opacity = array[3];
            }
            if (metallicRoughness.baseColorTexture !== undefined) {
                pending.push(parser.assignTexture(materialParams, 'map', metallicRoughness.baseColorTexture));
            }
            materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0;
            materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0;
            if (metallicRoughness.metallicRoughnessTexture !== undefined) {
                pending.push(parser.assignTexture(materialParams, 'metalnessMap', metallicRoughness.metallicRoughnessTexture));
                pending.push(parser.assignTexture(materialParams, 'roughnessMap', metallicRoughness.metallicRoughnessTexture));
            }
        }
        if (materialDef.doubleSided === true) {
            materialParams.side = DoubleSide;
        }
        var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE;
        if (alphaMode === ALPHA_MODES.BLEND) {
            materialParams.transparent = true;
        } else {
            materialParams.transparent = false;
            if (alphaMode === ALPHA_MODES.MASK) {
                materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5;
            }
        }
        if (materialDef.normalTexture !== undefined && materialType !== MeshBasicMaterial) {
            pending.push(parser.assignTexture(materialParams, 'normalMap', materialDef.normalTexture));
            materialParams.normalScale = new Vector2(1, 1);
            if (materialDef.normalTexture.scale !== undefined) {
                materialParams.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale);
            }
        }
        if (materialDef.occlusionTexture !== undefined && materialType !== MeshBasicMaterial) {
            pending.push(parser.assignTexture(materialParams, 'aoMap', materialDef.occlusionTexture));
            if (materialDef.occlusionTexture.strength !== undefined) {
                materialParams.aoMapIntensity = materialDef.occlusionTexture.strength;
            }
        }
        if (materialDef.emissiveFactor !== undefined && materialType !== MeshBasicMaterial) {
            materialParams.emissive = new Color().fromArray(materialDef.emissiveFactor);
        }
        if (materialDef.emissiveTexture !== undefined && materialType !== MeshBasicMaterial) {
            pending.push(parser.assignTexture(materialParams, 'emissiveMap', materialDef.emissiveTexture));
        }
        return Promise.all(pending).then(function () {
            var material;
            if (materialType === ShaderMaterial) {
                material = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams);
            } else {
                material = new materialType(materialParams);
            }
            if (materialDef.name !== undefined) material.name = materialDef.name;
            // baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding.
            if (material.map) material.map.encoding = sRGBEncoding;
            if (material.emissiveMap) material.emissiveMap.encoding = sRGBEncoding;
            if (material.specularMap) material.specularMap.encoding = sRGBEncoding;
            assignExtrasToUserData(material, materialDef);
            if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef);
            return material;
        });
    };
    /**
     * @param {BufferGeometry} geometry
     * @param {GLTF.Primitive} primitiveDef
     * @param {GLTFParser} parser
     * @return {Promise<BufferGeometry>}
     */
    function addPrimitiveAttributes(geometry, primitiveDef, parser) {
        var attributes = primitiveDef.attributes;
        var pending = [];
        function assignAttributeAccessor(accessorIndex, attributeName) {
            return parser.getDependency('accessor', accessorIndex)
                .then(function (accessor) {
                    geometry.addAttribute(attributeName, accessor);
                });
        }
        for (var gltfAttributeName in attributes) {
            var threeAttributeName = ATTRIBUTES[gltfAttributeName] || gltfAttributeName.toLowerCase();
            // Skip attributes already provided by e.g. Draco extension.
            if (threeAttributeName in geometry.attributes) continue;
            pending.push(assignAttributeAccessor(attributes[gltfAttributeName], threeAttributeName));
        }
        if (primitiveDef.indices !== undefined && !geometry.index) {
            var accessor = parser.getDependency('accessor', primitiveDef.indices).then(function (accessor) {
                geometry.setIndex(accessor);
            });
            pending.push(accessor);
        }
        assignExtrasToUserData(geometry, primitiveDef);
        return Promise.all(pending).then(function () {
            return primitiveDef.targets !== undefined
                ? addMorphTargets(geometry, primitiveDef.targets, parser)
                : geometry;
        });
    }
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
     *
     * Creates BufferGeometries from primitives.
     *
     * @param {Array<GLTF.Primitive>} primitives
     * @return {Promise<Array<BufferGeometry>>}
     */
    GLTFParser.prototype.loadGeometries = function (primitives) {
        var parser = this;
        var extensions = this.extensions;
        var cache = this.primitiveCache;
        function createDracoPrimitive(primitive) {
            return extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]
                .decodePrimitive(primitive, parser)
                .then(function (geometry) {
                    return addPrimitiveAttributes(geometry, primitive, parser);
                });
        }
        var pending = [];
        for (var i = 0, il = primitives.length; i < il; i++) {
            var primitive = primitives[i];
            var cacheKey = createPrimitiveKey(primitive);
            // See if we've already created this geometry
            var cached = cache[cacheKey];
            if (cached) {
                // Use the cached geometry if it exists
                pending.push(cached.promise);
            } else {
                var geometryPromise;
                if (primitive.extensions && primitive.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]) {
                    // Use DRACO geometry if available
                    geometryPromise = createDracoPrimitive(primitive);
                } else {
                    // Otherwise create a new geometry
                    geometryPromise = addPrimitiveAttributes(new BufferGeometry(), primitive, parser);
                }
                // Cache this geometry
                cache[cacheKey] = { primitive: primitive, promise: geometryPromise };
                pending.push(geometryPromise);
            }
        }
        return Promise.all(pending);
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes
     * @param {number} meshIndex
     * @return {Promise<Group|Mesh|SkinnedMesh>}
     */
    GLTFParser.prototype.loadMesh = function (meshIndex) {
        var parser = this;
        var json = this.json;
        var meshDef = json.meshes[meshIndex];
        var primitives = meshDef.primitives;
        var pending = [];
        for (var i = 0, il = primitives.length; i < il; i++) {
            var material = primitives[i].material === undefined
                ? createDefaultMaterial()
                : this.getDependency('material', primitives[i].material);
            pending.push(material);
        }
        return Promise.all(pending).then(function (originalMaterials) {
            return parser.loadGeometries(primitives).then(function (geometries) {
                var meshes = [];
                for (var i = 0, il = geometries.length; i < il; i++) {
                    var geometry = geometries[i];
                    var primitive = primitives[i];
                    // 1. create Mesh
                    var mesh;
                    var material = originalMaterials[i];
                    if (primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
                        primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
                        primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
                        primitive.mode === undefined) {
                        // .isSkinnedMesh isn't in glTF spec. See .markDefs()
                        mesh = meshDef.isSkinnedMesh === true
                            ? new SkinnedMesh(geometry, material)
                            : new Mesh(geometry, material);
                        if (mesh.isSkinnedMesh === true && !mesh.geometry.attributes.skinWeight.normalized) {
                            // we normalize floating point skin weight array to fix malformed assets (see #15319)
                            // it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
                            mesh.normalizeSkinWeights();
                        }
                        if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP) {
                            mesh.drawMode = TriangleStripDrawMode;
                        } else if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN) {
                            mesh.drawMode = TriangleFanDrawMode;
                        }
                    } else if (primitive.mode === WEBGL_CONSTANTS.LINES) {
                        mesh = new LineSegments(geometry, material);
                    } else if (primitive.mode === WEBGL_CONSTANTS.LINE_STRIP) {
                        mesh = new Line(geometry, material);
                    } else if (primitive.mode === WEBGL_CONSTANTS.LINE_LOOP) {
                        mesh = new LineLoop(geometry, material);
                    } else if (primitive.mode === WEBGL_CONSTANTS.POINTS) {
                        mesh = new Points(geometry, material);
                    } else {
                        throw new Error('THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode);
                    }
                    if (Object.keys(mesh.geometry.morphAttributes).length > 0) {
                        updateMorphTargets(mesh, meshDef);
                    }
                    mesh.name = meshDef.name || ('mesh_' + meshIndex);
                    if (geometries.length > 1) mesh.name += '_' + i;
                    assignExtrasToUserData(mesh, meshDef);
                    parser.assignFinalMaterial(mesh);
                    meshes.push(mesh);
                }
                if (meshes.length === 1) {
                    return meshes[0];
                }
                var group = new Group();
                for (var i = 0, il = meshes.length; i < il; i++) {
                    group.add(meshes[i]);
                }
                return group;
            });
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras
     * @param {number} cameraIndex
     * @return {Promise<THREE.Camera>}
     */
    GLTFParser.prototype.loadCamera = function (cameraIndex) {
        var camera;
        var cameraDef = this.json.cameras[cameraIndex];
        var params = cameraDef[cameraDef.type];
        if (!params) {
            console.warn('THREE.GLTFLoader: Missing camera parameters.');
            return;
        }
        if (cameraDef.type === 'perspective') {
            camera = new PerspectiveCamera(_Math.radToDeg(params.yfov), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6);
        } else if (cameraDef.type === 'orthographic') {
            camera = new OrthographicCamera(params.xmag / - 2, params.xmag / 2, params.ymag / 2, params.ymag / - 2, params.znear, params.zfar);
        }
        if (cameraDef.name !== undefined) camera.name = cameraDef.name;
        assignExtrasToUserData(camera, cameraDef);
        return Promise.resolve(camera);
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins
     * @param {number} skinIndex
     * @return {Promise<Object>}
     */
    GLTFParser.prototype.loadSkin = function (skinIndex) {
        var skinDef = this.json.skins[skinIndex];
        var skinEntry = { joints: skinDef.joints };
        if (skinDef.inverseBindMatrices === undefined) {
            return Promise.resolve(skinEntry);
        }
        return this.getDependency('accessor', skinDef.inverseBindMatrices).then(function (accessor) {
            skinEntry.inverseBindMatrices = accessor;
            return skinEntry;
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations
     * @param {number} animationIndex
     * @return {Promise<AnimationClip>}
     */
    GLTFParser.prototype.loadAnimation = function (animationIndex) {
        var json = this.json;
        var animationDef = json.animations[animationIndex];
        var pendingNodes = [];
        var pendingInputAccessors = [];
        var pendingOutputAccessors = [];
        var pendingSamplers = [];
        var pendingTargets = [];
        for (var i = 0, il = animationDef.channels.length; i < il; i++) {
            var channel = animationDef.channels[i];
            var sampler = animationDef.samplers[channel.sampler];
            var target = channel.target;
            var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated.
            var input = animationDef.parameters !== undefined ? animationDef.parameters[sampler.input] : sampler.input;
            var output = animationDef.parameters !== undefined ? animationDef.parameters[sampler.output] : sampler.output;
            pendingNodes.push(this.getDependency('node', name));
            pendingInputAccessors.push(this.getDependency('accessor', input));
            pendingOutputAccessors.push(this.getDependency('accessor', output));
            pendingSamplers.push(sampler);
            pendingTargets.push(target);
        }
        return Promise.all([
            Promise.all(pendingNodes),
            Promise.all(pendingInputAccessors),
            Promise.all(pendingOutputAccessors),
            Promise.all(pendingSamplers),
            Promise.all(pendingTargets)
        ]).then(function (dependencies) {
            var nodes = dependencies[0];
            var inputAccessors = dependencies[1];
            var outputAccessors = dependencies[2];
            var samplers = dependencies[3];
            var targets = dependencies[4];
            var tracks = [];
            for (var i = 0, il = nodes.length; i < il; i++) {
                var node = nodes[i];
                var inputAccessor = inputAccessors[i];
                var outputAccessor = outputAccessors[i];
                var sampler = samplers[i];
                var target = targets[i];
                if (node === undefined) continue;
                node.updateMatrix();
                node.matrixAutoUpdate = true;
                var TypedKeyframeTrack;
                switch (PATH_PROPERTIES[target.path]) {
                    case PATH_PROPERTIES.weights:
                        TypedKeyframeTrack = NumberKeyframeTrack;
                        break;
                    case PATH_PROPERTIES.rotation:
                        TypedKeyframeTrack = QuaternionKeyframeTrack;
                        break;
                    case PATH_PROPERTIES.position:
                    case PATH_PROPERTIES.scale:
                    default:
                        TypedKeyframeTrack = VectorKeyframeTrack;
                        break;
                }
                var targetName = node.name ? node.name : node.uuid;
                var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : InterpolateLinear;
                var targetNames = [];
                if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) {
                    // Node may be a Group (glTF mesh with several primitives) or a Mesh.
                    node.traverse(function (object) {
                        if (object.isMesh === true && object.morphTargetInfluences) {
                            targetNames.push(object.name ? object.name : object.uuid);
                        }
                    });
                } else {
                    targetNames.push(targetName);
                }
                var outputArray = outputAccessor.array;
                if (outputAccessor.normalized) {
                    var scale;
                    if (outputArray.constructor === Int8Array) {
                        scale = 1 / 127;
                    } else if (outputArray.constructor === Uint8Array) {
                        scale = 1 / 255;
                    } else if (outputArray.constructor == Int16Array) {
                        scale = 1 / 32767;
                    } else if (outputArray.constructor === Uint16Array) {
                        scale = 1 / 65535;
                    } else {
                        throw new Error('THREE.GLTFLoader: Unsupported output accessor component type.');
                    }
                    var scaled = new Float32Array(outputArray.length);
                    for (var j = 0, jl = outputArray.length; j < jl; j++) {
                        scaled[j] = outputArray[j] * scale;
                    }
                    outputArray = scaled;
                }
                for (var j = 0, jl = targetNames.length; j < jl; j++) {
                    var track = new TypedKeyframeTrack(
                        targetNames[j] + '.' + PATH_PROPERTIES[target.path],
                        inputAccessor.array,
                        outputArray,
                        interpolation
                    );
                    // Override interpolation with custom factory method.
                    if (sampler.interpolation === 'CUBICSPLINE') {
                        track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline(result) {
                            // A CUBICSPLINE keyframe in glTF has three output values for each input value,
                            // representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
                            // must be divided by three to get the interpolant's sampleSize argument.
                            return new GLTFCubicSplineInterpolant(this.times, this.values, this.getValueSize() / 3, result);
                        };
                        // Mark as CUBICSPLINE. `track.getInterpolation()` doesn't support custom interpolants.
                        track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;
                    }
                    tracks.push(track);
                }
            }
            var name = animationDef.name !== undefined ? animationDef.name : 'animation_' + animationIndex;
            return new AnimationClip(name, undefined, tracks);
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
     * @param {number} nodeIndex
     * @return {Promise<Object3D>}
     */
    GLTFParser.prototype.loadNode = function (nodeIndex) {
        var json = this.json;
        var extensions = this.extensions;
        var parser = this;
        var meshReferences = json.meshReferences;
        var meshUses = json.meshUses;
        var nodeDef = json.nodes[nodeIndex];
        return (function () {
            var pending = [];
            if (nodeDef.mesh !== undefined) {
                pending.push(parser.getDependency('mesh', nodeDef.mesh).then(function (mesh) {
                    var node;
                    if (meshReferences[nodeDef.mesh] > 1) {
                        var instanceNum = meshUses[nodeDef.mesh]++;
                        node = mesh.clone();
                        node.name += '_instance_' + instanceNum;
                        // onBeforeRender copy for Specular-Glossiness
                        node.onBeforeRender = mesh.onBeforeRender;
                        for (var i = 0, il = node.children.length; i < il; i++) {
                            node.children[i].name += '_instance_' + instanceNum;
                            node.children[i].onBeforeRender = mesh.children[i].onBeforeRender;
                        }
                    } else {
                        node = mesh;
                    }
                    // if weights are provided on the node, override weights on the mesh.
                    if (nodeDef.weights !== undefined) {
                        node.traverse(function (o) {
                            if (!o.isMesh) return;
                            for (var i = 0, il = nodeDef.weights.length; i < il; i++) {
                                o.morphTargetInfluences[i] = nodeDef.weights[i];
                            }
                        });
                    }
                    return node;
                }));
            }
            if (nodeDef.camera !== undefined) {
                pending.push(parser.getDependency('camera', nodeDef.camera));
            }
            if (nodeDef.extensions
                && nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL]
                && nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light !== undefined) {
                pending.push(parser.getDependency('light', nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light));
            }
            return Promise.all(pending);
        }()).then(function (objects) {
            var node;
            // .isBone isn't in glTF spec. See .markDefs
            if (nodeDef.isBone === true) {
                node = new Bone();
            } else if (objects.length > 1) {
                node = new Group();
            } else if (objects.length === 1) {
                node = objects[0];
            } else {
                node = new Object3D();
            }
            if (node !== objects[0]) {
                for (var i = 0, il = objects.length; i < il; i++) {
                    node.add(objects[i]);
                }
            }
            if (nodeDef.name !== undefined) {
                node.userData.name = nodeDef.name;
                node.name = PropertyBinding.sanitizeNodeName(nodeDef.name);
            }
            assignExtrasToUserData(node, nodeDef);
            if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef);
            if (nodeDef.matrix !== undefined) {
                var matrix = new Matrix4();
                matrix.fromArray(nodeDef.matrix);
                node.applyMatrix(matrix);
            } else {
                if (nodeDef.translation !== undefined) {
                    node.position.fromArray(nodeDef.translation);
                }
                if (nodeDef.rotation !== undefined) {
                    node.quaternion.fromArray(nodeDef.rotation);
                }
                if (nodeDef.scale !== undefined) {
                    node.scale.fromArray(nodeDef.scale);
                }
            }
            return node;
        });
    };
    /**
     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
     * @param {number} sceneIndex
     * @return {Promise<Scene>}
     */
    GLTFParser.prototype.loadScene = function () {
        // scene node hierachy builder
        function buildNodeHierachy(nodeId, parentObject, json, parser) {
            var nodeDef = json.nodes[nodeId];
            return parser.getDependency('node', nodeId).then(function (node) {
                if (nodeDef.skin === undefined) return node;
                // build skeleton here as well
                var skinEntry;
                return parser.getDependency('skin', nodeDef.skin).then(function (skin) {
                    skinEntry = skin;
                    var pendingJoints = [];
                    for (var i = 0, il = skinEntry.joints.length; i < il; i++) {
                        pendingJoints.push(parser.getDependency('node', skinEntry.joints[i]));
                    }
                    return Promise.all(pendingJoints);
                }).then(function (jointNodes) {
                    node.traverse(function (mesh) {
                        if (!mesh.isMesh) return;
                        var bones = [];
                        var boneInverses = [];
                        for (var j = 0, jl = jointNodes.length; j < jl; j++) {
                            var jointNode = jointNodes[j];
                            if (jointNode) {
                                bones.push(jointNode);
                                var mat = new Matrix4();
                                if (skinEntry.inverseBindMatrices !== undefined) {
                                    mat.fromArray(skinEntry.inverseBindMatrices.array, j * 16);
                                }
                                boneInverses.push(mat);
                            } else {
                                console.warn('THREE.GLTFLoader: Joint "%s" could not be found.', skinEntry.joints[j]);
                            }
                        }
                        mesh.bind(new Skeleton(bones, boneInverses), mesh.matrixWorld);
                    });
                    return node;
                });
            }).then(function (node) {
                // build node hierachy
                parentObject.add(node);
                var pending = [];
                if (nodeDef.children) {
                    var children = nodeDef.children;
                    for (var i = 0, il = children.length; i < il; i++) {
                        var child = children[i];
                        pending.push(buildNodeHierachy(child, node, json, parser));
                    }
                }
                return Promise.all(pending);
            });
        }
        return function loadScene(sceneIndex) {
            var json = this.json;
            var extensions = this.extensions;
            var sceneDef = this.json.scenes[sceneIndex];
            var parser = this;
            var scene = new Scene();
            if (sceneDef.name !== undefined) scene.name = sceneDef.name;
            assignExtrasToUserData(scene, sceneDef);
            if (sceneDef.extensions) addUnknownExtensionsToUserData(extensions, scene, sceneDef);
            var nodeIds = sceneDef.nodes || [];
            var pending = [];
            for (var i = 0, il = nodeIds.length; i < il; i++) {
                pending.push(buildNodeHierachy(nodeIds[i], scene, json, parser));
            }
            return Promise.all(pending).then(function () {
                return scene;
            });
        };
    }();
    return GLTFLoader;
}
miniprogram-9/jsm/loaders/MTLLoader.js
New file
@@ -0,0 +1,547 @@
/**
 * Loads a Wavefront .mtl file specifying materials
 *
 * @author angelxuanchang
 */
export default function (THREE) {
    let {
        Color,
        DefaultLoadingManager,
        FileLoader,
        FrontSide,
        Loader,
        LoaderUtils,
        MeshPhongMaterial,
        RepeatWrapping,
        TextureLoader,
        Vector2
    } = THREE;
    var MTLLoader = function (manager) {
        Loader.call(this, manager);
    };
    MTLLoader.prototype = Object.assign(Object.create(Loader.prototype), {
        constructor: MTLLoader,
        /**
         * Loads and parses a MTL asset from a URL.
         *
         * @param {String} url - URL to the MTL file.
         * @param {Function} [onLoad] - Callback invoked with the loaded object.
         * @param {Function} [onProgress] - Callback for download progress.
         * @param {Function} [onError] - Callback for download errors.
         *
         * @see setPath setResourcePath
         *
         * @note In order for relative texture references to resolve correctly
         * you must call setResourcePath() explicitly prior to load.
         */
        load: function (url, onLoad, onProgress, onError) {
            var scope = this;
            var path = (this.path === '') ? LoaderUtils.extractUrlBase(url) : this.path;
            var loader = new FileLoader(this.manager);
            loader.setPath(this.path);
            loader.load(url, function (text) {
                onLoad(scope.parse(text, path));
            }, onProgress, onError);
        },
        setMaterialOptions: function (value) {
            this.materialOptions = value;
            return this;
        },
        /**
         * Parses a MTL file.
         *
         * @param {String} text - Content of MTL file
         * @return {MTLLoader.MaterialCreator}
         *
         * @see setPath setResourcePath
         *
         * @note In order for relative texture references to resolve correctly
         * you must call setResourcePath() explicitly prior to parse.
         */
        parse: function (text, path) {
            var lines = text.split('\n');
            var info = {};
            var delimiter_pattern = /\s+/;
            var materialsInfo = {};
            for (var i = 0; i < lines.length; i++) {
                var line = lines[i];
                line = line.trim();
                if (line.length === 0 || line.charAt(0) === '#') {
                    // Blank line or comment ignore
                    continue;
                }
                var pos = line.indexOf(' ');
                var key = (pos >= 0) ? line.substring(0, pos) : line;
                key = key.toLowerCase();
                var value = (pos >= 0) ? line.substring(pos + 1) : '';
                value = value.trim();
                if (key === 'newmtl') {
                    // New material
                    info = { name: value };
                    materialsInfo[value] = info;
                } else {
                    if (key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke') {
                        var ss = value.split(delimiter_pattern, 3);
                        info[key] = [parseFloat(ss[0]), parseFloat(ss[1]), parseFloat(ss[2])];
                    } else {
                        info[key] = value;
                    }
                }
            }
            var materialCreator = new MTLLoader.MaterialCreator(this.resourcePath || path, this.materialOptions);
            materialCreator.setCrossOrigin(this.crossOrigin);
            materialCreator.setManager(this.manager);
            materialCreator.setMaterials(materialsInfo);
            return materialCreator;
        }
    });
    /**
     * Create a new THREE-MTLLoader.MaterialCreator
     * @param baseUrl - Url relative to which textures are loaded
     * @param options - Set of options on how to construct the materials
     *                  side: Which side to apply the material
     *                        FrontSide (default), THREE.BackSide, THREE.DoubleSide
     *                  wrap: What type of wrapping to apply for textures
     *                        RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
     *                  normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
     *                                Default: false, assumed to be already normalized
     *                  ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
     *                                  Default: false
     * @constructor
     */
    MTLLoader.MaterialCreator = function (baseUrl, options) {
        this.baseUrl = baseUrl || '';
        this.options = options;
        this.materialsInfo = {};
        this.materials = {};
        this.materialsArray = [];
        this.nameLookup = {};
        this.side = (this.options && this.options.side) ? this.options.side : FrontSide;
        this.wrap = (this.options && this.options.wrap) ? this.options.wrap : RepeatWrapping;
    };
    MTLLoader.MaterialCreator.prototype = {
        constructor: MTLLoader.MaterialCreator,
        crossOrigin: 'anonymous',
        setCrossOrigin: function (value) {
            this.crossOrigin = value;
            return this;
        },
        setManager: function (value) {
            this.manager = value;
        },
        setMaterials: function (materialsInfo) {
            this.materialsInfo = this.convert(materialsInfo);
            this.materials = {};
            this.materialsArray = [];
            this.nameLookup = {};
        },
        convert: function (materialsInfo) {
            if (!this.options) return materialsInfo;
            var converted = {};
            for (var mn in materialsInfo) {
                // Convert materials info into normalized form based on options
                var mat = materialsInfo[mn];
                var covmat = {};
                converted[mn] = covmat;
                for (var prop in mat) {
                    var save = true;
                    var value = mat[prop];
                    var lprop = prop.toLowerCase();
                    switch (lprop) {
                        case 'kd':
                        case 'ka':
                        case 'ks':
                            // Diffuse color (color under white light) using RGB values
                            if (this.options && this.options.normalizeRGB) {
                                value = [value[0] / 255, value[1] / 255, value[2] / 255];
                            }
                            if (this.options && this.options.ignoreZeroRGBs) {
                                if (value[0] === 0 && value[1] === 0 && value[2] === 0) {
                                    // ignore
                                    save = false;
                                }
                            }
                            break;
                        default:
                            break;
                    }
                    if (save) {
                        covmat[lprop] = value;
                    }
                }
            }
            return converted;
        },
        preload: function () {
            for (var mn in this.materialsInfo) {
                this.create(mn);
            }
        },
        getIndex: function (materialName) {
            return this.nameLookup[materialName];
        },
        getAsArray: function () {
            var index = 0;
            for (var mn in this.materialsInfo) {
                this.materialsArray[index] = this.create(mn);
                this.nameLookup[mn] = index;
                index++;
            }
            return this.materialsArray;
        },
        create: function (materialName) {
            if (this.materials[materialName] === undefined) {
                this.createMaterial_(materialName);
            }
            return this.materials[materialName];
        },
        createMaterial_: function (materialName) {
            // Create material
            var scope = this;
            var mat = this.materialsInfo[materialName];
            var params = {
                name: materialName,
                side: this.side
            };
            function resolveURL(baseUrl, url) {
                if (typeof url !== 'string' || url === '')
                    return '';
                // Absolute URL
                if (/^https?:\/\//i.test(url)) return url;
                return baseUrl + url;
            }
            function setMapForType(mapType, value) {
                if (params[mapType]) return; // Keep the first encountered texture
                var texParams = scope.getTextureParams(value, params);
                var map = scope.loadTexture(resolveURL(scope.baseUrl, texParams.url));
                map.repeat.copy(texParams.scale);
                map.offset.copy(texParams.offset);
                map.wrapS = scope.wrap;
                map.wrapT = scope.wrap;
                params[mapType] = map;
            }
            for (var prop in mat) {
                var value = mat[prop];
                var n;
                if (value === '') continue;
                switch (prop.toLowerCase()) {
                    // Ns is material specular exponent
                    case 'kd':
                        // Diffuse color (color under white light) using RGB values
                        params.color = new Color().fromArray(value);
                        break;
                    case 'ks':
                        // Specular color (color when light is reflected from shiny surface) using RGB values
                        params.specular = new Color().fromArray(value);
                        break;
                    case 'ke':
                        // Emissive using RGB values
                        params.emissive = new Color().fromArray(value);
                        break;
                    case 'map_kd':
                        // Diffuse texture map
                        setMapForType("map", value);
                        break;
                    case 'map_ks':
                        // Specular map
                        setMapForType("specularMap", value);
                        break;
                    case 'map_ke':
                        // Emissive map
                        setMapForType("emissiveMap", value);
                        break;
                    case 'norm':
                        setMapForType("normalMap", value);
                        break;
                    case 'map_bump':
                    case 'bump':
                        // Bump texture map
                        setMapForType("bumpMap", value);
                        break;
                    case 'map_d':
                        // Alpha map
                        setMapForType("alphaMap", value);
                        params.transparent = true;
                        break;
                    case 'ns':
                        // The specular exponent (defines the focus of the specular highlight)
                        // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
                        params.shininess = parseFloat(value);
                        break;
                    case 'd':
                        n = parseFloat(value);
                        if (n < 1) {
                            params.opacity = n;
                            params.transparent = true;
                        }
                        break;
                    case 'tr':
                        n = parseFloat(value);
                        if (this.options && this.options.invertTrProperty) n = 1 - n;
                        if (n > 0) {
                            params.opacity = 1 - n;
                            params.transparent = true;
                        }
                        break;
                    default:
                        break;
                }
            }
            this.materials[materialName] = new MeshPhongMaterial(params);
            return this.materials[materialName];
        },
        getTextureParams: function (value, matParams) {
            var texParams = {
                scale: new Vector2(1, 1),
                offset: new Vector2(0, 0)
            };
            var items = value.split(/\s+/);
            var pos;
            pos = items.indexOf('-bm');
            if (pos >= 0) {
                matParams.bumpScale = parseFloat(items[pos + 1]);
                items.splice(pos, 2);
            }
            pos = items.indexOf('-s');
            if (pos >= 0) {
                texParams.scale.set(parseFloat(items[pos + 1]), parseFloat(items[pos + 2]));
                items.splice(pos, 4); // we expect 3 parameters here!
            }
            pos = items.indexOf('-o');
            if (pos >= 0) {
                texParams.offset.set(parseFloat(items[pos + 1]), parseFloat(items[pos + 2]));
                items.splice(pos, 4); // we expect 3 parameters here!
            }
            texParams.url = items.join(' ').trim();
            return texParams;
        },
        loadTexture: function (url, mapping, onLoad, onProgress, onError) {
            var texture;
            var manager = (this.manager !== undefined) ? this.manager : DefaultLoadingManager;
            var loader = manager.getHandler(url);
            if (loader === null) {
                loader = new TextureLoader(manager);
            }
            if (loader.setCrossOrigin) loader.setCrossOrigin(this.crossOrigin);
            texture = loader.load(url, onLoad, onProgress, onError);
            if (mapping !== undefined) texture.mapping = mapping;
            return texture;
        }
    };
    return { MTLLoader };
}
miniprogram-9/jsm/loaders/OBJLoader.js
New file
@@ -0,0 +1,816 @@
/**
 * @author mrdoob / http://mrdoob.com/
 */
export default function (THREE) {
    let {
        BufferGeometry,
        FileLoader,
        Float32BufferAttribute,
        Group,
        LineBasicMaterial,
        LineSegments,
        Loader,
        LoaderUtils,
        Material,
        Mesh,
        MeshPhongMaterial,
        NoColors,
        Points,
        PointsMaterial,
        VertexColors
    } = THREE;
    // o object_name | g group_name
    var object_pattern = /^[og]\s*(.+)?/;
    // mtllib file_reference
    var material_library_pattern = /^mtllib /;
    // usemtl material_name
    var material_use_pattern = /^usemtl /;
    function ParserState() {
        var state = {
            objects: [],
            object: {},
            vertices: [],
            normals: [],
            colors: [],
            uvs: [],
            materialLibraries: [],
            startObject: function (name, fromDeclaration) {
                // If the current object (initial from reset) is not from a g/o declaration in the parsed
                // file. We need to use it for the first parsed g/o to keep things in sync.
                if (this.object && this.object.fromDeclaration === false) {
                    this.object.name = name;
                    this.object.fromDeclaration = (fromDeclaration !== false);
                    return;
                }
                var previousMaterial = (this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined);
                if (this.object && typeof this.object._finalize === 'function') {
                    this.object._finalize(true);
                }
                this.object = {
                    name: name || '',
                    fromDeclaration: (fromDeclaration !== false),
                    geometry: {
                        vertices: [],
                        normals: [],
                        colors: [],
                        uvs: []
                    },
                    materials: [],
                    smooth: true,
                    startMaterial: function (name, libraries) {
                        var previous = this._finalize(false);
                        // New usemtl declaration overwrites an inherited material, except if faces were declared
                        // after the material, then it must be preserved for proper MultiMaterial continuation.
                        if (previous && (previous.inherited || previous.groupCount <= 0)) {
                            this.materials.splice(previous.index, 1);
                        }
                        var material = {
                            index: this.materials.length,
                            name: name || '',
                            mtllib: (Array.isArray(libraries) && libraries.length > 0 ? libraries[libraries.length - 1] : ''),
                            smooth: (previous !== undefined ? previous.smooth : this.smooth),
                            groupStart: (previous !== undefined ? previous.groupEnd : 0),
                            groupEnd: - 1,
                            groupCount: - 1,
                            inherited: false,
                            clone: function (index) {
                                var cloned = {
                                    index: (typeof index === 'number' ? index : this.index),
                                    name: this.name,
                                    mtllib: this.mtllib,
                                    smooth: this.smooth,
                                    groupStart: 0,
                                    groupEnd: - 1,
                                    groupCount: - 1,
                                    inherited: false
                                };
                                cloned.clone = this.clone.bind(cloned);
                                return cloned;
                            }
                        };
                        this.materials.push(material);
                        return material;
                    },
                    currentMaterial: function () {
                        if (this.materials.length > 0) {
                            return this.materials[this.materials.length - 1];
                        }
                        return undefined;
                    },
                    _finalize: function (end) {
                        var lastMultiMaterial = this.currentMaterial();
                        if (lastMultiMaterial && lastMultiMaterial.groupEnd === - 1) {
                            lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
                            lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
                            lastMultiMaterial.inherited = false;
                        }
                        // Ignore objects tail materials if no face declarations followed them before a new o/g started.
                        if (end && this.materials.length > 1) {
                            for (var mi = this.materials.length - 1; mi >= 0; mi--) {
                                if (this.materials[mi].groupCount <= 0) {
                                    this.materials.splice(mi, 1);
                                }
                            }
                        }
                        // Guarantee at least one empty material, this makes the creation later more straight forward.
                        if (end && this.materials.length === 0) {
                            this.materials.push({
                                name: '',
                                smooth: this.smooth
                            });
                        }
                        return lastMultiMaterial;
                    }
                };
                // Inherit previous objects material.
                // Spec tells us that a declared material must be set to all objects until a new material is declared.
                // If a usemtl declaration is encountered while this new object is being parsed, it will
                // overwrite the inherited material. Exception being that there was already face declarations
                // to the inherited material, then it will be preserved for proper MultiMaterial continuation.
                if (previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function') {
                    var declared = previousMaterial.clone(0);
                    declared.inherited = true;
                    this.object.materials.push(declared);
                }
                this.objects.push(this.object);
            },
            finalize: function () {
                if (this.object && typeof this.object._finalize === 'function') {
                    this.object._finalize(true);
                }
            },
            parseVertexIndex: function (value, len) {
                var index = parseInt(value, 10);
                return (index >= 0 ? index - 1 : index + len / 3) * 3;
            },
            parseNormalIndex: function (value, len) {
                var index = parseInt(value, 10);
                return (index >= 0 ? index - 1 : index + len / 3) * 3;
            },
            parseUVIndex: function (value, len) {
                var index = parseInt(value, 10);
                return (index >= 0 ? index - 1 : index + len / 2) * 2;
            },
            addVertex: function (a, b, c) {
                var src = this.vertices;
                var dst = this.object.geometry.vertices;
                dst.push(src[a + 0], src[a + 1], src[a + 2]);
                dst.push(src[b + 0], src[b + 1], src[b + 2]);
                dst.push(src[c + 0], src[c + 1], src[c + 2]);
            },
            addVertexPoint: function (a) {
                var src = this.vertices;
                var dst = this.object.geometry.vertices;
                dst.push(src[a + 0], src[a + 1], src[a + 2]);
            },
            addVertexLine: function (a) {
                var src = this.vertices;
                var dst = this.object.geometry.vertices;
                dst.push(src[a + 0], src[a + 1], src[a + 2]);
            },
            addNormal: function (a, b, c) {
                var src = this.normals;
                var dst = this.object.geometry.normals;
                dst.push(src[a + 0], src[a + 1], src[a + 2]);
                dst.push(src[b + 0], src[b + 1], src[b + 2]);
                dst.push(src[c + 0], src[c + 1], src[c + 2]);
            },
            addColor: function (a, b, c) {
                var src = this.colors;
                var dst = this.object.geometry.colors;
                dst.push(src[a + 0], src[a + 1], src[a + 2]);
                dst.push(src[b + 0], src[b + 1], src[b + 2]);
                dst.push(src[c + 0], src[c + 1], src[c + 2]);
            },
            addUV: function (a, b, c) {
                var src = this.uvs;
                var dst = this.object.geometry.uvs;
                dst.push(src[a + 0], src[a + 1]);
                dst.push(src[b + 0], src[b + 1]);
                dst.push(src[c + 0], src[c + 1]);
            },
            addUVLine: function (a) {
                var src = this.uvs;
                var dst = this.object.geometry.uvs;
                dst.push(src[a + 0], src[a + 1]);
            },
            addFace: function (a, b, c, ua, ub, uc, na, nb, nc) {
                var vLen = this.vertices.length;
                var ia = this.parseVertexIndex(a, vLen);
                var ib = this.parseVertexIndex(b, vLen);
                var ic = this.parseVertexIndex(c, vLen);
                this.addVertex(ia, ib, ic);
                if (this.colors.length > 0) {
                    this.addColor(ia, ib, ic);
                }
                if (ua !== undefined && ua !== '') {
                    var uvLen = this.uvs.length;
                    ia = this.parseUVIndex(ua, uvLen);
                    ib = this.parseUVIndex(ub, uvLen);
                    ic = this.parseUVIndex(uc, uvLen);
                    this.addUV(ia, ib, ic);
                }
                if (na !== undefined && na !== '') {
                    // Normals are many times the same. If so, skip function call and parseInt.
                    var nLen = this.normals.length;
                    ia = this.parseNormalIndex(na, nLen);
                    ib = na === nb ? ia : this.parseNormalIndex(nb, nLen);
                    ic = na === nc ? ia : this.parseNormalIndex(nc, nLen);
                    this.addNormal(ia, ib, ic);
                }
            },
            addPointGeometry: function (vertices) {
                this.object.geometry.type = 'Points';
                var vLen = this.vertices.length;
                for (var vi = 0, l = vertices.length; vi < l; vi++) {
                    this.addVertexPoint(this.parseVertexIndex(vertices[vi], vLen));
                }
            },
            addLineGeometry: function (vertices, uvs) {
                this.object.geometry.type = 'Line';
                var vLen = this.vertices.length;
                var uvLen = this.uvs.length;
                for (var vi = 0, l = vertices.length; vi < l; vi++) {
                    this.addVertexLine(this.parseVertexIndex(vertices[vi], vLen));
                }
                for (var uvi = 0, l = uvs.length; uvi < l; uvi++) {
                    this.addUVLine(this.parseUVIndex(uvs[uvi], uvLen));
                }
            }
        };
        state.startObject('', false);
        return state;
    }
    //
    function OBJLoader(manager) {
        Loader.call(this, manager);
        this.materials = null;
    }
    OBJLoader.prototype = Object.assign(Object.create(Loader.prototype), {
        constructor: OBJLoader,
        setPath:function(value){
            this.path = value;
        },
        load: function (url, onLoad, onProgress, onError) {
            var scope = this;
            var path = (this.path === '') ? LoaderUtils.extractUrlBase(url) : this.path;
            var loader = new FileLoader(scope.manager);
            loader.setPath(this.path);
            loader.load(url, function (text) {
                onLoad(scope.parse(text,path));
            }, onProgress, onError);
        },
        setMaterials: function (materials) {
            this.materials = materials;
            return this;
        },
        parse: function (text) {
            console.time('OBJLoader');
            var state = new ParserState();
            if (text.indexOf('\r\n') !== - 1) {
                // This is faster than String.split with regex that splits on both
                text = text.replace(/\r\n/g, '\n');
            }
            if (text.indexOf('\\\n') !== - 1) {
                // join lines separated by a line continuation character (\)
                text = text.replace(/\\\n/g, '');
            }
            var lines = text.split('\n');
            var line = '', lineFirstChar = '';
            var lineLength = 0;
            var result = [];
            // Faster to just trim left side of the line. Use if available.
            var trimLeft = (typeof ''.trimLeft === 'function');
            for (var i = 0, l = lines.length; i < l; i++) {
                line = lines[i];
                line = trimLeft ? line.trimLeft() : line.trim();
                lineLength = line.length;
                if (lineLength === 0) continue;
                lineFirstChar = line.charAt(0);
                // @todo invoke passed in handler if any
                if (lineFirstChar === '#') continue;
                if (lineFirstChar === 'v') {
                    // \f -> 匹配一个换页
                    // \n -> 匹配一个换行符
                    // \r -> 匹配一个回车符
                    // \t -> 匹配一个制表符
                    // \v -> 匹配一个垂直制表符
                    // \s+ 表示匹配任意多个上面的字符
                    var data = line.split(/\s+/);
                    // var data = line.split(" ");
                    switch (data[0]) {
                        case 'v':
                            state.vertices.push(
                                parseFloat(data[1]),
                                parseFloat(data[2]),
                                parseFloat(data[3])
                            );
                            if (data.length >= 7) {
                                state.colors.push(
                                    parseFloat(data[4]),
                                    parseFloat(data[5]),
                                    parseFloat(data[6])
                                );
                            }
                            break;
                        case 'vn':
                            state.normals.push(
                                parseFloat(data[1]),
                                parseFloat(data[2]),
                                parseFloat(data[3])
                            );
                            break;
                        case 'vt':
                            state.uvs.push(
                                parseFloat(data[1]),
                                parseFloat(data[2])
                            );
                            break;
                    }
                } else if (lineFirstChar === 'f') {
                    var lineData = line.substr(1).trim();
                    var vertexData = lineData.split(/\s+/);
                    // var vertexData = lineData.split(" ");
                    var faceVertices = [];
                    // Parse the face vertex data into an easy to work with format
                    for (var j = 0, jl = vertexData.length; j < jl; j++) {
                        var vertex = vertexData[j];
                        if (vertex.length > 0) {
                            var vertexParts = vertex.split('/');
                            faceVertices.push(vertexParts);
                        }
                    }
                    // Draw an edge between the first vertex and all subsequent vertices to form an n-gon
                    var v1 = faceVertices[0];
                    for (var j = 1, jl = faceVertices.length - 1; j < jl; j++) {
                        var v2 = faceVertices[j];
                        var v3 = faceVertices[j + 1];
                        state.addFace(
                            v1[0], v2[0], v3[0],
                            v1[1], v2[1], v3[1],
                            v1[2], v2[2], v3[2]
                        );
                    }
                } else if (lineFirstChar === 'l') {
                    var lineParts = line.substring(1).trim().split(" ");
                    var lineVertices = [], lineUVs = [];
                    if (line.indexOf("/") === - 1) {
                        lineVertices = lineParts;
                    } else {
                        for (var li = 0, llen = lineParts.length; li < llen; li++) {
                            var parts = lineParts[li].split("/");
                            if (parts[0] !== "") lineVertices.push(parts[0]);
                            if (parts[1] !== "") lineUVs.push(parts[1]);
                        }
                    }
                    state.addLineGeometry(lineVertices, lineUVs);
                } else if (lineFirstChar === 'p') {
                    var lineData = line.substr(1).trim();
                    var pointData = lineData.split(" ");
                    state.addPointGeometry(pointData);
                } else if ((result = object_pattern.exec(line)) !== null) {
                    // o object_name
                    // or
                    // g group_name
                    // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
                    // var name = result[ 0 ].substr( 1 ).trim();
                    var name = (" " + result[0].substr(1).trim()).substr(1);
                    state.startObject(name);
                } else if (material_use_pattern.test(line)) {
                    // material
                    state.object.startMaterial(line.substring(7).trim(), state.materialLibraries);
                } else if (material_library_pattern.test(line)) {
                    // mtl file
                    state.materialLibraries.push(line.substring(7).trim());
                } else if (lineFirstChar === 's') {
                    result = line.split(' ');
                    // smooth shading
                    // @todo Handle files that have varying smooth values for a set of faces inside one geometry,
                    // but does not define a usemtl for each face set.
                    // This should be detected and a dummy material created (later MultiMaterial and geometry groups).
                    // This requires some care to not create extra material on each smooth value for "normal" obj files.
                    // where explicit usemtl defines geometry groups.
                    // Example asset: examples/models/obj/cerberus/Cerberus.obj
                    /*
                     * http://paulbourke.net/dataformats/obj/
                     * or
                     * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
                     *
                     * From chapter "Grouping" Syntax explanation "s group_number":
                     * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
                     * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
                     * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
                     * than 0."
                     */
                    if (result.length > 1) {
                        var value = result[1].trim().toLowerCase();
                        state.object.smooth = (value !== '0' && value !== 'off');
                    } else {
                        // ZBrush can produce "s" lines #11707
                        state.object.smooth = true;
                    }
                    var material = state.object.currentMaterial();
                    if (material) material.smooth = state.object.smooth;
                } else {
                    // Handle null terminated files without exception
                    if (line === '\0') continue;
                    throw new Error('THREE.OBJLoader: Unexpected line: "' + line + '"');
                }
            }
            state.finalize();
            var container = new Group();
            container.materialLibraries = [].concat(state.materialLibraries);
            for (var i = 0, l = state.objects.length; i < l; i++) {
                var object = state.objects[i];
                var geometry = object.geometry;
                var materials = object.materials;
                var isLine = (geometry.type === 'Line');
                var isPoints = (geometry.type === 'Points');
                var hasVertexColors = false;
                // Skip o/g line declarations that did not follow with any faces
                if (geometry.vertices.length === 0) continue;
                var buffergeometry = new BufferGeometry();
                buffergeometry.setAttribute('position', new Float32BufferAttribute(geometry.vertices, 3));
                if (geometry.normals.length > 0) {
                    buffergeometry.setAttribute('normal', new Float32BufferAttribute(geometry.normals, 3));
                } else {
                    buffergeometry.computeVertexNormals();
                }
                if (geometry.colors.length > 0) {
                    hasVertexColors = true;
                    buffergeometry.setAttribute('color', new Float32BufferAttribute(geometry.colors, 3));
                }
                if (geometry.uvs.length > 0) {
                    buffergeometry.setAttribute('uv', new Float32BufferAttribute(geometry.uvs, 2));
                }
                // Create materials
                var createdMaterials = [];
                for (var mi = 0, miLen = materials.length; mi < miLen; mi++) {
                    var sourceMaterial = materials[mi];
                    var material = undefined;
                    if (this.materials !== null) {
                        material = this.materials.create(sourceMaterial.name);
                        // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
                        if (isLine && material && !(material instanceof LineBasicMaterial)) {
                            var materialLine = new LineBasicMaterial();
                            Material.prototype.copy.call(materialLine, material);
                            materialLine.color.copy(material.color);
                            material = materialLine;
                        } else if (isPoints && material && !(material instanceof PointsMaterial)) {
                            var materialPoints = new PointsMaterial({ size: 10, sizeAttenuation: false });
                            Material.prototype.copy.call(materialPoints, material);
                            materialPoints.color.copy(material.color);
                            materialPoints.map = material.map;
                            material = materialPoints;
                        }
                    }
                    if (!material) {
                        if (isLine) {
                            material = new LineBasicMaterial();
                        } else if (isPoints) {
                            material = new PointsMaterial({ size: 1, sizeAttenuation: false });
                        } else {
                            material = new MeshPhongMaterial();
                        }
                        material.name = sourceMaterial.name;
                    }
                    material.flatShading = sourceMaterial.smooth ? false : true;
                    material.vertexColors = hasVertexColors ? VertexColors : NoColors;
                    createdMaterials.push(material);
                }
                // Create mesh
                var mesh;
                if (createdMaterials.length > 1) {
                    for (var mi = 0, miLen = materials.length; mi < miLen; mi++) {
                        var sourceMaterial = materials[mi];
                        buffergeometry.addGroup(sourceMaterial.groupStart, sourceMaterial.groupCount, mi);
                    }
                    if (isLine) {
                        mesh = new LineSegments(buffergeometry, createdMaterials);
                    } else if (isPoints) {
                        mesh = new Points(buffergeometry, createdMaterials);
                    } else {
                        mesh = new Mesh(buffergeometry, createdMaterials);
                    }
                } else {
                    if (isLine) {
                        mesh = new LineSegments(buffergeometry, createdMaterials[0]);
                    } else if (isPoints) {
                        mesh = new Points(buffergeometry, createdMaterials[0]);
                    } else {
                        mesh = new Mesh(buffergeometry, createdMaterials[0]);
                    }
                }
                mesh.name = object.name;
                container.add(mesh);
            }
            console.timeEnd('OBJLoader');
            return container;
        }
    });
    return OBJLoader;
}
miniprogram-9/jsm/loaders/RGBELoader.js
New file
@@ -0,0 +1,542 @@
/**
 * @author Nikos M. / https://github.com/foo123/
 */
export default function (THREE) {
let {
    DataTextureLoader,
    FloatType,
    HalfFloatType,
    LinearEncoding,
    LinearFilter,
    NearestFilter,
    RGBEEncoding,
    RGBEFormat,
    RGBFormat,
    UnsignedByteType
} = THREE;
// https://github.com/mrdoob/three.js/issues/5552
// http://en.wikipedia.org/wiki/RGBE_image_format
var RGBELoader = function ( manager ) {
    DataTextureLoader.call( this, manager );
    this.type = UnsignedByteType;
};
RGBELoader.prototype = Object.assign( Object.create( DataTextureLoader.prototype ), {
    constructor: RGBELoader,
    // adapted from http://www.graphics.cornell.edu/~bjw/rgbe.html
    parse: function ( buffer ) {
        var
            /* return codes for rgbe routines */
            //RGBE_RETURN_SUCCESS = 0,
            RGBE_RETURN_FAILURE = - 1,
            /* default error routine.  change this to change error handling */
            rgbe_read_error = 1,
            rgbe_write_error = 2,
            rgbe_format_error = 3,
            rgbe_memory_error = 4,
            rgbe_error = function ( rgbe_error_code, msg ) {
                switch ( rgbe_error_code ) {
                    case rgbe_read_error: console.error( "RGBELoader Read Error: " + ( msg || '' ) );
                        break;
                    case rgbe_write_error: console.error( "RGBELoader Write Error: " + ( msg || '' ) );
                        break;
                    case rgbe_format_error: console.error( "RGBELoader Bad File Format: " + ( msg || '' ) );
                        break;
                    default:
                    case rgbe_memory_error: console.error( "RGBELoader: Error: " + ( msg || '' ) );
                }
                return RGBE_RETURN_FAILURE;
            },
            /* offsets to red, green, and blue components in a data (float) pixel */
            //RGBE_DATA_RED = 0,
            //RGBE_DATA_GREEN = 1,
            //RGBE_DATA_BLUE = 2,
            /* number of floats per pixel, use 4 since stored in rgba image format */
            //RGBE_DATA_SIZE = 4,
            /* flags indicating which fields in an rgbe_header_info are valid */
            RGBE_VALID_PROGRAMTYPE = 1,
            RGBE_VALID_FORMAT = 2,
            RGBE_VALID_DIMENSIONS = 4,
            NEWLINE = "\n",
            fgets = function ( buffer, lineLimit, consume ) {
                lineLimit = ! lineLimit ? 1024 : lineLimit;
                var p = buffer.pos,
                    i = - 1, len = 0, s = '', chunkSize = 128,
                    chunk = String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) )
                ;
                while ( ( 0 > ( i = chunk.indexOf( NEWLINE ) ) ) && ( len < lineLimit ) && ( p < buffer.byteLength ) ) {
                    s += chunk; len += chunk.length;
                    p += chunkSize;
                    chunk += String.fromCharCode.apply( null, new Uint16Array( buffer.subarray( p, p + chunkSize ) ) );
                }
                if ( - 1 < i ) {
                    /*for (i=l-1; i>=0; i--) {
                        byteCode = m.charCodeAt(i);
                        if (byteCode > 0x7f && byteCode <= 0x7ff) byteLen++;
                        else if (byteCode > 0x7ff && byteCode <= 0xffff) byteLen += 2;
                        if (byteCode >= 0xDC00 && byteCode <= 0xDFFF) i--; //trail surrogate
                    }*/
                    if ( false !== consume ) buffer.pos += len + i + 1;
                    return s + chunk.slice( 0, i );
                }
                return false;
            },
            /* minimal header reading.  modify if you want to parse more information */
            RGBE_ReadHeader = function ( buffer ) {
                var line, match,
                    // regexes to parse header info fields
                    magic_token_re = /^#\?(\S+)$/,
                    gamma_re = /^\s*GAMMA\s*=\s*(\d+(\.\d+)?)\s*$/,
                    exposure_re = /^\s*EXPOSURE\s*=\s*(\d+(\.\d+)?)\s*$/,
                    format_re = /^\s*FORMAT=(\S+)\s*$/,
                    dimensions_re = /^\s*\-Y\s+(\d+)\s+\+X\s+(\d+)\s*$/,
                    // RGBE format header struct
                    header = {
                        valid: 0, /* indicate which fields are valid */
                        string: '', /* the actual header string */
                        comments: '', /* comments found in header */
                        programtype: 'RGBE', /* listed at beginning of file to identify it after "#?". defaults to "RGBE" */
                        format: '', /* RGBE format, default 32-bit_rle_rgbe */
                        gamma: 1.0, /* image has already been gamma corrected with given gamma. defaults to 1.0 (no correction) */
                        exposure: 1.0, /* a value of 1.0 in an image corresponds to <exposure> watts/steradian/m^2. defaults to 1.0 */
                        width: 0, height: 0 /* image dimensions, width/height */
                    };
                if ( buffer.pos >= buffer.byteLength || ! ( line = fgets( buffer ) ) ) {
                    return rgbe_error( rgbe_read_error, "no header found" );
                }
                /* if you want to require the magic token then uncomment the next line */
                if ( ! ( match = line.match( magic_token_re ) ) ) {
                    return rgbe_error( rgbe_format_error, "bad initial token" );
                }
                header.valid |= RGBE_VALID_PROGRAMTYPE;
                header.programtype = match[ 1 ];
                header.string += line + "\n";
                while ( true ) {
                    line = fgets( buffer );
                    if ( false === line ) break;
                    header.string += line + "\n";
                    if ( '#' === line.charAt( 0 ) ) {
                        header.comments += line + "\n";
                        continue; // comment line
                    }
                    if ( match = line.match( gamma_re ) ) {
                        header.gamma = parseFloat( match[ 1 ], 10 );
                    }
                    if ( match = line.match( exposure_re ) ) {
                        header.exposure = parseFloat( match[ 1 ], 10 );
                    }
                    if ( match = line.match( format_re ) ) {
                        header.valid |= RGBE_VALID_FORMAT;
                        header.format = match[ 1 ];//'32-bit_rle_rgbe';
                    }
                    if ( match = line.match( dimensions_re ) ) {
                        header.valid |= RGBE_VALID_DIMENSIONS;
                        header.height = parseInt( match[ 1 ], 10 );
                        header.width = parseInt( match[ 2 ], 10 );
                    }
                    if ( ( header.valid & RGBE_VALID_FORMAT ) && ( header.valid & RGBE_VALID_DIMENSIONS ) ) break;
                }
                if ( ! ( header.valid & RGBE_VALID_FORMAT ) ) {
                    return rgbe_error( rgbe_format_error, "missing format specifier" );
                }
                if ( ! ( header.valid & RGBE_VALID_DIMENSIONS ) ) {
                    return rgbe_error( rgbe_format_error, "missing image size specifier" );
                }
                return header;
            },
            RGBE_ReadPixels_RLE = function ( buffer, w, h ) {
                var data_rgba, offset, pos, count, byteValue,
                    scanline_buffer, ptr, ptr_end, i, l, off, isEncodedRun,
                    scanline_width = w, num_scanlines = h, rgbeStart
                ;
                if (
                    // run length encoding is not allowed so read flat
                    ( ( scanline_width < 8 ) || ( scanline_width > 0x7fff ) ) ||
                    // this file is not run length encoded
                    ( ( 2 !== buffer[ 0 ] ) || ( 2 !== buffer[ 1 ] ) || ( buffer[ 2 ] & 0x80 ) )
                ) {
                    // return the flat buffer
                    return new Uint8Array( buffer );
                }
                if ( scanline_width !== ( ( buffer[ 2 ] << 8 ) | buffer[ 3 ] ) ) {
                    return rgbe_error( rgbe_format_error, "wrong scanline width" );
                }
                data_rgba = new Uint8Array( 4 * w * h );
                if ( ! data_rgba || ! data_rgba.length ) {
                    return rgbe_error( rgbe_memory_error, "unable to allocate buffer space" );
                }
                offset = 0; pos = 0; ptr_end = 4 * scanline_width;
                rgbeStart = new Uint8Array( 4 );
                scanline_buffer = new Uint8Array( ptr_end );
                // read in each successive scanline
                while ( ( num_scanlines > 0 ) && ( pos < buffer.byteLength ) ) {
                    if ( pos + 4 > buffer.byteLength ) {
                        return rgbe_error( rgbe_read_error );
                    }
                    rgbeStart[ 0 ] = buffer[ pos ++ ];
                    rgbeStart[ 1 ] = buffer[ pos ++ ];
                    rgbeStart[ 2 ] = buffer[ pos ++ ];
                    rgbeStart[ 3 ] = buffer[ pos ++ ];
                    if ( ( 2 != rgbeStart[ 0 ] ) || ( 2 != rgbeStart[ 1 ] ) || ( ( ( rgbeStart[ 2 ] << 8 ) | rgbeStart[ 3 ] ) != scanline_width ) ) {
                        return rgbe_error( rgbe_format_error, "bad rgbe scanline format" );
                    }
                    // read each of the four channels for the scanline into the buffer
                    // first red, then green, then blue, then exponent
                    ptr = 0;
                    while ( ( ptr < ptr_end ) && ( pos < buffer.byteLength ) ) {
                        count = buffer[ pos ++ ];
                        isEncodedRun = count > 128;
                        if ( isEncodedRun ) count -= 128;
                        if ( ( 0 === count ) || ( ptr + count > ptr_end ) ) {
                            return rgbe_error( rgbe_format_error, "bad scanline data" );
                        }
                        if ( isEncodedRun ) {
                            // a (encoded) run of the same value
                            byteValue = buffer[ pos ++ ];
                            for ( i = 0; i < count; i ++ ) {
                                scanline_buffer[ ptr ++ ] = byteValue;
                            }
                            //ptr += count;
                        } else {
                            // a literal-run
                            scanline_buffer.set( buffer.subarray( pos, pos + count ), ptr );
                            ptr += count; pos += count;
                        }
                    }
                    // now convert data from buffer into rgba
                    // first red, then green, then blue, then exponent (alpha)
                    l = scanline_width; //scanline_buffer.byteLength;
                    for ( i = 0; i < l; i ++ ) {
                        off = 0;
                        data_rgba[ offset ] = scanline_buffer[ i + off ];
                        off += scanline_width; //1;
                        data_rgba[ offset + 1 ] = scanline_buffer[ i + off ];
                        off += scanline_width; //1;
                        data_rgba[ offset + 2 ] = scanline_buffer[ i + off ];
                        off += scanline_width; //1;
                        data_rgba[ offset + 3 ] = scanline_buffer[ i + off ];
                        offset += 4;
                    }
                    num_scanlines --;
                }
                return data_rgba;
            };
        var RGBEByteToRGBFloat = function ( sourceArray, sourceOffset, destArray, destOffset ) {
            var e = sourceArray[ sourceOffset + 3 ];
            var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
            destArray[ destOffset + 0 ] = sourceArray[ sourceOffset + 0 ] * scale;
            destArray[ destOffset + 1 ] = sourceArray[ sourceOffset + 1 ] * scale;
            destArray[ destOffset + 2 ] = sourceArray[ sourceOffset + 2 ] * scale;
        };
        var RGBEByteToRGBHalf = ( function () {
            // Source: http://gamedev.stackexchange.com/questions/17326/conversion-of-a-number-from-single-precision-floating-point-representation-to-a/17410#17410
            var floatView = new Float32Array( 1 );
            var int32View = new Int32Array( floatView.buffer );
            /* 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. */
            function toHalf( val ) {
                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;
            }
            return function ( sourceArray, sourceOffset, destArray, destOffset ) {
                var e = sourceArray[ sourceOffset + 3 ];
                var scale = Math.pow( 2.0, e - 128.0 ) / 255.0;
                destArray[ destOffset + 0 ] = toHalf( sourceArray[ sourceOffset + 0 ] * scale );
                destArray[ destOffset + 1 ] = toHalf( sourceArray[ sourceOffset + 1 ] * scale );
                destArray[ destOffset + 2 ] = toHalf( sourceArray[ sourceOffset + 2 ] * scale );
            };
        } )();
        var byteArray = new Uint8Array( buffer );
        byteArray.pos = 0;
        var rgbe_header_info = RGBE_ReadHeader( byteArray );
        if ( RGBE_RETURN_FAILURE !== rgbe_header_info ) {
            var w = rgbe_header_info.width,
                h = rgbe_header_info.height,
                image_rgba_data = RGBE_ReadPixels_RLE( byteArray.subarray( byteArray.pos ), w, h );
            if ( RGBE_RETURN_FAILURE !== image_rgba_data ) {
                switch ( this.type ) {
                    case UnsignedByteType:
                        var data = image_rgba_data;
                        var format = RGBEFormat; // handled as THREE.RGBAFormat in shaders
                        var type = UnsignedByteType;
                        break;
                    case FloatType:
                        var numElements = ( image_rgba_data.length / 4 ) * 3;
                        var floatArray = new Float32Array( numElements );
                        for ( var j = 0; j < numElements; j ++ ) {
                            RGBEByteToRGBFloat( image_rgba_data, j * 4, floatArray, j * 3 );
                        }
                        var data = floatArray;
                        var format = RGBFormat;
                        var type = FloatType;
                        break;
                    case HalfFloatType:
                        var numElements = ( image_rgba_data.length / 4 ) * 3;
                        var halfArray = new Uint16Array( numElements );
                        for ( var j = 0; j < numElements; j ++ ) {
                            RGBEByteToRGBHalf( image_rgba_data, j * 4, halfArray, j * 3 );
                        }
                        var data = halfArray;
                        var format = RGBFormat;
                        var type = HalfFloatType;
                        break;
                    default:
                        console.error( 'THREE.RGBELoader: unsupported type: ', this.type );
                        break;
                }
                return {
                    width: w, height: h,
                    data: data,
                    header: rgbe_header_info.string,
                    gamma: rgbe_header_info.gamma,
                    exposure: rgbe_header_info.exposure,
                    format: format,
                    type: type
                };
            }
        }
        return null;
    },
    setDataType: function ( value ) {
        this.type = value;
        return this;
    },
    load: function ( url, onLoad, onProgress, onError ) {
        function onLoadCallback( texture, texData ) {
            switch ( texture.type ) {
                case UnsignedByteType:
                    texture.encoding = RGBEEncoding;
                    texture.minFilter = NearestFilter;
                    texture.magFilter = NearestFilter;
                    texture.generateMipmaps = false;
                    texture.flipY = true;
                    break;
                case FloatType:
                    texture.encoding = LinearEncoding;
                    texture.minFilter = LinearFilter;
                    texture.magFilter = LinearFilter;
                    texture.generateMipmaps = false;
                    texture.flipY = true;
                    break;
                case HalfFloatType:
                    texture.encoding = LinearEncoding;
                    texture.minFilter = LinearFilter;
                    texture.magFilter = LinearFilter;
                    texture.generateMipmaps = false;
                    texture.flipY = true;
                    break;
            }
            if ( onLoad ) onLoad( texture, texData );
        }
        return DataTextureLoader.prototype.load.call( this, url, onLoadCallback, onProgress, onError );
    }
} );
return { RGBELoader };
}
miniprogram-9/jsm/loaders/STLLoader.js
New file
@@ -0,0 +1,409 @@
/**
 * @author aleeper / http://adamleeper.com/
 * @author mrdoob / http://mrdoob.com/
 * @author gero3 / https://github.com/gero3
 * @author Mugen87 / https://github.com/Mugen87
 * @author neverhood311 / https://github.com/neverhood311
 *
 * Description: A THREE loader for STL ASCII files, as created by Solidworks and other CAD programs.
 *
 * Supports both binary and ASCII encoded files, with automatic detection of type.
 *
 * The loader returns a non-indexed buffer geometry.
 *
 * Limitations:
 *  Binary decoding supports "Magics" color format (http://en.wikipedia.org/wiki/STL_(file_format)#Color_in_binary_STL).
 *  There is perhaps some question as to how valid it is to always assume little-endian-ness.
 *  ASCII decoding assumes file is UTF-8.
 *
 * Usage:
 *  var loader = new STLLoader();
 *  loader.load( './models/stl/slotted_disk.stl', function ( geometry ) {
 *    scene.add( new THREE.Mesh( geometry ) );
 *  });
 *
 * For binary STLs geometry might contain colors for vertices. To use it:
 *  // use the same code to load STL as above
 *  if (geometry.hasColors) {
 *    material = new THREE.MeshPhongMaterial({ opacity: geometry.alpha, vertexColors: THREE.VertexColors });
 *  } else { .... }
 *  var mesh = new THREE.Mesh( geometry, material );
 *
 * For ASCII STLs containing multiple solids, each solid is assigned to a different group.
 * Groups can be used to assign a different color by defining an array of materials with the same length of
 * geometry.groups and passing it to the Mesh constructor:
 *
 * var mesh = new THREE.Mesh( geometry, material );
 *
 * For example:
 *
 *  var materials = [];
 *  var nGeometryGroups = geometry.groups.length;
 *
 *  var colorMap = ...; // Some logic to index colors.
 *
 *  for (var i = 0; i < nGeometryGroups; i++) {
 *
 *        var material = new THREE.MeshPhongMaterial({
 *            color: colorMap[i],
 *            wireframe: false
 *        });
 *
 *  }
 *
 *  materials.push(material);
 *  var mesh = new THREE.Mesh(geometry, materials);
 */
export default function (THREE) {
    let {
        BufferAttribute,
        BufferGeometry,
        FileLoader,
        Float32BufferAttribute,
        Loader,
        LoaderUtils,
        Vector3
    } = THREE;
    var STLLoader = function ( manager ) {
        Loader.call( this, manager );
    };
    STLLoader.prototype = Object.assign( Object.create( Loader.prototype ), {
        constructor: STLLoader,
        load: function ( url, onLoad, onProgress, onError ) {
            var scope = this;
            var loader = new FileLoader( scope.manager );
            loader.setPath( scope.path );
            loader.setResponseType( 'arraybuffer' );
            loader.load( url, function ( text ) {
                try {
                    onLoad( scope.parse( text ) );
                } catch ( exception ) {
                    if ( onError ) {
                        onError( exception );
                    }
                }
            }, onProgress, onError );
        },
        parse: function ( data ) {
            function isBinary( data ) {
                var expect, face_size, n_faces, reader;
                reader = new DataView( data );
                face_size = ( 32 / 8 * 3 ) + ( ( 32 / 8 * 3 ) * 3 ) + ( 16 / 8 );
                n_faces = reader.getUint32( 80, true );
                expect = 80 + ( 32 / 8 ) + ( n_faces * face_size );
                if ( expect === reader.byteLength ) {
                    return true;
                }
                // An ASCII STL data must begin with 'solid ' as the first six bytes.
                // However, ASCII STLs lacking the SPACE after the 'd' are known to be
                // plentiful.  So, check the first 5 bytes for 'solid'.
                // Several encodings, such as UTF-8, precede the text with up to 5 bytes:
                // https://en.wikipedia.org/wiki/Byte_order_mark#Byte_order_marks_by_encoding
                // Search for "solid" to start anywhere after those prefixes.
                // US-ASCII ordinal values for 's', 'o', 'l', 'i', 'd'
                var solid = [ 115, 111, 108, 105, 100 ];
                for ( var off = 0; off < 5; off ++ ) {
                    // If "solid" text is matched to the current offset, declare it to be an ASCII STL.
                    if ( matchDataViewAt( solid, reader, off ) ) return false;
                }
                // Couldn't find "solid" text at the beginning; it is binary STL.
                return true;
            }
            function matchDataViewAt( query, reader, offset ) {
                // Check if each byte in query matches the corresponding byte from the current offset
                for ( var i = 0, il = query.length; i < il; i ++ ) {
                    if ( query[ i ] !== reader.getUint8( offset + i, false ) ) return false;
                }
                return true;
            }
            function parseBinary( data ) {
                var reader = new DataView( data );
                var faces = reader.getUint32( 80, true );
                var r, g, b, hasColors = false, colors;
                var defaultR, defaultG, defaultB, alpha;
                // process STL header
                // check for default color in header ("COLOR=rgba" sequence).
                for ( var index = 0; index < 80 - 10; index ++ ) {
                    if ( ( reader.getUint32( index, false ) == 0x434F4C4F /*COLO*/ ) &&
                        ( reader.getUint8( index + 4 ) == 0x52 /*'R'*/ ) &&
                        ( reader.getUint8( index + 5 ) == 0x3D /*'='*/ ) ) {
                        hasColors = true;
                        colors = new Float32Array( faces * 3 * 3 );
                        defaultR = reader.getUint8( index + 6 ) / 255;
                        defaultG = reader.getUint8( index + 7 ) / 255;
                        defaultB = reader.getUint8( index + 8 ) / 255;
                        alpha = reader.getUint8( index + 9 ) / 255;
                    }
                }
                var dataOffset = 84;
                var faceLength = 12 * 4 + 2;
                var geometry = new BufferGeometry();
                var vertices = new Float32Array( faces * 3 * 3 );
                var normals = new Float32Array( faces * 3 * 3 );
                for ( var face = 0; face < faces; face ++ ) {
                    var start = dataOffset + face * faceLength;
                    var normalX = reader.getFloat32( start, true );
                    var normalY = reader.getFloat32( start + 4, true );
                    var normalZ = reader.getFloat32( start + 8, true );
                    if ( hasColors ) {
                        var packedColor = reader.getUint16( start + 48, true );
                        if ( ( packedColor & 0x8000 ) === 0 ) {
                            // facet has its own unique color
                            r = ( packedColor & 0x1F ) / 31;
                            g = ( ( packedColor >> 5 ) & 0x1F ) / 31;
                            b = ( ( packedColor >> 10 ) & 0x1F ) / 31;
                        } else {
                            r = defaultR;
                            g = defaultG;
                            b = defaultB;
                        }
                    }
                    for ( var i = 1; i <= 3; i ++ ) {
                        var vertexstart = start + i * 12;
                        var componentIdx = ( face * 3 * 3 ) + ( ( i - 1 ) * 3 );
                        vertices[ componentIdx ] = reader.getFloat32( vertexstart, true );
                        vertices[ componentIdx + 1 ] = reader.getFloat32( vertexstart + 4, true );
                        vertices[ componentIdx + 2 ] = reader.getFloat32( vertexstart + 8, true );
                        normals[ componentIdx ] = normalX;
                        normals[ componentIdx + 1 ] = normalY;
                        normals[ componentIdx + 2 ] = normalZ;
                        if ( hasColors ) {
                            colors[ componentIdx ] = r;
                            colors[ componentIdx + 1 ] = g;
                            colors[ componentIdx + 2 ] = b;
                        }
                    }
                }
                geometry.addAttribute( 'position', new BufferAttribute( vertices, 3 ) );
                geometry.addAttribute( 'normal', new BufferAttribute( normals, 3 ) );
                if ( hasColors ) {
                    geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) );
                    geometry.hasColors = true;
                    geometry.alpha = alpha;
                }
                return geometry;
            }
            function parseASCII( data ) {
                var geometry = new BufferGeometry();
                var patternSolid = /solid([\s\S]*?)endsolid/g;
                var patternFace = /facet([\s\S]*?)endfacet/g;
                var faceCounter = 0;
                var patternFloat = /[\s]+([+-]?(?:\d*)(?:\.\d*)?(?:[eE][+-]?\d+)?)/.source;
                var patternVertex = new RegExp( 'vertex' + patternFloat + patternFloat + patternFloat, 'g' );
                var patternNormal = new RegExp( 'normal' + patternFloat + patternFloat + patternFloat, 'g' );
                var vertices = [];
                var normals = [];
                var normal = new Vector3();
                var result;
                var groupVertexes = [];
                var groupCount = 0;
                var startVertex = 0;
                var endVertex = 0;
                while ( ( result = patternSolid.exec( data ) ) !== null ) {
                    startVertex = endVertex;
                    var solid = result[ 0 ];
                    while ( ( result = patternFace.exec( solid ) ) !== null ) {
                        var vertexCountPerFace = 0;
                        var normalCountPerFace = 0;
                        var text = result[ 0 ];
                        while ( ( result = patternNormal.exec( text ) ) !== null ) {
                            normal.x = parseFloat( result[ 1 ] );
                            normal.y = parseFloat( result[ 2 ] );
                            normal.z = parseFloat( result[ 3 ] );
                            normalCountPerFace ++;
                        }
                        while ( ( result = patternVertex.exec( text ) ) !== null ) {
                            vertices.push( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) );
                            normals.push( normal.x, normal.y, normal.z );
                            vertexCountPerFace ++;
                            endVertex ++;
                        }
                        // every face have to own ONE valid normal
                        if ( normalCountPerFace !== 1 ) {
                            console.error( 'THREE.STLLoader: Something isn\'t right with the normal of face number ' + faceCounter );
                        }
                        // each face have to own THREE valid vertices
                        if ( vertexCountPerFace !== 3 ) {
                            console.error( 'THREE.STLLoader: Something isn\'t right with the vertices of face number ' + faceCounter );
                        }
                        faceCounter ++;
                    }
                    groupVertexes.push( { startVertex: startVertex, endVertex: endVertex } );
                    groupCount ++;
                }
                geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
                geometry.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );
                if ( groupCount > 0 ) {
                    for ( var i = 0; i < groupVertexes.length; i ++ ) {
                        geometry.addGroup( groupVertexes[ i ].startVertex, groupVertexes[ i ].endVertex, i );
                    }
                }
                return geometry;
            }
            function ensureString( buffer ) {
                if ( typeof buffer !== 'string' ) {
                    return LoaderUtils.decodeText( new Uint8Array( buffer ) );
                }
                return buffer;
            }
            function ensureBinary( buffer ) {
                if ( typeof buffer === 'string' ) {
                    var array_buffer = new Uint8Array( buffer.length );
                    for ( var i = 0; i < buffer.length; i ++ ) {
                        array_buffer[ i ] = buffer.charCodeAt( i ) & 0xff; // implicitly assumes little-endian
                    }
                    return array_buffer.buffer || array_buffer;
                } else {
                    return buffer;
                }
            }
            // start
            var binData = ensureBinary( data );
            return isBinary( binData ) ? parseBinary( binData ) : parseASCII( ensureString( data ) );
        }
    } );
    return { STLLoader };
}
miniprogram-9/jsm/pmrem/PMREMCubeUVPacker.js
New file
@@ -0,0 +1,254 @@
/**
 * @author Prashant Sharma / spidersharma03
 * @author Ben Houston / bhouston, https://clara.io
 *
 * This class takes the cube lods(corresponding to different roughness values), and creates a single cubeUV
 * Texture. The format for a given roughness set of faces is simply::
 * +X+Y+Z
 * -X-Y-Z
 * For every roughness a mip map chain is also saved, which is essential to remove the texture artifacts due to
 * minification.
 * Right now for every face a PlaneMesh is drawn, which leads to a lot of geometry draw calls, but can be replaced
 * later by drawing a single buffer and by sending the appropriate faceIndex via vertex attributes.
 * The arrangement of the faces is fixed, as assuming this arrangement, the sampling function has been written.
 */
export default function (THREE) {
let {
    BackSide,
    CubeUVReflectionMapping,
    LinearFilter,
    LinearToneMapping,
    Mesh,
    NoBlending,
    OrthographicCamera,
    PlaneBufferGeometry,
    RGBEEncoding,
    RGBM16Encoding,
    Scene,
    ShaderMaterial,
    Vector2,
    Vector3,
    WebGLRenderTarget
} = THREE;
var PMREMCubeUVPacker = ( function () {
    var camera = new OrthographicCamera();
    var scene = new Scene();
    var shader = getShader();
    var PMREMCubeUVPacker = function ( cubeTextureLods ) {
        this.cubeLods = cubeTextureLods;
        var size = cubeTextureLods[ 0 ].width * 4;
        var sourceTexture = cubeTextureLods[ 0 ].texture;
        var params = {
            format: sourceTexture.format,
            magFilter: sourceTexture.magFilter,
            minFilter: sourceTexture.minFilter,
            type: sourceTexture.type,
            generateMipmaps: sourceTexture.generateMipmaps,
            anisotropy: sourceTexture.anisotropy,
            encoding: ( sourceTexture.encoding === RGBEEncoding ) ? RGBM16Encoding : sourceTexture.encoding
        };
        if ( params.encoding === RGBM16Encoding ) {
            params.magFilter = LinearFilter;
            params.minFilter = LinearFilter;
        }
        this.CubeUVRenderTarget = new WebGLRenderTarget( size, size, params );
        this.CubeUVRenderTarget.texture.name = "PMREMCubeUVPacker.cubeUv";
        this.CubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
        this.objects = [];
        var geometry = new PlaneBufferGeometry( 1, 1 );
        var faceOffsets = [];
        faceOffsets.push( new Vector2( 0, 0 ) );
        faceOffsets.push( new Vector2( 1, 0 ) );
        faceOffsets.push( new Vector2( 2, 0 ) );
        faceOffsets.push( new Vector2( 0, 1 ) );
        faceOffsets.push( new Vector2( 1, 1 ) );
        faceOffsets.push( new Vector2( 2, 1 ) );
        var textureResolution = size;
        size = cubeTextureLods[ 0 ].width;
        var offset2 = 0;
        var c = 4.0;
        this.numLods = Math.log( cubeTextureLods[ 0 ].width ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
        for ( var i = 0; i < this.numLods; i ++ ) {
            var offset1 = ( textureResolution - textureResolution / c ) * 0.5;
            if ( size > 16 ) c *= 2;
            var nMips = size > 16 ? 6 : 1;
            var mipOffsetX = 0;
            var mipOffsetY = 0;
            var mipSize = size;
            for ( var j = 0; j < nMips; j ++ ) {
                // Mip Maps
                for ( var k = 0; k < 6; k ++ ) {
                    // 6 Cube Faces
                    var material = shader.clone();
                    material.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
                    material.envMap = this.cubeLods[ i ].texture;
                    material.uniforms[ 'faceIndex' ].value = k;
                    material.uniforms[ 'mapSize' ].value = mipSize;
                    var planeMesh = new Mesh( geometry, material );
                    planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX;
                    planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY;
                    planeMesh.material.side = BackSide;
                    planeMesh.scale.setScalar( mipSize );
                    this.objects.push( planeMesh );
                }
                mipOffsetY += 1.75 * mipSize;
                mipOffsetX += 1.25 * mipSize;
                mipSize /= 2;
            }
            offset2 += 2 * size;
            if ( size > 16 ) size /= 2;
        }
    };
    PMREMCubeUVPacker.prototype = {
        constructor: PMREMCubeUVPacker,
        update: function ( renderer ) {
            var size = this.cubeLods[ 0 ].width * 4;
            // top and bottom are swapped for some reason?
            camera.left = - size * 0.5;
            camera.right = size * 0.5;
            camera.top = - size * 0.5;
            camera.bottom = size * 0.5;
            camera.near = 0;
            camera.far = 1;
            camera.updateProjectionMatrix();
            for ( var i = 0; i < this.objects.length; i ++ ) {
                scene.add( this.objects[ i ] );
            }
            var gammaInput = renderer.gammaInput;
            var gammaOutput = renderer.gammaOutput;
            var toneMapping = renderer.toneMapping;
            var toneMappingExposure = renderer.toneMappingExposure;
            var currentRenderTarget = renderer.getRenderTarget();
            renderer.gammaInput = false;
            renderer.gammaOutput = false;
            renderer.toneMapping = LinearToneMapping;
            renderer.toneMappingExposure = 1.0;
            renderer.setRenderTarget( this.CubeUVRenderTarget );
            renderer.render( scene, camera );
            renderer.setRenderTarget( currentRenderTarget );
            renderer.toneMapping = toneMapping;
            renderer.toneMappingExposure = toneMappingExposure;
            renderer.gammaInput = gammaInput;
            renderer.gammaOutput = gammaOutput;
            for ( var i = 0; i < this.objects.length; i ++ ) {
                scene.remove( this.objects[ i ] );
            }
        },
        dispose: function () {
            for ( var i = 0, l = this.objects.length; i < l; i ++ ) {
                this.objects[ i ].material.dispose();
            }
            this.objects[ 0 ].geometry.dispose();
        }
    };
    function getShader() {
        var shaderMaterial = new ShaderMaterial( {
            uniforms: {
                "faceIndex": { value: 0 },
                "mapSize": { value: 0 },
                "envMap": { value: null },
                "testColor": { value: new Vector3( 1, 1, 1 ) }
            },
            vertexShader:
        "precision highp float;\
        varying vec2 vUv;\
        void main() {\
          vUv = uv;\
          gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\
        }",
            fragmentShader:
        "precision highp float;\
        varying vec2 vUv;\
        uniform samplerCube envMap;\
        uniform float mapSize;\
        uniform vec3 testColor;\
        uniform int faceIndex;\
        \
        void main() {\
          vec3 sampleDirection;\
          vec2 uv = vUv;\
          uv = uv * 2.0 - 1.0;\
          uv.y *= -1.0;\
          if(faceIndex == 0) {\
            sampleDirection = normalize(vec3(1.0, uv.y, -uv.x));\
          } else if(faceIndex == 1) {\
            sampleDirection = normalize(vec3(uv.x, 1.0, uv.y));\
          } else if(faceIndex == 2) {\
            sampleDirection = normalize(vec3(uv.x, uv.y, 1.0));\
          } else if(faceIndex == 3) {\
            sampleDirection = normalize(vec3(-1.0, uv.y, uv.x));\
          } else if(faceIndex == 4) {\
            sampleDirection = normalize(vec3(uv.x, -1.0, -uv.y));\
          } else {\
            sampleDirection = normalize(vec3(-uv.x, uv.y, -1.0));\
          }\
          vec4 color = envMapTexelToLinear( textureCube( envMap, sampleDirection ) );\
          gl_FragColor = linearToOutputTexel( color );\
        }",
            blending: NoBlending
        } );
        shaderMaterial.type = 'PMREMCubeUVPacker';
        return shaderMaterial;
    }
    return PMREMCubeUVPacker;
} )();
return { PMREMCubeUVPacker };
}
miniprogram-9/jsm/pmrem/PMREMGenerator.js
New file
@@ -0,0 +1,311 @@
/**
 * @author Prashant Sharma / spidersharma03
 * @author Ben Houston / bhouston, https://clara.io
 *
 * To avoid cube map seams, I create an extra pixel around each face. This way when the cube map is
 * sampled by an application later(with a little care by sampling the centre of the texel), the extra 1 border
 *    of pixels makes sure that there is no seams artifacts present. This works perfectly for cubeUV format as
 *    well where the 6 faces can be arranged in any manner whatsoever.
 * Code in the beginning of fragment shader's main function does this job for a given resolution.
 *    Run Scene_PMREM_Test.html in the examples directory to see the sampling from the cube lods generated
 *    by this class.
 */
export default function (THREE) {
let {
    DoubleSide,
    GammaEncoding,
    LinearEncoding,
    LinearFilter,
    LinearToneMapping,
    Mesh,
    NearestFilter,
    NoBlending,
    OrthographicCamera,
    PlaneBufferGeometry,
    Scene,
    ShaderMaterial,
    WebGLRenderTargetCube,
    sRGBEncoding
} = THREE;
var PMREMGenerator = ( function () {
    var shader = getShader();
    var camera = new OrthographicCamera( - 1, 1, 1, - 1, 0.0, 1000 );
    var scene = new Scene();
    var planeMesh = new Mesh( new PlaneBufferGeometry( 2, 2, 0 ), shader );
    planeMesh.material.side = DoubleSide;
    scene.add( planeMesh );
    scene.add( camera );
    var PMREMGenerator = function ( sourceTexture, samplesPerLevel, resolution ) {
        this.sourceTexture = sourceTexture;
        this.resolution = ( resolution !== undefined ) ? resolution : 256; // NODE: 256 is currently hard coded in the glsl code for performance reasons
        this.samplesPerLevel = ( samplesPerLevel !== undefined ) ? samplesPerLevel : 32;
        var monotonicEncoding = ( this.sourceTexture.encoding === LinearEncoding ) ||
            ( this.sourceTexture.encoding === GammaEncoding ) || ( this.sourceTexture.encoding === sRGBEncoding );
        this.sourceTexture.minFilter = ( monotonicEncoding ) ? LinearFilter : NearestFilter;
        this.sourceTexture.magFilter = ( monotonicEncoding ) ? LinearFilter : NearestFilter;
        this.sourceTexture.generateMipmaps = this.sourceTexture.generateMipmaps && monotonicEncoding;
        this.cubeLods = [];
        var size = this.resolution;
        var params = {
            format: this.sourceTexture.format,
            magFilter: this.sourceTexture.magFilter,
            minFilter: this.sourceTexture.minFilter,
            type: this.sourceTexture.type,
            generateMipmaps: this.sourceTexture.generateMipmaps,
            anisotropy: this.sourceTexture.anisotropy,
            encoding: this.sourceTexture.encoding
        };
        // how many LODs fit in the given CubeUV Texture.
        this.numLods = Math.log( size ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2
        for ( var i = 0; i < this.numLods; i ++ ) {
            var renderTarget = new WebGLRenderTargetCube( size, size, params );
            renderTarget.texture.name = "PMREMGenerator.cube" + i;
            this.cubeLods.push( renderTarget );
            size = Math.max( 16, size / 2 );
        }
    };
    PMREMGenerator.prototype = {
        constructor: PMREMGenerator,
        /*
         * Prashant Sharma / spidersharma03: More thought and work is needed here.
         * Right now it's a kind of a hack to use the previously convolved map to convolve the current one.
         * I tried to use the original map to convolve all the lods, but for many textures(specially the high frequency)
         * even a high number of samples(1024) dosen't lead to satisfactory results.
         * By using the previous convolved maps, a lower number of samples are generally sufficient(right now 32, which
         * gives okay results unless we see the reflection very carefully, or zoom in too much), however the math
         * goes wrong as the distribution function tries to sample a larger area than what it should be. So I simply scaled
         * the roughness by 0.9(totally empirical) to try to visually match the original result.
         * The condition "if(i <5)" is also an attemt to make the result match the original result.
         * This method requires the most amount of thinking I guess. Here is a paper which we could try to implement in future::
         * https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
         */
        update: function ( renderer ) {
            // Texture should only be flipped for CubeTexture, not for
            // a Texture created via WebGLRenderTargetCube.
            var tFlip = ( this.sourceTexture.isCubeTexture ) ? - 1 : 1;
            shader.defines[ 'SAMPLES_PER_LEVEL' ] = this.samplesPerLevel;
            shader.uniforms[ 'faceIndex' ].value = 0;
            shader.uniforms[ 'envMap' ].value = this.sourceTexture;
            shader.envMap = this.sourceTexture;
            shader.needsUpdate = true;
            var gammaInput = renderer.gammaInput;
            var gammaOutput = renderer.gammaOutput;
            var toneMapping = renderer.toneMapping;
            var toneMappingExposure = renderer.toneMappingExposure;
            var currentRenderTarget = renderer.getRenderTarget();
            renderer.toneMapping = LinearToneMapping;
            renderer.toneMappingExposure = 1.0;
            renderer.gammaInput = false;
            renderer.gammaOutput = false;
            for ( var i = 0; i < this.numLods; i ++ ) {
                var r = i / ( this.numLods - 1 );
                shader.uniforms[ 'roughness' ].value = r * 0.9; // see comment above, pragmatic choice
                // Only apply the tFlip for the first LOD
                shader.uniforms[ 'tFlip' ].value = ( i == 0 ) ? tFlip : 1;
                var size = this.cubeLods[ i ].width;
                shader.uniforms[ 'mapSize' ].value = size;
                this.renderToCubeMapTarget( renderer, this.cubeLods[ i ] );
                if ( i < 5 ) shader.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture;
            }
            renderer.setRenderTarget( currentRenderTarget );
            renderer.toneMapping = toneMapping;
            renderer.toneMappingExposure = toneMappingExposure;
            renderer.gammaInput = gammaInput;
            renderer.gammaOutput = gammaOutput;
        },
        renderToCubeMapTarget: function ( renderer, renderTarget ) {
            for ( var i = 0; i < 6; i ++ ) {
                this.renderToCubeMapTargetFace( renderer, renderTarget, i );
            }
        },
        renderToCubeMapTargetFace: function ( renderer, renderTarget, faceIndex ) {
            shader.uniforms[ 'faceIndex' ].value = faceIndex;
            renderer.setRenderTarget( renderTarget, faceIndex );
            renderer.clear();
            renderer.render( scene, camera );
        },
        dispose: function () {
            for ( var i = 0, l = this.cubeLods.length; i < l; i ++ ) {
                this.cubeLods[ i ].dispose();
            }
        },
    };
    function getShader() {
        var shaderMaterial = new ShaderMaterial( {
            defines: {
                "SAMPLES_PER_LEVEL": 20,
            },
            uniforms: {
                "faceIndex": { value: 0 },
                "roughness": { value: 0.5 },
                "mapSize": { value: 0.5 },
                "envMap": { value: null },
                "tFlip": { value: - 1 },
            },
            vertexShader:
                "varying vec2 vUv;\n\
                void main() {\n\
                    vUv = uv;\n\
                    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\
                }",
            fragmentShader:
                "#include <common>\n\
                varying vec2 vUv;\n\
                uniform int faceIndex;\n\
                uniform float roughness;\n\
                uniform samplerCube envMap;\n\
                uniform float mapSize;\n\
                uniform float tFlip;\n\
                \n\
                float GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n\
                    float a = ggxRoughness + 0.0001;\n\
                    a *= a;\n\
                    return ( 2.0 / a - 2.0 );\n\
                }\n\
                vec3 ImportanceSamplePhong(vec2 uv, mat3 vecSpace, float specPow) {\n\
                    float phi = uv.y * 2.0 * PI;\n\
                    float cosTheta = pow(1.0 - uv.x, 1.0 / (specPow + 1.0));\n\
                    float sinTheta = sqrt(1.0 - cosTheta * cosTheta);\n\
                    vec3 sampleDir = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);\n\
                    return vecSpace * sampleDir;\n\
                }\n\
                vec3 ImportanceSampleGGX( vec2 uv, mat3 vecSpace, float Roughness )\n\
                {\n\
                    float a = Roughness * Roughness;\n\
                    float Phi = 2.0 * PI * uv.x;\n\
                    float CosTheta = sqrt( (1.0 - uv.y) / ( 1.0 + (a*a - 1.0) * uv.y ) );\n\
                    float SinTheta = sqrt( 1.0 - CosTheta * CosTheta );\n\
                    return vecSpace * vec3(SinTheta * cos( Phi ), SinTheta * sin( Phi ), CosTheta);\n\
                }\n\
                mat3 matrixFromVector(vec3 n) {\n\
                    float a = 1.0 / (1.0 + n.z);\n\
                    float b = -n.x * n.y * a;\n\
                    vec3 b1 = vec3(1.0 - n.x * n.x * a, b, -n.x);\n\
                    vec3 b2 = vec3(b, 1.0 - n.y * n.y * a, -n.y);\n\
                    return mat3(b1, b2, n);\n\
                }\n\
                \n\
                vec4 testColorMap(float Roughness) {\n\
                    vec4 color;\n\
                    if(faceIndex == 0)\n\
                        color = vec4(1.0,0.0,0.0,1.0);\n\
                    else if(faceIndex == 1)\n\
                        color = vec4(0.0,1.0,0.0,1.0);\n\
                    else if(faceIndex == 2)\n\
                        color = vec4(0.0,0.0,1.0,1.0);\n\
                    else if(faceIndex == 3)\n\
                        color = vec4(1.0,1.0,0.0,1.0);\n\
                    else if(faceIndex == 4)\n\
                        color = vec4(0.0,1.0,1.0,1.0);\n\
                    else\n\
                        color = vec4(1.0,0.0,1.0,1.0);\n\
                    color *= ( 1.0 - Roughness );\n\
                    return color;\n\
                }\n\
                void main() {\n\
                    vec3 sampleDirection;\n\
                    vec2 uv = vUv*2.0 - 1.0;\n\
                    float offset = -1.0/mapSize;\n\
                    const float a = -1.0;\n\
                    const float b = 1.0;\n\
                    float c = -1.0 + offset;\n\
                    float d = 1.0 - offset;\n\
                    float bminusa = b - a;\n\
                    uv.x = (uv.x - a)/bminusa * d - (uv.x - b)/bminusa * c;\n\
                    uv.y = (uv.y - a)/bminusa * d - (uv.y - b)/bminusa * c;\n\
                    if (faceIndex==0) {\n\
                        sampleDirection = vec3(1.0, -uv.y, -uv.x);\n\
                    } else if (faceIndex==1) {\n\
                        sampleDirection = vec3(-1.0, -uv.y, uv.x);\n\
                    } else if (faceIndex==2) {\n\
                        sampleDirection = vec3(uv.x, 1.0, uv.y);\n\
                    } else if (faceIndex==3) {\n\
                        sampleDirection = vec3(uv.x, -1.0, -uv.y);\n\
                    } else if (faceIndex==4) {\n\
                        sampleDirection = vec3(uv.x, -uv.y, 1.0);\n\
                    } else {\n\
                        sampleDirection = vec3(-uv.x, -uv.y, -1.0);\n\
                    }\n\
                    vec3 correctedDirection = vec3( tFlip * sampleDirection.x, sampleDirection.yz );\n\
                    mat3 vecSpace = matrixFromVector( normalize( correctedDirection ) );\n\
                    vec3 rgbColor = vec3(0.0);\n\
                    const int NumSamples = SAMPLES_PER_LEVEL;\n\
                    vec3 vect;\n\
                    float weight = 0.0;\n\
                    for( int i = 0; i < NumSamples; i ++ ) {\n\
                        float sini = sin(float(i));\n\
                        float cosi = cos(float(i));\n\
                        float r = rand(vec2(sini, cosi));\n\
                        vect = ImportanceSampleGGX(vec2(float(i) / float(NumSamples), r), vecSpace, roughness);\n\
                        float dotProd = dot(vect, normalize(sampleDirection));\n\
                        weight += dotProd;\n\
                        vec3 color = envMapTexelToLinear(textureCube(envMap, vect)).rgb;\n\
                        rgbColor.rgb += color;\n\
                    }\n\
                    rgbColor /= float(NumSamples);\n\
                    //rgbColor = testColorMap( roughness ).rgb;\n\
                    gl_FragColor = linearToOutputTexel( vec4( rgbColor, 1.0 ) );\n\
                }",
            blending: NoBlending
        } );
        shaderMaterial.type = 'PMREMGenerator';
        return shaderMaterial;
    }
    return PMREMGenerator;
} )();
return { PMREMGenerator };
}
miniprogram-9/jsm/utils/SkeletonUtils.js
New file
@@ -0,0 +1,599 @@
/**
 * @author sunag / http://www.sunag.com.br
 */
export default function (THREE) {
let {
    AnimationClip,
    AnimationMixer,
    Euler,
    Matrix4,
    Quaternion,
    QuaternionKeyframeTrack,
    SkeletonHelper,
    Vector2,
    Vector3,
    VectorKeyframeTrack
} = THREE;
var SkeletonUtils = {
    retarget: function () {
        var pos = new Vector3(),
            quat = new Quaternion(),
            scale = new Vector3(),
            bindBoneMatrix = new Matrix4(),
            relativeMatrix = new Matrix4(),
            globalMatrix = new Matrix4();
        return function ( target, source, options ) {
            options = options || {};
            options.preserveMatrix = options.preserveMatrix !== undefined ? options.preserveMatrix : true;
            options.preservePosition = options.preservePosition !== undefined ? options.preservePosition : true;
            options.preserveHipPosition = options.preserveHipPosition !== undefined ? options.preserveHipPosition : false;
            options.useTargetMatrix = options.useTargetMatrix !== undefined ? options.useTargetMatrix : false;
            options.hip = options.hip !== undefined ? options.hip : "hip";
            options.names = options.names || {};
            var sourceBones = source.isObject3D ? source.skeleton.bones : this.getBones( source ),
                bones = target.isObject3D ? target.skeleton.bones : this.getBones( target ),
                bindBones,
                bone, name, boneTo,
                bonesPosition, i;
            // reset bones
            if ( target.isObject3D ) {
                target.skeleton.pose();
            } else {
                options.useTargetMatrix = true;
                options.preserveMatrix = false;
            }
            if ( options.preservePosition ) {
                bonesPosition = [];
                for ( i = 0; i < bones.length; i ++ ) {
                    bonesPosition.push( bones[ i ].position.clone() );
                }
            }
            if ( options.preserveMatrix ) {
                // reset matrix
                target.updateMatrixWorld();
                target.matrixWorld.identity();
                // reset children matrix
                for ( i = 0; i < target.children.length; ++ i ) {
                    target.children[ i ].updateMatrixWorld( true );
                }
            }
            if ( options.offsets ) {
                bindBones = [];
                for ( i = 0; i < bones.length; ++ i ) {
                    bone = bones[ i ];
                    name = options.names[ bone.name ] || bone.name;
                    if ( options.offsets && options.offsets[ name ] ) {
                        bone.matrix.multiply( options.offsets[ name ] );
                        bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
                        bone.updateMatrixWorld();
                    }
                    bindBones.push( bone.matrixWorld.clone() );
                }
            }
            for ( i = 0; i < bones.length; ++ i ) {
                bone = bones[ i ];
                name = options.names[ bone.name ] || bone.name;
                boneTo = this.getBoneByName( name, sourceBones );
                globalMatrix.copy( bone.matrixWorld );
                if ( boneTo ) {
                    boneTo.updateMatrixWorld();
                    if ( options.useTargetMatrix ) {
                        relativeMatrix.copy( boneTo.matrixWorld );
                    } else {
                        relativeMatrix.getInverse( target.matrixWorld );
                        relativeMatrix.multiply( boneTo.matrixWorld );
                    }
                    // ignore scale to extract rotation
                    scale.setFromMatrixScale( relativeMatrix );
                    relativeMatrix.scale( scale.set( 1 / scale.x, 1 / scale.y, 1 / scale.z ) );
                    // apply to global matrix
                    globalMatrix.makeRotationFromQuaternion( quat.setFromRotationMatrix( relativeMatrix ) );
                    if ( target.isObject3D ) {
                        var boneIndex = bones.indexOf( bone ),
                            wBindMatrix = bindBones ? bindBones[ boneIndex ] : bindBoneMatrix.getInverse( target.skeleton.boneInverses[ boneIndex ] );
                        globalMatrix.multiply( wBindMatrix );
                    }
                    globalMatrix.copyPosition( relativeMatrix );
                }
                if ( bone.parent && bone.parent.isBone ) {
                    bone.matrix.getInverse( bone.parent.matrixWorld );
                    bone.matrix.multiply( globalMatrix );
                } else {
                    bone.matrix.copy( globalMatrix );
                }
                if ( options.preserveHipPosition && name === options.hip ) {
                    bone.matrix.setPosition( pos.set( 0, bone.position.y, 0 ) );
                }
                bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
                bone.updateMatrixWorld();
            }
            if ( options.preservePosition ) {
                for ( i = 0; i < bones.length; ++ i ) {
                    bone = bones[ i ];
                    name = options.names[ bone.name ] || bone.name;
                    if ( name !== options.hip ) {
                        bone.position.copy( bonesPosition[ i ] );
                    }
                }
            }
            if ( options.preserveMatrix ) {
                // restore matrix
                target.updateMatrixWorld( true );
            }
        };
    }(),
    retargetClip: function ( target, source, clip, options ) {
        options = options || {};
        options.useFirstFramePosition = options.useFirstFramePosition !== undefined ? options.useFirstFramePosition : false;
        options.fps = options.fps !== undefined ? options.fps : 30;
        options.names = options.names || [];
        if ( ! source.isObject3D ) {
            source = this.getHelperFromSkeleton( source );
        }
        var numFrames = Math.round( clip.duration * ( options.fps / 1000 ) * 1000 ),
            delta = 1 / options.fps,
            convertedTracks = [],
            mixer = new AnimationMixer( source ),
            bones = this.getBones( target.skeleton ),
            boneDatas = [],
            positionOffset,
            bone, boneTo, boneData,
            name, i, j;
        mixer.clipAction( clip ).play();
        mixer.update( 0 );
        source.updateMatrixWorld();
        for ( i = 0; i < numFrames; ++ i ) {
            var time = i * delta;
            this.retarget( target, source, options );
            for ( j = 0; j < bones.length; ++ j ) {
                name = options.names[ bones[ j ].name ] || bones[ j ].name;
                boneTo = this.getBoneByName( name, source.skeleton );
                if ( boneTo ) {
                    bone = bones[ j ];
                    boneData = boneDatas[ j ] = boneDatas[ j ] || { bone: bone };
                    if ( options.hip === name ) {
                        if ( ! boneData.pos ) {
                            boneData.pos = {
                                times: new Float32Array( numFrames ),
                                values: new Float32Array( numFrames * 3 )
                            };
                        }
                        if ( options.useFirstFramePosition ) {
                            if ( i === 0 ) {
                                positionOffset = bone.position.clone();
                            }
                            bone.position.sub( positionOffset );
                        }
                        boneData.pos.times[ i ] = time;
                        bone.position.toArray( boneData.pos.values, i * 3 );
                    }
                    if ( ! boneData.quat ) {
                        boneData.quat = {
                            times: new Float32Array( numFrames ),
                            values: new Float32Array( numFrames * 4 )
                        };
                    }
                    boneData.quat.times[ i ] = time;
                    bone.quaternion.toArray( boneData.quat.values, i * 4 );
                }
            }
            mixer.update( delta );
            source.updateMatrixWorld();
        }
        for ( i = 0; i < boneDatas.length; ++ i ) {
            boneData = boneDatas[ i ];
            if ( boneData ) {
                if ( boneData.pos ) {
                    convertedTracks.push( new VectorKeyframeTrack(
                        ".bones[" + boneData.bone.name + "].position",
                        boneData.pos.times,
                        boneData.pos.values
                    ) );
                }
                convertedTracks.push( new QuaternionKeyframeTrack(
                    ".bones[" + boneData.bone.name + "].quaternion",
                    boneData.quat.times,
                    boneData.quat.values
                ) );
            }
        }
        mixer.uncacheAction( clip );
        return new AnimationClip( clip.name, - 1, convertedTracks );
    },
    getHelperFromSkeleton: function ( skeleton ) {
        var source = new SkeletonHelper( skeleton.bones[ 0 ] );
        source.skeleton = skeleton;
        return source;
    },
    getSkeletonOffsets: function () {
        var targetParentPos = new Vector3(),
            targetPos = new Vector3(),
            sourceParentPos = new Vector3(),
            sourcePos = new Vector3(),
            targetDir = new Vector2(),
            sourceDir = new Vector2();
        return function ( target, source, options ) {
            options = options || {};
            options.hip = options.hip !== undefined ? options.hip : "hip";
            options.names = options.names || {};
            if ( ! source.isObject3D ) {
                source = this.getHelperFromSkeleton( source );
            }
            var nameKeys = Object.keys( options.names ),
                nameValues = Object.values( options.names ),
                sourceBones = source.isObject3D ? source.skeleton.bones : this.getBones( source ),
                bones = target.isObject3D ? target.skeleton.bones : this.getBones( target ),
                offsets = [],
                bone, boneTo,
                name, i;
            target.skeleton.pose();
            for ( i = 0; i < bones.length; ++ i ) {
                bone = bones[ i ];
                name = options.names[ bone.name ] || bone.name;
                boneTo = this.getBoneByName( name, sourceBones );
                if ( boneTo && name !== options.hip ) {
                    var boneParent = this.getNearestBone( bone.parent, nameKeys ),
                        boneToParent = this.getNearestBone( boneTo.parent, nameValues );
                    boneParent.updateMatrixWorld();
                    boneToParent.updateMatrixWorld();
                    targetParentPos.setFromMatrixPosition( boneParent.matrixWorld );
                    targetPos.setFromMatrixPosition( bone.matrixWorld );
                    sourceParentPos.setFromMatrixPosition( boneToParent.matrixWorld );
                    sourcePos.setFromMatrixPosition( boneTo.matrixWorld );
                    targetDir.subVectors(
                        new Vector2( targetPos.x, targetPos.y ),
                        new Vector2( targetParentPos.x, targetParentPos.y )
                    ).normalize();
                    sourceDir.subVectors(
                        new Vector2( sourcePos.x, sourcePos.y ),
                        new Vector2( sourceParentPos.x, sourceParentPos.y )
                    ).normalize();
                    var laterialAngle = targetDir.angle() - sourceDir.angle();
                    var offset = new Matrix4().makeRotationFromEuler(
                        new Euler(
                            0,
                            0,
                            laterialAngle
                        )
                    );
                    bone.matrix.multiply( offset );
                    bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );
                    bone.updateMatrixWorld();
                    offsets[ name ] = offset;
                }
            }
            return offsets;
        };
    }(),
    renameBones: function ( skeleton, names ) {
        var bones = this.getBones( skeleton );
        for ( var i = 0; i < bones.length; ++ i ) {
            var bone = bones[ i ];
            if ( names[ bone.name ] ) {
                bone.name = names[ bone.name ];
            }
        }
        return this;
    },
    getBones: function ( skeleton ) {
        return Array.isArray( skeleton ) ? skeleton : skeleton.bones;
    },
    getBoneByName: function ( name, skeleton ) {
        for ( var i = 0, bones = this.getBones( skeleton ); i < bones.length; i ++ ) {
            if ( name === bones[ i ].name )
                return bones[ i ];
        }
    },
    getNearestBone: function ( bone, names ) {
        while ( bone.isBone ) {
            if ( names.indexOf( bone.name ) !== - 1 ) {
                return bone;
            }
            bone = bone.parent;
        }
    },
    findBoneTrackData: function ( name, tracks ) {
        var regexp = /\[(.*)\]\.(.*)/,
            result = { name: name };
        for ( var i = 0; i < tracks.length; ++ i ) {
            // 1 is track name
            // 2 is track type
            var trackData = regexp.exec( tracks[ i ].name );
            if ( trackData && name === trackData[ 1 ] ) {
                result[ trackData[ 2 ] ] = i;
            }
        }
        return result;
    },
    getEqualsBonesNames: function ( skeleton, targetSkeleton ) {
        var sourceBones = this.getBones( skeleton ),
            targetBones = this.getBones( targetSkeleton ),
            bones = [];
        search : for ( var i = 0; i < sourceBones.length; i ++ ) {
            var boneName = sourceBones[ i ].name;
            for ( var j = 0; j < targetBones.length; j ++ ) {
                if ( boneName === targetBones[ j ].name ) {
                    bones.push( boneName );
                    continue search;
                }
            }
        }
        return bones;
    },
    clone: function ( source ) {
        var sourceLookup = new Map();
        var cloneLookup = new Map();
        var clone = source.clone();
        parallelTraverse( source, clone, function ( sourceNode, clonedNode ) {
            sourceLookup.set( clonedNode, sourceNode );
            cloneLookup.set( sourceNode, clonedNode );
        } );
        clone.traverse( function ( node ) {
            if ( ! node.isSkinnedMesh ) return;
            var clonedMesh = node;
            var sourceMesh = sourceLookup.get( node );
            var sourceBones = sourceMesh.skeleton.bones;
            clonedMesh.skeleton = sourceMesh.skeleton.clone();
            clonedMesh.bindMatrix.copy( sourceMesh.bindMatrix );
            clonedMesh.skeleton.bones = sourceBones.map( function ( bone ) {
                return cloneLookup.get( bone );
            } );
            clonedMesh.bind( clonedMesh.skeleton, clonedMesh.bindMatrix );
        } );
        return clone;
    }
};
function parallelTraverse( a, b, callback ) {
    callback( a, b );
    for ( var i = 0; i < a.children.length; i ++ ) {
        parallelTraverse( a.children[ i ], b.children[ i ], callback );
    }
}
return { SkeletonUtils };
}
miniprogram-9/libs/three.weapp.js
New file
Diff too large
miniprogram-9/pages/ThreeModel/ThreeModel.js
New file
@@ -0,0 +1,262 @@
// pages/ThreeModel/ThreeModel.js
import getMTLLoader from '../../jsm/loaders/MTLLoader.js';
import getOBJLoader from '../../jsm/loaders/OBJLoader.js';
// import CameraControls from '../../jsm/controls/CameraControls'
import {
  OrbitControls
} from '../../jsm/controls/OrbitControls';
import * as THREE from '../../libs/three.weapp.js'
var {
  MTLLoader
} = getMTLLoader(THREE);
var OBJLoader = getOBJLoader(THREE);
var window = THREE.global;
var camera, scene, requestAnimationFrame, renderer, controls, jeepCar, cameracontrols, texture;
let t0 = new Date()
// var animateion = wx.createAnimation({
//   delay: 0,
//   duration: 800,
//   timingFunction: "ease"
// })
Page({
  /**
   * 页面的初始数据
   */
  data: {
    isOnAccelerometerChange: true,
    threeLeft: "0",
    vdisplay: "none",
    view: "1",
    change: false,
    back: true,
    thirdView: false
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.start()
  },
  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {
  },
  onUnload: function () {
  },
  start: function () {
    let that = this;
    wx.createSelectorQuery()
      .select('#c')
      .node()
      .exec((res) => {
        console.log(res)
        const canvas = new THREE.global.registerCanvas(res[0].node)
        console.log(canvas, "canvas")
        if (canvas != undefined) {
          canvas.width = canvas._width;
          canvas.height = canvas._height;
          requestAnimationFrame = canvas.requestAnimationFrame;
          that.init(canvas);
        }
      })
  },
  onProgress: function (xhr) {
    if (xhr.lengthComputable) {
      let percentComplete = xhr.loaded / xhr.total * 100;
      // console.log('model ' + Math.round(percentComplete, 2) + '% downloaded');
    }
  },
  onError: function () {},
  // 模型加载
  init: function (canvas) {
    let that = this;
    // render
    renderer = new THREE.WebGLRenderer({
      canvas,
      antialias: true,
      alpha: true,
      preserveDrawingBuffer:true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerHeight, window.innerHeight);
    // camera
    console.log(that.data.view, "函数内")
    if (that.data.view == "1") {
      camera = new THREE.PerspectiveCamera(10, 2, 1, -1, 100, 1000);
      camera.position.z = 500;
      camera.position.y = 300;
      camera.lookAt({
        x: 0,
        y: 200,
        z: 0
      })
    } else if (that.data.view == "2") {
      // camera.position.set(400,400,500)
      camera = new THREE.PerspectiveCamera(60, 2, 1, -1, 1, 10);
      camera.position.y = 20;
      camera.position.x = -10;
      camera.position.z = -10;
      camera.lookAt({
        x: 0,
        y: 50,
        z: 0
      })
    }
    // else if (that.data.view == "3") {
    //   camera = new THREE.PerspectiveCamera(45, 1, 1, 500);
    //   camera.position.set = (400,400,500);
    //   camera.lookAt({
    //     x:0,
    //     y:400,
    //     z:0
    //   })
    // }
    // controls
    controls = new OrbitControls(camera, canvas);
    controls.minZoom = 0.5;
    controls.target.set(1, 5, 1);
    // scan和light
    scene = new THREE.Scene();
    let ambientLight = new THREE.AmbientLight(0x444444, 0.4);
    scene.add(ambientLight);
    let AmbientLight = new THREE.AmbientLight(0xffffff, 1)
    // let pointLight = new THREE.PointLight(0xffffff, 3);
    camera.add(AmbientLight);
    scene.add(camera);
    // 加载obj模型
    let loader = new OBJLoader();
    loader.load('http://47.92.33.19:9001/version/OBJ/Diaobao.obj', function (obj) {
      console.log(obj)
    });
    // 加载mtl纹理
    let mtlLoader = new MTLLoader();
    mtlLoader.load(
      'http://47.92.33.19:9001/version/OBJ/Diaobao.mtl',
      function (materials) {
        materials.preload()
        // console.log(that, "this")
        loader.setMaterials(materials)
        // 加载obj
        loader.load('http://47.92.33.19:9001/version/OBJ/Diaobao.obj', function (obj) {
          // 模型文件太大,缩小一下比例,方便显示
          obj.scale.set(0.5, 0.5, 0.5)
          obj.position.y = -15
          // 设置可以投影
          obj.children.forEach(item => {
            item.castShadow = true
            item.receiveShadow = true
            // console.log(item,"item")
          })
          jeepCar = obj
          // 添加到场景
          scene.add(jeepCar)
        })
      }
    )
    that.animate()
  },
  animate: function () {
    let that = this;
    // controls.update();
    // let t1 = new Date(); //本次时间
    // let t = t1 - t0; // 时间差
    // camera.translateX(0.001 * t/3) //沿着Y轴旋转着从大到小
    // camera.translateZ(0.001 * t/3) //沿着Z轴由近到远
    // camera.rotateY(0.0001 * t/3); //物体的均匀从左到又平移可以用相机旋转Y轴来实现
    camera.lookAt(scene.position);
    requestAnimationFrame(that.animate);
    renderer.render(scene, camera);
  },
  // 模型拖动事件
  touchStart(e) {
    THREE.global.touchEventHandlerFactory('canvas', 'touchstart')(e)
  },
  touchMove(e) {
    THREE.global.touchEventHandlerFactory('canvas', 'touchmove')(e)
  },
  touchEnd(e) {
    THREE.global.touchEventHandlerFactory('canvas', 'touchend')(e)
  },
  // 页面内容事件
  // 切换碉堡内部和外部按键
  FirstView: function () {
    this.setData({
      change: !this.data.change
    })
    let that = this
    if (that.data.change) {
      this.setData({
        view: "2",
        thirdView: true
      })
    } else {
      this.setData({
        view: "1",
        thirdView: false
      })
    }
    that.start()
  },
  // 切换碉堡内部的视角
  // TView: function () {
  //   if (this.data.changeTop) {
  //     this.setData({
  //       view: "3",
  //       changeTop: !this.data.changeTop
  //     })
  //   } else {
  //     this.setData({
  //       view: "2",
  //       changeTop: !this.data.changeTop
  //     })
  //   }
  //   this.start()
  // },
  // 播放视频
  BindShow: function () {
    console.log("点击了")
    this.setData({
      threeLeft: "100%",
      vdisplay: "block",
      back: false
    })
  },
  // 回退上一页
  backIndex: function () {
    wx.navigateBack({
      delta: 0,
    })
  },
  // 视频后退按钮
  cloth: function () {
    console.log("点击了")
    this.setData({
      threeLeft: "0%",
      vdisplay: "none",
      back: true
    })
  },
})
miniprogram-9/pages/ThreeModel/ThreeModel.json
New file
@@ -0,0 +1,5 @@
{
  "navigationStyle":"custom",
  "pageOrientation": "landscape",
  "usingComponents": {}
}
miniprogram-9/pages/ThreeModel/ThreeModel.wxml
New file
@@ -0,0 +1,41 @@
<view style="width: 100%;height: 100%;overflow: hidden;">
<!--pages/hands/hands.wxml-->
<camera frame-size="medium"
        mode="normal"
        device-position="back"
        resolution="high"
        flash="off"/>
<!-- 操作按钮模型角度切换 -->
<view class="button" bindtap="FirstView">
<text class="iconfont icon-view firstView" id="change" bindanimationend="{{ani}}" style="position: absolute; left: 50rpx; top: 49rpx; width: 50rpx; height: 50rpx; display: block; box-sizing: border-box"></text>
<text >视角切换</text>
</view>
<view class="thirdView" wx:if="{{thirdView}}" bindtap="TView">
查看简介
</view>
<!-- 点击截图浏览视频 -->
<view class="playVideo">
<view class="mask">
<text bindtap="BindShow" class="iconfont icon-bofang"></text>
</view>
<image src="http://47.92.33.19:9001/version/OBJ/DB_dm.jpg" ></image>
</view>
<view style="display: {{vdisplay}};width: 100%;height: 100%;position: absolute;top:0;z-index: 350;left: 0;">
<text id="cloth" class="iconfont icon-guanbi"
bindtap="cloth"></text>
<video autoplay="true"
show-mute-btn="true"
bindended="cloth"
vslide-gesture-in-fullscreen="true"
 src="https://o.666visa.cn/video/home.mp4"></video>
</view>
<!-- 模型canvas组件 -->
<canvas style="left: {{threeLeft}};" type="webgl" id="c" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd" bindtouchcancel="touchCancel" bindlongtap="longTap" bindtap="tap"></canvas>
<icon  class="iconfont icon-guanbi" wx:if="{{back}}"   bindtap="backIndex"></icon>
<!-- <text class="iconfont icon-guanbi" wx:if="{{!display}}"></text> -->
</view>
miniprogram-9/pages/ThreeModel/ThreeModel.wxss
New file
@@ -0,0 +1,142 @@
/* pages/ThreeModel/ThreeModel.wxss */
/* pages/hands/hands.wxss */
camera {
  position: absolute;
  width: 100vw;
  height: 100vh;
  z-index: 10;
}
video{
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 380;
}
#c {
  width: 100%;
  height: 100%;
  position: absolute;
  z-index: 100;
}
text{
  display: block;
  width: 50rpx;
  font-size: 12rpx;
  color: white;
  text-align: center;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  bottom: 10rpx;
  z-index: 300;
}
.playVideo{
  position: absolute;
  z-index: 300;
  bottom: 0;
  left: 0;
  width: 200rpx;
  height: 150rpx;
}
image{
  position: absolute;
top: 0;
left: 0;
width: 200rpx;
height: 150rpx;
}
.mask{
  z-index: 300;
  width: 200rpx;
  height: 150rpx;
  position: relative;
  background-color: rgba(0, 0, 0, 0.8);
}
.icon-bofang{
  position: absolute;
  font-size: 50rpx;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-50%);
  margin: auto;
}
.button:active{
  background-color: teal;
   /* transform: rotateZ(180deg); */
}
#change{
  display: block;
  position: absolute;
  transition-duration: 0.1s;
  transform-origin: 0 0;
  width: 50rpx;
  height: 50rpx;
  top: 50%;
  left: 50%;
  transform: translate(-50%,-60%);
  font-size: 50rpx;
  color: white;
}
.video{
  left: 10px;
  top: 100px;
}
.button{
  transition-duration: 1s;
  position: absolute;
  right: 20px;
  top: 50%;
  transform: translateY(-50%);
  height: 100rpx;
  width: 100rpx;
  background-color: rgba(0, 0, 0, 0.3);
  z-index: 300;
  border-radius: 50%;
}
.firstView{
  right: 10px;
  top: 100px;
}
.secondView{
  right: 10px;
  top: 150px;
}
#cloth{
  z-index: 400;
}
.icon-guanbi{
  color: aliceblue;
  z-index: 350;
  position: absolute;
  top: 20rpx;
  left: 20rpx;
  display: block;
  width: 50rpx;
  height: 50rpx;
  font-size: 20rpx;
}
.thirdView{
  position: absolute;
  width: 70rpx;
  height: 70rpx;
  line-height: 70rpx;
  text-align: center;
  border-radius: 50%;
  background-color: rgba(0, 0, 0, 0.4);
  bottom: 20rpx;
  left: 50%;
  transform: translateX(-50%);
  font-size: 12rpx;
  transition-duration: 1s;
  color: white;
  z-index: 300;
}
.thirdView:active{
  background-color: teal;
}
miniprogram-9/pages/index/crsClient.js
New file
@@ -0,0 +1,51 @@
export class CrsClient {
  /**
   * @param config { {cloudKey: string, token: string, clientHost: string, jpegQuality: number} }
   * @param canvas { HTMLCanvasElement }
   */
  constructor(config, canvas) {
      this.config = config;
      this.canvas = canvas;
      this.context = canvas.getContext('2d');
  }
  /**
   * 发起一次CRS请求
   * @param frame { {data: ArrayBuffer, height: number, width: number} } 相机帧
   * @return {Promise}
   */
  queryImage(frame) {
      /*
       * 从相机帧开始,发起一次CRS请求的步骤:
       * 1. 将相机帧画到canvas上
       * 2. 调用canvas.toDataURL得到JPEG图片的base64 (需要小程序基础库2.12.0及以上)
       * 3. 填充请求参数
       * 4. 发送CRS请求并返回
       */
      let ctxImageData = this.context.createImageData(frame.width, frame.height); //#1
      ctxImageData.data.set(new Uint8ClampedArray(frame.data)); //#1
      this.context.putImageData(ctxImageData, 0, 0); //#1
      let dataUrl = this.canvas.toDataURL("image/jpeg", this.config.quality); //#2
      let base64 = dataUrl.substr(23); //#2 去除dataURL头,留下文件内容
      const params = { //#3 添加cloudKey参数
          image: base64,
          notracking: "true",
          appId: this.config.cloudKey,
      };
      return new Promise((resolve, reject) => { //#4 发送CRS请求
          wx.request({
              url: `${this.config.clientHost}/search/`,
              method: 'post',
              data: params,
              header: {
                  'Authorization': this.config.token,
                  'content-type': 'application/json'
              },
              success: res => resolve(res.data),
              fail: err => reject(err),
          });
      });
  }
}
miniprogram-9/pages/index/index.js
New file
@@ -0,0 +1,212 @@
import {CrsClient} from "./crsClient";
const systemInfo = wx.getSystemInfoSync();
const SELECT_TYPE = {
    NONE: 0,
    IMAGE: 1,
    VIDEO: 2,
};
Page({
    data: {
        showOverlay: true,
        showSelect: false,
        SELECT_TYPE: SELECT_TYPE,
        selectType: 0,
        //CRS配置
        config: {
            token: "7iSUp2KvzoX/oFQ0yvSRqRBlxGl4lebWl5aSaAhhvrXW3ISHVVivpPNnF2FzPW92Om19vJyG0gX0/lg+lEGXjSKbokh8vdlmW9JYU8i/Zz1cppifNB7masloftEuWU5/onxFnSmXPLuCtj+lbfZ/cZdYB95cJHCl/Z2nX+gLYOaJeYf8ZXjGIPOe26BvC6VMo2rgBEToebDFcyRina2AjLJeRGohoERyTNdysM0ZqKFLClQs15Fam7FbG3bH3MDYff8CtKoa8H3KrGvfP56xMjnx1wYJ8HCVYji6nmzPHR9MypLEZ6jztWg3/LfCTd1s24FVhRiHq+4t7DVH6iYk71sm317wqLVe+G62uWs7pHN+/CqBEEgQU3CAPus4uFiXTe/AAErZVmg5j5dU8lI64eVnBlHpuPPtpGJrneA0FBegBKa1uknuveHcRPToAhm+Jfs4s8oaf06I0PNl4Inwow==",
            cloudKey: "2c5cecc598d1868d829470f0cfbc6352",
            clientHost: 'https://cn1-crs.easyar.com:8443', //服务器一般不变
            jpegQuality: 0.7, //JPEG压缩质量,建议不低于70%
            minInterval: 1000, //最短的两次CRS请求间隔
        },
        //识别到这个数组中的ID就触发内容
        targetIds: [
            "2839b04f-04b8-447b-8ce2-9ccb2a290d90",
            "7324cccc-ac18-46c6-b3ef-5532e03d50e8",
            "b5f96a1e-40e1-420a-b27f-b7454b7c878d",
        ],
        showLoading: false,
        showLoadingText: "",
    },
    /** @type {CameraFrameListener} 相机帧回调 */
    listener: undefined,
    /** @type {HTMLCanvasElement} canvas对象 */
    canvas: undefined,
    /** @type {boolean} 是否需要持续识别,在点击“识别体验”之后和识别成功之前为true */
    runningCrs: undefined,
    /** @type {boolean} 当前是否正在进行CRS请求 */
    busy: undefined,
    /** @type {CrsClient} 负责发起CRS请求的对象 */
    crsClient: undefined,
    /** @type {number} 最后一次CRS请求的事件,用于判断是否满足最短请求间隔 */
    last: undefined,
    onLoad: function () {
    },
    onReady: function () {
        if (systemInfo.platform === "devtools") { //开发工具不会触发initdone事件,于是在onReady手动触发
            this.onCameraInit();
        }
    },
    showLoading(text) {
        this.setData({
            showLoading: true,
            showLoadingText: text,
        });
    },
    hideLoading() {
        this.setData({
            showLoading: false,
        });
    },
    //图像识别部分:
    onShow: function () {
        if (this.listener) this.listener.start(); //页面隐藏时相机帧的监听会自动停止,但恢复展示时不会自动启动,这里手动启动
    },
    onCameraInit: function () {
        //找到canvas对象
        const query = wx.createSelectorQuery();
        query.select('#capture')
            .fields({node: true})
            .exec((res) => {
                const canvas = res[0].node;
                //设置canvas内部尺寸为480*640,frame-size="medium"的设置下相机帧大多是480*640
                canvas.width = 480;
                canvas.height = 640;
                this.canvas = canvas;
                this.crsClient = new CrsClient(this.data.config, this.canvas);
                //开始监听相机帧
                let cameraContext = wx.createCameraContext();
                this.listener = cameraContext.onCameraFrame(frame => {
                    if (!this.canvas) return;
                    let canvas = this.canvas;
                    //如果尺寸不匹配,就修改canvas尺寸以适应相机帧
                    if (canvas.width !== frame.width || canvas.height !== frame.height) {
                        canvas.width = frame.width;
                        canvas.height = frame.height;
                    }
                    this.queryImage(frame);
                });
                this.listener.start();
            });
    },
    queryImage: function (frame) {
        if (!this.runningCrs || this.busy || !this.crsClient) return;
        //最短的两次CRS请求间隔
        let now = new Date().getTime();
        if (this.last && (now - this.last < this.data.config.minInterval)) return;
        this.last = now;
        this.busy = true; //如果正在进行CRS请求,就不允许再次请求
        this.crsClient.queryImage(frame).then(res => {
            console.log(res,"res")
            if (!this.runningCrs) return; //避免在停止后仍然触发
            let result = res && res.result;
            if (!result) return;
            if (result.target) {
                console.log("识别成功", result.target.targetId);
                //如果待触发的id列表中存在识别到的这个id,就触发
                if (this.data.targetIds.find(targetId => targetId === result.target.targetId)) {
                    console.log("4444444444")
                    this.onResult(result.target);
                }
                // wx.navigateTo({
                //     url: '/pages/hands/hands',
                //   })
            } else {
                console.log("识别失败", result.message);
            }
            this.busy = false;
        }).catch(e => {
            this.busy = false;
            console.log(e);
        }); //小程序iOS端不支持finally,所以在then和catch里分别设置busy = false
    },
    onResult: function (target) {
        this.runningCrs = false;
        this.hideLoading();
        console.log("触发内容!",target);
        if (target.meta) {
            console.log("meta base64:", target.meta);
           wx.navigateTo({
                    url: '/pages/ThreeModel/ThreeModel',
                  })
        }
        this.setData({
            showOverlay: false,
            showContent: true,
            selectType: SELECT_TYPE.IMAGE,
        });
    },
    //界面:
    back: function () {
        this.runningCrs = false;
        this.setData({
            showOverlay: true,
            showContent: false,
            selectType: SELECT_TYPE.NONE,
        });
        this.hideLoading();
    },
    experience: function () {
        this.setData({
            showOverlay: false,
            showContent: true,
            selectType: SELECT_TYPE.IMAGE,
        });
    },
    goThreeD:function(){
        wx.navigateTo({
          url: '/pages/ThreeModel/ThreeModel',
        })
    },
    scan: function () {
        this.runningCrs = true;
        this.setData({
            showOverlay: false,
            showContent: false,
            selectType: SELECT_TYPE.NONE,
        });
        this.showLoading("识别中");
    },
    download: function () {
        wx.saveImageToPhotosAlbum({
            filePath: "/images/namecard.jpg",
            success: res => {
                wx.showToast({title: "已保存到相册", icon: "none"});
            },
            fail: res => {
                wx.showToast({title: "保存失败", icon: "none"});
            },
        });
    },
    selectContent: function (e) {
        this.setData({
            selectType: e.currentTarget.dataset.contenttype,
        });
    },
});
miniprogram-9/pages/index/index.json
New file
@@ -0,0 +1,5 @@
{
  "usingComponents": {
    "loading": "/components/loading/loading"
  }
}
miniprogram-9/pages/index/index.wxml
New file
@@ -0,0 +1,28 @@
<!-- 相机组件,放在UI最底层显示相机内容 -->
<camera frame-size="medium"
        bindinitdone="onCameraInit"
        mode="normal"
        device-position="back"
        resolution="high"
        flash="off"/>
<!-- canvas组件,用于进行图片压缩,位置在屏幕外,不可见 -->
<canvas type="2d"
        id="capture"
        style="width:1px; height: 1px;"/>
<view id="overlay" wx:if="{{showOverlay}}">
    <button bindtap="goThreeD">点击我去3D页面</button>
    <view id="distinguish"  bindtap="scan">
        <text
         class="iconfont icon-yanzhengmashibie" id="icon"></text>
        <text id="scan" class="primary-button">点击识别</text>
    </view>
</view>
<text
bindtap="back"
 class="iconfont icon-guanbi" wx:if="{{showLoading}}"></text>
<view id="loading" wx:if="{{showLoading}}">
    <loading text="{{showLoadingText}}"></loading>
</view>
miniprogram-9/pages/index/index.wxss
New file
@@ -0,0 +1,72 @@
camera {
  position: absolute;
  width: 100vw;
  height: 100vh;
  z-index: 10;
}
#capture {
  position: absolute;
  left: 750rpx;
}
#overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background: rgba(63, 63, 63, 0.6);
  z-index: 30;
}
/* 识别样式 */
#loading {
  position: absolute;
  width: fit-content;
  height: fit-content;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  margin: auto;
  z-index: 40;
}
.icon-guanbi{
  display: block;
  width: 70rpx;
  height: 70rpx;
  position: absolute;
  top: 50rpx;
  left: 50rpx;
  font-size: 70rpx;
  color: black;
  z-index: 200;
}
#distinguish{
  width: 400rpx;
  height: 100rpx;
  background-color: turquoise;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  margin: auto;
  border-radius: 15rpx;
  display: flex;
  justify-content: center;
  align-items: center;
}
#icon{
  display: block;
  width: 60rpx;
  height: 60rpx;
  font-size: 60rpx;
  color: white;
}
miniprogram-9/project.config.json
New file
@@ -0,0 +1,77 @@
{
  "description": "项目配置文件",
  "packOptions": {
    "ignore": []
  },
  "setting": {
    "urlCheck": false,
    "es6": true,
    "enhance": true,
    "postcss": true,
    "preloadBackgroundData": false,
    "minified": true,
    "newFeature": false,
    "coverView": true,
    "nodeModules": false,
    "autoAudits": false,
    "showShadowRootInWxmlPanel": true,
    "scopeDataCheck": false,
    "uglifyFileName": false,
    "checkInvalidKey": true,
    "checkSiteMap": true,
    "uploadWithSourceMap": true,
    "compileHotReLoad": false,
    "lazyloadPlaceholderEnable": false,
    "useMultiFrameRuntime": true,
    "useApiHook": true,
    "useApiHostProcess": true,
    "babelSetting": {
      "ignore": [],
      "disablePlugins": [],
      "outputPath": ""
    },
    "enableEngineNative": false,
    "useIsolateContext": true,
    "userConfirmedBundleSwitch": false,
    "packNpmManually": false,
    "packNpmRelationList": [],
    "minifyWXSS": true,
    "disableUseStrict": false,
    "minifyWXML": true,
    "showES6CompileOption": false,
    "useCompilerPlugins": false
  },
  "compileType": "miniprogram",
  "libVersion": "2.20.1",
  "appid": "wx0a4472ff1d1b0ddd",
  "projectname": "DiaobaoFinallyTest",
  "debugOptions": {
    "hidedInDevtools": []
  },
  "scripts": {},
  "staticServerOptions": {
    "baseURL": "",
    "servePath": ""
  },
  "isGameTourist": false,
  "condition": {
    "search": {
      "list": []
    },
    "conversation": {
      "list": []
    },
    "game": {
      "list": []
    },
    "plugin": {
      "list": []
    },
    "gamePlugin": {
      "list": []
    },
    "miniprogram": {
      "list": []
    }
  }
}
miniprogram-9/sitemap.json
New file
@@ -0,0 +1,7 @@
{
  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
  "rules": [{
  "action": "allow",
  "page": "*"
  }]
}
miniprogram-9/utils/util.js
New file
@@ -0,0 +1,19 @@
const formatTime = date => {
  const year = date.getFullYear()
  const month = date.getMonth() + 1
  const day = date.getDate()
  const hour = date.getHours()
  const minute = date.getMinutes()
  const second = date.getSeconds()
  return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
}
const formatNumber = n => {
  n = n.toString()
  return n[1] ? n : `0${n}`
}
module.exports = {
  formatTime
}