Skip to content

Commit

Permalink
extract function
Browse files Browse the repository at this point in the history
  • Loading branch information
nokonoko1203 committed Nov 29, 2023
1 parent c392805 commit 226eb5a
Showing 1 changed file with 133 additions and 111 deletions.
244 changes: 133 additions & 111 deletions nusamai-gltf/examples/geometry_to_gltf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,81 +264,87 @@ struct Args {
filenames: Vec<String>,
}

fn main() {
let args = Args::parse();

let mut all_mpolys = Vec::new();

for filename in args.filenames {
let xml = fs::read_to_string(filename).unwrap();
let mut reader = NsReader::from_str(&xml);
reader.trim_text(true);
match parse_body(&mut reader) {
Ok(mpolys) => {
println!(
"features={features} polygons={polygons}",
features = mpolys.len(),
polygons = mpolys.iter().flatten().count()
);
all_mpolys.extend(mpolys);
}
Err(e) => match e {
ParseError::XmlError(e) => {
println!("Error at position {}: {:?}", reader.buffer_position(), e)
}
},
};
}

// NOTE: この時点で MultiPolygon にジオメトリデータが詰め込まれている状態
//
// ここから先は glTF 形式での出力を行う。

// 中心の経緯度を求める
let (mu_lat, mu_lng) = {
let (mut mu_lat, mut mu_lng) = (0.0, 0.0);
let mut num_features = 0;
for mpoly in &all_mpolys {
let (mut feat_mu_lng, mut feat_mu_lat) = (0.0, 0.0);
let mut num_verts = 0;
for poly in mpoly {
for v in poly.coords().chunks_exact(3) {
num_verts += 1;
feat_mu_lng += v[0];
feat_mu_lat += v[1];
}
}
if num_verts > 0 {
num_features += 1;
mu_lat += feat_mu_lng / num_verts as f64;
mu_lng += feat_mu_lat / num_verts as f64;
}
fn make_glb(gltf_string: String, indices: Vec<u32>, vertices: IndexSet<[u32; 3]>) -> Vec<u8> {
// JSONチャンクをバイナリに変換し、4の倍数に調整
let json_chunk = gltf_string.as_bytes();
let json_chunk_len = json_chunk.len();
let json_chunk_padded = {
let mut v = json_chunk.to_vec();
while v.len() % 4 != 0 {
v.push(0); // 4バイト境界に合わせるために0でパディング
}
(mu_lat / num_features as f64, mu_lng / num_features as f64)
v
};
println!("{} {}", mu_lat, mu_lng);

// 三角分割
// verticesは頂点の配列だが、u32のビットパターンで格納されている
let (indices, vertices) = tessellation(&all_mpolys, mu_lng, mu_lat).unwrap();
// JSONチャンクヘッダー
// この長さはパディングを含まない元のJSONデータの長さ
let json_chunk_header = [
json_chunk_len as u32, // パディングなしの長さ
0x4E4F534A, // JSON (リトルエンディアンで "JSON")
];

// バイナリバッファを作成
let mut file = BufWriter::new(fs::File::create("./data/data.bin").unwrap());
// バイナリデータの読み込み
// let binary_data = fs::read("./data/data.bin").unwrap();

// indicesとverticesをファイルに書き込まずメモリ上に保持する
let mut indices_buf = Vec::new();
let mut vertices_buf = Vec::new();

// glTFのバイナリはリトルエンディアン
for index in &indices {
file.write_u32::<LittleEndian>(*index).unwrap();
indices_buf.write_u32::<LittleEndian>(*index).unwrap();
}

for vertex in &vertices {
for v in vertex {
// glTFのバイナリはリトルエンディアン
file.write_f32::<LittleEndian>(f32::from_bits(*v)).unwrap();
vertices_buf
.write_f32::<LittleEndian>(f32::from_bits(*v))
.unwrap();
}
}

// ファイルを書き込み
let _ = file.flush();
let binary_data = [&indices_buf[..], &vertices_buf[..]].concat();
let binary_len = indices_buf.len() + vertices_buf.len();

// バイナリチャンクヘッダー
let bin_chunk_header = [
binary_len as u32,
0x004E4942, // BIN (リトルエンディアンで "BIN")
];

// ファイル全体の長さ
// この長さはパディングを含む
let total_length = 12 + 8 + json_chunk_padded.len() + 8 + binary_len;

// GLBヘッダー
let glb_header = [
0x46546C67, // glTF (リトルエンディアンで "glTF")
2,
total_length as u32, // ファイル全体の長さ
];

// ファイル作成前にバイナリを作成
let mut glb = Vec::new();

// ヘッダーの書き込み
glb.write_all(&glb_header[0].to_le_bytes());
glb.write_all(&glb_header[1].to_le_bytes());
glb.write_all(&glb_header[2].to_le_bytes());

// JSONチャンクの書き込み
glb.write_u32::<LittleEndian>(json_chunk_header[0]);
glb.write_u32::<LittleEndian>(json_chunk_header[1]);
glb.write_all(&json_chunk_padded);

// バイナリチャンクの書き込み
glb.write_u32::<LittleEndian>(bin_chunk_header[0]);
glb.write_u32::<LittleEndian>(bin_chunk_header[1]);
glb.write_all(&binary_data);

glb
}

fn make_gltf_json(indices: &Vec<u32>, vertices: &IndexSet<[u32; 3]>) -> String {
// glTF のモデルを作成
let mut gltf = Gltf::new();

Expand Down Expand Up @@ -392,7 +398,7 @@ fn main() {
accessor2.type_ = AccessorType::Vec3;
let mut max_vertex: [f32; 3] = [f32::MIN; 3];
let mut min_vertex: [f32; 3] = [f32::MAX; 3];
for vertex in &vertices {
for vertex in vertices {
for (i, v) in vertex.iter().enumerate() {
let v = f32::from_bits(*v);
if v > max_vertex[i] {
Expand Down Expand Up @@ -446,67 +452,83 @@ fn main() {

// glTF の JSON を出力
let gltf_string = gltf.to_string().unwrap();
fs::write("./data/data.gltf", &gltf_string).unwrap();

// glbを作成
// fs::write("./data/data.gltf", &gltf_string).unwrap();
gltf_string
}

// JSONチャンクをバイナリに変換し、4の倍数に調整
let json_chunk = gltf_string.as_bytes();
let json_chunk_len = json_chunk.len();
let json_chunk_padded = {
let mut v = json_chunk.to_vec();
while v.len() % 4 != 0 {
v.push(0); // 4バイト境界に合わせるために0でパディング
fn calc_center(all_mpolys: &Vec<nusamai_geometry::MultiPolygon<'_, 3>>) -> (f64, f64) {
// 中心の経緯度を求める
let (mu_lat, mu_lng) = {
let (mut mu_lat, mut mu_lng) = (0.0, 0.0);
let mut num_features = 0;
for mpoly in all_mpolys {
let (mut feat_mu_lng, mut feat_mu_lat) = (0.0, 0.0);
let mut num_verts = 0;
for poly in mpoly {
for v in poly.coords().chunks_exact(3) {
num_verts += 1;
feat_mu_lng += v[0];
feat_mu_lat += v[1];
}
}
if num_verts > 0 {
num_features += 1;
mu_lat += feat_mu_lng / num_verts as f64;
mu_lng += feat_mu_lat / num_verts as f64;
}
}
v
(mu_lat / num_features as f64, mu_lng / num_features as f64)
};
println!("{} {}", mu_lat, mu_lng);
(mu_lat, mu_lng)
}
fn main() {
let args = Args::parse();

// JSONチャンクヘッダー
// この長さはパディングを含まない元のJSONデータの長さ
let json_chunk_header = [
json_chunk_len as u32, // パディングなしの長さ
0x4E4F534A, // JSON (リトルエンディアンで "JSON")
];
let mut all_mpolys = Vec::new();

// バイナリデータの読み込み
let binary_data = fs::read("./data/data.bin").unwrap();
let binary_len = binary_data.len();
for filename in args.filenames {
let xml = fs::read_to_string(filename).unwrap();
let mut reader = NsReader::from_str(&xml);
reader.trim_text(true);
match parse_body(&mut reader) {
Ok(mpolys) => {
println!(
"features={features} polygons={polygons}",
features = mpolys.len(),
polygons = mpolys.iter().flatten().count()
);
all_mpolys.extend(mpolys);
}
Err(e) => match e {
ParseError::XmlError(e) => {
println!("Error at position {}: {:?}", reader.buffer_position(), e)
}
},
};
}

// バイナリチャンクヘッダー
let bin_chunk_header = [
binary_len as u32,
0x004E4942, // BIN (リトルエンディアンで "BIN")
];
// NOTE: この時点で MultiPolygon にジオメトリデータが詰め込まれている状態
//
// ここから先は glb 形式での出力を行う。

// ファイル全体の長さ
// この長さはパディングを含む
let total_length = 12 + 8 + json_chunk_padded.len() + 8 + binary_len;
let (mu_lat, mu_lng) = calc_center(&all_mpolys);

// GLBヘッダー
let glb_header = [
0x46546C67, // glTF (リトルエンディアンで "glTF")
2,
total_length as u32, // ファイル全体の長さ
];
// 三角分割
// verticesは頂点の配列だが、u32のビットパターンで格納されている
let (indices, vertices) = tessellation(&all_mpolys, mu_lng, mu_lat).unwrap();

// ファイルを作成
let mut file = BufWriter::new(fs::File::create("./data/data.glb").unwrap());
// glTFのJSON文字列を作成
let gltf_string = make_gltf_json(&indices, &vertices);

// ヘッダーの書き込み
file.write_all(&glb_header[0].to_le_bytes());
file.write_all(&glb_header[1].to_le_bytes());
file.write_all(&glb_header[2].to_le_bytes());
// glbを作成

// JSONチャンクの書き込み
file.write_u32::<LittleEndian>(json_chunk_header[0]);
file.write_u32::<LittleEndian>(json_chunk_header[1]);
file.write_all(&json_chunk_padded);
let glb = make_glb(gltf_string, indices, vertices);

// バイナリチャンクの書き込み
file.write_u32::<LittleEndian>(bin_chunk_header[0]);
file.write_u32::<LittleEndian>(bin_chunk_header[1]);
file.write_all(&binary_data);
// ファイルを作成
let mut file = BufWriter::new(fs::File::create("./data/data.glb").unwrap());

// ファイルのフラッシュ
// ファイルの書き込み
file.write_all(&glb.as_slice());
file.flush();
}

0 comments on commit 226eb5a

Please sign in to comment.