Skip to content

Commit

Permalink
Update bevy and add aabb
Browse files Browse the repository at this point in the history
  • Loading branch information
DotWith committed Dec 24, 2023
1 parent 04fec38 commit a59ca68
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 85 deletions.
8 changes: 4 additions & 4 deletions bevy_rmesh/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bevy_rmesh"
version = "0.2.0"
version = "0.3.0"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A Bevy extension for RMesh loading"
Expand All @@ -12,12 +12,12 @@ keywords = ["bevy"]
exclude = ["assets/*"]

[dependencies]
bevy = { version = "0.11.0", default-features = false, features = ["bevy_asset", "bevy_pbr", "bevy_render", "bevy_scene"] }
bevy = { version = "0.12.1", default-features = false, features = ["bevy_asset", "bevy_pbr", "bevy_render", "bevy_scene"] }
anyhow = "1.0.68"
rmesh = { path = "../rmesh", version = "0.3.2" }
rmesh = { path = "../rmesh", version = "0.3.3" }

[dev-dependencies]
bevy = "0.11.0"
bevy = "0.12.1"

[[example]]
name = "view"
Expand Down
29 changes: 21 additions & 8 deletions bevy_rmesh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,42 @@ pub use rmesh;

mod loader;

use bevy::{prelude::*, reflect::{TypeUuid, TypePath}};
use bevy::{
prelude::*,
reflect::TypePath,
render::{renderer::RenderDevice, texture::CompressedImageFormats},
};

#[derive(Default)]
pub struct RMeshPlugin;

impl Plugin for RMeshPlugin {
fn build(&self, app: &mut App) {
app.init_asset_loader::<RMeshLoader>()
.add_asset::<Room>()
.add_asset::<RoomMesh>();
app.init_asset::<Room>()
.init_asset::<RoomMesh>()
.preregister_asset_loader::<RMeshLoader>(&["rmesh"]);
}

fn finish(&self, app: &mut App) {
let supported_compressed_formats = match app.world.get_resource::<RenderDevice>() {
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),

None => CompressedImageFormats::NONE,
};
app.register_asset_loader(RMeshLoader {
supported_compressed_formats,
});
}
}

#[derive(Debug, TypeUuid, TypePath)]
#[uuid = "f652caee-aace-49da-9ea3-a314de33b38f"]
#[derive(Asset, Debug, TypePath)]
pub struct Room {
pub scene: Handle<Scene>,
pub meshes: Vec<RoomMesh>,
// pub entity_meshes: Vec<RoomMesh>,
}

#[derive(Debug, TypeUuid, TypePath)]
#[uuid = "2d373e3d-cb2a-468c-ab25-2724f624dd2f"]
#[derive(Asset, Debug, TypePath)]
pub struct RoomMesh {
pub mesh: Handle<Mesh>,
pub material: Handle<StandardMaterial>,
Expand Down
128 changes: 58 additions & 70 deletions bevy_rmesh/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,59 @@ use std::path::Path;

use crate::{Room, RoomMesh};
use anyhow::Result;
use bevy::asset::AssetPath;
use bevy::asset::{AssetLoader, LoadContext, LoadedAsset};
use bevy::asset::io::Reader;
use bevy::asset::AsyncReadExt;
use bevy::asset::{AssetLoader, LoadContext};
use bevy::prelude::*;
use bevy::render::renderer::RenderDevice;
use bevy::render::texture::{CompressedImageFormats, ImageType};
use bevy::render::primitives::Aabb;
use bevy::render::texture::{CompressedImageFormats, ImageSampler, ImageType};
use bevy::render::{
mesh::{Indices, Mesh},
render_resource::PrimitiveTopology,
};
use bevy::utils::BoxedFuture;
use rmesh::{read_rmesh, ROOM_SCALE};

pub struct RMeshLoader {
supported_compressed_formats: CompressedImageFormats,
pub(crate) supported_compressed_formats: CompressedImageFormats,
}

impl AssetLoader for RMeshLoader {
type Asset = Room;
type Settings = ();
type Error = anyhow::Error;
fn load<'a>(
&'a self,
bytes: &'a [u8],
reader: &'a mut Reader,
_settings: &'a (),
load_context: &'a mut LoadContext,
) -> BoxedFuture<'a, Result<()>> {
Box::pin(
async move { load_rmesh(bytes, load_context, self.supported_compressed_formats).await },
)
) -> bevy::utils::BoxedFuture<'a, Result<Room, Self::Error>> {
Box::pin(async move {
let mut bytes = Vec::new();
reader.read_to_end(&mut bytes).await?;
load_rmesh(self, &bytes, load_context).await
})
}

