Compare commits
9 Commits
b3deecc02f
...
072b54cafe
| Author | SHA1 | Date |
|---|---|---|
|
|
072b54cafe | |
|
|
24a86cb77a | |
|
|
37a8ca5440 | |
|
|
4d1a333414 | |
|
|
420bfed5e9 | |
|
|
b33af565f2 | |
|
|
6b4896f0cb | |
|
|
bf1f65afe5 | |
|
|
15fd82da34 |
|
|
@ -1,2 +1,3 @@
|
||||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tiff filter=lfs diff=lfs merge=lfs -text
|
||||||
*.data filter=lfs diff=lfs merge=lfs -text
|
*.data filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ use {
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct DemRenderer {
|
pub struct DemRenderer {
|
||||||
source: Rc<Dem>,
|
pub source: Rc<Dem>,
|
||||||
pipeline: wgpu::RenderPipeline,
|
pipeline: wgpu::RenderPipeline,
|
||||||
bind_group: wgpu::BindGroup,
|
bind_group: wgpu::BindGroup,
|
||||||
vertex_buffer: wgpu::Buffer,
|
vertex_buffer: wgpu::Buffer,
|
||||||
|
|
@ -577,5 +577,5 @@ fn get_animated_camera_position(animation_phase: f32, dem_size: f32) -> glam::Ve
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
//#[cfg(test)]
|
||||||
mod tests;
|
//mod tests;
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,8 @@ async fn run_compute_shader_test(
|
||||||
required_features: wgpu::Features::empty(),
|
required_features: wgpu::Features::empty(),
|
||||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||||
|
trace: wgpu::Trace::Off,
|
||||||
},
|
},
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
@ -87,7 +87,7 @@ async fn run_compute_shader_test(
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
let buffer_slice = staging_buffer.slice(..);
|
let buffer_slice = staging_buffer.slice(..);
|
||||||
buffer_slice.map_async(wgpu::MapMode::Read, move |v| tx.send(v).unwrap());
|
buffer_slice.map_async(wgpu::MapMode::Read, move |v| tx.send(v).unwrap());
|
||||||
device.poll(wgpu::Maintain::wait()).panic_on_timeout();
|
assert!(device.poll(wgpu::PollType::wait()).unwrap().wait_finished());
|
||||||
if let Ok(Ok(())) = rx.recv() {
|
if let Ok(Ok(())) = rx.recv() {
|
||||||
buffer_slice.get_mapped_range().to_vec()
|
buffer_slice.get_mapped_range().to_vec()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -99,7 +99,7 @@ async fn run_compute_shader_test(
|
||||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||||
struct Vec3 {
|
struct Vec3 {
|
||||||
elements: [f32; 3],
|
elements: [f32; 3],
|
||||||
_padding: f32
|
_padding: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -114,7 +114,7 @@ struct TestInput {
|
||||||
fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
|
fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
|
||||||
Vec3 {
|
Vec3 {
|
||||||
elements: [x, y, z],
|
elements: [x, y, z],
|
||||||
_padding: 0.0
|
_padding: 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::mvu::{Event, File, MvuApp, Size2i, FrameTimer};
|
use crate::mvu::{Event, File, FrameTimer, MvuApp, Size2i};
|
||||||
use {
|
use {
|
||||||
log::info,
|
log::info,
|
||||||
std::borrow::Cow,
|
std::borrow::Cow,
|
||||||
|
|
@ -11,6 +11,9 @@ mod dem_renderer;
|
||||||
mod raster;
|
mod raster;
|
||||||
use dem_renderer::DemRenderer;
|
use dem_renderer::DemRenderer;
|
||||||
|
|
||||||
|
mod statistics_reporter;
|
||||||
|
use statistics_reporter::StatisticsReporter;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
dem: Option<Rc<raster::Dem>>,
|
dem: Option<Rc<raster::Dem>>,
|
||||||
|
|
@ -30,6 +33,7 @@ struct Context {
|
||||||
queue: Queue,
|
queue: Queue,
|
||||||
scene_data: Option<DemRenderer>,
|
scene_data: Option<DemRenderer>,
|
||||||
frame_timer: Box<dyn FrameTimer>,
|
frame_timer: Box<dyn FrameTimer>,
|
||||||
|
statistics_reporter: StatisticsReporter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|
@ -59,14 +63,17 @@ impl MvuApp<Model> for App {
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("Failed to find an appropriate adapter");
|
.expect("Failed to find an appropriate adapter");
|
||||||
info!("Using {} backend with {}", adapter.get_info().backend, adapter.get_info().name);
|
info!(
|
||||||
|
"Using {} backend with {}",
|
||||||
|
adapter.get_info().backend,
|
||||||
|
adapter.get_info().name
|
||||||
|
);
|
||||||
|
|
||||||
let (device, queue) = adapter
|
let (device, queue) = adapter
|
||||||
.request_device(&wgpu::DeviceDescriptor {
|
.request_device(&wgpu::DeviceDescriptor {
|
||||||
label: None,
|
label: None,
|
||||||
required_features: wgpu::Features::empty(),
|
required_features: wgpu::Features::empty(),
|
||||||
required_limits: wgpu::Limits::default()
|
required_limits: wgpu::Limits::default().using_resolution(adapter.limits()),
|
||||||
.using_resolution(adapter.limits()),
|
|
||||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||||
trace: Trace::Off,
|
trace: Trace::Off,
|
||||||
})
|
})
|
||||||
|
|
@ -123,7 +130,8 @@ impl MvuApp<Model> for App {
|
||||||
render_pipeline,
|
render_pipeline,
|
||||||
queue,
|
queue,
|
||||||
scene_data: None,
|
scene_data: None,
|
||||||
frame_timer: frame_timer,
|
frame_timer,
|
||||||
|
statistics_reporter: StatisticsReporter::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
info!("Initialized {}x{}.", size.width, size.height);
|
info!("Initialized {}x{}.", size.width, size.height);
|
||||||
|
|
@ -149,14 +157,22 @@ impl MvuApp<Model> for App {
|
||||||
match event {
|
match event {
|
||||||
Event::MouseButtonPressed => model,
|
Event::MouseButtonPressed => model,
|
||||||
Event::OpenTestFile(file_path) => open_test_file(file_path, model.clone()),
|
Event::OpenTestFile(file_path) => open_test_file(file_path, model.clone()),
|
||||||
|
Event::ClearScene => clear_scene(model.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn view(&mut self, model: Rc<Model>) -> Result<(), Box<dyn std::error::Error>> {
|
async fn view(&mut self, model: Rc<Model>) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
if let Some(context) = &mut self.context {
|
if let Some(context) = &mut self.context {
|
||||||
context.frame_timer.mark_frame_start();
|
context.frame_timer.mark_frame_start();
|
||||||
if context.scene_data.is_none() {
|
context
|
||||||
|
.statistics_reporter
|
||||||
|
.log_frame_time_seconds(context.frame_timer.get_frame_time_seconds());
|
||||||
if let Some(dem) = &model.dem {
|
if let Some(dem) = &model.dem {
|
||||||
|
if context
|
||||||
|
.scene_data
|
||||||
|
.as_ref()
|
||||||
|
.is_none_or(|scene_data| scene_data.source.id != dem.id)
|
||||||
|
{
|
||||||
context.scene_data = Some(DemRenderer::new(
|
context.scene_data = Some(DemRenderer::new(
|
||||||
dem.clone(),
|
dem.clone(),
|
||||||
&context.device,
|
&context.device,
|
||||||
|
|
@ -164,6 +180,8 @@ impl MvuApp<Model> for App {
|
||||||
&context.queue,
|
&context.queue,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
context.scene_data = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let frame = context
|
let frame = context
|
||||||
|
|
@ -215,3 +233,10 @@ fn open_test_file(file: Box<dyn File>, model: Rc<Model>) -> Rc<Model> {
|
||||||
let dem = Some(Rc::new(raster::Dem::load_from_image(file)));
|
let dem = Some(Rc::new(raster::Dem::load_from_image(file)));
|
||||||
Rc::new(Model { dem, ..*model })
|
Rc::new(Model { dem, ..*model })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn clear_scene(model: Rc<Model>) -> Rc<Model> {
|
||||||
|
Rc::new(Model {
|
||||||
|
dem: None,
|
||||||
|
..*model
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::mvu::File;
|
use crate::mvu::File;
|
||||||
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Dem {
|
pub struct Dem {
|
||||||
|
|
@ -11,6 +12,7 @@ pub struct Dem {
|
||||||
pub z_min: f32,
|
pub z_min: f32,
|
||||||
pub z_max: f32,
|
pub z_max: f32,
|
||||||
pub grid: Vec<u16>,
|
pub grid: Vec<u16>,
|
||||||
|
pub id: Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for Dem {
|
impl std::fmt::Debug for Dem {
|
||||||
|
|
@ -46,6 +48,18 @@ pub struct DemBvhLayer {
|
||||||
pub data: Vec<u16>,
|
pub data: Vec<u16>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
pub struct Id(u32);
|
||||||
|
|
||||||
|
impl Id {
|
||||||
|
fn get_next() -> Self {
|
||||||
|
NEXT_ID.fetch_add(1, Ordering::SeqCst);
|
||||||
|
Self(NEXT_ID.load(Ordering::SeqCst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NEXT_ID: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
/// Find the smallest number that's larger then the input and that is also one
|
/// Find the smallest number that's larger then the input and that is also one
|
||||||
/// less than a power of two.
|
/// less than a power of two.
|
||||||
fn round_bound(v: u32) -> u32 {
|
fn round_bound(v: u32) -> u32 {
|
||||||
|
|
@ -198,6 +212,7 @@ impl Dem {
|
||||||
z_min,
|
z_min,
|
||||||
z_max,
|
z_max,
|
||||||
grid,
|
grid,
|
||||||
|
id: Id::get_next(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -283,6 +298,7 @@ mod tests {
|
||||||
z_min: z1.min(z2),
|
z_min: z1.min(z2),
|
||||||
z_max: z1.max(z2),
|
z_max: z1.max(z2),
|
||||||
grid,
|
grid,
|
||||||
|
id: Id::get_next()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.boxed()
|
.boxed()
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ impl File for std::io::Cursor<Vec<u8>> {}
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
MouseButtonPressed,
|
MouseButtonPressed,
|
||||||
OpenTestFile(Box<dyn File>),
|
OpenTestFile(Box<dyn File>),
|
||||||
|
ClearScene,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,16 @@ impl PteropusCanvas {
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub async fn clear_scene(&mut self) {
|
||||||
|
self.model = self
|
||||||
|
.app
|
||||||
|
.lock()
|
||||||
|
.expect("get app mutex")
|
||||||
|
.update(Rc::clone(&self.model), Event::ClearScene)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_canvas(&self) -> HtmlCanvasElement {
|
pub fn get_canvas(&self) -> HtmlCanvasElement {
|
||||||
let window = web_sys::window().expect("get window");
|
let window = web_sys::window().expect("get window");
|
||||||
let document = window.document().expect("get HTML document");
|
let document = window.document().expect("get HTML document");
|
||||||
|
|
|
||||||
|
|
@ -2,61 +2,20 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Pteropus</title>
|
<title>Pteropus</title>
|
||||||
<style>
|
<link rel="stylesheet" href="pteropus-frame.css">
|
||||||
#pteropus-canvas {
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div id="whole-window">
|
||||||
|
<script type="module" src="pteropus-frame.js"></script>
|
||||||
<canvas id="pteropus-canvas"></canvas>
|
<canvas id="pteropus-canvas"></canvas>
|
||||||
<script type="module">
|
<div id="bottom-panel">
|
||||||
import init, {init_pteropus} from '../pkg/pteropus.js';
|
<input type="file" id="file-upload-file-input" accept="image/tiff, .tif, .tiff">
|
||||||
|
<button id="clear-scene-button">Clear Scene</button>
|
||||||
function fileDragOverHandler(event) {
|
<button id="load-small-sample-button">Load Small Sample</button>
|
||||||
event.preventDefault();
|
<button id="load-medium-sample-button">Load Medium Sample</button>
|
||||||
}
|
<button id="load-large-sample-button">Load Large Sample</button>
|
||||||
|
</div>
|
||||||
async function run() {
|
</div>
|
||||||
await init();
|
</body>
|
||||||
|
|
||||||
let pteropus = await init_pteropus();
|
|
||||||
|
|
||||||
let test_file_data = null;
|
|
||||||
let needs_resize = true;
|
|
||||||
|
|
||||||
let mainCanvas = document.getElementById("pteropus-canvas");
|
|
||||||
mainCanvas.addEventListener("drop", async (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
test_file_data = event.dataTransfer.files[0]
|
|
||||||
});
|
|
||||||
mainCanvas.addEventListener("dragover", fileDragOverHandler);
|
|
||||||
const resizeObserver = new ResizeObserver((entries) => {
|
|
||||||
needs_resize = true;
|
|
||||||
});
|
|
||||||
resizeObserver.observe(mainCanvas)
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
await pteropus.render();
|
|
||||||
// TODO: I really want to do this asynchronously, with the drop
|
|
||||||
// event listener calling pteropus_load_file directly, but I need
|
|
||||||
// to figure out how to get that to work.
|
|
||||||
if(test_file_data) {
|
|
||||||
await pteropus.load_file(test_file_data);
|
|
||||||
test_file_data = null
|
|
||||||
}
|
|
||||||
if(needs_resize) {
|
|
||||||
await pteropus.on_resize();
|
|
||||||
needs_resize = false;
|
|
||||||
}
|
|
||||||
await new Promise(requestAnimationFrame);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
run();
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,27 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#whole-window {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pteropus-canvas {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#bottom-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 0;
|
||||||
|
margin: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left: 0.2rem;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
import init, {init_pteropus} from '../pkg/pteropus.js';
|
||||||
|
|
||||||
|
function fileDragOverHandler(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
await init();
|
||||||
|
|
||||||
|
let pteropus = await init_pteropus();
|
||||||
|
|
||||||
|
let test_file_data = null;
|
||||||
|
let needs_resize = true;
|
||||||
|
let clear_scene = false;
|
||||||
|
|
||||||
|
let mainCanvas = document.getElementById("pteropus-canvas");
|
||||||
|
mainCanvas.addEventListener("drop", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
test_file_data = event.dataTransfer.files[0]
|
||||||
|
});
|
||||||
|
mainCanvas.addEventListener("dragover", fileDragOverHandler);
|
||||||
|
const resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
needs_resize = true;
|
||||||
|
});
|
||||||
|
resizeObserver.observe(mainCanvas);
|
||||||
|
|
||||||
|
let fileUploadButton = document.getElementById("file-upload-file-input");
|
||||||
|
fileUploadButton.addEventListener("change", () => {
|
||||||
|
const files = fileUploadButton.files;
|
||||||
|
if(files.length > 0) {
|
||||||
|
test_file_data = files[0]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let clearSceneButton = document.getElementById("clear-scene-button");
|
||||||
|
clearSceneButton.addEventListener("click", () => {
|
||||||
|
clear_scene = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{
|
||||||
|
buttonId: "load-small-sample-button",
|
||||||
|
url: "small_sample.tiff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonId: "load-medium-sample-button",
|
||||||
|
url: "medium_sample.tiff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
buttonId: "load-large-sample-button",
|
||||||
|
url: "large_sample.tiff"
|
||||||
|
}
|
||||||
|
].forEach( (sample) => {
|
||||||
|
let loadSampleButton = document.getElementById(sample.buttonId);
|
||||||
|
loadSampleButton.addEventListener("click", async () => {
|
||||||
|
const response = await fetch(sample.url);
|
||||||
|
if(!response.ok) {
|
||||||
|
console.error("Couild not load sample file.")
|
||||||
|
}
|
||||||
|
test_file_data = await response.blob()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
await pteropus.render();
|
||||||
|
if(needs_resize) {
|
||||||
|
await pteropus.on_resize();
|
||||||
|
needs_resize = false;
|
||||||
|
}
|
||||||
|
if(clear_scene) {
|
||||||
|
await pteropus.clear_scene();
|
||||||
|
clear_scene = false;
|
||||||
|
}
|
||||||
|
// TODO: I really want to do this asynchronously, with the drop
|
||||||
|
// event listener calling pteropus_load_file directly, but I need
|
||||||
|
// to figure out how to get that to work.
|
||||||
|
if(test_file_data) {
|
||||||
|
await pteropus.load_file(test_file_data);
|
||||||
|
test_file_data = null
|
||||||
|
}
|
||||||
|
await new Promise(requestAnimationFrame);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
Binary file not shown.
Loading…
Reference in New Issue