Skip to content

Commit

Permalink
gltf sink: ENU座標系で出力する (#603)
Browse files Browse the repository at this point in the history
Close #602

### Description(変更内容)

See #602

- 「glTF出力」のglTFを 局所座標系(ENU座標系)で出力する。
- グローバルバウンディングボックスの計算を改善する(事前に計算しておくことが必要、かつ、より良い)

![Screenshot 2024-07-10 at 15 16
50](https://github.com/MIERUNE/plateau-gis-converter/assets/5351911/693a7a86-adff-4b1d-9239-c2b90017e1c2)

### Notes(連絡事項)

- おまけで生成される 3D Tiles 用のデータの正しさはまだ確認していないです。

---------

Co-authored-by: nokonoko1203 <[email protected]>
  • Loading branch information
ciscorn and nokonoko1203 authored Jul 11, 2024
1 parent e69c595 commit 11e5a8b
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 115 deletions.
1 change: 1 addition & 0 deletions nusamai/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
52 changes: 1 addition & 51 deletions nusamai/src/sink/gltf/gltf_writer.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
use std::{
fs::File,
io::{BufWriter, Write},
path::Path,
};
use std::io::Write;

use byteorder::{ByteOrder, LittleEndian};
use indexmap::IndexSet;
Expand All @@ -17,7 +13,6 @@ use crate::{
pub fn write_gltf_glb<W: Write>(
feedback: &feedback::Feedback,
writer: W,
translation: [f64; 3],
vertices: impl IntoIterator<Item = [u32; 9]>,
primitives: Primitives,
metadata_encoder: metadata::MetadataEncoder,
Expand Down Expand Up @@ -237,7 +232,6 @@ pub fn write_gltf_glb<W: Write>(
}],
nodes: vec![Node {
mesh: (!primitives.is_empty()).then_some(0),
translation,
..Default::default()
}],
meshes: gltf_meshes,
Expand Down Expand Up @@ -269,47 +263,3 @@ pub fn write_gltf_glb<W: Write>(

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<cesiumtiles::tileset::Content> = 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(())
}
100 changes: 36 additions & 64 deletions nusamai/src/sink/gltf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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<f64> = Earcut::new();
let mut buf3d: Vec<[f64; 3]> = Vec::new();
Expand All @@ -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() {
Expand All @@ -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
Expand Down Expand Up @@ -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());

Expand All @@ -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(())
}
}

0 comments on commit 11e5a8b

Please sign in to comment.