fn extensions(&self) -> &[&str] {
&["rmesh"]
}
}

impl FromWorld for RMeshLoader {
fn from_world(world: &mut World) -> Self {
let supported_compressed_formats = match world.get_resource::<RenderDevice>() {
Some(render_device) => CompressedImageFormats::from_features(render_device.features()),
None => CompressedImageFormats::all(),
};
Self {
supported_compressed_formats,
}
}
}

/// Loads an entire rmesh file.
async fn load_rmesh<'a, 'b>(
loader: &RMeshLoader,
bytes: &'a [u8],
load_context: &'a mut LoadContext<'b>,
supported_compressed_formats: CompressedImageFormats,
) -> Result<()> {
) -> Result<Room> {
let header = read_rmesh(bytes)?;

let mut meshes = vec![];
// let mut entity_meshes = vec![];

for i in 0..header.meshes.len() {
let mesh = &header.meshes[i];
let mut result_mesh = Mesh::new(PrimitiveTopology::TriangleList);
for (i, complex_mesh) in header.meshes.iter().enumerate() {
let mut mesh = Mesh::new(PrimitiveTopology::TriangleList);

let positions: Vec<_> = mesh
let positions: Vec<_> = complex_mesh
.vertices
.iter()
.map(|v| {
Expand All @@ -73,7 +66,7 @@ async fn load_rmesh<'a, 'b>(
})
.collect();

let tex_coords: Vec<_> = mesh
let tex_coords: Vec<_> = complex_mesh
.vertices
.iter()
.flat_map(|v| {
Expand All @@ -83,49 +76,43 @@ async fn load_rmesh<'a, 'b>(
]
})
.collect();
let indices = mesh
let indices = complex_mesh
.triangles
.iter()
.flat_map(|strip| strip.iter().rev().copied())
.collect();
result_mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
result_mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, tex_coords);
result_mesh.set_indices(Some(Indices::U32(indices)));
result_mesh.duplicate_vertices();
result_mesh.compute_flat_normals();
mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, positions);
mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, tex_coords);
mesh.set_indices(Some(Indices::U32(indices)));
mesh.duplicate_vertices();
mesh.compute_flat_normals();

let mesh_handle =
load_context.set_labeled_asset(&format!("Mesh{0}", i), LoadedAsset::new(result_mesh));
let mesh = load_context.add_labeled_asset(format!("Mesh{0}", i), mesh);

let base_color_texture = if let Some(path) = &mesh.textures[1].path {
let base_color_texture = if let Some(path) = &complex_mesh.textures[1].path {
let texture = load_texture(
&String::from(path),
load_context,
supported_compressed_formats,
loader.supported_compressed_formats,
)
.await?;
Some(
load_context
.set_labeled_asset(&format!("Texture{0}", i), LoadedAsset::new(texture)),
)
Some(load_context.add_labeled_asset(format!("Texture{0}", i), texture))
} else {
None
};

let material = load_context.set_labeled_asset(
&format!("Material{0}", i),
LoadedAsset::new(StandardMaterial {
let material = load_context.add_labeled_asset(
format!("Material{0}", i),
StandardMaterial {
base_color_texture,
..Default::default()
}),
},
);

meshes.push(RoomMesh {
mesh: mesh_handle,
material,
});
meshes.push(RoomMesh { mesh, material });
}

// TODO: add setting if we want to load models with "x"
// for entity in &header.entities {
// if let Some(rmesh::EntityType::Model(data)) = &entity.entity_type {
// let name = &String::from(data.name.clone());
Expand Down Expand Up @@ -159,21 +146,26 @@ async fn load_rmesh<'a, 'b>(

let scene = {
let mut world = World::default();
let mut scene_load_context = load_context.begin_labeled_asset();

world
.spawn(SpatialBundle::INHERITED_IDENTITY)
.with_children(|parent| {
for i in 0..header.meshes.len() {
let mesh_label = format!("Mesh{0}", i);
let mesh_asset_path =
AssetPath::new_ref(load_context.path(), Some(&mesh_label));
let mat_label = format!("Material{0}", i);
let mat_asset_path = AssetPath::new_ref(load_context.path(), Some(&mat_label));
parent.spawn(PbrBundle {
mesh: load_context.get_handle(mesh_asset_path),
material: load_context.get_handle(mat_asset_path),
let mut mesh_entity = parent.spawn(PbrBundle {
mesh: scene_load_context.get_label_handle(&mesh_label),
material: scene_load_context.get_label_handle(&mat_label),
..Default::default()
});
let complex_mesh = &header.meshes[i];
if let Some((min, max)) = rmesh::calculate_bounds(&complex_mesh.vertices) {
mesh_entity.insert(Aabb::from_min_max(
Vec3::from_slice(&min),
Vec3::from_slice(&max),
));
}
}
for entity in header.entities {
if let Some(entity_type) = entity.entity_type {
Expand Down Expand Up @@ -225,11 +217,7 @@ async fn load_rmesh<'a, 'b>(
rmesh::EntityType::Model(data) => {
let name = &String::from(data.name.clone());
let mesh_label = format!("EntityMesh{0}", name);
let mesh_asset_path =
AssetPath::new_ref(load_context.path(), Some(&mesh_label));
let mat_label = format!("EntityMaterial{0}", name);
let mat_asset_path =
AssetPath::new_ref(load_context.path(), Some(&mat_label));

parent.spawn(PbrBundle {
transform: Transform {
Expand All @@ -252,8 +240,8 @@ async fn load_rmesh<'a, 'b>(
)
.into(),
},
mesh: load_context.get_handle(mesh_asset_path),
material: load_context.get_handle(mat_asset_path),
mesh: scene_load_context.get_label_handle(&mesh_label),
material: scene_load_context.get_label_handle(&mat_label),
..Default::default()
});
}
Expand All @@ -263,21 +251,20 @@ async fn load_rmesh<'a, 'b>(
}
});

load_context.set_labeled_asset("Scene", LoadedAsset::new(Scene::new(world)))
let loaded_scene = scene_load_context.finish(Scene::new(world), None);
load_context.add_loaded_labeled_asset("Scene", loaded_scene)
};

load_context.set_default_asset(LoadedAsset::new(Room {
Ok(Room {
scene,
// entity_meshes,
meshes,
}));

Ok(())
})
}

async fn load_texture<'a>(
path: &str,
load_context: &LoadContext<'a>,
load_context: &mut LoadContext<'a>,
supported_compressed_formats: CompressedImageFormats,
) -> Result<Image> {
let parent = load_context.path().parent().unwrap();
Expand All @@ -292,5 +279,6 @@ async fn load_texture<'a>(
image_type,
supported_compressed_formats,
true,
ImageSampler::Default,
)?)
}
6 changes: 3 additions & 3 deletions rmesh/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rmesh"
version = "0.3.2"
version = "0.3.3"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A parser for the rmesh extension"
Expand All @@ -10,8 +10,8 @@ repository = "https://github.com/scpcbredux/rmesh/"
readme = "../README.md"

[dependencies]
thiserror = "1.0.38"
binrw = "0.11.1"
thiserror = "1.0.51"
binrw = "0.13.3"

[[example]]
name = "read"
Expand Down
32 changes: 32 additions & 0 deletions rmesh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,35 @@ pub fn write_rmesh(header: &Header) -> Result<Vec<u8>, RMeshError> {

Ok(bytes)
}

/// Used for aabb calc
pub fn calculate_bounds(vertices: &Vec<Vertex>) -> Option<([f32; 3], [f32; 3])> {
if vertices.is_empty() {
return None;
}

let mut min_x = f32::INFINITY;
let mut min_y = f32::INFINITY;
let mut min_z = f32::INFINITY;
let mut max_x = f32::NEG_INFINITY;
let mut max_y = f32::NEG_INFINITY;
let mut max_z = f32::NEG_INFINITY;

for vertex in vertices {
let [x, y, z] = vertex.position;

// Update min values
min_x = min_x.min(x);
min_y = min_y.min(y);
min_z = min_z.min(z);

// Update max values
max_x = max_x.max(x);
max_y = max_y.max(y);
max_z = max_z.max(z);
}

let min_point = [min_x, min_y, min_z];
let max_point = [max_x, max_y, max_z];
Some((min_point, max_point))
}

0 comments on commit a59ca68

Please sign in to comment.