UNPKG

@csi-foxbyte/cityjson-to-3d-tiles

Version:

A Node.js library that converts CityJSON files into Cesium 3D Tiles—complete with automatic texture atlas packing, Basis compression, three LOD levels, and customizable threading.

2 lines • 5.75 kB
import {Logger}from'@gltf-transform/core';import {EXTMeshGPUInstancing}from'@gltf-transform/extensions';import {Box3,Vector3,Matrix4,Quaternion}from'three';import {mergeDocuments,flatten,unpartition,prune,draco}from'@gltf-transform/functions';import {EXTInstanceFeatures}from'../EXTMeshFeatures/EXTInstanceFeatures.js';import {EXTStructuralMetadata}from'../EXTMeshFeatures/EXTStructuralMetadata.js';import {BinaryPropertyTableBuilder}from'../EXTMeshFeatures/BinaryPropertyTableBuilder.js';import {StructuralMetadataPropertyTables}from'../EXTMeshFeatures/StructuralMetadataPropertyTables.js';import {getIO}from'./io.js';Logger.DEFAULT_INSTANCE=new Logger(Logger.Verbosity.SILENT);async function wt(h,M,O=0){if(h.length===0)return null;const L=await getIO(),g=new Box3;h.forEach(t=>{g.expandByPoint(new Vector3(t.data.minX,t.data.minY,t.data.minHeight)),g.expandByPoint(new Vector3(t.data.maxX,t.data.maxY,t.data.maxHeight));});const E=new Map,r=new Map;for(const t of h){const e=await M.get("SELECT doc, isInstanced, transformationMatrix, refId from data WHERE name = ?",[t.data.name]);if(!e)throw new Error(`No CityObject found with name "${t.data.name}"`);if(!e.refId)throw new Error(`CityObject with name "${t.data.name}" has no associated refId!`);r.has(e.refId)||r.set(e.refId,[]);const s=new Matrix4().fromArray(e.transformationMatrix.split("@").map(n=>parseFloat(n))),A=new Vector3,i=new Quaternion,y=new Vector3;if(s.decompose(A,i,y),r.get(e.refId).push({position:A,rotation:i,scale:y,gridItem:t.data}),E.has(e.refId))continue;const c=await M.get(`SELECT doc${O} AS doc FROM instancedData WHERE id = ?`,[e.refId]);if(!c)throw new Error(`No CityObject found with name "${t.data.name}"`);if(!c.doc)throw new Error(`CityObject with name "${t.data.name}" has no associated doc!`);const l=await L.readBinary(c.doc);l.getRoot().listMaterials().forEach(n=>{n.setAlphaMode("MASK");}),E.set(e.refId,l);}const _=Array.from(E.entries()).reverse(),[m,a]=_[0],F=r.get(m).map(({position:t})=>t),u=new Vector3;F.forEach(t=>u.add(t)),u.divideScalar(F.length),a.getRoot().listNodes()[0].setTranslation(u.toArray());const x=a.createExtension(EXTMeshGPUInstancing).setRequired(true),V=a.createAccessor("instance_positions").setArray(new Float32Array(r.get(m).flatMap(({position:t})=>t.clone().sub(u).toArray()))).setType("VEC3"),B=a.createAccessor("instance_rotations").setArray(new Float32Array(r.get(m).flatMap(({rotation:t})=>t.toArray()))).setType("VEC4"),X=a.createAccessor("instance_scales").setArray(new Float32Array(r.get(m).flatMap(({scale:t})=>t.toArray()))).setType("VEC3");let C=0;const R=a.createAccessor("instance_ids").setArray(new Uint16Array(r.get(m).map(()=>C++))).setType("SCALAR"),v=x.createInstancedMesh().setAttribute("TRANSLATION",V).setAttribute("ROTATION",B).setAttribute("SCALE",X).setAttribute("_FEATURE_ID_0",R),I=a.createExtension(EXTStructuralMetadata),T={id:"schema_01",name:"Schema 01",description:"An example schema",version:"1.0.0",enums:{},classes:{tree:{name:"Tree",description:"Jo",properties:{id:{name:"ID",type:"STRING"}}}}},N=new Set;for(const t of Array.from(r.values()).flatMap(e=>e.map(s=>s.gridItem.attributes)))for(const[e]of Object.entries(t))T.classes.tree.properties[e]={name:e,type:"STRING"},N.add(e);const P=I.createStructuralMetadata().setSchema(I.createSchemaFrom(T)),p={};Array.from(r.values()).forEach(t=>t.forEach(e=>{for(const s of N.values())p[s]||(p[s]=[]),p[s].push(String(e.gridItem.attributes[s]??"-"));}));const j=BinaryPropertyTableBuilder.create(T,"tree","Property Table").addProperty("id",Array.from(r.values()).flatMap(t=>t.map(e=>e.gridItem.name))).addProperties(p).build(),b=StructuralMetadataPropertyTables.create(I,j);P.addPropertyTable(b),a.getRoot().setExtension("EXT_structural_metadata",P);const f=a.createExtension(EXTInstanceFeatures),G=f.createFeatureId().setAttribute(0).setPropertyTable(b).setFeatureCount(new Set(R.getArray()).size),U=f.createInstanceFeatures().addFeatureId(G);a.getRoot().listNodes()[0].setExtension("EXT_mesh_gpu_instancing",v).setExtension("EXT_instance_features",U);for(const[t,e]of _.slice(1)){const s=mergeDocuments(a,e),A=a.getRoot().listScenes()[0],i=s.get(e.getRoot().listScenes()[0]),y=a.createNode().setName("SceneB");if(i.listChildren().length!==1)throw new Error("Instanced mesh should only have 1 node!");const c=y.addChild(i.listChildren()[0]).setName(t),l=r.get(t).map(({position:o})=>o),n=new Vector3;l.forEach(o=>n.add(o)),n.divideScalar(l.length);const w=c.getTranslation();c.listChildren()[0].setTranslation([w[0]+n.x,w[1]+n.y,w[2]+n.z]);const $=a.createAccessor("instance_positions").setArray(new Float32Array(r.get(t).flatMap(({position:o})=>o.clone().sub(n).toArray()))).setType("VEC3"),k=a.createAccessor("instance_rotations").setArray(new Float32Array(r.get(t).flatMap(({rotation:o})=>o.toArray()))).setType("VEC4"),z=a.createAccessor("instance_scales").setArray(new Float32Array(r.get(t).flatMap(({scale:o})=>o.toArray()))).setType("VEC3"),D=a.createAccessor("instance_ids").setArray(new Uint16Array(r.get(t).map(()=>C++))).setType("SCALAR"),H=x.createInstancedMesh().setAttribute("TRANSLATION",$).setAttribute("ROTATION",k).setAttribute("SCALE",z).setAttribute("_FEATURE_ID_0",D),q=f.createFeatureId().setAttribute(0).setPropertyTable(b).setFeatureCount(new Set(D.getArray()).size),Q=f.createInstanceFeatures().addFeatureId(q);c.listChildren()[0].setExtension("EXT_mesh_gpu_instancing",H).setExtension("EXT_instance_features",Q),A.addChild(c),i.dispose();}return await a.transform(flatten(),unpartition(),prune(),draco({method:"edgebreaker",quantizationVolume:"mesh"})),{document:a,localBBox:g}}export{wt as generateInstancedDocument};//# sourceMappingURL=generateInstancedDocument.js.map //# sourceMappingURL=generateInstancedDocument.js.map