Compare commits

..

4 Commits

Author SHA1 Message Date
d3d4048a5c Nice wide lines 2024-09-23 23:04:49 +03:00
b8f0ce0b68 Provide a tangent at each traced point 2024-09-23 22:24:43 +03:00
1d57ca8a93 Rotation-independent line width 2024-09-23 22:14:13 +03:00
dca80473d0 Crude wide lines 2024-09-23 22:02:38 +03:00
6 changed files with 138 additions and 92 deletions

View File

@ -166,13 +166,13 @@ fn draw_ray_2(gc: &mut Vec<Draw>, space: &Space, camera: Location, dir: Vec3) {
gc.new_path(); gc.new_path();
gc.move_to(pos.x, pos.y); gc.move_to(pos.x, pos.y);
for pt in &path.points[1..] { for pt in &path.points[1..] {
gc.line_to(pt.x, pt.y); gc.line_to(pt.pos.x, pt.pos.y);
} }
let end_pos = *path let end_pos = *path
.points .points
.last() .last()
.expect("the starting point is always in the path"); .expect("the starting point is always in the path");
let dir_pos = end_pos + 1000.0 * path.end_dir; let dir_pos = end_pos.forward(1000. / DT).pos;
gc.line_to(dir_pos.x, dir_pos.y); gc.line_to(dir_pos.x, dir_pos.y);
gc.stroke(); gc.stroke();
} }

View File

@ -20,21 +20,7 @@ mod scene;
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex { struct Vertex {
position: [f32; 3], position: [f32; 3],
} tangent: [f32; 3],
impl Vertex {
fn desc() -> wgpu::VertexBufferLayout<'static> {
use std::mem;
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
offset: mem::offset_of!(Self, position) as u64,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
}],
}
}
} }
struct Wireframe { struct Wireframe {
@ -48,28 +34,14 @@ fn prepare_scene(device: &wgpu::Device) -> Vec<Wireframe> {
.into_iter() .into_iter()
.map(|line| { .map(|line| {
let color = line.color; let color = line.color;
let data: Vec<Vertex>; let data: Vec<Vertex> = line
match line.line { .pts
scene::Line::Lines(_) => todo!(), .into_iter()
scene::Line::Strip(pts) => { .map(|r| Vertex {
data = pts position: r.pos.to_array(),
.into_iter() tangent: r.dir.to_array(),
.map(|p| Vertex { })
position: p.to_array(), .collect();
})
.collect();
}
scene::Line::Loop(pts) => {
let first = pts.first().copied();
data = pts
.into_iter()
.chain(first)
.map(|p| Vertex {
position: p.to_array(),
})
.collect();
}
};
let size = data.len() as u32; let size = data.len() as u32;
let data = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let data = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"), label: Some("Vertex Buffer"),
@ -172,6 +144,8 @@ static KEYS_ROTATE: &'static [(PhysicalKey, Vec3)] = &[
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct CameraUniform { struct CameraUniform {
mvp: [[f32; 4]; 4], mvp: [[f32; 4]; 4],
scale: [f32; 2],
pad: [u32; 2],
} }
#[repr(C)] #[repr(C)]
@ -193,7 +167,6 @@ struct State<'a> {
cam: camctl::CameraLocation, cam: camctl::CameraLocation,
t1: Instant, t1: Instant,
camera_uniform: CameraUniform,
camera_buffer: wgpu::Buffer, camera_buffer: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup, camera_bind_group: wgpu::BindGroup,
@ -257,13 +230,11 @@ impl<'a> State<'a> {
let cam = camctl::CameraLocation::new(); let cam = camctl::CameraLocation::new();
let t1 = Instant::now(); let t1 = Instant::now();
let camera_uniform = CameraUniform { let camera_buffer = device.create_buffer(&wgpu::BufferDescriptor {
mvp: cam.view_mtx().to_cols_array_2d(),
};
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"), label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[camera_uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: mem::size_of::<CameraUniform>() as u64,
mapped_at_creation: false,
}); });
let camera_bind_group_layout = let camera_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -311,7 +282,34 @@ impl<'a> State<'a> {
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module: &shader, module: &shader,
entry_point: "vs_main", entry_point: "vs_main",
buffers: &[Vertex::desc()], buffers: &[wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Vertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: &[
wgpu::VertexAttribute {
offset: mem::offset_of!(Vertex, position) as u64,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: mem::offset_of!(Vertex, tangent) as u64,
shader_location: 1,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: (mem::size_of::<Vertex>() + mem::offset_of!(Vertex, position))
as u64,
shader_location: 2,
format: wgpu::VertexFormat::Float32x3,
},
wgpu::VertexAttribute {
offset: (mem::size_of::<Vertex>() + mem::offset_of!(Vertex, tangent))
as u64,
shader_location: 3,
format: wgpu::VertexFormat::Float32x3,
},
],
}],
compilation_options: Default::default(), compilation_options: Default::default(),
}, },
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
@ -328,7 +326,7 @@ impl<'a> State<'a> {
compilation_options: Default::default(), compilation_options: Default::default(),
}), }),
primitive: wgpu::PrimitiveState { primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::LineStrip, topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default() ..Default::default()
}, },
depth_stencil: None, depth_stencil: None,
@ -356,7 +354,6 @@ impl<'a> State<'a> {
scene, scene,
camera_buffer, camera_buffer,
camera_bind_group, camera_bind_group,
camera_uniform,
window, window,
} }
} }
@ -397,12 +394,13 @@ impl<'a> State<'a> {
); );
let view = my_to_gl * self.cam.view_mtx(); let view = my_to_gl * self.cam.view_mtx();
let mvp = proj * view; let mvp = proj * view;
self.camera_uniform.mvp = mvp.to_cols_array_2d(); let camera_uniform = CameraUniform {
self.queue.write_buffer( mvp: mvp.to_cols_array_2d(),
&self.camera_buffer, scale: (1. / size).to_array(),
0, pad: [0; 2],
bytemuck::cast_slice(&[self.camera_uniform]), };
); self.queue
.write_buffer(&self.camera_buffer, 0, bytemuck::bytes_of(&camera_uniform));
} }
fn render(&mut self) -> Result<(), wgpu::SurfaceError> { fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
@ -447,7 +445,7 @@ impl<'a> State<'a> {
}; };
render_pass.set_push_constants(ShaderStages::VERTEX, 0, bytemuck::bytes_of(&line)); render_pass.set_push_constants(ShaderStages::VERTEX, 0, bytemuck::bytes_of(&line));
render_pass.set_vertex_buffer(0, wireframe.data.slice(..)); render_pass.set_vertex_buffer(0, wireframe.data.slice(..));
render_pass.draw(0..wireframe.size, 0..1); render_pass.draw(0..4, 0..wireframe.size - 1);
} }
} }

