Overview
The Workspace system provides centralized asset management for Atlas Engine. It handles resource discovery, loading, and organization through a singleton interface that maintains resource metadata and groups.
Core Concepts
Resource
A Resource represents a single asset on disk:
struct Resource {
fs ::path path; // Filesystem path to the asset
std ::string name; // Human-readable identifier
ResourceType type; // Type classification
};
ResourceGroup
A ResourceGroup organizes related resources:
struct ResourceGroup {
std ::string groupName; // Group identifier
std ::vector < Resource > resources; // Resources in this group
Resource findResource ( const std :: string & name );
};
Getting Started
Accessing the Workspace
#include <atlas/workspace.h>
// Get the singleton instance
Workspace & workspace = Workspace :: get ();
// Set the root path for all relative paths
workspace . setRootPath ( "assets/" );
Resource Types
The engine supports several built-in resource types:
enum class ResourceType {
File , // Generic file
Image , // Texture image (PNG, JPG, etc.)
SpecularMap , // Specular/roughness maps
Audio , // Sound files
Font , // Font files
Model // 3D model files
};
Creating Resources
Single Resource
// Create a texture resource
Resource brickTexture = workspace . createResource (
"textures/brick.png" ,
"BrickTexture" ,
ResourceType ::Image
);
// Create an audio resource
Resource footstep = workspace . createResource (
"audio/footstep.wav" ,
"FootstepSound" ,
ResourceType ::Audio
);
// Create a model resource
Resource character = workspace . createResource (
"models/character.obj" ,
"MainCharacter" ,
ResourceType ::Model
);
Resource creation handles:
Path resolution (relative to root path)
Duplicate detection (workspace.cpp:15-19)
Automatic logging via Tracer (workspace.cpp:21)
Path Resolution
The workspace automatically resolves relative paths:
// From workspace.cpp:23-28
if (rootPath && path . is_relative ()) {
res . path = rootPath . value () / path;
} else {
res . path = path;
}
Retrieving Resources
By Name
Resource texture = workspace . getResource ( "BrickTexture" );
if ( texture . name . empty ()) {
// Resource not found (warning logged at workspace.cpp:52)
}
All Resources
std ::vector < Resource > allResources = workspace . getAllResources ();
for ( const auto & res : allResources) {
std ::cout << res . name << ": " << res . path << std ::endl;
}
By Type
// Get all textures
std ::vector < Resource > textures =
workspace . getResourcesByType ( ResourceType ::Image);
// Get all audio files
std ::vector < Resource > sounds =
workspace . getResourcesByType ( ResourceType ::Audio);
// Get all models
std ::vector < Resource > models =
workspace . getResourcesByType ( ResourceType ::Model);
Implementation filters resources by type (workspace.cpp:58-66).
Resource Groups
Creating Groups
Group related assets together:
// Create skybox resources
std ::vector < Resource > skyboxFaces = {
workspace . createResource ( "skybox/right.jpg" , "SkyboxRight" , ResourceType ::Image),
workspace . createResource ( "skybox/left.jpg" , "SkyboxLeft" , ResourceType ::Image),
workspace . createResource ( "skybox/top.jpg" , "SkyboxTop" , ResourceType ::Image),
workspace . createResource ( "skybox/bottom.jpg" , "SkyboxBottom" , ResourceType ::Image),
workspace . createResource ( "skybox/front.jpg" , "SkyboxFront" , ResourceType ::Image),
workspace . createResource ( "skybox/back.jpg" , "SkyboxBack" , ResourceType ::Image)
};
ResourceGroup skybox = workspace . createResourceGroup (
"Skybox" ,
skyboxFaces
);
Group creation is logged via Tracer (workspace.cpp:38).
Retrieving Groups
// Get a specific group
ResourceGroup skybox = workspace . getResourceGroup ( "Skybox" );
// Get all groups
std ::vector < ResourceGroup > allGroups = workspace . getAllResourceGroups ();
Finding Resources in Groups
ResourceGroup skybox = workspace . getResourceGroup ( "Skybox" );
Resource rightFace = skybox . findResource ( "SkyboxRight" );
if ( rightFace . name . empty ()) {
// Not found (error logged at workspace.cpp:88)
}
Implementation searches group resources (workspace.cpp:82-90).
Real-World Examples
Material System
void loadMaterial ( const std :: string & materialName ) {
Workspace & ws = Workspace :: get ();
// Load material textures
Resource albedo = ws . getResource (materialName + "_Albedo" );
Resource normal = ws . getResource (materialName + "_Normal" );
Resource roughness = ws . getResource (materialName + "_Roughness" );
Resource metallic = ws . getResource (materialName + "_Metallic" );
// Create material from resources
Material mat;
mat . albedoMap = loadTexture ( albedo . path );
mat . normalMap = loadTexture ( normal . path );
mat . roughnessMap = loadTexture ( roughness . path );
mat . metallicMap = loadTexture ( metallic . path );
return mat;
}
Level Loading
void loadLevel ( const std :: string & levelName ) {
Workspace & ws = Workspace :: get ();
ws . setRootPath ( "levels/" + levelName + "/" );
// Load level geometry
auto models = ws . getResourcesByType ( ResourceType ::Model);
for ( const auto & model : models) {
loadModel ( model . path );
}
// Load level textures
auto textures = ws . getResourcesByType ( ResourceType ::Image);
for ( const auto & tex : textures) {
loadTexture ( tex . path );
}
// Load level audio
auto sounds = ws . getResourcesByType ( ResourceType ::Audio);
for ( const auto & sound : sounds) {
loadAudio ( sound . path );
}
}
Character Asset Bundle
ResourceGroup createCharacterBundle ( const std :: string & characterName ) {
Workspace & ws = Workspace :: get ();
std ::vector < Resource > characterAssets = {
// Model
ws . createResource (
"characters/" + characterName + "/model.fbx" ,
characterName + "_Model" ,
ResourceType ::Model
),
// Textures
ws . createResource (
"characters/" + characterName + "/albedo.png" ,
characterName + "_Albedo" ,
ResourceType ::Image
),
ws . createResource (
"characters/" + characterName + "/normal.png" ,
characterName + "_Normal" ,
ResourceType ::Image
),
// Audio
ws . createResource (
"characters/" + characterName + "/voice.wav" ,
characterName + "_Voice" ,
ResourceType ::Audio
)
};
return ws . createResourceGroup (
characterName + "_Bundle" ,
characterAssets
);
}
Runtime Asset Discovery
void discoverAssets ( const std :: filesystem :: path & directory ) {
Workspace & ws = Workspace :: get ();
for ( const auto & entry : std :: filesystem :: recursive_directory_iterator (directory)) {
if ( ! entry . is_regular_file ()) continue ;
std ::string ext = entry . path (). extension (). string ();
ResourceType type = ResourceType ::File;
// Classify by extension
if (ext == ".png" || ext == ".jpg" || ext == ".tga" ) {
type = ResourceType ::Image;
} else if (ext == ".wav" || ext == ".ogg" || ext == ".mp3" ) {
type = ResourceType ::Audio;
} else if (ext == ".obj" || ext == ".fbx" || ext == ".gltf" ) {
type = ResourceType ::Model;
} else if (ext == ".ttf" || ext == ".otf" ) {
type = ResourceType ::Font;
}
ws . createResource (
entry . path (),
entry . path (). stem (). string (),
type
);
}
}
Error Handling
The workspace system provides feedback through Tracer logging:
// Creating duplicate resources (workspace.cpp:16-18)
Resource res1 = workspace . createResource ( "tex.png" , "MyTex" , ResourceType ::Image);
Resource res2 = workspace . createResource ( "tex.png" , "MyTex" , ResourceType ::Image);
// Logs: "Resource already exists: MyTex" and returns existing resource
// Retrieving non-existent resources (workspace.cpp:52)
Resource missing = workspace . getResource ( "DoesNotExist" );
// Logs: "Resource not found: DoesNotExist"
// Returns empty Resource with empty name
// Searching non-existent groups (workspace.cpp:78)
ResourceGroup missing = workspace . getResourceGroup ( "NoSuchGroup" );
// Logs: "Resource group not found: NoSuchGroup"
// Returns empty ResourceGroup
// Finding missing resources in groups (workspace.cpp:88)
ResourceGroup group = workspace . getResourceGroup ( "MyGroup" );
Resource missing = group . findResource ( "NotInGroup" );
// Logs: "Resource not found in group: NotInGroup"
Design Patterns
Singleton Pattern
The Workspace uses the singleton pattern for global access:
class Workspace {
public:
static Workspace & get () {
static Workspace instance;
return instance;
}
Workspace ( const Workspace & ) = delete ;
Workspace & operator = ( const Workspace & ) = delete ;
private:
Workspace () : resources ({}), resourceGroups ({}), rootPath ( std ::nullopt) {}
~Workspace () = default ;
};
Source: workspace.h:91-170
Resource Deduplication
Resources are deduplicated by name:
Resource Workspace :: createResource ( const fs :: path & path ,
const std :: string & name ,
ResourceType type ) {
// Check for existing resource
for ( const auto & res : resources) {
if ( res . name == name) {
atlas_log ( "Resource already exists: " + name);
return res; // Return existing
}
}
// Create new resource...
}
Source: workspace.cpp:13-33
Integration with Other Systems
With Tracer
Workspace operations are logged via Tracer:
#include "atlas/tracer/log.h"
// All workspace operations use tracer macros
atlas_log ( "Creating resource: " + name); // workspace.cpp:21
atlas_warning ( "Resource not found: " + name); // workspace.cpp:52
atlas_error ( "Resource not found in group: " + name); // workspace.cpp:88
With Texture System
Texture loading integrates with workspace:
// Get texture from workspace
Resource texResource = workspace . getResource ( "BrickTexture" );
// Load through texture system
Texture tex = Texture :: fromFile ( texResource . path . string ());
With Model System
Model loading uses workspace paths:
Resource modelResource = workspace . getResource ( "Character" );
Model model = Model :: load ( modelResource . path . string ());
Best Practices
Set Root Path Early Configure the root path at application startup for consistent path resolution
Use Descriptive Names Give resources clear, unique names for easy retrieval
Group Related Assets Use ResourceGroups to organize assets that belong together
Check for Existence Always verify resources exist before using them
Resource names are used for deduplication, so creating a resource with an existing name returns the original resource rather than creating a duplicate.
The Workspace system uses std::filesystem for path operations, providing cross-platform path handling. All paths are stored as fs::path objects (workspace.h:34).