From fa6b55da28f5bd92d89fa7f3b27ba5833be6f9a9 Mon Sep 17 00:00:00 2001 From: jiawanlong Date: Fri, 21 Mar 2025 16:42:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=8E=8B=E5=B9=B3=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=88=E5=8F=AF=E4=BB=A5=E6=8C=89=E7=85=A7=E5=A4=9A?= =?UTF-8?q?=E8=BE=B9=E5=BD=A2=E8=BF=9B=E8=A1=8C=E4=B8=8D=E5=90=8C=E9=AB=98?= =?UTF-8?q?=E5=BA=A6=E5=8E=8B=E5=B9=B3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/cesiumEx/3.1.7、模型压平.html | 71 +++++++ examples/cesiumEx/config.js | 7 + examples/cesiumEx/flat.js | 217 ++++++++++++++++++++++ examples/cesiumEx/img/3.1.7、模型压平.jpg | Bin 0 -> 5042 bytes 4 files changed, 295 insertions(+) create mode 100644 examples/cesiumEx/3.1.7、模型压平.html create mode 100644 examples/cesiumEx/flat.js create mode 100644 examples/cesiumEx/img/3.1.7、模型压平.jpg diff --git a/examples/cesiumEx/3.1.7、模型压平.html b/examples/cesiumEx/3.1.7、模型压平.html new file mode 100644 index 00000000..405b9637 --- /dev/null +++ b/examples/cesiumEx/3.1.7、模型压平.html @@ -0,0 +1,71 @@ + + + + + + + + + + + + +
+ + + + + \ No newline at end of file diff --git a/examples/cesiumEx/config.js b/examples/cesiumEx/config.js index 5f8e8558..06955099 100644 --- a/examples/cesiumEx/config.js +++ b/examples/cesiumEx/config.js @@ -548,6 +548,7 @@ var exampleConfig = { thumbnail: "2.4.6、点击获取子节点.jpg", fileName: "2.4.6、点击获取子节点" }, + ] }, } @@ -596,6 +597,12 @@ var exampleConfig = { thumbnail: "3.1.6、点击获取属性.jpg", fileName: "3.1.6、点击获取属性" }, + { + name: "3.1.7、模型压平", + name_en: "3.1.7、模型压平", + thumbnail: "3.1.7、模型压平.jpg", + fileName: "3.1.7、模型压平" + }, ] }, } diff --git a/examples/cesiumEx/flat.js b/examples/cesiumEx/flat.js new file mode 100644 index 00000000..24cc73d3 --- /dev/null +++ b/examples/cesiumEx/flat.js @@ -0,0 +1,217 @@ +/** + * @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; + } + + +} + \ No newline at end of file diff --git a/examples/cesiumEx/img/3.1.7、模型压平.jpg b/examples/cesiumEx/img/3.1.7、模型压平.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5dbeb2bacf264bf6b9047824ce577f67490756e3 GIT binary patch literal 5042 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