Skip to content

Commit

Permalink
More work on asset database
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkleiny committed Apr 2, 2024
1 parent 95a5f76 commit 6da2658
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 21 deletions.
Empty file added modules/assets/assets/test.ase
Empty file.
27 changes: 25 additions & 2 deletions modules/assets/src/exporters.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
use common::OutputStream;
use std::any::{Any, TypeId};

use common::{OutputStream, VirtualPath};

/// An error that can occur when exporting an asset.
#[derive(Debug)]
pub enum AssetExportError {}

/// Exports assets to a specific format.
pub trait AssetExporter: Send + Sync + 'static {
/// The type of asset processed by this exporter.
type Asset;

/// Returns whether this export can export the given asset type and path.
fn can_export(&self, _path: VirtualPath) -> bool {
true
}

/// Exports an asset to the given stream.
fn export(&self, asset: &Self::Asset, stream: &mut dyn OutputStream) -> Result<(), AssetExportError>;
}

/// A trait for exporting untyped assets.
pub trait UntypedAssetExporter: Send + Sync + 'static {
fn can_export(&self, asset_type: TypeId, path: VirtualPath) -> bool;
fn export(&self, asset: &dyn Any, stream: &mut dyn OutputStream) -> Result<(), AssetExportError>;
}

/// Allow any typed asset importer to be used as an untyped asset importer.
impl<A: 'static, T: AssetExporter<Asset = A>> UntypedAssetExporter for T {
fn can_export(&self, asset_type: TypeId, path: VirtualPath) -> bool {
asset_type == TypeId::of::<A>() && self.can_export(path)
}

fn export(&self, asset: &dyn Any, stream: &mut dyn OutputStream) -> Result<(), AssetExportError> {
self.export(asset.downcast_ref::<A>().unwrap(), stream)
}
}
27 changes: 25 additions & 2 deletions modules/assets/src/importers.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
use common::InputStream;
use std::any::{Any, TypeId};

use common::{InputStream, VirtualPath};

/// An error that can occur when exporting an asset.
#[derive(Debug)]
pub enum AssetImportError {}

/// Imports assets from a specific format.
pub trait AssetImporter: Send + Sync + 'static {
/// The type of asset processed by this importer.
type Asset;

/// Returns whether this importer can import the given asset type and path.
fn can_import(&self, _path: VirtualPath) -> bool {
true
}

/// Imports an asset from the given stream.
fn import(&self, data: &mut dyn InputStream) -> Result<Self::Asset, AssetImportError>;
}

/// A trait for importing untyped assets.
pub trait UntypedAssetImporter: Send + Sync + 'static {
fn can_import(&self, asset_type: TypeId, path: VirtualPath) -> bool;
fn import(&self, data: &mut dyn InputStream) -> Result<Box<dyn Any>, AssetImportError>;
}

/// Allow any typed asset importer to be used as an untyped asset importer.
impl<A: 'static, T: AssetImporter<Asset = A>> UntypedAssetImporter for T {
fn can_import(&self, asset_type: TypeId, path: VirtualPath) -> bool {
asset_type == TypeId::of::<A>() && self.can_import(path)
}

fn import(&self, data: &mut dyn InputStream) -> Result<Box<dyn Any>, AssetImportError> {
Ok(Box::new(self.import(data)?))
}
}
96 changes: 83 additions & 13 deletions modules/assets/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Asset management for Surreal.
use std::any::Any;
use std::any::{Any, TypeId};

use common::{Arena, FastHashMap, ToVirtualPath};
use common::{Arena, FastHashMap, FileSystemError, ToVirtualPath};
pub use exporters::*;
pub use importers::*;

Expand Down Expand Up @@ -32,19 +32,46 @@ common::impl_arena_index!(AssetId, "Identifies an asset in an asset database.");
#[derive(Default)]
pub struct AssetDatabase {
// core asset storage
_assets: Arena<AssetId, AssetState>,
assets: Arena<AssetId, AssetState>,

// lookup tables
_assets_by_path: FastHashMap<String, AssetId>,
_assets_by_key: FastHashMap<String, AssetId>,
_assets_by_guid: FastHashMap<String, AssetId>,

// importers/exporters
importers: Vec<Box<dyn UntypedAssetImporter>>,
exporters: Vec<Box<dyn UntypedAssetExporter>>,
}

/// A possible error when working with the asset database.
#[derive(Debug)]
pub enum AssetDatabaseError {
InvalidPath,
InvalidVersion,
NoImporterFound,
NoExporterFound,
FileSystemError(FileSystemError),
FailedToImport(AssetImportError),
FailedToExport(AssetExportError),
}

impl From<FileSystemError> for AssetDatabaseError {
fn from(error: FileSystemError) -> Self {
Self::FileSystemError(error)
}
}

impl From<AssetImportError> for AssetDatabaseError {
fn from(error: AssetImportError) -> Self {
Self::FailedToImport(error)
}
}

