FyroxEngine / Fyrox

3D and 2D game engine written in Rust
https://fyrox.rs
MIT License
7.49k stars 340 forks source link

Feature: ResourceIo system #549

Closed jacobtread closed 8 months ago

jacobtread commented 8 months ago

Description

Sorry in advance for the large commit

Warning This is a breaking change, any custom resource loaders will need to be extended as I modified the arguments for load. Things that read files such as DataSource require an additional argument for the resource_io to use (I've updated the examples, tests, and editor to just use FsResourceIo)

This is basically a virtual file system implementation

I have swapped out a majority of the resource handling code (I think there's still some code that's using WalkDir however thats in the editor code only so I don't think its nessicary to port it) with an interface that I have added to the ResourceManager named ResourceIo, this is a similar abstraction to what the Bevy engine uses and abstracts the file reading and exist checking operations away from the std::fs types.

The interface provides load_file which reads the entire bytes of the file, file_reader which opens the file for streamed reading (The fallback for android and wasm just uses a cursor wrapped around the bytes from load_file) and exists which returns whether the path exists or not, walk_directory which provides directory walking (Uses walkdir but is no-op for android and wasm as im not sure if that can be done easily on those platforms) , is_file which checks if the provided path is a file, and is_dir which checks if the provided path is a directory (Defaulting this to false on wasm as I don't know if that can be checked reliably) read_directory which provides a list of paths within a directory (currently no-op for android and wasm but an android impl is likely possible just will require some extra work)

I have created a default implementation called FsResourceIo which is just an abstraction over your existing io functions from fyrox_core::io along with some new ones that I've added image

I went through and replaced most of the existing usages of the functions from fyrox_core::io with usages of the abstraction

Reason

This abstraction allows resources to be loaded from custom and packed file formats rather than having resources only and always loaded from file system paths. My use case for this is the game uses a custom packed file format to store all of its assets so I can use this feature to seek and read within the packed file format without having to extract it. It also enables the usage of zip files for storing assets if someone wants to make a ResourceIo for that purpose.

Below is my example for the custom packed asset format ResourceIo: image

Then swapping the default resource_io: image

Changes

Related issues

Not implemented

I have not implemented the system fully into the editor, the editor just uses FsResourceIo for all the operations that require it as it will likely require some extra ui and such for including other formats into the editor (Not sure how youd go about that)

Resource Loader Differences

Previous

#[derive(Eq, PartialEq, Debug)]
struct MyResourceLoader;

impl ResourceLoader for MyResourceLoader {
    fn extensions(&self) -> &[&str] {
        &[]
    }

    fn data_type_uuid(&self) -> Uuid {
        Default::default()
    }

    fn load(
        &self,
        resource: UntypedResource,
        event_broadcaster: ResourceEventBroadcaster,
        reload: bool,
    ) -> BoxedLoaderFuture {
        Box::pin(async move {
            let bytes = fyrox_core::io::load_file(resource.path().as_ref()).await.unwrap();
            // ... do something with bytes
            Ok(())
        });
    }
}

New

#[derive(Eq, PartialEq, Debug)]
struct MyResourceLoader;

impl ResourceLoader for MyResourceLoader {
    fn extensions(&self) -> &[&str] {
        &[]
    }

    fn data_type_uuid(&self) -> Uuid {
        Default::default()
    }

    fn load(
        &self,
        resource: UntypedResource,
        event_broadcaster: ResourceEventBroadcaster,
        reload: bool,
        io: Arc<dyn ResourceIo>,
    ) -> BoxedLoaderFuture {
        Box::pin(async move {
            let bytes = io.load_file(resource.path().as_ref()).await.unwrap();
            // ... do something with bytes
            Ok(())
        });
    }
}
mrDIMAS commented 8 months ago

Very nice PR, thank you! The editor could live without VFS for now, so no problems with that. It could be changed later if needed.

Barugon commented 8 months ago

Nice!