mirror of
				https://github.com/jiawanlong/Cesium-Examples.git
				synced 2025-11-04 09:14:17 +00:00 
			
		
		
		
	
		
			
	
	
		
			217 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			217 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								/**
							 | 
						|||
| 
								 | 
							
								 * @class
							 | 
						|||
| 
								 | 
							
								 * @description 3dtiles模型压平
							 | 
						|||
| 
								 | 
							
								 */
							 | 
						|||
| 
								 | 
							
								class Flat {
							 | 
						|||
| 
								 | 
							
								    /**
							 | 
						|||
| 
								 | 
							
								     * 
							 | 
						|||
| 
								 | 
							
								     * @param {Cesium.Cesium3DTileset} tileset 三维模型
							 | 
						|||
| 
								 | 
							
								     * @param {Object} opt 
							 | 
						|||
| 
								 | 
							
								     * @param {Number} opt.flatHeight 压平高度 
							 | 
						|||
| 
								 | 
							
								     */
							 | 
						|||
| 
								 | 
							
								    constructor(tileset, opt) {
							 | 
						|||
| 
								 | 
							
								        if (!tileset) return;
							 | 
						|||
| 
								 | 
							
								        this.tileset = tileset;
							 | 
						|||
| 
								 | 
							
								        this.opt = opt || {};
							 | 
						|||
| 
								 | 
							
								        this.flatHeight = this.opt.flatHeight || 0;
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        this.center = tileset.boundingSphere.center.clone();
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        this.matrix = Cesium.Transforms.eastNorthUpToFixedFrame(this.center.clone());
							 | 
						|||
| 
								 | 
							
								        this.localMatrix = Cesium.Matrix4.inverse(this.matrix, new Cesium.Matrix4());
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        // 多面的坐标数组
							 | 
						|||
| 
								 | 
							
								        this.regionList = [];
							 | 
						|||
| 
								 | 
							
								        // 多个面坐标转为局部模型坐标
							 | 
						|||
| 
								 | 
							
								        this.localPositionsArr = [];
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    /**
							 | 
						|||
| 
								 | 
							
								     * 添加压平面
							 | 
						|||
| 
								 | 
							
								     * @param {Object} attr 参数
							 | 
						|||
| 
								 | 
							
								     * @param {Cesium.Cartesian3[]} attr.positions 压平面坐标
							 | 
						|||
| 
								 | 
							
								     * @param {Number} attr.height 压平深度,当前不支持单独设置
							 | 
						|||
| 
								 | 
							
								     * @param {Number} attr.id 唯一标识
							 | 
						|||
| 
								 | 
							
								     */
							 | 
						|||
| 
								 | 
							
								    addRegion(attr) {
							 | 
						|||
| 
								 | 
							
								        let { positions, height, id } = attr || {};
							 | 
						|||
| 
								 | 
							
								        // this.flatHeight = height;
							 | 
						|||
| 
								 | 
							
								        if (!id) id = (new Date()).getTime() + "" + Number(Math.random() * 1000).toFixed(0);
							 | 
						|||
| 
								 | 
							
								        this.regionList.push(attr);
							 | 
						|||
| 
								 | 
							
								        for (let i = 0; i < this.regionList.length; i++) {
							 | 
						|||
| 
								 | 
							
								            let item = this.regionList[i];
							 | 
						|||
| 
								 | 
							
								            const positions = item.positions;
							 | 
						|||
| 
								 | 
							
								            let localCoor = this.cartesiansToLocal(positions);
							 | 
						|||
| 
								 | 
							
								            this.localPositionsArr.push(localCoor);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        const funstr = this.getIsinPolygonFun(this.localPositionsArr);
							 | 
						|||
| 
								 | 
							
								        let str = ``;
							 | 
						|||
| 
								 | 
							
								        for (let i = 0; i < this.localPositionsArr.length; i++) {
							 | 
						|||
| 
								 | 
							
								            const coors = this.localPositionsArr[i];
							 | 
						|||
| 
								 | 
							
								            const n = coors.length;
							 | 
						|||
| 
								 | 
							
								            let instr = ``;
							 | 
						|||
| 
								 | 
							
								            coors.forEach((coordinate, index) => {
							 | 
						|||
| 
								 | 
							
								                instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
							 | 
						|||
| 
								 | 
							
								            })
							 | 
						|||
| 
								 | 
							
								            str += `
							 | 
						|||
| 
								 | 
							
								                ${instr}
							 | 
						|||
| 
								 | 
							
								                if(isPointInPolygon_${n}(position2D)){
							 | 
						|||
| 
								 | 
							
								                    vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
							 | 
						|||
| 
								 | 
							
								                    vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
							 | 
						|||
| 
								 | 
							
								                    vsOutput.positionMC.xy = model_local_position_transformed.xy;
							 | 
						|||
| 
								 | 
							
								                    vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
							 | 
						|||
| 
								 | 
							
								                    return;
							 | 
						|||
| 
								 | 
							
								                }`;
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        this.updateShader(funstr, str);
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    /**
							 | 
						|||
| 
								 | 
							
								     * 根据id删除压平的面
							 | 
						|||
| 
								 | 
							
								     * @param {String} id 唯一标识
							 | 
						|||
| 
								 | 
							
								     */
							 | 
						|||
| 
								 | 
							
								    removeRegionById(id) {
							 | 
						|||
| 
								 | 
							
								        if (!id) return;
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        this.regionList = this.regionList.filter((attr) => {
							 | 
						|||
| 
								 | 
							
								            return attr.id != id;
							 | 
						|||
| 
								 | 
							
								        })
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        this.localPositionsArr = [];
							 | 
						|||
| 
								 | 
							
								        for (let i = 0; i < this.regionList.length; i++) {
							 | 
						|||
| 
								 | 
							
								            let item = this.regionList[i];
							 | 
						|||
| 
								 | 
							
								            const positions = item.positions;
							 | 
						|||
| 
								 | 
							
								            let localCoor = this.cartesiansToLocal(positions);
							 | 
						|||
| 
								 | 
							
								            this.localPositionsArr.push(localCoor);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        const funstr = this.getIsinPolygonFun(this.localPositionsArr);
							 | 
						|||
| 
								 | 
							
								        let str = ``;
							 | 
						|||
| 
								 | 
							
								        for (let i = 0; i < this.localPositionsArr.length; i++) {
							 | 
						|||
| 
								 | 
							
								            const coors = this.localPositionsArr[i];
							 | 
						|||
| 
								 | 
							
								            const n = coors.length;
							 | 
						|||
| 
								 | 
							
								            let instr = ``;
							 | 
						|||
| 
								 | 
							
								            coors.forEach((coordinate, index) => {
							 | 
						|||
| 
								 | 
							
								                instr += `points_${n}[${index}] = vec2(${coordinate[0]}, ${coordinate[1]});\n`;
							 | 
						|||
| 
								 | 
							
								            })
							 | 
						|||
| 
								 | 
							
								            str += `
							 | 
						|||
| 
								 | 
							
								                ${instr}
							 | 
						|||
| 
								 | 
							
								                if(isPointInPolygon_${n}(position2D)){
							 | 
						|||
| 
								 | 
							
								                    vec4 tileset_local_position_transformed = vec4(tileset_local_position.x, tileset_local_position.y, ground_z, 1.0);
							 | 
						|||
| 
								 | 
							
								                    vec4 model_local_position_transformed = czm_inverseModel * u_tileset_localToWorldMatrix * tileset_local_position_transformed;
							 | 
						|||
| 
								 | 
							
								                    vsOutput.positionMC.xy = model_local_position_transformed.xy;
							 | 
						|||
| 
								 | 
							
								                    vsOutput.positionMC.z = model_local_position_transformed.z+ modelMC.z*0.002;
							 | 
						|||
| 
								 | 
							
								                    return;
							 | 
						|||
| 
								 | 
							
								                }`;
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        this.updateShader(funstr, str);
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    /**
							 | 
						|||
| 
								 | 
							
								     * 销毁
							 | 
						|||
| 
								 | 
							
								     */
							 | 
						|||
| 
								 | 
							
								    destroy() {
							 | 
						|||
| 
								 | 
							
								        this.tileset.customShader = undefined;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    /**
							 | 
						|||
| 
								 | 
							
								     * 根据数组长度,构建 判断点是否在面内 的压平函数
							 | 
						|||
| 
								 | 
							
								     */
							 | 
						|||
| 
								 | 
							
								    getIsinPolygonFun(polygons) {
							 | 
						|||
| 
								 | 
							
								        let pmap = polygons.map((polygon) => polygon.length);
							 | 
						|||
| 
								 | 
							
								        let uniqueArray = this.getUniqueArray(pmap);
							 | 
						|||
| 
								 | 
							
								        let str = ``;
							 | 
						|||
| 
								 | 
							
								        uniqueArray.forEach(length => {
							 | 
						|||
| 
								 | 
							
								            str += `
							 | 
						|||
| 
								 | 
							
								                vec2 points_${length}[${length}];
							 | 
						|||
| 
								 | 
							
								                bool isPointInPolygon_${length}(vec2 point){
							 | 
						|||
| 
								 | 
							
								                int nCross = 0; // 交点数
							 | 
						|||
| 
								 | 
							
								                const int n = ${length}; 
							 | 
						|||
| 
								 | 
							
								                for(int i = 0; i < n; i++){
							 | 
						|||
| 
								 | 
							
								                    vec2 p1 = points_${length}[i];
							 | 
						|||
| 
								 | 
							
								                    vec2 p2 = points_${length}[int(mod(float(i+1),float(n)))];
							 | 
						|||
| 
								 | 
							
								                    if(p1[1] == p2[1]){
							 | 
						|||
| 
								 | 
							
								                        continue;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    if(point[1] < min(p1[1], p2[1])){
							 | 
						|||
| 
								 | 
							
								                        continue;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    if(point[1] >= max(p1[1], p2[1])){
							 | 
						|||
| 
								 | 
							
								                        continue;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                    float x = p1[0] + ((point[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]);
							 | 
						|||
| 
								 | 
							
								                    if(x > point[0]){
							 | 
						|||
| 
								 | 
							
								                     nCross++;
							 | 
						|||
| 
								 | 
							
								                    }
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								                return int(mod(float(nCross), float(2))) == 1;
							 | 
						|||
| 
								 | 
							
								                }
							 | 
						|||
| 
								 | 
							
								            `
							 | 
						|||
| 
								 | 
							
								        })
							 | 
						|||
| 
								 | 
							
								        return str
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    updateShader(vtx1, vtx2) {
							 | 
						|||
| 
								 | 
							
								        let flatCustomShader = new Cesium.CustomShader({
							 | 
						|||
| 
								 | 
							
								            uniforms: {
							 | 
						|||
| 
								 | 
							
								                u_tileset_localToWorldMatrix: {
							 | 
						|||
| 
								 | 
							
								                    type: Cesium.UniformType.MAT4,
							 | 
						|||
| 
								 | 
							
								                    value: this.matrix,
							 | 
						|||
| 
								 | 
							
								                },
							 | 
						|||
| 
								 | 
							
								                u_tileset_worldToLocalMatrix: {
							 | 
						|||
| 
								 | 
							
								                    type: Cesium.UniformType.MAT4,
							 | 
						|||
| 
								 | 
							
								                    value: this.localMatrix,
							 | 
						|||
| 
								 | 
							
								                },
							 | 
						|||
| 
								 | 
							
								                u_flatHeight: {
							 | 
						|||
| 
								 | 
							
								                    type: Cesium.UniformType.FLOAT,
							 | 
						|||
| 
								 | 
							
								                    value: this.flatHeight,
							 | 
						|||
| 
								 | 
							
								                },
							 | 
						|||
| 
								 | 
							
								            },
							 | 
						|||
| 
								 | 
							
								            vertexShaderText: `
							 | 
						|||
| 
								 | 
							
								            // 所有isPointInPolygon函数
							 | 
						|||
| 
								 | 
							
								            ${vtx1}
							 | 
						|||
| 
								 | 
							
								            void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput){
							 | 
						|||
| 
								 | 
							
								                vec3 modelMC = vsInput.attributes.positionMC;
							 | 
						|||
| 
								 | 
							
								                vec4 model_local_position = vec4(modelMC.x, modelMC.y, modelMC.z, 1.0);
							 | 
						|||
| 
								 | 
							
								                vec4 tileset_local_position = u_tileset_worldToLocalMatrix * czm_model * model_local_position;
							 | 
						|||
| 
								 | 
							
								                vec2 position2D = vec2(tileset_local_position.x,tileset_local_position.y);
							 | 
						|||
| 
								 | 
							
								                float ground_z = 0.0 + u_flatHeight;
							 | 
						|||
| 
								 | 
							
								                // 多个多边形区域
							 | 
						|||
| 
								 | 
							
								                ${vtx2}
							 | 
						|||
| 
								 | 
							
								            }`,
							 | 
						|||
| 
								 | 
							
								        });
							 | 
						|||
| 
								 | 
							
								        this.tileset.customShader = flatCustomShader;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    // 数组去重,不能处理嵌套的数组
							 | 
						|||
| 
								 | 
							
								    getUniqueArray = (arr) => {
							 | 
						|||
| 
								 | 
							
								        return arr.filter(function (item, index, arr) {
							 | 
						|||
| 
								 | 
							
								            //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
							 | 
						|||
| 
								 | 
							
								            return arr.indexOf(item, 0) === index;
							 | 
						|||
| 
								 | 
							
								        });
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								    // 世界坐标转数组局部坐标
							 | 
						|||
| 
								 | 
							
								    cartesiansToLocal(positions) {
							 | 
						|||
| 
								 | 
							
								        let arr = [];
							 | 
						|||
| 
								 | 
							
								        for (let i = 0; i < positions.length; i++) {
							 | 
						|||
| 
								 | 
							
								            let position = positions[i];
							 | 
						|||
| 
								 | 
							
								            let localp = Cesium.Matrix4.multiplyByPoint(
							 | 
						|||
| 
								 | 
							
								                this.localMatrix,
							 | 
						|||
| 
								 | 
							
								                position.clone(),
							 | 
						|||
| 
								 | 
							
								                new Cesium.Cartesian3()
							 | 
						|||
| 
								 | 
							
								            )
							 | 
						|||
| 
								 | 
							
								            arr.push([localp.x, localp.y]);
							 | 
						|||
| 
								 | 
							
								        }
							 | 
						|||
| 
								 | 
							
								        return arr;
							 | 
						|||
| 
								 | 
							
								    }
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								 
							 | 
						|||
| 
								 | 
							
								}
							 | 
						|||
| 
								 | 
							
								 
							 |