wgpu_app/
vt.rs

1//! vt.rs
2//!
3//! Vertex index texture FaceInf VIP YRP CameraAngle WG
4//!
5
6use std::f32::consts;
7use std::error::Error;
8use image;
9use bytemuck;
10use glam;
11use wgpu;
12
13/// Vertex must be 4 * (4 4 4 2 1)
14#[repr(C)]
15#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
16pub struct Vertex {
17  /// pos (x, y, z, 1.0)
18  pub pos: [f32; 4],
19  /// norm (x, y, z, 1.0)
20  pub norm: [f32; 4],
21  /// col (r, g, b, x)
22  pub col: [u32; 4],
23  /// tex (u, v)
24  pub tex_coord: [f32; 2],
25  /// ix
26  pub ix: u32
27}
28
29/// construct Vertex from i8 i8
30pub fn vtx(pos: [i8; 3], tc: [i8; 2]) -> Vertex {
31  Vertex{
32    pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
33    norm: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
34    col: (0..4).map(|i| if i < 3 { (pos[i] + 2) as u32 * 255 / 4 } else { 255 }
35      ).collect::<Vec<_>>().try_into().unwrap(),
36    tex_coord: [tc[0] as f32, tc[1] as f32],
37    ix: 0
38  }
39}
40
41/// construct Vertex from i8 f32
42pub fn vtf(pos: [i8; 3], tc: [f32; 2]) -> Vertex {
43  Vertex{
44    pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
45    norm: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
46    col: (0..4).map(|i| if i < 3 { (pos[i] + 2) as u32 * 255 / 4 } else { 255 }
47      ).collect::<Vec<_>>().try_into().unwrap(),
48    tex_coord: tc,
49    ix: 0
50  }
51}
52
53/// FaceInf
54#[derive(Debug)]
55pub struct FaceInf {
56  /// number of faces
57  pub nfaces: u64,
58  /// index list [u64; n + 1]
59  pub il: Vec<u64>,
60  /// texture function (i, param, max_i) 0 &le; i &lt; max_i
61  pub tf: fn ((usize, usize, usize)) -> usize
62}
63
64/// Vertex Index Param
65#[derive(Debug)]
66pub struct VIP {
67  /// vertex series
68  pub vs: wgpu::Buffer,
69  /// index series
70  pub is: wgpu::Buffer,
71  /// param FaceInf
72  pub p: FaceInf
73}
74
75/// set location and scale to VIP
76pub fn locscale(o: &[f32; 3], scale: f32, vi: (Vec<Vertex>, Vec<u16>, FaceInf))
77  -> (Vec<Vertex>, Vec<u16>, FaceInf) {
78  (
79    vi.0.into_iter().enumerate().map(
80      |(j, Vertex{pos: p, col: c, norm: n, tex_coord: tc, ix: _})| {
81      Vertex{
82        pos: p.iter().enumerate().map(|(i, &v)| {
83          if i < 3 { o[i] + v * scale } else { v } // expect else v always 1.0
84        }).collect::<Vec<_>>().try_into().unwrap(),
85        col: c,
86        norm: n,
87        tex_coord: tc,
88        ix: j as u32
89      }
90    }).collect(),
91    vi.1,
92    vi.2
93  )
94}
95
96/// construct VIP (cube 6 textures)
97pub fn create_vertices_cube_6_textures(
98  tf: fn ((usize, usize, usize)) -> usize)
99  -> (Vec<Vertex>, Vec<u16>, FaceInf) {
100  let vertex_data = [ // FrontFace::Ccw culling Face::Back
101    // +X (1, 0, 0) right
102    vtx([1, -1, 1], [0, 1]),
103    vtx([1, -1, -1], [1, 1]),
104    vtx([1, 1, -1], [1, 0]),
105    vtx([1, 1, 1], [0, 0]),
106    // -X (-1, 0, 0) left
107    vtx([-1, -1, 1], [0, 1]),
108    vtx([-1, 1, 1], [1, 1]),
109    vtx([-1, 1, -1], [1, 0]),
110    vtx([-1, -1, -1], [0, 0]),
111    // +Y (0, 1, 0) back
112    vtx([1, 1, -1], [0, 1]),
113    vtx([-1, 1, -1], [1, 1]),
114    vtx([-1, 1, 1], [1, 0]),
115    vtx([1, 1, 1], [0, 0]),
116    // -Y (0, -1, 0) front
117    vtx([1, -1, -1], [0, 1]),
118    vtx([1, -1, 1], [1, 1]),
119    vtx([-1, -1, 1], [1, 0]),
120    vtx([-1, -1, -1], [0, 0]),
121    // +Z (0, 0, 1) top
122    vtx([-1, 1, 1], [0, 1]),
123    vtx([-1, -1, 1], [1, 1]),
124    vtx([1, -1, 1], [1, 0]),
125    vtx([1, 1, 1], [0, 0]),
126
127// extra for test
128#[cfg(interrupt_vertex)] vtx([0, 2, 0], [0, 1]),
129#[cfg(interrupt_vertex)] vtx([2, 2, 0], [1, 1]),
130#[cfg(interrupt_vertex)] vtx([2, 0, 0], [1, 0]),
131#[cfg(interrupt_vertex)] vtx([0, 0, 0], [0, 0]),
132
133    // -Z (0, 0, -1) bottom
134    vtx([-1, 1, -1], [0, 1]),
135    vtx([1, 1, -1], [1, 1]),
136    vtx([1, -1, -1], [1, 0]),
137    vtx([-1, -1, -1], [0, 0])
138  ];
139
140  let index_data: &[u16] = &[
141    0, 1, 3, 2, 3, 1, // +X right
142    4, 5, 7, 6, 7, 5, // -X left
143    8, 9, 11, 10, 11, 9, // +Y back
144    12, 13, 15, 14, 15, 13, // -Y front
145    16, 17, 19, 18, 19, 17, // +Z top
146
147// extra for test
148#[cfg(interrupt_vertex)] 24,
149#[cfg(interrupt_vertex)] 25,
150#[cfg(interrupt_vertex)] 27,
151#[cfg(interrupt_vertex)] 26,
152#[cfg(interrupt_vertex)] 27,
153#[cfg(interrupt_vertex)] 25,
154
155    20, 21, 23, 22, 23, 21 // -Z bottom
156  ];
157
158  let nfaces = index_data.len() as u64 / 6;
159  (vertex_data.to_vec(), index_data.to_vec(), FaceInf{
160    nfaces,
161    il: (0..=nfaces).map(|i| i * 6).collect(),
162    tf: tf}) // |(i, _, _)| {i} (default)
163}
164
165/// construct VIP (cube expansion plan)
166pub fn create_vertices_cube_expansion_plan(
167  tf: fn ((usize, usize, usize)) -> usize)
168  -> (Vec<Vertex>, Vec<u16>, FaceInf) {
169  let vertex_data = [ // FrontFace::Ccw culling Face::Back
170    // +X (1, 0, 0) right
171    vtf([1, -1, 1], [0.25, 0.0]),
172    vtf([1, -1, -1], [0.25, 0.25]),
173    vtf([1, 1, -1], [0.5, 0.25]),
174    vtf([1, 1, 1], [0.5, 0.0]),
175    // -X (-1, 0, 0) left
176    vtf([-1, -1, 1], [0.25, 0.75]),
177    vtf([-1, 1, 1], [0.5, 0.75]),
178    vtf([-1, 1, -1], [0.5, 0.5]),
179    vtf([-1, -1, -1], [0.25, 0.5]),
180    // +Y (0, 1, 0) back
181    vtf([1, 1, -1], [0.5, 0.25]),
182    vtf([-1, 1, -1], [0.75, 0.25]),
183    vtf([-1, 1, 1], [0.75, 0.0]),
184    vtf([1, 1, 1], [0.5, 0.0]),
185    // -Y (0, -1, 0) front
186    vtf([1, -1, -1], [0.0, 0.5]),
187    vtf([1, -1, 1], [0.0, 0.75]),
188    vtf([-1, -1, 1], [0.25, 0.75]),
189    vtf([-1, -1, -1], [0.25, 0.5]),
190    // +Z (0, 0, 1) top
191    vtf([-1, 1, 1], [0.5, 0.75]),
192    vtf([-1, -1, 1], [0.25, 0.75]),
193    vtf([1, -1, 1], [0.25, 1.0]),
194    vtf([1, 1, 1], [0.5, 1.0]),
195    // -Z (0, 0, -1) bottom
196    vtf([-1, 1, -1], [0.5, 0.5]),
197    vtf([1, 1, -1], [0.5, 0.25]),
198    vtf([1, -1, -1], [0.25, 0.25]),
199    vtf([-1, -1, -1], [0.25, 0.5])
200  ];
201
202  let index_data: &[u16] = &[
203    0, 1, 3, 2, 3, 1, // +X right
204    4, 5, 7, 6, 7, 5, // -X left
205    8, 9, 11, 10, 11, 9, // +Y back
206    12, 13, 15, 14, 15, 13, // -Y front
207    16, 17, 19, 18, 19, 17, // +Z top
208    20, 21, 23, 22, 23, 21 // -Z bottom
209  ];
210
211  let nfaces = index_data.len() as u64 / 6;
212  (vertex_data.to_vec(), index_data.to_vec(), FaceInf{
213    nfaces,
214    il: (0..=nfaces).map(|i| i * 6).collect(),
215    tf: tf}) // |_| {N} (expansion plan texture number)
216}
217
218/// u8 Vec from image file
219pub fn load_texels(fname: &str)
220  -> Result<((u32, u32, u32, u32), Vec<u8>), Box<dyn Error>> {
221  let im = image::open(fname)?; // bpr may be 3b align to 4b
222  let (h, w) = (im.height(), im.width());
223/*
224  print!("load [{}] ", fname);
225  if let Some(ref img) = im.as_rgb8() {
226    println!("may be rgb {} convert", img.as_raw().len()); // as_raw() &Vec<u8>
227  } else {
228    println!("may be rgba through");
229  }
230*/
231  let buf = im.into_rgba8().to_vec(); // convert to 4b
232  let bytes_per_row = buf.len() as u32 / h; // bpr always align to 4b
233  let d = bytes_per_row / w; // expect d is always 4
234  println!("{} {} {} {} {}", h, w, d, bytes_per_row, buf.len());
235  Ok(((h, w, d, bytes_per_row), buf))
236}
237
238/// u8 Vec rgba colors
239pub fn create_texels_rgba(size: usize, cols4u: &Vec<&[u8; 4]>)
240  -> ((u32, u32, u32, u32), Vec<u8>) {
241  // 0u8 black 255u8 max rgba (tex{x, y, z, w} in the shader)
242  // bytes_per_row=Some(4 * size) TextureFormat=Rgba8Uint when rgba
243  let mut hwd = (size as u32, size as u32, 4, 0);
244  hwd.3 = hwd.2 * hwd.1;
245  (hwd, (0..(hwd.3 * hwd.0) as usize).map(|id| {
246    let bpr = 256 * 4;
247    let v = id / bpr;
248    let w = id % bpr;
249    let u = w / 4;
250    (if v < 128 {
251      if u < 128 { cols4u[0] } else { cols4u[1] }
252    } else {
253      if u < 128 { cols4u[2] } else { cols4u[3] }
254    })[w % 4]
255  }).collect())
256}
257
258/// u8 Vec mandelbrot
259pub fn create_texels_mandelbrot_4c(size: usize, col4f: &[f32; 4])
260  -> ((u32, u32, u32, u32), Vec<u8>) {
261  // 0u8 white 255u8 black (1.0 - (v * factor)) in the shader) x 4
262  // bytes_per_row=Some(4 * size) TextureFormat=Rgba8Uint when rgba
263  // bytes_per_row=Some(size) TextureFormat=R8Uint when gray
264  let mut hwd = (size as u32, size as u32, 4, 0);
265  hwd.3 = hwd.2 * hwd.1;
266  (hwd, (0..(hwd.3 * hwd.0) as usize).map(|id| { // rgba
267    let c = id / 4;
268    // get high five for recognizing this ;)
269    let cx = 3.0 * (c % size) as f32 / (size - 1) as f32 - 2.0;
270    let cy = 2.0 * (c / size) as f32 / (size - 1) as f32 - 1.0;
271    let (mut x, mut y, mut count) = (cx, cy, 0);
272    while count < 0xFF && x * x + y * y < 4.0 {
273      let old_x = x;
274      x = x * x - y * y + cx;
275      y = 2.0 * old_x * y + cy;
276      count += 1;
277    }
278    ((1.0 - (count as f32 / 255.0) * col4f[id % 4]) * 255.0) as u8
279  }).collect())
280}
281
282/// Yaw Roll Pitch
283#[derive(Debug)]
284pub struct YRP {
285  /// yaw Z axis
286  pub yaw: f32,
287  /// roll Y axis
288  pub roll: f32,
289  /// pitch X axis
290  pub pitch: f32,
291  /// tick
292  pub tick: u64
293}
294
295/// CameraAngle
296#[derive(Debug)]
297pub struct CameraAngle {
298  /// pos
299  pub pos: glam::Vec3,
300  /// lookat
301  pub lookat: glam::Vec3,
302  /// top
303  pub top: glam::Vec3
304}
305
306/// CameraAngle
307impl CameraAngle {
308  /// construct
309  pub fn new(pos: glam::Vec3, lookat: glam::Vec3, top: glam::Vec3) -> Self {
310    CameraAngle{pos, lookat, top}
311  }
312
313  /// construct
314  pub fn from_yrp(yrp: &YRP) -> Self {
315    log::warn!("y:{} r:{} p:{}", yrp.yaw, yrp.roll, yrp.pitch);
316    let cs = |t: f32| {let r = t * 3.14159 / 180.0; (r.cos(), r.sin())};
317    let (yc, ys) = cs(yrp.yaw);
318    let (rc, rs) = cs(yrp.roll);
319    let (pc, ps) = cs(yrp.pitch);
320    let (tc, _ts) = cs(yrp.tick as f32);
321    let radius = (tc + 2.0) * 2.0; // 2.0 <-> 6.0
322    let (radius_c, radius_s) = (radius * pc, radius * ps);
323    let pos = glam::Vec3::new(radius_c * yc, radius_c * ys, radius_s);
324    let lookat = glam::Vec3::ZERO;
325    let top = glam::Vec3::new(yc * rs, -ys * rs, rc); // or glam::Vec3::Z or -Z
326    log::warn!("{:5.2} {:5.2} {:5.2} {:5.2} {:5.2} {:5.2}",
327      pos.x, pos.y, pos.z, top.x, top.y, top.z);
328    CameraAngle{pos, lookat, top}
329  }
330
331  /// projection view matrix
332  pub fn generate_mvp(&self, aspect_ratio: f32) -> glam::Mat4 {
333    let projection = glam::Mat4::perspective_rh(
334      consts::FRAC_PI_4, aspect_ratio, 1.0, 10.0);
335    let view = glam::Mat4::look_at_rh(self.pos, self.lookat, self.top); // _lh
336    projection * view
337  }
338}
339
340/// TexSZ must impl AsRef&lt;[u32; 4]&gt;
341#[repr(C)]
342#[derive(Debug)]
343pub struct TexSZ {
344  /// w
345  pub w: u32,
346  /// h
347  pub h: u32,
348  /// ext x=mode: 0 square, 1 landscape, 2 portrait, 3 y=max: square
349  pub ext: [u32; 2]
350}
351
352/// TexSZ
353impl AsRef<[u32; 4]> for TexSZ {
354  /// as_ref &amp;[u32; 4]
355  fn as_ref(&self) -> &[u32; 4] {
356unsafe {
357    std::slice::from_raw_parts(&self.w as *const u32, 4).try_into().unwrap()
358}
359  }
360}
361
362/// TextureBindGroup (wgpu::BindGroup with texture size)
363#[derive(Debug)]
364pub struct TextureBindGroup {
365  /// wgpu::BindGroup
366  pub group: wgpu::BindGroup,
367  /// sz (always hold copy)
368  pub sz: TexSZ,
369  /// wgpu::Buffer for sz
370  pub buf: wgpu::Buffer
371}
372
373/// World of GL
374#[derive(Debug)]
375pub struct WG {
376  /// VIPs
377  pub vips: Vec<VIP>,
378  /// vector of TextureBindGroup
379  pub bind_group: Vec<TextureBindGroup>,
380  /// current bind group
381  pub bg: usize,
382  /// mvp (always hold copy)
383  pub mvp: glam::Mat4,
384  /// wgpu::Buffer for mvp
385  pub uniform_buf: wgpu::Buffer,
386  /// pipeline
387  pub pipeline: wgpu::RenderPipeline,
388  /// pipeline_wire
389  pub pipeline_wire: Option<wgpu::RenderPipeline>,
390  /// draw wire without(true) or with(false) texture
391  pub wire: bool
392}
393
394/// draw_vip
395macro_rules! draw_vip {
396  ($self: ident, $rp: ident, // self, wgpu::RenderPass,
397   $vbuf: ident, ($vs: expr, $ve: expr), // wgpu::Buffer, (u64, u64),
398   $ibuf: ident, ($is: expr, $ie: expr), // wgpu::Buffer, (u64, u64),
399   $tid: expr, $icnt: expr) => { // usize, u32
400    $rp.push_debug_group("Prepare data for draw.");
401    $rp.set_pipeline(&$self.pipeline);
402    $rp.set_bind_group(0, &$self.bind_group[$tid].group, &[]);
403    $rp.set_index_buffer($ibuf.slice($is..$ie), wgpu::IndexFormat::Uint16);
404    $rp.set_vertex_buffer(0, $vbuf.slice($vs..$ve));
405    $rp.pop_debug_group();
406    $rp.insert_debug_marker("Draw!");
407    if !&$self.wire {
408      $rp.draw_indexed(0..$icnt, 0, 0..1);
409    }
410    if let Some(ref pipe) = &$self.pipeline_wire {
411      $rp.set_pipeline(pipe);
412      $rp.draw_indexed(0..$icnt, 0, 0..1);
413    }
414  }
415}
416
417/// World of GL
418impl WG {
419  /// update matrix
420  pub fn update_matrix(
421    &mut self,
422    config: &wgpu::SurfaceConfiguration,
423    _device: &wgpu::Device,
424    queue: &wgpu::Queue,
425    yrp: &YRP) {
426    self.mvp = CameraAngle::from_yrp(yrp).generate_mvp(
427      config.width as f32 / config.height as f32);
428    queue.write_buffer(&self.uniform_buf, 0,
429      bytemuck::cast_slice(self.mvp.as_ref())); // &[f32; 16]
430  }
431
432  /// draw
433  pub fn draw(
434    &mut self,
435    view: &wgpu::TextureView,
436    device: &wgpu::Device,
437    queue: &wgpu::Queue) {
438    device.push_error_scope(wgpu::ErrorFilter::Validation);
439    let mut encoder = device.create_command_encoder(
440      &wgpu::CommandEncoderDescriptor{label: None});
441    let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor{
442      label: None,
443      color_attachments: &[Some(wgpu::RenderPassColorAttachment{
444        view,
445        resolve_target: None,
446        ops: wgpu::Operations{
447          load: wgpu::LoadOp::Clear(
448            wgpu::Color{r: 0.1, g: 0.2, b: 0.3, a: 1.0}),
449          store: true
450        }
451      })],
452      depth_stencil_attachment: None
453    });
454    for VIP{vs: vertex_buf, is: index_buf, p: fi} in &self.vips {
455/*
456      // draw faces of vip at a time by one texture
457      let isz = std::mem::size_of::<u16>() as u64; // index bytes
458      let ipv = index_buf.size() / isz; // indices per vip
459      let tid = (fi.tf)((0, self.bg, self.bind_group.len()));
460      draw_vip!(self, rpass,
461        vertex_buf, (0, vertex_buf.size()),
462        index_buf, (0, index_buf.size()),
463        tid, ipv as u32);
464*/
465/**/
466      // draw separate face for bind to each texture
467      for i in 0..fi.nfaces {
468        let isz = std::mem::size_of::<u16>() as u64; // index bytes
469//        let ipf = (index_buf.size() / isz) / fi.nfaces; // indices per face
470        let ip_s = fi.il[i as usize] * isz; // s * ipf * isz (start of face)
471        let ip_e = fi.il[i as usize + 1] * isz; // e * ipf * isz (end of face)
472        let ipf = (ip_e - ip_s) / isz; // indices of this face
473
474        let vsz = std::mem::size_of::<Vertex>() as u64; // vertex bytes
475        let vpf = (vertex_buf.size() / vsz) / fi.nfaces; // vertices per face
476        let (vp_s, vp_e) = (0, fi.nfaces * vpf * vsz); // all vertices
477
478        let tid = (fi.tf)((i as usize, self.bg, self.bind_group.len()));
479        draw_vip!(self, rpass,
480          vertex_buf, (vp_s, vp_e), // == (..)
481          index_buf, (ip_s, ip_e),
482          tid, ipf as u32);
483      }
484/**/
485    }
486    drop(rpass); // to release encoder
487    queue.submit(Some(encoder.finish()));
488  }
489}