@csi-foxbyte/mesh-dem-to-terrain
Version:
A Node.js library that converts DEM meshes (DXF, GeoJSON, etc.) into Cesium Terrain Tiles (quantized-mesh or heightmap) with flexible zoom-level control and multi-threading support.
2 lines • 5.39 kB
JavaScript
import {WebMercatorProjection,Ellipsoid,Cartesian3}from'cesium';import P from'lodash';import {Box3,Vector3,Sphere}from'three';import {getNewellsNormal}from'./helpers.js';const y=3*8+2*4+4*8+3*8;function N(g){return g<<1^g>>31}function x(g,e,i){let r=0;for(const c of i){const h=c,n=h-r;r=h;const t=N(n);g.writeUInt16LE(t,e),e+=2;}return e}function f(g,e,i,r,c){const h=c?(t,o)=>g.writeUInt32LE(t,o):(t,o)=>g.writeUInt16LE(t,o),n=c?4:2;if(r){let t=0;for(const o of i){const s=t-o;h(s,e),e+=n,s===0&&t++;}return e}else {for(const t of i)h(t,e),e+=n;return e}}class O{constructor(e,i,r,c,h,n,t){this.u=new Uint16Array;this.v=new Uint16Array;this.height=new Uint16Array;this.octEncodedNormalsCount=0;this.octEncodedNormals=[];this.minimumHeight=1/0,this.maximumHeight=-1/0;const o=new Box3,s=[];for(let m=0;m<e.length;){const l=[e[m++],e[m++],e[m++]],d=new WebMercatorProjection(Ellipsoid.WGS84).unproject(new Cartesian3(...l)),p=Ellipsoid.WGS84.cartographicToCartesian(d);s.push(new Vector3(p.x,p.y,p.z)),o.expandByPoint(s[s.length-1]),this.minimumHeight=Math.min(this.minimumHeight,l[2]),this.maximumHeight=Math.max(this.maximumHeight,l[2]);}t.max.z=this.maximumHeight,t.min.z=this.minimumHeight;const a=new Sphere().setFromPoints(s),u=new Vector3;o.getCenter(u),this.indices=i,this.eastIndices=r,this.westIndices=c,this.southIndices=h,this.northIndices=n,this.centerX=u.x,this.centerY=u.y,this.centerZ=u.z,this.boundingSphereCenterX=a.center.x,this.boundingSphereCenterY=a.center.y,this.boundingSphereCenterZ=a.center.z,this.boundingSphereRadius=a.radius,this.horizonOcclusionPointX=NaN,this.horizonOcclusionPointY=-1/0,this.horizonOcclusionPointZ=NaN,this.vertexCount=e.length/3,this.triangleCount=i.length/3,this.createPositionVectors(e,t),this._computeVertexNormals(s,i),this._computeHorizonOcclusionPoint(s);}_computeHorizonOcclusionPoint(e){const i=new Cartesian3;function r(s,a){const u=Ellipsoid.WGS84.transformPositionToScaledSpace(s),m=new Vector3(u.x,u.y,u.z);let l=m.lengthSq(),d=Math.sqrt(l);const p=m.divideScalar(d);l=Math.max(1,l),d=Math.max(1,d);const S=p.dot(a),z=p.cross(a).length(),I=1/d,v=Math.sqrt(l-1)*I;return 1/(S*I-z*v)}const c=Ellipsoid.WGS84.transformPositionToScaledSpace(new Cartesian3(this.centerX,this.centerY,this.centerZ)),h=new Vector3(c.x,c.y,c.z);let t=-1/0;for(const s of e){i.x=s.x,i.y=s.y,i.z=s.z;const a=r(i,h);t=Math.max(a,t);}h.multiplyScalar(t);const o=Ellipsoid.WGS84.transformPositionFromScaledSpace(new Cartesian3(h.x,h.y,h.z));this.horizonOcclusionPointX=o.x,this.horizonOcclusionPointY=o.y,this.horizonOcclusionPointZ=o.z;}_computeVertexNormals(e,i){const r=e.map(()=>new Vector3(0,0,0));for(let t=0;t<i.length;){const o=i[t++],s=e[o],a=i[t++],u=e[a],m=i[t++],l=e[m],d=getNewellsNormal([s,u,l]);r[o].add(d),r[a].add(d),r[m].add(d);}r.forEach(t=>{t.length()<1e-6?t.set(0,0,1):t.normalize();});const c=[],h=t=>Math.round((P.clamp(t,-1,1)*.5+.5)*255),n=t=>t<0?-1:1;for(const t of r){const o=Math.abs(t.x)+Math.abs(t.y)+Math.abs(t.z);let s=t.x/o,a=t.y/o;if(t.z<0){const u=1-Math.abs(a)*n(s),m=1-Math.abs(s)*n(a);s=u,a=m;}c.push(h(s),h(a));}this.octEncodedNormals=c,this.octEncodedNormalsCount=c.length;}createPositionVectors(e,i){const r=new Uint16Array(e.length/3),c=new Uint16Array(e.length/3),h=new Uint16Array(e.length/3);let n=0;for(let t=0;t<e.length;){const o=e[t++],s=e[t++],a=e[t++],u=Math.round((o-i.min.x)/Math.max(i.max.x-i.min.x,.001)*32767),m=Math.round((s-i.min.y)/Math.max(i.max.y-i.min.y,.001)*32767),l=Math.round((a-this.minimumHeight)/Math.max(this.maximumHeight-this.minimumHeight,.001)*32767);r[n]=u,c[n]=m,h[n]=l,n++;}this.u=r,this.v=c,this.height=h;}async serialize(){const e=this.triangleCount>65536,i=4+2*this.vertexCount+2*this.vertexCount+2*this.vertexCount,r=e?4:2,c=(y+i)%r,h=y+i+c+4+r*this.indices.length+4+r*this.westIndices.length+4+r*this.southIndices.length+4+r*this.eastIndices.length+4+r*this.northIndices.length+1+4+1*this.vertexCount*2,n=Buffer.alloc(h);let t=0;n.writeDoubleLE(this.centerX,t),t+=8,n.writeDoubleLE(this.centerY,t),t+=8,n.writeDoubleLE(this.centerZ,t),t+=8,n.writeFloatLE(this.minimumHeight,t),t+=4,n.writeFloatLE(this.maximumHeight,t),t+=4,n.writeDoubleLE(this.boundingSphereCenterX,t),t+=8,n.writeDoubleLE(this.boundingSphereCenterY,t),t+=8,n.writeDoubleLE(this.boundingSphereCenterZ,t),t+=8,n.writeDoubleLE(this.boundingSphereRadius,t),t+=8,n.writeDoubleLE(this.horizonOcclusionPointX,t),t+=8,n.writeDoubleLE(this.horizonOcclusionPointY,t),t+=8,n.writeDoubleLE(this.horizonOcclusionPointZ,t),t+=8,n.writeUInt32LE(this.vertexCount,t),t+=4,t=x(n,t,this.u),t=x(n,t,this.v),t=x(n,t,this.height);for(let o=0;o<c;o++)n.writeUint8(0,t++);if(t%r!==0)throw new Error("Needs padding!");n.writeUint32LE(this.triangleCount,t),t+=4,t=f(n,t,this.indices,true,e),n.writeUint32LE(this.westIndices.length,t),t+=4,t=f(n,t,this.westIndices,false,e),n.writeUint32LE(this.southIndices.length,t),t+=4,t=f(n,t,this.southIndices,false,e),n.writeUint32LE(this.eastIndices.length,t),t+=4,t=f(n,t,this.eastIndices,false,e),n.writeUint32LE(this.northIndices.length,t),t+=4,t=f(n,t,this.northIndices,false,e),n.writeUint8(1,t++),n.writeUint32LE(this.octEncodedNormalsCount*2,t),t+=4;for(const o of this.octEncodedNormals)n.writeUint8(o,t++);if(t!==h)throw new Error("Buffer length didnt match!");return n}}export{O as QuantizedMesh};//# sourceMappingURL=quantizedMesh.js.map
//# sourceMappingURL=quantizedMesh.js.map