Skip to content

Commit

Permalink
glTF: 法線ベクトルの計算 (#458)
Browse files Browse the repository at this point in the history
close #389 

- 3DTilesの実装に従い、normalの計算をしました
- (ちゃんと影が出ていると思います。)


![image](https://github.com/MIERUNE/PLATEAU-GIS-Converter/assets/31245655/06339dcf-e0eb-4d8c-90d4-53ac71fa9f9a)


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

- **新機能**
	- 3D空間での頂点の法線を計算する機能を追加しました。
- **リファクタ**
	- 頂点の構造を法線ベクトルを含むように変更しました。
	- 頂点配列を扱うロジックを更新しました。

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
nokonoko1203 authored Mar 12, 2024
1 parent 62c4ee1 commit d9161e7
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 47 deletions.
36 changes: 18 additions & 18 deletions nusamai/src/sink/gltf/gltf_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub fn write_gltf_glb<W: Write>(
feedback: &feedback::Feedback,
writer: W,
translation: [f64; 3],
vertices: impl IntoIterator<Item = [u32; 6]>,
vertices: impl IntoIterator<Item = [u32; 9]>,
primitives: Primitives,
features: Features,
schema: &Schema,
Expand All @@ -43,12 +43,12 @@ pub fn write_gltf_glb<W: Write>(
let mut position_max = [f64::MIN; 3];
let mut position_min = [f64::MAX; 3];

const VERTEX_BYTE_STRIDE: usize = 4 * 6; // 4-bytes (f32) x 6
const VERTEX_BYTE_STRIDE: usize = 4 * 9; // 4-bytes (u32) x 9

let buffer_offset = bin_content.len();
let mut buf = [0; VERTEX_BYTE_STRIDE];
for v in vertices {
let [x, y, z, u, v, feature_id] = v;
let [x, y, z, nx, ny, nz, u, v, feature_id] = v;
position_min = [
f64::min(position_min[0], f32::from_bits(x) as f64),
f64::min(position_min[1], f32::from_bits(y) as f64),
Expand All @@ -60,7 +60,7 @@ pub fn write_gltf_glb<W: Write>(
f64::max(position_max[2], f32::from_bits(z) as f64),
];

LittleEndian::write_u32_into(&[x, y, z, u, v, feature_id], &mut buf);
LittleEndian::write_u32_into(&[x, y, z, nx, ny, nz, u, v, feature_id], &mut buf);
bin_content.write_all(&buf)?;
vertices_count += 1;
}
Expand All @@ -87,20 +87,20 @@ pub fn write_gltf_glb<W: Write>(
..Default::default()
});

// // accessor (normal)
// gltf_accessors.push(Accessor {
// buffer_view: Some(gltf_buffer_views.len() as u32 - 1),
// byte_offset: 4 * 3,
// component_type: ComponentType::Float,
// count: vertices_count,
// type_: AccessorType::Vec3,
// ..Default::default()
// });
// accessor (normal)
gltf_accessors.push(Accessor {
buffer_view: Some(gltf_buffer_views.len() as u32 - 1),
byte_offset: 4 * 3,
component_type: ComponentType::Float,
count: vertices_count,
type_: AccessorType::Vec3,
..Default::default()
});

// accessor (tex_coords)
gltf_accessors.push(Accessor {
buffer_view: Some(gltf_buffer_views.len() as u32 - 1),
byte_offset: 4 * 3,
byte_offset: 4 * 6,
component_type: ComponentType::Float,
count: vertices_count,
type_: AccessorType::Vec2,
Expand All @@ -110,7 +110,7 @@ pub fn write_gltf_glb<W: Write>(
// accessor (feature_id)
gltf_accessors.push(Accessor {
buffer_view: Some(gltf_buffer_views.len() as u32 - 1),
byte_offset: 4 * 5,
byte_offset: 4 * 8,
component_type: ComponentType::Float,
count: vertices_count,
type_: AccessorType::Scalar,
Expand Down Expand Up @@ -144,12 +144,12 @@ pub fn write_gltf_glb<W: Write>(
..Default::default()
});

let mut attributes = vec![("POSITION".to_string(), 0)];
let mut attributes = vec![("POSITION".to_string(), 0), ("NORMAL".to_string(), 1)];
// TODO: For no-texture data, it's better to exclude u, v from the vertex buffer
if mat.base_texture.is_some() {
attributes.push(("TEXCOORD_0".to_string(), 1));
attributes.push(("TEXCOORD_0".to_string(), 2));
}
attributes.push(("_FEATURE_ID_0".to_string(), 2));
attributes.push(("_FEATURE_ID_0".to_string(), 3));

gltf_primitives.push(MeshPrimitive {
attributes: attributes.into_iter().collect(),
Expand Down
61 changes: 32 additions & 29 deletions nusamai/src/sink/gltf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
mod gltf_writer;
mod material;
mod metadata;
mod utils;

use indexmap::IndexSet;
use itertools::Itertools;
use std::fs::File;
use std::io::BufWriter;
use std::path::PathBuf;
use std::sync::Mutex;

use ahash::{HashMap, HashSet, RandomState};
use earcut_rs::{utils3d::project3d_to_2d, Earcut};
use indexmap::IndexSet;

use itertools::Itertools;
use nusamai_citygml::{object::ObjectStereotype, schema::Schema, GeometryType, Value};
use nusamai_geometry::MultiPolygon;
use nusamai_geometry::Polygon;
Expand All @@ -27,13 +28,9 @@ use crate::parameters::*;
use crate::pipeline::{Feedback, PipelineError, Receiver, Result};
use crate::sink::{DataRequirements, DataSink, DataSinkProvider, SinkInfo};

use gltf_writer::write_3dtiles;
// use positions::Vertex;

// use self::gltf_writer::build_base_gltf;
use self::gltf_writer::write_gltf_glb;
use self::material::Material;
use self::material::Texture;
use gltf_writer::{write_3dtiles, write_gltf_glb};
use material::{Material, Texture};
use utils::calculate_normal;

pub struct GltfSinkProvider {}

Expand Down Expand Up @@ -273,7 +270,7 @@ impl DataSink for GltfSink {
let mut buf2d: Vec<f64> = Vec::new(); // 2d-projected [x, y]
let mut index_buf: Vec<u32> = Vec::new();

let mut vertices: IndexSet<[u32; 6], RandomState> = IndexSet::default(); // [x, y, z, u, v, feature_id]
let mut vertices: IndexSet<[u32; 9], RandomState> = IndexSet::default(); // [x, y, z, nx, ny, nz, u, v, feature_id]
let mut primitives: Primitives = Default::default();

// make vertices and indices
Expand Down Expand Up @@ -311,25 +308,31 @@ impl DataSink for GltfSink {
let primitive = primitives.entry(mat).or_default();
primitive.feature_ids.insert(feature_id as u32);

if project3d_to_2d(poly.coords(), num_outer, 5, &mut buf2d) {
// earcut
earcutter.earcut(&buf2d, poly.hole_indices(), 2, &mut index_buf);

// collect triangles
primitive.indices.extend(index_buf.iter().map(|idx| {
let pos = *idx as usize * 5;
let [x, y, z, u, v] = poly.coords()[pos..pos + 5].try_into().unwrap();
let vbits = [
(x as f32).to_bits(),
(y as f32).to_bits(),
(z as f32).to_bits(),
(u as f32).to_bits(),
(v as f32).to_bits(),
(feature_id as f32).to_bits(),
];
let (index, _) = vertices.insert_full(vbits);
index as u32
}));
if let Some((nx, ny, nz)) = calculate_normal(poly.exterior().coords(), 5) {
if project3d_to_2d(poly.coords(), num_outer, 5, &mut buf2d) {
// earcut
earcutter.earcut(&buf2d, poly.hole_indices(), 2, &mut index_buf);

// collect triangles
primitive.indices.extend(index_buf.iter().map(|idx| {
let pos = *idx as usize * 5;
let [x, y, z, u, v] =
poly.coords()[pos..pos + 5].try_into().unwrap();
let vbits = [
(x as f32).to_bits(),
(y as f32).to_bits(),
(z as f32).to_bits(),
(nx as f32).to_bits(),
(ny as f32).to_bits(),
(nz as f32).to_bits(),
(u as f32).to_bits(),
(v as f32).to_bits(),
(feature_id as f32).to_bits(),
];
let (index, _) = vertices.insert_full(vbits);
index as u32
}));
}
}
}
}
Expand Down
34 changes: 34 additions & 0 deletions nusamai/src/sink/gltf/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#[inline]
fn cross((ax, ay, az): (f64, f64, f64), (bx, by, bz): (f64, f64, f64)) -> (f64, f64, f64) {
(ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)
}

pub fn calculate_normal(vertices: &[f64], dim: usize) -> Option<(f64, f64, f64)> {
let len = vertices.len();
if len < dim * 3 {
// At least 3 vertices required
return None;
}
let last_point = (
vertices[len - dim],
vertices[len - dim + 1],
vertices[len - dim + 2],
);

let (sum, _) =
vertices
.chunks_exact(dim)
.fold(((0., 0., 0.), last_point), |(acc, prev), data| {
let (x, y, z) = (data[0], data[1], data[2]);
let c = cross(
(prev.0 - x, prev.1 - y, prev.2 - z),
(prev.0 + x, prev.1 + y, prev.2 + z),
);
((acc.0 + c.0, acc.1 + c.1, acc.2 + c.2), (x, y, z))
});

match (sum.0 * sum.0 + sum.1 * sum.1 + sum.2 * sum.2).sqrt() {
d if d < 1e-30 => None,
d => Some((sum.0 / d, sum.1 / d, sum.2 / d)),
}
}

0 comments on commit d9161e7

Please sign in to comment.