Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup and document engine-sample.assets #508

Merged
merged 5 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/pages/3_examples/2_engine/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ multiple plugins of the engine:

- @subpage examples-engine-settings - @copybrief examples-engine-settings
- @subpage examples-engine-renderer - @copybrief examples-engine-renderer
- @subpage examples-engine-scene - @copybrief examples-engine-scene
- @subpage examples-engine-scene - @copybrief examples-engine-scene
- @subpage examples-engine-assets - @copybrief examples-engine-assets
3 changes: 2 additions & 1 deletion engine/samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ make_sample(DIR "settings")
make_sample(DIR "events")
make_sample(DIR "systems")
make_sample(DIR "input" ASSETS)
make_sample(DIR "assets/json" ASSETS)
make_sample(DIR "assets/bridge" ASSETS)
make_sample(DIR "assets/json" ASSETS)
make_sample(DIR "assets/saving" ASSETS)
make_sample(DIR "renderer")
make_sample(DIR "collisions" COMPONENTS)
make_sample(DIR "scene" COMPONENTS ASSETS)
Expand Down
39 changes: 23 additions & 16 deletions engine/samples/assets/bridge/main.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
/// @file
/// @brief A sample demonstrating how to create a custom asset bridge.
///
/// This sample demonstrates how we can create a custom asset bridge to load assets of a custom
/// type. In this case, a bridge is created to load text files (.txt) as strings. The bridge is
/// registered with the asset manager, and then a text file is loaded and printed to the console.

#include <cubos/engine/assets/bridges/file.hpp>
#include <cubos/engine/assets/plugin.hpp>
#include <cubos/engine/settings/settings.hpp>
Expand All @@ -15,6 +8,7 @@ using cubos::core::memory::Stream;

using namespace cubos::engine;

/// [TextBridge]
/// This bridge inherits from the FileBridge, since it will be loading/saving assets from/to single
/// files.
class TextBridge : public FileBridge
Expand All @@ -24,7 +18,9 @@ class TextBridge : public FileBridge
: FileBridge(typeid(std::string))
{
}
/// [TextBridge]

/// [TextBridge::loadFromFile]
bool loadFromFile(Assets& assets, const AnyAsset& handle, Stream& stream) override
{
// Dump the file's contents into a string.
Expand All @@ -35,7 +31,9 @@ class TextBridge : public FileBridge
assets.store(handle, std::move(contents));
return true;
}
/// [TextBridge::loadFromFile]

/// [TextBridge::saveToFile]
bool saveToFile(const Assets& assets, const AnyAsset& handle, Stream& stream) override
{
// Get the asset's data.
Expand All @@ -46,35 +44,44 @@ class TextBridge : public FileBridge
return true;
}
};
/// [TextBridge::saveToFile]

// We specify the same UUID as the one defined in the `.meta` file.
static const Asset<std::string> SampleAsset = AnyAsset("6f42ae5a-59d1-5df3-8720-83b8df6dd536");

static void config(Write<Settings> settings)
static void configSystem(Write<Settings> settings)
{
settings->setString("assets.io.path", SAMPLE_ASSETS_FOLDER);
}

static void bridge(Write<Assets> assets)
/// [Registering the bridge]
static void bridgeSystem(Write<Assets> assets)
{
// Add a custom bridge to load .txt files.
assets->registerBridge(".txt", std::make_unique<TextBridge>());
}
/// [Registering the bridge]

static void load(Read<Assets> assets)
/// [Loading the asset]
// Assets are identified through UUIDs which are defined in their .meta files.
static const Asset<std::string> SampleAsset = AnyAsset("6f42ae5a-59d1-5df3-8720-83b8df6dd536");

static void loadSystem(Read<Assets> assets)
{
// Access the text asset - will be loaded automatically.
auto text = assets->read(SampleAsset);
Stream::stdOut.print(*text);
}
/// [Loading the asset]

