侧边栏壁纸
  • 累计撰写 21 篇文章
  • 累计创建 6 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Cesium 地形开挖

南风过境
2023-12-22 / 0 评论 / 0 点赞 / 18 阅读 / 11006 字 / 正在检测是否收录...

参考

Cesium地形开挖-附带源码 - 掘金 (juejin.cn)

说明

代码直接用的上述链接中的,我只做了一些小改动:

  1. 原代码支持拖拽边界点后更新开挖范围,但是在拖拽时会带着球一起动,会造成拖拽不起作用,所以我在LEFT_DOWNLEFT_UP 这两个鼠标事件中分别对地图拖拽旋转功能设置了开关

  2. stop() 方法中添加了清除所有 entity 和鼠标事件的方法,写得比较简略,一次性全清了,各位可以根据自己的需求去调整清理

  3. 原作者墙体和底部的材质简单用了颜色,我从网上选了张泥土材质的图片替换掉了原本的贴图(贴图我放在文章下面),看上去舒服点儿,getAssetsFile 这个方法只是用来获取图片 Url 的方法,各位可以根据自己的项目情况采用合适的引入方式

  4. clippingPlaneCollection() 方法中另外添加了一行代码this.viewer.scene.globe.depthTestAgainstTerrain = true; 这是开启深度检测的,否则最终开挖出来的效果看上去在球上飘

目前功能还有些不足,后期待有时间逐步完善:

  1. 连续开挖多个会有问题

  2. 不能开挖凹多边形

完整代码

class TerrainCutting {
    constructor(viewer) {
        this.viewer = viewer; //场景
        //注册区域挖掘
        this.clippingPlanesArray = []; //裁剪区域数组
        this.clippingPoint = []; //点位
        this.temporayrPolygonEntity = null; //多边形对象
        this.clippingWallEntities = null;//挖掘墙体
        this.clippingBootomWallEntities = null; //挖掘墙体底部区域
        this.cesiumEvent = null; //鼠标事件
        this.activeClickPick = null; //当前点击的覆盖物
        this.infoWindowElement = null;//信息窗体Dom
        this.preRenderEvent = null;//窗口更新事件
        this.clippingDeepValue = 200;//定义挖掘深度
        this.cesiumEventState = ""; //鼠标按压状态
    }
    //开始挖掘
    create() {
        //开始之前清除数据
        this.cesiumEvent = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
        this.cesiumEvent.setInputAction((event) => {
            //更新鼠标状态
            this.cesiumEventState = "leftClick";
            //当前点击覆盖物
            this.activeClickPick = this.viewer.scene.pick(event.position);
            //点击空白区域重新绘制
            if (!this.activeClickPick || !this.activeClickPick.id) {
                //恢复地形开挖状态
                if (this.clippingWallEntities) {
                    this.destroy();
                }

                //绘制地形开挖边界
                this.saveClippingPlaneCollectionData(event);
            }
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);

        //鼠标按下事件
        this.cesiumEvent.setInputAction((event) => {
            //更新鼠标状态
            this.cesiumEventState = "leftDown";
            //当前点击覆盖物
            this.activeClickPick = this.viewer.scene.pick(event.position);
            if (this.clippingWallEntities) {
                if (this.activeClickPick && this.activeClickPick.id) {
                    //选中点击位置
                    this.selectClickPoint();
                }
            }

        }, Cesium.ScreenSpaceEventType.LEFT_DOWN);

        //鼠标抬起事件
        this.cesiumEvent.setInputAction((event) => {
            //更新鼠标状态
            this.cesiumEventState = "leftUp";
            this.viewer.scene.screenSpaceCameraController.enableRotate = true;

        }, Cesium.ScreenSpaceEventType.LEFT_UP);

        //鼠标移动事件
        this.cesiumEvent.setInputAction((event) => {

            if (this.activeClickPick && this.activeClickPick.id) {
                //判断鼠标是否为按压状态
                if (this.cesiumEventState == "leftDown") {
                    this.viewer.scene.screenSpaceCameraController.enableRotate = false;

                    this.changeLayerPointPostion(event);
                }
            }

        }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

        this.cesiumEvent.setInputAction((event) => {
            //更新鼠标状态
            this.cesiumEventState = "rightClick";

            this.clippingPlaneCollection();
            this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
            // this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
            this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
    }

    //停止挖掘
    //解绑事件,销毁挖掘图层,用于在其他操作之前,关闭挖掘功能
    stop() {
        this.viewer.scene.screenSpaceCameraController.enableRotate = true;
        this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
        this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
        this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
        this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_UP)
        this.cesiumEvent.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOWN)
        this.viewer.entities.removeAll();
        this.clippingWallEntities = null;
        this.clippingBootomWallEntities = null;
        this.clippingPlanesArray = null;
        this.clippingPoint = null;
        this.viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: [],
            edgeWidth: 0
        });
    }
    //销毁挖掘图层,不解绑事件,用于重新绘制
    destroy() {

    }
    //保存鼠标点击获取到的挖掘区域,并且根据坐标绘制出开挖区域
    //保存区域挖掘数据
    saveClippingPlaneCollectionData(event) {
        var cartesian = this.viewer.camera.pickEllipsoid(event.position, this.viewer.scene.globe.ellipsoid);
        this.clippingPlanesArray.push(cartesian);
        //绘制点
        var clippingPoint = this.viewer.entities.add({
            name: "定位点",
            position: cartesian,
            point: {
                color: Cesium.Color.SKYBLUE,
                pixelSize: 10,
                outlineColor: Cesium.Color.YELLOW,
                outlineWidth: 3,
                disableDepthTestDistance: Number.POSITIVE_INFINITY,
                heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            }
        });
        this.clippingPoint.push(clippingPoint);
        //绘制开挖区域                             
        if (!this.temporayrPolygonEntity) {
            this.temporayrPolygonEntity = this.viewer.entities.add({
                polygon: {
                    hierarchy: new Cesium.CallbackProperty(() => {
                        return new Cesium.PolygonHierarchy(this.clippingPlanesArray);
                    }, false),
                    heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
                    material: Cesium.Color.RED.withAlpha(0.5)
                }
            });
        }
    }