impl From<AssetExportError> for AssetDatabaseError {
fn from(error: AssetExportError) -> Self {
Self::FailedToExport(error)
}
}

/// Represents the internal state of an asset.
Expand All @@ -56,15 +83,54 @@ pub enum AssetState {

impl AssetDatabase {
/// Opens the asset database at the given path.
pub fn open(path: impl ToVirtualPath) -> Result<Self, AssetDatabaseError> {
let _path = path.to_virtual_path();
pub fn open(_path: impl ToVirtualPath) -> Result<Self, AssetDatabaseError> {
// TODO: make this actually open the database
Ok(Self::default())
}

todo!()
/// Adds an importer to the database.
pub fn add_importer(&mut self, importer: impl UntypedAssetImporter + 'static) {
self.importers.push(Box::new(importer));
}

/// Adds an exporter to the database.
pub fn add_exporter(&mut self, exporter: impl UntypedAssetExporter + 'static) {
self.exporters.push(Box::new(exporter));
}

/// Gets an asset from the database, or loads it from the file system.
pub fn load<'a, A>(&self, _path: impl ToVirtualPath) -> Asset<A> {
todo!()
pub fn load<A: 'static>(&mut self, path: impl ToVirtualPath) -> Result<Asset<A>, AssetDatabaseError> {
let path = path.to_virtual_path();

for importer in &self.importers {
if importer.can_import(TypeId::of::<A>(), path.clone()) {
let mut stream = path.open_input_stream()?;

let asset = importer.import(&mut stream)?;
let asset_id = self.assets.insert(AssetState::Loaded(asset));

return Ok(Asset::from_id(asset_id));
}
}

Err(AssetDatabaseError::NoImporterFound)
}

/// Exports an asset to the file system.
pub fn export<A: 'static>(&mut self, asset: &A, path: impl ToVirtualPath) -> Result<(), AssetDatabaseError> {
let path = path.to_virtual_path();

for exporter in &self.exporters {
if exporter.can_export(TypeId::of::<A>(), path.clone()) {
let mut stream = path.open_output_stream()?;

exporter.export(asset, &mut stream)?;

return Ok(());
}
}

Err(AssetDatabaseError::NoExporterFound)
}
}

Expand All @@ -78,13 +144,17 @@ impl AssetDatabase {
///
/// This struct is a 'thin' wrapper around the asset, and is cheap to copy.
pub struct Asset<A> {
_id: AssetId,
_kind: std::marker::PhantomData<A>,
id: AssetId,
kind: std::marker::PhantomData<A>,
}

impl<A> Default for Asset<A> {
fn default() -> Self {
todo!()
impl<A> Asset<A> {
/// Creates a new asset reference from an asset ID.
pub const fn from_id(id: AssetId) -> Self {
Self {
id,
kind: std::marker::PhantomData,
}
}
}

Expand Down
26 changes: 26 additions & 0 deletions modules/assets/tests/asset-database.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use common::{InputStream, VirtualPath};
use surreal_assets::{AssetDatabase, AssetImporter, AssetImportError};

#[test]
pub fn it_should_build_a_valid_asset_database() {
let mut database = AssetDatabase::open("assets").unwrap();

database.add_importer(AsepriteFileImporter);

let test = database.load::<AsepriteFile>("assets/test.ase").unwrap();
}

pub struct AsepriteFile;
pub struct AsepriteFileImporter;

impl AssetImporter for AsepriteFileImporter {
type Asset = AsepriteFile;

fn can_import(&self, path: VirtualPath) -> bool {
return path.extension().ends_with("aseprite") || path.extension().ends_with("ase");
}

fn import(&self, data: &mut dyn InputStream) -> Result<Self::Asset, AssetImportError> {
todo!()
}
}
1 change: 0 additions & 1 deletion modules/audio/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ fn main() {
.build();

println!("cargo:rustc-link-search={}", build_dir.join("build").display());

println!(
"cargo:rustc-link-search={}",
build_dir.join("build").join("Release").display()
Expand Down
2 changes: 1 addition & 1 deletion modules/graphics/src/opengl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ impl GraphicsBackend for OpenGLGraphicsBackend {
}
}

fn texture_blit_to_display(&self, texture: TextureId) -> Result<(), TextureError> {
fn texture_blit_to_display(&self, _texture: TextureId) -> Result<(), TextureError> {
todo!()
}

Expand Down
4 changes: 2 additions & 2 deletions modules/physics/src/simplex/world2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,11 +124,11 @@ impl SimplexWorld2D {
}
}

fn detect_collisions(&self, body_tree: QuadTree<BodyId>) -> Vec<CollisionEvent> {
fn detect_collisions(&self, _body_tree: QuadTree<BodyId>) -> Vec<CollisionEvent> {
todo!()
}

fn integrate_collisions(&self, collision_event: CollisionEvent) {
fn integrate_collisions(&self, _collision_event: CollisionEvent) {
todo!()
}

Expand Down

0 comments on commit 6da2658

Please sign in to comment.