Skip to content

Commit

Permalink
Port module to bevy 0.6
Browse files Browse the repository at this point in the history
  • Loading branch information
Game4all committed Jan 1, 2022
1 parent 38a4412 commit a788efb
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 153 deletions.
10 changes: 6 additions & 4 deletions Cargo.toml
Expand Up @@ -5,19 +5,21 @@ license = "MIT"
version = "0.3.0"
repository = "https://github.com/Game4all/bevy_vox_mesh"
authors = ["Lucas A. <game4allyt@gmail.com>"]
edition = "2018"
edition = "2021"
exclude = ["assets/*"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bevy = { version = "0.5.0", default-features = false, features = ["render"] }
bevy = { git = "https://github.com/bevyengine/bevy", branch = "main" }
dot_vox = "4.1.0"
building-blocks = { version = "0.7.0", default-features = false, features = ["mesh"] }
ndshape = "0.2.0"
block-mesh = { git = "https://github.com/bonsairobo/block-mesh-rs" }
ndcopy = "0.2.0"
anyhow = "1.0.38"

[dev-dependencies]
bevy = "0.5.0"
bevy = { git = "https://github.com/bevyengine/bevy", branch = "main" }

[[example]]
name = "basic"
Expand Down
25 changes: 16 additions & 9 deletions src/lib.rs
Expand Up @@ -25,44 +25,51 @@
//!}
//!```

use bevy::prelude::*;
use bevy::{
app::{App, Plugin},
prelude::AddAsset,
};
use block_mesh::{QuadCoordinateConfig, RIGHT_HANDED_Y_UP_CONFIG};

mod loader;
#[doc(inline)]
use loader::VoxLoader;

mod mesh;
mod mesher;
mod voxel;

/// The core plugin adding functionality for loading `.vox` files.
///
/// Registers an [`bevy::asset::AssetLoader`] capable of loading modes in `.vox` files as usable [`bevy::render::mesh::Mesh`].
pub struct VoxMeshPlugin {
flip_uvs_vertically: bool,
config: QuadCoordinateConfig,
v_flip_faces: bool,
}

impl VoxMeshPlugin {
/// Creates a [`VoxMeshPlugin`] instance with the specified parameters
///
/// # Arguments
/// * `flip_uvs_vertically` - Sets whether the mesh UVs should be flipped vertically when loading voxel models.
pub fn with_options(flip_uvs_vertically: bool) -> Self {
/// * `config` - The quad coordinates configuration ([`QuadCoordinateConfig`]) to use when meshing models.
pub fn with_options(config: QuadCoordinateConfig, v_flip_faces: bool) -> Self {
Self {
flip_uvs_vertically,
config,
v_flip_faces,
}
}
}

impl Default for VoxMeshPlugin {
fn default() -> Self {
Self::with_options(true) //UVs should be flipped vertically by default as the main backend of WGPU is Vulkan
Self::with_options(RIGHT_HANDED_Y_UP_CONFIG, true)
}
}

impl Plugin for VoxMeshPlugin {
fn build(&self, app: &mut AppBuilder) {
fn build(&self, app: &mut App) {
app.add_asset_loader(VoxLoader {
flip_uvs_vertically: self.flip_uvs_vertically,
config: self.config.clone(),
v_flip_face: self.v_flip_faces,
});
}
}
30 changes: 18 additions & 12 deletions src/loader.rs
@@ -1,17 +1,16 @@
use anyhow::{anyhow, Error};
use bevy::asset::{AssetLoader, LoadContext, LoadedAsset};

use crate::mesher::mesh_model;
use block_mesh::QuadCoordinateConfig;

/// An asset loader capable of loading models in `.vox` files as usable [`bevy::render::mesh::Mesh`]es.
///
///
/// The meshes generated by this asset loader only use standard [`bevy::render::mesh::Mesh`] attributes for easier compatibility with shaders.
/// You can load multiple models from the same `.vox` file by appending `#model{no}` to the asset loading path, where `{no}` corresponds to the model index in the file.
#[derive(Default)]
pub struct VoxLoader {
/// Whether to flip the UVs vertically when meshing the models.
/// You may want to change this to false if you aren't using Vulkan as a graphical backend for bevy , else this should default to true.
pub flip_uvs_vertically: bool,
pub(crate) config: QuadCoordinateConfig,
pub(crate) v_flip_face: bool
}

impl AssetLoader for VoxLoader {
Expand All @@ -21,7 +20,7 @@ impl AssetLoader for VoxLoader {
load_context: &'a mut LoadContext,
) -> bevy::utils::BoxedFuture<'a, Result<(), Error>> {
Box::pin(async move {
self.load_magica_voxel_file(bytes, load_context)?;
self.process_vox_file(bytes, load_context)?;
Ok(())
})
}
Expand All @@ -32,7 +31,7 @@ impl AssetLoader for VoxLoader {
}

impl VoxLoader {
fn load_magica_voxel_file<'a>(
fn process_vox_file<'a>(
&self,
bytes: &'a [u8],
load_context: &'a mut LoadContext,
Expand All @@ -49,12 +48,19 @@ impl VoxLoader {
.collect();

for (index, model) in file.models.iter().enumerate() {
let mesh = mesh_model(model, &palette, self.flip_uvs_vertically);
let (shape, buffer) = crate::voxel::load_from_model(model);
let mesh = crate::mesh::mesh_model(shape, &buffer, &palette, &self.config, self.v_flip_face);

load_context
.set_labeled_asset(&format!("model{}", index), LoadedAsset::new(mesh.clone()));
if index == 0 {
load_context.set_default_asset(LoadedAsset::new(mesh.clone()));
match index {
0 => {
load_context.set_default_asset(LoadedAsset::new(mesh.clone()));
}
_ => {
load_context.set_labeled_asset(
&format!("model{}", index),
LoadedAsset::new(mesh.clone()),
);
}
}
}

Expand Down
95 changes: 64 additions & 31 deletions src/mesh.rs
@@ -1,40 +1,73 @@
use building_blocks::mesh::{OrientedCubeFace, UnorientedQuad};

/// Helper struct to organize mesh data for bevy.
#[derive(Default)]
pub(crate) struct VoxMesh {
pub positions: Vec<[f32; 3]>,
pub normals: Vec<[f32; 3]>,
pub colors: Vec<[u8; 4]>,
pub uvs: Vec<[f32; 2]>,
pub indices: Vec<u32>,
}
use bevy::render::{
mesh::{Indices, Mesh, VertexAttributeValues},
render_resource::PrimitiveTopology,
};
use block_mesh::{greedy_quads, GreedyQuadsBuffer, QuadCoordinateConfig};
use ndshape::{Shape, Shape3u32};

impl VoxMesh {
pub(crate) fn add_quad(
&mut self,
face: &OrientedCubeFace,
quad: &UnorientedQuad,
palette_index: u32,
palette: &[[u8; 4]],
flip_v: bool
) {
let start_index = self.positions.len() as u32;
use crate::voxel::Voxel;

//todo: maybe use u8's instead of f32's for position and normal attributes since magica voxel max size per model per dimension is 256.
pub(crate) fn mesh_model(
buffer_shape: Shape3u32,
buffer: &[Voxel],
palette: &[[u8; 4]],
quads_config: &QuadCoordinateConfig,
v_flip_face: bool,
) -> Mesh {
let mut greedy_quads_buffer = GreedyQuadsBuffer::new(buffer_shape.size() as usize);

self.positions
.extend_from_slice(&face.quad_mesh_positions(quad));
greedy_quads(
buffer,
&buffer_shape,
[0; 3],
buffer_shape.as_array().map(|x| x - 1),
&quads_config.faces,
&mut greedy_quads_buffer,
);

self.normals.extend_from_slice(&face.quad_mesh_normals());
let num_indices = greedy_quads_buffer.quads.num_quads() * 6;
let num_vertices = greedy_quads_buffer.quads.num_quads() * 4;

self.colors
.extend_from_slice(&[palette[palette_index as usize]; 4]);
let mut indices = Vec::with_capacity(num_indices);
let mut positions = Vec::with_capacity(num_vertices);
let mut normals = Vec::with_capacity(num_vertices);
let mut uvs = Vec::with_capacity(num_vertices);
let mut colors = Vec::with_capacity(num_vertices);

self.uvs
.extend_from_slice(&face.simple_tex_coords(flip_v, &quad));
let mut render_mesh = Mesh::new(PrimitiveTopology::TriangleList);

self.indices
.extend_from_slice(&face.quad_mesh_indices(start_index));
for (group, face) in greedy_quads_buffer
.quads
.groups
.iter()
.zip(quads_config.faces.as_ref())
{
for quad in group.iter() {
let palette_index = buffer[buffer_shape.linearize(quad.minimum) as usize].0;
colors.extend_from_slice(&[palette[palette_index as usize]; 4]);
indices.extend_from_slice(&face.quad_mesh_indices(positions.len() as u32));
positions.extend_from_slice(&face.quad_mesh_positions(quad, 1.0));
uvs.extend_from_slice(&face.tex_coords(quads_config.u_flip_face, v_flip_face, quad));
normals.extend_from_slice(&face.quad_mesh_normals());
}
}

render_mesh.set_attribute(
Mesh::ATTRIBUTE_POSITION,
VertexAttributeValues::Float32x3(positions),
);

render_mesh.set_attribute(
Mesh::ATTRIBUTE_NORMAL,
VertexAttributeValues::Float32x3(normals),
);
render_mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, VertexAttributeValues::Float32x2(uvs));
render_mesh.set_attribute(
Mesh::ATTRIBUTE_COLOR,
VertexAttributeValues::Unorm8x4(colors),
);

render_mesh.set_indices(Some(Indices::U32(indices.clone())));

render_mesh
}
97 changes: 0 additions & 97 deletions src/mesher.rs

This file was deleted.

41 changes: 41 additions & 0 deletions src/voxel.rs
@@ -0,0 +1,41 @@
use block_mesh::{MergeVoxel, Voxel as BlockyVoxel};
use dot_vox::Model;
use ndshape::{Shape, Shape3u32};

// trait implementation rules requires the use of a newtype to allow meshing.
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct Voxel(pub(crate) u8);

pub(crate) const EMPTY_VOXEL: Voxel = Voxel(255);

impl BlockyVoxel for Voxel {
fn is_empty(&self) -> bool {
self.0 == EMPTY_VOXEL.0
}

fn is_opaque(&self) -> bool {
self.0 != EMPTY_VOXEL.0
}
}

impl MergeVoxel for Voxel {
type MergeValue = Voxel;

fn merge_value(&self) -> Self::MergeValue {
*self
}
}

pub(crate) fn load_from_model(model: &Model) -> (Shape3u32, Vec<Voxel>) {
let model_shape = Shape3u32::new([model.size.x + 2, model.size.z + 2, model.size.y + 2]);
let mut data = vec![EMPTY_VOXEL; model_shape.size() as usize];

model.voxels.iter().for_each(|voxel| {
let index =
model_shape.linearize([voxel.x as u32 + 1, voxel.z as u32 + 1, voxel.y as u32 + 1])
as usize;
data[index] = Voxel(voxel.i);
});

(model_shape, data)
}

0 comments on commit a788efb

Please sign in to comment.