diff --git a/nusamai/Cargo.toml b/nusamai/Cargo.toml index 2ab6bcc0b..e1fdf950e 100644 --- a/nusamai/Cargo.toml +++ b/nusamai/Cargo.toml @@ -51,6 +51,7 @@ chrono = "0.4.35" kv-extsort = { git = "https://github.com/MIERUNE/kv-extsort-rs.git" } bytemuck = { version = "1.16.0", features = ["derive"] } dda-voxelize = "0.2.0-alpha.1" +glam = "0.28.0" [dev-dependencies] rand = "0.8.5" diff --git a/nusamai/src/sink/gltf/gltf_writer.rs b/nusamai/src/sink/gltf/gltf_writer.rs index acafa32fc..f5ad4d945 100644 --- a/nusamai/src/sink/gltf/gltf_writer.rs +++ b/nusamai/src/sink/gltf/gltf_writer.rs @@ -1,8 +1,4 @@ -use std::{ - fs::File, - io::{BufWriter, Write}, - path::Path, -}; +use std::io::Write; use byteorder::{ByteOrder, LittleEndian}; use indexmap::IndexSet; @@ -17,7 +13,6 @@ use crate::{ pub fn write_gltf_glb( feedback: &feedback::Feedback, writer: W, - translation: [f64; 3], vertices: impl IntoIterator, primitives: Primitives, metadata_encoder: metadata::MetadataEncoder, @@ -237,7 +232,6 @@ pub fn write_gltf_glb( }], nodes: vec![Node { mesh: (!primitives.is_empty()).then_some(0), - translation, ..Default::default() }], meshes: gltf_meshes, @@ -269,47 +263,3 @@ pub fn write_gltf_glb( Ok(()) } - -// This is the code to verify the operation with Cesium -pub fn write_3dtiles( - bounding_volume: [f64; 6], - output_path: &Path, - filenames: &[String], -) -> std::io::Result<()> { - // write 3DTiles - let tileset_path = output_path.join("tileset.json"); - - let contents: Vec = filenames - .iter() - .map(|filename| { - let uri = filename.to_string(); - cesiumtiles::tileset::Content { - uri, - ..Default::default() - } - }) - .collect(); - - let tileset = cesiumtiles::tileset::Tileset { - geometric_error: 1e+100, - asset: cesiumtiles::tileset::Asset { - version: "1.1".to_string(), - ..Default::default() - }, - root: cesiumtiles::tileset::Tile { - bounding_volume: cesiumtiles::tileset::BoundingVolume { - region: Some(bounding_volume), - ..Default::default() - }, - contents: Some(contents), - ..Default::default() - }, - ..Default::default() - }; - - let mut tileset_file = File::create(tileset_path)?; - let tileset_writer = BufWriter::with_capacity(1024 * 1024, &mut tileset_file); - serde_json::to_writer_pretty(tileset_writer, &tileset)?; - - Ok(()) -} diff --git a/nusamai/src/sink/gltf/mod.rs b/nusamai/src/sink/gltf/mod.rs index 3983409f8..2f37b5bac 100644 --- a/nusamai/src/sink/gltf/mod.rs +++ b/nusamai/src/sink/gltf/mod.rs @@ -2,13 +2,14 @@ mod gltf_writer; mod material; -use std::{fs::File, io::BufWriter, path::PathBuf, sync::Mutex}; +use std::{f64::consts::FRAC_PI_2, fs::File, io::BufWriter, path::PathBuf, sync::Mutex}; use crate::sink::cesiumtiles::utils::calculate_normal; use ahash::{HashMap, HashSet, RandomState}; use earcut::{utils3d::project3d_to_2d, Earcut}; use flatgeom::MultiPolygon; -use gltf_writer::{write_3dtiles, write_gltf_glb}; +use glam::{DMat4, DVec3, DVec4}; +use gltf_writer::write_gltf_glb; use indexmap::IndexSet; use itertools::Itertools; use material::{Material, Texture}; @@ -292,22 +293,40 @@ impl DataSink for GltfSink { Ok::<(), PipelineError>(()) }); + let classified_features = classified_features.into_inner().unwrap(); + // Bounding volume for the entire dataset - let global_bvol = Mutex::new(BoundingVolume::default()); + let global_bvol = { + let mut global_bvol = BoundingVolume::default(); + for features in classified_features.values() { + global_bvol.update(&features.bounding_volume); + } + global_bvol + }; + let tileset_content_files = Mutex::new(Vec::new()); - let classified_features = classified_features.into_inner().unwrap(); + let transform_matrix = { + let bounds = &global_bvol; + let center_lng = (bounds.min_lng + bounds.max_lng) / 2.0; + let center_lat = (bounds.min_lat + bounds.max_lat) / 2.0; + + let psi = ((1. - ellipsoid.e_sq()) * center_lat.to_radians().tan()).atan(); + + let (tx, ty, tz) = geodetic_to_geocentric(&ellipsoid, center_lng, center_lat, 0.); + let h = (tx * tx + ty * ty + tz * tz).sqrt(); + + DMat4::from_translation(DVec3::new(0., -h, 0.)) + * DMat4::from_rotation_x(-(FRAC_PI_2 - psi)) + * DMat4::from_rotation_y((-center_lng - 90.).to_radians()) + }; + let _ = transform_matrix.inverse(); classified_features .into_par_iter() .try_for_each(|(typename, mut features)| { feedback.ensure_not_canceled()?; - global_bvol - .lock() - .unwrap() - .update(&features.bounding_volume); - // Triangulation let mut earcutter: Earcut = Earcut::new(); let mut buf3d: Vec<[f64; 3]> = Vec::new(); @@ -319,21 +338,6 @@ impl DataSink for GltfSink { let mut metadata_encoder = metadata::MetadataEncoder::new(schema); - // triangulation and make vertices and primitives - let translation = { - let bounds = features.bounding_volume; - let (tx, ty, tz) = geodetic_to_geocentric( - &ellipsoid, - (bounds.min_lng + bounds.max_lng) / 2.0, - (bounds.min_lat + bounds.max_lat) / 2.0, - 0., - ); - // z-up to y-up - let [tx, ty, tz] = [tx, tz, -ty]; - // double-precision to single-precision - [(tx as f32) as f64, (ty as f32) as f64, (tz as f32) as f64] - }; - // make vertices and indices let mut feature_id = 0; for feature in features.features.iter_mut() { @@ -345,17 +349,14 @@ impl DataSink for GltfSink { .transform_inplace(|&[lng, lat, height, u, v]| { // geographic to geocentric let (x, y, z) = geodetic_to_geocentric(&ellipsoid, lng, lat, height); - // z-up to y-up - // subtract the translation + let v_xyz = DVec4::new(x, z, -y, 1.0); + // local ENU coordinate + let v_enu = transform_matrix * v_xyz; + // println!("enu: {:?}", v_enu); + // flip the texture v-coordinate - [ - x - translation[0], - z - translation[1], - -y - translation[2], - u, - 1.0 - v, - ] + [v_enu[0], v_enu[1], v_enu[2], u, 1.0 - v] }); // Encode properties @@ -423,13 +424,7 @@ impl DataSink for GltfSink { // Write glTF (.glb) let file_path = { - let filename = format!( - "{}.glb", - typename - .split_once(':') - .map(|(_, s)| s) - .unwrap_or(&typename) - ); + let filename = format!("{}.glb", typename.replace(':', "_")); // Save the filename to the content list of the tileset.json (3D Tiles) tileset_content_files.lock().unwrap().push(filename.clone()); @@ -439,34 +434,11 @@ impl DataSink for GltfSink { let mut file = File::create(file_path)?; let writer = BufWriter::with_capacity(1024 * 1024, &mut file); - write_gltf_glb( - feedback, - writer, - translation, - vertices, - primitives, - metadata_encoder, - )?; + write_gltf_glb(feedback, writer, vertices, primitives, metadata_encoder)?; Ok::<(), PipelineError>(()) })?; - // write 3DTiles - let bounds = global_bvol.lock().unwrap(); - let region: [f64; 6] = [ - bounds.min_lng.to_radians(), - bounds.min_lat.to_radians(), - bounds.max_lng.to_radians(), - bounds.max_lat.to_radians(), - bounds.min_height, - bounds.max_height, - ]; - write_3dtiles( - region, - &self.output_path, - &tileset_content_files.lock().unwrap(), - )?; - Ok(()) } }