feat(All):Initial
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
import MapLayer from "@/cesium/modules/mapLayer";
|
||||
import CameraUtils from "@/cesium/modules/cameraUtils";
|
||||
import * as Cesium from 'cesium';
|
||||
import * as THREE from 'three';
|
||||
import {App,Hooks,Utils} from "@astral3d/engine";
|
||||
import {render, VNode} from "vue";
|
||||
|
||||
/**
|
||||
* @Date 2023-03-07
|
||||
* @Author 二三
|
||||
* @Description: cesium核心
|
||||
*/
|
||||
const onDownPosition = new THREE.Vector2();
|
||||
const onUpPosition = new THREE.Vector2();
|
||||
|
||||
export default class CesiumApp {
|
||||
//挂载的 Dom的Id
|
||||
dom: HTMLElement;
|
||||
//canvas存放的父级dom
|
||||
cesiumParentElement:HTMLElement;
|
||||
//
|
||||
viewer: Cesium.Viewer;
|
||||
//
|
||||
helper: Cesium.EventHelper;
|
||||
//cesium模块
|
||||
module: {
|
||||
mapLayer: MapLayer,
|
||||
cameraUtils:CameraUtils
|
||||
}
|
||||
//three对象
|
||||
public _three:{
|
||||
camera:THREE.PerspectiveCamera,
|
||||
scene:THREE.Scene,
|
||||
sceneHelpers:THREE.Scene,
|
||||
showSceneHelpers:boolean,
|
||||
renderer:THREE.WebGLRenderer
|
||||
}
|
||||
|
||||
constructor(dom) {
|
||||
this.dom = dom;
|
||||
// 提前实例化相关模块
|
||||
this.module = {
|
||||
mapLayer: new MapLayer(),
|
||||
cameraUtils:new CameraUtils()
|
||||
};
|
||||
|
||||
Cesium.Ion.defaultAccessToken = App.project.getKey("sceneInfo.cesiumConfig.token");
|
||||
/**
|
||||
* 避免https://api.cesium.com/v1/assets/1/endpoint?access_token=xxxx请求
|
||||
* 1、baseLayerPicker: false必须添加
|
||||
* 2、必须给一个imageryProvider作为默认底图
|
||||
*/
|
||||
this.viewer = this.getViewer();
|
||||
this.helper = new Cesium.EventHelper();
|
||||
this.handleInitCesiumAfter();
|
||||
|
||||
/* 初始化threejs相关(使用threejs场景的camera、scene、renderer) */
|
||||
this.cesiumParentElement = this.viewer.cesiumWidget.canvas.parentElement as HTMLElement;
|
||||
this._three = {
|
||||
camera: App.camera,
|
||||
scene: App.scene,
|
||||
sceneHelpers: new THREE.Scene(), //不使用window.editor.sceneHelpers,避免两个辅助场景内容重合
|
||||
showSceneHelpers:true,
|
||||
renderer:new THREE.WebGLRenderer({
|
||||
antialias: true,
|
||||
alpha: true,
|
||||
// logarithmicDepthBuffer:false,//重叠闪烁
|
||||
})
|
||||
}
|
||||
// 注意这里,直接把three容器(canvas 添加到 cesium中,在cesium的canvas之下),这样的话,两个canvas才会重叠起来。
|
||||
this.cesiumParentElement.appendChild(this._three.renderer.domElement);
|
||||
|
||||
//添加场景的事件监听
|
||||
this.eventListener();
|
||||
}
|
||||
|
||||
getViewer(){
|
||||
return new Cesium.Viewer(this.dom, {
|
||||
animation: false, //是否创建动画小器件,左下角仪表
|
||||
baseLayerPicker: true, //是否显示图层选择器
|
||||
fullscreenButton: false, //是否显示全屏按钮
|
||||
geocoder: true, //是否显示geocoder小器件,位置查找工具
|
||||
homeButton: false, //是否显示Home按钮,返回初始位置
|
||||
vrButton: false, // VR
|
||||
infoBox: false, //是否显示点击要素之后显示的信息
|
||||
sceneModePicker: false, //是否显示3D/2D选择器 - 选择视角的模式(球体、平铺、斜视平铺)
|
||||
selectionIndicator: false, //是否显示选取指示器组件
|
||||
timeline: false, //是否显示底部时间轴
|
||||
navigationHelpButton: false, //是否显示右上角的帮助按钮
|
||||
scene3DOnly: true, //如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
|
||||
// @ts-ignore
|
||||
clock: new Cesium.Clock(), //用于控制当前时间的时钟对象
|
||||
selectedImageryProviderViewModel: undefined, //当前图像图层的显示模型,仅baseLayerPicker设为true有意义
|
||||
selectedTerrainProviderViewModel: undefined, //当前地形图层的显示模型,仅baseLayerPicker设为true有意义
|
||||
imageryProvider: this.module.mapLayer.getDefaultLayer(App.project.getKey("sceneInfo.cesiumConfig.mapType")),
|
||||
terrainProvider: new Cesium.EllipsoidTerrainProvider(), //地形图层提供者,仅baseLayerPicker设为false有意义
|
||||
fullscreenElement: document.body, //全屏时渲染的HTML元素,
|
||||
useDefaultRenderLoop: false, //如果需要控制渲染循环,则设为true
|
||||
targetFrameRate: undefined, //使用默认render loop时的帧率
|
||||
showRenderLoopErrors: true, //如果设为true,将在一个HTML面板中显示错误信息
|
||||
automaticallyTrackDataSourceClocks: true, //自动追踪最近添加的数据源的时钟设置
|
||||
contextOptions: undefined, //传递给Scene对象的上下文参数(scene.options)
|
||||
sceneMode: Cesium.SceneMode.SCENE3D, //初始场景模式
|
||||
mapProjection: new Cesium.WebMercatorProjection(), //地图投影体系
|
||||
dataSources: new Cesium.DataSourceCollection(), //需要进行可视化的数据源的集合
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景事件监听
|
||||
*/
|
||||
eventListener(){
|
||||
this.cesiumParentElement.addEventListener('pointerdown',this.onMouseDown.bind(this),true);
|
||||
this.cesiumParentElement.addEventListener('pointerup', this.onMouseUp.bind(this),true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标按下处理
|
||||
* @param event
|
||||
*/
|
||||
onMouseDown(event) {
|
||||
event.preventDefault();
|
||||
const array = Utils.getMousePosition(this.cesiumParentElement, event.clientX, event.clientY);
|
||||
onDownPosition.fromArray( array );
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标抬起处理
|
||||
* @param event
|
||||
*/
|
||||
onMouseUp(event) {
|
||||
const array = Utils.getMousePosition(this.cesiumParentElement, event.clientX, event.clientY );
|
||||
onUpPosition.fromArray(array);
|
||||
this.handleClick();
|
||||
}
|
||||
|
||||
/**
|
||||
* three 场景点击事件
|
||||
*/
|
||||
handleClick() {
|
||||
if (onDownPosition.distanceTo(onUpPosition) === 0) {
|
||||
const intersects = this.getIntersects(onUpPosition);
|
||||
if(intersects.length === 0) return;
|
||||
let clickObject = intersects[0].object;
|
||||
|
||||
Hooks.useDispatchSignal("cesium_clickThreeScene",clickObject);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化cesium 场景后的处理操作
|
||||
*/
|
||||
handleInitCesiumAfter() {
|
||||
// @ts-ignore 去除logo
|
||||
this.viewer.cesiumWidget.creditContainer.style.display = 'none';
|
||||
|
||||
//实例化完图层再添加标记图层
|
||||
App.project.getKey("sceneInfo.cesiumConfig.markMap") && this.viewer.imageryLayers.addImageryProvider(this.module.mapLayer.getMarkMapByDefaultLayer() as Cesium.UrlTemplateImageryProvider);
|
||||
|
||||
//为modules setViewer
|
||||
this.module.mapLayer.setViewer(this.viewer);
|
||||
this.module.cameraUtils.setViewer(this.viewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加中国地图遮罩
|
||||
*/
|
||||
addChinaMask() {
|
||||
//使用GeoJsonDataSource加载json格式的数据
|
||||
let geojsonOptions = {
|
||||
clampToGround: true, //必须添加,否则添加实体对象会被覆盖
|
||||
};
|
||||
let dataSource = Cesium.GeoJsonDataSource.load(
|
||||
'/upyun/assets/geojson/china.json',
|
||||
geojsonOptions
|
||||
);
|
||||
dataSource.then(data => {
|
||||
this.viewer.dataSources.add(data);
|
||||
let entities = data.entities.values;
|
||||
for (let i = 0; i < entities.length; i++) {
|
||||
let entity = entities[i];
|
||||
// @ts-ignore
|
||||
entity.polygon.material = Cesium.Color.fromCssColorString('rgba(0,0,0,.2)');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取与鼠标点击位置射线相交的对象数组
|
||||
*/
|
||||
getIntersects(point) {
|
||||
//声明 rayCaster 和 mouse 变量
|
||||
let rayCaster = new THREE.Raycaster();
|
||||
let mouse = new THREE.Vector2();
|
||||
|
||||
mouse.set( ( point.x * 2 ) - 1, - ( point.y * 2 ) + 1 );
|
||||
//通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
|
||||
rayCaster.setFromCamera(mouse, this._three.camera);
|
||||
|
||||
//获取与射线相交的对象数组, 其中的元素按照距离排序,越近的越靠前。
|
||||
//+true,是对其后代进行查找,这个在这里必须加,因为模型是由很多部分组成的,后代非常多。
|
||||
return rayCaster.intersectObjects(this._three.scene.children, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加VNode至 viewer._toolbar
|
||||
*/
|
||||
addVNodeToViewer(vNode:VNode){
|
||||
//@ts-ignore
|
||||
render(vNode,this.viewer._toolbar);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置cesium场景
|
||||
*/
|
||||
reset(){
|
||||
console.log("%c重置cesium场景", "background-color: #e0005a; color: #ffffff; font-weight: bold; padding: 4px;border-radius:3px;");
|
||||
//销毁viewer
|
||||
this.viewer.destroy();
|
||||
// 新建viewer
|
||||
this.viewer = this.getViewer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
destroy(){
|
||||
// 移除事件监听
|
||||
this.cesiumParentElement.removeEventListener('pointerdown', this.onMouseDown);
|
||||
this.cesiumParentElement.removeEventListener('pointerup', this.onMouseUp);
|
||||
|
||||
//销毁modules
|
||||
this.module.cameraUtils.destroy();
|
||||
|
||||
//销毁viewer
|
||||
this.viewer.destroy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import {Hooks} from "@astral3d/engine";
|
||||
|
||||
/**
|
||||
* @Date 2022-06-09
|
||||
* @Author 二三
|
||||
* @param {*} viewer Cesium.Viewer
|
||||
* @Description: cesium相机类
|
||||
*/
|
||||
export default class CameraUtils {
|
||||
viewer: Cesium.Viewer | null;
|
||||
entity: Cesium.Entity | null;
|
||||
|
||||
constructor() {
|
||||
this.viewer = null;
|
||||
this.entity = null;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
Hooks.useAddSignal("cesium_flyTo", this.flyTo.bind(this))
|
||||
}
|
||||
|
||||
setViewer(viewer) {
|
||||
this.viewer = viewer;
|
||||
|
||||
//修改鼠标操作方式
|
||||
this.changeMouseOperate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改鼠标操作方式
|
||||
*/
|
||||
changeMouseOperate() {
|
||||
let screenSpaceCameraController = (this.viewer as Cesium.Viewer).scene.screenSpaceCameraController;
|
||||
//修改缩放操作
|
||||
screenSpaceCameraController.zoomEventTypes = [Cesium.CameraEventType.WHEEL, Cesium.CameraEventType.PINCH];
|
||||
//修改旋转操作
|
||||
screenSpaceCameraController.tiltEventTypes = [Cesium.CameraEventType.PINCH, Cesium.CameraEventType.RIGHT_DRAG];
|
||||
}
|
||||
|
||||
flyTo(lng, lat, distance, pitch = -15, heading = 0.0) {
|
||||
const viewer = this.viewer as Cesium.Viewer;
|
||||
viewer.camera.flyTo({
|
||||
destination: Cesium.Cartesian3.fromDegrees(lng, lat, distance),
|
||||
complete: () => {
|
||||
if (this.entity) viewer.entities.remove(this.entity);
|
||||
|
||||
this.entity = new Cesium.Entity({
|
||||
id: 'flyToWJM',
|
||||
position: Cesium.Cartesian3.fromDegrees(lng, lat),
|
||||
//该实体关联的点
|
||||
point: {
|
||||
pixelSize: 1,
|
||||
color: Cesium.Color.WHITE.withAlpha(0.9),
|
||||
outlineColor: Cesium.Color.WHITE.withAlpha(0.9),
|
||||
outlineWidth: 1
|
||||
}
|
||||
});
|
||||
viewer.entities.add(this.entity);
|
||||
viewer.flyTo(this.entity, {
|
||||
offset: {
|
||||
heading: Cesium.Math.toRadians(heading), //航向角(弧度)
|
||||
pitch: Cesium.Math.toRadians(pitch), //俯仰角(弧度)
|
||||
range: distance //距中心的距离,以米为单位
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁
|
||||
*/
|
||||
destroy() {
|
||||
Hooks.useRemoveSignal("cesium_flyTo", this.flyTo.bind(this))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import {App} from "@astral3d/engine";
|
||||
|
||||
/**
|
||||
* @Date 2023-03-07
|
||||
* @Author 二三
|
||||
* @Description: cesium地图底图图层管理
|
||||
*/
|
||||
export default class MapLayer{
|
||||
viewer: Cesium.Viewer | null;
|
||||
|
||||
//底图集合
|
||||
layers:{
|
||||
[s:string]:{
|
||||
satellite?: Cesium.UrlTemplateImageryProvider | Cesium.WebMapTileServiceImageryProvider, //卫星影像图
|
||||
mark?: Cesium.UrlTemplateImageryProvider , //标记图
|
||||
vector?: Cesium.UrlTemplateImageryProvider //矢量图
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.viewer = null;
|
||||
|
||||
const cesiumConfig = App.project.getKey("sceneInfo.cesiumConfig");
|
||||
this.layers = {
|
||||
Amap:{//高德
|
||||
satellite: new Cesium.UrlTemplateImageryProvider({
|
||||
url: "https://webst01.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}",
|
||||
minimumLevel: 3,
|
||||
maximumLevel: 18
|
||||
}),
|
||||
mark: new Cesium.UrlTemplateImageryProvider({
|
||||
url: "http://webst02.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
|
||||
minimumLevel: 3,
|
||||
maximumLevel: 18
|
||||
}),
|
||||
vector: new Cesium.UrlTemplateImageryProvider({
|
||||
url: "https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}",
|
||||
minimumLevel: 3,
|
||||
maximumLevel: 18
|
||||
})
|
||||
},
|
||||
Tianditu:{//天地图
|
||||
satellite: new Cesium.WebMapTileServiceImageryProvider({//球面墨卡托投影
|
||||
url:`http://t{s}.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${cesiumConfig.tiandituTk}`,
|
||||
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
|
||||
layer: 'tdtImgLayer',
|
||||
style: 'default',
|
||||
format: 'image/jpeg',
|
||||
tileMatrixSetID: 'GoogleMapsCompatible', //使用谷歌的瓦片切片方式
|
||||
}),
|
||||
mark: new Cesium.UrlTemplateImageryProvider({
|
||||
url: `http://t0.tianditu.gov.cn/cva_w/wmts?tk=${cesiumConfig.tiandituTk}`,
|
||||
minimumLevel: 3,
|
||||
maximumLevel: 18
|
||||
}),
|
||||
vector: new Cesium.UrlTemplateImageryProvider({
|
||||
url: `http://t0.tianditu.gov.cn/vec_w/wmts?tk=${cesiumConfig.tiandituTk}`,
|
||||
minimumLevel: 3,
|
||||
maximumLevel: 18
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setViewer(viewer){this.viewer = viewer;}
|
||||
|
||||
/**
|
||||
* 获取默认底图
|
||||
* @param layer 底图类型,默认卫星影像图 enum: satellite | vector
|
||||
*/
|
||||
getDefaultLayer(layer:'satellite' | 'vector' = "satellite"){
|
||||
return this.layers[App.project.getKey("sceneInfo.cesiumConfig.map")][layer];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认底图对应得标记图
|
||||
*/
|
||||
getMarkMapByDefaultLayer(){
|
||||
return this.layers[App.project.getKey("sceneInfo.cesiumConfig.map")].mark || this.layers.Amap.mark;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
import * as Cesium from "cesium";
|
||||
|
||||
/**
|
||||
* 笛卡尔坐标转换经纬度坐标
|
||||
* @param {*} car3_ps
|
||||
* @returns
|
||||
*/
|
||||
export function getLngLatByCartesian3(car3_ps) {
|
||||
if (!(car3_ps instanceof Cesium.Cartesian3))
|
||||
throw new Error("参数非 Cesium.Cartesian3 类型")
|
||||
let _cartographic = Cesium.Cartographic.fromCartesian(car3_ps);
|
||||
let _lat = Cesium.Math.toDegrees(_cartographic.latitude);
|
||||
let _lng = Cesium.Math.toDegrees(_cartographic.longitude);
|
||||
let _alt = _cartographic.height;
|
||||
return {longitude: _lng, latitude: _lat, elevation: _alt};
|
||||
}
|
||||
@@ -0,0 +1,439 @@
|
||||
import {h} from "vue";
|
||||
import * as THREE from "three";
|
||||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
||||
import {App,Hooks,Utils,SetPositionCommand,SetRotationCommand,SetScaleCommand} from "@astral3d/engine";
|
||||
import {NIcon} from "naive-ui";
|
||||
import { GolfOutline } from '@vicons/ionicons5';
|
||||
import * as Cesium from 'cesium';
|
||||
import CesiumApp from "@/cesium/cesiumApp";
|
||||
|
||||
/**
|
||||
* @Date 2023-02-06
|
||||
* @Author 二三
|
||||
* @Description: cesium视图出口
|
||||
*/
|
||||
export default class ViewPort {
|
||||
//核心类
|
||||
app: CesiumApp;
|
||||
//canvas存放的父级dom
|
||||
cesiumParentElement: HTMLElement;
|
||||
//three对象
|
||||
public _three: {
|
||||
camera: THREE.PerspectiveCamera,
|
||||
scene: THREE.Scene,
|
||||
sceneHelpers: THREE.Scene,
|
||||
showSceneHelpers:boolean,
|
||||
renderer: THREE.WebGLRenderer,
|
||||
transformControls:TransformControls | null,
|
||||
box: THREE.Box3,//包围盒
|
||||
};
|
||||
//选中的包围盒
|
||||
threeSelectionBox: THREE.Box3Helper;
|
||||
// threejs场景定位范围
|
||||
minWGS84: Array<number>;
|
||||
maxWGS84: Array<number>;
|
||||
animationFrameID: number | null;
|
||||
|
||||
constructor(dom) {
|
||||
this.app = new CesiumApp(dom);
|
||||
this.cesiumParentElement = this.app.cesiumParentElement;
|
||||
window.CesiumApp = this.app;
|
||||
|
||||
this.minWGS84 = [100.75483412680308, 22.026856925803223];
|
||||
this.maxWGS84 = [100.86206068991605, 21.979558335999187];
|
||||
|
||||
this._three = {
|
||||
...this.app._three,
|
||||
transformControls:null,
|
||||
box: new THREE.Box3()
|
||||
}
|
||||
this.threeSelectionBox = new THREE.Box3Helper(this._three.box);
|
||||
(this.threeSelectionBox.material as THREE.Material).depthTest = false;
|
||||
(this.threeSelectionBox.material as THREE.Material).transparent = true;
|
||||
this.threeSelectionBox.visible = false;
|
||||
this._three.sceneHelpers.add(this.threeSelectionBox);
|
||||
|
||||
this.animationFrameID = null;
|
||||
|
||||
//处理three canvas 的 transformControls,以便于场景融合;
|
||||
this.cesiumParentElement.addEventListener("mousemove",this.handleThreeMouseMove.bind(this))
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// 将三维球定位到中国
|
||||
this.app.viewer?.camera.setView({
|
||||
// Cesium的坐标是以地心为原点,一向指向南美洲,一向指向亚洲,一向指向北极州
|
||||
// fromDegrees()方法,将经纬度和高程转换为世界坐标
|
||||
destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 24000000),
|
||||
orientation: {
|
||||
// 指向
|
||||
heading: Cesium.Math.toRadians(348.4202942851978),
|
||||
// 视角
|
||||
pitch: Cesium.Math.toRadians(-90),
|
||||
roll: Cesium.Math.toRadians(0),
|
||||
},
|
||||
});
|
||||
|
||||
this.signalsRegister(true);
|
||||
|
||||
this.handleCesiumEvent();
|
||||
|
||||
this.app.addChinaMask();
|
||||
|
||||
this.addButtonToViewer();
|
||||
|
||||
this.initThreeTransformControls();
|
||||
|
||||
this.calcCenter();
|
||||
|
||||
this.fusionCanvas();
|
||||
|
||||
this.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* 相关signals注册
|
||||
*/
|
||||
signalsRegister(isAdd:boolean = true) {
|
||||
let _this = this;
|
||||
//停止requestAnimationFrame
|
||||
function stopLoop(){
|
||||
cancelAnimationFrame((_this.animationFrameID as number));
|
||||
}
|
||||
//显示three辅助
|
||||
function handleShowHelpers(showHelpers:boolean){
|
||||
_this._three.showSceneHelpers = showHelpers;
|
||||
(_this._three.transformControls as TransformControls).enabled = showHelpers;
|
||||
_this.renderThree();
|
||||
}
|
||||
|
||||
const signals = {
|
||||
"cesium_stopLoop":stopLoop,
|
||||
"showHelpersChanged":handleShowHelpers,
|
||||
"cesium_destroy":this.destroy.bind(this)
|
||||
}
|
||||
|
||||
Object.keys(signals).forEach(name => {
|
||||
isAdd ? Hooks.useAddSignal(name,signals[name]) : Hooks.useRemoveSignal(name,signals[name]);
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
addButtonToViewer(){
|
||||
let _this = this;
|
||||
const vNode = h("button",{
|
||||
type:"button",
|
||||
class:"cesium-button cesium-toolbar-button",
|
||||
title:"飞行至ThreeJS场景",
|
||||
onClick(){
|
||||
_this.flyToThree();
|
||||
}
|
||||
},h(NIcon,{size:22},h(GolfOutline)))
|
||||
|
||||
this.app.addVNodeToViewer(vNode)
|
||||
}
|
||||
|
||||
/**
|
||||
* cesium 开始加载后的相关操作
|
||||
*/
|
||||
handleCesiumEvent() {
|
||||
/* 监听cesium 加载完成事件 */
|
||||
const removeOnloadCallback = this.app.helper.add(this.app.viewer.scene.globe.tileLoadProgressEvent, event => {
|
||||
if (event == 0) {
|
||||
console.log('-------------cesium 加载完成---------------');
|
||||
this.flyToThree();
|
||||
removeOnloadCallback();
|
||||
}
|
||||
});
|
||||
|
||||
// 绑定屏幕空间事件(单击)
|
||||
// let handler = new Cesium.ScreenSpaceEventHandler(this.app.viewer.scene.canvas);
|
||||
// handler.setInputAction(function (event) {
|
||||
// console.log('左键单击', event);
|
||||
// }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
||||
|
||||
//监听Cesium相机高度变化
|
||||
this.app.viewer.camera.changed.addEventListener(() => {
|
||||
// 变化后高度
|
||||
// let height= viewer.camera.positionCartographic.height;
|
||||
});
|
||||
|
||||
//移除左键双击事件
|
||||
this.app.viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化three变换控制器
|
||||
*/
|
||||
initThreeTransformControls() {
|
||||
let objectPositionOnDown: THREE.Vector3 | null = null;
|
||||
let objectRotationOnDown: THREE.Euler | null = null;
|
||||
let objectScaleOnDown: THREE.Vector3 | null = null;
|
||||
this._three.transformControls = new TransformControls(this._three.camera, this._three.renderer.domElement);
|
||||
let transformControls = this._three.transformControls;
|
||||
transformControls.addEventListener('change', () => {
|
||||
transformControls.updateMatrixWorld();
|
||||
const object = transformControls.object;
|
||||
if (object !== undefined) {
|
||||
this._three.box.setFromObject(object, true);
|
||||
const helper = App.helpers[object.id];
|
||||
if (helper !== undefined && !helper.isSkeletonHelper) {
|
||||
helper.update();
|
||||
}
|
||||
Hooks.useDispatchSignal("objectChanged", object);
|
||||
|
||||
// 绑定物体不能去到地下
|
||||
if(object.position.y < 0){
|
||||
object.position.setY(0);
|
||||
}
|
||||
}
|
||||
|
||||
this.renderThree();
|
||||
});
|
||||
transformControls.addEventListener('mouseDown', ()=>{
|
||||
const object = transformControls.object as THREE.Object3D;
|
||||
objectPositionOnDown = object.position.clone();
|
||||
objectRotationOnDown = object.rotation.clone();
|
||||
objectScaleOnDown = object.scale.clone();
|
||||
|
||||
// TODO 此时应该停止其他控制器和cesium渲染
|
||||
// controls.enabled = false;
|
||||
Hooks.useDispatchSignal("cesium_stopLoop");
|
||||
});
|
||||
//记录行为
|
||||
transformControls.addEventListener('mouseUp', ()=>{
|
||||
const object = transformControls.object;
|
||||
|
||||
if (object !== undefined) {
|
||||
switch (transformControls.getMode()) {
|
||||
case 'translate':
|
||||
if (!objectPositionOnDown?.equals(object.position)) {
|
||||
App.execute(new SetPositionCommand(object, object.position, objectPositionOnDown));
|
||||
}
|
||||
break;
|
||||
case 'rotate':
|
||||
if (!objectRotationOnDown?.equals(object.rotation)) {
|
||||
App.execute(new SetRotationCommand(object, object.rotation, objectRotationOnDown));
|
||||
}
|
||||
break;
|
||||
case 'scale':
|
||||
if (!objectScaleOnDown?.equals(object.scale)) {
|
||||
App.execute(new SetScaleCommand(object, object.scale, objectScaleOnDown));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO 此时应该重新启用其他控制器和cesium渲染
|
||||
//controls.enabled = true;
|
||||
this.loop()
|
||||
});
|
||||
transformControls.traverse(child => {
|
||||
child.userData.isTransformControls = true;
|
||||
})
|
||||
this._three.sceneHelpers.add(transformControls);
|
||||
|
||||
//相关方法
|
||||
const attachObject = (object) => {
|
||||
// this.threeSelectionBox.visible = false;
|
||||
transformControls.detach();
|
||||
if (object !== null && object !== this._three.scene && object !== this._three.camera) {
|
||||
// this._three.box.setFromObject(object, true);
|
||||
// if (!this._three.box.isEmpty()) {
|
||||
// this.threeSelectionBox.visible = true;
|
||||
// }
|
||||
// console.log("attach",object)
|
||||
transformControls.attach(object);
|
||||
}
|
||||
this.renderThree();
|
||||
}
|
||||
|
||||
/* transformControls相关的监听 */
|
||||
//监听融合场景下的three scene下的点击
|
||||
Hooks.useAddSignal("cesium_clickThreeScene", (object) => {
|
||||
attachObject(object)
|
||||
})
|
||||
Hooks.useAddSignal("transformModeChanged", (mode) => {
|
||||
transformControls.setMode(mode);
|
||||
});
|
||||
Hooks.useAddSignal("objectSelected", (object) => {
|
||||
attachObject(object)
|
||||
});
|
||||
Hooks.useAddSignal("objectRemoved", (object) => {
|
||||
if (object === transformControls.object) {
|
||||
transformControls.detach();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算场景中心位置等信息
|
||||
*/
|
||||
calcCenter(){
|
||||
let cartToVec = function (cart) {
|
||||
return new THREE.Vector3(cart.x, cart.y, cart.z);
|
||||
};
|
||||
|
||||
//将Three.js网格配置为与地球中心位置垂直向上
|
||||
let minWGS84 = this.minWGS84;
|
||||
let maxWGS84 = this.maxWGS84;
|
||||
// 转换纬度/长中心位置为笛卡尔3
|
||||
let center = cartToVec(Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2));
|
||||
//得到面向模型的前向方向
|
||||
let centerHigh = Cesium.Cartesian3.fromDegrees((minWGS84[0] + maxWGS84[0]) / 2, (minWGS84[1] + maxWGS84[1]) / 2, 1);
|
||||
//使用从左下到左上的方向作为上向量
|
||||
let bottomLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], minWGS84[1]));
|
||||
let topLeft = cartToVec(Cesium.Cartesian3.fromDegrees(minWGS84[0], maxWGS84[1]));
|
||||
let latDir = new THREE.Vector3().subVectors(bottomLeft, topLeft).normalize();
|
||||
// 配置实体的位置和方向
|
||||
this._three.scene.position.copy(center);
|
||||
this._three.scene.lookAt(centerHigh.x, centerHigh.y, centerHigh.z);
|
||||
this._three.scene.up.copy(latDir);
|
||||
this._three.scene.rotateX(Math.PI / 2);
|
||||
//sceneHelpers 也需要相同操作
|
||||
// this._three.sceneHelpers.position.copy(center);
|
||||
// this._three.sceneHelpers.lookAt(centerHigh.x, centerHigh.y, centerHigh.z);
|
||||
// this._three.sceneHelpers.up.copy(latDir);
|
||||
// this._three.sceneHelpers.rotateX(Math.PI / 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理three canvas 的 transformControls,以便于场景融合;
|
||||
*/
|
||||
handleThreeMouseMove(event){
|
||||
//如果未绑定变换物体则不执行任何逻辑
|
||||
if(!this._three.transformControls?.object) return;
|
||||
|
||||
const threeCanvas = this._three.renderer.domElement;
|
||||
|
||||
const onDownPosition = new THREE.Vector2();
|
||||
let rayCaster = new THREE.Raycaster();
|
||||
let mouse = new THREE.Vector2();
|
||||
|
||||
const array = Utils.getMousePosition(threeCanvas, event.clientX, event.clientY);
|
||||
onDownPosition.fromArray(array);
|
||||
|
||||
mouse.set( ( onDownPosition.x * 2 ) - 1, - ( onDownPosition.y * 2 ) + 1 );
|
||||
//通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
|
||||
rayCaster.setFromCamera(mouse, this._three.camera);
|
||||
|
||||
const intersects = rayCaster.intersectObjects(this._three.sceneHelpers.children, true);
|
||||
|
||||
if(intersects.length === 0) {
|
||||
threeCanvas.style.pointerEvents = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
let _object = intersects[0].object;
|
||||
|
||||
if(!_object.userData.isTransformControls){
|
||||
threeCanvas.style.pointerEvents = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
if(_object.type !== "TransformControlsPlane" && _object.type !== "Line" && _object.children.length === 0){
|
||||
threeCanvas.style.pointerEvents = 'auto';
|
||||
}else{
|
||||
threeCanvas.style.pointerEvents = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 飞行至three场景
|
||||
*/
|
||||
flyToThree(){
|
||||
Hooks.useDispatchSignal("cesium_flyTo", (this.minWGS84[0] + this.maxWGS84[0]) / 2,(this.minWGS84[1] + this.maxWGS84[1]) / 2,3000,-40)
|
||||
}
|
||||
|
||||
/* ----------------------------------- 渲染相关 ----------------------------------------------*/
|
||||
|
||||
/**
|
||||
* cesium 渲染
|
||||
*/
|
||||
renderCesium() {
|
||||
this.app.viewer.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* 两个画布相机融合
|
||||
*/
|
||||
fusionCanvas() {
|
||||
// @ts-ignore 用Cesium注册Three.js场景
|
||||
this._three.camera.fov = Cesium.Math.toDegrees(this.app.viewer.camera.frustum.fovy) //ThreeJS FOV是垂直的
|
||||
|
||||
//克隆Cesium Camera的投影位置
|
||||
// Three.js对象将出现在相同的位置,因为Cesium Globe上面
|
||||
this._three.camera.matrixAutoUpdate = false;
|
||||
// 注意这里,three高版本这行代码需要放在 three.camera.matrixWorld 之前
|
||||
this._three.camera.lookAt(0, 0, 0);
|
||||
|
||||
let w = this.cesiumParentElement.offsetWidth;
|
||||
let h = this.cesiumParentElement.offsetHeight;
|
||||
this._three.camera.aspect = w / h;
|
||||
this._three.camera.updateProjectionMatrix();
|
||||
|
||||
this._three.renderer.setSize(w,h);
|
||||
this._three.renderer.clear();
|
||||
this._three.scene.updateMatrixWorld();
|
||||
}
|
||||
|
||||
/**
|
||||
* three 渲染器的渲染
|
||||
*/
|
||||
renderThree(){
|
||||
let cvm = this.app.viewer.camera.viewMatrix;
|
||||
let civm = this.app.viewer.camera.inverseViewMatrix;
|
||||
|
||||
this._three.camera.matrixWorld.set(
|
||||
civm[0], civm[4], civm[8], civm[12],
|
||||
civm[1], civm[5], civm[9], civm[13],
|
||||
civm[2], civm[6], civm[10], civm[14],
|
||||
civm[3], civm[7], civm[11], civm[15]
|
||||
);
|
||||
|
||||
this._three.camera.matrixWorldInverse.set(
|
||||
cvm[0], cvm[4], cvm[8], cvm[12],
|
||||
cvm[1], cvm[5], cvm[9], cvm[13],
|
||||
cvm[2], cvm[6], cvm[10], cvm[14],
|
||||
cvm[3], cvm[7], cvm[11], cvm[15]
|
||||
);
|
||||
|
||||
this._three.renderer.render(this._three.scene, this._three.camera);
|
||||
|
||||
if (this._three.camera === App.viewportCamera) {
|
||||
this._three.renderer.autoClear = false;
|
||||
if (this._three.showSceneHelpers) {
|
||||
// this._three.sceneHelpers.updateMatrixWorld();
|
||||
this._three.renderer.render(this._three.sceneHelpers, this._three.camera);
|
||||
}
|
||||
this._three.renderer.autoClear = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 同步渲染
|
||||
loop() {
|
||||
this.animationFrameID = requestAnimationFrame(this.loop.bind(this));
|
||||
this.renderCesium();
|
||||
this.renderThree();
|
||||
}
|
||||
|
||||
//销毁
|
||||
destroy(){
|
||||
//停止渲染
|
||||
cancelAnimationFrame((this.animationFrameID as number));
|
||||
//清除window的挂载
|
||||
window.CesiumApp = undefined;
|
||||
//销毁变换控制器
|
||||
this._three.transformControls?.dispose();
|
||||
|
||||
// 移除signal
|
||||
this.signalsRegister(false);
|
||||
|
||||
// 销毁app内容
|
||||
this.app.destroy();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user