Compare commits

...

5 Commits

Author SHA1 Message Date
Matthew Gordon ab2e366e7f Factor out DEM texture creation into separate function 2024-11-22 20:40:56 -04:00
Matthew Gordon e7180e57bf Fix issues found by `cargo clippy`, including bugs 2024-11-22 20:32:15 -04:00
Matthew Gordon a4503c3dbf Load DEM texture 2024-11-22 20:20:06 -04:00
Matthew Gordon 8906f6e47a Build BVH Quadtree for DEM 2024-11-16 15:03:16 -04:00
Matthew Gordon e26af202ef Render volume of DEM instead of cube
Instead of a +/-1 cube, draw the AABB of the DEM
2024-11-15 10:07:04 -04:00
10 changed files with 697 additions and 178 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
/target /target
/proptest-regressions
*~ *~
\#*# \#*#
.#* .#*

179
Cargo.lock generated
View File

@ -176,15 +176,30 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 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]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.8.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3"
dependencies = [ 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]] [[package]]
name = "bit-vec" name = "bit-vec"
version = "0.8.0" version = "0.8.0"
@ -244,6 +259,12 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]] [[package]]
name = "bytes" name = "bytes"
version = "1.8.0" version = "1.8.0"
@ -502,6 +523,12 @@ dependencies = [
"windows-sys 0.52.0", "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]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.34" version = "1.0.34"
@ -512,6 +539,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.5.0" version = "0.5.0"
@ -851,6 +884,12 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.162" version = "0.2.162"
@ -867,6 +906,12 @@ dependencies = [
"windows-targets 0.52.6", "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]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.3" version = "0.1.3"
@ -971,7 +1016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e" checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bit-set", "bit-set 0.8.0",
"bitflags 2.6.0", "bitflags 2.6.0",
"cfg_aliases 0.1.1", "cfg_aliases 0.1.1",
"codespan-reporting", "codespan-reporting",
@ -1024,6 +1069,16 @@ dependencies = [
"jni-sys", "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]] [[package]]
name = "num_enum" name = "num_enum"
version = "0.7.3" version = "0.7.3"
@ -1369,6 +1424,15 @@ dependencies = [
"windows-sys 0.59.0", "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]] [[package]]
name = "presser" name = "presser"
version = "0.3.1" version = "0.3.1"
@ -1399,6 +1463,26 @@ version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" 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]] [[package]]
name = "pteropus" name = "pteropus"
version = "0.1.0" version = "0.1.0"
@ -1410,6 +1494,7 @@ dependencies = [
"futures", "futures",
"glam", "glam",
"log", "log",
"proptest",
"tiff", "tiff",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@ -1419,6 +1504,12 @@ dependencies = [
"winit", "winit",
] ]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]] [[package]]
name = "quick-xml" name = "quick-xml"
version = "0.36.2" version = "0.36.2"
@ -1437,6 +1528,45 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.3" version = "0.1.3"
@ -1521,6 +1651,18 @@ dependencies = [
"windows-sys 0.52.0", "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]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -1671,6 +1813,19 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "termcolor" name = "termcolor"
version = "1.4.1" version = "1.4.1"
@ -1775,6 +1930,12 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e" checksum = "5902c5d130972a0000f60860bfbf46f7ca3db5391eddfedd1b8728bd9dc96c0e"
[[package]]
name = "unarray"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"
@ -1811,6 +1972,15 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 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]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.5.0" version = "2.5.0"
@ -2087,7 +2257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e0c68e7b6322a03ee5b83fcd92caeac5c2a932f6457818179f4652ad2a9c065" checksum = "0e0c68e7b6322a03ee5b83fcd92caeac5c2a932f6457818179f4652ad2a9c065"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"bit-vec", "bit-vec 0.8.0",
"bitflags 2.6.0", "bitflags 2.6.0",
"cfg_aliases 0.1.1", "cfg_aliases 0.1.1",
"document-features", "document-features",
@ -2114,7 +2284,7 @@ dependencies = [
"android_system_properties", "android_system_properties",
"arrayvec", "arrayvec",
"ash", "ash",
"bit-set", "bit-set 0.8.0",
"bitflags 2.6.0", "bitflags 2.6.0",
"block", "block",
"bytemuck", "bytemuck",
@ -2569,6 +2739,7 @@ version = "0.7.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [ dependencies = [
"byteorder",
"zerocopy-derive", "zerocopy-derive",
] ]

View File

@ -30,6 +30,8 @@ web-sys = { version = "0.3", features = [
"Element", "Element",
]} ]}
[dev-dependencies]
proptest = "1.5.0"
[target.'cfg(target_arch = "wasm32")'.dev-dependencies] [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.34" wasm-bindgen-test = "0.3.34"

View File

@ -1,7 +1,7 @@
use super::raster::Dem; use super::raster::Dem;
use { use {
bytemuck::{Pod, Zeroable}, bytemuck::{Pod, Zeroable},
std::{borrow::Cow, rc::Rc}, std::{borrow::Cow, num::NonZero, rc::Rc},
wgpu::util::DeviceExt, wgpu::util::DeviceExt,
}; };
@ -12,7 +12,8 @@ pub struct DemRenderer {
vertex_buffer: wgpu::Buffer, vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer,
index_count: usize, index_count: usize,
uniforms: Uniforms, camera: Camera,
uniforms: UniformBufferManager,
animation_start: std::time::Instant, animation_start: std::time::Instant,
} }
@ -22,33 +23,36 @@ struct Vertex {
position: [f32; 4], position: [f32; 4],
} }
struct Uniforms { impl Vertex {
projection_matrix: glam::Mat4, fn new(x: f32, y: f32, z: f32) -> Self {
view_matrix: glam::Mat4, Self {
buffer: wgpu::Buffer, position: [x, y, z, 1.0],
}
}
} }
impl Uniforms { struct Camera {
fn new(device: &wgpu::Device, aspect_ratio: f32) -> Self { projection_matrix: glam::Mat4,
let projection_matrix = view_matrix: glam::Mat4,
glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, aspect_ratio, 1.0, 10.0); }
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 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 { Self {
projection_matrix, projection_matrix,
view_matrix, view_matrix,
buffer,
} }
} }
fn set_camera_position(&mut self, position: glam::Vec3) { fn set_position(&mut self, camera_position: glam::Vec3, camera_look_at: glam::Vec3) {
self.view_matrix = glam::Mat4::look_at_rh(position, glam::Vec3::ZERO, glam::Vec3::Z); self.view_matrix = glam::Mat4::look_at_rh(camera_position, camera_look_at, glam::Vec3::Z);
} }
fn set_aspect_ratio(&mut self, ratio: f32) { 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); glam::Mat4::perspective_rh(std::f32::consts::FRAC_PI_4, ratio, 1.0, 10.0);
} }
fn update_buffer(&self, queue: &wgpu::Queue) { fn get_matrix(&self) -> glam::Mat4 {
let camera_matrix = self.projection_matrix * self.view_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)) }
#[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 binding(&self) -> wgpu::BindingResource<'_> { fn set_camera_matrix(&mut self, camera_matrix: glam::Mat4) {
self.buffer.as_entire_binding() 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>, source: Rc<Dem>,
device: &wgpu::Device, device: &wgpu::Device,
surface_config: &wgpu::SurfaceConfiguration, surface_config: &wgpu::SurfaceConfiguration,
queue: &wgpu::Queue,
) -> Self { ) -> 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 { let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("DemRenderer Vertex Buffer"), label: Some("DemRenderer Vertex Buffer"),
@ -89,25 +143,27 @@ impl DemRenderer {
let index_count = index_data.len(); let index_count = index_data.len();
let uniforms = Uniforms::new( let (dem_texture_view, dem_texture_size) = load_dem_texture(&source, device, queue);
device,
surface_config.width as f32 / surface_config.height as f32, 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 { let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None, label: None,
entries: &[ entries: &[
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 0, binding: 0,
visibility: wgpu::ShaderStages::VERTEX, visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false, has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(64), min_binding_size: wgpu::BufferSize::new(UNIFORM_BUFFER_SIZE),
}, },
count: None, count: None,
}, },
/*wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 1, binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT, visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture { ty: wgpu::BindingType::Texture {
@ -116,16 +172,22 @@ impl DemRenderer {
view_dimension: wgpu::TextureViewDimension::D2, view_dimension: wgpu::TextureViewDimension::D2,
}, },
count: None, count: None,
},*/ },
], ],
}); });
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_group_layout, layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry { entries: &[
binding: 0, wgpu::BindGroupEntry {
resource: uniforms.binding(), binding: 0,
}], resource: uniforms.get_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&dem_texture_view),
},
],
label: Some("DemRendererBindGroup"), label: Some("DemRendererBindGroup"),
}); });
@ -182,6 +244,7 @@ impl DemRenderer {
index_buffer, index_buffer,
index_count, index_count,
animation_start: std::time::Instant::now(), animation_start: std::time::Instant::now(),
camera,
uniforms, uniforms,
} }
} }
@ -190,8 +253,14 @@ impl DemRenderer {
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("DemRendererCommandEncoder"), label: Some("DemRendererCommandEncoder"),
}); });
self.uniforms self.camera.set_position(
.set_camera_position(get_animated_camera_position(self.animation_start.elapsed())); 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); self.uniforms.update_buffer(queue);
{ {
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -216,46 +285,61 @@ impl DemRenderer {
} }
queue.submit(Some(encoder.finish())); queue.submit(Some(encoder.finish()));
} }
}
fn vertex(pos: [i8; 3]) -> Vertex { fn get_max_dem_dimension(&self) -> f32 {
Vertex { let dem = &self.source;
position: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0], (dem.x_max - dem.x_min)
.max(dem.y_max - dem.y_min)
.max(dem.z_max - dem.x_min)
}
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 = [ let vertex_data = [
// top (0, 0, 1) // top (0, 0, 1)
vertex([-1, -1, 1]), Vertex::new(x_min, y_min, z_max),
vertex([1, -1, 1]), Vertex::new(x_max, y_min, z_max),
vertex([1, 1, 1]), Vertex::new(x_max, y_max, z_max),
vertex([-1, 1, 1]), Vertex::new(x_min, y_max, z_max),
// bottom (0, 0, -1) // bottom (0, 0, -1)
vertex([-1, 1, -1]), Vertex::new(x_min, y_max, z_min),
vertex([1, 1, -1]), Vertex::new(x_max, y_max, z_min),
vertex([1, -1, -1]), Vertex::new(x_max, y_min, z_min),
vertex([-1, -1, -1]), Vertex::new(x_min, y_min, z_min),
// right (1, 0, 0) // right (1, 0, 0)
vertex([1, -1, -1]), Vertex::new(x_max, y_min, z_min),
vertex([1, 1, -1]), Vertex::new(x_max, y_max, z_min),
vertex([1, 1, 1]), Vertex::new(x_max, y_max, z_max),
vertex([1, -1, 1]), Vertex::new(x_max, y_min, z_max),
// left (-1, 0, 0) // left (-1, 0, 0)
vertex([-1, -1, 1]), Vertex::new(x_min, y_min, z_max),
vertex([-1, 1, 1]), Vertex::new(x_min, y_max, z_max),
vertex([-1, 1, -1]), Vertex::new(x_min, y_max, z_min),
vertex([-1, -1, -1]), Vertex::new(x_min, y_min, z_min),
// front (0, 1, 0) // front (0, 1, 0)
vertex([1, 1, -1]), Vertex::new(x_max, y_max, z_min),
vertex([-1, 1, -1]), Vertex::new(x_min, y_max, z_min),
vertex([-1, 1, 1]), Vertex::new(x_min, y_max, z_max),
vertex([1, 1, 1]), Vertex::new(x_max, y_max, z_max),
// back (0, -1, 0) // back (0, -1, 0)
vertex([1, -1, 1]), Vertex::new(x_max, y_min, z_max),
vertex([-1, -1, 1]), Vertex::new(x_min, y_min, z_max),
vertex([-1, -1, -1]), Vertex::new(x_min, y_min, z_min),
vertex([1, -1, -1]), Vertex::new(x_max, y_min, z_min),
]; ];
let index_data: &[u16] = &[ let index_data: &[u16] = &[
@ -270,11 +354,54 @@ fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
(vertex_data.to_vec(), index_data.to_vec()) (vertex_data.to_vec(), index_data.to_vec())
} }
fn get_animated_camera_position(animation_time: std::time::Duration) -> glam::Vec3 { fn load_dem_texture(
let animation_phase = 2.0 * std::f32::consts::PI * (animation_time.as_secs_f32() % 10.0) / 10.0; source: &Dem,
glam::Vec3::new( device: &wgpu::Device,
5.0 * f32::sin(animation_phase), queue: &wgpu::Queue,
5.0 * f32::cos(animation_phase), ) -> (wgpu::TextureView, glam::UVec2) {
3.0, 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,
) )
} }

