Skip to content

Commit

Permalink
地物の間引きロジックを微調整・低解像度化のロジックを修正・WebPの利用 (#660)
Browse files Browse the repository at this point in the history
<!-- Close or Related Issues -->
close #655
close #656

### What I did(変更内容)
<!-- Please describe the motivation behind this PR and the changes it
introduces. -->
<!-- どのような変更をしますか? 目的は? -->

- 地物の間引きロジックを微調整
  - より、間引かないように調整
- 低解像度化のロジックを修正
  - 超高解像度のテクスチャのピクセルあたりの距離を制限することが伝わるように名称を変更
  - 低解像度化ロジックにGeometricErrorを利用
  - ピクセルあたりの距離は、すべての辺の平均値を返却するように変更
- geometricErrorが0であれば低解像度化は行わず(係数1.0)、大きくなれば大きくなるほど低解像度化(係数0.0)されるように調整
-
ピクセルあたりの距離を制限するため係数と、geometricErrorに応じて低解像度化するための係数は別々に計算して、最終的なものを掛け合わせる
- WebPの利用
- その他
  - ズームレベルをユーザーが指定可能に(UIはいじってない)
  - Sinkの処理順を変更し、なるべく最適な大きさのアトラスを出力するように変更
  - OBJ/glTF Sinkも合わせて対応

### Notes(連絡事項)
<!-- If manual testing is required, please describe the procedure. -->
<!-- 手動での動作確認が必要なら手順を簡単に伝えてください。そのほか連絡事項など。 -->

None / なし
  • Loading branch information
nokonoko1203 authored Oct 11, 2024
1 parent 8c5ca67 commit c249c45
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 108 deletions.
1 change: 1 addition & 0 deletions nusamai/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ 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"
atlas-packer = { git = "https://github.com/MIERUNE/atlas-packer.git" }
# atlas-packer = { path = "../atlas_packer" };
tempfile = "3.10.1"
glam = "0.29.0"

Expand Down
142 changes: 73 additions & 69 deletions nusamai/src/sink/cesiumtiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use std::{

use ahash::RandomState;
use atlas_packer::{
export::{AtlasExporter as _, JpegAtlasExporter},
export::{AtlasExporter as _, WebpAtlasExporter},
pack::AtlasPacker,
place::{GuillotineTexturePlacer, TexturePlacerConfig},
texture::{
Expand Down Expand Up @@ -49,8 +49,11 @@ use crate::{
};
use utils::calculate_normal;

use super::option::{limit_texture_resolution_parameter, output_parameter};
use super::texture_resolution::get_texture_downsample_scale_of_polygon;
use super::{
option::{limit_texture_resolution_parameter, output_parameter},
texture_resolution::apply_downsample_factor,
};

pub struct CesiumTilesSinkProvider {}

Expand All @@ -65,6 +68,32 @@ impl DataSinkProvider for CesiumTilesSinkProvider {
fn sink_options(&self) -> Parameters {
let mut params = Parameters::new();
params.define(output_parameter());
params.define(ParameterDefinition {
key: "min_z".into(),
entry: ParameterEntry {
description: "Minumum zoom level".into(),
required: true,
parameter: ParameterType::Integer(IntegerParameter {
value: Some(15),
min: Some(0),
max: Some(20),
}),
label: Some("最小ズームレベル".into()),
},
});
params.define(ParameterDefinition {
key: "max_z".into(),
entry: ParameterEntry {
description: "Maximum zoom level".into(),
required: true,
parameter: ParameterType::Integer(IntegerParameter {
value: Some(18),
min: Some(0),
max: Some(20),
}),
label: Some("最大ズームレベル".into()),
},
});
params.define(limit_texture_resolution_parameter(false));

params
Expand All @@ -79,6 +108,8 @@ impl DataSinkProvider for CesiumTilesSinkProvider {

fn create(&self, params: &Parameters) -> Box<dyn DataSink> {
let output_path = get_parameter_value!(params, "@output", FileSystemPath);
let min_z = get_parameter_value!(params, "min_z", Integer).unwrap() as u8;
let max_z = get_parameter_value!(params, "max_z", Integer).unwrap() as u8;
let limit_texture_resolution =
*get_parameter_value!(params, "limit_texture_resolution", Boolean);
let transform_settings = self.transformer_options();
Expand All @@ -87,6 +118,8 @@ impl DataSinkProvider for CesiumTilesSinkProvider {
output_path: output_path.as_ref().unwrap().into(),
transform_settings,
limit_texture_resolution,
min_z,
max_z,
})
}
}
Expand All @@ -95,6 +128,8 @@ struct CesiumTilesSink {
output_path: PathBuf,
transform_settings: TransformerRegistry,
limit_texture_resolution: Option<bool>,
min_z: u8,
max_z: u8,
}

impl DataSink for CesiumTilesSink {
Expand All @@ -118,9 +153,8 @@ impl DataSink for CesiumTilesSink {

let tile_id_conv = TileIdMethod::Hilbert;

// TODO: configurable
let min_zoom = 15;
let max_zoom = 18;
let min_zoom = self.min_z;
let max_zoom = self.max_z;

let limit_texture_resolution = self.limit_texture_resolution;

Expand Down Expand Up @@ -300,7 +334,7 @@ fn tile_writing_stage(

// Texture cache
// use default cache size
let texture_cache = TextureCache::new(100_000_000);
let texture_cache = TextureCache::new(200_000_000);
let texture_size_cache = TextureSizeCache::new();

// Use a temporary directory for embedding in glb.
Expand Down Expand Up @@ -344,7 +378,7 @@ fn tile_writing_stage(
let geom_error = tiling::geometric_error(tile_zoom, tile_y);
feedback.info(format!(
"tile: z={tile_zoom}, x={tile_x}, y={tile_y} (lng: [{min_lng} => {max_lng}], \
lat: [{min_lat} => {max_lat}) geometricError: {geom_error}"
lat: [{min_lat} => {max_lat}] geometricError: {geom_error}"
));
let content_path = {
let normalized_typename = typename.replace(':', "_");
Expand All @@ -369,51 +403,6 @@ fn tile_writing_stage(

let mut metadata_encoder = metadata::MetadataEncoder::new(schema);

// Check the size of all the textures and calculate the power of 2 of the largest size
let mut max_width = 0;
let mut max_height = 0;
for serialized_feat in feats.iter() {
feedback.ensure_not_canceled()?;

let feature = {
let (feature, _): (SlicedFeature, _) =
bincode::serde::decode_from_slice(serialized_feat, bincode_config)
.map_err(|err| {
PipelineError::Other(format!(
"Failed to deserialize a sliced feature: {:?}",
err
))
})?;

feature
};

for (_, orig_mat_id) in feature
.polygons
.iter()
.zip_eq(feature.polygon_material_ids.iter())
{
let mat = feature.materials[*orig_mat_id as usize].clone();
let t = mat.base_texture.clone();
if let Some(base_texture) = t {
let texture_uri = base_texture.uri.to_file_path().unwrap();
let texture_size = texture_size_cache.get_or_insert(&texture_uri);
max_width = max_width.max(texture_size.0);
max_height = max_height.max(texture_size.1);
}
}
}
let max_width = max_width.next_power_of_two();
let max_height = max_height.next_power_of_two();

// initialize texture packer
// To reduce unnecessary draw calls, set the lower limit for max_width and max_height to 4096
let config = TexturePlacerConfig {
width: max_width.max(2048),
height: max_height.max(2048),
padding: 0,
};

let packer = Mutex::new(AtlasPacker::default());

// transform features
Expand Down Expand Up @@ -488,6 +477,10 @@ fn tile_writing_stage(
format!("{}_{}_{}_{}_{}", z, x, y, feature_id, poly_count)
};

// Check the size of all the textures and calculate the power of 2 of the largest size
let mut max_width = 0;
let mut max_height = 0;

// Load all textures into the Packer
for (feature_id, feature) in features.iter().enumerate() {
for (poly_count, (mat, poly)) in feature
Expand Down Expand Up @@ -516,13 +509,17 @@ fn tile_writing_stage(
let texture_uri = base_texture.uri.to_file_path().unwrap();
let texture_size = texture_size_cache.get_or_insert(&texture_uri);

let downsample_scale = get_texture_downsample_scale_of_polygon(
&original_vertices,
texture_size,
limit_texture_resolution,
);
let factor = apply_downsample_factor(tile_zoom, downsample_scale as f32);
let downsample_scale = if limit_texture_resolution.unwrap_or(false) {
get_texture_downsample_scale_of_polygon(
&original_vertices,
texture_size,
) as f32
} else {
1.0
};

let geom_error = tiling::geometric_error(tile_zoom, tile_y);
let factor = apply_downsample_factor(geom_error, downsample_scale as f32);
let downsample_factor = DownsampleFactor::new(&factor);
let cropped_texture = PolygonMappedTexture::new(
&texture_uri,
Expand All @@ -531,6 +528,12 @@ fn tile_writing_stage(
downsample_factor,
);

let scaled_width = (texture_size.0 as f32 * factor) as u32;
let scaled_height = (texture_size.1 as f32 * factor) as u32;

max_width = max_width.max(scaled_width);
max_height = max_height.max(scaled_height);

// Unique id required for placement in atlas
let (z, x, y) = tile_id_conv.id_to_zxy(tile_id);
let texture_id = generate_texture_id(z, x, y, feature_id, poly_count);
Expand All @@ -543,13 +546,24 @@ fn tile_writing_stage(
}
}

let max_width = max_width.next_power_of_two();
let max_height = max_height.next_power_of_two();

// initialize texture packer
// To reduce unnecessary draw calls, set the lower limit for max_width and max_height to 1024
let config = TexturePlacerConfig {
width: max_width.max(1024),
height: max_height.max(1024),
padding: 0,
};

let placer = GuillotineTexturePlacer::new(config.clone());
let packer = packer.into_inner().unwrap();

// Packing the loaded textures into an atlas
let packed = packer.pack(placer);

let exporter = JpegAtlasExporter::default();
let exporter = WebpAtlasExporter::default();
let ext = exporter.clone().get_extension().to_string();

// Obtain the UV coordinates placed in the atlas by specifying the ID
Expand Down Expand Up @@ -725,13 +739,3 @@ fn tile_writing_stage(

Ok(())
}

fn apply_downsample_factor(z: u8, downsample_scale: f32) -> f32 {
let f = match z {
0..=14 => 0.0,
15..=16 => 0.25,
17 => 0.5,
_ => 1.0,
};
f * downsample_scale
}
2 changes: 1 addition & 1 deletion nusamai/src/sink/cesiumtiles/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub fn slice_to_tiles<E>(
tiling::scheme::zxy_from_lng_lat(zoom, lng_center, lat_center);
tiling::scheme::geometric_error(zoom, y)
};
let threshold = geom_error * 2.0; // TODO: adjustable
let threshold = geom_error / 0.9; // TODO: adjustable
if approx_dx < threshold
&& approx_dy < threshold
&& approx_dh < threshold
Expand Down
15 changes: 10 additions & 5 deletions nusamai/src/sink/gltf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,11 +451,16 @@ impl DataSink for GltfSink {

let texture_uri = base_texture.uri.to_file_path().unwrap();
let texture_size = texture_size_cache.get_or_insert(&texture_uri);
let downsample_scale = get_texture_downsample_scale_of_polygon(
&original_vertices,
texture_size,
self.limit_texture_resolution,
) as f32;

let downsample_scale = if self.limit_texture_resolution.unwrap_or(false)
{
get_texture_downsample_scale_of_polygon(
&original_vertices,
texture_size,
) as f32
} else {
1.0
};

let downsample_factor = DownsampleFactor::new(&downsample_scale);

Expand Down
15 changes: 10 additions & 5 deletions nusamai/src/sink/obj/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,11 +455,16 @@ impl DataSink for ObjSink {

let texture_uri = base_texture.uri.to_file_path().unwrap();
let texture_size = texture_size_cache.get_or_insert(&texture_uri);
let downsample_scale = get_texture_downsample_scale_of_polygon(
&original_vertices,
texture_size,
self.limit_texture_resolution,
) as f32;

let downsample_scale = if self.limit_texture_resolution.unwrap_or(false)
{
get_texture_downsample_scale_of_polygon(
&original_vertices,
texture_size,
) as f32
} else {
1.0
};

let downsample_factor = DownsampleFactor::new(&downsample_scale);

Expand Down
Loading

0 comments on commit c249c45

Please sign in to comment.