Loading textures from gltf blob and gltf.bin's, fix loading multiple meshses in a single model

This commit is contained in:
SeanOMik 2023-10-21 22:19:34 -04:00
parent fd9f4bee2a
commit 7ae59c0415
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
19 changed files with 399 additions and 147 deletions

44
Cargo.lock generated
View File

@ -1294,6 +1294,7 @@ dependencies = [
"tracing-appender",
"tracing-log",
"tracing-subscriber",
"uuid",
"wgpu",
"winit",
]
@ -1552,7 +1553,7 @@ checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
dependencies = [
"num-integer",
"num-traits",
"rand",
"rand 0.4.6",
"rustc-serialize",
]
@ -1867,6 +1868,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-easy"
version = "0.3.0"
@ -1934,6 +1941,27 @@ dependencies = [
"winapi",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
@ -1949,6 +1977,15 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "range-alloc"
version = "0.1.3"
@ -2483,11 +2520,12 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "uuid"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
dependencies = [
"getrandom",
"rand 0.8.5",
]
[[package]]

View File

@ -36,3 +36,4 @@ aligned-vec = "0.5.0"
tracing-appender = "0.2.2"
stopwatch = "0.0.7"
petgraph = "0.6.4"
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }

View File

@ -1333,6 +1333,7 @@ dependencies = [
"tracing-appender",
"tracing-log",
"tracing-subscriber",
"uuid",
"wgpu",
"winit",
]
@ -1581,7 +1582,7 @@ checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
dependencies = [
"num-integer",
"num-traits",
"rand",
"rand 0.4.6",
"rustc-serialize",
]
@ -1877,6 +1878,12 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-easy"
version = "0.3.0"
@ -1944,6 +1951,27 @@ dependencies = [
"winapi",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core 0.6.4",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.3.1"
@ -1959,6 +1987,15 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "range-alloc"
version = "0.1.3"
@ -2508,11 +2545,12 @@ checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
[[package]]
name = "uuid"
version = "1.4.1"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc"
dependencies = [
"getrandom",
"rand 0.8.5",
]
[[package]]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,137 @@
{
"asset":{
"generator":"Khronos glTF Blender I/O v3.6.6",
"version":"2.0"
},
"scene":0,
"scenes":[
{
"name":"Scene",
"nodes":[
0
]
}
],
"nodes":[
{
"mesh":0,
"name":"Cube"
}
],
"materials":[
{
"doubleSided":true,
"name":"Material",
"pbrMetallicRoughness":{
"baseColorTexture":{
"index":0
},
"metallicFactor":0,
"roughnessFactor":0.5
}
}
],
"meshes":[
{
"name":"Cube",
"primitives":[
{
"attributes":{
"POSITION":0,
"TEXCOORD_0":1,
"NORMAL":2
},
"indices":3,
"material":0
}
]
}
],
"textures":[
{
"sampler":0,
"source":0
}
],
"images":[
{
"mimeType":"image/png",
"name":"uvgrid",
"uri":"uvgrid.png"
}
],
"accessors":[
{
"bufferView":0,
"componentType":5126,
"count":24,
"max":[
1,
1,
1
],
"min":[
-1,
-1,
-1
],
"type":"VEC3"
},
{
"bufferView":1,
"componentType":5126,
"count":24,
"type":"VEC2"
},
{
"bufferView":2,
"componentType":5126,
"count":24,
"type":"VEC3"
},
{
"bufferView":3,
"componentType":5123,
"count":36,
"type":"SCALAR"
}
],
"bufferViews":[
{
"buffer":0,
"byteLength":288,
"byteOffset":0,
"target":34962
},
{
"buffer":0,
"byteLength":192,
"byteOffset":288,
"target":34962
},
{
"buffer":0,
"byteLength":288,
"byteOffset":480,
"target":34962
},
{
"buffer":0,
"byteLength":72,
"byteOffset":768,
"target":34963
}
],
"samplers":[
{
"magFilter":9729,
"minFilter":9987
}
],
"buffers":[
{
"byteLength":840,
"uri":"texture-sep.bin"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -75,35 +75,18 @@ async fn main() {
let mut resman = world.get_resource_mut::<ResourceManager>().unwrap();
let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
//let cube_model = resman.request::<Model>("assets/cube-embedded.gltf").unwrap();
let cube_model = resman.request::<Model>("assets/cube-texture-embedded.gltf").unwrap();
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
let cube_model = resman.request::<Model>("assets/texture-sep/texture-sep.gltf").unwrap();
drop(resman);
/* world.spawn((MeshComponent::new(
Mesh {
vertices: VERTICES.to_vec(),
indices: Some(INDICES.to_vec())
}, Material {
shader_id: 0,
texture: diffuse_texture.clone()
}),
TransformComponent::from(Transform::from_xyz(0.005, 0.0, -2.0)),
));
world.spawn((MeshComponent::new(
Mesh {
vertices: VERTICES.to_vec(),
indices: Some(INDICES.to_vec())
}, Material {
shader_id: 0,
texture: diffuse_texture
}),
TransformComponent::from(Transform::from_xyz(0.005, 0.7, -0.5)),
/* world.spawn((
ModelComponent(cube_model.clone()),
TransformComponent::from(Transform::from_xyz(3.0, 0.5, -2.2)),
)); */
world.spawn((
ModelComponent(cube_model),
TransformComponent::from(Transform::from_xyz(0.005, 0.5, -2.2)),
ModelComponent(antique_camera_model),
TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)),
));
let mut camera = CameraComponent::new_3d();
@ -115,7 +98,6 @@ async fn main() {
Ok(())
};
//world.insert_resource(fps_counter::FPSCounter::new());
let fps_system = |world: &mut World| -> anyhow::Result<()> {
let mut counter: RefMut<fps_counter::FPSCounter> = world.get_resource_mut().unwrap();

View File

@ -27,6 +27,17 @@ impl From<ModelLoaderError> for LoaderError {
}
}
pub(crate) struct GltfLoadContext<'a> {
pub resource_manager: &'a mut ResourceManager,
pub gltf: &'a gltf::Gltf,
/// Path to the gltf
pub gltf_path: &'a str,
/// The path to the directory that the gltf is contained in.
pub gltf_parent_path: &'a str,
/// List of buffers in the gltf
pub buffers: &'a Vec<Vec<u8>>,
}
#[derive(Default)]
pub struct ModelLoader;
@ -59,7 +70,10 @@ impl ModelLoader {
// read the positions
if let Some(pos) = reader.read_positions() {
debug!("Mesh mode is {:?}", prim.mode());
if prim.mode() != gltf::mesh::Mode::Triangles {
todo!("Load position primitives that aren't triangles"); // TODO
}
let pos: Vec<glam::Vec3> = pos.map(|t| t.into()).collect();
new_mesh.add_attribute(MeshVertexAttribute::Position, VertexAttributeData::Vec3(pos));
}
@ -114,7 +128,7 @@ impl ModelLoader {
impl ResourceLoader for ModelLoader {
fn extensions(&self) -> &[&str] {
&[
"gltf"
"gltf", "glb"
]
}
@ -132,11 +146,27 @@ impl ResourceLoader for ModelLoader {
parent_path.pop();
let parent_path = parent_path.display().to_string();
/* let (document, buffers, images) = gltf::import(&path)?;
let buffers: Vec<Vec<u8>> = buffers.into_iter().map(|b| b.0).collect();
let scene = document.scenes().next().unwrap();
let materials: Vec<Material> = document.materials()
.map(|mat| Material::from_gltf(resource_manager, &parent_path, mat)).collect();
let meshes: Vec<Mesh> = scene.nodes()
.map(|node| self.process_node(&buffers, &materials, node))
.flatten().collect(); */
let gltf = gltf::Gltf::open(path)?;
let mut use_bin = false;
let buffers: Vec<Vec<u8>> = gltf.buffers().map(|b| match b.source() {
gltf::buffer::Source::Bin => gltf.blob.as_deref().map(|v| v.to_vec())
.ok_or(ModelLoaderError::MissingBin(path.to_string())),
gltf::buffer::Source::Bin => {
use_bin = true;
gltf.blob.as_deref().map(|v| v.to_vec())
.ok_or(ModelLoaderError::MissingBin(path.to_string()))
},
gltf::buffer::Source::Uri(uri) => util::gltf_read_buffer_uri(&parent_path, uri)
.map_err(|e| ModelLoaderError::UriDecodingError(e)),
}).flatten().collect();
@ -144,13 +174,21 @@ impl ResourceLoader for ModelLoader {
// TODO: Read in multiple scenes
let scene = gltf.scenes().next().unwrap();
// Load the materials
let mut context = GltfLoadContext {
resource_manager,
gltf: &gltf,
gltf_path: path,
gltf_parent_path: &parent_path,
buffers: &buffers,
};
let materials: Vec<Material> = gltf.materials()
.map(|mat| Material::from_gltf(resource_manager, &parent_path, mat)).collect();
.map(|mat| Material::from_gltf(&mut context, mat)).collect();
let meshes: Vec<Mesh> = scene.nodes()
.map(|node| self.process_node(&buffers, &materials, node))
.flatten().collect();
debug!("Loaded {} meshes, and {} materials from '{}'", meshes.len(), materials.len(), path);
Ok(Arc::new(Resource::with_data(path, Model::new(meshes))))
}

View File

@ -1,6 +1,6 @@
use std::{fs::File, io::{BufReader, Read}, collections::hash_map::DefaultHasher, hash::{Hash, Hasher}};
use crate::{Texture, ResHandle, ResourceManager, util};
use crate::{Texture, ResHandle, ResourceManager, util, loader::model::GltfLoadContext};
/// PBR metallic roughness
#[derive(Clone, Debug, Default)]
@ -159,10 +159,11 @@ impl Material {
}
}
fn read_source(resource_manager: &mut ResourceManager, gltf_rel_path: &str, src: gltf::image::Source) -> Result<Vec<u8>, util::UriReadError> {
fn read_source(context: &mut GltfLoadContext, src: gltf::image::Source) -> Result<Vec<u8>, util::UriReadError> {
let gltf_rel_path = context.gltf_parent_path;
// TODO: Don't copy sources
match src {
gltf::image::Source::View { view, mime_type } => {
gltf::image::Source::View { view, mime_type: _ } => {
let buf = view.buffer();
let src = buf.source();
@ -170,7 +171,12 @@ impl Material {
let len = view.length();
match src {
gltf::buffer::Source::Bin => todo!("Read material source from gltf Bin"),
gltf::buffer::Source::Bin => {
let mut b = context.gltf.blob.clone().unwrap();
b.drain(0..offset);
b.truncate(len);
Ok(b)
},
gltf::buffer::Source::Uri(uri) => {
util::gltf_read_buffer_uri(gltf_rel_path, uri)
.map(|mut buf| {
@ -181,23 +187,23 @@ impl Material {
}
}
},
gltf::image::Source::Uri { uri, mime_type } => {
gltf::image::Source::Uri { uri, mime_type: _ } => {
util::gltf_read_buffer_uri(gltf_rel_path, uri)
},
}
}
fn load_texture(resource_manager: &mut ResourceManager, gltf_rel_path: &str, texture_info: gltf::texture::Info<'_>) -> ResHandle<Texture> {
fn load_texture(context: &mut GltfLoadContext, texture_info: gltf::texture::Info<'_>) -> ResHandle<Texture> {
// TODO: texture_info.tex_coord()
let tex = texture_info.texture();
let img = tex.source();
let src = img.source();
let buf = Material::read_source(resource_manager, gltf_rel_path, src).unwrap();
let buf = Material::read_source(context, src).unwrap();
let buflen = buf.len();
let mime_type = infer::get(&buf).expect("Failed to get file type").mime_type();
resource_manager.load_bytes::<Texture>(&uuid::Uuid::new_v4().to_string(), mime_type,
context.resource_manager.load_bytes::<Texture>(&uuid::Uuid::new_v4().to_string(), mime_type,
buf, 0, buflen).unwrap()
}
@ -205,21 +211,17 @@ impl Material {
///
/// `gltf_rel_path`: The relative path of the gltf file,
/// e.g. gltf model path is "resource/models/player.gltf", the relative path would be "resource/models/"
pub fn from_gltf(resource_manager: &mut ResourceManager, gltf_rel_path: &str, gltf_mat: gltf::Material) -> Self {
pub(crate) fn from_gltf(context: &mut GltfLoadContext, gltf_mat: gltf::Material) -> Self {
let pbr_rough = gltf_mat.pbr_metallic_roughness();
let base_color = pbr_rough.base_color_factor().into();
let metallic = pbr_rough.metallic_factor();
let roughness = pbr_rough.roughness_factor();
let base_color_texture = pbr_rough.base_color_texture()
.map(|info| Material::load_texture(resource_manager, gltf_rel_path, info));
.map(|info| Material::load_texture(context, info));
let metallic_roughness_texture = pbr_rough.metallic_roughness_texture()
.map(|info| Material::load_texture(resource_manager, gltf_rel_path, info));
/* let base_color_texture = if let Some(base_tex_info) = pbr_rough.base_color_texture() {
Some(Material::load_texture(resource_manager, gltf_rel_path, base_tex_info))
} else { None }; */
.map(|info| Material::load_texture(context, info));
Material {
name: gltf_mat.name()

View File

@ -77,13 +77,25 @@ pub enum MeshVertexAttribute {
Other(String),
}
#[derive(Clone, Default, edict::Component)]
#[derive(Clone, edict::Component)]
pub struct Mesh {
pub uuid: uuid::Uuid,
pub attributes: HashMap<MeshVertexAttribute, VertexAttributeData>,
pub indices: Option<MeshIndices>,
material: Option<Material>,
}
impl Default for Mesh {
fn default() -> Self {
Self {
uuid: uuid::Uuid::new_v4(),
attributes: Default::default(),
indices: Default::default(),
material: Default::default()
}
}
}
impl Mesh {
pub fn add_attribute(&mut self, attribute: MeshVertexAttribute, data: VertexAttributeData) {
self.attributes.insert(attribute, data);

View File

@ -19,20 +19,23 @@ pub enum UriReadError {
///
/// * `containing_path`: The path of the containing folder of the buffers "parent",
/// the parent being where this buffer is defined in,
/// i.e. parent="resources/models/player.gltf", containing="reousrce/models"
/// i.e. parent="resources/models/player.gltf", containing="resource/models"
pub(crate) fn gltf_read_buffer_uri(containing_path: &str, uri: &str) -> Result<Vec<u8>, UriReadError> {
let uri = uri.strip_prefix("data").ok_or(UriReadError::None)?;
let (mime, data) = uri.split_once(",").ok_or(UriReadError::None)?;
if let Some((mime, data)) = uri.strip_prefix("data")
.and_then(|uri| uri.split_once(",")) {
let (_mime, is_base64) = match mime.strip_suffix(";base64") {
Some(mime) => (mime, true),
None => (mime, false),
};
if is_base64 {
base64::engine::general_purpose::STANDARD.decode(data).map_err(|e| UriReadError::Base64Decode(e))
base64::engine::general_purpose::STANDARD.decode(data)
.map_err(|e| UriReadError::Base64Decode(e))
} else {
let full_path = format!("{containing_path}/{data}");
Ok(data.as_bytes().to_vec())
}
} else {
let full_path = format!("{containing_path}/{uri}");
std::fs::read(&full_path).map_err(|e| UriReadError::IoError(e))
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

1
rust-toolchain Normal file
View File

@ -0,0 +1 @@
nightly

View File

@ -297,7 +297,7 @@ impl Game {
.with(fmt::layer().with_writer(stdout_layer))
.with(filter::Targets::new()
.with_target("lyra_engine", Level::TRACE)
.with_target("wgpu_core", Level::INFO)
.with_target("wgpu_core", Level::WARN)
.with_default(Level::DEBUG))
.init();

View File

@ -1,3 +1,5 @@
#![feature(hash_extract_if)]
pub mod game;
pub mod render;
pub mod input_event;

View File

@ -2,48 +2,23 @@ use edict::EntityId;
use crate::math::Transform;
//use super::mesh::Mesh;
use lyra_resource::Mesh;
pub struct RenderJob {
mesh: Mesh,
entity: EntityId,
pub entity: EntityId,
pub shader_id: u64,
pub mesh_buffer_id: uuid::Uuid,
transform: Transform,
last_transform: Option<Transform>, // TODO: render interpolation
pub transform: Transform,
pub last_transform: Option<Transform>, // TODO: render interpolation
}
impl RenderJob {
pub fn new(mesh: Mesh, entity: EntityId, transform: Transform, last_transform: Option<Transform>) -> Self {
pub fn new(entity: EntityId, shader_id: u64, mesh_buffer_id: uuid::Uuid, transform: Transform, last_transform: Option<Transform>) -> Self {
Self {
mesh,
entity,
shader_id,
mesh_buffer_id,
transform,
last_transform,
}
}
pub fn mesh(&self)-> &Mesh {
&self.mesh
}
pub fn entity(&self)-> EntityId {
self.entity
}
pub fn transform(&self)-> &Transform {
&self.transform
}
pub fn set_transform(&mut self, transform: Transform){
self.transform = transform;
}
pub fn last_transform(&self)-> Option<&Transform> {
self.last_transform.as_ref()
}
pub fn set_last_transform(&mut self, last_transform: Transform){
self.last_transform = Some(last_transform);
}
}

View File

@ -17,6 +17,7 @@ use crate::ecs::components::camera::CameraComponent;
use crate::ecs::components::mesh::MeshComponent;
use crate::ecs::components::model::ModelComponent;
use crate::ecs::components::transform::TransformComponent;
use crate::math::Transform;
use super::camera::RenderCamera;
use super::desc_buf_lay::DescVertexBufferLayout;
@ -68,13 +69,15 @@ struct TransformBuffers {
impl TransformBuffers {
/// Update an entity's buffer with the new transform. Will panic if the entity isn't stored
fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) {
fn update_entity(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform: glam::Mat4) -> TransformBufferIndices {
let indices = self.not_updated.remove(&entity)
.or_else(|| self.just_updated.remove(&entity))
.expect("Use 'insert_entity' for new entities");
self.just_updated.insert(entity, indices);
let (_, buffer, _) = self.buffer_bindgroups.get(indices.buffer_index).unwrap();
queue.write_buffer(buffer, indices.transform_index as u64 * limits.min_uniform_buffer_offset_alignment as u64, bytemuck::bytes_of(&transform));
indices
}
/// Insert a new entity into the buffer, returns where it was stored.
@ -104,6 +107,22 @@ impl TransformBuffers {
indices
}
/// Update or insert an entities transform
fn update_or_insert<TFn>(&mut self, queue: &wgpu::Queue, limits: &Limits, entity: EntityId, transform_fn: TFn) -> TransformBufferIndices
where TFn: Fn() -> glam::Mat4
{
if self.contains(entity) {
self.update_entity(queue, limits, entity, transform_fn())
} else {
self.insert_entity(queue, limits, entity, transform_fn())
}
}
/// Returns true if the entity's transform is stored (does not mean its up-to-date).
fn contains(&self, entity: EntityId) -> bool {
self.not_updated.contains_key(&entity) || self.just_updated.contains_key(&entity)
}
/// Collect the dead entities, mark entities and not updated for next updates.
fn tick(&mut self) {
// take the dead entities, these were ones that were not updated this tick
@ -111,7 +130,6 @@ impl TransformBuffers {
.map(|t| t.clone()).collect();
self.dead_indices = dead;
// swap just_updated into not_updated
self.not_updated = self.just_updated.clone();
self.just_updated.clear();
}
@ -143,7 +161,8 @@ pub struct BasicRenderer {
pub render_pipelines: HashMap<u64, Arc<FullRenderPipeline>>,
pub render_jobs: VecDeque<RenderJob>,
buffer_storage: HashMap<EntityId, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
mesh_buffers: HashMap<uuid::Uuid, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
entity_meshes: HashMap<EntityId, uuid::Uuid>,
transform_buffers: TransformBuffers,
transform_bind_group_layout: BindGroupLayout,
@ -205,7 +224,7 @@ impl BasicRenderer {
false => surface_caps.present_modes[0]
}; */
println!("present mode: {:?}", present_mode);
debug!("present mode: {:?}", present_mode);
let surface_format = surface_caps.formats.iter()
.copied()
@ -380,7 +399,8 @@ impl BasicRenderer {
},
render_pipelines: HashMap::new(),
render_jobs: VecDeque::new(),
buffer_storage: HashMap::new(),
mesh_buffers: HashMap::new(),
entity_meshes: HashMap::new(),
render_limits,
transform_buffers,
@ -405,17 +425,16 @@ impl BasicRenderer {
s
}
fn update_mesh_buffers(&mut self, entity: EntityId, mesh: &Mesh) {
if let Some(buffers) = self.buffer_storage.get_mut(&entity) {
fn update_mesh_buffers(&mut self, _entity: EntityId, mesh: &Mesh) {
if let Some(buffers) = self.mesh_buffers.get_mut(&mesh.uuid) {
// check if the buffer sizes dont match. If they dont, completely remake the buffers
let vertices = mesh.position().unwrap();
if buffers.buffer_vertex.count() != vertices.len() {
drop(buffers);
debug!("Recreating buffers for mesh");
debug!("Recreating buffers for mesh {}", mesh.uuid.to_string());
let (vert, idx) = self.create_vertex_index_buffers(mesh);
// have to re-get buffers because of borrow checker
let buffers = self.buffer_storage.get_mut(&entity).unwrap();
let buffers = self.mesh_buffers.get_mut(&mesh.uuid).unwrap();
buffers.buffer_indices = idx;
buffers.buffer_vertex = vert;
@ -448,13 +467,11 @@ impl BasicRenderer {
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
debug!("Pos count: {}, tex coords count: {}", positions.len(), tex_coords.len());
assert!(positions.len() == tex_coords.len());
let vertex_inputs: Vec<Vertex> = std::iter::zip(positions, tex_coords.into_iter())
.map(|(v, t)| Vertex::new(v.clone(), t))
.collect();
println!("Vertex inputs: {:?}", vertex_inputs.as_slice());
let vertex_buffer = self.device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
@ -565,6 +582,28 @@ impl BasicRenderer {
buffers.next_indices = indices;
indices
}
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
fn process_mesh(&mut self, entity: EntityId, transform: Transform, mesh: &Mesh) -> bool {
let indices = self.transform_buffers.update_or_insert(&self.queue, &self.render_limits,
entity, || transform.calculate_mat4());
if self.mesh_buffers.contains_key(&mesh.uuid) {
false
} else {
// check if the transform buffers need to be expanded
if self.transform_buffers.should_expand() {
self.expand_transform_buffers();
}
// create the mesh's buffers
let buffers = self.create_mesh_buffers(mesh, indices);
self.mesh_buffers.insert(mesh.uuid, buffers);
self.entity_meshes.insert(entity, mesh.uuid);
true
}
}
}
impl Renderer for BasicRenderer {
@ -574,38 +613,21 @@ impl Renderer for BasicRenderer {
let mut alive_entities = HashSet::new();
for (entity, model, model_epoch, transform) in main_world.query::<(Entities, &ModelComponent, EpochOf<ModelComponent>, &TransformComponent)>().iter() {
let model = model.data.as_ref().unwrap().as_ref();
let model_mesh = model.meshes.first().unwrap();
// Create the render job and push it to the queue
let job = RenderJob::new(model_mesh.clone(), entity, transform.transform, None);
self.render_jobs.push_back(job);
alive_entities.insert(entity);
if self.buffer_storage.get(&entity).is_none() {
// check if the transform buffers need to be expanded
if self.transform_buffers.should_expand() {
self.expand_transform_buffers();
}
let model = model.data.as_ref().unwrap().as_ref();
// insert transform into buffers
let indices = self.transform_buffers.insert_entity(&self.queue, &self.render_limits,
entity, transform.transform.calculate_mat4());
// create the mesh's buffers
let buffers = self.create_mesh_buffers(model_mesh, indices);
self.buffer_storage.insert(entity, buffers);
} else {
// update entity transforms
self.transform_buffers.update_entity(&self.queue, &self.render_limits,
entity, transform.transform.calculate_mat4());
// if the model was updated, update its buffers
for mesh in model.meshes.iter() {
if !self.process_mesh(entity, transform.transform, mesh) {
if model_epoch == last_epoch {
self.update_mesh_buffers(entity, model_mesh);
self.update_mesh_buffers(entity, mesh);
}
}
let shader = mesh.material().shader_uuid.unwrap_or(0);
let job = RenderJob::new(entity, shader, mesh.uuid, transform.transform, None);
self.render_jobs.push_back(job);
}
}
for (entity, mesh, mesh_epoch, transform) in main_world.query::<(Entities, &MeshComponent, EpochOf<MeshComponent>, &TransformComponent)>().iter() {
@ -617,8 +639,12 @@ impl Renderer for BasicRenderer {
// when buffer storage length does not match the amount of iterated entities,
// remove all dead entities, and their buffers, if they weren't iterated over
if self.buffer_storage.len() != alive_entities.len() {
self.buffer_storage.retain(|e, _| alive_entities.contains(e));
if self.mesh_buffers.len() != alive_entities.len() {
let removed_entities: Vec<uuid::Uuid> = self.entity_meshes
.extract_if(|e, _| !alive_entities.contains(e))
.map(|(_, v)| v)
.collect();
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
}
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
@ -663,13 +689,12 @@ impl Renderer for BasicRenderer {
// Pop off jobs from the queue as they're being processed
while let Some(job) = self.render_jobs.pop_front() {
if let Some(pipeline) = self.render_pipelines.get(&job.mesh().material().shader_uuid.unwrap_or(0)) {
if let Some(pipeline) = self.render_pipelines.get(&job.shader_id) {
// specify to use this pipeline
render_pass.set_pipeline(pipeline.get_wgpu_pipeline());
// get the mesh (containing vertices) and the buffers from storage
let mesh = job.mesh();
let buffers = self.buffer_storage.get(&job.entity()).unwrap();
let buffers = self.mesh_buffers.get(&job.mesh_buffer_id).unwrap();
// Bind the optional texture
if let Some(tex) = buffers.texture_bindgroup.as_ref() {
@ -695,13 +720,11 @@ impl Renderer for BasicRenderer {
render_pass.set_index_buffer(indices.buffer().slice(..), idx_type.clone());
render_pass.draw_indexed(0..indices_len, 0, 0..1);
} else {
let vertices = mesh.position().unwrap();
let vertex_count = buffers.buffer_vertex.count();
render_pass.set_vertex_buffer(buffers.buffer_vertex.slot(), buffers.buffer_vertex.buffer().slice(..));
render_pass.draw(0..vertices.len() as u32, 0..1);
render_pass.draw(0..vertex_count as u32, 0..1);
}
} else {
warn!("Failure to find RenderPipeline with shader id of '{}'!", job.mesh().material().shader_uuid.unwrap_or(0));
}
}
}