Project Module
The Project module manages project configuration, resource paths, and project-wide settings in Astra. It provides a centralized way to organize and access project-level data and configurations.
Purpose
The Project module provides:
- Project structure definition and management
- Resource path resolution with base directory support
- System configuration storage (Physics, Render)
- Project lifecycle operations (create, save)
- Project metadata management
Key Components
Project Class
The core Project class manages project state and configuration.
Key Methods:
create(ProjectConfig)- Creates a new project (static factory method)save(ElasticArena&)- Saves the project to fileget_config()- Returns project configurationget_project_id()- Returns unique project identifier
WARNING
The load() method is currently non-functional (implementation commented out).
ProjectConfig
Configuration structure containing project settings:
struct ProjectConfig {
std::string name = "Untitled";
std::string directory;
std::string manifest;
std::vector<WindowConfig> windows;
std::vector<SystemConfig> systems;
ProjectResourceConfig resources;
ProjectSerializationConfig serialization;
};ProjectSerializer
Handles project file serialization and deserialization:
class ProjectSerializer : public Serializer {
public:
ProjectSerializer(Ref<Project> project, Ref<SerializationContext> ctx);
ProjectSerializer();
void serialize() override;
void deserialize() override;
};- Uses the Streams module's
SerializationContextfor format-agnostic serialization serialize()writes project name, directory, and resources directorydeserialize()reads full project manifest including windows, systems, and resources- Supports path prefixes:
@engine/and@project/for base directory aliases
Architecture
Project Structure
A typical Astra project has this structure:
MyGame/
├── project.json # Project manifest file
├── resources/ # Project resources
│ ├── scenes/ # Scene files
│ ├── textures/ # Texture files
│ ├── models/ # 3D models
│ ├── materials/ # Material definitions
│ └── shaders/ # Shader files
└── cache/ # Build cacheManager Access
Project is accessible through the ProjectManager singleton:
// Get active project
Ref<Project> project = active_project();
// Or via manager
Ref<ProjectManager> pm = project_manager();
Ref<Project> project = pm->get_active_project();
// Access configuration
ProjectConfig& config = project->get_config();Configuration Hierarchy
Project configurations are organized hierarchically:
ProjectConfig
├── WindowConfig (vector)
├── SystemConfig (vector)
│ ├── PhysicsSystemConfig
│ └── RenderSystemConfig
├── ProjectResourceConfig
└── ProjectSerializationConfigKey Features
Project Creation
Create a new project programmatically:
ProjectConfig config;
config.name = "MyGame";
config.directory = "/path/to/MyGame";
config.manifest = "project.json";
config.resources.directory = "resources";
config.serialization.format = SerializationFormat::Json;
// Create project
Ref<Project> project = Project::create(config);
// Add to manager (makes it active)
project_manager()->add_project(project);Project Loading
Note: The Project::load() method is currently non-functional (implementation is commented out in source). Project loading must be implemented through manual deserialization:
// ProjectSerializer can deserialize from a context
// but Project::load() wrapper is not yet functional
// Access active project once loaded
Ref<Project> project = active_project();
if (project) {
ProjectConfig& config = project->get_config();
std::string name = config.name;
}Resource Path Resolution
The project module provides a Path class and PathManager for resolving paths:
// Create a path relative to project resources
Ref<Path> texturePath = Path::create("textures/wall.png", BaseDirectory::Project);
// Or use user-defined literals
auto shaderPath = "shaders/pbr.axsl"_project;
auto engineAsset = "icons/logo.png"_engine;
// Resolve to absolute filesystem path
std::filesystem::path absolutePath = path_manager()->resolve(texturePath);Path Base Directories:
BaseDirectory::Engine- Resolves againstASTRALIX_ASSETS_DIR(compile-time constant)BaseDirectory::Project- Resolves against project directory + resources directory
Configuration Management
Access and modify project configurations:
Ref<Project> project = active_project();
ProjectConfig& config = project->get_config();
// Window configuration (vector of windows)
if (!config.windows.empty()) {
config.windows[0].width = 1920;
config.windows[0].height = 1080;
config.windows[0].title = "My Game";
}
// System configurations (iterate to find specific system)
for (auto& system : config.systems) {
if (system.type == SystemType::Render) {
auto& renderConfig = std::get<RenderSystemConfig>(system.content);
renderConfig.msaa.is_enabled = true;
renderConfig.msaa.samples = 4;
}
if (system.type == SystemType::Physics) {
auto& physicsConfig = std::get<PhysicsSystemConfig>(system.content);
physicsConfig.gravity = glm::vec3(0, -9.81f, 0);
}
}
// Resource configuration
config.resources.directory = "resources";Project Saving
Save project changes:
ElasticArena arena;
Ref<Project> project = active_project();
project->save(arena);Note: The save implementation currently has the FileStreamWriter call commented out in source.
Integration with Other Modules
With Application Module
Application loads the project during initialization:
void Application::init() {
// Create project configuration
ProjectConfig config;
config.name = "MyGame";
config.directory = "/path/to/MyGame";
config.resources.directory = "resources";
// Create and register project
Ref<Project> project = Project::create(config);
project_manager()->add_project(project);
// Use project config for initialization
ProjectConfig& cfg = project->get_config();
// Create windows from config
for (const auto& windowConfig : cfg.windows) {
window_manager()->create_window(windowConfig);
}
// Initialize systems from config
for (const auto& systemConfig : cfg.systems) {
// System initialization based on type
}
}With Resource Module
Resource module uses project paths for asset loading:
void ResourceManager::load_texture(const std::string& name) {
// Create path relative to project resources
auto texturePath = Path::create("textures/" + name, BaseDirectory::Project);
// Resolve to absolute path
std::filesystem::path absolutePath = path_manager()->resolve(texturePath);
// Load texture from filesystem
load_texture_from_file(absolutePath);
}With Streams Module
Project uses Streams for serialization:
void ProjectSerializer::serialize() {
// Access context via operator[]
auto ctx = (*m_context)["project"];
// Write project fields
ctx["name"] = m_project->get_config().name;
ctx["directory"] = m_project->get_config().directory;
ctx["resources"]["directory"] = m_project->get_config().resources.directory;
}
void ProjectSerializer::deserialize() {
// Read from context
auto ctx = (*m_context)["project"];
// Parse fields
std::string name = ctx["name"].as<std::string>();
std::string directory = ctx["directory"].as<std::string>();
// Parse windows, systems, and resources
// ...
}With Editor Module
Editor uses project for asset browsing:
void ContentBrowserLayer::refresh() {
// Get project resources directory
Ref<Project> project = active_project();
if (!project) return;
std::string resourcesDir = project->get_config().resources.directory;
// Scan for assets
scan_directory(resourcesDir);
}Configuration Types
WindowConfig
Window and display settings:
struct WindowConfig {
WindowID id;
std::string title;
bool headless;
int height;
int width;
};SystemConfig
Generic system configuration with type-specific content:
struct SystemConfig {
std::string name;
SystemType type;
std::variant<std::monostate, PhysicsSystemConfig, RenderSystemConfig> content;
};
enum SystemType { Physics, Render };RenderSystemConfig
Rendering system settings:
struct RenderSystemConfig {
std::string backend;
MSAAConfig msaa;
std::string window_id;
bool headless = false;
RendererBackend backend_to_api();
RenderTarget::MSAA msaa_to_render_target_msaa();
};
struct MSAAConfig {
int samples;
bool is_enabled;
};PhysicsSystemConfig
Physics system settings:
struct PhysicsSystemConfig {
std::string backend;
glm::vec3 gravity;
std::string pvd_host = "127.0.0.1";
int pvd_port;
int pvd_timeout = 5;
};ProjectResourceConfig
Resource directory configuration:
struct ProjectResourceConfig {
std::string directory;
};ProjectSerializationConfig
Serialization format configuration:
struct ProjectSerializationConfig {
SerializationFormat format;
SerializationFormat formatFromString(const std::string& format);
};File Format
Project manifest files are stored in JSON format (typically project.json):
{
"project": {
"name": "MyGame",
"directory": "/path/to/MyGame",
"resources": {
"directory": "resources"
},
"windows": [
{
"id": "main-window",
"title": "My Game",
"width": 1920,
"height": 1080,
"headless": false
}
],
"systems": [
{
"name": "render",
"type": "render",
"content": {
"backend": "opengl",
"window_id": "main-window",
"headless": false,
"msaa": {
"is_enabled": true,
"samples": 4
}
}
},
{
"name": "physics",
"type": "physics",
"content": {
"backend": "physx",
"gravity": [0, -9.81, 0],
"pvd_host": "127.0.0.1",
"pvd_port": 5425,
"pvd_timeout": 5
}
}
]
}
}Path Prefixes:
@engine/- References engine assets directory@project/- References project resources directory
File Locations
/src/modules/project/
├── path.hpp # Path class declaration
├── path.cpp # Path implementation
├── project.hpp # Project class and config structs
├── project.cpp # Project implementation
├── managers/
│ ├── path-manager.hpp # PathManager singleton
│ ├── path-manager.cpp
│ ├── project-manager.hpp # ProjectManager singleton
│ └── project-manager.cpp
└── serializers/
├── path-serializer.hpp # Path serialization (empty)
├── path-serializer.cpp
├── project-serializer.hpp # ProjectSerializer class
└── project-serializer.cppRelated Modules
- Application - Loads project during initialization
- Streams - Used for project serialization
- Engine - Uses project for scene paths
- Editor - Provides UI for project management
Best Practices
- Create Early: Create and register the project before initializing other modules
- Use Path Class: Use the
Pathclass with base directories instead of raw strings - Check Active Project: Always check if
active_project()returns non-null before use - Validate Config: Validate configuration values after deserialization
- Use Type-Safe Access: Use
std::get<>with proper type checking for SystemConfig content
Performance Considerations
- Project creation is typically done once at startup
- PathManager caches resolved paths internally
- Use
Pathobjects and resolve once rather than repeated string concatenation - Path resolution involves filesystem operations; cache results when possible
Common Patterns
Project Initialization
Standard project initialization flow:
int main(int argc, char** argv) {
// Create project configuration
ProjectConfig config;
config.name = "MyGame";
config.directory = "/path/to/MyGame";
config.manifest = "project.json";
config.resources.directory = "resources";
// Create and register project
Ref<Project> project = Project::create(config);
project_manager()->add_project(project);
// Create and run application
Application& app = Application::get();
app.init();
app.run();
app.shutdown();
return 0;
}Note: Project loading from file is not yet functional. Projects must be created programmatically.
Resource Path Helpers
Create helper functions for common resource paths:
namespace astralix {
std::filesystem::path get_texture_path(const std::string& name) {
auto path = Path::create("textures/" + name, BaseDirectory::Project);
return path_manager()->resolve(path);
}
std::filesystem::path get_model_path(const std::string& name) {
auto path = Path::create("models/" + name, BaseDirectory::Project);
return path_manager()->resolve(path);
}
std::filesystem::path get_shader_path(const std::string& name) {
auto path = Path::create("shaders/" + name, BaseDirectory::Project);
return path_manager()->resolve(path);
}
// Or use UDL for convenience
auto texturePath = "textures/wall.png"_project;
auto resolvedPath = path_manager()->resolve(texturePath);
}Configuration Updates
Update and persist configuration changes:
void update_graphics_settings(const GraphicsSettings& settings) {
Ref<Project> project = active_project();
if (!project) return;
ProjectConfig& config = project->get_config();
// Find render system
for (auto& system : config.systems) {
if (system.type == SystemType::Render) {
auto& renderConfig = std::get<RenderSystemConfig>(system.content);
renderConfig.msaa.is_enabled = settings.msaa_enabled;
renderConfig.msaa.samples = settings.msaa_samples;
break;
}
}
// Save project
ElasticArena arena;
project->save(arena);
}Project Templates
Create projects from templates:
namespace astralix {
Ref<Project> create_2d_project(const std::string& name, const std::string& directory) {
ProjectConfig config;
config.name = name;
config.directory = directory;
config.resources.directory = "resources";
// Add 2D-optimized render system
SystemConfig renderSystem;
renderSystem.name = "render";
renderSystem.type = SystemType::Render;
RenderSystemConfig renderConfig;
renderConfig.backend = "opengl";
renderConfig.msaa.is_enabled = false;
renderSystem.content = renderConfig;
config.systems.push_back(renderSystem);
// 2D physics with no gravity
SystemConfig physicsSystem;
physicsSystem.name = "physics";
physicsSystem.type = SystemType::Physics;
PhysicsSystemConfig physicsConfig;
physicsConfig.backend = "physx";
physicsConfig.gravity = glm::vec3(0, 0, 0);
physicsSystem.content = physicsConfig;
config.systems.push_back(physicsSystem);
return Project::create(config);
}
Ref<Project> create_3d_project(const std::string& name, const std::string& directory) {
ProjectConfig config;
config.name = name;
config.directory = directory;
config.resources.directory = "resources";
// Add 3D-optimized render system
SystemConfig renderSystem;
renderSystem.name = "render";
renderSystem.type = SystemType::Render;
RenderSystemConfig renderConfig;
renderConfig.backend = "opengl";
renderConfig.msaa.is_enabled = true;
renderConfig.msaa.samples = 4;
renderSystem.content = renderConfig;
config.systems.push_back(renderSystem);
// 3D physics with gravity
SystemConfig physicsSystem;
physicsSystem.name = "physics";
physicsSystem.type = SystemType::Physics;
PhysicsSystemConfig physicsConfig;
physicsConfig.backend = "physx";
physicsConfig.gravity = glm::vec3(0, -9.81f, 0);
physicsSystem.content = physicsConfig;
config.systems.push_back(physicsSystem);
return Project::create(config);
}
}Multiple Project Management
The ProjectManager can track multiple projects:
namespace astralix {
void load_multiple_projects() {
// Create first project
ProjectConfig config1;
config1.name = "Project1";
config1.directory = "/path/to/project1";
Ref<Project> project1 = Project::create(config1);
// Create second project
ProjectConfig config2;
config2.name = "Project2";
config2.directory = "/path/to/project2";
Ref<Project> project2 = Project::create(config2);
// Add both to manager
Ref<ProjectManager> pm = project_manager();
pm->add_project(project1); // Becomes active
pm->add_project(project2); // Becomes active (replaces project1)
// Get all projects
std::vector<Ref<Project>> allProjects = pm->get_projects();
// Get active project ID
ProjectID activeId = pm->get_active_project_id();
}
}