View File

@ -1,5 +1,6 @@
struct CameraUniform { struct CameraUniform {
mvp: mat4x4<f32>, mvp: mat4x4<f32>,
scale: vec2<f32>,
} }
@group(0) @binding(0) @group(0) @binding(0)
var<uniform> camera: CameraUniform; var<uniform> camera: CameraUniform;
@ -7,10 +8,17 @@ var<uniform> camera: CameraUniform;
struct LineUniform { struct LineUniform {
color: vec3<f32>, color: vec3<f32>,
} }
const width = 0.1;
var<push_constant> line: LineUniform; var<push_constant> line: LineUniform;
struct VertexInput { struct SegmentInput {
@location(0) position: vec3<f32>, @location(0) a: vec3<f32>,
@location(1) ad: vec3<f32>,
@location(2) b: vec3<f32>,
@location(3) bd: vec3<f32>,
}
struct OffsetInput {
@builtin(vertex_index) idx: u32,
} }
struct VertexOutput { struct VertexOutput {
@ -19,15 +27,34 @@ struct VertexOutput {
} }
@vertex @vertex
fn vs_main(model: VertexInput) -> VertexOutput { fn vs_main(seg: SegmentInput, off: OffsetInput) -> VertexOutput {
var out: VertexOutput; var out: VertexOutput;
out.vertex_color = line.color; out.vertex_color = line.color;
out.clip_position = camera.mvp * vec4(model.position, 1.0); var pt: vec3<f32>;
var dir: vec3<f32>;
switch (off.idx) {
case 0u: { pt = seg.a; dir = seg.ad; }
case 1u: { pt = seg.a; dir = seg.ad; }
case 2u: { pt = seg.b; dir = seg.bd; }
case 3u: { pt = seg.b; dir = seg.bd; }
default: {}
}
var sgn: f32;
switch (off.idx) {
case 0u: { sgn = -1.; }
case 1u: { sgn = 1.; }
case 2u: { sgn = -1.; }
case 3u: { sgn = 1.; }
default: {}
}
let pt_cs = camera.mvp * vec4(pt, 1.);
let dir_cs = camera.mvp * vec4(dir, 0.);
let normal_cs = camera.scale * normalize(vec2(-dir_cs.y, dir_cs.x));
out.clip_position = pt_cs + vec4(sgn * width * normal_cs, 0., 0.);
return out; return out;
} }
@fragment @fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let opacity = pow(0.5 - 0.5 * in.clip_position.z, 0.25); return 0.5 * vec4(in.vertex_color, 1.0);
return opacity * vec4(in.vertex_color, 1.0);
} }