    //实现挖掘功能,用于挖掘区域的创建和更新
    //开始挖掘
    clippingPlaneCollection() {

        //开始挖掘之前移除已经挖掘过的区域
        if (this.clippingWallEntities) {
            this.viewer.entities.remove(this.clippingWallEntities);
            this.clippingWallEntities = null;
        }

        if (this.clippingBootomWallEntities) {
            this.viewer.entities.remove(this.clippingBootomWallEntities);
            this.clippingBootomWallEntities = null;
        }

        this.viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: [],
            edgeWidth: 0
        });

        var points = this.clippingPlanesArray;



        var pointsLength = points.length;
        var clippingPlanes = [];
        for (var i = 0; i < pointsLength; ++i) {
            var nextIndex = (i + 1) % pointsLength;

            var midpoint = Cesium.Cartesian3.add(points[i], points[nextIndex], new Cesium.Cartesian3());
            midpoint = Cesium.Cartesian3.multiplyByScalar(midpoint, 0.5, midpoint);

            var up = Cesium.Cartesian3.normalize(midpoint, new Cesium.Cartesian3());
            var right = Cesium.Cartesian3.subtract(points[nextIndex], midpoint, new Cesium.Cartesian3());
            right = Cesium.Cartesian3.normalize(right, right);

            var normal = Cesium.Cartesian3.cross(right, up, new Cesium.Cartesian3());
            normal = Cesium.Cartesian3.normalize(normal, normal);

            var originCenteredPlane = new Cesium.Plane(normal, 0.0);
            var distance = Cesium.Plane.getPointDistance(originCenteredPlane, midpoint);

            clippingPlanes.push(new Cesium.ClippingPlane(normal, distance));
        }

        this.viewer.scene.globe.clippingPlanes = new Cesium.ClippingPlaneCollection({
            planes: clippingPlanes,
            edgeWidth: 1
        });

        this.viewer.scene.globe.depthTestAgainstTerrain = true;

        // 侧边墙体
        this.clippingWallEntities = this.viewer.entities.add({
            corridor: {
                positions: [
                    ...this.clippingPlanesArray,
                    this.clippingPlanesArray[0],
                    this.clippingPlanesArray[1]
                ],
                height: this.clippingDeepValue * -1,
                extrudedHeight: 0,
                width: 1,
                cornerType: Cesium.CornerType.ROUNDED,
                material: new Cesium.ImageMaterialProperty({
                    image: getAssetsFile('img/ExcavationAnalysis/wallMaterial.png'),
                    repeat: new Cesium.Cartesian2(1000, 1000),
                }),

                outline: false
            },
        });
        //底部墙体
        this.clippingBootomWallEntities = this.viewer.entities.add({
            polygon: {
                hierarchy: this.clippingPlanesArray,
                height: this.clippingDeepValue * -1,
                extrudedHeight: this.clippingDeepValue * -1 + 1,
                material: new Cesium.ImageMaterialProperty({
                    image: getAssetsFile('img/ExcavationAnalysis/wallMaterial.png'),
                    repeat: new Cesium.Cartesian2(1, 1),
                }),
            },
        });

    }

    //修改定位点选中状态
    selectClickPoint() {
        if (this.activeClickPick.id.name == "定位点") {
            for (var i = 0; i < this.clippingPoint.length; i++) {
                this.clippingPoint[i].point.color = Cesium.Color.SKYBLUE;
            }
            this.activeClickPick.id.point.color = Cesium.Color.RED;
        }
    }

    //修改选中点的位置,更新矩形边框位置
    changeLayerPointPostion(event) {
        var cartesian = this.viewer.camera.pickEllipsoid(event.endPosition, this.viewer.scene.globe.ellipsoid);
        this.activeClickPick.id.position = cartesian;
        //根据ID得到修改的index
        var activeIndex = -1;
        for (var i = 0; i < this.clippingPoint.length; i++) {
            if (this.activeClickPick.id._id == this.clippingPoint[i]._id) {
                activeIndex = i;
            }
        }
        this.clippingPlanesArray[activeIndex] = cartesian;
        //修改挖掘区域
        if (this.clippingWallEntities) {
            this.clippingPlaneCollection();
        }
    }

}

材质贴图

效果

0

评论区