loam_render/raymarch/scene/
primitive.rs1use glam::Vec3;
5use loam_math::{Space, WgslSpace};
6use loam_shape::Shape;
7
8use super::literal::wgsl_f32;
9use super::SENTINEL_DISTANCE;
10
11pub trait Primitive {
12 fn to_wgsl<S: WgslSpace>(&self, space: &S, name: &str) -> String;
13
14 fn eval<S: Space<Point = Vec3, Vector = Vec3>>(&self, space: &S, p: Vec3) -> f32;
15}
16
17impl Primitive for Shape {
18 fn to_wgsl<S: WgslSpace>(&self, space: &S, name: &str) -> String {
19 match self {
20 Shape::Sphere { center, radius } => format!(
21 "fn {name}(p: vec3<f32>) -> f32 {{\n\
22 \treturn loam_distance(p, vec3<f32>({cx}, {cy}, {cz})) - ({r});\n\
23 }}\n",
24 name = name,
25 cx = wgsl_f32(center.x),
26 cy = wgsl_f32(center.y),
27 cz = wgsl_f32(center.z),
28 r = wgsl_f32(*radius),
29 ),
30 Shape::Box3 { half_extents } => format!(
31 "fn {name}(p: vec3<f32>) -> f32 {{\n\
32 \tlet b = vec3<f32>({hx}, {hy}, {hz});\n\
33 \tlet q = abs(p) - b;\n\
34 \treturn length(max(q, vec3<f32>(0.0))) + min(max(q.x, max(q.y, q.z)), 0.0);\n\
35 }}\n",
36 name = name,
37 hx = wgsl_f32(half_extents.x),
38 hy = wgsl_f32(half_extents.y),
39 hz = wgsl_f32(half_extents.z),
40 ),
41 Shape::HalfSpace { normal, offset } if space.is_chart_flat() => format!(
42 "fn {name}(p: vec3<f32>) -> f32 {{\n\
43 \treturn dot(p, vec3<f32>({nx}, {ny}, {nz})) - ({d});\n\
44 }}\n",
45 name = name,
46 nx = wgsl_f32(normal.x),
47 ny = wgsl_f32(normal.y),
48 nz = wgsl_f32(normal.z),
49 d = wgsl_f32(*offset),
50 ),
51 Shape::HalfSpace { .. }
52 | Shape::HalfSpace4D { .. }
53 | Shape::Polygon2D { .. }
54 | Shape::ConvexPolytope3D { .. }
55 | Shape::ConvexPolytope4D { .. }
56 | Shape::HyperSphere4D { .. } => {
57 format!("fn {name}(_p: vec3<f32>) -> f32 {{\n\treturn {SENTINEL_DISTANCE:e};\n}}\n",)
58 }
59 }
60 }
61
62 fn eval<S: Space<Point = Vec3, Vector = Vec3>>(&self, space: &S, p: Vec3) -> f32 {
63 match self {
64 Shape::Sphere { center, radius } => space.distance(p, *center) - *radius,
65
66 Shape::Box3 { half_extents } => {
67 let q = p.abs() - *half_extents;
68 q.max(Vec3::ZERO).length() + q.x.max(q.y.max(q.z)).min(0.0)
69 }
70
71 Shape::HalfSpace { normal, offset } if space.is_chart_flat() => {
72 p.dot(*normal) - *offset
73 }
74
75 Shape::HalfSpace { .. }
76 | Shape::HalfSpace4D { .. }
77 | Shape::Polygon2D { .. }
78 | Shape::ConvexPolytope3D { .. }
79 | Shape::ConvexPolytope4D { .. }
80 | Shape::HyperSphere4D { .. } => SENTINEL_DISTANCE,
81 }
82 }
83}