Files
vulkan-test-rs/src/common/scene.rs
T

120 lines
4.2 KiB
Rust

use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::io::Read;
use std::path::Path;
/// A scene, which holds all information required to render a world.
///
/// This includes all mesh information, and all the information that is required to place
/// instances of these meshes in the world.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[repr(C)]
pub struct Scene {
/// The name of the scene.
pub name: String,
/// The collection of paths to the meshes, relative to the current scene file.
/// This collection is keyed on the ID of the mesh, which can be used later on to place
/// the meshes in the scenes with specific instances. This ID will also be used to register
/// the meshes with the application's registries, which will allow the program to associate
/// specific behavior with the mesh.
pub mesh_paths: HashMap<String, String>,
/// A collection of instances that are keyed by the mesh's IDs. Each instance can be used to
/// place a [`Mesh`] in the rendered scene.
pub instances: HashMap<String, SceneInstance>,
}
impl Scene {
/// Converts this `Scene` into its byte representation.
pub fn bytes(&self) -> Vec<u8> {
postcard::to_allocvec(self).expect("Couldn't serialize scene")
}
/// Reads a `scene` from the given path. Automatically resolves all paths for the meshes that
/// are referenced in the struct.
///
/// Reading a scene file allows for all meshes to be loaded at once, which therefore allows
/// the program to allocate pipeline buffers just once during the load of this. It would likely
/// be reasonable to create a `MeshRegistry` and `InstanceRegistry` at the same time, with
/// respect to a single loaded `Scene`.
pub fn from_file<P: AsRef<Path>>(path: P) -> Self {
let path = path.as_ref();
let mut file = std::fs::File::open(path).unwrap();
let mut contents = Vec::new();
let _ = file.read_to_end(&mut contents).unwrap();
let scene: Self =
postcard::from_bytes(contents.as_slice()).expect("Couldn't deserialize scene");
Self::resolve_paths(path, scene)
}
pub fn from_file_json<P: AsRef<Path>>(path: P) -> Self {
let path = path.as_ref();
let file = std::fs::File::open(path).unwrap();
let scene: Self = serde_json::from_reader(file).unwrap();
Self::resolve_paths(path, scene)
}
fn resolve_paths(scene_path: &Path, mut scene: Self) -> Self {
let base = scene_path.parent().unwrap_or(&Path::new("."));
scene.mesh_paths = scene
.mesh_paths
.into_iter()
.map(|(id, mesh_path)| {
let res = base.join(mesh_path);
let str = res.to_str().unwrap_or_default();
(id, str.to_string())
})
.collect::<HashMap<String, String>>();
scene
}
}
/// Contains all information about an instance in the scene, specifically the name of the mesh it
/// should use and the translation, rotation, and scale of it.
///
/// After deserialization, this struct should be converted into an [`Instance`], which contains
/// defined vectors that can be used to work with the object before sending it to the shaders.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[repr(C)]
pub struct SceneInstance {
pub translation: [f32; 3],
pub rotation: [f32; 3],
pub scale: [f32; 3],
}
#[cfg(test)]
pub mod create_files {
use std::error::Error;
use std::io::Write;
pub use super::*;
#[test]
pub fn create_test_scene() -> Result<(), Box<dyn Error>> {
let scene = Scene {
name: "Testing Scene".to_string(),
mesh_paths: HashMap::from([
("venator".to_string(), "Venator.obj".to_string())
]),
instances: HashMap::from([
("venator".to_string(), SceneInstance {
translation: [0.0, 0.0, 0.0],
rotation: [0.0, 0.0, 0.0],
scale: [1.0, 1.0, 1.0]
})
])
};
let json = serde_json::to_string_pretty(&scene)?;
let mut file = std::fs::File::create("test_scene.scene")?;
file.write_all(json.as_bytes())?;
Ok(())
}
}