View File

@ -8,27 +8,44 @@ use refraction::types::{Location, Object, Ray};
use refraction::utils::put_object; use refraction::utils::put_object;
pub enum Line { pub enum Line {
Lines(Vec<(Vec3, Vec3)>), Strip(Vec<Ray>),
Strip(Vec<Vec3>), Loop(Vec<Ray>),
Loop(Vec<Vec3>),
} }
pub struct FancyLine { pub struct FancyLine {
pub color: Vec3, pub color: Vec3,
pub line: Line, pub pts: Vec<Ray>,
} }
fn paint(onto: &mut Vec<FancyLine>, color: Vec3, lines: Vec<Line>) { fn paint(onto: &mut Vec<FancyLine>, color: Vec3, lines: Vec<Line>) {
onto.extend(lines.into_iter().map(move |line| FancyLine { color, line })) onto.extend(lines.into_iter().map(move |line| FancyLine {
color,
pts: match line {
Line::Strip(pts) => pts,
Line::Loop(mut pts) => {
pts.push(*pts.first().unwrap());
pts
}
},
}))
} }
fn draw_rect(center: Vec3, u: Vec3, v: Vec3) -> Line { fn draw_line(a: Vec3, b: Vec3) -> Line {
Line::Loop(vec![ let dir = (b - a).normalize();
center - u - v, Line::Strip(vec![Ray { pos: a, dir }, Ray { pos: b, dir }])
center + u - v, }
center + u + v,
center - u + v, fn draw_rect(center: Vec3, u: Vec3, v: Vec3) -> Vec<Line> {
]) let a = center - u - v;
let b = center + u - v;
let c = center + u + v;
let d = center - u + v;
vec![
draw_line(a, b),
draw_line(b, c),
draw_line(c, d),
draw_line(d, a),
]
} }
fn draw_ellipse(center: Vec3, u: Vec3, v: Vec3) -> Line { fn draw_ellipse(center: Vec3, u: Vec3, v: Vec3) -> Line {
@ -38,7 +55,10 @@ fn draw_ellipse(center: Vec3, u: Vec3, v: Vec3) -> Line {
(0..segments) (0..segments)
.map(|k| k as f32 * step) .map(|k| k as f32 * step)
.map(Vec2::from_angle) .map(Vec2::from_angle)
.map(|d| center + d.x * u + d.y * v) .map(|d| Ray {
pos: center + d.x * u + d.y * v,
dir: -d.y * u + d.x * v,
})
.collect(), .collect(),
) )
} }
@ -107,7 +127,7 @@ fn draw_ray_2(gc: &mut Vec<Line>, space: &Space, camera: Location, dir: Vec3) {
let end_pos = *pts let end_pos = *pts
.last() .last()
.expect("the starting point is always in the path"); .expect("the starting point is always in the path");
let dir_pos = end_pos + 10000.0 * path.end_dir; let dir_pos = end_pos.forward(10000.0);
pts.push(dir_pos); pts.push(dir_pos);
gc.push(Line::Strip(pts)); gc.push(Line::Strip(pts));
} }
@ -135,7 +155,7 @@ impl Renderable for Tube {
.map(|k| k as f32 * step) .map(|k| k as f32 * step)
.map(Vec2::from_angle) .map(Vec2::from_angle)
.map(|d| vec3(d.x, 0., d.y)) .map(|d| vec3(d.x, 0., d.y))
.map(|d| draw_rect(r * d, w * d, l)); .flat_map(|d| draw_rect(r * d, w * d, l));
let caps = iproduct!([self.inner_radius, self.outer_radius], [-l, l]) let caps = iproduct!([self.inner_radius, self.outer_radius], [-l, l])
.map(|(r, l)| draw_ellipse(l, vec3(r, 0., 0.), vec3(0., 0., r))); .map(|(r, l)| draw_ellipse(l, vec3(r, 0., 0.), vec3(0., 0., r)));
chain!(along, caps).collect() chain!(along, caps).collect()

View File

@ -1,5 +1,4 @@
use crate::types::{Hit, Location, Ray}; use crate::types::{Hit, Location, Ray};
use glam::Vec3;
pub trait Traceable { pub trait Traceable {
/// Traces a ray from a given starting point. `ray` is relative to the camera. /// Traces a ray from a given starting point. `ray` is relative to the camera.
@ -19,8 +18,7 @@ pub trait OptimizedTraceable: Traceable {
} }
pub struct RayPath { pub struct RayPath {
pub points: Vec<Vec3>, pub points: Vec<Ray>,
pub end_dir: Vec3,
} }
pub trait DebugTraceable: Traceable { pub trait DebugTraceable: Traceable {

View File

@ -146,16 +146,25 @@ impl Space {
.collect() .collect()
} }
pub fn line(&self, a: Vec3, b: Vec3, step: f32) -> Vec<Vec3> { pub fn line(&self, a: Vec3, b: Vec3, step: f32) -> Vec<Ray> {
match self.which_subspace(a) { match self.which_subspace(a) {
Outer => vec![b], Outer => vec![Ray {
pos: b,
dir: (b - a).normalize(),
}],
Inner => { Inner => {
let cs = InnerCS(self.tube); let cs = InnerCS(self.tube);
let n = ((b - a).length() / step) as usize + 1; let n = ((b - a).length() / step) as usize + 1;
let a = cs.global_to_flat(a); let a = cs.global_to_flat(a);
let b = cs.global_to_flat(b); let b = cs.global_to_flat(b);
let dir = (b - a).normalize();
(1..=n) (1..=n)
.map(|k| cs.flat_to_global(a.lerp(b, k as f32 / n as f32))) .map(|k| {
cs.flat_to_global(Ray {
pos: a.lerp(b, k as f32 / n as f32),
dir,
})
})
.collect() .collect()
} }
Boundary => panic!("Can't draw a line here!"), Boundary => panic!("Can't draw a line here!"),
@ -210,9 +219,9 @@ impl DebugTraceable for Space {
let mut hits = vec![]; let mut hits = vec![];
let mut ray = self.camera_ray_to_abs(camera, ray); let mut ray = self.camera_ray_to_abs(camera, ray);
let trace_to_flat = |points: &mut Vec<Vec3>, ray| { let trace_to_flat = |points: &mut Vec<Ray>, ray| {
for ray in self.trace_iter(ray).skip(1) { for ray in self.trace_iter(ray).skip(1) {
points.push(ray.pos); points.push(ray);
if let Some(hitter) = self.obj_hitter(ray.pos) { if let Some(hitter) = self.obj_hitter(ray.pos) {
return (ray, hitter(self, ray)); return (ray, hitter(self, ray));
} }
@ -220,18 +229,12 @@ impl DebugTraceable for Space {
unreachable!("Space::trace_iter terminated!") unreachable!("Space::trace_iter terminated!")
}; };
points.push(ray.pos); points.push(ray);
for _ in 0..100 { for _ in 0..100 {
let (ray_into_flat, ret) = trace_to_flat(&mut points, ray); let (ray_into_flat, ret) = trace_to_flat(&mut points, ray);
hits.extend(ret.objects); // TODO fix distance hits.extend(ret.objects); // TODO fix distance
let Some(ray_outta_flat) = ret.end else { let Some(ray_outta_flat) = ret.end else {
return ( return (hits, RayPath { points });
hits,
RayPath {
points,
end_dir: ray_into_flat.dir.normalize(),
},
);
}; };
points.extend(self.line(ray_into_flat.pos, ray_outta_flat.pos, 10.0)); points.extend(self.line(ray_into_flat.pos, ray_outta_flat.pos, 10.0));
ray = ray_outta_flat; ray = ray_outta_flat;