Browse Source

added event loop and windowing

main
Thomas Johnson 7 months ago
commit
b98fd77c1a
  1. 2
      .gitignore
  2. 11
      Cargo.toml
  3. 55
      src/geometry.rs
  4. 9
      src/main.rs
  5. 185
      src/render_loop.rs

2
.gitignore

@ -0,0 +1,2 @@
/target
Cargo.lock

11
Cargo.toml

@ -0,0 +1,11 @@
[package]
name = "hyper1"
version = "0.1.0"
edition = "2018"
resolver = "2"
[dependencies]
nalgebra = "0.30.1"
winit = "0.26"
wgpu = "0.12"
pollster = "0.2"

55
src/geometry.rs

@ -0,0 +1,55 @@
use nalgebra::{Vector4};
pub type HypVec3 = Vector4<f64>;
/// Lorentz inner product of two vectors
pub fn lorentz(a: HypVec3, b: HypVec3) -> f64 {
a[1] * b[1] + a[2] * b[2] + a[3] * b[3] - a[0] * b[0]
}
pub fn lorentz_norm(a: HypVec3) -> f64 {
lorentz(a, a)
}
pub fn normalize_timelike(a: HypVec3) -> Option<HypVec3> {
let n = -lorentz_norm(a);
if !(n > 0.0) {
None
} else {
Some(a * n.sqrt().recip())
}
}
pub fn normalize_spacelike(a: HypVec3) -> Option<HypVec3> {
let n = lorentz_norm(a);
if !(n > 0.0) {
None
} else {
Some(a * n.sqrt().recip())
}
}
/// Represents a line (or a ray) in hyperbolic space.
pub struct Line {
/// A point that the line passes through.
timelike: HypVec3,
/// A spacelike unit representing the direction of the line or ray.
direction: HypVec3,
}
/// Represents a surface with constant curvature. In the hyperboloid representation of the space, the surface represents the points whose Lorentz inner product with `direction` is equal to `offset`.
pub struct Surface {
/// The vector whose Lorentz inner product with each point on the surface is constant. If this is timelike, the surface is a sphere, and this is its center. If this is spacelike, the surface is hypercyclic, and this is its plane. Otherwise, the surface is horocyclic, and this represents the convergence point of its perpendiculars.
direction: HypVec3,
/// If `direction` is a timelike unit, this is the negation of the hyperbolic cosine of the radius of the sphere around the corresponding point; if `direction` is a spacelike unit, this is the hyperbolic sine of the distance from the corresponding plane.
offset: f64,
}
impl Line {
/// Create a ray from timelike unit `from` to `to`.
fn new_ray(from: HypVec3, to: HypVec3) -> Option<Line> {
let timelike = from;
let direction = normalize_spacelike((to - from) + timelike * lorentz(timelike, (to - from)))?;
Some(Line { timelike, direction })
}
}

9
src/main.rs

@ -0,0 +1,9 @@
mod geometry;
mod render_loop;
fn main() {
println!("initializing render loop");
let (el, rl) = pollster::block_on(render_loop::RenderLoop::new()).expect("could not create render loop!");
println!("running render loop");
rl.run(el);
}

185
src/render_loop.rs

