Compare commits
5 Commits
e89c1e4c3d
...
ab2e366e7f
| Author | SHA1 | Date |
|---|---|---|
|
|
ab2e366e7f | |
|
|
e7180e57bf | |
|
|
a4503c3dbf | |
|
|
8906f6e47a | |
|
|
e26af202ef |
|
|
@ -1,4 +1,5 @@
|
|||
/target
|
||||
/proptest-regressions
|
||||
*~
|
||||
\#*#
|
||||
.#*
|
||||
|
|
@ -176,15 +176,30 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1"
|
||||
dependencies = [
|
||||
"bit-vec 0.6.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-set"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bit-vec 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
|
||||
|
||||
[[package]]
|
||||
name = "bit-vec"
|
||||
version = "0.8.0"
|
||||
|
|
@ -244,6 +259,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.8.0"
|
||||
|
|
@ -502,6 +523,12 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.34"
|
||||
|
|
@ -512,6 +539,12 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.5.0"
|
||||
|
|
@ -851,6 +884,12 @@ version = "3.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.162"
|
||||
|
|
@ -867,6 +906,12 @@ dependencies = [
|
|||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.3"
|
||||
|
|
@ -971,7 +1016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-set",
|
||||
"bit-set 0.8.0",
|
||||
"bitflags 2.6.0",
|
||||
"cfg_aliases 0.1.1",
|
||||
"codespan-reporting",
|
||||
|
|
@ -1024,6 +1069,16 @@ dependencies = [
|
|||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_enum"
|
||||
version = "0.7.3"
|
||||
|
|
@ -1369,6 +1424,15 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "presser"
|
||||
version = "0.3.1"
|
||||
|
|
@ -1399,6 +1463,26 @@ version = "1.0.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d"
|
||||
|
||||
[[package]]
|
||||
name = "proptest"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d"
|
||||
dependencies = [
|
||||
"bit-set 0.5.3",
|
||||
"bit-vec 0.6.3",
|
||||
"bitflags 2.6.0",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"rand_xorshift",
|
||||
"regex-syntax",
|
||||
"rusty-fork",
|
||||
"tempfile",
|
||||
"unarray",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pteropus"
|
||||
version = "0.1.0"
|
||||
|
|
@ -1410,6 +1494,7 @@ dependencies = [
|
|||
"futures",
|
||||
"glam",
|
||||
"log",
|
||||
"proptest",
|
||||
"tiff",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
|
|
@ -1419,6 +1504,12 @@ dependencies = [
|
|||
"winit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.36.2"
|
||||
|
|
@ -1437,6 +1528,45 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_xorshift"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "range-alloc"
|
||||
version = "0.1.3"
|
||||
|
|
@ -1521,6 +1651,18 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusty-fork"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"quick-error",
|
||||
"tempfile",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
|
|
@ -1671,6 +1813,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
|
|
@ -1775,6 +1930,12 @@ version = "0.25.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e"
|
||||
|
||||
[[package]]
|
||||
name = "unarray"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
|
|
@ -1811,6 +1972,15 @@ version = "0.9.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
|
|
@ -2087,7 +2257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "0e0c68e7b6322a03ee5b83fcd92caeac5c2a932f6457818179f4652ad2a9c065"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-vec",
|
||||
"bit-vec 0.8.0",
|
||||
"bitflags 2.6.0",
|
||||
"cfg_aliases 0.1.1",
|
||||
"document-features",
|
||||
|
|
@ -2114,7 +2284,7 @@ dependencies = [
|
|||
"android_system_properties",
|
||||
"arrayvec",
|
||||
"ash",
|
||||
"bit-set",
|
||||
"bit-set 0.8.0",
|
||||
"bitflags 2.6.0",
|
||||
"block",
|
||||
"bytemuck",
|
||||
|
|
@ -2569,6 +2739,7 @@ version = "0.7.35"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ web-sys = { version = "0.3", features = [
|
|||
"Element",
|
||||
]}
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.5.0"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.34"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use super::raster::Dem;
|
||||
use {
|
||||
bytemuck::{Pod, Zeroable},
|
||||
std::{borrow::Cow, rc::Rc},
|
||||
std::{borrow::Cow, num::NonZero, rc::Rc},
|
||||
wgpu::util::DeviceExt,
|
||||
};
|
||||
|
||||
|
|
@ -12,7 +12,8 @@ pub struct DemRenderer {
|
|||
vertex_buffer: wgpu::Buffer,
|
||||
index_buffer: wgpu::Buffer,
|
||||
index_count: usize,
|
||||
uniforms: Uniforms,
|
||||
camera: Camera,
|
||||
uniforms: UniformBufferManager,
|
||||
animation_start: std::time::Instant,
|
||||
}
|
||||
|
||||
|
|
@ -22,33 +23,36 @@ struct Vertex {
|
|||
position: [f32; 4],
|
||||
}
|
||||
|
||||
struct Uniforms {
|
||||
projection_matrix: glam::Mat4,
|
||||
view_matrix: glam::Mat4,
|
||||
buffer: wgpu::Buffer,
|
||||
impl Vertex {
|
||||
fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self {
|
||||
position: [x, y, z, 1.0],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Uniforms {
|
||||
fn new(device: &wgpu::Device, aspect_ratio: f32) -> Self {
|
||||
let projection_matrix =
|
||||
glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, aspect_ratio, 1.0, 10.0);
|
||||
struct Camera {
|
||||
projection_matrix: glam::Mat4,
|
||||
view_matrix: glam::Mat4,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
fn new(viewport_aspect_ratio: f32) -> Self {
|
||||
let projection_matrix = glam::Mat4::perspective_rh(
|
||||
std::f32::consts::FRAC_PI_4,
|
||||
viewport_aspect_ratio,
|
||||
1.0,
|
||||
10000.0,
|
||||
);
|
||||
let view_matrix = glam::Mat4::look_at_rh(glam::Vec3::ZERO, glam::Vec3::ZERO, glam::Vec3::Z);
|
||||
let camera_matrix = projection_matrix * view_matrix;
|
||||
let camera_matrix_ref: &[f32; 16] = camera_matrix.as_ref();
|
||||
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Uniform Buffer"),
|
||||
contents: bytemuck::cast_slice(camera_matrix_ref),
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
Self {
|
||||
projection_matrix,
|
||||
view_matrix,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_camera_position(&mut self, position: glam::Vec3) {
|
||||
self.view_matrix = glam::Mat4::look_at_rh(position, glam::Vec3::ZERO, glam::Vec3::Z);
|
||||
fn set_position(&mut self, camera_position: glam::Vec3, camera_look_at: glam::Vec3) {
|
||||
self.view_matrix = glam::Mat4::look_at_rh(camera_position, camera_look_at, glam::Vec3::Z);
|
||||
}
|
||||
|
||||
fn set_aspect_ratio(&mut self, ratio: f32) {
|
||||
|
|
@ -56,14 +60,63 @@ impl Uniforms {
|
|||
glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, ratio, 1.0, 10.0);
|
||||
}
|
||||
|
||||
fn update_buffer(&self, queue: &wgpu::Queue) {
|
||||
let camera_matrix = self.projection_matrix * self.view_matrix;
|
||||
let camera_matrix_ref: &[f32; 16] = camera_matrix.as_ref();
|
||||
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(camera_matrix_ref))
|
||||
fn get_matrix(&self) -> glam::Mat4 {
|
||||
self.projection_matrix * self.view_matrix
|
||||
}
|
||||
}
|
||||
|
||||
fn binding(&self) -> wgpu::BindingResource<'_> {
|
||||
self.buffer.as_entire_binding()
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
struct UniformBufferContents {
|
||||
camera_matrix: [f32; 16],
|
||||
dem_texture_size: [u32; 2],
|
||||
}
|
||||
// This struct is 72 bytes but WGSL expects the buffer size to be be a multiple
|
||||
// of the alignment of the member with the largest alignment. The mat4x4<f32>
|
||||
// type has a 16-byte alignment, so we round up to 16 * 5 = 80.
|
||||
const UNIFORM_BUFFER_SIZE: u64 = 80;
|
||||
|
||||
struct UniformBufferManager {
|
||||
cpu_buffer: UniformBufferContents,
|
||||
gpu_buffer: wgpu::Buffer,
|
||||
}
|
||||
|
||||
impl UniformBufferManager {
|
||||
fn new(device: &wgpu::Device) -> Self {
|
||||
let cpu_buffer = UniformBufferContents::zeroed();
|
||||
let gpu_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Uniform Buffer"),
|
||||
contents: &[0u8; UNIFORM_BUFFER_SIZE as usize],
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
});
|
||||
Self {
|
||||
cpu_buffer,
|
||||
gpu_buffer,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_camera_matrix(&mut self, camera_matrix: glam::Mat4) {
|
||||
self.cpu_buffer
|
||||
.camera_matrix
|
||||
.clone_from_slice(&camera_matrix.to_cols_array())
|
||||
}
|
||||
|
||||
fn set_dem_texture_size(&mut self, dem_texture_size: glam::UVec2) {
|
||||
self.cpu_buffer
|
||||
.dem_texture_size
|
||||
.clone_from_slice(&dem_texture_size.to_array());
|
||||
}
|
||||
|
||||
fn get_binding(&self) -> wgpu::BindingResource<'_> {
|
||||
wgpu::BindingResource::Buffer(wgpu::BufferBinding {
|
||||
buffer: &self.gpu_buffer,
|
||||
offset: 0,
|
||||
size: NonZero::new(UNIFORM_BUFFER_SIZE),
|
||||
})
|
||||
}
|
||||
|
||||
fn update_buffer(&self, queue: &wgpu::Queue) {
|
||||
queue.write_buffer(&self.gpu_buffer, 0, bytemuck::bytes_of(&self.cpu_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,8 +125,9 @@ impl DemRenderer {
|
|||
source: Rc<Dem>,
|
||||
device: &wgpu::Device,
|
||||
surface_config: &wgpu::SurfaceConfiguration,
|
||||
queue: &wgpu::Queue,
|
||||
) -> Self {
|
||||
let (vertex_data, index_data) = create_vertices();
|
||||
let (vertex_data, index_data) = create_vertices(&source);
|
||||
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("DemRenderer Vertex Buffer"),
|
||||
|
|
@ -89,25 +143,27 @@ impl DemRenderer {
|
|||
|
||||
let index_count = index_data.len();
|
||||
|
||||
let uniforms = Uniforms::new(
|
||||
device,
|
||||
surface_config.width as f32 / surface_config.height as f32,
|
||||
);
|
||||
let (dem_texture_view, dem_texture_size) = load_dem_texture(&source, device, queue);
|
||||
|
||||
let camera = Camera::new(surface_config.width as f32 / surface_config.height as f32);
|
||||
|
||||
let mut uniforms = UniformBufferManager::new(device);
|
||||
uniforms.set_dem_texture_size(dem_texture_size);
|
||||
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
label: None,
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::VERTEX,
|
||||
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Buffer {
|
||||
ty: wgpu::BufferBindingType::Uniform,
|
||||
has_dynamic_offset: false,
|
||||
min_binding_size: wgpu::BufferSize::new(64),
|
||||
min_binding_size: wgpu::BufferSize::new(UNIFORM_BUFFER_SIZE),
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
/*wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
|
|
@ -116,16 +172,22 @@ impl DemRenderer {
|
|||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
},
|
||||
count: None,
|
||||
},*/
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
entries: &[wgpu::BindGroupEntry {
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: uniforms.binding(),
|
||||
}],
|
||||
resource: uniforms.get_binding(),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&dem_texture_view),
|
||||
},
|
||||
],
|
||||
label: Some("DemRendererBindGroup"),
|
||||
});
|
||||
|
||||
|
|
@ -182,6 +244,7 @@ impl DemRenderer {
|
|||
index_buffer,
|
||||
index_count,
|
||||
animation_start: std::time::Instant::now(),
|
||||
camera,
|
||||
uniforms,
|
||||
}
|
||||
}
|
||||
|
|
@ -190,8 +253,14 @@ impl DemRenderer {
|
|||
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("DemRendererCommandEncoder"),
|
||||
});
|
||||
self.uniforms
|
||||
.set_camera_position(get_animated_camera_position(self.animation_start.elapsed()));
|
||||
self.camera.set_position(
|
||||
get_animated_camera_position(
|
||||
self.animation_start.elapsed(),
|
||||
self.get_max_dem_dimension(),
|
||||
),
|
||||
self.get_dem_centre(),
|
||||
);
|
||||
self.uniforms.set_camera_matrix(self.camera.get_matrix());
|
||||
self.uniforms.update_buffer(queue);
|
||||
{
|
||||
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
|
|
@ -216,46 +285,61 @@ impl DemRenderer {
|
|||
}
|
||||
queue.submit(Some(encoder.finish()));
|
||||
}
|
||||
|
||||
fn get_max_dem_dimension(&self) -> f32 {
|
||||
let dem = &self.source;
|
||||
(dem.x_max - dem.x_min)
|
||||
.max(dem.y_max - dem.y_min)
|
||||
.max(dem.z_max - dem.x_min)
|
||||
}
|
||||
|
||||
fn vertex(pos: [i8; 3]) -> Vertex {
|
||||
Vertex {
|
||||
position: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
|
||||
fn get_dem_centre(&self) -> glam::Vec3 {
|
||||
let dem = &self.source;
|
||||
let min_corner = glam::Vec3::new(dem.x_min, dem.y_min, dem.z_min);
|
||||
let max_corner = glam::Vec3::new(dem.x_max, dem.y_max, dem.z_max);
|
||||
min_corner + (max_corner - min_corner) / 2.0
|
||||
}
|
||||
}
|
||||
|
||||
fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
|
||||
fn create_vertices(dem: &Rc<Dem>) -> (Vec<Vertex>, Vec<u16>) {
|
||||
let x_min = dem.x_min;
|
||||
let x_max = dem.x_max;
|
||||
let y_min = dem.y_min;
|
||||
let y_max = dem.y_max;
|
||||
let z_min = dem.z_min;
|
||||
let z_max = dem.z_max;
|
||||
|
||||
let vertex_data = [
|
||||
// top (0, 0, 1)
|
||||
vertex([-1, -1, 1]),
|
||||
vertex([1, -1, 1]),
|
||||
vertex([1, 1, 1]),
|
||||
vertex([-1, 1, 1]),
|
||||
Vertex::new(x_min, y_min, z_max),
|
||||
Vertex::new(x_max, y_min, z_max),
|
||||
Vertex::new(x_max, y_max, z_max),
|
||||
Vertex::new(x_min, y_max, z_max),
|
||||
// bottom (0, 0, -1)
|
||||
vertex([-1, 1, -1]),
|
||||
vertex([1, 1, -1]),
|
||||
vertex([1, -1, -1]),
|
||||
vertex([-1, -1, -1]),
|
||||
Vertex::new(x_min, y_max, z_min),
|
||||
Vertex::new(x_max, y_max, z_min),
|
||||
Vertex::new(x_max, y_min, z_min),
|
||||
Vertex::new(x_min, y_min, z_min),
|
||||
// right (1, 0, 0)
|
||||
vertex([1, -1, -1]),
|
||||
vertex([1, 1, -1]),
|
||||
vertex([1, 1, 1]),
|
||||
vertex([1, -1, 1]),
|
||||
Vertex::new(x_max, y_min, z_min),
|
||||
Vertex::new(x_max, y_max, z_min),
|
||||
Vertex::new(x_max, y_max, z_max),
|
||||
Vertex::new(x_max, y_min, z_max),
|
||||
// left (-1, 0, 0)
|
||||
vertex([-1, -1, 1]),
|
||||
vertex([-1, 1, 1]),
|
||||
vertex([-1, 1, -1]),
|
||||
vertex([-1, -1, -1]),
|
||||
Vertex::new(x_min, y_min, z_max),
|
||||
Vertex::new(x_min, y_max, z_max),
|
||||
Vertex::new(x_min, y_max, z_min),
|
||||
Vertex::new(x_min, y_min, z_min),
|
||||
// front (0, 1, 0)
|
||||
vertex([1, 1, -1]),
|
||||
vertex([-1, 1, -1]),
|
||||
vertex([-1, 1, 1]),
|
||||
vertex([1, 1, 1]),
|
||||
Vertex::new(x_max, y_max, z_min),
|
||||
Vertex::new(x_min, y_max, z_min),
|
||||
Vertex::new(x_min, y_max, z_max),
|
||||
Vertex::new(x_max, y_max, z_max),
|
||||
// back (0, -1, 0)
|
||||
vertex([1, -1, 1]),
|
||||
vertex([-1, -1, 1]),
|
||||
vertex([-1, -1, -1]),
|
||||
vertex([1, -1, -1]),
|
||||
Vertex::new(x_max, y_min, z_max),
|
||||
Vertex::new(x_min, y_min, z_max),
|
||||
Vertex::new(x_min, y_min, z_min),
|
||||
Vertex::new(x_max, y_min, z_min),
|
||||
];
|
||||
|
||||
let index_data: &[u16] = &[
|
||||
|
|
@ -270,11 +354,54 @@ fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
|
|||
(vertex_data.to_vec(), index_data.to_vec())
|
||||
}
|
||||
|
||||
fn get_animated_camera_position(animation_time: std::time::Duration) -> glam::Vec3 {
|
||||
let animation_phase = 2.0 * std::f32::consts::PI * (animation_time.as_secs_f32() % 10.0) / 10.0;
|
||||
glam::Vec3::new(
|
||||
5.0 * f32::sin(animation_phase),
|
||||
5.0 * f32::cos(animation_phase),
|
||||
3.0,
|
||||
fn load_dem_texture(
|
||||
source: &Dem,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
) -> (wgpu::TextureView, glam::UVec2) {
|
||||
let texture_size = wgpu::Extent3d {
|
||||
width: source.num_cells_x,
|
||||
height: source.num_cells_y,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
label: Some("Dem Texture"),
|
||||
size: texture_size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::R16Uint,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
view_formats: &[],
|
||||
});
|
||||
|
||||
queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
bytemuck::cast_slice(&source.grid[..]),
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(std::mem::size_of::<u16>() as u32 * source.num_cells_x),
|
||||
rows_per_image: Some(source.num_cells_y),
|
||||
},
|
||||
texture_size,
|
||||
);
|
||||
|
||||
(
|
||||
texture.create_view(&wgpu::TextureViewDescriptor::default()),
|
||||
glam::UVec2::new(source.num_cells_x, source.num_cells_y),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_animated_camera_position(animation_time: std::time::Duration, dem_size: f32) -> glam::Vec3 {
|
||||
let animation_phase = 2.0 * std::f32::consts::PI * (animation_time.as_secs_f32() % 10.0) / 10.0;
|
||||
glam::Vec3::new(
|
||||
dem_size * f32::sin(animation_phase),
|
||||
dem_size * f32::cos(animation_phase),
|
||||
dem_size * 0.7,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,34 @@
|
|||
struct VertexOutput {
|
||||
@location(0) test: vec2<f32>,
|
||||
@builtin(position) position: vec4<f32>,
|
||||
};
|
||||
|
||||
@group(0)
|
||||
@binding(0)
|
||||
var<uniform> transform: mat4x4<f32>;
|
||||
struct Uniforms {
|
||||
transform: mat4x4<f32>,
|
||||
dem_texture_size: vec2<u32>
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> uniforms: Uniforms;
|
||||
|
||||
@group(0) @binding(1)
|
||||
var dem_texture: texture_2d<u32>;
|
||||
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@location(0) position: vec4<f32>,
|
||||
) -> VertexOutput {
|
||||
var result: VertexOutput;
|
||||
result.position = transform * position;
|
||||
result.position = uniforms.transform * position;
|
||||
result.test = position.xy;
|
||||
return result;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_solid(vertex: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return vec4<f32>(1.0, 1.0, 1.0, 1.0);
|
||||
let texture_index = vec2<i32>(fract(abs(vertex.test)/500.0)*vec2<f32>(uniforms.dem_texture_size));
|
||||
let v_i = textureLoad(dem_texture, texture_index, 0).r;
|
||||
let v = f32(v_i) / 65535.0;
|
||||
return vec4<f32>(v, v, 1.0, 1.0);
|
||||
}
|
||||
|
|
@ -153,6 +153,7 @@ impl MvuApp<Model> for App {
|
|||
dem.clone(),
|
||||
&context.device,
|
||||
&context.config,
|
||||
&context.queue,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -199,5 +200,5 @@ impl MvuApp<Model> for App {
|
|||
|
||||
fn open_test_file(file_path: PathBuf, model: Rc<Model>) -> Rc<Model> {
|
||||
let dem = Some(Rc::new(raster::Dem::load_from_image(&file_path)));
|
||||
Rc::new(Model { dem })
|
||||
Rc::new(Model { dem, ..*model })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,114 +1,159 @@
|
|||
use {
|
||||
std::{fs::File, path::PathBuf},
|
||||
tiff,
|
||||
};
|
||||
use std::{fs::File, path::PathBuf};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dem {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub min_x: f32,
|
||||
pub max_x: f32,
|
||||
pub min_y: f32,
|
||||
pub max_y: f32,
|
||||
pub min_z: f32,
|
||||
pub max_z: f32,
|
||||
pub num_cells_x: u32,
|
||||
pub num_cells_y: u32,
|
||||
pub x_min: f32,
|
||||
pub x_max: f32,
|
||||
pub y_min: f32,
|
||||
pub y_max: f32,
|
||||
pub z_min: f32,
|
||||
pub z_max: f32,
|
||||
pub grid: Vec<u16>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Dem {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Dem {{ dims: {} {}, x: {} {}, y: {} {}, z: {} {}, grid.len: {} }}",
|
||||
self.num_cells_x,
|
||||
self.num_cells_y,
|
||||
self.x_min,
|
||||
self.x_max,
|
||||
self.y_min,
|
||||
self.y_max,
|
||||
self.z_min,
|
||||
self.z_max,
|
||||
self.grid.len()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dem {
|
||||
fn get(&self, x: u32, y: u32) -> u16 {
|
||||
self.grid[(y * self.width + x) as usize]
|
||||
fn get(&self, i_x: u32, i_y: u32) -> u16 {
|
||||
self.grid[(i_y * self.num_cells_x + i_x) as usize]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DemBvh {
|
||||
pub dem: Dem,
|
||||
pub layers: Vec<DemBvhLayer>,
|
||||
}
|
||||
|
||||
pub struct DemBvhLayer {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub size: u32,
|
||||
pub data: Vec<u16>,
|
||||
}
|
||||
|
||||
impl DemBvh {
|
||||
fn new(dem: Dem) -> DemBvh {
|
||||
assert!(dem.width == dem.height);
|
||||
// width-1 must be power of two
|
||||
assert!(dem.width - 1 & (dem.width - 2) == 0);
|
||||
let mut layers = vec![create_first_dembvh_layer(&dem)];
|
||||
while layers.last().unwrap().width > 1 {
|
||||
layers.push(create_next_dembvh_layer(&layers.last().unwrap()));
|
||||
/// Find the smallest number that's larger then the input and that is also one
|
||||
/// less than a power of two.
|
||||
fn round_bound(v: u32) -> u32 {
|
||||
let mut remaining = v;
|
||||
let mut power_of_2 = 1;
|
||||
while remaining > 0 {
|
||||
remaining >>= 1;
|
||||
power_of_2 <<= 1;
|
||||
}
|
||||
DemBvh { dem, layers }
|
||||
power_of_2 - 1
|
||||
}
|
||||
|
||||
impl DemBvh {
|
||||
fn new(dem: &Dem) -> DemBvh {
|
||||
let mut layers = vec![create_first_dembvh_layer(dem)];
|
||||
while layers.last().unwrap().size > 1 {
|
||||
layers.push(create_next_dembvh_layer(layers.last().unwrap()));
|
||||
}
|
||||
DemBvh { layers }
|
||||
}
|
||||
}
|
||||
|
||||
impl DemBvhLayer {
|
||||
fn get_value(&self, x: u32, y: u32) -> (u16, u16) {
|
||||
fn get_value(&self, i_x: u32, i_y: u32) -> (u16, u16) {
|
||||
(
|
||||
self.data[(y * self.width * 2 + x * 2) as usize],
|
||||
self.data[(y * self.width * 2 + x * 2 + 1) as usize],
|
||||
self.data[((i_y * self.size + i_x) * 2) as usize],
|
||||
self.data[((i_y * self.size + i_x) * 2 + 1) as usize],
|
||||
)
|
||||
}
|
||||
|
||||
fn set_value(&mut self, x: u32, y: u32, min: u16, max: u16) {
|
||||
self.data[(y * self.width * 2 + x * 2) as usize] = min;
|
||||
self.data[(y * self.width * 2 + x * 2 + 1) as usize] = max;
|
||||
fn set_value(&mut self, i_x: u32, i_y: u32, min: u16, max: u16) {
|
||||
self.data[(i_y * self.size * 2 + i_x * 2) as usize] = min;
|
||||
self.data[(i_y * self.size * 2 + i_x * 2 + 1) as usize] = max;
|
||||
}
|
||||
}
|
||||
|
||||
/// If all the input values are zero, returns (0,0); otherwise ruturns a tuple
|
||||
/// containing the minimum and maximum nonzero input values.
|
||||
fn minmax_nonzero(a: u16, b: u16, c: u16, d: u16) -> (u16, u16) {
|
||||
[a, b, c, d].into_iter().fold((0, 0), |(min, max), value| {
|
||||
if value == 0 {
|
||||
(min, max)
|
||||
} else {
|
||||
(
|
||||
if min == 0 { value } else { min.min(value) },
|
||||
if max == 0 { value } else { max.max(value) },
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn minmax_nonzero_pairs(a: (u16, u16), b: (u16, u16), c: (u16, u16), d: (u16, u16)) -> (u16, u16) {
|
||||
[a, b, c, d]
|
||||
.into_iter()
|
||||
.fold((0, 0), |(min, max), (elem_min, elem_max)| {
|
||||
// If min is zero then max should be as well, and vice versa.
|
||||
if elem_min == 0 {
|
||||
(min, max)
|
||||
} else {
|
||||
(
|
||||
if min == 0 {
|
||||
elem_min
|
||||
} else {
|
||||
min.min(elem_min)
|
||||
},
|
||||
max.max(elem_max),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn create_first_dembvh_layer(dem: &Dem) -> DemBvhLayer {
|
||||
let width = dem.width - 1;
|
||||
let height = dem.height - 1;
|
||||
let size = round_bound(dem.num_cells_x.max(dem.num_cells_y));
|
||||
let mut result = DemBvhLayer {
|
||||
width,
|
||||
height,
|
||||
data: vec![0; (width * height * 2) as usize],
|
||||
size,
|
||||
data: vec![0; (size * size * 2) as usize],
|
||||
};
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let min = dem
|
||||
.get(x, y)
|
||||
.min(dem.get(x + 1, y))
|
||||
.min(dem.get(x, y + 1))
|
||||
.min(dem.get(x + 1, y + 1));
|
||||
let max = dem
|
||||
.get(x, y)
|
||||
.max(dem.get(x + 1, y))
|
||||
.max(dem.get(x, y + 1))
|
||||
.max(dem.get(x + 1, y + 1));
|
||||
result.set_value(x, y, min, max);
|
||||
for i_y in 0..dem.num_cells_y - 1 {
|
||||
for i_x in 0..dem.num_cells_x - 1 {
|
||||
let (min, max) = minmax_nonzero(
|
||||
dem.get(i_x, i_y),
|
||||
dem.get(i_x + 1, i_y),
|
||||
dem.get(i_x, i_y + 1),
|
||||
dem.get(i_x + 1, i_y + 1),
|
||||
);
|
||||
result.set_value(i_x, i_y, min, max);
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn create_next_dembvh_layer(prev: &DemBvhLayer) -> DemBvhLayer {
|
||||
let width = prev.width / 2;
|
||||
let height = prev.height / 2;
|
||||
let size = prev.size / 2;
|
||||
let mut result = DemBvhLayer {
|
||||
width,
|
||||
height,
|
||||
data: vec![0; (width * height * 2) as usize],
|
||||
size,
|
||||
data: vec![0; (size * size * 2) as usize],
|
||||
};
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let min = prev
|
||||
.get_value(x, y)
|
||||
.0
|
||||
.min(prev.get_value(x + 1, y).0)
|
||||
.min(prev.get_value(x, y + 1).0)
|
||||
.min(prev.get_value(x + 1, y + 1).0);
|
||||
let max = prev
|
||||
.get_value(x, y)
|
||||
.1
|
||||
.max(prev.get_value(x + 1, y).1)
|
||||
.max(prev.get_value(x, y + 1).1)
|
||||
.max(prev.get_value(x + 1, y + 1).1);
|
||||
result.set_value(x, y, min, max);
|
||||
for i_y in 0..size {
|
||||
for i_x in 0..size {
|
||||
let prev_i_x = i_x * 2;
|
||||
let prev_i_y = i_y * 2;
|
||||
let (min, max) = minmax_nonzero_pairs(
|
||||
prev.get_value(prev_i_x, prev_i_y),
|
||||
prev.get_value(prev_i_x + 1, prev_i_y),
|
||||
prev.get_value(prev_i_x, prev_i_y + 1),
|
||||
prev.get_value(prev_i_x + 1, prev_i_y + 1),
|
||||
);
|
||||
result.set_value(i_x, i_y, min, max);
|
||||
}
|
||||
}
|
||||
result
|
||||
|
|
@ -126,31 +171,31 @@ impl Dem {
|
|||
pub fn load_from_image(file_path: &PathBuf) -> Dem {
|
||||
let file = File::open(file_path).unwrap();
|
||||
let mut tiff = tiff::decoder::Decoder::new(file).unwrap();
|
||||
let (width, height) = tiff.dimensions().unwrap();
|
||||
let (num_cells_x, num_cells_y) = tiff.dimensions().unwrap();
|
||||
match tiff.read_image().unwrap() {
|
||||
tiff::decoder::DecodingResult::F32(f32_values) => {
|
||||
let min_x = -500.0;
|
||||
let max_x = 500.0;
|
||||
let min_y = -500.0;
|
||||
let max_y = 500.0;
|
||||
let (min_z, max_z) = f32_values[1..]
|
||||
let x_min = -500.0;
|
||||
let x_max = 500.0;
|
||||
let y_min = -500.0;
|
||||
let y_max = 500.0;
|
||||
let (z_min, z_max) = f32_values[1..]
|
||||
.iter()
|
||||
.fold((f32_values[0], f32_values[0]), |(min, max), elem| {
|
||||
(min.min(*elem), max.max(*elem))
|
||||
});
|
||||
let grid = f32_values
|
||||
.iter()
|
||||
.map(|v| normalized_f32_to_u16(normalize_f32(*v, min_z, max_z)))
|
||||
.map(|v| normalized_f32_to_u16(normalize_f32(*v, z_min, z_max)))
|
||||
.collect();
|
||||
Dem {
|
||||
width,
|
||||
height,
|
||||
min_x,
|
||||
max_x,
|
||||
min_y,
|
||||
max_y,
|
||||
min_z,
|
||||
max_z,
|
||||
num_cells_x,
|
||||
num_cells_y,
|
||||
x_min,
|
||||
x_max,
|
||||
y_min,
|
||||
y_max,
|
||||
z_min,
|
||||
z_max,
|
||||
grid,
|
||||
}
|
||||
}
|
||||
|
|
@ -160,3 +205,165 @@ impl Dem {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use proptest::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_round_bound() {
|
||||
for input in 2..10000 {
|
||||
let result = round_bound(input);
|
||||
assert!(
|
||||
if result < 2 {
|
||||
result == 1
|
||||
} else {
|
||||
(result + 1) & result == 0
|
||||
},
|
||||
"Result must be one less than a power of two."
|
||||
);
|
||||
assert!(result >= input, "Result must be greater than input");
|
||||
assert!(
|
||||
result / 2 < input,
|
||||
"Half of result must be smaller then input."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_minmax_nonzero() {
|
||||
assert_eq!((0, 0), minmax_nonzero(0, 0, 0, 0));
|
||||
assert_eq!((2, 2), minmax_nonzero(2, 0, 0, 0));
|
||||
assert_eq!((2, 2), minmax_nonzero(0, 2, 0, 0));
|
||||
assert_eq!((2, 2), minmax_nonzero(0, 0, 2, 0));
|
||||
assert_eq!((2, 2), minmax_nonzero(0, 0, 0, 2));
|
||||
assert_eq!((2, 3), minmax_nonzero(2, 3, 0, 0));
|
||||
assert_eq!((2, 3), minmax_nonzero(2, 0, 3, 0));
|
||||
assert_eq!((2, 3), minmax_nonzero(2, 0, 0, 3));
|
||||
assert_eq!((2, 3), minmax_nonzero(0, 3, 2, 0));
|
||||
assert_eq!((2, 3), minmax_nonzero(0, 3, 0, 2));
|
||||
assert_eq!((2, 4), minmax_nonzero(2, 3, 4, 0));
|
||||
assert_eq!((2, 4), minmax_nonzero(0, 4, 3, 2));
|
||||
assert_eq!((5, 10), minmax_nonzero(6, 10, 5, 9));
|
||||
}
|
||||
|
||||
fn arbitrary_u16_vec_from_length(length: u32) -> impl Strategy<Value = Vec<u16>> {
|
||||
let mut result = vec![];
|
||||
for _ in 1..=length {
|
||||
result.push(any::<u16>());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn arbitrary_dem(max_size: u32) -> BoxedStrategy<Dem> {
|
||||
(1..max_size, 1..max_size)
|
||||
.prop_flat_map(|(num_cells_x, num_cells_y)| {
|
||||
(
|
||||
Just(num_cells_x),
|
||||
Just(num_cells_y),
|
||||
proptest::num::f32::NORMAL,
|
||||
proptest::num::f32::NORMAL,
|
||||
proptest::num::f32::NORMAL,
|
||||
proptest::num::f32::NORMAL,
|
||||
proptest::num::f32::NORMAL,
|
||||
proptest::num::f32::NORMAL,
|
||||
arbitrary_u16_vec_from_length(num_cells_x * num_cells_y),
|
||||
)
|
||||
})
|
||||
.prop_map(
|
||||
|(num_cells_x, num_cells_y, x1, x2, y1, y2, z1, z2, grid)| Dem {
|
||||
num_cells_x,
|
||||
num_cells_y,
|
||||
x_min: x1.min(x2),
|
||||
x_max: x1.max(x2),
|
||||
y_min: y1.min(y2),
|
||||
y_max: y1.max(y2),
|
||||
z_min: z1.min(z2),
|
||||
z_max: z1.max(z2),
|
||||
grid,
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
proptest! {
|
||||
//#![proptest_config(ProptestConfig {cases: 30, .. ProptestConfig::default() })]
|
||||
|
||||
#[test]
|
||||
fn dembvh_is_zero_outside_source_bounds(dem in arbitrary_dem(50)) {
|
||||
let target = DemBvh::new(&dem);
|
||||
assert!(target.layers.len() > 0);
|
||||
let first_layer = target.layers.first().unwrap();
|
||||
assert!(first_layer.size >= dem.num_cells_x-1);
|
||||
assert!(first_layer.size >= dem.num_cells_y-1);
|
||||
for i_y in dem.num_cells_y..first_layer.size {
|
||||
for i_x in 0..dem.num_cells_x {
|
||||
assert_eq!((0,0), first_layer.get_value(i_x, i_y));
|
||||
}
|
||||
}
|
||||
for i_y in 0..dem.num_cells_y {
|
||||
for i_x in dem.num_cells_x..first_layer.size {
|
||||
assert_eq!((0,0), first_layer.get_value(i_x, i_y));
|
||||
}
|
||||
}
|
||||
for i_y in dem.num_cells_y..first_layer.size {
|
||||
for i_x in dem.num_cells_x..first_layer.size {
|
||||
assert_eq!((0,0), first_layer.get_value(i_x, i_y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn dembvh_first_layer_has_correct_values(dem in arbitrary_dem(50)) {
|
||||
let target = DemBvh::new(&dem);
|
||||
assert!(target.layers.len() > 0);
|
||||
let first_layer = target.layers.first().unwrap();
|
||||
assert!(first_layer.size >= dem.num_cells_x-1);
|
||||
assert!(first_layer.size >= dem.num_cells_y-1);
|
||||
for i_y in 0..dem.num_cells_y-1 {
|
||||
for i_x in 0..dem.num_cells_x-1 {
|
||||
let corners:Vec<_> = [
|
||||
dem.get(i_x, i_y),
|
||||
dem.get(i_x, i_y+1),
|
||||
dem.get(i_x+1, i_y),
|
||||
dem.get(i_x+1, i_y+1)
|
||||
].into_iter().filter(|v| *v!=0).collect();
|
||||
let (found_min, found_max) = first_layer.get_value(i_x, i_y);
|
||||
assert_eq!(corners.iter().min(), Some(&found_min));
|
||||
assert_eq!(corners.iter().max(), Some(&found_max));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn dembvh_check_rest_of_layers(dem in arbitrary_dem(50)) {
|
||||
let target = DemBvh::new(&dem);
|
||||
assert!(target.layers.first().unwrap().size >= dem.num_cells_x-1);
|
||||
assert!(target.layers.first().unwrap().size >= dem.num_cells_y-1);
|
||||
for (prev_layer, layer) in target.layers.iter().zip(target.layers.iter().skip(1)){
|
||||
assert_eq!(layer.size, prev_layer.size/2);
|
||||
for i_y in 0..layer.size {
|
||||
for i_x in 0..layer.size {
|
||||
let corners:Vec<_> = [
|
||||
prev_layer.get_value(i_x * 2, i_y * 2),
|
||||
prev_layer.get_value(i_x * 2, i_y * 2 + 1),
|
||||
prev_layer.get_value(i_x * 2 + 1, i_y * 2),
|
||||
prev_layer.get_value(i_x * 2 + 1, i_y * 2 + 1)
|
||||
].into_iter().filter(|v| *v!=(0,0)).collect();
|
||||
let corner_mins: Vec<_> = corners.iter().map(|&(min, _)| min).collect();
|
||||
let corner_maxs: Vec<_> = corners.iter().map(|&(_, max)| max).collect();
|
||||
let (found_min, found_max) = layer.get_value(i_x, i_y);
|
||||
let found_min = if found_min == 0 { None } else { Some(&found_min) };
|
||||
let found_max = if found_max == 0 { None } else { Some(&found_max) };
|
||||
assert_eq!(corner_mins.iter().min(), found_min);
|
||||
assert_eq!(corner_maxs.iter().max(), found_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_eq!(1, target.layers.last().unwrap().size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,11 @@ mod native;
|
|||
#[cfg(target_arch = "x86_64")]
|
||||
pub use native::run;
|
||||
|
||||
mod mvu;
|
||||
mod app;
|
||||
mod mvu;
|
||||
use app::{App, Model};
|
||||
|
||||
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
|
||||
pub fn main() {
|
||||
run(App::new(), Rc::new(Model::new()));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ pub struct Size2i {
|
|||
|
||||
#[allow(async_fn_in_trait)]
|
||||
// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#where-the-gaps-lie
|
||||
pub trait MvuApp<M>
|
||||
{
|
||||
pub trait MvuApp<M> {
|
||||
async fn init(&mut self, instance: &Instance, surface: Surface<'static>, new_size: Size2i);
|
||||
async fn resize(&mut self, size: Size2i);
|
||||
async fn update(&self, model: Rc<M>, event: Event) -> Rc<M>;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ where
|
|||
block_on(self.app.init(&instance, surface, Size2i { width, height }));
|
||||
}
|
||||
|
||||
|
||||
// TODO: The idea with these `block_on()` calls is that eventually I'll
|
||||
// write an async executor that runs on top of the winit even loop and then
|
||||
// this stuff can be properly async.
|
||||
|
|
@ -62,7 +61,7 @@ where
|
|||
WindowEvent::RedrawRequested => {
|
||||
block_on(self.app.view(self.model.clone())).unwrap();
|
||||
self.window.as_ref().unwrap().request_redraw();
|
||||
},
|
||||
}
|
||||
WindowEvent::DroppedFile(file_path) => {
|
||||
self.model = block_on(
|
||||
self.app
|
||||
|
|
|
|||
Loading…
Reference in New Issue