View File

@ -1,21 +1,34 @@
struct VertexOutput { struct VertexOutput {
@location(0) test: vec2<f32>,
@builtin(position) position: vec4<f32>, @builtin(position) position: vec4<f32>,
}; };
@group(0) struct Uniforms {
@binding(0) transform: mat4x4<f32>,
var<uniform> 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 @vertex
fn vs_main( fn vs_main(
@location(0) position: vec4<f32>, @location(0) position: vec4<f32>,
) -> VertexOutput { ) -> VertexOutput {
var result: VertexOutput; var result: VertexOutput;
result.position = transform * position; result.position = uniforms.transform * position;
result.test = position.xy;
return result; return result;
} }
@fragment @fragment
fn fs_solid(vertex: VertexOutput) -> @location(0) vec4<f32> { 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);
} }

View File

@ -153,6 +153,7 @@ impl MvuApp<Model> for App {
dem.clone(), dem.clone(),
&context.device, &context.device,
&context.config, &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> { fn open_test_file(file_path: PathBuf, model: Rc<Model>) -> Rc<Model> {
let dem = Some(Rc::new(raster::Dem::load_from_image(&file_path))); let dem = Some(Rc::new(raster::Dem::load_from_image(&file_path)));
Rc::new(Model { dem }) Rc::new(Model { dem, ..*model })
} }

View File

@ -1,114 +1,159 @@
use { use std::{fs::File, path::PathBuf};
std::{fs::File, path::PathBuf},
tiff,
};
#[derive(Clone)] #[derive(Clone)]
pub struct Dem { pub struct Dem {
pub width: u32, pub num_cells_x: u32,
pub height: u32, pub num_cells_y: u32,
pub min_x: f32, pub x_min: f32,
pub max_x: f32, pub x_max: f32,
pub min_y: f32, pub y_min: f32,
pub max_y: f32, pub y_max: f32,
pub min_z: f32, pub z_min: f32,
pub max_z: f32, pub z_max: f32,
pub grid: Vec<u16>, 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 { impl Dem {
fn get(&self, x: u32, y: u32) -> u16 { fn get(&self, i_x: u32, i_y: u32) -> u16 {
self.grid[(y * self.width + x) as usize] self.grid[(i_y * self.num_cells_x + i_x) as usize]
} }
} }
pub struct DemBvh { pub struct DemBvh {
pub dem: Dem,
pub layers: Vec<DemBvhLayer>, pub layers: Vec<DemBvhLayer>,
} }
pub struct DemBvhLayer { pub struct DemBvhLayer {
pub width: u32, pub size: u32,
pub height: u32,
pub data: Vec<u16>, pub data: Vec<u16>,
} }
/// 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;
}
power_of_2 - 1
}
impl DemBvh { impl DemBvh {
fn new(dem: Dem) -> DemBvh { fn new(dem: &Dem) -> DemBvh {
assert!(dem.width == dem.height); let mut layers = vec![create_first_dembvh_layer(dem)];
// width-1 must be power of two while layers.last().unwrap().size > 1 {
assert!(dem.width - 1 & (dem.width - 2) == 0); layers.push(create_next_dembvh_layer(layers.last().unwrap()));
let mut layers = vec![create_first_dembvh_layer(&dem)];
while layers.last().unwrap().width > 1 {
layers.push(create_next_dembvh_layer(&layers.last().unwrap()));
} }
DemBvh { dem, layers } DemBvh { layers }
} }
} }
impl DemBvhLayer { 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[((i_y * self.size + i_x) * 2) as usize],
self.data[(y * self.width * 2 + x * 2 + 1) 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) { fn set_value(&mut self, i_x: u32, i_y: u32, min: u16, max: u16) {
self.data[(y * self.width * 2 + x * 2) as usize] = min; self.data[(i_y * self.size * 2 + i_x * 2) as usize] = min;
self.data[(y * self.width * 2 + x * 2 + 1) as usize] = max; 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 { fn create_first_dembvh_layer(dem: &Dem) -> DemBvhLayer {
let width = dem.width - 1; let size = round_bound(dem.num_cells_x.max(dem.num_cells_y));
let height = dem.height - 1;
let mut result = DemBvhLayer { let mut result = DemBvhLayer {
width, size,
height, data: vec![0; (size * size * 2) as usize],
data: vec![0; (width * height * 2) as usize],
}; };
for y in 0..height { for i_y in 0..dem.num_cells_y - 1 {
for x in 0..width { for i_x in 0..dem.num_cells_x - 1 {
let min = dem let (min, max) = minmax_nonzero(
.get(x, y) dem.get(i_x, i_y),
.min(dem.get(x + 1, y)) dem.get(i_x + 1, i_y),
.min(dem.get(x, y + 1)) dem.get(i_x, i_y + 1),
.min(dem.get(x + 1, y + 1)); dem.get(i_x + 1, i_y + 1),
let max = dem );
.get(x, y) result.set_value(i_x, i_y, min, max);
.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);
} }
} }
result result
} }
fn create_next_dembvh_layer(prev: &DemBvhLayer) -> DemBvhLayer { fn create_next_dembvh_layer(prev: &DemBvhLayer) -> DemBvhLayer {
let width = prev.width / 2; let size = prev.size / 2;
let height = prev.height / 2;
let mut result = DemBvhLayer { let mut result = DemBvhLayer {
width, size,
height, data: vec![0; (size * size * 2) as usize],
data: vec![0; (width * height * 2) as usize],
}; };
for y in 0..height { for i_y in 0..size {
for x in 0..width { for i_x in 0..size {
let min = prev let prev_i_x = i_x * 2;
.get_value(x, y) let prev_i_y = i_y * 2;
.0 let (min, max) = minmax_nonzero_pairs(
.min(prev.get_value(x + 1, y).0) prev.get_value(prev_i_x, prev_i_y),
.min(prev.get_value(x, y + 1).0) prev.get_value(prev_i_x + 1, prev_i_y),
.min(prev.get_value(x + 1, y + 1).0); prev.get_value(prev_i_x, prev_i_y + 1),
let max = prev prev.get_value(prev_i_x + 1, prev_i_y + 1),
.get_value(x, y) );
.1 result.set_value(i_x, i_y, min, max);
.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);
} }
} }
result result
@ -126,31 +171,31 @@ impl Dem {
pub fn load_from_image(file_path: &PathBuf) -> Dem { pub fn load_from_image(file_path: &PathBuf) -> Dem {
let file = File::open(file_path).unwrap(); let file = File::open(file_path).unwrap();
let mut tiff = tiff::decoder::Decoder::new(file).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() { match tiff.read_image().unwrap() {
tiff::decoder::DecodingResult::F32(f32_values) => { tiff::decoder::DecodingResult::F32(f32_values) => {
let min_x = -500.0; let x_min = -500.0;
let max_x = 500.0; let x_max = 500.0;
let min_y = -500.0; let y_min = -500.0;
let max_y = 500.0; let y_max = 500.0;
let (min_z, max_z) = f32_values[1..] let (z_min, z_max) = f32_values[1..]
.iter() .iter()
.fold((f32_values[0], f32_values[0]), |(min, max), elem| { .fold((f32_values[0], f32_values[0]), |(min, max), elem| {
(min.min(*elem), max.max(*elem)) (min.min(*elem), max.max(*elem))
}); });
let grid = f32_values let grid = f32_values
.iter() .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(); .collect();
Dem { Dem {
width, num_cells_x,
height, num_cells_y,
min_x, x_min,
max_x, x_max,
min_y, y_min,
max_y, y_max,
min_z, z_min,
max_z, z_max,
grid, 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);
}
}
}

View File

@ -12,12 +12,11 @@ mod native;
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
pub use native::run; pub use native::run;
mod mvu;
mod app; mod app;
use app::{App,Model}; mod mvu;
use app::{App, Model};
#[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))] #[cfg_attr(target_arch = "wasm32", wasm_bindgen(start))]
pub fn main() { pub fn main() {
run(App::new(), Rc::new(Model::new())); run(App::new(), Rc::new(Model::new()));
} }

View File

@ -16,8 +16,7 @@ pub struct Size2i {
#[allow(async_fn_in_trait)] #[allow(async_fn_in_trait)]
// https://blog.rust-lang.org/2023/12/21/async-fn-rpit-in-traits.html#where-the-gaps-lie // 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 init(&mut self, instance: &Instance, surface: Surface<'static>, new_size: Size2i);
async fn resize(&mut self, size: Size2i); async fn resize(&mut self, size: Size2i);
async fn update(&self, model: Rc<M>, event: Event) -> Rc<M>; async fn update(&self, model: Rc<M>, event: Event) -> Rc<M>;

View File

@ -45,7 +45,6 @@ where
block_on(self.app.init(&instance, surface, Size2i { width, height })); block_on(self.app.init(&instance, surface, Size2i { width, height }));
} }
// TODO: The idea with these `block_on()` calls is that eventually I'll // 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 // write an async executor that runs on top of the winit even loop and then
// this stuff can be properly async. // this stuff can be properly async.
@ -62,7 +61,7 @@ where
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
block_on(self.app.view(self.model.clone())).unwrap(); block_on(self.app.view(self.model.clone())).unwrap();
self.window.as_ref().unwrap().request_redraw(); self.window.as_ref().unwrap().request_redraw();
}, }
WindowEvent::DroppedFile(file_path) => { WindowEvent::DroppedFile(file_path) => {
self.model = block_on( self.model = block_on(
self.app self.app