commit
b98fd77c1a
5 changed files with 262 additions and 0 deletions
-
2.gitignore
-
11Cargo.toml
-
55src/geometry.rs
-
9src/main.rs
-
185src/render_loop.rs
@ -0,0 +1,2 @@ |
|||
/target |
|||
Cargo.lock |
@ -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" |
@ -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 })
|
|||
}
|
|||
}
|
@ -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);
|
|||
}
|
@ -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;
|
|||
}
|
|||
_ => ()
|
|||
}
|
|||
})
|
|||
}
|
|||
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue