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.