int main()
{
auto cubos = Cubos();

/// [Configuration]
cubos.addPlugin(assetsPlugin);
cubos.startupSystem(config).tagged("cubos.settings");
cubos.startupSystem(bridge).tagged("cubos.assets.bridge");
cubos.startupSystem(load).tagged("cubos.assets");
cubos.startupSystem(bridgeSystem).tagged("cubos.assets.bridge");
cubos.startupSystem(loadSystem).tagged("cubos.assets");
/// [Configuration]

cubos.startupSystem(configSystem).tagged("cubos.settings");
cubos.run();
return 0;
}
59 changes: 59 additions & 0 deletions engine/samples/assets/bridge/page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Introducion and Custom Bridges {#examples-engine-assets-bridge}

@brief Basic @ref assets-plugin plugin features and creating your own bridges.

This example demonstrates how we can create a custom asset bridge to load
assets of a given type. More specifically, we'll go through how we can create a
bridge which loads `std::string`s from text files.

We define our bridge class by inheriting from @ref cubos::engine::FileBridge,
which offers a simple interface for loading and saving assets from files.

@snippet assets/bridge/main.cpp TextBridge

We pass `typeid(std::string)` to the base class, so that the engine knows which
type of assets this bridge can load. Then, we'll need to implement both the @ref
cubos::engine::FileBridge::loadFromFile "loadFromFile" and @ref
cubos::engine::FileBridge::saveToFile "saveToFile" methods.

In the first method, we receive the @ref cubos::engine::Assets
"assets manager" where we should store the loaded data, the handle to the asset
we're loading, and a stream to read the file data from.

@snippet assets/bridge/main.cpp TextBridge::loadFromFile

In the second method, we receive the @ref cubos::engine::Assets
"assets manager", the handle to the asset we're saving, and a stream to write
the file data to.

@snippet assets/bridge/main.cpp TextBridge::saveToFile

Now that we have our bridge type, we must register it with the assets manager
before using it.

@snippet assets/bridge/main.cpp Registering the bridge

After this system runs, any time we load an asset whose path ends with `.txt`,
the assets manager will use our bridge to load it.

In this sample we have a file `sample.txt` on the `assets/` directory containing the following text:

@include assets/bridge/assets/sample.txt

We also have a file `sample.txt.meta`, which describes the asset for the
engine. In this case, we only need to specify its UUID, which was generated on
a [UUID generator website](https://www.uuidgenerator.net/):

@include assets/bridge/assets/sample.txt.meta

Then, we can load it from our code:

@snippet assets/bridge/main.cpp Loading the asset

Some care must be taken when registering bridges or loading assets during the
startup phase. Systems which add bridges should be tagged with
`cubos.assets.bridge` so that they run before any assets are loaded.
Similarly, startup systems which load assets should be tagged with
`cubos.assets` so that they run after all bridges have been registered.

@snippet assets/bridge/main.cpp Configuration
6 changes: 6 additions & 0 deletions engine/samples/assets/json/assets/sample.strings
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"strings": [
"Hello",
"World"
]
}
3 changes: 3 additions & 0 deletions engine/samples/assets/json/assets/sample.strings.meta
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"id": "6f42ae5a-59d1-5df3-8720-83b8df6dd536"
}
98 changes: 30 additions & 68 deletions engine/samples/assets/json/main.cpp
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
/// @file
/// @brief A sample demonstrating how to save and load JSON assets.
///
/// This sample demonstrates how we can use the JSONBridge to easily save and load serializable
/// assets as JSON files. If we had a serializable asset and we wanted to store it as binary, we
/// could simply replace the JSONBridge with a BinaryBridge and the sample would work the same way.
///
/// In this sample, we will be creating an asset of type MyAsset, saving it to a JSON file, and
/// then loading it back from the file.

#include <vector>

#include <cubos/core/data/fs/file_system.hpp>
Expand All @@ -25,95 +15,67 @@ using cubos::core::memory::Stream;

using namespace cubos::engine;

/// [Asset type]
/// A simple serializable type which we will be saving and loading.
struct MyAsset
struct Strings
{
int an_integer;
std::vector<std::string> some_strings;
std::vector<std::string> strings;
};
/// [Asset type]

// We must implement the cubos::core::data::serialize function for our type, so that it can be
// serialized by the JSONBridge.
/// [Serialization definition]
template <>
void cubos::core::data::serialize<MyAsset>(Serializer& ser, const MyAsset& obj, const char* name)
void cubos::core::data::serialize<Strings>(Serializer& ser, const Strings& obj, const char* name)
{
ser.beginObject(name);
ser.write(obj.an_integer, "an_integer");
ser.write(obj.some_strings, "some_strings");
ser.write(obj.strings, "strings");
ser.endObject();
}

