Skip to main content

xgl/
vertices.rs

1// Copyright (C) 2025-2026 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: (Apache-2.0 OR MIT)
3
4use std::fmt::Display;
5use std::fmt::Formatter;
6use std::fmt::Result as FmtResult;
7use std::marker::PhantomData;
8use std::mem::offset_of;
9
10use anyhow::Context as _;
11use anyhow::Result;
12
13use crate::sys;
14use crate::sys::Gl as _;
15
16
17#[derive(Debug, Eq, PartialEq)]
18pub enum AttribType {
19  Position,
20  Normal,
21  Texture,
22  Color,
23}
24
25impl Display for AttribType {
26  fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
27    let ty = match self {
28      Self::Position => "position",
29      Self::Normal => "normal",
30      Self::Texture => "texture",
31      Self::Color => "color",
32    };
33    f.write_str(ty)
34  }
35}
36
37
38#[derive(Debug)]
39pub struct Attrib {
40  pub size: i32,
41  pub type_: sys::Type,
42  pub normalize: bool,
43  pub stride: i32,
44  pub offset: i32,
45}
46
47pub trait Attribs {
48  const ATTRIBS: &'static [(AttribType, Attrib)];
49}
50
51
52/// A vertex with only position information.
53#[derive(Debug, Default)]
54#[repr(C)]
55pub struct VertexP3f {
56  pub x: f32,
57  pub y: f32,
58  pub z: f32,
59}
60
61impl Attribs for VertexP3f {
62  const ATTRIBS: &'static [(AttribType, Attrib)] = &[(
63    AttribType::Position,
64    Attrib {
65      size: 3,
66      type_: sys::Type::Float,
67      normalize: false,
68      stride: size_of::<Self>() as _,
69      offset: 0,
70    },
71  )];
72}
73
74
75/// A vertex with position and texture information.
76#[derive(Debug, Default)]
77#[repr(C)]
78pub struct VertexP3fT2f {
79  // Position
80  pub x: f32,
81  pub y: f32,
82  pub z: f32,
83  // Texture
84  pub u: f32,
85  pub v: f32,
86}
87
88impl Attribs for VertexP3fT2f {
89  const ATTRIBS: &'static [(AttribType, Attrib)] = &[
90    (
91      AttribType::Position,
92      Attrib {
93        size: 3,
94        type_: sys::Type::Float,
95        normalize: false,
96        stride: size_of::<Self>() as _,
97        offset: 0,
98      },
99    ),
100    (
101      AttribType::Texture,
102      Attrib {
103        size: 2,
104        type_: sys::Type::Float,
105        normalize: false,
106        stride: size_of::<Self>() as _,
107        offset: offset_of!(Self, u) as _,
108      },
109    ),
110  ];
111}
112
113
114/// A vertex with position and normal information.
115#[derive(Debug, Default)]
116#[repr(C)]
117pub struct VertexP3fN3f {
118  // Position
119  pub x: f32,
120  pub y: f32,
121  pub z: f32,
122  // Normal
123  pub nx: f32,
124  pub ny: f32,
125  pub nz: f32,
126}
127
128impl Attribs for VertexP3fN3f {
129  const ATTRIBS: &'static [(AttribType, Attrib)] = &[
130    (
131      AttribType::Position,
132      Attrib {
133        size: 3,
134        type_: sys::Type::Float,
135        normalize: false,
136        stride: size_of::<Self>() as _,
137        offset: 0,
138      },
139    ),
140    (
141      AttribType::Normal,
142      Attrib {
143        size: 3,
144        type_: sys::Type::Float,
145        normalize: false,
146        stride: size_of::<Self>() as _,
147        offset: offset_of!(Self, nx) as _,
148      },
149    ),
150  ];
151}
152
153
154/// A vertex with position, texture, and normal information.
155#[derive(Debug, Default)]
156#[repr(C)]
157pub struct VertexP3fT2fN3f {
158  // Position
159  pub x: f32,
160  pub y: f32,
161  pub z: f32,
162  // Texture
163  pub u: f32,
164  pub v: f32,
165  // Normal
166  pub nx: f32,
167  pub ny: f32,
168  pub nz: f32,
169}
170
171impl Attribs for VertexP3fT2fN3f {
172  const ATTRIBS: &'static [(AttribType, Attrib)] = &[
173    (
174      AttribType::Position,
175      Attrib {
176        size: 3,
177        type_: sys::Type::Float,
178        normalize: false,
179        stride: size_of::<Self>() as _,
180        offset: 0,
181      },
182    ),
183    (
184      AttribType::Texture,
185      Attrib {
186        size: 2,
187        type_: sys::Type::Float,
188        normalize: false,
189        stride: size_of::<Self>() as _,
190        offset: offset_of!(Self, u) as _,
191      },
192    ),
193    (
194      AttribType::Normal,
195      Attrib {
196        size: 3,
197        type_: sys::Type::Float,
198        normalize: false,
199        stride: size_of::<Self>() as _,
200        offset: offset_of!(Self, nx) as _,
201      },
202    ),
203  ];
204}
205
206
207/// A vertex buffer object.
208#[derive(Debug)]
209pub struct VertexBuffer<T> {
210  /// The GL context.
211  context: sys::Context,
212  /// The vertex buffer object.
213  vbo: sys::VertexBufferObject,
214  /// The "target" to bind to.
215  target: sys::VertexBufferTarget,
216  /// The number of items in the buffer.
217  count: usize,
218  /// Phantom data using `T`.
219  _phantom: PhantomData<T>,
220}
221
222impl<T> VertexBuffer<T>
223where
224  T: Sized,
225{
226  #[inline]
227  pub fn from_vertices(
228    vertices: &[T],
229    usage: sys::VertexBufferUsage,
230    context: &sys::Context,
231  ) -> Result<Self> {
232    Self::from_data(sys::VertexBufferTarget::Array, usage, vertices, context)
233  }
234
235  #[inline]
236  pub fn from_indices(
237    indices: &[T],
238    usage: sys::VertexBufferUsage,
239    context: &sys::Context,
240  ) -> Result<Self> {
241    Self::from_data(
242      sys::VertexBufferTarget::ElementArray,
243      usage,
244      indices,
245      context,
246    )
247  }
248
249  fn from_data(
250    target: sys::VertexBufferTarget,
251    usage: sys::VertexBufferUsage,
252    data: &[T],
253    context: &sys::Context,
254  ) -> Result<Self> {
255    let vbo = context
256      .create_vertex_buffer()
257      .context("failed to create vertex buffer")?;
258
259    let slf = Self {
260      context: context.clone(),
261      vbo,
262      target,
263      count: data.len(),
264      _phantom: PhantomData,
265    };
266    let () = slf.bind();
267    let () = context.set_vertex_buffer_data(target, usage, data);
268    let () = slf.unbind();
269    Ok(slf)
270  }
271
272  /// Update part of the buffer's contents with new data.
273  pub fn update(&self, data: &[T], offset: usize) {
274    debug_assert!(
275      offset + data.len() <= self.count,
276      "{offset} | {} | {}",
277      data.len(),
278      self.count
279    );
280
281    let () = self.bind();
282    let () =
283      self
284        .context
285        .set_vertex_buffer_sub_data(self.target, data, i32::try_from(offset).unwrap());
286    let () = self.unbind();
287  }
288
289  #[inline]
290  pub fn bind(&self) {
291    let () = self
292      .context
293      .bind_vertex_buffer(self.target, Some(&self.vbo));
294  }
295
296  #[inline]
297  pub fn unbind(&self) {
298    let () = self.context.bind_vertex_buffer(self.target, None);
299  }
300
301  #[inline]
302  pub fn item_count(&self) -> usize {
303    self.count
304  }
305}
306
307impl<T> Drop for VertexBuffer<T> {
308  #[inline]
309  fn drop(&mut self) {
310    let () = self.context.delete_vertex_buffer(&self.vbo);
311  }
312}
313
314
315/// A vertex array object.
316#[derive(Debug)]
317pub struct VertexArray {
318  /// The GL context.
319  context: sys::Context,
320  /// The vertex array object.
321  vao: sys::VertexArrayObject,
322}
323
324impl VertexArray {
325  pub fn new<V>(
326    vertex_buffer: &VertexBuffer<V>,
327    attrib_indices: &[(u32, AttribType)],
328    context: &sys::Context,
329  ) -> Result<Self>
330  where
331    V: Attribs,
332  {
333    let slf = Self {
334      context: context.clone(),
335      vao: context.create_vertex_array()?,
336    };
337    let () = slf.bind();
338    let () = vertex_buffer.bind();
339
340    let result = V::ATTRIBS.iter().try_for_each(|(attrib_type, attrib)| {
341      let (idx, _) = attrib_indices
342        .iter()
343        .find(|(_, ty)| ty == attrib_type)
344        .with_context(|| format!("failed to find {attrib_type} vertex attribute index"))?;
345
346      let () = context.enable_vertex_attrib_array(*idx);
347      let () = context.set_vertex_attrib_pointer(
348        *idx,
349        attrib.size,
350        attrib.type_,
351        attrib.normalize,
352        attrib.stride,
353        attrib.offset,
354      );
355      Ok(())
356    });
357
358    let () = vertex_buffer.unbind();
359    let () = slf.unbind();
360
361    result.map(|()| slf)
362  }
363
364  /// Create an empty vertex array.
365  pub fn empty(context: &sys::Context) -> Result<Self> {
366    let slf = Self {
367      vao: context.create_vertex_array()?,
368      context: context.clone(),
369    };
370
371    Ok(slf)
372  }
373
374  #[inline]
375  pub fn bind(&self) {
376    let () = self.context.bind_vertex_array(Some(&self.vao));
377  }
378
379  #[inline]
380  fn unbind(&self) {
381    let () = self.context.bind_vertex_array(None);
382  }
383}
384
385impl Drop for VertexArray {
386  #[inline]
387  fn drop(&mut self) {
388    let () = self.context.delete_vertex_array(&self.vao);
389  }
390}