The maptalks+three.js+vue webpack project implements 3D models on 2D maps

We are either walking in the pit or on the way to the pit_(:зゝ∠)_

The final effect is as shown in the figure: (add a "3D map" toolbar button on the map, click to paste the built 3D model on the 2D map and click to display the bullet box)

The following are based on the introduction and initialization of the maptalks map. How to introduce and use maptalks can be viewed on the blog https://blog.csdn.net/liona_koukou/article/details/84316065

1. Install the maptalks.three package

npm install maptalks.three

2. Install the three package

npm install three

3. Install obj-loader and mtl-loader packages

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

4. Introduce the model model file to the public (it is placed here because of the reading path problem after packaging, and it is currently found that it can be read correctly after packaging)

5. Vue page code

import package

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

The initialized map object is

this.map

Here's how to render a 3D model

// 渲染三维
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)
}

The above code needs to pay attention to the reading path of the model data file

// 相对路径参数,
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'

The value of process.env.BASE_URL can be customized in vue.config.js (cli3.0)

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

I didn't give a detailed explanation about the code of draw3D. If necessary, a detailed version of the method will be introduced.

Guess you like

Origin blog.csdn.net/liona_koukou/article/details/85231410