1use common::{
2 figure::Segment,
3 util::{linear_to_srgba, srgb_to_linear_fast},
4 vol::{FilledVox, IntoFullVolIterator, ReadVol, SizedVol},
5};
6use euc::{Pipeline, buffer::Buffer2d, rasterizer};
7use image::{DynamicImage, RgbaImage};
8use vek::*;
9
10#[derive(Copy, Clone)]
11pub enum SampleStrat {
12 None,
13 SuperSampling(u8),
14 PixelCoverage,
15}
16
17#[derive(Clone, Copy)]
18pub struct Transform {
19 pub ori: Quaternion<f32>,
20 pub offset: Vec3<f32>,
21 pub zoom: f32,
22 pub orth: bool,
23 pub stretch: bool,
24}
25impl Default for Transform {
26 fn default() -> Self {
27 Self {
28 ori: Quaternion::identity(),
29 offset: Vec3::zero(),
30 zoom: 1.0,
31 orth: true,
32 stretch: true,
33 }
34 }
35}
36
37struct Voxel {
38 mvp: Mat4<f32>,
39 light_dir: Vec3<f32>,
40}
41
42#[derive(Copy, Clone)]
44struct Vert {
45 pos: Vec3<f32>,
46 col: Rgb<f32>,
47 norm: Vec3<f32>,
48 ao_level: u8,
49}
50impl Vert {
51 fn new(pos: Vec3<f32>, col: Rgb<f32>, norm: Vec3<f32>, ao_level: u8) -> Self {
52 Vert {
53 pos,
54 col,
55 norm,
56 ao_level,
57 }
58 }
59}
60
61#[derive(Clone, Copy)]
62struct VsOut(Rgba<f32>);
63impl euc::Interpolate for VsOut {
64 #[inline(always)]
65 fn lerp2(a: Self, b: Self, x: f32, y: f32) -> Self {
66 Self(a.0.map2(b.0, |a, b| a.mul_add(x, b * y)))
68 }
69
70 #[inline(always)]
71 fn lerp3(a: Self, b: Self, c: Self, x: f32, y: f32, z: f32) -> Self {
72 Self(
74 a.0.map2(b.0.map2(c.0, |b, c| b.mul_add(y, c * z)), |a, bc| {
75 a.mul_add(x, bc)
76 }),
77 )
78 }
79}
80
81impl Pipeline for Voxel {
82 type Pixel = [u8; 4];
83 type Vertex = Vert;
84 type VsOut = VsOut;
85
86 #[inline(always)]
87 fn vert(
88 &self,
89 Vert {
90 pos,
91 col,
92 norm,
93 ao_level,
94 }: &Self::Vertex,
95 ) -> ([f32; 4], Self::VsOut) {
96 let ambiance = 0.25;
97 let diffuse = norm.dot(-self.light_dir).max(0.0);
98 let brightness = 2.5;
99 let light = Rgb::from(*ao_level as f32 / 4.0) * (diffuse + ambiance) * brightness;
100 let color = light * srgb_to_linear_fast(*col);
101 let position = (self.mvp * Vec4::from_point(*pos)).into_array();
102 (position, VsOut(Rgba::from_opaque(color)))
103 }
104
105 #[inline(always)]
106 fn frag(&self, color: &Self::VsOut) -> Self::Pixel {
107 linear_to_srgba(color.0)
108 .map(|e| (e * 255.0) as u8)
109 .into_array()
110 }
111}
112
113pub fn draw_vox(
114 segment: &Segment,
115 output_size: Vec2<u16>,
116 transform: Transform,
117 sample_strat: SampleStrat,
118) -> RgbaImage {
119 let output_size = output_size.map(|e| e as usize);
120 debug_assert!(output_size.map(|e| e != 0).reduce_and());
121
122 let ori_mat = Mat4::from(transform.ori);
123 let rotated_segment_dims = (ori_mat * Vec4::from_direction(segment.size().map(|e| e as f32)))
124 .xyz()
125 .map(|e| e.abs());
126
127 let dims = match sample_strat {
128 SampleStrat::None => output_size,
129 SampleStrat::SuperSampling(min_samples) => {
130 output_size * (min_samples as f32).sqrt().ceil() as usize
131 },
132 SampleStrat::PixelCoverage => Vec2::new(
138 rotated_segment_dims.x.round() as usize,
139 rotated_segment_dims.y.round() as usize,
140 ),
141 }
142 .into_array();
143
144 debug_assert!(dims[0] != 0 && dims[1] != 0);
145
146 let mut color = Buffer2d::new(dims, [0; 4]);
148 let mut depth = Buffer2d::new(dims, 1.0);
149
150 let (w, h, d) = segment.size().map(|e| e as f32).into_tuple();
151
152 let mvp = if transform.orth {
153 Mat4::<f32>::orthographic_rh_no(FrustumPlanes {
154 left: -1.0,
155 right: 1.0,
156 bottom: -1.0,
157 top: 1.0,
158 near: 0.0,
159 far: 1.0,
160 })
161 } else {
162 Mat4::<f32>::perspective_fov_rh_no(
163 1.1, dims[0] as f32, dims[1] as f32, 0.0,
167 1.0,
168 )
169 } * Mat4::scaling_3d(
170 if transform.stretch {
172 rotated_segment_dims.map(|e| 2.0 / e)
173 } else {
174 let s = w.max(h).max(d);
175 Vec3::from(2.0 / s)
176 } * transform.zoom,
177 ) * Mat4::translation_3d(transform.offset)
178 * ori_mat
179 * Mat4::translation_3d([-w / 2.0, -h / 2.0, -d / 2.0]);
180
181 Voxel {
182 mvp,
183 light_dir: Vec3::broadcast(-1.0).normalized(),
184 }
185 .draw::<rasterizer::Triangles<_>, _>(
186 &generate_mesh(segment, Vec3::from(0.0)),
187 &mut color,
188 Some(&mut depth),
189 );
190
191 let rgba_img = RgbaImage::from_vec(
192 dims[0] as u32,
193 dims[1] as u32,
194 color
195 .as_ref()
196 .iter()
197 .flatten()
198 .copied()
199 .collect::<Vec<u8>>(),
200 )
201 .unwrap();
202
203 match sample_strat {
204 SampleStrat::None => rgba_img,
205 SampleStrat::SuperSampling(_) => DynamicImage::ImageRgba8(rgba_img)
206 .resize_exact(
207 output_size.x as u32,
208 output_size.y as u32,
209 image::imageops::FilterType::Triangle,
210 )
211 .to_rgba8(),
212 SampleStrat::PixelCoverage => super::pixel_art::resize_pixel_art(
213 &rgba_img,
214 output_size.x as u32,
215 output_size.y as u32,
216 ),
217 }
218}
219
220fn ao_level(side1: bool, corner: bool, side2: bool) -> u8 {
221 if side1 && side2 {
222 0
223 } else {
224 3 - [side1, corner, side2].iter().filter(|e| **e).count() as u8
225 }
226}
227fn create_quad(
229 origin: Vec3<f32>,
230 unit_x: Vec3<f32>,
231 unit_y: Vec3<f32>,
232 norm: Vec3<f32>,
233 col: Rgb<f32>,
234 occluders: [bool; 8],
235) -> [Vert; 6] {
236 let a_ao = ao_level(occluders[0], occluders[1], occluders[2]);
237 let b_ao = ao_level(occluders[2], occluders[3], occluders[4]);
238 let c_ao = ao_level(occluders[4], occluders[5], occluders[6]);
239 let d_ao = ao_level(occluders[6], occluders[7], occluders[0]);
240
241 let a = Vert::new(origin, col, norm, a_ao);
242 let b = Vert::new(origin + unit_x, col, norm, b_ao);
243 let c = Vert::new(origin + unit_x + unit_y, col, norm, c_ao);
244 let d = Vert::new(origin + unit_y, col, norm, d_ao);
245
246 let (a, b, c, d) = if a_ao + c_ao > b_ao + d_ao {
248 (d, a, b, c)
249 } else {
250 (a, b, c, d)
251 };
252
253 [
254 a, b, c, c, d, a, ]
257}
258
259fn generate_mesh(segment: &Segment, offs: Vec3<f32>) -> Vec<Vert> {
260 let mut vertices = Vec::new();
261
262 for (pos, vox) in segment.full_vol_iter() {
263 if let Some(col) = vox.get_color() {
264 let col = col.map(|e| e as f32 / 255.0);
265
266 let is_filled = |pos| segment.get(pos).map(|v| v.is_filled()).unwrap_or(false);
267
268 let occluders = |unit_x, unit_y, dir| {
269 [
271 is_filled(pos + dir - unit_x),
272 is_filled(pos + dir - unit_x - unit_y),
273 is_filled(pos + dir - unit_y),
274 is_filled(pos + dir + unit_x - unit_y),
275 is_filled(pos + dir + unit_x),
276 is_filled(pos + dir + unit_x + unit_y),
277 is_filled(pos + dir + unit_y),
278 is_filled(pos + dir - unit_x + unit_y),
279 ]
280 };
281
282 if !is_filled(pos - Vec3::unit_x()) {
284 vertices.extend_from_slice(&create_quad(
285 offs + pos.map(|e| e as f32) + Vec3::unit_y(),
286 -Vec3::unit_y(),
287 Vec3::unit_z(),
288 -Vec3::unit_x(),
289 col,
290 occluders(-Vec3::unit_y(), Vec3::unit_z(), -Vec3::unit_x()),
291 ));
292 }
293 if !is_filled(pos + Vec3::unit_x()) {
295 vertices.extend_from_slice(&create_quad(
296 offs + pos.map(|e| e as f32) + Vec3::unit_x(),
297 Vec3::unit_y(),
298 Vec3::unit_z(),
299 Vec3::unit_x(),
300 col,
301 occluders(Vec3::unit_y(), Vec3::unit_z(), Vec3::unit_x()),
302 ));
303 }
304 if !is_filled(pos - Vec3::unit_y()) {
306 vertices.extend_from_slice(&create_quad(
307 offs + pos.map(|e| e as f32),
308 Vec3::unit_x(),
309 Vec3::unit_z(),
310 -Vec3::unit_y(),
311 col,
312 occluders(Vec3::unit_x(), Vec3::unit_z(), -Vec3::unit_y()),
313 ));
314 }
315 if !is_filled(pos + Vec3::unit_y()) {
317 vertices.extend_from_slice(&create_quad(
318 offs + pos.map(|e| e as f32) + Vec3::unit_y(),
319 Vec3::unit_z(),
320 Vec3::unit_x(),
321 Vec3::unit_y(),
322 col,
323 occluders(Vec3::unit_z(), Vec3::unit_x(), Vec3::unit_y()),
324 ));
325 }
326 if !is_filled(pos - Vec3::unit_z()) {
328 vertices.extend_from_slice(&create_quad(
329 offs + pos.map(|e| e as f32),
330 Vec3::unit_y(),
331 Vec3::unit_x(),
332 -Vec3::unit_z(),
333 col,
334 occluders(Vec3::unit_y(), Vec3::unit_x(), -Vec3::unit_z()),
335 ));
336 }
337 if !is_filled(pos + Vec3::unit_z()) {
339 vertices.extend_from_slice(&create_quad(
340 offs + pos.map(|e| e as f32) + Vec3::unit_z(),
341 Vec3::unit_x(),
342 Vec3::unit_y(),
343 Vec3::unit_z(),
344 col,
345 occluders(Vec3::unit_x(), Vec3::unit_y(), Vec3::unit_z()),
346 ));
347 }
348 }
349 }
350
351 vertices
352}