Ver Fonte

添加threejs

胡佳骏 há 1 ano atrás
pai
commit
fd8440f0b3
52 ficheiros alterados com 5196 adições e 238 exclusões
  1. 4 1
      miniprogram/app.json
  2. BIN
      miniprogram/assets/image/p1-bg.png
  3. BIN
      miniprogram/assets/image/p1-btbg.png
  4. BIN
      miniprogram/assets/image/p1-bttext.png
  5. BIN
      miniprogram/assets/image/p1-text.png
  6. BIN
      miniprogram/assets/image/saomiao.png
  7. BIN
      miniprogram/assets/image/shibietu.png
  8. BIN
      miniprogram/assets/image/zhaohuan.png
  9. 1 1
      miniprogram/components/xr-ar-threeDof/index.wxml
  10. 33 34
      miniprogram/components/xr-classic-video/index.js
  11. 105 51
      miniprogram/components/xr-classic-video/index.wxml
  12. 5 0
      miniprogram/components/xr-classic-wxball/index.js
  13. 3 0
      miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.json
  14. 66 0
      miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.ts
  15. 2 0
      miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxml
  16. 1 0
      miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxss
  17. 373 0
      miniprogram/pages/2dmarker-ar/2dmarker-ar.js
  18. 5 0
      miniprogram/pages/2dmarker-ar/2dmarker-ar.json
  19. 46 0
      miniprogram/pages/2dmarker-ar/2dmarker-ar.wxml
  20. 144 0
      miniprogram/pages/2dmarker-ar/2dmarker-ar.wxss
  21. 14 20
      miniprogram/pages/ar-classic/scene-classic-video/index.js
  22. 37 10
      miniprogram/pages/ar-classic/scene-classic-video/index.wxml
  23. 29 0
      miniprogram/pages/ar-classic/scene-classic-video/index.wxss
  24. 2 0
      miniprogram/pages/ar-classic/scene-classic-wxball/index.wxml
  25. 3217 0
      miniprogram/pages/ar/loaders/gltf-loader.js
  26. BIN
      miniprogram/pages/ar/plane-ar-v2-marker/add.png
  27. 527 0
      miniprogram/pages/ar/plane-ar-v2-marker/behavior.js
  28. BIN
      miniprogram/pages/ar/plane-ar-v2-marker/move.gif
  29. 93 0
      miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.js
  30. 5 0
      miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.json
  31. 29 0
      miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.wxml
  32. 19 0
      miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.wxss
  33. 0 0
      miniprogram/pages/ar/plane-ar-v2-marker/threejs-miniprogram.js
  34. 139 0
      miniprogram/pages/ar/plane-ar-v2-marker/yuvBehavior.js
  35. 3 0
      miniprogram/pages/gltf/scene-gltf-light-loading.json
  36. 66 0
      miniprogram/pages/gltf/scene-gltf-light-loading.ts
  37. 2 0
      miniprogram/pages/gltf/scene-gltf-light-loading.wxml
  38. 1 0
      miniprogram/pages/gltf/scene-gltf-light-loading.wxss
  39. 3 0
      miniprogram/pages/index.json
  40. 66 0
      miniprogram/pages/index.ts
  41. 2 0
      miniprogram/pages/index.wxml
  42. 1 0
      miniprogram/pages/index.wxss
  43. 5 50
      miniprogram/pages/index/data/index.js
  44. 13 10
      miniprogram/pages/index/index.js
  45. 14 18
      miniprogram/pages/index/index.wxml
  46. 47 41
      miniprogram/pages/index/index.wxss
  47. 3 0
      miniprogram/pages/scene-ar-threeDof/index.json
  48. 66 0
      miniprogram/pages/scene-ar-threeDof/index.ts
  49. 2 0
      miniprogram/pages/scene-ar-threeDof/index.wxml
  50. 1 0
      miniprogram/pages/scene-ar-threeDof/index.wxss
  51. 1 1
      project.config.json
  52. 1 1
      project.private.config.json

+ 4 - 1
miniprogram/app.json

