shdrlib 0.1.5

A three-tiered Vulkan shader compilation and rendering framework built in pure Rust
Documentation
//! Mesh abstraction for simplified geometry management
//!
//! The `Mesh` type bundles vertex and index buffers together, eliminating
//! the need for separate bind operations and providing a cleaner API for
//! common rendering tasks.

use crate::core::{Buffer, Device};
use ash::vk;
use std::sync::Arc;

/// A complete mesh with vertex and optional index data
///
/// Bundles vertex buffers, index buffers, and draw parameters into a single
/// convenient type. This dramatically simplifies rendering code by reducing
/// multiple bind operations to a single `draw_mesh()` call.
///
/// The mesh automatically cleans up GPU resources when dropped.
///
/// # Example
///
/// ```rust,no_run
/// use shdrlib::ez::*;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut renderer = EzRenderer::new()?;
///
/// // Create a pipeline
/// let pipeline = renderer.quick_pipeline(
///     "#version 450\nvoid main() {}",
///     "#version 450\nlayout(location=0) out vec4 c; void main() { c = vec4(1.0); }"
/// )?;
///
/// // Create a mesh from vertex and index data
/// let vertices: Vec<f32> = vec![0.0, 0.0, 1.0, 0.0, 0.5, 1.0];
/// let indices: Vec<u16> = vec![0, 1, 2];
/// let mesh = renderer.create_mesh(&vertices, Some(&indices))?;
///
/// // Draw the entire mesh in one call
/// renderer.render_frame(|frame| {
///     frame.draw_mesh(pipeline, &mesh)?;
///     Ok(())
/// })?;
/// # Ok(())
/// # }
/// ```
pub struct Mesh {
    /// Device for cleanup
    device: Arc<Device>,

    /// The vertex buffer containing vertex data
    vertex_buffer: Buffer,

    /// Optional index buffer for indexed drawing
    index_buffer: Option<Buffer>,

    /// Number of vertices in the vertex buffer
    vertex_count: u32,

    /// Number of indices in the index buffer (if present)
    index_count: Option<u32>,

    /// Index type (u16 or u32)
    index_type: vk::IndexType,

    /// Vertex buffer binding point (usually 0)
    binding: u32,
}

impl Mesh {
    /// Create a new mesh with vertex data only (non-indexed)
    ///
    /// # Arguments
    ///
    /// * `device` - Device for resource cleanup
    /// * `vertex_buffer` - Buffer containing vertex data
    /// * `vertex_count` - Number of vertices
    /// * `binding` - Vertex buffer binding point (usually 0)
    pub fn new(device: Arc<Device>, vertex_buffer: Buffer, vertex_count: u32, binding: u32) -> Self {
        Self {
            device,
            vertex_buffer,
            index_buffer: None,
            vertex_count,
            index_count: None,
            index_type: vk::IndexType::UINT16, // Default, unused for non-indexed
            binding,
        }
    }

    /// Create a new indexed mesh
    ///
    /// # Arguments
    ///
    /// * `device` - Device for resource cleanup
    /// * `vertex_buffer` - Buffer containing vertex data
    /// * `vertex_count` - Number of vertices
    /// * `index_buffer` - Buffer containing index data
    /// * `index_count` - Number of indices
    /// * `index_type` - Type of indices (UINT16 or UINT32)
    /// * `binding` - Vertex buffer binding point (usually 0)
    pub fn new_indexed(
        device: Arc<Device>,
        vertex_buffer: Buffer,
        vertex_count: u32,
        index_buffer: Buffer,
        index_count: u32,
        index_type: vk::IndexType,
        binding: u32,
    ) -> Self {
        Self {
            device,
            vertex_buffer,
            index_buffer: Some(index_buffer),
            vertex_count,
            index_count: Some(index_count),
            index_type,
            binding,
        }
    }

    /// Get the vertex buffer
    #[inline]
    pub fn vertex_buffer(&self) -> &Buffer {
        &self.vertex_buffer
    }

    /// Get the index buffer (if present)
    #[inline]
    pub fn index_buffer(&self) -> Option<&Buffer> {
        self.index_buffer.as_ref()
    }

    /// Get the number of vertices
    #[inline]
    pub fn vertex_count(&self) -> u32 {
        self.vertex_count
    }

    /// Get the number of indices (if indexed mesh)
    #[inline]
    pub fn index_count(&self) -> Option<u32> {
        self.index_count
    }

    /// Check if this mesh uses indexed drawing
    #[inline]
    pub fn is_indexed(&self) -> bool {
        self.index_buffer.is_some()
    }

    /// Get the index type
    #[inline]
    pub fn index_type(&self) -> vk::IndexType {
        self.index_type
    }

    /// Get the vertex buffer binding point
    #[inline]
    pub fn binding(&self) -> u32 {
        self.binding
    }

    /// Bind this mesh's buffers to a command buffer
    ///
    /// This is an internal helper used by `Frame::draw_mesh()`.
    /// Users should prefer calling `frame.draw_mesh()` directly.
    #[inline]
    pub(crate) fn bind(&self, device: &ash::Device, command_buffer: vk::CommandBuffer) {
        // SAFETY: device and command buffer handles are valid
        unsafe {
            // Bind vertex buffer
            device.cmd_bind_vertex_buffers(
                command_buffer,
                self.binding,
                &[self.vertex_buffer.handle()],
                &[0],
            );

            // Bind index buffer if present
            if let Some(ref index_buffer) = self.index_buffer {
                device.cmd_bind_index_buffer(
                    command_buffer,
                    index_buffer.handle(),
                    0,
                    self.index_type,
                );
            }
        }
    }

    /// Issue the draw command for this mesh
    ///
    /// This is an internal helper used by `Frame::draw_mesh()`.
    /// Users should prefer calling `frame.draw_mesh()` directly.
    #[inline]
    pub(crate) fn draw(&self, device: &ash::Device, command_buffer: vk::CommandBuffer) {
        // SAFETY: device and command buffer handles are valid
        unsafe {
            if let Some(index_count) = self.index_count {
                // Indexed draw
                device.cmd_draw_indexed(
                    command_buffer,
                    index_count,
                    1,  // instance_count
                    0,  // first_index
                    0,  // vertex_offset
                    0,  // first_instance
                );
            } else {
                // Non-indexed draw
                device.cmd_draw(
                    command_buffer,
                    self.vertex_count,
                    1, // instance_count
                    0, // first_vertex
                    0, // first_instance
                );
            }
        }
    }
}

impl Drop for Mesh {
    fn drop(&mut self) {
        // Cleanup buffers in reverse order of creation
        if let Some(ref mut index_buffer) = self.index_buffer {
            index_buffer.destroy(&self.device);
        }
        self.vertex_buffer.destroy(&self.device);
    }
}