From 864125e9e412aa5350ad1b2737ddf1e390dc0d4d Mon Sep 17 00:00:00 2001 From: Teruki TADA Date: Thu, 29 Aug 2024 00:57:48 +0900 Subject: [PATCH] =?UTF-8?q?=E3=83=86=E3=82=AF=E3=82=B9=E3=83=81=E3=83=A3?= =?UTF-8?q?=E3=81=AE=E3=82=B9=E3=82=B1=E3=83=BC=E3=83=AA=E3=83=B3=E3=82=B0?= =?UTF-8?q?=20(#586)=20(#623)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close #586 ### What I did(変更内容) 各ポリゴンについて、一定の距離に対するテクスチャのピクセルの数を制限し、制限を超える場合はテクスチャをスケーリングします。 (文章が長くなるので、距離ごとのピクセル数 = PD とします) 具体的な処理を述べると - 各ポリゴンに対して、構成するvertexの(x,y,z,u,v)から、各辺のユークリッド空間上の距離とテクスチャ上の距離(ピクセル数)を割り出します。その後、各辺ごとのPD(=ピクセル数/ユークリッド距離)の最小値を割り出し、これをポリゴン全体のPDとしています。 - PDの制限値としてMaxPDを設けます (例えばMaxPD=6のときは、許容するPDの最大値が6となります) 。 - MaxPD/PDを求めます。これが1を下回る場合(=テクスチャの解像度が過剰である場合)は、テクスチャのdownsampleの倍率に適用されます。 ### Notes(連絡事項) - uv座標をテクスチャのピクセルの座標に変える処理 (`uv_to_pixel_coords`)を追加しているのですが、`atlas-packer`側の実装と重複しています。ここは仕様が頻りに変わる処理ではなく、`atlas-packer`側の実装を変更してまでDRYを貫く必要性はないと感じているため、ここでは一旦重複した状態のままでコミットしています。 - 現状MaxPDの具体的な数値は、外部からの入力でなく、定数`MAX_PIXEL_PER_DISTANCE`として宣言しています(=15.0)。 --------- Co-authored-by: nokonoko1203 --- nusamai/src/sink/cesiumtiles/mod.rs | 64 +++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/nusamai/src/sink/cesiumtiles/mod.rs b/nusamai/src/sink/cesiumtiles/mod.rs index c733b6d0..32222509 100644 --- a/nusamai/src/sink/cesiumtiles/mod.rs +++ b/nusamai/src/sink/cesiumtiles/mod.rs @@ -46,6 +46,21 @@ use crate::{ }; use utils::calculate_normal; +const MAX_PIXEL_PER_DISTANCE: f64 = 30.0; + +// WARN: This function has an equivalent in `atlas-packer/src/texture.rs`. +fn uv_to_pixel_coords(uv_coords: &[(f64, f64)], width: u32, height: u32) -> Vec<(u32, u32)> { + uv_coords + .iter() + .map(|(u, v)| { + ( + (u.clamp(0.0, 1.0) * width as f64).min(width as f64 - 1.0) as u32, + ((1.0 - v.clamp(0.0, 1.0)) * height as f64).min(height as f64 - 1.0) as u32, + ) + }) + .collect() +} + pub struct CesiumTilesSinkProvider {} impl DataSinkProvider for CesiumTilesSinkProvider { @@ -502,6 +517,7 @@ fn tile_writing_stage( .iter() .map(|[x, y, z, u, v]| (*x, *y, *z, *u, *v)) .collect::>(); + let uv_coords = original_vertices .iter() .map(|(_, _, _, u, v)| (*u, *v)) @@ -509,7 +525,46 @@ 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 factor = apply_downsample_factor(tile_zoom); + + let pixel_coords = + uv_to_pixel_coords(&uv_coords, texture_size.0, texture_size.1); + + let pixel_per_distance = (0..original_vertices.len()) + .map(|i| { + let j = (i + 1) % original_vertices.len(); + let (euc0, txl0) = ( + ( + original_vertices[i].0, + original_vertices[i].1, + original_vertices[i].2, + ), + pixel_coords[i], + ); + let (euc1, txl1) = ( + ( + original_vertices[j].0, + original_vertices[j].1, + original_vertices[j].2, + ), + pixel_coords[j], + ); + let euc_dist = ((euc0.0 - euc1.0).powi(2) + + (euc0.1 - euc1.1).powi(2) + + (euc0.2 - euc1.2).powi(2)) + .sqrt(); + let txl_dist = ((txl0.0 as f64 - txl1.0 as f64).powi(2) + + (txl0.1 as f64 - txl1.1 as f64).powi(2)) + .sqrt(); + txl_dist / euc_dist + }) + .min_by(|a, b| a.total_cmp(b)) + .unwrap_or(1.0); + + let max_pixel_per_distance = MAX_PIXEL_PER_DISTANCE; + let downsample_scale = + (1.0_f64).min(max_pixel_per_distance / pixel_per_distance); + let factor = apply_downsample_factor(tile_zoom, downsample_scale as f32); + let downsample_factor = DownsampleFactor::new(&factor); let cropped_texture = CroppedTexture::new( &texture_uri, @@ -675,11 +730,12 @@ fn tile_writing_stage( Ok(()) } -fn apply_downsample_factor(z: u8) -> f32 { - match z { +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 }