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.move_to(pos.x, pos.y);
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
.points
.last()
.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.stroke();
}

View File

@ -20,21 +20,7 @@ mod scene;
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex {
position: [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,
}],
}
}
tangent: [f32; 3],
}
struct Wireframe {
@ -48,28 +34,14 @@ fn prepare_scene(device: &wgpu::Device) -> Vec<Wireframe> {
.into_iter()
.map(|line| {
let color = line.color;
let data: Vec<Vertex>;
match line.line {
scene::Line::Lines(_) => todo!(),
scene::Line::Strip(pts) => {
data = pts
let data: Vec<Vertex> = line
.pts
.into_iter()
.map(|p| Vertex {
position: p.to_array(),
.map(|r| Vertex {
position: r.pos.to_array(),
tangent: r.dir.to_array(),
})
.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 data = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
@ -172,6 +144,8 @@ static KEYS_ROTATE: &'static [(PhysicalKey, Vec3)] = &[
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct CameraUniform {
mvp: [[f32; 4]; 4],
scale: [f32; 2],
pad: [u32; 2],
}
#[repr(C)]
@ -193,7 +167,6 @@ struct State<'a> {
cam: camctl::CameraLocation,
t1: Instant,
camera_uniform: CameraUniform,
camera_buffer: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup,
@ -257,13 +230,11 @@ impl<'a> State<'a> {
let cam = camctl::CameraLocation::new();
let t1 = Instant::now();
let camera_uniform = CameraUniform {
mvp: cam.view_mtx().to_cols_array_2d(),
};
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
let camera_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[camera_uniform]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
size: mem::size_of::<CameraUniform>() as u64,
mapped_at_creation: false,
});
let camera_bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -311,7 +282,34 @@ impl<'a> State<'a> {
vertex: wgpu::VertexState {
module: &shader,
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(),
},
fragment: Some(wgpu::FragmentState {
@ -328,7 +326,7 @@ impl<'a> State<'a> {
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::LineStrip,
topology: wgpu::PrimitiveTopology::TriangleStrip,
..Default::default()
},
depth_stencil: None,
@ -356,7 +354,6 @@ impl<'a> State<'a> {
scene,
camera_buffer,
camera_bind_group,
camera_uniform,
window,
}
}
@ -397,12 +394,13 @@ impl<'a> State<'a> {
);
let view = my_to_gl * self.cam.view_mtx();
let mvp = proj * view;
self.camera_uniform.mvp = mvp.to_cols_array_2d();
self.queue.write_buffer(
&self.camera_buffer,
0,
bytemuck::cast_slice(&[self.camera_uniform]),
);
let camera_uniform = CameraUniform {
mvp: mvp.to_cols_array_2d(),
scale: (1. / size).to_array(),
pad: [0; 2],
};
self.queue
.write_buffer(&self.camera_buffer, 0, bytemuck::bytes_of(&camera_uniform));
}
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_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 {
mvp: mat4x4<f32>,
scale: vec2<f32>,
}
@group(0) @binding(0)
var<uniform> camera: CameraUniform;
@ -7,10 +8,17 @@ var<uniform> camera: CameraUniform;
struct LineUniform {
color: vec3<f32>,
}
const width = 0.1;
var<push_constant> line: LineUniform;
struct VertexInput {
@location(0) position: vec3<f32>,
struct SegmentInput {
@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 {
@ -19,15 +27,34 @@ struct VertexOutput {
}
@vertex
fn vs_main(model: VertexInput) -> VertexOutput {
fn vs_main(seg: SegmentInput, off: OffsetInput) -> VertexOutput {
var out: VertexOutput;
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;
}
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
let opacity = pow(0.5 - 0.5 * in.clip_position.z, 0.25);
return opacity * vec4(in.vertex_color, 1.0);
return 0.5 * vec4(in.vertex_color, 1.0);
}

View File

@ -8,27 +8,44 @@ use refraction::types::{Location, Object, Ray};
use refraction::utils::put_object;
pub enum Line {
Lines(Vec<(Vec3, Vec3)>),
Strip(Vec<Vec3>),
Loop(Vec<Vec3>),
Strip(Vec<Ray>),
Loop(Vec<Ray>),
}
pub struct FancyLine {
pub color: Vec3,
pub line: Line,
pub pts: Vec<Ray>,
}
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 {
Line::Loop(vec![
center - u - v,
center + u - v,
center + u + v,
center - u + v,
])
fn draw_line(a: Vec3, b: Vec3) -> Line {
let dir = (b - a).normalize();
Line::Strip(vec![Ray { pos: a, dir }, Ray { pos: b, dir }])
}
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 {
@ -38,7 +55,10 @@ fn draw_ellipse(center: Vec3, u: Vec3, v: Vec3) -> Line {
(0..segments)
.map(|k| k as f32 * step)
.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(),
)
}
@ -107,7 +127,7 @@ fn draw_ray_2(gc: &mut Vec<Line>, space: &Space, camera: Location, dir: Vec3) {
let end_pos = *pts
.last()
.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);
gc.push(Line::Strip(pts));
}
@ -135,7 +155,7 @@ impl Renderable for Tube {
.map(|k| k as f32 * step)
.map(Vec2::from_angle)
.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])
.map(|(r, l)| draw_ellipse(l, vec3(r, 0., 0.), vec3(0., 0., r)));
chain!(along, caps).collect()

View File

@ -1,5 +1,4 @@
use crate::types::{Hit, Location, Ray};
use glam::Vec3;
pub trait Traceable {
/// 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 points: Vec<Vec3>,
pub end_dir: Vec3,
pub points: Vec<Ray>,
}
pub trait DebugTraceable: Traceable {

View File

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