solstice/
mesh.rs

1use super::{
2    buffer::{Buffer, BufferType, MappedBuffer, Usage},
3    vertex::{Vertex, VertexFormat},
4    Context,
5};
6
7// The lifetime of this slice is explicitly given because I'm not sure the compiler
8// can safely infer it in this unsafe function call. It might be fine but better safe
9// than sorry.
10fn to_bytes<'a, V>(data: &'a [V]) -> &'a [u8]
11where
12    V: Sized,
13{
14    unsafe {
15        std::slice::from_raw_parts::<'a, u8>(
16            data.as_ptr() as *const _,
17            data.len() * std::mem::size_of::<V>(),
18        )
19    }
20}
21
22fn from_bytes<'a, V>(data: &'a [u8]) -> &'a [V]
23where
24    V: Sized,
25{
26    unsafe {
27        std::slice::from_raw_parts::<'a, V>(
28            data.as_ptr() as *const _,
29            data.len() / std::mem::size_of::<V>(),
30        )
31    }
32}
33
34fn set_buffer<T>(buffer: &mut MappedBuffer, data: &[T], offset: usize)
35where
36    T: Sized,
37{
38    buffer.write(to_bytes(data), offset * std::mem::size_of::<T>());
39}
40
41fn get_buffer<T>(buffer: &MappedBuffer) -> &[T]
42where
43    T: Sized,
44{
45    from_bytes(buffer.memory_map())
46}
47
48pub type BindingInfo<'a> = (&'a VertexFormat, usize, u32, super::BufferKey, BufferType);
49
50/// The Mesh represents a set of vertices to be drawn by a shader program and how to draw them.
51/// A well-formed Vertex implementation will provide information to the mesh about it's layout in
52/// order to properly sync the data to the GPU. A derive macro exists in
53/// [`Vertex`](solstice_derive::Vertex) that will derive this implementation for you.
54///
55/// ```
56/// use solstice::vertex::{VertexFormat, AttributeType};
57/// #[repr(C)]
58/// #[derive(Copy, Clone, bytemuck::Zeroable, bytemuck::Pod)]
59/// struct TestVertex {
60///     position: [f32; 2],
61///     color: [f32; 4],
62/// }
63///
64/// impl solstice::vertex::Vertex for TestVertex {
65///     fn build_bindings() -> &'static [VertexFormat] {
66///         &[VertexFormat {
67///             name: "position",
68///             offset: 0,
69///             atype: AttributeType::F32F32,
70///             normalize: false,
71///         }, VertexFormat {
72///             name: "color",
73///             offset: std::mem::size_of::<[f32; 2]>(),
74///             atype: AttributeType::F32F32F32F32,
75///             normalize: false
76///         }]
77///     }
78/// }
79///
80/// let vertex_data = vec![
81///     TestVertex {
82///         position: [0.5, 0.],
83///         color: [1., 0., 0., 1.]
84///     },
85///     TestVertex {
86///         position: [0., 1.0],
87///         color: [0., 1., 0., 1.]
88///     },
89///     TestVertex {
90///         position: [1., 0.],
91///         color: [0., 0., 1., 1.]
92///     },
93/// ];
94/// ```
95///
96/// Vertex data is then copied into the mesh.
97///
98/// ```ignore
99/// let mut mesh = solstice::mesh::Mesh::new(&mut ctx, 3);
100/// mesh.set_vertices(&vertex_data, 0);
101/// ```
102///
103/// Once constructed, a Mesh is of an immutable size but the draw range can be modified to
104/// effectively change it's size without changing the underlying memory's size.
105///
106/// ```ignore
107/// let mut mesh = solstice::mesh::Mesh::new(&mut ctx, 3000).unwrap();
108/// mesh.set_draw_range(Some(0..3)); // draws only the first three vertices of the 3000 allocated
109/// ```
110#[derive(Debug, PartialEq, Clone)]
111pub struct VertexMesh<V> {
112    vbo: Buffer,
113    draw_range: Option<std::ops::Range<usize>>,
114    draw_mode: super::DrawMode,
115    type_marker: std::marker::PhantomData<V>,
116}
117
118impl<V> VertexMesh<V>
119where
120    V: Vertex,
121{
122    /// Construct a Mesh with a given number of vertices.
123    pub fn new(ctx: &mut Context, size: usize) -> Result<Self, super::GraphicsError> {
124        let vbo = Buffer::new(
125            ctx,
126            size * std::mem::size_of::<V>(),
127            BufferType::Vertex,
128            Usage::Dynamic,
129        )?;
130        Ok(Self::with_buffer(vbo))
131    }
132
133    pub fn with_data(ctx: &mut Context, vertices: &[V]) -> Result<Self, super::GraphicsError> {
134        let vbo = Buffer::with_data(ctx, to_bytes(vertices), BufferType::Vertex, Usage::Dynamic)?;
135        Ok(Self::with_buffer(vbo))
136    }
137
138    pub fn with_buffer(buffer: Buffer) -> Self {
139        Self {
140            vbo: buffer,
141            draw_range: None,
142            draw_mode: super::DrawMode::Triangles,
143            type_marker: std::marker::PhantomData,
144        }
145    }
146
147    /// Write new data into a range of the Mesh's vertex data.
148    pub fn set_vertices(&self, ctx: &mut super::Context, vertices: &[V], offset: usize) {
149        ctx.bind_buffer(self.vbo.handle(), self.vbo.buffer_type());
150        ctx.buffer_static_draw(
151            &self.vbo,
152            to_bytes(vertices),
153            offset * std::mem::size_of::<V>(),
154        )
155    }
156
157    /// Sets the range of vertices to be drawn. Passing `None` will draw the entire mesh.
158    pub fn set_draw_range(&mut self, draw_range: Option<std::ops::Range<usize>>) {
159        self.draw_range = draw_range;
160    }
161
162    /// Sets how the vertex data should be tesselated.
163    pub fn set_draw_mode(&mut self, draw_mode: super::DrawMode) {
164        self.draw_mode = draw_mode;
165    }
166
167    pub fn draw_range(&self) -> std::ops::Range<usize> {
168        self.draw_range.clone().unwrap_or(0..(self.len()))
169    }
170
171    pub fn draw_mode(&self) -> super::DrawMode {
172        self.draw_mode
173    }
174
175    pub fn len(&self) -> usize {
176        self.vbo.size() / std::mem::size_of::<V>()
177    }
178}
179
180#[derive(Debug, PartialEq)]
181pub struct MappedVertexMesh<V> {
182    inner: VertexMesh<V>,
183    memory_map: MappedBuffer,
184}
185
186impl<V> MappedVertexMesh<V>
187where
188    V: Vertex,
189{
190    pub fn new(ctx: &mut super::Context, size: usize) -> Result<Self, super::GraphicsError> {
191        let inner = VertexMesh::new(ctx, size)?;
192        let memory_map =
193            MappedBuffer::with_shape(inner.vbo.clone(), [size * std::mem::size_of::<V>()]);
194        Ok(Self { inner, memory_map })
195    }
196
197    pub fn set_vertices(&mut self, vertices: &[V], offset: usize) {
198        set_buffer(&mut self.memory_map, vertices, offset)
199    }
200
201    pub fn get_vertices(&self) -> &[V] {
202        get_buffer(&self.memory_map)
203    }
204
205    pub fn unmap(&mut self, ctx: &mut super::Context) -> &VertexMesh<V> {
206        self.memory_map.unmap(ctx);
207        self.inner()
208    }
209
210    pub fn inner(&self) -> &VertexMesh<V> {
211        &self.inner
212    }
213}
214
215/// A mesh with vertex data that is indexed with separate data.
216///
217/// This is useful if you have a number of vertices that you would otherwise have to duplicate
218/// because indices are generally smaller than a vertex so duplicating them is more performant.
219#[derive(Debug, PartialEq, Clone)]
220pub struct IndexedMesh<V, I> {
221    mesh: VertexMesh<V>,
222    ibo: Buffer,
223    type_marker: std::marker::PhantomData<I>,
224}
225
226impl<V, I> IndexedMesh<V, I>
227where
228    V: Vertex,
229    I: Index,
230{
231    /// Construct a mesh with a given number of vertices and indices.
232    pub fn new(
233        ctx: &mut Context,
234        vertex_count: usize,
235        index_count: usize,
236    ) -> Result<Self, super::GraphicsError> {
237        let ibo = Buffer::new(
238            ctx,
239            index_count * std::mem::size_of::<I>(),
240            BufferType::Index,
241            Usage::Dynamic,
242        )?;
243        let mesh = VertexMesh::new(ctx, vertex_count)?;
244        Ok(Self {
245            mesh,
246            ibo,
247            type_marker: std::marker::PhantomData,
248        })
249    }
250
251    pub fn with_data(
252        ctx: &mut Context,
253        vertices: &[V],
254        indices: &[I],
255    ) -> Result<Self, super::GraphicsError> {
256        let ibo = Buffer::with_data(ctx, to_bytes(indices), BufferType::Index, Usage::Dynamic)?;
257        let mesh = VertexMesh::with_data(ctx, vertices)?;
258        Ok(Self {
259            mesh,
260            ibo,
261            type_marker: std::marker::PhantomData,
262        })
263    }
264
265    /// Construct an indexed mesh from a non-indexed mesh.
266    pub fn with_mesh(
267        ctx: &mut Context,
268        mesh: VertexMesh<V>,
269        index_count: usize,
270    ) -> Result<Self, super::GraphicsError> {
271        let ibo = Buffer::new(
272            ctx,
273            index_count * std::mem::size_of::<I>(),
274            BufferType::Index,
275            mesh.vbo.usage(),
276        )?;
277        Ok(Self {
278            mesh,
279            ibo,
280            type_marker: std::marker::PhantomData,
281        })
282    }
283
284    /// Write new data into a range of the Mesh's vertex data.
285    pub fn set_vertices(&self, ctx: &mut Context, vertices: &[V], offset: usize) {
286        self.mesh.set_vertices(ctx, vertices, offset)
287    }
288
289    /// Write new data into a range of the Mesh's vertex data.
290    pub fn set_indices(&self, ctx: &mut Context, indices: &[I], offset: usize) {
291        ctx.bind_buffer(self.ibo.handle(), self.ibo.buffer_type());
292        ctx.buffer_static_draw(
293            &self.ibo,
294            to_bytes(indices),
295            offset * std::mem::size_of::<I>(),
296        )
297    }
298
299    pub fn set_draw_range(&mut self, draw_range: Option<std::ops::Range<usize>>) {
300        self.mesh.set_draw_range(draw_range)
301    }
302
303    pub fn draw_range(&self) -> std::ops::Range<usize> {
304        self.mesh
305            .draw_range
306            .clone()
307            .unwrap_or(0..(self.ibo.size() / std::mem::size_of::<I>()))
308    }
309
310    pub fn set_draw_mode(&mut self, draw_mode: super::DrawMode) {
311        self.mesh.set_draw_mode(draw_mode)
312    }
313
314    pub fn len(&self) -> usize {
315        self.ibo.size() / std::mem::size_of::<I>()
316    }
317}
318
319#[derive(Debug, PartialEq)]
320pub struct MappedIndexedMesh<V, I> {
321    inner: IndexedMesh<V, I>,
322    vbo: MappedBuffer,
323    ibo: MappedBuffer,
324}
325
326impl<V, I> MappedIndexedMesh<V, I>
327where
328    V: Vertex,
329    I: Index,
330{
331    pub fn new(
332        gl: &mut Context,
333        vertex_count: usize,
334        index_count: usize,
335    ) -> Result<Self, super::GraphicsError> {
336        let inner = IndexedMesh::new(gl, vertex_count, index_count)?;
337        let vbo = MappedBuffer::with_shape(inner.mesh.vbo.clone(), inner.mesh.vbo.size());
338        let ibo = MappedBuffer::with_shape(inner.ibo.clone(), inner.ibo.size());
339        Ok(Self { inner, vbo, ibo })
340    }
341
342    pub fn with_data(
343        ctx: &mut Context,
344        vertices: Vec<V>,
345        indices: Vec<I>,
346    ) -> Result<Self, super::GraphicsError> {
347        let inner = IndexedMesh::with_data(ctx, &vertices, &indices)?;
348        let vbo = MappedBuffer::from_vec(inner.mesh.vbo.clone(), unsafe {
349            let mut vertices = std::mem::ManuallyDrop::new(vertices);
350            Vec::from_raw_parts(
351                vertices.as_mut_ptr() as *mut _,
352                vertices.len() * std::mem::size_of::<V>(),
353                vertices.capacity() * std::mem::size_of::<V>(),
354            )
355        });
356        let ibo = MappedBuffer::from_vec(inner.ibo.clone(), unsafe {
357            let mut indices = std::mem::ManuallyDrop::new(indices);
358            Vec::from_raw_parts(
359                indices.as_mut_ptr() as *mut _,
360                indices.len() * std::mem::size_of::<I>(),
361                indices.capacity() * std::mem::size_of::<I>(),
362            )
363        });
364        Ok(Self { inner, vbo, ibo })
365    }
366
367    pub fn vertex_capacity(&self) -> usize {
368        self.vbo.memory_map.len() / std::mem::size_of::<V>()
369    }
370
371    pub fn index_capacity(&self) -> usize {
372        self.ibo.memory_map.len() / std::mem::size_of::<I>()
373    }
374
375    pub fn set_draw_range(&mut self, draw_range: Option<std::ops::Range<usize>>) {
376        self.inner.set_draw_range(draw_range)
377    }
378
379    pub fn draw_range(&self) -> std::ops::Range<usize> {
380        self.inner.draw_range()
381    }
382
383    pub fn set_vertices(&mut self, vertices: &[V], offset: usize) {
384        set_buffer(&mut self.vbo, vertices, offset)
385    }
386
387    pub fn get_vertices(&self) -> &[V] {
388        get_buffer(&self.vbo)
389    }
390
391    pub fn set_indices(&mut self, indices: &[I], offset: usize) {
392        set_buffer(&mut self.ibo, indices, offset)
393    }
394
395    pub fn get_indices(&self) -> &[I] {
396        get_buffer(&self.ibo)
397    }
398
399    pub fn unmap(&mut self, ctx: &mut Context) -> &IndexedMesh<V, I> {
400        self.vbo.unmap(ctx);
401        self.ibo.unmap(ctx);
402        self.inner()
403    }
404
405    pub fn inner(&self) -> &IndexedMesh<V, I> {
406        &self.inner
407    }
408}
409
410#[derive(Clone, Debug, PartialEq, Eq)]
411pub struct AttachedAttributes<'a> {
412    pub buffer: &'a Buffer,
413    pub formats: &'a [VertexFormat],
414    pub step: u32,
415    pub stride: usize,
416}
417
418pub trait Mesh {
419    fn attachments(&self) -> Vec<AttachedAttributes>;
420    fn draw(
421        &self,
422        ctx: &mut super::Context,
423        draw_range: std::ops::Range<usize>,
424        draw_mode: super::DrawMode,
425        instance_count: usize,
426    );
427}
428
429impl<V: Vertex> Mesh for VertexMesh<V> {
430    fn attachments(&self) -> Vec<AttachedAttributes> {
431        vec![AttachedAttributes {
432            buffer: &self.vbo,
433            formats: V::build_bindings(),
434            step: 0,
435            stride: std::mem::size_of::<V>(),
436        }]
437    }
438
439    fn draw(
440        &self,
441        ctx: &mut super::Context,
442        draw_range: std::ops::Range<usize>,
443        draw_mode: super::DrawMode,
444        instance_count: usize,
445    ) {
446        if draw_range.start >= draw_range.end {
447            return;
448        }
449
450        let (count, offset) = (
451            (draw_range.end - draw_range.start) as i32,
452            draw_range.start as i32,
453        );
454        if instance_count > 1 {
455            ctx.draw_arrays_instanced(draw_mode, offset, count, instance_count as i32);
456        } else {
457            ctx.draw_arrays(draw_mode, offset, count);
458        }
459    }
460}
461
462impl<V: Vertex> Mesh for &VertexMesh<V> {
463    fn attachments(&self) -> Vec<AttachedAttributes> {
464        VertexMesh::attachments(self)
465    }
466
467    fn draw(
468        &self,
469        ctx: &mut super::Context,
470        draw_range: std::ops::Range<usize>,
471        draw_mode: super::DrawMode,
472        instance_count: usize,
473    ) {
474        VertexMesh::draw(self, ctx, draw_range, draw_mode, instance_count)
475    }
476}
477
478impl<V: Vertex, I: Index> Mesh for IndexedMesh<V, I> {
479    fn attachments(&self) -> Vec<AttachedAttributes> {
480        self.mesh.attachments()
481    }
482
483    fn draw(
484        &self,
485        ctx: &mut super::Context,
486        draw_range: std::ops::Range<usize>,
487        draw_mode: super::DrawMode,
488        instance_count: usize,
489    ) {
490        if draw_range.start >= draw_range.end {
491            return;
492        }
493
494        let (count, offset) = (
495            (draw_range.end - draw_range.start) as i32,
496            draw_range.start as i32,
497        );
498
499        let ibo = &self.ibo;
500        ctx.bind_buffer(ibo.handle(), ibo.buffer_type());
501        if instance_count > 1 {
502            ctx.draw_elements_instanced(
503                draw_mode,
504                count,
505                I::GL_TYPE,
506                offset,
507                instance_count as i32,
508            );
509        } else {
510            ctx.draw_elements(draw_mode, count, I::GL_TYPE, offset);
511        }
512    }
513}
514
515impl<V: Vertex, I: Index> Mesh for &IndexedMesh<V, I> {
516    fn attachments(&self) -> Vec<AttachedAttributes> {
517        IndexedMesh::attachments(self)
518    }
519
520    fn draw(
521        &self,
522        ctx: &mut super::Context,
523        draw_range: std::ops::Range<usize>,
524        draw_mode: super::DrawMode,
525        instance_count: usize,
526    ) {
527        IndexedMesh::draw(self, ctx, draw_range, draw_mode, instance_count)
528    }
529}
530
531#[derive(Debug, Clone, PartialEq, Eq)]
532pub struct MultiMesh<'a> {
533    ibo: Option<(&'a Buffer, u32)>,
534    attachments: Vec<AttachedAttributes<'a>>,
535}
536
537impl<'a> Mesh for MultiMesh<'a> {
538    fn attachments(&self) -> Vec<AttachedAttributes> {
539        self.attachments.clone()
540    }
541
542    fn draw(
543        &self,
544        ctx: &mut Context,
545        draw_range: std::ops::Range<usize>,
546        draw_mode: super::DrawMode,
547        instance_count: usize,
548    ) {
549        match self.ibo {
550            None => {
551                if draw_range.start >= draw_range.end {
552                    return;
553                }
554
555                let (count, offset) = (
556                    (draw_range.end - draw_range.start) as i32,
557                    draw_range.start as i32,
558                );
559                if instance_count > 1 {
560                    ctx.draw_arrays_instanced(draw_mode, offset, count, instance_count as i32);
561                } else {
562                    ctx.draw_arrays(draw_mode, offset, count);
563                }
564            }
565            Some((ibo, element_type)) => {
566                if draw_range.start >= draw_range.end {
567                    return;
568                }
569
570                let (count, offset) = (
571                    (draw_range.end - draw_range.start) as i32,
572                    draw_range.start as i32,
573                );
574
575                ctx.bind_buffer(ibo.handle(), ibo.buffer_type());
576                if instance_count > 1 {
577                    ctx.draw_elements_instanced(
578                        draw_mode,
579                        count,
580                        element_type,
581                        offset,
582                        instance_count as i32,
583                    );
584                } else {
585                    ctx.draw_elements(draw_mode, count, element_type, offset);
586                }
587            }
588        }
589    }
590}
591
592impl Mesh for &MultiMesh<'_> {
593    fn attachments(&self) -> Vec<AttachedAttributes> {
594        MultiMesh::attachments(self)
595    }
596
597    fn draw(
598        &self,
599        ctx: &mut Context,
600        draw_range: std::ops::Range<usize>,
601        draw_mode: crate::DrawMode,
602        instance_count: usize,
603    ) {
604        MultiMesh::draw(self, ctx, draw_range, draw_mode, instance_count)
605    }
606}
607
608pub trait MeshAttacher: Mesh {
609    fn attach<'a, T: Mesh>(&'a self, other: &'a T) -> MultiMesh<'a> {
610        Self::attach_with_step(self, other, 0)
611    }
612
613    fn attach_with_step<'a, T: Mesh>(&'a self, other: &'a T, step: u32) -> MultiMesh<'a>;
614}
615
616impl<V: Vertex> MeshAttacher for VertexMesh<V> {
617    fn attach_with_step<'a, T: Mesh>(&'a self, other: &'a T, step: u32) -> MultiMesh<'a> {
618        let mut attachments = self.attachments();
619        attachments.extend(other.attachments().into_iter().map(|mut a| {
620            a.step = step;
621            a
622        }));
623        MultiMesh {
624            ibo: None,
625            attachments,
626        }
627    }
628}
629impl<V: Vertex, I: Index> MeshAttacher for IndexedMesh<V, I> {
630    fn attach_with_step<'a, T: Mesh>(&'a self, other: &'a T, step: u32) -> MultiMesh<'a> {
631        let mut attachments = self.attachments();
632        attachments.extend(other.attachments().into_iter().map(|mut a| {
633            a.step = step;
634            a
635        }));
636        MultiMesh {
637            ibo: Some((&self.ibo, I::GL_TYPE)),
638            attachments,
639        }
640    }
641}
642
643pub trait Index {
644    const GL_TYPE: u32;
645}
646
647impl Index for u32 {
648    const GL_TYPE: u32 = glow::UNSIGNED_INT;
649}
650
651impl Index for u16 {
652    const GL_TYPE: u32 = glow::UNSIGNED_SHORT;
653}