maptalks+three.js+vue webpack項目實現二維地圖上貼三維模型

我們不是走在坑裏就是走在前往坑的路上_(:зゝ∠)_

最終效果如圖:(地圖上添加一個“三維地圖”的toolbar按鈕,點擊後在二維地圖上貼上建好的三維模型點擊顯示彈框)

以下都在已經引入並且初始化maptalks地圖的基礎上,如何引入使用maptalks可以查看博客https://blog.csdn.net/liona_koukou/article/details/84316065

1、安裝maptalks.three包

npm install maptalks.three

2、安裝three包

npm install three

3、安裝obj-loader和mtl-loader包

npm i --save three-obj-mtl-loader

4、引入model模型文件到public下(放在這裏是因爲打包後讀取路徑問題,目前發現放在這裏才能在打包後正確讀取)

5、Vue頁面代碼

引入包

import * as THREE from 'three'
import * as maptalks from 'maptalks'
import { ThreeLayer } from 'maptalks.three'
import { MTLLoader, OBJLoader } from 'three-obj-mtl-loader'

初始化的地圖對象是

this.map

下面是渲染三維模型的方法

// 渲染三維
draw3D() {
  const that = this
  // 三維地圖
  var three_flag = false
  // ///單體化交互開始
  var INTERSECTED
  this.map.on('click', function(e) {
    //    console.log(e)
    var raycaster = new THREE.Raycaster()
    var mouse = new THREE.Vector2()
    const camera = threeLayer.getCamera()
    const scene = threeLayer.getScene()
    if (!scene) return

    const size = that.map.getSize()
    const width = size.width; const height = size.height
    mouse.x = (e.containerPoint.x / width) * 2 - 1
    mouse.y = -((e.containerPoint.y) / height) * 2 + 1

    raycaster.setFromCamera(mouse, camera)
    raycaster.linePrecision = 3

    var intersects = raycaster.intersectObjects(scene.children, true)
    // var intersects = raycaster.intersectObject(points);
    if (!intersects) return
    if (Array.isArray(intersects) && intersects.length === 0) return
    console.log(intersects)
    // 這裏我們操作第一個相交的物體
    if (intersects.length > 0) {
      if (INTERSECTED != intersects[0].object) {
        if (INTERSECTED) {
          // INTERSECTED.material.color.setHex(INTERSECTED.currentHex);
          // INTERSECTED.scale.set(1,1,1);
          if (INTERSECTED.material.length === undefined) {
            INTERSECTED.material.color.setHex(INTERSECTED.currentHex)
          } else {
            for (var i = 0; i < INTERSECTED.material.length; i++) {
              INTERSECTED.material[i].color.setHex(INTERSECTED.currentHex)
            }
          }
        }
        INTERSECTED = intersects[0].object

        // 設置相交的第一個物體的顏色
        // INTERSECTED.currentHex = INTERSECTED.material[0].color.getHex();
        INTERSECTED.currentHex = 16777215
        // 將該物體設爲隨機的其他顏色
        // INTERSECTED.material.opacity = 0.2;

        // INTERSECTED.material.transparent = true;
        // INTERSECTED.material.opacity = 0.2;
        // INTERSECTED.material.needsUpdate = true;
        // INTERSECTED.material.transparent = false;

        // INTERSECTED.material.color.setHex(0xff0000);
        if (INTERSECTED.material.length === undefined) {
          INTERSECTED.material.color.setHex(0x1E90FF)
        } else {
          for (var i = 0; i < INTERSECTED.material.length; i++) {
            INTERSECTED.material[i].color.setHex(0x1E90FF)
          }
        }
      }
      // //////////////
      var lonlat = e.coordinate
      if (true) {
        var options = {
          'autoOpenOn': 'null', // set to null if not to open window when clicking on map
          'single': true,
          'width': 410,
          'height': 190,
          'custom': true,
          'autoCloseOn': 'click',
          'dy': -316,
          'content': '<div class="content build-content">' +
            '<div class="pop-img"><img src="http://pde56fqkk.bkt.clouddn.com/1544760152593.jpg"/><p class="pop-name build-pop-name" id="viewDetial"><span class="text-ellipsis" title="浦軟大廈">浦軟大廈</span><a>詳情<i class="el-icon-arrow-right"></i></a></p></div>' +
            '<div class="pop-txt"><ul><li>入駐企業:<span>12 家</span> </li><li>登記人員:<span>1000 人</span> </li><li>今日訪客:<span>100 人</span> </li><li>登記車輛:<span>500 輛</span> </li><li>實時人數:<span>0 人</span> </li><li>監控點位:<span>0 個</span> </li><li>人臉門禁:<span>0 個</span> </li><li>消防設施:<span>0 個</span></li></ul></div>' +
            '</div>'
        }
        var infoWindow = new maptalks.ui.InfoWindow(options)
        infoWindow.addTo(that.map).show(lonlat)
      }
    } else {
      // 當射線離開的時候變爲原來的顏色
      if (INTERSECTED) {
        // INTERSECTED.material.color.set(INTERSECTED.currentHex);
        if (INTERSECTED.material.length === undefined) {
          INTERSECTED.material.color.setHex(INTERSECTED.currentHex)
        } else {
          for (var i = 0; i < INTERSECTED.material.length; i++) {
            INTERSECTED.material[i].color.setHex(INTERSECTED.currentHex)
          }
          // INTERSECTED.scale.set(1,1,1);
        }
      }
      INTERSECTED = null
    }
    threeLayer.renderScene()
  })

  function closeBox() {
    var theClose = document.getElementById('close_id')
    var cont = document.getElementById('infow')
    cont.style.display = 'none'
  }

  // ///單體化交互結束
  // the ThreeLayer to draw buildings
  // //ThreeLayer初始化
  var threeLayer = new ThreeLayer('t_forbcmp', {
    forceRenderOnMoving: true,
    forceRenderOnRotating: true,
    animation: true
  })

  threeLayer.prepareToDraw = function(gl, scene, camera) {
    var me = this
    // var light = new THREE.PointLight(0xffffff);
    // camera.add(light);
    // let axes=new THREE.AxesHelper(200000000);
    // scene.add(axes);
    var light0 = new THREE.DirectionalLight('#ffffff', 0.5)
    light0.position.set(800, 800, 800).normalize()
    light0.castShadow = true
    camera.add(light0)
    // 環境光
    var light01 = new THREE.AmbientLight('#f7fdf9')
    light01.castShadow = true
    scene.add(light01)
    // var light1 = new THREE.DirectionalLight("#ffffff");
    // light1.position.set(-800,-800,800).normalize();
    // light1.castShadow = true;
    // camera.add(light1);

    // 測試加載obj和mtl貼圖
    // addmtlLoaderTest(13.438186479666001,52.530305072175594);
    // addmtlLoaderTestforMTL(13.436186479666001,52.530305072175594);
    // 相對路徑參數,
    var mtlPath = process.env.BASE_URL + 'model/obj/'
    var mtlName = '3d_puruan_new.mtl'
    var objPath = process.env.BASE_URL + 'model/obj/'
    var objName = '3d_puruan3.obj'
    var objlon = 121.60499979860407
    var objlat = 31.20150084741559
    addLoaderForObj(objlon, objlat, mtlPath, mtlName, objPath, objName)
  }

  threeLayer.addTo(that.map).hide()

  // ////////////////加載模型相關
  // 加載obj+mtl
  function addLoaderForObj(lon, lat, mtlPath, mtlName, objPath, objName) {
    const me = threeLayer
    const scene = me.getScene()
    const scale = -0.0007
    var mtlLoader = new MTLLoader()
    // 加載貼圖mtl
    mtlLoader.setPath(mtlPath)
    mtlLoader.load(mtlName, function(materials) {
      materials.preload()
      var objLoader = new OBJLoader()
      objLoader.setMaterials(materials)
      // 加載模型obj  Math.PI*3/2
      objLoader.setPath(objPath)
      objLoader.load(objName, function(object) {
        object.traverse(function(child) {
          if (child instanceof THREE.Mesh) {
            child.scale.set(scale, scale, scale)
            child.rotation.set(-Math.PI / 2, Math.PI, 0)
            // 賦予基礎材質的顏色,無色(0xFFFFFF)調試色0x0000FF
            for (var i = 0; i < child.material.length; i++) {
              child.material[i].color.setHex(0x0000FF)
            }
          }
        })

        var v = threeLayer.coordinateToVector3(new maptalks.Coordinate(lon, lat))
        object.position.set(v.x, v.y, 0)
        scene.add(object)
        mtlLoaded = true
        threeLayer.renderScene()
      })
      // var mm = new THREE.MeshPhongMaterial({color:0xFF0000});
      // objLoader.setMaterials( mm );
      // objLoader.setMaterials(materials);
    })
  }
  var toolbar = new maptalks.control.Toolbar({
    position: { 'right': 40, 'bottom': 40 },
    items: [
      {
        item: '二三維圖層切換',
        click: function() {
          if (three_flag === false) {
            that.map.animateTo({
              center: [121.6050804009, 31.2015354151],
              zoom: 18,
              pitch: 45
            }, {
              duration: 2000
            })
            threeLayer.show()
            three_flag = true
          } else {
            that.map.animateTo({
              center: [121.6050804009, 31.2015354151],
              zoom: 18,
              pitch: 0
            }, {
              duration: 2000
            })
            threeLayer.hide()
            three_flag = false
          }
          console.log('obj模型')
        }
      }
    ]
  }).addTo(this.map)
}

上面這段代碼需要注意的是模型數據文件的讀取路徑

// 相對路徑參數,
var mtlPath = process.env.BASE_URL + 'model/obj/'
var mtlName = '3d_puruan_new.mtl'
var objPath = process.env.BASE_URL + 'model/obj/'
var objName = '3d_puruan3.obj'

關於process.env.BASE_URL的值可以在vue.config.js裏自定義設置(cli3.0)

baseUrl: process.env.NODE_ENV === 'production' ? '/bcmp-web/' : '/',

關於draw3D的代碼我沒有進行詳細的解釋,如果需要會出一個詳細版的方法使用介紹

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章