// We must also implement the cubos::core::data::deserialize function for our type, so that it can
// be deserialized by the JSONBridge.
template <>
void cubos::core::data::deserialize<MyAsset>(Deserializer& des, MyAsset& obj)
void cubos::core::data::deserialize<Strings>(Deserializer& des, Strings& obj)
{
des.beginObject();
des.read(obj.an_integer);
des.read(obj.some_strings);
des.read(obj.strings);
des.endObject();
}
/// [Serialization definition]

static void config(Write<Settings> settings)
static void configSystem(Write<Settings> settings)
{
settings->setString("assets.io.path", SAMPLE_ASSETS_FOLDER);

// If we want to save assets, we must set this to false.
settings->setBool("assets.io.readOnly", false);
}

static void bridge(Write<Assets> assets)
/// [Register bridge]
static void bridgeSystem(Write<Assets> assets)
{
// Add the a JSONBridge for the .my extension.
assets->registerBridge(".my", std::make_unique<JSONBridge<MyAsset>>());
assets->registerBridge(".strings", std::make_unique<JSONBridge<Strings>>());
}
/// [Register bridge]

static void save_and_load(Write<Assets> assets)
{
// Create a new asset (with a random UUID).
auto handle = assets->create(MyAsset{
.an_integer = 42,
.some_strings = {"Hello", "World"},
});

// Set a path for the asset - necessary for the JSONBridge to know where to save the asset.
// It is important to set the correct extension, so that the asset manager knows which bridge
// to use.
assets->writeMeta(handle)->set("path", "/assets/sample/asset.my");

// Save the asset.
assets->save(handle);

// After making the handle weak, the asset won't have any more strong references.
handle.makeWeak();
/// [Loading the asset]
static const Asset<Strings> SampleAsset = AnyAsset("6f42ae5a-59d1-5df3-8720-83b8df6dd536");

// By calling cleanup, we can remove the asset from memory, forcing it to be reloaded from disk
// the next time it is accessed.
assets->cleanup();

// Access the asset - will be loaded automatically.
auto read = assets->read(handle);
Stream::stdOut.printf("Integer: {}\n", read->an_integer);
for (const auto& str : read->some_strings)
static void loadSystem(Write<Assets> assets)
{
auto read = assets->read(SampleAsset);
for (const auto& str : read->strings)
{
Stream::stdOut.printf("String: {}\n", str);
}

// Wait for input before exiting.
Stream::stdOut.print("You can now check the contents of the file!\nPress enter to exit...");
Stream::stdIn.get();

// Cleanup the created asset.
FileSystem::destroy("/assets/sample");
}
/// [Loading the asset]

int main()
{
auto cubos = Cubos();
Cubos cubos{};

/// [Configuration]
cubos.addPlugin(assetsPlugin);
cubos.startupSystem(config).tagged("cubos.settings");
cubos.startupSystem(bridge).tagged("cubos.assets.bridge");
cubos.startupSystem(save_and_load).tagged("cubos.assets");
cubos.startupSystem(bridgeSystem).tagged("cubos.assets.bridge");
cubos.startupSystem(loadSystem).tagged("cubos.assets");
/// [Configuration]

cubos.startupSystem(configSystem).tagged("cubos.settings");
cubos.run();
return 0;
}
26 changes: 26 additions & 0 deletions engine/samples/assets/json/page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Loading Serializable Assets {#examples-engine-assets-json}

@brief Loading serializable assets from JSON.

We'll use the following type as an example:

@snippet assets/json/main.cpp Asset type

We can make it serializable by implementing the following specializations:
RiscadoA marked this conversation as resolved.
Show resolved Hide resolved

@snippet assets/json/main.cpp Serialization definition

Then, we must register a bridge for this type. We provide @ref
cubos::engine::JSONBridge "JSONBridge" for easily loading and saving
serializable assets as JSON.

@snippet assets/json/main.cpp Register bridge

With the bridge registered, we can just load it from its handle:

@snippet assets/json/main.cpp Loading the asset

These sytems are configured the usual way, as explained in @ref
examples-engine-assets-bridge.

@snippet assets/json/main.cpp Configuration
7 changes: 7 additions & 0 deletions engine/samples/assets/page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Assets {#examples-engine-assets}

@brief How to use the @ref assets-plugin plugin.

- @subpage examples-engine-assets-bridge - @copybrief examples-engine-assets-bridge
- @subpage examples-engine-assets-json - @copybrief examples-engine-assets-json
- @subpage examples-engine-assets-saving - @copybrief examples-engine-assets-saving
Loading
Loading