@@ -1,6 +1,9 @@
 {
   "pages": [
-    "pages/ar-classic/scene-classic-video/index"
+    "pages/index/index",
+    "pages/ar-classic/scene-classic-video/index",
+    "pages/ar/scene-ar-threeDof/index",
+    "pages/ar/plane-ar-v2-marker/plane-ar-v2-marker"
   ],
   "usingComponents": {},
   "window": {

BIN
miniprogram/assets/image/p1-bg.png


BIN
miniprogram/assets/image/p1-btbg.png


BIN
miniprogram/assets/image/p1-bttext.png


BIN
miniprogram/assets/image/p1-text.png


BIN
miniprogram/assets/image/saomiao.png


BIN
miniprogram/assets/image/shibietu.png


BIN
miniprogram/assets/image/zhaohuan.png


+ 1 - 1
miniprogram/components/xr-ar-threeDof/index.wxml

@@ -1,4 +1,4 @@
-<xr-scene ar-system="modes:Plane Marker; planeMode: 1;" bind:ready="handleReady" bind:ar-ready="handleARReady">
+<xr-scene ar-system="modes:threeDof ; planeMode: 1;" bind:ready="handleReady" bind:ar-ready="handleARReady">
   <xr-assets bind:progress="handleAssetsProgress" bind:loaded="handleAssetsLoaded">
     <xr-asset-load type="gltf" asset-id="gltf-item" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/just_a_girl/index.glb" />
   </xr-assets>

+ 33 - 34
miniprogram/components/xr-classic-video/index.js

@@ -2,8 +2,13 @@ Component({
   behaviors: [require('../common/share-behavior').default],
   properties: {},
   data: {
+    loadedSaoMiao:false,
     loaded: false,
     arReady: false,
+    loaded2:false,
+    position2:[0,0,0],
+    rot:0,
+    str:"1"
   },
   lifetimes: {
     async attached() {
@@ -15,56 +20,50 @@ Component({
       const xrScene = this.scene = detail.value;
       console.log('xr-scene', xrScene);
     },
-    handleAssetsProgress: function ({detail}) {
-      console.log('assets progress', detail.value);
+    handleAssetsProgress: function({detail}) {
+      this.triggerEvent('assetsProgress', detail.value);
     },
     handleAssetsLoaded: function ({detail}) {
       console.log('assets loaded', detail.value);
+      this.triggerEvent('assetsLoaded', detail.value);
       this.setData({
         loaded: true
       });
+    },handleGltfLoaded2: function() {
+      
     },handleGltfLoaded: function() {
- 
-      const xrScene = this.scene;
-
-      const xrFrameSystem = wx.getXrFrameSystem();
       
-      this.wxball = xrScene.getElementById('wxball');
-
-      this.wxballTransform = this.wxball.getComponent(xrFrameSystem.Transform);
-      this.wxballAnimator = this.wxball.getComponent(xrFrameSystem.Animator);
-
-      this.wxballAnimator.play('gltfAnimation', {
-        loop: -1,
-      });
-
-      this.wxballAnimator.play('gltfAnimation#0', {
-        loop: -1,
-      });
-      this.wxballTransform.visible = true;
     },
     handleTrackerSwitch: function ({detail}) {
+
+
       const active = detail.value;
-      console.log('handleTrackerSwitch', detail);
+      if(active)
+      {
+        this.setData({
+          loaded2: true
+        });
+      }
+    },handleARTrackerState({detail}) {
+      // 事件的值即为`ARTracker`实例
+      const tracker = detail.value;
+      // 获取当前状态和错误信息
+      const {state, errorMessage} = tracker;
 
-      this.wxballAnimator = this.wxball.getComponent(xrFrameSystem.Animator);
+      const element = detail.el;
 
-      this.wxballAnimator.play('gltfAnimation', {
-        loop: -1,
-      });
 
-      this.wxballAnimator.play('gltfAnimation#0', {
-        loop: -1,
-      });
+      if( state==2)
+      {
+
+        this.triggerEvent('assetsstr',"state"  + state);
+        
+/*
+        this.triggerEvent('assetsstr',"x:"  + (180/Math.PI* element.scene.getNodeById("Marker").rotation.x) +"\ny:"  +  (180/Math.PI*element.scene.getNodeById("Marker").rotation.y)+"\nz:"  +  (180/Math.PI*element.scene.getNodeById("Marker").rotation.z));*/
       
-     // const video = this.scene.assets.getAsset('video-texture', 'hikari');
-      if (active) {
-      //  video.play();
-        this.wxballTransform.visible = true;
-      } else {
-     //   video.stop();
-        this.wxballTransform.visible = false;
       }
+
+
     },
     handleAnimationStop: function() {
       console.log('animation Stop');

+ 105 - 51
miniprogram/components/xr-classic-video/index.wxml

@@ -1,83 +1,137 @@
-<xr-scene ar-system="modes:Plane Marker; planeMode: 1;" bind:ready="handleReady" bind:ar-ready="handleARReady">
+<xr-scene ar-system="modes:Marker Plane; planeMode: 1;" bind:ready="handleReady" bind:ar-ready="handleARReady">
   
   <xr-assets bind:progress="handleAssetsProgress" bind:loaded="handleAssetsLoaded">
 
     <xr-asset-load type="env-data" asset-id="env1" src="https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/env-test.bin" />
 
-    <xr-asset-load type="gltf" asset-id="gltf-long" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/1.glb" />
+    <xr-asset-load type="gltf" asset-id="gltf-long" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/2low_poly_dragon_white_dh10.glb" />
     <xr-asset-load type="gltf" asset-id="gltf-yanhua" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/4.glb" />
     <xr-asset-load type="gltf" asset-id="gltf-binglong" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/3.glb" />
     <xr-asset-load type="gltf" asset-id="gltf-lanjiachong" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/5.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-huangfeilong" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/6.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-lanniao" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/7.glb" />
+    <xr-asset-load type="gltf" asset-id="gltf-huangfeilong" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/fourseasonsgriffons_spring.glb" />
+    <xr-asset-load type="gltf" asset-id="gltf-lanniao" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/fourseasonsgriffons_winter.glb" />
     <xr-asset-load type="gltf" asset-id="gltf-denglong" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/8.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-heiguaishou" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/9.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-lvfeilong" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/10.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-jingyu" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/12.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-baiseniu" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/13.glb" />
-    <xr-asset-load type="gltf" asset-id="gltf-baiseniu2" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/14.glb" />
   </xr-assets>
-  <xr-env env-data="env1"/>
-  <xr-node wx:if="{{loaded}}">
-    <xr-ar-tracker mode="Marker" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/1.jpg" bind:ar-tracker-switch="handleTrackerSwitch">
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.001 0.001 0.001" position="0 -1.5 0" model="gltf-long"
-        bind:gltf-loaded="handleGltfLoaded"
-        anim-autoplay
-      />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="1 -1 0" model="gltf-yanhua"
-        bind:gltf-loaded="handleGltfLoaded"
-        anim-autoplay
-      />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="1 1 1" position="-1 0 0" model="gltf-binglong"
-        bind:gltf-loaded="handleGltfLoaded"
-        anim-autoplay
-      />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="30 30 30" position="-1 0 -1" model="gltf-lanjiachong"
-        bind:gltf-loaded="handleGltfLoaded"
-        anim-autoplay
-      />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="30 30 30" position="0 0 -1" model="gltf-huangfeilong"
-        bind:gltf-loaded="handleGltfLoaded"
-        anim-autoplay
-      />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="1 1 1" position="1 0 -1" model="gltf-lanniao"
-        bind:gltf-loaded="handleGltfLoaded"
-        anim-autoplay
-      />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="200 200 200" position="1 0 1" model="gltf-denglong"
-        bind:gltf-loaded="handleGltfLoaded"
+  <xr-env node-id="World" env-data="env1"/>
+
+    <xr-node node-id="MarkerNode" wx:if="{{loaded}}">
+      <xr-ar-tracker node-id="Marker" mode="Marker" src="https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/1.jpg" bind:ar-tracker-switch="handleTrackerSwitch"
+      bind:ar-tracker-state="handleARTrackerState">
+      </xr-ar-tracker>
+      <xr-node  node-id="models" wx:if="{{loaded2}}" >
+        <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="500 500 500" position="-5 0.5 5"    model="gltf-long"
+            anim-autoplay
+          />
+      
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="25 25 25" position="0 -1.5 -40"    model="gltf-binglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="300 300 300" position="-10 0 0"    model="gltf-lanjiachong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="30 30 30" position="10 -1 -10"    model="gltf-huangfeilong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="10 10 10" position="15 -1 0"  rotation="0 180 0"   model="gltf-lanniao" 
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="1 1.5 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="1 1 1"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="0 3 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="3 3 1"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="1 2 3"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="0 1.5 3"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="3 1.4 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="2 1.4 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="2 1.5 2"    model="gltf-denglong"
+            anim-autoplay
+          />
+
+
+
+          
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-1 1.5 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-1 1.7 -1"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="0 1.6 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-3 1.5 -1"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-1 1 -3"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="0 1.5 -3"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-3 1.5 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-2 2 0"    model="gltf-denglong"
+            anim-autoplay
+          />
+          <xr-gltf id="wxball2" node-id="mesh-gltf-wxball2" scale="100 100 100" position="-2 1.5 -2"    model="gltf-denglong"
+            anim-autoplay
+          />
+
+
+          <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="1 1.5 0" model="gltf-yanhua"
         anim-autoplay
       />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="3 3 3" position="0 0 1" model="gltf-heiguaishou"
-        bind:gltf-loaded="handleGltfLoaded"
+          <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="3 1.5 0" model="gltf-yanhua"
         anim-autoplay
       />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.003 0.003 0.003" position="-1 0 1" model="gltf-lvfeilong"
-        bind:gltf-loaded="handleGltfLoaded"
+          <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="3 1.5 3" model="gltf-yanhua"
         anim-autoplay
       />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.03 0.03 0.03" position="-2 0 0" model="gltf-jingyu"
-        bind:gltf-loaded="handleGltfLoaded"
+
+      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="-1 1.5 0" model="gltf-yanhua"
         anim-autoplay
       />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.005 0.005 0.005"position="2 0 0" model="gltf-baiseniu"
-        bind:gltf-loaded="handleGltfLoaded"
+          <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="-3 1.5 0" model="gltf-yanhua"
         anim-autoplay
       />
-      <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="1 1 1"position="0 0 -2" model="gltf-baiseniu2"
-        bind:gltf-loaded="handleGltfLoaded"
+          <xr-gltf id="wxball" node-id="mesh-gltf-wxball" scale="0.1 0.1 0.1" position="-3 1.5 -3" model="gltf-yanhua"
         anim-autoplay
       />
-    </xr-ar-tracker>
-    
+      </xr-node>
+
+
+
+    </xr-node>
+   
     <xr-camera
       id="camera" node-id="camera" position="1 1 1" clear-color="0.925 0.925 0.925 1"
       background="ar" is-ar-camera
     />
-  </xr-node>
   
   <xr-node node-id="lights">
     <xr-light type="ambient" color="1 1 1" intensity="0.3" />
     <xr-light type="directional" rotation="30 60 0" color="1 1 1" intensity="1" />
   </xr-node>
+
+
 </xr-scene>
+
+
+

+ 5 - 0
miniprogram/components/xr-classic-wxball/index.js

@@ -77,6 +77,11 @@ Component({
         this.wxballAnimator.resume('gltfAnimation');
         this.wxballAnimator.resume('gltfAnimation#0');
       }
+    },handleARTrackerState({detail}) {
+      // 事件的值即为`ARTracker`实例
+      const tracker = detail.value;
+      // 获取当前状态和错误信息
+      const {state, errorMessage} = tracker;
     },
     handleAnimationStop: function() {
       console.log('animation Stop');

+ 3 - 0
miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 66 - 0
miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.ts

@@ -0,0 +1,66 @@
+// mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.ts
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload() {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh() {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom() {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage() {
+
+  }
+})

+ 2 - 0
miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxml

@@ -0,0 +1,2 @@
+<!--mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxml-->
+<text>mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxml</text>

+ 1 - 0
miniprogram/mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxss

@@ -0,0 +1 @@
+/* mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/last-gate.txt.wxss */

+ 373 - 0
miniprogram/pages/2dmarker-ar/2dmarker-ar.js

@@ -0,0 +1,373 @@
+import arBehavior from '../behavior/behavior-ar'
+import threeBehavior from '../behavior/behavior-three'
+
+// VK 投影矩阵参数定义
+const NEAR = 0.01
+const FAR = 1000
+
+Component({
+  behaviors: [arBehavior, threeBehavior],
+  data: {
+    theme: 'light',
+    widthScale: 1,      // canvas宽度缩放值
+    heightScale: 0.6,   // canvas高度缩放值
+    markerImgList: [],  // 使用的 marker 列表
+    chooseImgList: [], // 使用的 图片 列表
+    hintBoxList: [],  // 显示提示盒子列表
+  },
+  markerIndex: 0,  // 使用的 marker 索引
+  hintInfo: undefined, // 提示框信息
+  lifetimes: {
+      /**
+      * 生命周期函数--监听页面加载
+      */
+      detached() {
+      console.log("页面detached")
+      if (wx.offThemeChange) {
+        wx.offThemeChange()
+      }
+      },
+      ready() {
+      console.log("页面准备完全")
+        this.setData({
+          theme: wx.getSystemInfoSync().theme || 'light'
+        })
+
+        if (wx.onThemeChange) {
+          wx.onThemeChange(({theme}) => {
+            this.setData({theme})
+          })
+        }
+      },
+  },
+
+  methods: {
+    // 对应案例的初始化逻辑,由统一的 behavior 触发
+    init() {
+      // 初始化 Three.js,用于模型相关的渲染
+      this.initTHREE()
+
+      // 初始化 GL,基于 Three.js 的 Context,用于相机YUV渲染
+      this.initYUV()
+
+      // 初始化VK
+      // start完毕后,进行更新渲染循环
+      this.initVK();
+
+      this.markerIndex = 0;
+
+      // 添加 识别包围盒子
+      this.add3DBox();
+    },
+    initVK() {
+      // VKSession 配置
+      const session = this.session = wx.createVKSession({
+        track: {
+          plane: {
+            mode: 3
+          },
+          marker: true,
+        },
+        version: 'v1',
+        gl: this.gl
+      });
+
+      session.start(err => {
+        if (err) return console.error('VK error: ', err)
+
+        console.log('@@@@@@@@ VKSession.version', session.version)
+
+        //  VKSession EVENT resize
+        session.on('resize', () => {
+          this.calcCanvasSize();
+        })
+
+        // VKSession EVENT addAnchors
+        session.on('addAnchors', anchors => {
+          // console.log("addAnchor", anchors);
+
+          this.left.visible = true;
+          this.right.visible = true;
+          this.top.visible = true;
+          this.bottom.visible = true;
+        })
+
+        // VKSession EVENT updateAnchors
+        session.on('updateAnchors', anchors => {
+          // marker 模式下,目前仅有一个识别目标,可以直接取
+          const anchor = anchors[0];
+          const markerId = anchor.id;
+          const size = anchor.size;
+          this.hintInfo = {
+            markerId: markerId,
+            size: size
+          }
+        })
+        
+        // VKSession removeAnchors
+        // 识别目标丢失时,会触发一次
+        session.on('removeAnchors', anchors => {
+          this.left.visible = false;
+          this.right.visible = false;
+          this.top.visible = false;
+          this.bottom.visible = false;
+
+          if (this.data.hintBoxList && this.data.hintBoxList.length > 0) {
+            // 清理信息
+            this.hintInfo = undefined;
+            // 存在列表的情况,去除remove
+            this.setData({
+              hintBoxList: []
+            });
+          }
+        });
+
+
+        console.log('ready to initloop')
+        // start 初始化完毕后,进行更新渲染循环
+        this.initLoop();
+      });
+
+    },
+    loop() {
+      // console.log('loop')
+
+      // 获取 VKFrame
+      const frame = this.session.getVKFrame(this.canvas.width, this.canvas.height)
+
+      // 成功获取 VKFrame 才进行
+      if(!frame) { return; }
+
+      // 更新相机 YUV 数据
+      this.renderYUV(frame)
+
+      // 获取 VKCamera
+      const VKCamera = frame.camera
+
+      // 相机
+      if (VKCamera) {
+        // 接管 ThreeJs 相机矩阵更新,Marker模式下,主要由视图和投影矩阵改变渲染效果
+        this.camera.matrixAutoUpdate = false
+
+        // 视图矩阵
+        this.camera.matrixWorldInverse.fromArray(VKCamera.viewMatrix);
+        this.camera.matrixWorld.getInverse(this.camera.matrixWorldInverse);
+
+        // 投影矩阵
+        const projectionMatrix = VKCamera.getProjectionMatrix(NEAR, FAR)
+        this.camera.projectionMatrix.fromArray(projectionMatrix)
+        this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix)
+      }
+
+      // 绘制而为提示框的逻辑
+      if (this.hintInfo) {
+        // 存在提示信息,则更新
+        const THREE = this.THREE;
+
+        // 原点偏移矩阵,VK情况下,marker 点对应就是 0 0 0,世界矩阵可以认为是一个单位矩阵
+        // marker 右侧点可以理解是 0.5 0 0
+        const center = new THREE.Vector3();
+        const right = new THREE.Vector3(0.5, 0, 0);
+
+        // 获取设备空间坐标
+        const devicePos = center.clone().project(this.camera);
+
+        // 转换坐标系,从 (-1, 1) 转到 (0, 100),同时移到左上角 0 0,右下角 1 1
+        const screenPos = new THREE.Vector3(0, 0, 0);
+        screenPos.x = devicePos.x * 50 + 50;
+        screenPos.y = 50 - devicePos.y * 50;
+
+        // 获取右侧点信息
+        const deviceRightPos = right.clone().project(this.camera);
+        const screenRightPos = new THREE.Vector3(0, 0, 0);
+        screenRightPos.x = deviceRightPos.x * 50 + 50;
+
+        const markerHalfWidth = screenRightPos.x - screenPos.x;
+        
+        this.setData({
+          hintBoxList: [
+            {
+              markerId: this.hintInfo.markerId,
+              left: screenPos.x - markerHalfWidth,
+              top: screenPos.y - markerHalfWidth,
+              width: markerHalfWidth * this.data.domWidth * 2 / 100,
+              height: markerHalfWidth * this.data.domWidth * 2 / 100,
+            }
+          ]
+        });
+      }
+
+      this.renderer.autoClearColor = false
+      this.renderer.state.setCullFace(this.THREE.CullFaceBack)
+      this.renderer.render(this.scene, this.camera)
+      this.renderer.state.setCullFace(this.THREE.CullFaceNone)
+    },
+    add3DBox() {
+      // 添加marker需要的 三维包围框
+
+      const THREE = this.THREE;
+      const scene = this.scene;
+
+      const material = new THREE.MeshPhysicalMaterial( {
+        metalness: 0.0,
+        roughness: 0.1,
+        color: 0x64f573,
+      } );
+      const geometry = new THREE.BoxGeometry( 1, 1, 1 );
+
+      const borderSize = 0.1;
+
+      const left = new THREE.Mesh( geometry, material );
+      left.position.set(-0.5, 0, 0 );
+      left.rotation.set(-Math.PI / 2, 0, 0 )
+      left.scale.set(borderSize, 1.1, borderSize);
+      scene.add( left );
+      left.visible = false;
+      this.left = left;
+
+      const right = new THREE.Mesh( geometry, material );
+      right.position.set(0.5, 0, 0 );
+      right.rotation.set(-Math.PI / 2, 0, 0 )
+      right.scale.set(borderSize, 1.1, borderSize);
+      scene.add( right );
+      right.visible = false;
+      this.right = right;
+
+      const top = new THREE.Mesh( geometry, material );
+      top.position.set(0, 0, 0.5 );
+      top.rotation.set(0, 0, 0 )
+      top.scale.set(1.1, borderSize, borderSize);
+      scene.add( top );
+      top.visible = false;
+      this.top = top;
+
+      const bottom = new THREE.Mesh( geometry, material );
+      bottom.position.set(0, 0, -0.5 );
+      bottom.rotation.set(0, 0, 0 )
+      bottom.scale.set(1.1, borderSize, borderSize);
+      scene.add( bottom );
+      bottom.visible = false;
+      this.bottom = bottom;
+
+      console.log('add3DBox is finish')
+    },
+    chooseMedia() { 
+      // marker图片上传逻辑
+      wx.chooseMedia({
+        count: 9,
+        mediaType: ['image'],
+        sizeType: ['original'],
+        success: res => {
+          console.log('chooseMedia res', res)
+
+          const chooseImgListRes = [];
+          for (let i = 0; i < res.tempFiles.length; i++) {
+            const imgUrl = res.tempFiles[i].tempFilePath;
+            chooseImgListRes.push(imgUrl);
+          }
+
+          console.log('set chooseImgList', chooseImgListRes)
+          this.setData({
+            chooseImgList: chooseImgListRes,
+          })
+        },
+        fail: res => {
+          console.error(res)
+        }
+      })
+    },
+    async addMarker() {
+      console.log('addMarker')
+      const fs = wx.getFileSystemManager()
+      
+      const markerImgListRes = this.data.markerImgList.concat([]);
+      const preMarkerIndex = this.markerIndex;
+
+      console.log('pre markerImgList', preMarkerIndex, markerImgListRes);
+      
+      // 检查与添加 marker 函数
+      const chooseImgCount = this.data.chooseImgList.length;
+      let handledCount = 0;
+      const checkMarkerAdded = () => {
+        if (handledCount === chooseImgCount) {
+          this.markerIndex = markerImgListRes.length;
+
+          console.log('markerImgList set', markerImgListRes, this.markerIndex);
+          this.setData({
+            chooseImgList: [],
+            markerImgList: markerImgListRes
+          });
+        }
+      }
+      
+      // 准备进行choose的图片保存到fs
+      for (let i = 0; i < chooseImgCount; i++) {
+        const chooseImgUrl = this.data.chooseImgList[i];
+        const fileEnd = chooseImgUrl.split('.').slice(-1)[0];
+        const fileIndex = preMarkerIndex + i;
+        // 算法侧目前只认 map png jpg jpeg 后缀文件
+        const filePath = `${wx.env.USER_DATA_PATH}/marker-ar-${fileIndex}.${fileEnd}`;
+
+        const saveAndAddMarker = () => {
+          console.log('saveFileSync start', filePath, chooseImgUrl);
+          // 存入文件系统,并添加到marker
+          fs.saveFile({
+            filePath,
+            tempFilePath: chooseImgUrl,
+            success: ()=> {
+              console.log('[addMarker] --> ', filePath)
+              const markerId = this.session.addMarker(filePath)
+              markerImgListRes.push({
+                markerId: markerId,
+                filePath: filePath
+              })
+              handledCount++;
+              checkMarkerAdded();
+            },
+            fail: res => {
+              console.error(res)
+              console.log('文件保存失败', filePath);
+              handledCount++;
+              checkMarkerAdded();
+            }
+          })
+        }
+
+        console.log('uploadFile Path', filePath);
+        // 确定文件,存在即删除
+        fs.stat({
+          path: filePath,
+          success: (res) => {
+            if (res.stats.isFile()) {
+              fs.unlinkSync(filePath);
+              console.log('fs unlinkSync', filePath);
+            }
+            saveAndAddMarker();
+          },
+          fail: (res) => {
+            console.error(res)
+            console.log('fs中不存在,直接写入', filePath);
+
+            saveAndAddMarker();
+          }
+        })
+      }
+
+    },
+    removeMarker() {
+      if (this.data.markerImgList) {
+        for (let i = 0; i < this.data.markerImgList.length; i++) {
+          const markerImg = this.data.markerImgList[i];
+          this.session.removeMarker(markerImg.markerId);
+        }
+        this.markerIndex = 0;
+        this.setData({
+          markerImgList: [],
+        })
+      }
+    },
+    getAllMarker() {
+      console.log(this.session.getAllMarker())
+    },
+  },
+})

+ 5 - 0
miniprogram/pages/2dmarker-ar/2dmarker-ar.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {},
+  "disableScroll": true,
+  "navigationBarTitleText": "2DMarkerAR"
+}

+ 46 - 0
miniprogram/pages/2dmarker-ar/2dmarker-ar.wxml

@@ -0,0 +1,46 @@
+
+<view class="page wrap-fix" data-weui-theme="{{theme}}">
+  <view class="canvas-wrap" style="width: {{widthScale * 100}}%; height: {{heightScale * 100}}%">
+    <canvas type="webgl" id="canvas"></canvas>
+    
+    <view class="hint-box"  wx:for="{{hintBoxList}}"  wx:for-item="hintBox" wx:key="hintBoxId"
+      style="left: {{hintBox.left}}%; top: {{hintBox.top}}%; width: {{hintBox.width}}px;  height: {{hintBox.height}}px;"
+    >
+      <p class="hint-id">识别id: {{hintBox.markerId}}</p>
+    </view>
+  </view>
+
+  <view class="hint-words">提示:选择用于识别的图片, 点击添加为marker, 然后手机摄像头对准下方所选图片</view>
+
+  <view class="wrap-option">
+    <button type="primary" disabled="{{markerImgList.length === 0}}" bindtap="removeMarker">删除已有 marker</button>
+    <button type="primary" disabled="{{markerImgList.length === 0}}" bindtap="getAllMarker">获取所有 marker</button>
+  </view>
+  <view class="wrap-marker">
+    <view class="wrap-left">
+      <button type="primary" class="btn-left" disabled="{{chooseImgList.length === 0}}" bindtap="addMarker">添加 marker</button>
+    </view>
+    <view class="wrap-right img-list">
+      <view class="img-list">
+        <image wx:for="{{markerImgList}}"  wx:for-item="markerImg" wx:key="markerIndex" src="{{markerImg.filePath}}" mode="aspectFit" />
+      </view>
+      <view class="hint-empty" wx:if="{{markerImgList.length === 0}}">
+        <p>上传图片后点击左侧按钮<br/>将图片添加到 Marker 识别列表</p>
+      </view>
+    </view>
+  </view>
+  <view class="wrap-choose">
+    <view class="wrap-left">
+      <button type="primary" class="btn-left" bindtap="chooseMedia">选择识别图片</button>
+    </view>
+    <view class="wrap-right">
+      <view class="img-list">
+        <image wx:for="{{chooseImgList}}"  wx:for-item="chooseImg" wx:key="chooseIndex" src="{{chooseImg}}" mode="aspectFit" />
+      </view>
+      <view class="hint-empty" wx:if="{{chooseImgList.length === 0}}">
+        <p>点击左侧按钮上传图片(允许上传1-9张)</p>
+      </view>
+    </view>
+  </view>
+</view>
+

+ 144 - 0
miniprogram/pages/2dmarker-ar/2dmarker-ar.wxss

@@ -0,0 +1,144 @@
+.canvas-wrap {
+  position: relative;
+  width: 100%;
+  background-color: #000;
+}
+
+.canvas-wrap #canvas {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.hint-box {
+  position: absolute;
+  left: 0%;
+  top: 0%;
+  width: 0;
+  height: 0;
+}
+.hint-id {
+  position: absolute;
+  left: -6rpx;
+  right: -6rpx;
+  bottom: 100%;
+  color: #fff;
+  font-size: 20rpx;
+  line-height: 40rpx;
+  text-align: center;
+  text-overflow: ellipsis;
+  background-color: #07c160;
+  border-radius: 8rpx;
+
+}
+
+.wrap-fix {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+}
+
+.hint-words {
+  position: absolute;
+  left: 20rpx;
+  top: 20rpx;
+  right: 20rpx;
+  padding: 10rpx 20rpx;
+  line-height: 40rpx;
+  text-align: center;
+  border-radius: 6rpx;
+  background-color: rgba(0, 0, 0, .3);
+  font-size: 26rpx;
+  color: #fff;
+}
+
+.wrap-left {
+  position: absolute;
+  left: 0;
+  top: 0;
+  bottom: 30rpx;
+  width: 160rpx;
+}
+
+button.btn-left {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  padding: 12rpx;
+  font-size: 30rpx;
+  line-height: 1.2;
+}
+
+.wrap-right {
+  position: absolute;
+  left: 180rpx;
+  right: 20rpx;
+  top: 0;
+  bottom: 30rpx;
+  display: flex;
+}
+
+.img-list {
+  display: flex;
+  overflow: auto;
+}
+
+.img-list image {
+  height: 100%;
+}
+
+.hint-empty {
+  position: relative;
+  flex: 1;
+  border: 4rpx dotted rgba(0, 0, 0, .3);
+  border-radius: 6rpx;
+}
+
+.hint-empty p {
+  position: absolute;
+  left: 20rpx;
+  right: 20rpx;
+  top: 50%;
+  transform: translate(0, -50%);
+  text-align: center;
+  font-size: 26rpx;
+  color: rgba(0, 0, 0, .3);
+}
+
+.wrap-choose {
+  position: absolute;
+  left: 20rpx;
+  right: 20rpx;
+  bottom: 0;
+  height: 15%;
+}
+
+.wrap-marker {
+  position: absolute;
+  left: 20rpx;
+  right: 20rpx;
+  bottom: 15%;
+  height: 15%;
+}
+
+.wrap-option {
+  position: absolute;
+  left: 20rpx;
+  right: 0;
+  bottom: 30%;
+  height: 10%;
+  display: flex;
+}
+
+.wrap-option button {
+  flex: 1;
+  margin: 20rpx 20rpx 20rpx 0;
+  font-size: 30rpx;
+  line-height: 1.2;
+}

+ 14 - 20
miniprogram/pages/ar-classic/scene-classic-video/index.js

@@ -1,26 +1,20 @@
 var sceneReadyBehavior = require('../../behavior-scene/scene-ready');
-var handleDecodedXML = require('../../behavior-scene/util').handleDecodedXML;
-var xmlCode = `&lt;xr-scene ar-system=&quot;modes:Marker&quot; bind:ready=&quot;handleReady&quot;&gt;
-  &lt;xr-assets bind:progress=&quot;handleAssetsProgress&quot; bind:loaded=&quot;handleAssetsLoaded&quot;&gt;
-  &lt;xr-asset-load
-      type=&quot;video-texture&quot; asset-id=&quot;hikari&quot;
-      src=&quot;https://mmbizwxaminiprogram-1258344707.cos.ap-guangzhou.myqcloud.com/xr-frame/demo/xr-frame-team/2dmarker/hikari.mp4&quot; options=&quot;autoPlay:true,loop:true&quot;
-    /&gt;
-  &lt;/xr-assets&gt;
-  &lt;xr-node wx:if=&quot;{{loaded}}&quot;&gt;
-    &lt;xr-ar-tracker mode=&quot;Marker&quot; src=&quot;https://pics5.baidu.com/feed/80cb39dbb6fd526642ca5d3ef8dc3420d5073602.png&quot;&gt;
-      &lt;xr-mesh node-id=&quot;mesh-plane&quot; geometry=&quot;plane&quot; uniforms=&quot;u_baseColorMap: video-hikari&quot; /&gt;
-    &lt;/xr-ar-tracker&gt;
-    &lt;xr-camera
-      id=&quot;camera&quot; node-id=&quot;camera&quot; position=&quot;1 1 1&quot; clear-color=&quot;0.925 0.925 0.925 1&quot;
-      background=&quot;ar&quot; is-ar-camera
-    /&gt;
-  &lt;/xr-node&gt;
-&lt;/xr-scene&gt;`;
 Page({
   behaviors:[sceneReadyBehavior],
-  data: {
-    xmlCode: '<div class="codeWrap">' + handleDecodedXML(xmlCode) + '</div>'
+  handleProgress: function({detail}) {
+    console.log('assets progress', detail);
+
+    this.setData({progressInfo: `${~~(detail.progress * 100)} %\n`});
+  },
+  handleLoaded: function({detail}) {
+    console.log('assets loaded', detail);
+
+    this.setData({loaded: true});
+  },
+  handlestr: function({detail}) {
+    //console.log('assets handlestr', detail);
+
+    this.setData({loadedSaoMiao: true});
   }
 });
 

+ 37 - 10
miniprogram/pages/ar-classic/scene-classic-video/index.wxml

@@ -1,13 +1,40 @@
 <xr-demo-viewer
->
-  <xr-classic-video
-    disable-scroll
-    id="main-frame"
-    width="{{renderWidth}}"
-    height="{{renderHeight}}"
-    style="width:150%;height:100%;display:block;"
-    markerImg="{{markerImg}}"
-    bind:arTrackerState="handleARTrackerState"
-  />
+ style="width: 701rpx; height: 844rpx; display: inline; box-sizing: content-box">
+     <xr-classic-video
+        disable-scroll
+        id="main-frame"
+        width="{{renderWidth}}"
+        height="{{renderHeight}}"
+        style="width:150%;height:100%;display:block;"
+        markerImg="{{markerImg}}"
+        bind:arTrackerState="handleARTrackerState"
+        bind:assetsProgress="handleProgress"
+        bind:assetsLoaded="handleLoaded"
+        bind:assetsstr="handlestr"
+    />
+<!--wx:if="{{!loaded}}"-->
+
+  <view  wx:if="{{!loaded}}" style="position: absolute;display: flex; justify-content: center; align-items: center; left: 0;top: 0;width: 100%;height:100%;background-color: rgba(0, 0, 0,1); text-align: center;line-height: 24px;">
+    <text style="color: white;font-size: 18px;">{{progressInfo}}</text>
+  </view>
+<!--  wx:if="{{loaded&&!loadedSaoMiao}}"-->
+
+
+  <view  wx:if="{{loaded&&!loadedSaoMiao}}" style="position: absolute; display: flex; justify-content: center; align-items: center; left: 146rpx; top: 261rpx; width: 100%; height: 441rpx; background-color: rgba(0, 0, 0,0); text-align: center; line-height: 24px; box-sizing: content-box">
+
+    
+  <image class="saomiao" src='/assets/image/saomiao.png'    style="position: relative; left: -143rpx; top: 92rpx; width: 753rpx; height: 818rpx; display: flex; box-sizing: border-box"></image>
+  </view>
+
+  <view  wx:if="{{loaded&&!loadedSaoMiao}}" style="position: absolute; display: flex; justify-content: center; align-items: center; left: 146rpx; top: 261rpx; width: 100%; height: 441rpx; background-color: rgba(0, 0, 0,0); text-align: center; line-height: 24px; box-sizing: content-box">
+
+    
+<image class="shibietu" src='/assets/image/shibietu.png'    style="position: relative; left: -150rpx; top: 856rpx; width: 750rpx; height: 268rpx; display: flex; box-sizing: border-box"></image>
+</view>
+  <view  style="position: absolute; display: flex; justify-content: center; align-items: center; left: 146rpx; top: 261rpx; width: 100%; height: 441rpx; background-color: rgba(0, 0, 0,0); text-align: center; line-height: 24px; box-sizing: content-box">
+
+    
+<image  wx:if="{{loaded&&loadedSaoMiao}}"  class="zhaohuan" src='/assets/image/zhaohuan.png'    style="position: relative; left: -164rpx; top: 704rpx; width: 554rpx; height: 211rpx; display: flex; box-sizing: border-box"></image>
+</view>
 
 </xr-demo-viewer>

+ 29 - 0
miniprogram/pages/ar-classic/scene-classic-video/index.wxss

@@ -0,0 +1,29 @@
+
+  .saomiao{  
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 150rpx;
+    height: 60rpx;
+  }
+  .zhaohuan{  
+    display: flex;
+    justify-content: end;
+    align-items: center;
+    width: 150rpx;
+    height: 60rpx;
+  }
+  .shibietu{  
+    display: flex;
+    justify-content: end;
+    align-items: center;
+    width: 150rpx;
+    height: 60rpx;
+  }
+  .wenzi{  
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 150rpx;
+    height: 60rpx;
+  }

+ 2 - 0
miniprogram/pages/ar-classic/scene-classic-wxball/index.wxml

@@ -11,6 +11,8 @@
     height="{{renderHeight}}"
     style="width:{{width}}px;height:{{height}}px;top:{{top}}px;left:{{left}}px;display:block;"
     bind:arTrackerState="handleARTrackerState"
+    bind:assetsProgress="handleProgress"
+    bind:assetsLoaded="handleLoaded"
   />
 
   <view wx:if="{{arTrackerShow}}" style="text-align: center;padding: 12px;">

+ 3217 - 0
miniprogram/pages/ar/loaders/gltf-loader.js

@@ -0,0 +1,3217 @@
+export function registerGLTFLoader(THREE) {
+
+  /**
+ * @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
+ */
+
+  THREE.GLTFLoader = (function () {
+
+    function GLTFLoader(manager) {
+
+      this.manager = (manager !== undefined) ? manager : THREE.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 = THREE.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 THREE.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 = THREE.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 = THREE.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 THREE.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 THREE.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 THREE.DirectionalLight(color);
+          lightNode.target.position.set(0, 0, - 1);
+          lightNode.add(lightNode.target);
+          break;
+
+        case 'point':
+          lightNode = new THREE.PointLight(color);
+          lightNode.distance = range;
+          break;
+
+        case 'spot':
+          lightNode = new THREE.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 THREE.MeshBasicMaterial;
+
+    };
+
+    GLTFMaterialsUnlitExtension.prototype.extendParams = function (materialParams, materialDef, parser) {
+
+      var pending = [];
+
+      materialParams.color = new THREE.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: THREE.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 = THREE.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 THREE.ShaderMaterial;
+
+        },
+
+        extendParams: function (materialParams, materialDef, parser) {
+
+          var pbrSpecularGlossiness = materialDef.extensions[this.name];
+
+          var shader = THREE.ShaderLib['standard'];
+
+          var uniforms = THREE.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 THREE.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 THREE.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 THREE.Color(0.0, 0.0, 0.0);
+          materialParams.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
+          materialParams.specular = new THREE.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 THREE.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  {THREE.ShaderMaterial} source
+         * @return {THREE.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) {
+
+      THREE.Interpolant.call(this, parameterPositions, sampleValues, sampleSize, resultBuffer);
+
+    }
+
+    GLTFCubicSplineInterpolant.prototype = Object.create(THREE.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: THREE.NearestFilter,
+      9729: THREE.LinearFilter,
+      9984: THREE.NearestMipmapNearestFilter,
+      9985: THREE.LinearMipmapNearestFilter,
+      9986: THREE.NearestMipmapLinearFilter,
+      9987: THREE.LinearMipmapLinearFilter
+    };
+
+    var WEBGL_WRAPPINGS = {
+      33071: THREE.ClampToEdgeWrapping,
+      33648: THREE.MirroredRepeatWrapping,
+      10497: THREE.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: THREE.InterpolateLinear,
+      STEP: THREE.InterpolateDiscrete
+    };
+
+    var ALPHA_MODES = {
+      OPAQUE: 'OPAQUE',
+      MASK: 'MASK',
+      BLEND: 'BLEND'
+    };
+
+    var MIME_TYPE_FORMATS = {
+      'image/png': THREE.RGBAFormat,
+      'image/jpeg': THREE.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 THREE.MeshStandardMaterial({
+        color: 0xFFFFFF,
+        emissive: 0x000000,
+        metalness: 1,
+        roughness: 1,
+        transparent: false,
+        depthTest: true,
+        side: THREE.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 {THREE.Object3D|THREE.Material|THREE.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 {THREE.BufferGeometry} geometry
+     * @param {Array<GLTF.Target>} targets
+     * @param {GLTFParser} parser
+     * @return {Promise<THREE.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 {THREE.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 THREE.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 THREE.TextureLoader(this.options.manager);
+      this.textureLoader.setCrossOrigin(this.options.crossOrigin);
+
+      this.fileLoader = new THREE.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 THREE.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<THREE.Object3D|THREE.Material|THREE.Texture|THREE.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<THREE.BufferAttribute|THREE.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 THREE.InterleavedBuffer(array, byteStride / elementBytes);
+
+            parser.cache.add(ibCacheKey, ib);
+
+          }
+
+          bufferAttribute = new THREE.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 THREE.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) {
+
+          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 = THREE.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 (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] || THREE.LinearFilter;
+        texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || THREE.LinearMipmapLinearFilter;
+        texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || THREE.RepeatWrapping;
+        texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || THREE.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 = THREE.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  {THREE.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 THREE.PointsMaterial();
+          THREE.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 THREE.LineBasicMaterial();
+          THREE.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 = THREE.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 THREE.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<THREE.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 = THREE.MeshStandardMaterial;
+
+        var metallicRoughness = materialDef.pbrMetallicRoughness || {};
+
+        materialParams.color = new THREE.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 = THREE.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 !== THREE.MeshBasicMaterial) {
+
+        pending.push(parser.assignTexture(materialParams, 'normalMap', materialDef.normalTexture));
+
+        materialParams.normalScale = new THREE.Vector2(1, 1);
+
+        if (materialDef.normalTexture.scale !== undefined) {
+
+          materialParams.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale);
+
+        }
+
+      }
+
+      if (materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {
+
+        pending.push(parser.assignTexture(materialParams, 'aoMap', materialDef.occlusionTexture));
+
+        if (materialDef.occlusionTexture.strength !== undefined) {
+
+          materialParams.aoMapIntensity = materialDef.occlusionTexture.strength;
+
+        }
+
+      }
+
+      if (materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial) {
+
+        materialParams.emissive = new THREE.Color().fromArray(materialDef.emissiveFactor);
+
+      }
+
+      if (materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {
+
+        pending.push(parser.assignTexture(materialParams, 'emissiveMap', materialDef.emissiveTexture));
+
+      }
+
+      return Promise.all(pending).then(function () {
+
+        var material;
+
+        if (materialType === THREE.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 = THREE.sRGBEncoding;
+        if (material.emissiveMap) material.emissiveMap.encoding = THREE.sRGBEncoding;
+        if (material.specularMap) material.specularMap.encoding = THREE.sRGBEncoding;
+
+        assignExtrasToUserData(material, materialDef);
+
+        if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef);
+
+        return material;
+
+      });
+
+    };
+
+    /**
+     * @param {THREE.BufferGeometry} geometry
+     * @param {GLTF.Primitive} primitiveDef
+     * @param {GLTFParser} parser
+     * @return {Promise<THREE.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<THREE.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 THREE.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<THREE.Group|THREE.Mesh|THREE.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 THREE.SkinnedMesh(geometry, material)
+                : new THREE.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 = THREE.TriangleStripDrawMode;
+
+              } else if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN) {
+
+                mesh.drawMode = THREE.TriangleFanDrawMode;
+
+              }
+
+            } else if (primitive.mode === WEBGL_CONSTANTS.LINES) {
+
+              mesh = new THREE.LineSegments(geometry, material);
+
+            } else if (primitive.mode === WEBGL_CONSTANTS.LINE_STRIP) {
+
+              mesh = new THREE.Line(geometry, material);
+
+            } else if (primitive.mode === WEBGL_CONSTANTS.LINE_LOOP) {
+
+              mesh = new THREE.LineLoop(geometry, material);
+
+            } else if (primitive.mode === WEBGL_CONSTANTS.POINTS) {
+
+              mesh = new THREE.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 THREE.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 THREE.PerspectiveCamera(THREE.Math.radToDeg(params.yfov), params.aspectRatio || 1, params.znear || 1, params.zfar || 2e6);
+
+      } else if (cameraDef.type === 'orthographic') {
+
+        camera = new THREE.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<THREE.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 = THREE.NumberKeyframeTrack;
+              break;
+
+            case PATH_PROPERTIES.rotation:
+
+              TypedKeyframeTrack = THREE.QuaternionKeyframeTrack;
+              break;
+
+            case PATH_PROPERTIES.position:
+            case PATH_PROPERTIES.scale:
+            default:
+
+              TypedKeyframeTrack = THREE.VectorKeyframeTrack;
+              break;
+
+          }
+
+          var targetName = node.name ? node.name : node.uuid;
+
+          var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : THREE.InterpolateLinear;
+
+          var targetNames = [];
+
+          if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) {
+
+            // Node may be a THREE.Group (glTF mesh with several primitives) or a THREE.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 THREE.AnimationClip(name, undefined, tracks);
+
+      });
+
+    };
+
+    /**
+     * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
+     * @param {number} nodeIndex
+     * @return {Promise<THREE.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 THREE.Bone();
+
+        } else if (objects.length > 1) {
+
+          node = new THREE.Group();
+
+        } else if (objects.length === 1) {
+
+          node = objects[0];
+
+        } else {
+
+          node = new THREE.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 = THREE.PropertyBinding.sanitizeNodeName(nodeDef.name);
+
+        }
+
+        assignExtrasToUserData(node, nodeDef);
+
+        if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef);
+
+        if (nodeDef.matrix !== undefined) {
+
+          var matrix = new THREE.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<THREE.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 THREE.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 THREE.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 THREE.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;
+
+  })();
+}

BIN
miniprogram/pages/ar/plane-ar-v2-marker/add.png


+ 527 - 0
miniprogram/pages/ar/plane-ar-v2-marker/behavior.js

@@ -0,0 +1,527 @@
+import {
+  createScopedThreejs
+} from './threejs-miniprogram'
+import {
+  registerGLTFLoader
+} from '../loaders/gltf-loader'
+
+const info = wx.getSystemInfoSync()
+ // 此处如果为jpeg,则后缀名也需要改成对应后缀
+// const filePath = `${wx.env.USER_DATA_PATH}/marker-ar1.jpg`
+// const mapfilePath = `${wx.env.USER_DATA_PATH}/marker-ar.jpg`
+
+export default function getBehavior() {
+  return Behavior({
+    data: {
+      width: 1,
+      height: 1,
+      fps: 0,
+      memory: 0,
+      cpu: 0,
+      markerId: [],
+      imgList: [],
+    },
+    methods: {
+      onReady() {
+        wx.createSelectorQuery()
+          .select('#webgl')
+          .node()
+          .exec(res => {
+            this.canvas = res[0].node
+
+            const info = wx.getSystemInfoSync()
+            const pixelRatio = info.pixelRatio
+            const calcSize = (width, height) => {
+              console.log(`canvas size: width = ${width} , height = ${height}`)
+              this.canvas.width = width * pixelRatio
+              this.canvas.height = height * pixelRatio
+              this.setData({
+                width,
+                height,
+              })
+            }
+            calcSize(info.windowWidth, info.windowHeight * 0.6)
+
+          //  this.downloadFile()
+
+            this.initVK()
+          })
+      },
+      onUnload() {
+        if (this._texture) {
+          this._texture.dispose()
+          this._texture = null
+        }
+        if (this.renderer) {
+          this.renderer.dispose()
+          this.renderer = null
+        }
+        if (this.scene) {
+          this.scene.dispose()
+          this.scene = null
+        }
+        if (this.camera) this.camera = null
+        if (this.model) this.model = null
+        if (this._insertModel) this._insertModel = null
+        if (this._insertModels) this._insertModels = null
+        if (this.planeBox) this.planeBox = null
+        if (this.mixers) {
+          this.mixers.forEach(mixer => mixer.uncacheRoot(mixer.getRoot()))
+          this.mixers = null
+        }
+        if (this.clock) this.clock = null
+
+        if (this.THREE) this.THREE = null
+        if (this._tempTexture && this._tempTexture.gl) {
+          this._tempTexture.gl.deleteTexture(this._tempTexture)
+          this._tempTexture = null
+        }
+        if (this._fb && this._fb.gl) {
+          this._fb.gl.deleteFramebuffer(this._fb)
+          this._fb = null
+        }
+        if (this._program && this._program.gl) {
+          this._program.gl.deleteProgram(this._program)
+          this._program = null
+        }
+        if (this.canvas) this.canvas = null
+        if (this.gl) this.gl = null
+        if (this.session) this.session = null
+        if (this.anchor2DList) this.anchor2DList = []
+      },
+      chooseMedia() {
+        wx.chooseMedia({
+          count: 1,
+          mediaType: ['image'],
+          success: res => {
+            console.log('chooseMedia res', res)
+            const imgUrl = res.tempFiles[0].tempFilePath
+            wx.getImageInfo({
+              src: imgUrl,
+              success: res => {
+                const {
+                  width,
+                  height
+                } = res
+                console.log('getImageInfo res', res)
+
+                this.addMarker(imgUrl)
+              },
+              fail: res => {
+                console.error(res)
+              }
+            })
+  
+          },
+          fail: res => {
+            console.error(res)
+          }
+        })
+      },
+      errImg(e){
+        console.log("err img",e)
+      },
+      loadImg(){
+        console.log("load img")
+      },
+      add3DMarker(){
+        const mapfilePath = `${wx.env.USER_DATA_PATH}/marker-ar1.map`
+
+        console.log('now download....')
+
+        const fs = wx.getFileSystemManager()
+  
+        const download = callback => wx.downloadFile({
+            // 此处设置为识别的2d对象的jpg地址
+            url: 'http://dldir1.qq.com/weixin/checkresupdate/ncov_7bf1a739c43f4f80b3fb3488b592f355.map',
+            success(res) {
+              console.log("downloadFile success:", res)
+                fs.saveFile({
+                  mapfilePath,
+                    tempFilePath: res.tempFilePath,
+                    success: callback,
+                })
+            },
+            fail(e) {
+              console.log("downloadFile fail:", e)
+            }
+        })
+
+        const add = () => {
+          console.log('[add3DMarker] --> ', mapfilePath)
+
+          fs.access({
+            path: mapfilePath,
+            success(res) {
+              // 文件存在
+              console.log("文件存在")
+              console.log(res)
+            },
+            fail(res) {
+              // 文件不存在或其他错误
+              console.log("文件不存在")
+              console.error(res)
+            }
+          })
+          this.session.addMarker(mapfilePath)
+        }
+        download(add)
+      },
+      add3DMarker2(){
+        const mapfilePath = `${wx.env.USER_DATA_PATH}/marker-ar.map`
+
+        console.log('now download....')
+
+        const fs = wx.getFileSystemManager()
+  
+        const download = callback => wx.downloadFile({
+            // 此处设置为识别的2d对象的jpg地址
+            url: 'http://dldir1.qq.com/weixin/checkresupdate/ncov_7bf1a739c43f4f80b3fb3488b592f355.map',
+            success(res) {
+              console.log("downloadFile success:", res)
+                fs.saveFile({
+                  mapfilePath,
+                    tempFilePath: res.tempFilePath,
+                    success: callback,
+                })
+            },
+            fail(e) {
+              console.log("downloadFile fail:", e)
+            }
+        })
+
+        const add = () => {
+          console.log('[add3DMarker] --> ', mapfilePath)
+          this.session.addMarker(mapfilePath)
+        }
+        download(add)
+      },
+      addMarker(url) {
+        // if (this.data.markerId) return
+        const fs = wx.getFileSystemManager()
+        // 此处如果为jpeg,则后缀名也需要改成对应后缀
+        // const filePath = `${wx.env.USER_DATA_PATH}/marker-ar.map`
+        const filePath = `${wx.env.USER_DATA_PATH}/marker-ar`+this.data.markerId.length+`.jpeg`
+  
+        const download = callback => {
+          fs.saveFile({
+            filePath,
+            tempFilePath: url,
+            success: callback,
+            fail: res => {
+              console.error(res)
+            }
+          })
+        }
+        const add = () => {
+          console.log('[addMarker'+this.data.markerId.length+'] --> ', filePath)
+          var id = this.session.addMarker(filePath)
+          this.data.markerId.push(id)
+          this.data.imgList.push(filePath)
+
+          this.setData({
+            markerId:this.data.markerId,
+            imgList: this.data.imgList
+          })
+        }
+        download(add)
+      },
+
+      removeAllMarker() {
+        if (this.data.markerId.length!=0) {
+          for(let i = 0; i <this.data.markerId.length; i++){
+          this.session.removeMarker(this.data.markerId[i])
+          }
+          this.data.markerId = []
+          this.data.imgList = []
+          this.setData({
+            markerId: this.data.markerId,
+            imgList: this.data.imgList
+          })
+          this.removeRobot()
+        }
+      },
+      getAllMarker() {
+        console.log(this.session.getAllMarker())
+      },
+      
+      initVK() {
+        // 初始化 threejs
+        this.initTHREE()
+
+        // 自定义初始化
+        if (this.init) this.init()
+
+        console.log('this.gl', this.gl)
+
+        this.session = wx.createVKSession({
+          track: {
+            plane: {
+              mode: 1
+            },
+            marker: true,
+          },
+          version: 'v2',
+          gl: this.gl,
+        })
+
+        const THREE = this.THREE 
+
+
+       const session = this.session
+        session.start(err => {
+          
+          if (err) {
+            this.setData({
+              errMsg:'VK error: ' + err
+            })
+            return console.error('VK error: ', err)
+          }
+
+          console.log('@@@@@@@@ VKSession.version', session.version)
+
+          const canvas = this.canvas
+
+          const calcSize = (width, height, pixelRatio) => {
+            console.log(`canvas size: width = ${width} , height = ${height}`)
+            this.canvas.width = width * pixelRatio
+            this.canvas.height = height * pixelRatio
+            this.setData({
+              width,
+              height,
+            })
+          }
+
+          session.on('resize', () => {
+            const info = wx.getSystemInfoSync()
+            calcSize(info.windowWidth, info.windowHeight * 0.6, info.pixelRatio)
+          })
+
+          const loader = new THREE.GLTFLoader()
+          loader.load('https://dldir1.qq.com/weixin/miniprogram/RobotExpressive_aa2603d917384b68bb4a086f32dabe83.glb', gltf => {
+            this.model = {
+              scene: gltf.scene,
+              animations: gltf.animations,
+            }
+
+            console.log("model加载完成")
+          })
+
+          this.clock = new THREE.Clock()
+
+          loader.load('https://dldir1.qq.com/weixin/miniprogram/reticle_4b6cc19698ca4a08b31fd3c95ce412ec.glb', gltf => {
+            const reticle = this.reticle = gltf.scene
+
+            reticle.visible = false
+            this.scene.add(reticle)
+          })
+
+          this.setData({
+            showTip: true
+          })
+          setTimeout(()=>{
+            this.setData({
+              showTip: false
+            })
+          }, 10000)
+
+          // anchor 检测
+          const createPlane = size => {
+            const geometry = new THREE.PlaneGeometry(size.width, size.height)
+            const material = new THREE.MeshBasicMaterial({
+              color: 0xffffff,
+              side: THREE.DoubleSide,
+              transparent: true,
+              opacity: 0.5
+            })
+            const mesh = new THREE.Mesh(geometry, material)
+            mesh.rotateX(Math.PI / 2)
+            const cnt = new THREE.Object3D()
+            cnt.add(mesh)
+            return cnt
+          }
+          const updateMatrix = (object, m) => {
+            object.matrixAutoUpdate = false
+            object.matrix.fromArray(m)
+          }
+          session.on('addAnchors', anchors => {
+            console.log("add anchor")
+            anchors.forEach(anchor => {
+              console.log("type: ", anchor.type)
+              const size = anchor.size
+              let object
+              if (anchor.type == 0) {
+                 object = createPlane(size)
+                 this.setData({
+                    showTip: false
+                  })
+              } else {
+                if (!this.model) {
+                  console.warn('this.model 还没加载完成 !!!!!')
+                  return
+                }
+                  object = new THREE.Object3D()
+                  const model = this.getRobot()
+                  model.rotateX(-Math.PI / 2)
+                  object.add(model)
+              }
+
+              object._id = anchor.id
+              object._size = size
+              updateMatrix(object, anchor.transform)
+              this.planeBox.add(object)
+            })
+          })
+          session.on('updateAnchors', anchors => {
+            console.log("update")
+            const map = anchors.reduce((temp, item) => {
+              temp[item.id] = item
+              console.log(item.id, item)
+              return temp
+            }, {})
+            this.planeBox.children.forEach(object => {
+              if (object._id && map[object._id]) {
+                const anchor = map[object._id]
+                const size = anchor.size
+                if (size && object._size && (size.width !== object._size.width || size.height !== object._size.height)) {
+                  this.planeBox.remove(object)
+                  object = createPlane(size)
+                  this.planeBox.add(object)
+                }
+
+                object._id = anchor.id
+                object._size = size
+                updateMatrix(object, anchor.transform)
+              }
+            })
+          })
+          session.on('removeAnchors', anchors => {
+             console.log("remove anchor")
+            const map = anchors.reduce((temp, item) => {
+              console.log("type:", item.type)
+              temp[item.id] = item
+              return temp
+            }, {})
+            this.planeBox.children.forEach(object => {
+              if (object._id && map[object._id]) this.planeBox.remove(object)
+            })
+          })
+
+          // 平面集合
+          const planeBox = this.planeBox = new THREE.Object3D()
+          this.scene.add(planeBox)
+
+          //限制调用帧率
+          let fps = 30
+          let fpsInterval = 1000 / fps
+          let last = Date.now()
+
+          // 逐帧渲染
+          const onFrame = timestamp => {
+              let now = Date.now()
+              const mill = now - last
+              // 经过了足够的时间
+              if (mill > fpsInterval) {
+                  last = now - (mill % fpsInterval); //校正当前时间
+                  const frame = session.getVKFrame(canvas.width, canvas.height)
+                  if (frame) {
+                  this.render(frame)
+                  }
+              }
+              session.requestAnimationFrame(onFrame)
+          }
+          session.requestAnimationFrame(onFrame)
+        })
+      },
+      removeRobot(){
+        this.planeBox.children.forEach(object => {
+           this.planeBox.remove(object)
+         })
+      },
+      initTHREE() {
+        const THREE = this.THREE = createScopedThreejs(this.canvas)
+        registerGLTFLoader(THREE)
+
+        // 相机
+        this.camera = new THREE.Camera()
+
+        // 场景
+        const scene = this.scene = new THREE.Scene()
+
+        // 光源
+        const light1 = new THREE.HemisphereLight(0xffffff, 0x444444) // 半球光
+        light1.position.set(0, 0.2, 0)
+        scene.add(light1)
+        const light2 = new THREE.DirectionalLight(0xffffff) // 平行光
+        light2.position.set(0, 0.2, 0.1)
+        scene.add(light2)
+
+        // 渲染层
+        const renderer = this.renderer = new THREE.WebGLRenderer({
+          antialias: true,
+          alpha: true
+        })
+        renderer.gammaOutput = true
+        renderer.gammaFactor = 2.2
+      },
+      updateAnimation() {
+        const dt = this.clock.getDelta()
+        if (this.mixers) this.mixers.forEach(mixer => mixer.update(dt))
+      },
+      copyRobot() {
+        const THREE = this.THREE
+        const {
+          scene,
+          animations
+        } = cloneGltf(this.model, THREE)
+        scene.scale.set(0.05, 0.05, 0.05)
+
+        // 动画混合器
+        const mixer = new THREE.AnimationMixer(scene)
+        for (let i = 0; i < animations.length; i++) {
+          const clip = animations[i]
+          if (clip.name === 'Dance') {
+            const action = mixer.clipAction(clip)
+            action.play()
+          }
+        }
+
+        this.mixers = this.mixers || []
+        this.mixers.push(mixer)
+
+        scene._mixer = mixer
+        return scene
+      },
+      getRobot() {
+        const THREE = this.THREE
+
+        const model = new THREE.Object3D()
+        model.add(this.copyRobot())
+
+        this._insertModels = this._insertModels || []
+        this._insertModels.push(model)
+
+        if (this._insertModels.length > 5) {
+          const needRemove = this._insertModels.splice(0, this._insertModels.length - 5)
+          needRemove.forEach(item => {
+            if (item._mixer) {
+              const mixer = item._mixer
+              this.mixers.splice(this.mixers.indexOf(mixer), 1)
+              mixer.uncacheRoot(mixer.getRoot())
+            }
+            if (item.parent) item.parent.remove(item)
+          })
+        }
+        return model
+      },
+      onTouchEnd(evt) {
+        if (this.scene && this.model && this.reticle) {
+          const model = this.getRobot()
+          model.position.copy(this.reticle.position)
+          model.rotation.copy(this.reticle.rotation)
+          this.scene.add(model)
+        }
+      }
+    },
+  })
+}

BIN
miniprogram/pages/ar/plane-ar-v2-marker/move.gif


+ 93 - 0
miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.js

@@ -0,0 +1,93 @@
+import getBehavior from './behavior'
+import yuvBehavior from './yuvBehavior'
+
+const NEAR = 0.001
+const FAR = 1000
+
+let time = 0;
+let countNumber = 20;
+let count = 0;
+
+Component({
+    behaviors: [getBehavior(), yuvBehavior],
+    data: {
+      theme: 'light',
+    },
+    lifetimes: {
+        /**
+        * 生命周期函数--监听页面加载
+        */
+        detached() {
+        console.log("页面detached")
+        if (wx.offThemeChange) {
+          wx.offThemeChange()
+        }
+        },
+        ready() {
+        console.log("页面准备完全")
+          this.setData({
+            theme: wx.getSystemInfoSync().theme || 'light'
+          })
+  
+          if (wx.onThemeChange) {
+            wx.onThemeChange(({theme}) => {
+              this.setData({theme})
+            })
+          }
+        },
+    },
+    methods: {
+        init() {
+            this.initGL()
+        },
+        render(frame) {
+            let start = Date.now()
+
+            this.renderGL(frame)
+            const camera = frame.camera
+
+            // 修改光标位置
+            const reticle = this.reticle
+            if (reticle) {
+                const hitTestRes = this.session.hitTest(0.5, 0.5)
+                if (hitTestRes.length) {
+                    reticle.matrixAutoUpdate = false
+                    reticle.matrix.fromArray(hitTestRes[0].transform)
+                    reticle.matrix.decompose(reticle.position, reticle.quaternion, reticle.scale)
+                    if(reticle.position.z !=0 )
+                      reticle.visible = true
+                } else {
+                    reticle.visible = false
+                }
+            }
+
+            // 更新动画
+            this.updateAnimation()
+
+            // 相机
+            if (camera) {
+                this.camera.matrixAutoUpdate = false
+                this.camera.matrixWorldInverse.fromArray(camera.viewMatrix)
+                this.camera.matrixWorld.getInverse(this.camera.matrixWorldInverse)
+
+                const projectionMatrix = camera.getProjectionMatrix(NEAR, FAR)
+                this.camera.projectionMatrix.fromArray(projectionMatrix)
+                this.camera.projectionMatrixInverse.getInverse(this.camera.projectionMatrix)
+            }
+
+            this.renderer.autoClearColor = false
+            this.renderer.render(this.scene, this.camera)
+            this.renderer.state.setCullFace(this.THREE.CullFaceNone)
+
+            let end = Date.now()
+
+            time += end-start
+            count++
+            // if(count >= countNumber){
+            //   console.log(`${count}次平均耗时统计为${time/count}ms`)
+            //   count = 0
+            //   time = 0
+            // }
+        },
+    },
+})

+ 5 - 0
miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.json

@@ -0,0 +1,5 @@
+{
+  "usingComponents": {},
+  "disableScroll": true,
+  "navigationBarTitleText": "水平面AR-v2-marker识别"
+}

+ 29 - 0
miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.wxml

@@ -0,0 +1,29 @@
+<view class="container page" data-weui-theme="{{theme}}">
+  <canvas type="webgl" id="webgl" style="width: {{width}}px; height: {{height}}px" bindtouchend="onTouchEnd">
+      <view wx:if="{{errMsg}}" class="page-body-text tc" style="position: absolute; top: {{height / 2}}px; left: {{width / 4}}px;  width:50%; color:red">{{errMsg}}</view>
+      <image wx:if="{{showTip}}" style="position: absolute; top: 25%; left: 25%; width: 50%; height: 50%;" src="./move.gif"></image>
+    </canvas>
+
+  <cover-view class="btn-cnt">
+      <!-- <button type="primary" style="width: 50vw;" bindtap="add3DMarker">添加3D marker</button>
+      <button type="primary" style="width: 50vw;" bindtap="add3DMarker2">添加3D marker 2</button> -->
+      <button type="primary" disabled="{{markerId.length == 0}}"  style="width: 50vw;" bindtap="removeAllMarker">删除全部 marker</button>
+      <button type="primary" disabled="{{markerId.length == 0}}"  style="width: 50vw;" bindtap="getAllMarker">获取所有 marker</button>
+  </cover-view>
+
+
+  <view class="photo-cnt">
+    <view class="page-body-text tc" style="height:20%">提示:触碰屏幕任意点, 光标标记指示的是真实水平面,触碰平面可以生成机器人,通过点击加号添加多张图片,进行marker识别</view>
+  </view>
+
+  <scroll-view class="scroll-view_H" scroll-x type="list" enable-flex style="width: 100%; height: 20vh; display:flex;">
+  <view>
+    <image style="border-radius: 15px; width: 30vw; height: 30vw; margin-left: 0%" bindtap="chooseMedia" src="./add.png" />
+  </view>  
+  <view wx:for="{{imgList}}" wx:for-index="imgIndex" wx:for-item="imgItem">
+      <image  src="{{imgItem}}" wx:if="{{imgItem}}" style=" border-radius: 15px; width: 30vw; height: 30vw; margin-left: 25 + {{imgIndex}} * 25%" mode="aspectFit" binderror="errImg" bindload="loadImg" data-err-img="{{imgIndex}}" />
+  </view>
+      
+  </scroll-view>
+
+</view>

+ 19 - 0
miniprogram/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker.wxss

@@ -0,0 +1,19 @@
+.btn-cnt {
+  display: flex;
+}
+
+.photo-cnt {
+position: absolute;
+margin-top: 60vh;
+}
+
+.btn-choose {
+flex:0 50%;
+display: flex;
+height: 5vh;
+margin-bottom: 5vh;
+}
+
+.scroll-view_H {
+    white-space: nowrap;
+  }

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
miniprogram/pages/ar/plane-ar-v2-marker/threejs-miniprogram.js


+ 139 - 0
miniprogram/pages/ar/plane-ar-v2-marker/yuvBehavior.js

@@ -0,0 +1,139 @@
+const yuvBehavior = Behavior({
+    methods: {
+        initShader() {
+            const gl = this.gl = this.renderer.getContext()
+            const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM)
+            const vs = `
+        attribute vec2 a_position;
+        attribute vec2 a_texCoord;
+        uniform mat3 displayTransform;
+        varying vec2 v_texCoord;
+        void main() {
+          vec3 p = displayTransform * vec3(a_position, 0);
+          gl_Position = vec4(p, 1);
+          v_texCoord = a_texCoord;
+        }
+      `
+            const fs = `
+        precision highp float;
+
+        uniform sampler2D y_texture;
+        uniform sampler2D uv_texture;
+        varying vec2 v_texCoord;
+        void main() {
+          vec4 y_color = texture2D(y_texture, v_texCoord);
+          vec4 uv_color = texture2D(uv_texture, v_texCoord);
+
+          float Y, U, V;
+          float R ,G, B;
+          Y = y_color.r;
+          U = uv_color.r - 0.5;
+          V = uv_color.a - 0.5;
+          
+          R = Y + 1.402 * V;
+          G = Y - 0.344 * U - 0.714 * V;
+          B = Y + 1.772 * U;
+          
+          gl_FragColor = vec4(R, G, B, 1.0);
+        }
+      `
+            const vertShader = gl.createShader(gl.VERTEX_SHADER)
+            gl.shaderSource(vertShader, vs)
+            gl.compileShader(vertShader)
+
+            const fragShader = gl.createShader(gl.FRAGMENT_SHADER)
+            gl.shaderSource(fragShader, fs)
+            gl.compileShader(fragShader)
+
+            const program = this._program = gl.createProgram()
+            this._program.gl = gl
+            gl.attachShader(program, vertShader)
+            gl.attachShader(program, fragShader)
+            gl.deleteShader(vertShader)
+            gl.deleteShader(fragShader)
+            gl.linkProgram(program)
+            gl.useProgram(program)
+
+            const uniformYTexture = gl.getUniformLocation(program, 'y_texture')
+            gl.uniform1i(uniformYTexture, 5)
+            const uniformUVTexture = gl.getUniformLocation(program, 'uv_texture')
+            gl.uniform1i(uniformUVTexture, 6)
+
+            this._dt = gl.getUniformLocation(program, 'displayTransform')
+            gl.useProgram(currentProgram)
+        },
+        initVAO() {
+            const gl = this.renderer.getContext()
+            const ext = gl.getExtension('OES_vertex_array_object')
+            this.ext = ext
+
+            const currentVAO = gl.getParameter(gl.VERTEX_ARRAY_BINDING)
+            const vao = ext.createVertexArrayOES()
+
+            ext.bindVertexArrayOES(vao)
+
+            const posAttr = gl.getAttribLocation(this._program, 'a_position')
+            const pos = gl.createBuffer()
+            gl.bindBuffer(gl.ARRAY_BUFFER, pos)
+            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, -1, 1, 1, -1, -1, -1]), gl.STATIC_DRAW)
+            gl.vertexAttribPointer(posAttr, 2, gl.FLOAT, false, 0, 0)
+            gl.enableVertexAttribArray(posAttr)
+            vao.posBuffer = pos
+
+            const texcoordAttr = gl.getAttribLocation(this._program, 'a_texCoord')
+            const texcoord = gl.createBuffer()
+            gl.bindBuffer(gl.ARRAY_BUFFER, texcoord)
+            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 1, 0, 1, 1, 0, 0, 0]), gl.STATIC_DRAW)
+            gl.vertexAttribPointer(texcoordAttr, 2, gl.FLOAT, false, 0, 0)
+            gl.enableVertexAttribArray(texcoordAttr)
+            vao.texcoordBuffer = texcoord
+
+            ext.bindVertexArrayOES(currentVAO)
+            this._vao = vao
+        },
+        initGL() {
+            this.initShader()
+            this.initVAO()
+        },
+        renderGL(frame) {
+            const gl = this.renderer.getContext()
+            gl.disable(gl.DEPTH_TEST)
+            const {
+                yTexture,
+                uvTexture
+            } = frame.getCameraTexture(gl, 'yuv')
+            const displayTransform = frame.getDisplayTransform()
+            if (yTexture && uvTexture) {
+                const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM)
+                const currentActiveTexture = gl.getParameter(gl.ACTIVE_TEXTURE)
+                const currentVAO = gl.getParameter(gl.VERTEX_ARRAY_BINDING)
+
+                gl.useProgram(this._program)
+                this.ext.bindVertexArrayOES(this._vao)
+
+                gl.uniformMatrix3fv(this._dt, false, displayTransform)
+                gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1)
+
+                gl.activeTexture(gl.TEXTURE0 + 5)
+                const bindingTexture5 = gl.getParameter(gl.TEXTURE_BINDING_2D)
+                gl.bindTexture(gl.TEXTURE_2D, yTexture)
+
+                gl.activeTexture(gl.TEXTURE0 + 6)
+                const bindingTexture6 = gl.getParameter(gl.TEXTURE_BINDING_2D)
+                gl.bindTexture(gl.TEXTURE_2D, uvTexture)
+
+                gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)
+
+                gl.bindTexture(gl.TEXTURE_2D, bindingTexture6)
+                gl.activeTexture(gl.TEXTURE0 + 5)
+                gl.bindTexture(gl.TEXTURE_2D, bindingTexture5)
+
+                gl.useProgram(currentProgram)
+                gl.activeTexture(currentActiveTexture)
+                this.ext.bindVertexArrayOES(currentVAO)
+            }
+        },
+    },
+})
+
+export default yuvBehavior

+ 3 - 0
miniprogram/pages/gltf/scene-gltf-light-loading.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 66 - 0
miniprogram/pages/gltf/scene-gltf-light-loading.ts

@@ -0,0 +1,66 @@
+// pages/gltf/scene-gltf-light-loading.ts
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload() {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh() {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom() {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage() {
+
+  }
+})

+ 2 - 0
miniprogram/pages/gltf/scene-gltf-light-loading.wxml

@@ -0,0 +1,2 @@
+<!--pages/gltf/scene-gltf-light-loading.wxml-->
+<text>pages/gltf/scene-gltf-light-loading.wxml</text>

+ 1 - 0
miniprogram/pages/gltf/scene-gltf-light-loading.wxss

@@ -0,0 +1 @@
+/* pages/gltf/scene-gltf-light-loading.wxss */

+ 3 - 0
miniprogram/pages/index.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 66 - 0
miniprogram/pages/index.ts

@@ -0,0 +1,66 @@
+// pages/index.ts
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload() {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh() {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom() {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage() {
+
+  }
+})

+ 2 - 0
miniprogram/pages/index.wxml

@@ -0,0 +1,2 @@
+<!--pages/index.wxml-->
+<text>pages/index.wxml</text>

+ 1 - 0
miniprogram/pages/index.wxss

@@ -0,0 +1 @@
+/* pages/index.wxss */

+ 5 - 50
miniprogram/pages/index/data/index.js

@@ -129,55 +129,10 @@ const ar = {
   name: 'AR能力',
   tag: 'func',
   childArr: [{
-      name: 'Camera',
-      label: 'AR 相机渲染',
-      path: '/pages/ar/scene-ar-camera/index',
-    },
-    {
-      name: 'Basic',
-      label: 'AR 平面识别',
-      path: '/pages/ar/scene-ar-basic/index',
-    },
-    {
-      name: 'ThreeDof',
-      label: 'AR 相机旋转',
-      path: '/pages/ar/scene-ar-threeDof/index',
-    },
-    {
-      name: '2DMarker',
-      label: 'AR 2DMarker',
-      path: '/pages/ar/scene-ar-2dmarker/index',
-    },
-    {
-      name: 'OSDMarker',
-      label: 'AR OSDMarker',
-      path: '/pages/ar/scene-ar-osdmarker/index',
-    },
-    {
-      name: 'Face',
-      label: 'AR 人脸',
-      path: '/pages/ar/scene-ar-face/index',
-    },
-    {
-      name: 'Hand',
-      label: 'AR 人手',
-      path: '/pages/ar/scene-ar-hand/index',
-    },
-    {
-      name: 'Body',
-      label: 'AR 人体',
-      path: '/pages/ar/scene-ar-body/index',
-    },
-    {
-      name: 'Plane+Marker',
-      label: 'AR 平面 Marker融合',
-      path: '/pages/ar/scene-ar-vio-marker/index',
-    },
-    {
-      name: 'Plane+Depth',
-      label: 'AR 平面深度剔除',
-      path: '/pages/ar/scene-ar-vio-depth/index',
-    },
+    name: '扫图',
+    label: 'AR ',
+    path: '/pages/ar-classic/scene-classic-video/index',
+  }
   ],
 };
 
@@ -511,4 +466,4 @@ const production = {
   ]
 };
 
-export default [base, ar, gltf, physics, particle, customParticle, custom, messageTemplate, arTemplate, customTemplate, gltfEditTemplate, toolTemplate, classic, scan, production];
+export default [ar];

+ 13 - 10
miniprogram/pages/index/index.js

@@ -1,3 +1,4 @@
+/*
 // 自定义组件
 import '../../xr-custom/components/AutoRotate';
 import '../../xr-custom/components/Particle/SystemProperty/BasicParticle';
@@ -12,7 +13,7 @@ import '../../xr-custom/elements/xr-shining-star';
 // 自定义effect
 import '../../xr-custom/assets/effect-shining';
 import '../../xr-custom/assets/effect-last-record-final';
-
+*/
 import list from './data/index';
 
 let lastOpened = false;
@@ -33,13 +34,6 @@ Page({
       first = false;
     }
     
-    wx.navigateTo({
-      url: "/pages/ar-classic/scene-classic-video/index?path=/pages/ar-classic/scene-classic-video/index",
-      success: () => {
-      },
-      fail: () => {
-      },
-    });
   },
   onShareAppMessage() {
     return {
@@ -52,9 +46,18 @@ Page({
     }
   },
   clickHandle(e) {
-    let root = this.data.root;
-    let { name, path } = e.detail.item;
 
+
+    wx.navigateTo({
+     url: '/pages/ar/plane-ar-v2-marker/plane-ar-v2-marker',
+   // url: '/pages/ar-classic/scene-classic-video/index',
+      success: () => {
+      },
+      fail: () => {
+      },
+    });
+    return;
+    
     if (!path) {
       return;
     }

+ 14 - 18
miniprogram/pages/index/index.wxml

@@ -1,19 +1,15 @@
+<!-- pages/index/index.wxml -->
+
+<view class="page-background">
+
+  <image class="text" src='/assets/image/p1-text.png'    style="position: relative; left: 126rpx; top: 421rpx"></image>
+
+
+
+
+
+<button class="tapbtn1" plain="true" bindtap="clickHandle"  style="position: relative; left: 1rpx; top: 1110rpx">
+  <image class="tapimg1" src='/assets/image/p1-btbg.png' ></image>
+</button>
+</view>
 
-<scroll-view class="main" scroll-y="{{true}}">
-  <view class="slogen-wrap" bind:tap="handleLastRecord">
-    <view class="slogen">XR<view class="special">Frame</view></view>
-    <view class="intro">
-      小程序低成本高性能XR解决方案
-    </view>
-  </view>
-
-  <pull-down-list
-    wx:for="{{list}}"
-    name="{{item.name}}"
-    tag="{{item.tag}}"
-    root="{{root}}"
-    wx:key="name"
-    childArr="{{item.childArr}}"
-    bind:click="clickHandle"
-  />
-</scroll-view>

+ 47 - 41
miniprogram/pages/index/index.wxss

@@ -1,46 +1,52 @@
+page {
 
-.main {
-  position: absolute;
-  left: 0;
-  top: 0;
   width: 100%;
+  
   height: 100%;
-  box-sizing: border-box;
-  padding-left: 32rpx;
-  padding-right: 32rpx;
-  padding-top: 48rpx;
-  padding-bottom: 80rpx;
-}
-.slogen-wrap {
-  padding-top: 100rpx;
-  padding-left: 16rpx;
-  padding-right: 16rpx;
-  margin-bottom: 48rpx;
-}
-.slogen {
-  text-align: left;
-  font-size: 70rpx;
-  font-weight: bold;
-  color: rgb(115, 239, 43);
-  line-height: 80rpx;
-}
-.slogen .special {
-  display: inline-block;
-  font-size: 80rpx;
-  font-weight: 300;
-  color: #000;
-}
+  
+  background-size: 100% 100%;
+  
+  position: relative;
+  
+  color: #fff;
+  
+  background-image: url('https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/p1-bg.png');
+  
+  }
 
-.intro {
-  text-align: left;
-  font-size: 28rpx;
-  color: rgba(0, 0, 0, 0.4);
-}
+  .btbg{
+    background-image: url('https://wx-model-1317557471.cos.ap-shanghai.myqcloud.com/p1-btbg.png');
+  }
+
+  .text{
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 500rpx;
+    height: 90rpx;
+  }
 
-.nav-btn {
-  text-align: center;
-  background: #eeeeee;
-  margin: 20rpx 0;
-  padding: 30rpx;
-  border-radius: 5px;
-}
+  .bttext{  
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    width: 150rpx;
+    height: 60rpx;
+  }
+  /**index.wxss**/
+ 
+.tapimg1{  
+  display: flex;
+  justify-content:end;
+  align-items: center;
+  width: 220rpx;
+  height: 220rpx;
+}
+ 
+.tapbtn1[plain]{
+  padding: 0;
+  border:none;   
+  width: 220rpx;
+  height: 220rpx;
+}
+  

+ 3 - 0
miniprogram/pages/scene-ar-threeDof/index.json

@@ -0,0 +1,3 @@
+{
+  "usingComponents": {}
+}

+ 66 - 0
miniprogram/pages/scene-ar-threeDof/index.ts

@@ -0,0 +1,66 @@
+// pages/scene-ar-threeDof/index.ts
+Page({
+
+  /**
+   * 页面的初始数据
+   */
+  data: {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面加载
+   */
+  onLoad() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面初次渲染完成
+   */
+  onReady() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面显示
+   */
+  onShow() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面隐藏
+   */
+  onHide() {
+
+  },
+
+  /**
+   * 生命周期函数--监听页面卸载
+   */
+  onUnload() {
+
+  },
+
+  /**
+   * 页面相关事件处理函数--监听用户下拉动作
+   */
+  onPullDownRefresh() {
+
+  },
+
+  /**
+   * 页面上拉触底事件的处理函数
+   */
+  onReachBottom() {
+
+  },
+
+  /**
+   * 用户点击右上角分享
+   */
+  onShareAppMessage() {
+
+  }
+})

+ 2 - 0
miniprogram/pages/scene-ar-threeDof/index.wxml

@@ -0,0 +1,2 @@
+<!--pages/scene-ar-threeDof/index.wxml-->
+<text>pages/scene-ar-threeDof/index.wxml</text>

+ 1 - 0
miniprogram/pages/scene-ar-threeDof/index.wxss

@@ -0,0 +1 @@
+/* pages/scene-ar-threeDof/index.wxss */

+ 1 - 1
project.config.json

@@ -57,7 +57,7 @@
     "ignore": [],
     "include": []
   },
-  "appid": "wx0bcdf49b26e53b96",
+  "appid": "wx9a280bbd81c64f56",
   "projectname": "xr-frame-demo",
   "libVersion": "3.0.0"
 }

+ 1 - 1
project.private.config.json

@@ -1,5 +1,5 @@
 {
-  "projectname": "xr-frame-demo",
+  "projectname": "WXXR",
   "setting": {
     "compileHotReLoad": true,
     "useIsolateContext": true

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff