use super::Vertex;
use crate::{
pipeline::{
AsVertexBufferDescriptor, IndexFormat, PrimitiveTopology, RenderPipelines,
VertexBufferDescriptor, VertexBufferDescriptors, VertexFormat,
},
renderer::{BufferInfo, BufferUsage, RenderResourceContext, RenderResourceId},
};
use bevy_app::prelude::{EventReader, Events};
use bevy_asset::{AssetEvent, Assets, Handle};
use bevy_core::AsBytes;
use bevy_ecs::{Local, Query, Res, ResMut};
use bevy_math::*;
use bevy_utils::HashSet;
use std::borrow::Cow;
use thiserror::Error;
pub const VERTEX_BUFFER_ASSET_INDEX: usize = 0;
pub const INDEX_BUFFER_ASSET_INDEX: usize = 1;
#[derive(Clone, Debug)]
pub enum VertexAttributeValues {
Float(Vec<f32>),
Float2(Vec<[f32; 2]>),
Float3(Vec<[f32; 3]>),
Float4(Vec<[f32; 4]>),
}
impl VertexAttributeValues {
pub fn len(&self) -> usize {
match *self {
VertexAttributeValues::Float(ref values) => values.len(),
VertexAttributeValues::Float2(ref values) => values.len(),
VertexAttributeValues::Float3(ref values) => values.len(),
VertexAttributeValues::Float4(ref values) => values.len(),
}
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get_bytes(&self) -> &[u8] {
match self {
VertexAttributeValues::Float(values) => values.as_slice().as_bytes(),
VertexAttributeValues::Float2(values) => values.as_slice().as_bytes(),
VertexAttributeValues::Float3(values) => values.as_slice().as_bytes(),
VertexAttributeValues::Float4(values) => values.as_slice().as_bytes(),
}
}
}
impl From<&VertexAttributeValues> for VertexFormat {
fn from(values: &VertexAttributeValues) -> Self {
match values {
VertexAttributeValues::Float(_) => VertexFormat::Float,
VertexAttributeValues::Float2(_) => VertexFormat::Float2,
VertexAttributeValues::Float3(_) => VertexFormat::Float3,
VertexAttributeValues::Float4(_) => VertexFormat::Float4,
}
}
}
#[derive(Debug)]
pub struct VertexAttribute {
pub name: Cow<'static, str>,
pub values: VertexAttributeValues,
}
impl VertexAttribute {
pub const NORMAL: &'static str = "Vertex_Normal";
pub const POSITION: &'static str = "Vertex_Position";
pub const UV: &'static str = "Vertex_Uv";
pub fn position(positions: Vec<[f32; 3]>) -> Self {
VertexAttribute {
name: Self::POSITION.into(),
values: VertexAttributeValues::Float3(positions),
}
}
pub fn normal(normals: Vec<[f32; 3]>) -> Self {
VertexAttribute {
name: Self::NORMAL.into(),
values: VertexAttributeValues::Float3(normals),
}
}
pub fn uv(uvs: Vec<[f32; 2]>) -> Self {
VertexAttribute {
name: Self::UV.into(),
values: VertexAttributeValues::Float2(uvs),
}
}
}
#[derive(Error, Debug)]
pub enum MeshToVertexBufferError {
#[error("VertexBufferDescriptor requires a VertexBufferAttribute this Mesh does not contain.")]
MissingVertexAttribute { attribute_name: Cow<'static, str> },
#[error("Mesh VertexAttribute VertexFormat is incompatible with VertexBufferDescriptor VertexAttribute VertexFormat.")]
IncompatibleVertexAttributeFormat {
attribute_name: Cow<'static, str>,
descriptor_format: VertexFormat,
mesh_format: VertexFormat,
},
}
#[derive(Debug)]
pub struct Mesh {
pub primitive_topology: PrimitiveTopology,
pub attributes: Vec<VertexAttribute>,
pub indices: Option<Vec<u32>>,
}
impl Mesh {
pub fn new(primitive_topology: PrimitiveTopology) -> Self {
Mesh {
primitive_topology,
attributes: Vec::new(),
indices: None,
}
}
pub fn get_vertex_buffer_bytes(
&self,
vertex_buffer_descriptor: &VertexBufferDescriptor,
fill_missing_attributes: bool,
) -> Result<Vec<u8>, MeshToVertexBufferError> {
let length = self.attributes.first().map(|a| a.values.len()).unwrap_or(0);
let mut bytes = vec![0; vertex_buffer_descriptor.stride as usize * length];
for vertex_attribute in vertex_buffer_descriptor.attributes.iter() {
match self
.attributes
.iter()
.find(|a| vertex_attribute.name == a.name)
{
Some(mesh_attribute) => {
let attribute_bytes = mesh_attribute.values.get_bytes();
let attribute_size = vertex_attribute.format.get_size() as usize;
for (i, vertex_slice) in attribute_bytes.chunks(attribute_size).enumerate() {
let vertex_offset = vertex_buffer_descriptor.stride as usize * i;
let attribute_offset = vertex_offset + vertex_attribute.offset as usize;
bytes[attribute_offset..attribute_offset + attribute_size]
.copy_from_slice(vertex_slice);
}
}
None => {
if !fill_missing_attributes {
return Err(MeshToVertexBufferError::MissingVertexAttribute {
attribute_name: vertex_attribute.name.clone(),
});
}
}
}
}
Ok(bytes)
}
pub fn get_index_buffer_bytes(&self, index_format: IndexFormat) -> Option<Vec<u8>> {
self.indices.as_ref().map(|indices| match index_format {
IndexFormat::Uint16 => indices
.iter()
.map(|i| *i as u16)
.collect::<Vec<u16>>()
.as_slice()
.as_bytes()
.to_vec(),
IndexFormat::Uint32 => indices.as_slice().as_bytes().to_vec(),
})
}
}
pub mod shape {
use super::{Mesh, VertexAttribute};
use crate::pipeline::PrimitiveTopology;
use bevy_math::*;
use hexasphere::Hexasphere;
pub struct Cube {
pub size: f32,
}
impl Default for Cube {
fn default() -> Self {
Cube { size: 1.0 }
}
}
impl From<Cube> for Mesh {
fn from(cube: Cube) -> Self {
let size = cube.size;
let vertices = &[
([-size, -size, size], [0., 0., size], [0., 0.]),
([size, -size, size], [0., 0., size], [size, 0.]),
([size, size, size], [0., 0., size], [size, size]),
([-size, size, size], [0., 0., size], [0., size]),
([-size, size, -size], [0., 0., -size], [size, 0.]),
([size, size, -size], [0., 0., -size], [0., 0.]),
([size, -size, -size], [0., 0., -size], [0., size]),
([-size, -size, -size], [0., 0., -size], [size, size]),
([size, -size, -size], [size, 0., 0.], [0., 0.]),
([size, size, -size], [size, 0., 0.], [size, 0.]),
([size, size, size], [size, 0., 0.], [size, size]),
([size, -size, size], [size, 0., 0.], [0., size]),
([-size, -size, size], [-size, 0., 0.], [size, 0.]),
([-size, size, size], [-size, 0., 0.], [0., 0.]),
([-size, size, -size], [-size, 0., 0.], [0., size]),
([-size, -size, -size], [-size, 0., 0.], [size, size]),
([size, size, -size], [0., size, 0.], [size, 0.]),
([-size, size, -size], [0., size, 0.], [0., 0.]),
([-size, size, size], [0., size, 0.], [0., size]),
([size, size, size], [0., size, 0.], [size, size]),
([size, -size, size], [0., -size, 0.], [0., 0.]),
([-size, -size, size], [0., -size, 0.], [size, 0.]),
([-size, -size, -size], [0., -size, 0.], [size, size]),
([size, -size, -size], [0., -size, 0.], [0., size]),
];
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
for (position, normal, uv) in vertices.iter() {
positions.push(*position);
normals.push(*normal);
uvs.push(*uv);
}
let indices = vec![
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4,
8, 9, 10, 10, 11, 8,
12, 13, 14, 14, 15, 12,
16, 17, 18, 18, 19, 16,
20, 21, 22, 22, 23, 20,
];
Mesh {
primitive_topology: PrimitiveTopology::TriangleList,
attributes: vec![
VertexAttribute::position(positions),
VertexAttribute::normal(normals),
VertexAttribute::uv(uvs),
],
indices: Some(indices),
}
}
}
pub struct Quad {
pub size: Vec2,
pub flip: bool,
}
impl Quad {
pub fn new(size: Vec2) -> Self {
Self { size, flip: false }
}
pub fn flipped(size: Vec2) -> Self {
Self { size, flip: true }
}
}
impl From<Quad> for Mesh {
fn from(quad: Quad) -> Self {
let extent_x = quad.size.x() / 2.0;
let extent_y = quad.size.y() / 2.0;
let north_west = vec2(-extent_x, extent_y);
let north_east = vec2(extent_x, extent_y);
let south_west = vec2(-extent_x, -extent_y);
let south_east = vec2(extent_x, -extent_y);
let vertices = if quad.flip {
[
(
[south_east.x(), south_east.y(), 0.0],
[0.0, 0.0, 1.0],
[1.0, 1.0],
),
(
[north_east.x(), north_east.y(), 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0],
),
(
[north_west.x(), north_west.y(), 0.0],
[0.0, 0.0, 1.0],
[0.0, 0.0],
),
(
[south_west.x(), south_west.y(), 0.0],
[0.0, 0.0, 1.0],
[0.0, 1.0],
),
]
} else {
[
(
[south_west.x(), south_west.y(), 0.0],
[0.0, 0.0, 1.0],
[0.0, 1.0],
),
(
[north_west.x(), north_west.y(), 0.0],
[0.0, 0.0, 1.0],
[0.0, 0.0],
),
(
[north_east.x(), north_east.y(), 0.0],
[0.0, 0.0, 1.0],
[1.0, 0.0],
),
(
[south_east.x(), south_east.y(), 0.0],
[0.0, 0.0, 1.0],
[1.0, 1.0],
),
]
};
let indices = vec![0, 2, 1, 0, 3, 2];
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
for (position, normal, uv) in vertices.iter() {
positions.push(*position);
normals.push(*normal);
uvs.push(*uv);
}
Mesh {
primitive_topology: PrimitiveTopology::TriangleList,
attributes: vec![
VertexAttribute::position(positions),
VertexAttribute::normal(normals),
VertexAttribute::uv(uvs),
],
indices: Some(indices),
}
}
}
pub struct Plane {
pub size: f32,
}
impl From<Plane> for Mesh {
fn from(plane: Plane) -> Self {
let extent = plane.size / 2.0;
let vertices = [
([extent, 0.0, -extent], [0.0, 1.0, 0.0], [1.0, 1.0]),
([extent, 0.0, extent], [0.0, 1.0, 0.0], [1.0, 0.0]),
([-extent, 0.0, extent], [0.0, 1.0, 0.0], [0.0, 0.0]),
([-extent, 0.0, -extent], [0.0, 1.0, 0.0], [0.0, 1.0]),
];
let indices = vec![0, 2, 1, 0, 3, 2];
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
for (position, normal, uv) in vertices.iter() {
positions.push(*position);
normals.push(*normal);
uvs.push(*uv);
}
Mesh {
primitive_topology: PrimitiveTopology::TriangleList,
attributes: vec![
VertexAttribute::position(positions),
VertexAttribute::normal(normals),
VertexAttribute::uv(uvs),
],
indices: Some(indices),
}
}
}
pub struct Icosphere {
pub radius: f32,
pub subdivisions: usize,
}
impl Default for Icosphere {
fn default() -> Self {
Self {
radius: 1.0,
subdivisions: 5,
}
}
}
impl From<Icosphere> for Mesh {
fn from(sphere: Icosphere) -> Self {
if sphere.subdivisions >= 80 {
let temp_sphere = Hexasphere::new(sphere.subdivisions, |_| ());
panic!(
"Cannot create an icosphere of {} subdivisions due to there being too many vertices being generated: {} (Limited to 65535 vertices or 79 subdivisions)",
sphere.subdivisions,
temp_sphere.raw_points().len()
);
}
let hexasphere = Hexasphere::new(sphere.subdivisions, |point| {
let inclination = point.z().acos();
let azumith = point.y().atan2(point.x());
let norm_inclination = 1.0 - (inclination / std::f32::consts::PI);
let norm_azumith = (azumith / std::f32::consts::PI) * 0.5;
[norm_inclination, norm_azumith]
});
let raw_points = hexasphere.raw_points();
let points = raw_points
.iter()
.map(|&p| (p * sphere.radius).into())
.collect::<Vec<[f32; 3]>>();
let normals = raw_points
.iter()
.copied()
.map(Into::into)
.collect::<Vec<[f32; 3]>>();
let uvs = hexasphere.raw_data().to_owned();
let mut indices = Vec::with_capacity(hexasphere.indices_per_main_triangle() * 20);
for i in 0..20 {
hexasphere.get_indices(i, &mut indices);
}
Mesh {
primitive_topology: PrimitiveTopology::TriangleList,
attributes: vec![
VertexAttribute::position(points),
VertexAttribute::normal(normals),
VertexAttribute::uv(uvs),
],
indices: Some(indices),
}
}
}
}
fn remove_current_mesh_resources(
render_resource_context: &dyn RenderResourceContext,
handle: Handle<Mesh>,
) {
if let Some(RenderResourceId::Buffer(buffer)) =
render_resource_context.get_asset_resource(handle, VERTEX_BUFFER_ASSET_INDEX)
{
render_resource_context.remove_buffer(buffer);
render_resource_context.remove_asset_resource(handle, VERTEX_BUFFER_ASSET_INDEX);
}
if let Some(RenderResourceId::Buffer(buffer)) =
render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX)
{
render_resource_context.remove_buffer(buffer);
render_resource_context.remove_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX);
}
}
#[derive(Default)]
pub struct MeshResourceProviderState {
mesh_event_reader: EventReader<AssetEvent<Mesh>>,
vertex_buffer_descriptor: Option<&'static VertexBufferDescriptor>,
}
pub fn mesh_resource_provider_system(
mut state: Local<MeshResourceProviderState>,
render_resource_context: Res<Box<dyn RenderResourceContext>>,
meshes: Res<Assets<Mesh>>,
mut vertex_buffer_descriptors: ResMut<VertexBufferDescriptors>,
mesh_events: Res<Events<AssetEvent<Mesh>>>,
mut query: Query<(&Handle<Mesh>, &mut RenderPipelines)>,
) {
let vertex_buffer_descriptor = match state.vertex_buffer_descriptor {
Some(value) => value,
None => {
let vertex_buffer_descriptor = Vertex::as_vertex_buffer_descriptor();
vertex_buffer_descriptors.set(vertex_buffer_descriptor.clone());
state.vertex_buffer_descriptor = Some(vertex_buffer_descriptor);
vertex_buffer_descriptor
}
};
let mut changed_meshes = HashSet::<Handle<Mesh>>::default();
let render_resource_context = &**render_resource_context;
for event in state.mesh_event_reader.iter(&mesh_events) {
match event {
AssetEvent::Created { handle } => {
changed_meshes.insert(*handle);
}
AssetEvent::Modified { handle } => {
changed_meshes.insert(*handle);
remove_current_mesh_resources(render_resource_context, *handle);
}
AssetEvent::Removed { handle } => {
remove_current_mesh_resources(render_resource_context, *handle);
changed_meshes.remove(handle);
}
}
}
for changed_mesh_handle in changed_meshes.iter() {
if let Some(mesh) = meshes.get(changed_mesh_handle) {
let vertex_bytes = mesh
.get_vertex_buffer_bytes(&vertex_buffer_descriptor, true)
.unwrap();
let vertex_buffer = render_resource_context.create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::VERTEX,
..Default::default()
},
&vertex_bytes,
);
let index_bytes = mesh.get_index_buffer_bytes(IndexFormat::Uint16).unwrap();
let index_buffer = render_resource_context.create_buffer_with_data(
BufferInfo {
buffer_usage: BufferUsage::INDEX,
..Default::default()
},
&index_bytes,
);
render_resource_context.set_asset_resource(
*changed_mesh_handle,
RenderResourceId::Buffer(vertex_buffer),
VERTEX_BUFFER_ASSET_INDEX,
);
render_resource_context.set_asset_resource(
*changed_mesh_handle,
RenderResourceId::Buffer(index_buffer),
INDEX_BUFFER_ASSET_INDEX,
);
}
}
for (handle, mut render_pipelines) in &mut query.iter() {
if let Some(mesh) = meshes.get(&handle) {
for render_pipeline in render_pipelines.pipelines.iter_mut() {
render_pipeline.specialization.primitive_topology = mesh.primitive_topology;
}
}
if let Some(RenderResourceId::Buffer(vertex_buffer)) =
render_resource_context.get_asset_resource(*handle, VERTEX_BUFFER_ASSET_INDEX)
{
render_pipelines.bindings.set_vertex_buffer(
"Vertex",
vertex_buffer,
render_resource_context
.get_asset_resource(*handle, INDEX_BUFFER_ASSET_INDEX)
.and_then(|r| {
if let RenderResourceId::Buffer(buffer) = r {
Some(buffer)
} else {
None
}
}),
);
}
}
}
#[cfg(test)]
mod tests {
use super::{AsVertexBufferDescriptor, Mesh, VertexAttribute};
use crate::{mesh::Vertex, pipeline::PrimitiveTopology};
use bevy_core::AsBytes;
#[test]
fn test_get_vertex_bytes() {
let vertices = &[
([0., 0., 0.], [1., 1., 1.], [2., 2.]),
([3., 3., 3.], [4., 4., 4.], [5., 5.]),
([6., 6., 6.], [7., 7., 7.], [8., 8.]),
];
let mut positions = Vec::new();
let mut normals = Vec::new();
let mut uvs = Vec::new();
for (position, normal, uv) in vertices.iter() {
positions.push(*position);
normals.push(*normal);
uvs.push(*uv);
}
let mesh = Mesh {
primitive_topology: PrimitiveTopology::TriangleStrip,
attributes: vec![
VertexAttribute::position(positions),
VertexAttribute::normal(normals),
VertexAttribute::uv(uvs),
],
indices: None,
};
let expected_vertices = &[
Vertex {
position: [0., 0., 0.],
normal: [1., 1., 1.],
uv: [2., 2.],
},
Vertex {
position: [3., 3., 3.],
normal: [4., 4., 4.],
uv: [5., 5.],
},
Vertex {
position: [6., 6., 6.],
normal: [7., 7., 7.],
uv: [8., 8.],
},
];
let descriptor = Vertex::as_vertex_buffer_descriptor();
assert_eq!(
mesh.get_vertex_buffer_bytes(descriptor, true).unwrap(),
expected_vertices.as_bytes(),
"buffer bytes are equal"
);
}
}