在平常的工作中,难免会用到倾斜摄影,当加载倾斜摄影的时候,最头疼的就是倾斜摄影的偏移问题,在代码中进行修改加载倾斜摄影的偏移参数,虽然简单但过于麻烦,也耽误开发的效率,因此我就本着能不能在三维场景中对倾斜摄影进行手动操作,无需再改代码并可将倾斜摄影放在较为正确的位置。为了实现这个功能,我就从网上找一些相关的资料,参照一些网上的大佬的代码进行修改,并补充相关知识,并不是那么及时的完成了这篇文章。希望大家多多收藏和点赞。
下文就是本文的实现思路和代码,并对文中的代码做出一定的注释,对于文章有不对的地方也希望大家进行指正,有大家的参与我才会有动力继续写下去,虽然产量微乎其微😄,但我希望对大家有帮助啊,取之开源用之开源啊。
接下来就开始直入主题啊,首先是绘制旋转标识器和平移标识器,毕竟有方向才好进行辨别啊。
移动指示器的绘制比较简单,通过Cesium.PolylineCollection()
将指示器的三个方向方向添加到其中,并将绘制形成的指示器通过primitive
的方式进行添加。 代码主要思路如下:
javascript
/**
* @description 绘制箭头
* @param {object} originDegree 初始点坐标点
* @param {object} targetDegree 目标点坐标点
*/
initLineArrow(originDegree, targetDegree, length) {
const arrows = new Cesium.PolylineCollection()
//坐标系方向存放原始坐标和目的点坐标
const xPos = [
originDegree.lng,originDegree.lat,originDegree.alt,
targetDegree.lng,originDegree.lat,originDegree.alt
]
const xArrow = this.drawArrow(arrows,'model_edit_xArrow',xPos,Cesium.Color.GREEN)
const yPos = [
originDegree.lng,originDegree.lat,originDegree.alt,
originDegree.lng,targetDegree.lat,originDegree.alt
]
const yArrow = this.drawArrow(arrows,'model_edit_yArrow',yPos,Cesium.Color.BLUE)
const zPos = [
originDegree.lng,originDegree.lat,originDegree.alt,
originDegree.lng,originDegree.lat,targetDegree.alt
]
const zArrow = this.drawArrow(arrows,'model_edit_zArrow',zPos,Cesium.Color.RED)
this._coordArrows = this._viewer.scene.primitives.add(arrows)
this._coordArrows._name = 'CoordAxis'
}
/**
* @description 绘制空间坐标轴(移动指示器)
* @param {Cesium.Collection} arrows 坐标轴集合
* @param {string} name xyz轴名称
* @param {Array} positions 坐标系位置
* @param {Cesium.Color} color 坐标系颜色
*/
drawArrow(arrows, name, positions, color) {
const arrow = arrows.add({
positions: Cesium.Cartesian3.fromDegreesArrayHeights(positions),
width: this._defaultWidth,
material: Cesium.Material.fromType(Cesium.Material.PolylineArrowType, {
color: color
})
})
arrow._name = name
}
移动指示器的绘制原理也类似于移动指示器的绘制,通过Cesium.PolylineCollection()
将指示器的三个旋转方向添加到其中,并将绘制形成的指示器通过primitive
的方式进行添加。
javascript
/**
* @description 绘制旋转转轴
* @param {string} name 转轴的名字
* @param {Array} position 转轴的位置
* @param {Cesium.Matrix4} matrix 东北坐标系矩阵
* @param {Cesium.Color} color 转轴颜色
* @returns
*/
createAxisSphere(name, position, matrix, color) {
let result = new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
id: name,
geometry: new Cesium.PolylineGeometry({
positions: position,
width: 5
}),
attributes: {
color: Cesium.ColorGeometryInstanceAttribute.fromColor(color)
}
}),
releaseGeometryInstances: false,
appearance: new Cesium.PolylineColorAppearance({
translucent: false
}),
modelMatrix: matrix
})
result._name = name
this._coordCircle.push(result)
return result
}
/**
* @description 绘制旋转指示器
* @param {number} lng 经度 单位:度
* @param {number} lat 纬度 单位:度
* @param {number} height 高度 单位:米
* @param {number} radius 半径 单位:米
*/
createCircle(lng, lat, height, radius) {
const position = []
for (let i = 0; i <= 360; i += 3) {
const sin = Math.sin(Cesium.Math.toRadians(i))
const cos = Math.cos(Cesium.Math.toRadians(i))
const x = radius * cos
const y = radius * sin
position.push(new Cesium.Cartesian3(x, y, 0))
}
const matrix = Cesium.Transforms.eastNorthUpToFixedFrame(
new Cesium.Cartesian3.fromDegrees(lng, lat, height)
)
//绕Z轴
const axisSphereZ = this.createAxisSphere(
'model_edit_zCircle',
position,
matrix,
Cesium.Color.RED
)
this._viewer.scene.primitives.add(axisSphereZ)
//绕Y轴
const axisSphereY = this.createAxisSphere(
'model_edit_yCircle',
position,
matrix,
Cesium.Color.GREEN
)
this._viewer.scene.primitives.add(axisSphereY)
let my = Cesium.Matrix3.fromRotationY(Cesium.Math.toRadians(90))
let rotationY = Cesium.Matrix4.fromRotationTranslation(my)
Cesium.Matrix4.multiply(
axisSphereY.geometryInstances.modelMatrix,
rotationY,
axisSphereY.geometryInstances.modelMatrix
)
//绕X轴
const axisSphereX = this.createAxisSphere(
'model_edit_xCircle',
position,
matrix,
Cesium.Color.BLUE
)
this._viewer.scene.primitives.add(axisSphereX)
let mx = Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(90))
let rotationX = Cesium.Matrix4.fromRotationTranslation(mx)
Cesium.Matrix4.multiply(
axisSphereX.geometryInstances.modelMatrix,
rotationX,
axisSphereX.geometryInstances.modelMatrix
)
}
以上代码就可以将移动指示器和旋转指示器绘制出来了,当然对于代码出现的this.xx变量不知道是啥不要着急啊,你只需要这是需要全局使用的变量,把它们当作定义好的即可,后面会慢慢做出解释哈。
绘制完两个指示器之后就是准备开始对模型进行操作了。
旋转操作
javascript
/**
* 开始旋转编辑
*/
editRtation() {
const option = this.initParam()
this.createCircle(
option.originDegree.lng,
option.originDegree.lat,
option.originDegree.alt,
option.length
)
}
移动操作
javascript
/**
* 开始编辑平移
*/
editTranslation() {
const option = this.initParam()
const length = option.length
let translateCartesian = new Cesium.Cartesian3(length, length, length) //单位为米
let originPos = JSON.parse(JSON.stringify(this._b3dm.boundingSphere.center))
let targetDegree = this.getTransPostion(originPos, translateCartesian)
this.initLineArrow(option.originDegree, targetDegree, length)
}
操作过程中,要随时更新模型位置和指示器位置,因此要做鼠标操作中进行操作,更新模型位置和指示器位置
鼠标操作
javascript
//初始化鼠标事件(移动,按下,抬起)
initEvent() {
const $this = this
const viewer = this._viewer
$this._handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
$this._handler.setInputAction(function(event) {
let pick = viewer.scene.pick(event.position) //获取的pick对象
if (
pick &&
pick.primitive &&
pick.primitive._name &&
pick.primitive._name.indexOf('model_edit') != -1
) {
viewer.scene.screenSpaceCameraController.enableRotate = false //锁定相机
//高亮加粗显示
$this._currentPick = pick.primitive
$this._currentPick.width = 25
let downPos = viewer.scene.camera.pickEllipsoid(
event.position,
viewer.scene.globe.ellipsoid
)
let _tx = 0,
_ty = 0,
_tz = 0 //xyz方向的平移量(经纬度,经纬度,米)
let _rx = 0,
_ry = 0,
_rz = 0 //xyz方向的旋转量(度)
// 防止点击到地球之外报错,加个判断
if (downPos && Cesium.defined(downPos)) {
_tx = 0
_ty = 0
_tz = 0
_rx = 0
_ry = 0
_rz = 0
const downDegree = CoordTransform.transformCartesianToWGS84(
downPos
)
$this._handler.setInputAction(function(movement) {
let endPos = viewer.scene.camera.pickEllipsoid(
movement.endPosition,
viewer.scene.globe.ellipsoid
)
const endDegree = CoordTransform.transformCartesianToWGS84(
endPos
)
const _yPix = movement.endPosition.y - event.position.y
const _xPix = movement.endPosition.x - event.position.x
switch ($this._currentPick._name) {
case 'model_edit_xArrow':
_tx = endDegree.lng - downDegree.lng
break
case 'model_edit_yArrow':
_ty = endDegree.lat - downDegree.lat
break
case 'model_edit_zArrow':
_tz = -$this._dStep * _yPix
break
case 'model_edit_xCircle':
_rx = $this._rStep * _yPix
break
case 'model_edit_yCircle':
_ry = $this._rStep * _xPix
break
case 'model_edit_zCircle':
_rz = $this._rStep * _xPix
break
}
$this.updateModel($this._params, _tx, _ty, _tz, _rx, _ry, _rz)
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}
$this._handler.setInputAction(function(eve) {
viewer.scene.screenSpaceCameraController.enableRotate = true // 取消相机锁定
$this._currentPick.width = $this._defaultWidth
$this._currentPick = undefined
$this._params.tx += _tx
$this._params.ty += _ty
$this._params.tz += _tz
$this._params.rx += _rx
$this._params.ry += _ry
$this._params.rz += _rz
//为viewer绑定LEFT_UP事件监听器(执行函数,监听的事件)
$this._handler.removeInputAction(
Cesium.ScreenSpaceEventType.MOUSE_MOVE
) // 解除viewer的LEFT_UP事件监听器
$this._handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP) // 解除viewer的LEFT_UP事件监听器
}, Cesium.ScreenSpaceEventType.LEFT_UP)
}
}, Cesium.ScreenSpaceEventType.LEFT_DOWN)
}
最后就是销毁事件了
销毁移动指示器
javascript
removeCoordCircle() {
this._coordCircle.forEach(element => {
this._viewer.scene.primitives.remove(element)
})
this._coordCircle = []
}
销毁旋转指示器
javascript
removeCoordArrows() {
if (this._coordArrows) {
this._viewer.scene.primitives.remove(this._coordArrows)
this._coordArrows = undefined
}
}
销毁鼠标事件
javascript
/**
* 关闭/注销
*/
destroy() {
this.removeAllTools()
this._handler.destroy()
}
前端调用方式
vue
<template>
<div>
<div class="edit">
<el-button size="medium" @click="editObj">开始编辑</el-button>
<el-button size="medium" @click="rotation">旋转</el-button>
<el-button size="medium" @click="translation">平移</el-button>
<el-button size="medium" @click="destroy">关闭编辑</el-button>
</div>
<div id="cesiumContainer"></div>
</div>
</template>
<script setup>
import * as Cesium from "cesium"
import "cesium/Source/Widgets/widgets.css"
import initCesium from "@/cesiumUtils/initCesium"
import { onMounted } from "vue";
import EditB3DM from "@/cesiumUtils/EditB3DM"
let viewer = null;
let tilesetModel = null
let b3dm = null;
//生命周期钩子
onMounted(async () => {
viewer = await initCesium("cesiumContainer");
tilesetModel = new Cesium.Cesium3DTileset({
url: "/3dtiles/data/tileset.json"
});
viewer.scene.primitives.add(tilesetModel);
viewer.flyTo(tilesetModel)
})
//开始编辑
const editObj = () => {
b3dm = new EditB3DM(viewer, tilesetModel, 1, 1)
return b3dm
}
//旋转
const rotation = () => {
b3dm.editRtation()
}
//移动操作
const translation = () => {
b3dm.editTranslation()
}
//关闭编辑
const destroy = () => {
b3dm.destroy()
}
</script>
</style>
展示效果
旋转
移动
以上就是本文的全部内容了,对于本文的代码量有点过多,就不在本文进行展示进而占用大量篇幅,对于想要源代码的内容,可访问本人的旋转操作代码,对于本文中的this.xx是什么含义可通过源代码进行得知啊。
以后本教程的全部源代码都会及时在我的GitHub主页上进行开源,这上边的代码的注释也更加详细,也会。当然也希望大家多多star,您的star就是我的动力,当然这篇文章也希望大家多多点赞和收藏,更希望大家多多评论批评指正,希望和大家一起进步哈。