@ -0,0 +1,185 @@
use winit::{event_loop::{EventLoop, ControlFlow}, window::WindowBuilder, window::Window, event::{Event, WindowEvent, DeviceEvent}, dpi::{Size, PhysicalSize}};
use wgpu::{Instance, Surface, Backends, Features, Adapter, DeviceDescriptor, Device, Queue, Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, Extent3d, CommandEncoder, CommandEncoderDescriptor, CommandBuffer, ImageCopyTexture, Origin3d, TextureAspect, SurfaceConfiguration, SurfaceTexture, PresentMode};
const DEFAULT_DIMS: [u32; 2] = [100, 100];
pub struct RenderLoop {
window: Window,
instance: Instance,
surface: Surface,
adapter: Adapter,
device: Device,
queue: Queue,
texture_buffer: Texture,
}
impl RenderLoop {
// TODO: add an error type
pub async fn new() -> Option<(EventLoop<()>, RenderLoop)> {
let event_loop = EventLoop::new();
let window = WindowBuilder::new()
.with_visible(true)
.with_inner_size(Size::Physical(PhysicalSize {
width: DEFAULT_DIMS[0],
height: DEFAULT_DIMS[1],
}))
.build(&event_loop).ok()?;
let instance = Instance::new(Backends::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
let required_features = Features::empty();
let mut adapters: Vec<_> = instance.enumerate_adapters(Backends::all()).filter(|a| a.is_surface_supported(&surface) && a.features().contains(required_features)).collect();
if adapters.is_empty() {
return None;
}
println!("available adapters:");
for (i, adapter) in adapters.iter().enumerate() {
let info = adapter.get_info();
println!("{}: {}", i, info.name);
}
// TODO: intelligently select an adapter
let adapter = adapters.swap_remove(1);
let limits = adapter.limits();
let device_descriptor = DeviceDescriptor {
label: None,
features: required_features,
limits: limits.clone(),
};
let (device, queue) = adapter.request_device(&device_descriptor, None).await.ok()?;
let win_size = window.inner_size();
println!("ws: {:?}", win_size);
let texture = device.create_texture(
&TextureDescriptor {
label: None,
size: Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8Unorm,
usage: TextureUsages::COPY_SRC | TextureUsages::COPY_DST,
}
);
surface.configure(&device, &SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_DST,
format: TextureFormat::Bgra8Unorm,
width: win_size.width,
height: win_size.height,
present_mode: PresentMode::Mailbox,
});
Some((event_loop, RenderLoop {
window,
instance,
surface,
adapter,
device,
queue,
texture_buffer: texture,
})
)
}
async fn rebuild(&mut self) {
self.surface = unsafe { self.instance.create_surface(&self.window) };
let win_size = self.window.inner_size();
self.texture_buffer = self.device.create_texture(
&TextureDescriptor {
label: None,
size: Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Bgra8Unorm,
usage: TextureUsages::COPY_SRC | TextureUsages::COPY_DST,
}
);
self.surface.configure(&self.device, &SurfaceConfiguration {
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_DST,
format: TextureFormat::Bgra8Unorm,
width: win_size.width,
height: win_size.height,
present_mode: PresentMode::Mailbox,
});
}
async fn render(&mut self) -> Option<()>{
let st = loop {
let sto = self.surface.get_current_texture().ok();
if let Some(st@SurfaceTexture { suboptimal: false, .. }) = sto {
break st;
} else {
self.rebuild().await;
}
};
let mut encoder = self.device.create_command_encoder(&CommandEncoderDescriptor { label: None });
let win_size = self.window.inner_size();
encoder.copy_texture_to_texture(
ImageCopyTexture {
texture: &self.texture_buffer,
mip_level: 0,
origin: Origin3d::ZERO,
aspect: TextureAspect::All,
},
ImageCopyTexture {
texture: &st.texture,
mip_level: 0,
origin: Origin3d::ZERO,
aspect: TextureAspect::All,
},
Extent3d {
width: win_size.width,
height: win_size.height,
depth_or_array_layers: 1,
}
);
self.queue.submit(Some(encoder.finish()));
// I need this here or my flakey GPU driver crashes
self.device.poll(wgpu::Maintain::Wait);
self.queue.on_submitted_work_done().await;
st.present();
Some(())
}
pub fn run(mut self, event_loop: EventLoop<()>) {
event_loop.run(move |ev, _, cf| {
//println!("running event loop: {:?}", ev);
*cf = ControlFlow::Wait;
match ev {
Event::MainEventsCleared => {
self.window.request_redraw();
}
Event::RedrawRequested(_) => {
pollster::block_on(self.render());
}
Event::WindowEvent {
event: WindowEvent::Resized(new_size),
..
} => {
pollster::block_on(self.rebuild());
}
Event::WindowEvent {
event: WindowEvent::CloseRequested | WindowEvent::Destroyed,
..
} => {
*cf = ControlFlow::Exit;
}
_ => ()
}
})
}
}
Loading…
Cancel
Save