Repository navigation
Project Structure
Xavier Beheydt edited this page Jan 31, 2026
·
2 revisions
Version: 0.1.0 Last Updated: 2026-01-30
- Overview
- Technical Stack
- Directory Structure
- Core Concepts
- Python Integration
- External Tools Integration
- Templates
- CMake Configuration
- Workflow
AetherEngine is a modern C++23 game engine designed for both 2D and 3D games, with Vulkan rendering, Python scripting, and a strong focus on AI/ML integration. The project follows a modular architecture where:
- Engine: The core runtime (always compiled)
- Plugins: Technical extensions to the engine (physics, audio backends)
- Modules: Reusable gameplay building blocks (FPS framework, inventory system)
- Games: Final products, linked as external projects via symlinks
┌─────────────────────────────────────────────────────────────────┐
│ AetherEngine │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ ENGINE CORE │ │
│ │ ┌────────┐ ┌──────────┐ ┌───────┐ ┌───────────┐ │ │
│ │ │ Core │ │ Graphics │ │ Audio │ │ Scripting │ │ │
│ │ └────────┘ └──────────┘ └───────┘ └───────────┘ │ │
│ │ ┌────────┐ ┌──────────┐ ┌───────┐ ┌───────────┐ │ │
│ │ │ ECS │ │ Network │ │ AI │ │ Physics │ │ │
│ │ └────────┘ └──────────┘ └───────┘ └───────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ PLUGINS │ │
│ │ (Technical engine extensions) │ │
│ │ ┌────────────────┐ ┌───────────────┐ ┌──────────────┐ │ │
│ │ │ physics_jolt │ │ audio_openal │ │ physics_bullet│ │ │
│ │ └────────────────┘ └───────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ MODULES │ │
│ │ (Reusable gameplay building blocks) │ │
│ │ ┌───────────────┐ ┌──────────────┐ ┌─────────────────┐ │ │
│ │ │ fps_framework │ │ inventory │ │ dialogue_system │ │ │
│ │ └───────────────┘ └──────────────┘ └─────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ GAMES │ │
│ │ (Final playable products) │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │ │
│ │ │ my_fps_game │ │ my_rpg_game │ │ │
│ │ └─────────────────┘ └─────────────────┘ │ │
│ └──────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Component | Technology | Notes |
|---|---|---|
| Language | C++23 | Modern C++ with modules support |
| Build System | CMake 3.31+ | With presets and modern features |
| Graphics API | Vulkan | Primary and only graphics backend |
| Windowing | SDL3 | Cross-platform window management |
| Editor UI | Dear ImGui | Immediate mode GUI for editor |
| ECS | EnTT | Entity Component System (or custom) |
| Physics | Jolt Physics | Primary physics engine (swappable via plugins) |
| Scripting | Python 3.12+ | Gameplay scripting with uv package manager |
| Component | Technology | Notes |
|---|---|---|
| Scene Format | glTF / OpenUSD | Scene description and interchange |
| 3D DCC | Blender | Primary 3D content creation, live connection |
| 2D Image | GIMP | Photo editing and textures |
| 2D Painting | Krita | Digital painting and texture creation |
| Vector Graphics | Inkscape | SVG and vector assets |
| Live Sync | Omniverse-style | Real-time DCC to engine connection |
Note: GIMP vs Krita - They serve different purposes:
- GIMP: Photo manipulation, compositing, texture editing
- Krita: Digital painting, hand-painted textures, concept art
| Component | Technology | Notes |
|---|---|---|
| Networking | Custom / ENet | Online multiplayer support |
| AI/ML | PyTorch / ONNX | Machine Learning and Deep Learning integration |
| AI Runtime | ONNX Runtime | ML model inference in engine |
| Component | Technology | Notes |
|---|---|---|
| IDE/Editor | VSCode / Neovim | With custom plugins for engine integration |
| Profiling | Tracy | Real-time frame profiler |
| GPU Debug | RenderDoc | Graphics debugging and analysis |
| Code Analysis | clang-tidy | Static analysis |
| Formatting | clang-format | Code style enforcement |
┌─────────────────────────────────────────────────────────────────────────────┐
│ AETHER ENGINE │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EDITOR (ImGui) │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Viewport │ │ Hierarchy│ │Inspector │ │ Assets │ │ Console │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ENGINE CORE │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Core │ │ Graphics │ │ Audio │ │ Scripting │ │ │
│ │ │ │ │ (Vulkan) │ │ │ │ (Python) │ │ │
│ │ │ - Types │ │ - Renderer │ │ - Playback │ │ - Bindings │ │ │
│ │ │ - Memory │ │ - Shaders │ │ - 3D Sound │ │ - Runtime │ │ │
│ │ │ - Logger │ │ - Materials │ │ - Mixer │ │ - Hot-reload│ │ │
│ │ │ - Config │ │ - Pipeline │ │ │ │ │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ │ │
│ │ │ Physics │ │ ECS │ │ Network │ │ AI │ │ │
│ │ │ (Jolt) │ │ (EnTT) │ │ │ │ (ML/DL) │ │ │
│ │ │ │ │ │ │ - Client │ │ │ │ │
│ │ │ - Rigidbody │ │ - Entities │ │ - Server │ │ - ONNX │ │ │
│ │ │ - Colliders │ │ - Components│ │ - Replicat° │ │ - PyTorch │ │ │
│ │ │ - Queries │ │ - Systems │ │ - Lobbies │ │ - Inference│ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ └────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ Resources │ │ Input │ │ Window │ │ │
│ │ │ │ │ │ │ (SDL3) │ │ │
│ │ │ - Loaders │ │ - Keyboard │ │ │ │ │
│ │ │ - Caching │ │ - Mouse │ │ - Events │ │ │
│ │ │ - Streaming │ │ - Gamepad │ │ - Context │ │ │
│ │ │ - glTF/USD │ │ - Actions │ │ - Vulkan │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ EXTERNAL CONNECTIONS │ │
│ │ │ │
│ │ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ │
│ │ │ Blender │ │ VSCode │ │ Tracy │ │ RenderDoc │ │ │
│ │ │ (Live) │ │ (Plugin) │ │ (Profile) │ │ (Debug) │ │ │
│ │ └───────────┘ └───────────┘ └───────────┘ └───────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
AetherEngine/
│
├── 📁 sources/ # All C++ source code
│ ├── engine/ # Engine core
│ │ ├── core/ # Types, memory, logging, config
│ │ │ ├── public/
│ │ │ │ └── aether/core/
│ │ │ ├── private/
│ │ │ └── CMakeLists.txt
│ │ ├── graphics/ # Vulkan renderer
│ │ ├── audio/ # Audio system
│ │ ├── physics/ # Physics interface (Jolt default)
│ │ ├── input/ # Input handling
│ │ ├── scripting/ # Python integration
│ │ ├── resources/ # Asset loading (glTF, USD)
│ │ ├── ecs/ # Entity Component System (EnTT wrapper)
│ │ ├── network/ # Networking for online games
│ │ ├── ai/ # AI/ML integration
│ │ ├── window/ # SDL3 windowing
│ │ └── CMakeLists.txt
│ │
│ ├── editor/ # Editor application (ImGui)
│ │ ├── public/
│ │ ├── private/
│ │ │ ├── panels/ # Editor panels (hierarchy, inspector...)
│ │ │ ├── widgets/ # Custom ImGui widgets
│ │ │ └── main.cpp
│ │ └── CMakeLists.txt
│ │
│ ├── runtime/ # Game runtime executable
│ │ ├── private/
│ │ │ └── main.cpp
│ │ └── CMakeLists.txt
│ │
│ └── programs/ # CLI tools
│ ├── asset_baker/ # Asset processing
│ ├── shader_compiler/ # SPIR-V compilation
│ └── scene_converter/ # glTF/USD conversion
│
├── 📁 plugins/ # Technical engine extensions
│ ├── physics_jolt/ # Jolt Physics (default)
│ ├── physics_bullet/ # Bullet Physics alternative
│ ├── physics_physx/ # PhysX alternative
│ ├── audio_openal/ # OpenAL audio backend
│ ├── audio_fmod/ # FMOD alternative
│ ├── ai_onnx/ # ONNX Runtime for ML inference
│ └── ai_pytorch/ # PyTorch integration
│
├── 📁 modules/ # Reusable gameplay blocks
│ ├── fps_framework/ # First-person shooter mechanics
│ ├── tps_framework/ # Third-person mechanics
│ ├── inventory_system/ # Items, storage, equipment
│ ├── dialogue_system/ # Dialogues, choices, branching
│ ├── quest_system/ # Quests, objectives, rewards
│ ├── save_system/ # Save/load game state
│ ├── multiplayer/ # Online game framework
│ └── ai_behaviors/ # Common AI behaviors (FSM, BT, GOAP)
│
├── 📁 games/ # Symlinks to external projects
│ ├── my_game -> ~/projects/my_game
│ └── .gitkeep
│
├── 📁 templates/ # Project templates
│ ├── games/
│ │ ├── empty/ # Minimal game
│ │ ├── fps/ # FPS template
│ │ ├── platformer/ # 2D/3D platformer
│ │ ├── rpg/ # RPG template
│ │ └── multiplayer/ # Online game template
│ └── modules/
│ └── empty/
│
├── 📁 runtimes/ # Scripting environments
│ └── python/
│ ├── engine/ # Base runtime environment
│ │ ├── pyproject.toml
│ │ └── uv.lock
│ └── dev/ # Development environment
│ ├── pyproject.toml
│ └── uv.lock
│
├── 📁 extern/ # Third-party dependencies (submodules)
│ ├── sdl3/ # SDL3 windowing
│ ├── imgui/ # Dear ImGui
│ ├── entt/ # EnTT ECS
│ ├── jolt/ # Jolt Physics
│ ├── glm/ # Math library
│ ├── volk/ # Vulkan loader
│ ├── vma/ # Vulkan Memory Allocator
│ ├── tracy/ # Profiler
│ ├── spdlog/ # Logging
│ └── ...
│
├── 📁 cmake/ # CMake modules and helpers
│ ├── helpers/
│ │ ├── AetherHelpers.cmake # Game/Module helper functions
│ │ ├── ShaderCompilation.cmake # SPIR-V compilation
│ │ └── ...
│ └── modules/
│ ├── FindVulkan.cmake
│ ├── FindSDL3.cmake
│ └── ...
│
├── 📁 configs/ # Centralized configuration
│ ├── engine/
│ │ ├── engine.toml
│ │ └── graphics.toml
│ ├── editor/
│ │ └── editor.toml
│ └── dev/
│ └── debug.toml
│
├── 📁 assets/ # Built-in engine assets
│ ├── shaders/ # Built-in shaders
│ ├── fonts/ # Default fonts
│ └── icons/ # Editor icons
│
├── 📁 tests/ # Unit and integration tests
│ ├── unit/
│ └── integration/
│
├── 📁 docs/ # Documentation
│ ├── architecture/
│ ├── api/
│ └── guides/
│
├── 📁 scripts/ # Utility scripts
│ ├── create_game.py
│ ├── create_module.py
│ ├── link_game.py
│ └── python_setup.py
│
├── 📁 tools/ # External tool integrations
│ ├── blender/ # Blender addon for live sync
│ ├── vscode/ # VSCode extension
│ └── neovim/ # Neovim plugin
│
├── CMakeLists.txt
├── CMakePresets.json
├── justfile
└── README.md
The engine is the core of AetherEngine, providing all fundamental systems:
| Module | Technology | Description |
|---|---|---|
core |
- | Types, memory, logging, config |
graphics |
Vulkan | Renderer, shaders, materials, pipeline |
audio |
OpenAL/FMOD | Audio playback, 3D sound, mixer |
physics |
Jolt | Physics simulation interface |
input |
SDL3 | Input handling, action mapping |
scripting |
Python + pybind11 | Python integration, hot-reload |
resources |
glTF/OpenUSD | Asset loading, serialization |
ecs |
EnTT | Entity Component System |
network |
Custom | Client/server, replication, lobbies |
ai |
ONNX/PyTorch | ML inference, behavior systems |
window |
SDL3 | Window management, Vulkan context |
sources/engine/core/
├── public/
│ └── aether/core/
│ ├── engine.hpp
│ ├── types.hpp
│ ├── memory.hpp
│ ├── logger.hpp
│ └── config.hpp
├── private/
│ ├── engine.cpp
│ ├── memory.cpp
│ ├── logger.cpp
│ └── config.cpp
└── CMakeLists.txt
add_library(AetherEngine_Core)
add_library(AetherEngine::Core ALIAS AetherEngine_Core)
target_sources(AetherEngine_Core
PRIVATE
private/engine.cpp
private/memory.cpp
private/logger.cpp
private/config.cpp
PUBLIC
FILE_SET HEADERS
BASE_DIRS public
FILES
public/aether/core/engine.hpp
public/aether/core/types.hpp
public/aether/core/memory.hpp
public/aether/core/logger.hpp
public/aether/core/config.hpp
)
target_include_directories(AetherEngine_Core
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/public>
$<INSTALL_INTERFACE:include>
)
target_link_libraries(AetherEngine_Core
PUBLIC
spdlog::spdlog
glm::glm
PRIVATE
Tracy::TracyClient
)
target_compile_features(AetherEngine_Core PUBLIC cxx_std_23)// sources/engine/ecs/public/aether/ecs/world.hpp
#pragma once
#include <entt/entt.hpp>
#include <aether/core/types.hpp>
namespace aether {
/**
* @brief Wrapper around EnTT registry providing a cleaner API.
*/
class World {
public:
using Entity = entt::entity;
Entity create_entity();
void destroy_entity(Entity entity);
template<typename T, typename... Args>
T& add_component(Entity entity, Args&&... args) {
return m_registry.emplace<T>(entity, std::forward<Args>(args)...);
}
template<typename T>
T& get_component(Entity entity) {
return m_registry.get<T>(entity);
}
template<typename T>
bool has_component(Entity entity) const {
return m_registry.any_of<T>(entity);
}
template<typename... Components>
auto view() {
return m_registry.view<Components...>();
}
entt::registry& registry() { return m_registry; }
private:
entt::registry m_registry;
};
} // namespace aether// sources/engine/physics/public/aether/physics/physics_world.hpp
#pragma once
#include <aether/core/types.hpp>
#include <glm/glm.hpp>
namespace aether {
/**
* @brief Abstract physics world interface.
*
* This allows swapping physics backends via plugins
* (Jolt, Bullet, PhysX).
*/
class IPhysicsWorld {
public:
virtual ~IPhysicsWorld() = default;
virtual void step(float delta_time) = 0;
virtual BodyId create_rigid_body(const RigidBodyDesc& desc) = 0;
virtual void destroy_rigid_body(BodyId id) = 0;
virtual void set_linear_velocity(BodyId id, const glm::vec3& velocity) = 0;
virtual glm::vec3 get_linear_velocity(BodyId id) const = 0;
virtual void add_force(BodyId id, const glm::vec3& force) = 0;
virtual void add_impulse(BodyId id, const glm::vec3& impulse) = 0;
virtual RaycastResult raycast(const glm::vec3& origin,
const glm::vec3& direction,
float max_distance) const = 0;
};
} // namespace aether| Aspect | Plugin | Module |
|---|---|---|
| Level | Engine (technical) | Gameplay (game logic) |
| Extends | Engine capabilities | Gameplay features |
| Used by | Engine directly | Games |
| Examples |
physics_jolt, audio_fmod, ai_onnx
|
fps_framework, inventory_system
|
| Loading | At engine startup | On game demand |
| Dependencies | Engine core only | Engine + other modules |
| Swappable | Yes, via config | Yes, games choose which to use |
plugins/
├── physics_jolt/ # Jolt Physics (DEFAULT)
├── physics_bullet/ # Bullet Physics alternative
├── physics_physx/ # PhysX alternative
├── audio_openal/ # OpenAL audio backend
├── audio_fmod/ # FMOD alternative
├── ai_onnx/ # ONNX Runtime for ML inference
└── ai_pytorch/ # PyTorch C++ integration
plugins/physics_jolt/
├── sources/
│ ├── public/
│ │ └── aether/plugins/physics_jolt/
│ │ └── jolt_world.hpp
│ └── private/
│ ├── jolt_world.cpp
│ └── jolt_debug_renderer.cpp
├── CMakeLists.txt
└── README.md
// plugins/physics_jolt/sources/public/aether/plugins/physics_jolt/jolt_world.hpp
#pragma once
#include <aether/physics/physics_world.hpp>
#include <Jolt/Jolt.h>
namespace aether::plugins {
class JoltPhysicsWorld : public IPhysicsWorld {
public:
JoltPhysicsWorld();
~JoltPhysicsWorld() override;
void step(float delta_time) override;
BodyId create_rigid_body(const RigidBodyDesc& desc) override;
void destroy_rigid_body(BodyId id) override;
// ... implementation of IPhysicsWorld interface
private:
JPH::PhysicsSystem m_physics_system;
// ... Jolt-specific members
};
} // namespace aether::plugins
// Plugin registration
AETHER_PLUGIN(JoltPhysicsWorld, "physics")modules/
├── fps_framework/ # Weapons, characters, hitscan, damage
├── tps_framework/ # Third-person camera, cover system
├── inventory_system/ # Items, storage, equipment
├── dialogue_system/ # Dialogues, choices, branching
├── quest_system/ # Quests, objectives, rewards
├── save_system/ # Save/load game state
├── multiplayer/ # Networking framework for games
└── ai_behaviors/ # FSM, Behavior Trees, GOAP
modules/fps_framework/
├── sources/
│ ├── public/
│ │ └── fps_framework/
│ │ ├── weapon.hpp
│ │ ├── character.hpp
│ │ ├── hitscan.hpp
│ │ └── damage.hpp
│ ├── private/
│ │ ├── weapon.cpp
│ │ ├── character.cpp
│ │ ├── hitscan.cpp
│ │ └── damage.cpp
│ └── CMakeLists.txt
├── scripts/
│ ├── weapon_behavior.py
│ └── damage_system.py
├── assets/
│ └── effects/
└── CMakeLists.txt
Games are separate repositories linked via symlinks in the games/ directory. This allows:
- Independent versioning per game
- Private repositories for games
- Separate teams for engine vs gameplay
- Selective cloning
~/projects/my_game/ # Separate Git repository
│
├── 📁 sources/
│ └── game/
│ ├── public/
│ │ └── my_game/
│ │ ├── game.hpp
│ │ └── player.hpp
│ ├── private/
│ │ ├── game.cpp
│ │ └── player.cpp
│ └── CMakeLists.txt
│
├── 📁 scripts/ # Python gameplay scripts
│ ├── behaviors/
│ │ └── player_controller.py
│ ├── systems/
│ │ └── combat.py
│ └── ui/
│ └── hud.py
│
├── 📁 assets/
│ ├── models/ # glTF models
│ ├── textures/
│ ├── audio/
│ ├── levels/ # Scene files (glTF/USD)
│ └── shaders/ # Game-specific shaders
│
├── 📁 configs/
│ ├── game.toml
│ └── input.toml
│
├── 📁 runtimes/
│ └── python/
│ ├── pyproject.toml
│ └── uv.lock
│
├── CMakeLists.txt
└── README.md
// sources/engine/core/public/aether/core/game.hpp
#pragma once
#include <aether/core/types.hpp>
#include <aether/core/config.hpp>
#include <string>
#include <vector>
namespace aether {
class Engine;
class IGame {
public:
virtual ~IGame() = default;
// Lifecycle
virtual void on_init(Engine& engine) = 0;
virtual void on_shutdown() = 0;
// Per-frame
virtual void on_update(float delta_time) = 0;
virtual void on_render() = 0;
// Networking (optional)
virtual void on_network_update() {}
// Metadata
virtual std::string get_name() const = 0;
virtual std::string get_version() const = 0;
virtual GameConfig get_config() const = 0;
};
struct GameConfig {
std::string name;
std::string version;
std::vector<std::string> modules;
std::vector<std::string> plugins;
std::string scripts_path;
std::string assets_path;
// Networking
bool is_multiplayer = false;
NetworkConfig network;
};
} // namespace aether
#define AETHER_GAME(GameClass) \
extern "C" { \
AETHER_EXPORT aether::IGame* create_game() { \
return new GameClass(); \
} \
AETHER_EXPORT void destroy_game(aether::IGame* g) { \
delete g; \
} \
}// games/my_game/sources/game/public/my_game/game.hpp
#pragma once
#include <aether/core/game.hpp>
namespace my_game {
class Game final : public aether::IGame {
public:
void on_init(aether::Engine& engine) override;
void on_shutdown() override;
void on_update(float delta_time) override;
void on_render() override;
std::string get_name() const override { return "My Awesome Game"; }
std::string get_version() const override { return "0.1.0"; }
aether::GameConfig get_config() const override;
private:
aether::Engine* m_engine = nullptr;
aether::World* m_world = nullptr;
};
} // namespace my_game// games/my_game/sources/game/private/game.cpp
#include <my_game/game.hpp>
#include <aether/core/engine.hpp>
#include <aether/core/logger.hpp>
#include <aether/ecs/world.hpp>
#include <aether/scripting/python_runtime.hpp>
#include <aether/graphics/renderer.hpp>
// Modules
#include <fps_framework/character.hpp>
#include <fps_framework/weapon.hpp>
#include <inventory_system/inventory.hpp>
namespace my_game {
void Game::on_init(aether::Engine& engine) {
m_engine = &engine;
m_world = &engine.get<aether::World>();
aether::Log::info("[MyGame] Initializing...");
// Setup Python scripting
auto& python = engine.get<aether::PythonRuntime>();
python.add_script_path("games/my_game/scripts");
python.load_module("behaviors.player_controller");
// Create player entity
auto player = m_world->create_entity();
m_world->add_component<fps_framework::Character>(player);
m_world->add_component<fps_framework::WeaponHolder>(player);
m_world->add_component<inventory_system::Inventory>(player);
// Load starting level
engine.get<aether::SceneLoader>().load("assets/levels/start.gltf");
aether::Log::info("[MyGame] Ready!");
}
void Game::on_shutdown() {
aether::Log::info("[MyGame] Shutting down...");
}
void Game::on_update(float delta_time) {
// Game systems run via ECS
}
void Game::on_render() {
// Custom rendering if needed
}
aether::GameConfig Game::get_config() const {
return {
.name = "My Awesome Game",
.version = "0.1.0",
.modules = {"fps_framework", "inventory_system"},
.plugins = {"physics_jolt"},
.scripts_path = "scripts",
.assets_path = "assets",
.is_multiplayer = false
};
}
} // namespace my_game
AETHER_GAME(my_game::Game)# games/my_game/configs/game.toml
[game]
name = "My Awesome Game"
version = "0.1.0"
entry_point = "my_game::Game"
[dependencies]
modules = ["fps_framework", "inventory_system"]
plugins = ["physics_jolt"]
[scripting]
scripts_path = "scripts"
main_script = "main.py"
use_game_env = true
[assets]
path = "assets"
scene_format = "gltf" # or "usd"
[graphics]
# Vulkan-specific settings
vsync = true
msaa_samples = 4
hdr = true
[physics]
# Using Jolt via plugin
fixed_timestep = 0.016666 # 60 Hz
gravity = [0.0, -9.81, 0.0]┌─────────────────────────────────────────────────────────────┐
│ ENGINE (runtime) │
│ runtimes/python/engine/ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Dependencies: │ │
│ │ - pybind11 (C++ bindings) │ │
│ │ - numpy (math operations) │ │
│ │ - tomli (config parsing) │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────────┐ │
│ │ DEV (development) │ │ GAME (per-game) │ │
│ │ runtimes/python/dev │ │ games/x/runtimes/python │ │
│ │ │ │ │ │
│ │ Extends: engine │ │ Extends: engine │ │
│ │ + pytest │ │ + game-specific deps │ │
│ │ + ruff, black │ │ + ML libraries │ │
│ │ + sphinx, mypy │ │ │ │
│ └─────────────────────┘ └─────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
# runtimes/python/engine/pyproject.toml
[project]
name = "aether-runtime"
version = "0.1.0"
description = "AetherEngine Python runtime environment"
requires-python = ">=3.12"
dependencies = [
"pybind11>=2.12",
"numpy>=2.0",
"tomli>=2.0",
]
[tool.uv]
python = "3.12"# runtimes/python/dev/pyproject.toml
[project]
name = "aether-dev"
version = "0.1.0"
description = "AetherEngine development environment"
requires-python = ">=3.12"
dependencies = []
[project.optional-dependencies]
dev = [
# Testing
"pytest>=8.0",
"pytest-cov>=4.0",
# Linting & Formatting
"ruff>=0.4",
"black>=24.0",
"mypy>=1.10",
# Documentation
"sphinx>=7.0",
"sphinx-rtd-theme>=2.0",
# Debug
"ipython>=8.0",
"rich>=13.0",
]
ai = [
# ML/DL development
"torch>=2.0",
"onnx>=1.15",
"onnxruntime>=1.17",
]
[tool.uv]
python = "3.12"
[tool.uv.workspace]
members = ["../engine"]# games/my_game/runtimes/python/pyproject.toml
[project]
name = "my_game-scripts"
version = "0.1.0"
description = "Python gameplay scripts for My Game"
requires-python = ">=3.12"
dependencies = [
# Game-specific Python dependencies
# "pillow>=10.0", # Image processing
# "pyyaml>=6.0", # YAML configs
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"ruff>=0.4",
]
ai = [
# If game uses ML
"onnxruntime>=1.17",
]
[tool.uv]
python = "3.12"
[tool.ruff]
line-length = 100
target-version = "py312"# games/my_game/scripts/behaviors/player_controller.py
from aether.core import Component, Input, Vector3, Log
from aether.graphics import Transform
from aether.physics import Rigidbody
from fps_framework import Character, Weapon
class PlayerController(Component):
"""First-person player controller with full movement."""
def __init__(self):
super().__init__()
self.move_speed = 5.0
self.sprint_multiplier = 1.8
self.jump_force = 8.0
self.mouse_sensitivity = 0.3
def on_start(self):
self.transform = self.entity.get_component(Transform)
self.character = self.entity.get_component(Character)
self.rigidbody = self.entity.get_component(Rigidbody)
self.current_weapon: Weapon | None = None
# Lock cursor for FPS controls
Input.set_cursor_mode("locked")
Log.info("PlayerController initialized")
def on_update(self, dt: float):
self._handle_movement(dt)
self._handle_look(dt)
self._handle_combat()
def _handle_movement(self, dt: float):
direction = Vector3.zero()
if Input.is_action_pressed("move_forward"):
direction.z += 1
if Input.is_action_pressed("move_backward"):
direction.z -= 1
if Input.is_action_pressed("move_left"):
direction.x -= 1
if Input.is_action_pressed("move_right"):
direction.x += 1
speed = self.move_speed
if Input.is_action_pressed("sprint") and self.character.stamina > 0:
speed *= self.sprint_multiplier
self.character.use_stamina(dt * 10)
if direction.length_squared() > 0:
direction = self.transform.transform_direction(direction.normalized())
self.character.move(direction * speed * dt)
if Input.is_action_just_pressed("jump") and self.character.is_grounded:
self.rigidbody.add_impulse(Vector3(0, self.jump_force, 0))
def _handle_look(self, dt: float):
look_x = Input.get_axis("look_horizontal") * self.mouse_sensitivity
look_y = Input.get_axis("look_vertical") * self.mouse_sensitivity
self.character.rotate_view(look_x, look_y)
def _handle_combat(self):
if self.current_weapon is None:
return
if Input.is_action_pressed("fire"):
self.current_weapon.fire()
if Input.is_action_just_pressed("reload"):
self.current_weapon.reload()
if Input.is_action_just_pressed("aim"):
self.current_weapon.start_aim()
if Input.is_action_just_released("aim"):
self.current_weapon.stop_aim()# games/my_game/scripts/ai/enemy_brain.py
from aether.core import Component, Vector3, Log
from aether.ai import ONNXModel
import numpy as np
class EnemyBrain(Component):
"""Neural network-driven enemy AI."""
def __init__(self, model_path: str = "assets/models/enemy_ai.onnx"):
super().__init__()
self.model = ONNXModel(model_path)
self.decision_interval = 0.1 # 10 Hz decision making
self.time_since_decision = 0.0
def on_start(self):
self.transform = self.entity.get_component("Transform")
self.character = self.entity.get_component("Character")
Log.info(f"EnemyBrain loaded model: {self.model.path}")
def on_update(self, dt: float):
self.time_since_decision += dt
if self.time_since_decision >= self.decision_interval:
self.time_since_decision = 0.0
self._make_decision()
def _make_decision(self):
# Gather observations
obs = self._get_observations()
# Run inference
action = self.model.predict(obs)
# Execute action
self._execute_action(action)
def _get_observations(self) -> np.ndarray:
"""Gather world state for the neural network."""
player = self.world.find_entity_by_tag("Player")
if player is None:
return np.zeros(10)
player_pos = player.get_component("Transform").position
my_pos = self.transform.position
direction_to_player = (player_pos - my_pos).normalized()
distance = (player_pos - my_pos).length()
return np.array([
direction_to_player.x,
direction_to_player.y,
direction_to_player.z,
distance / 50.0, # Normalized distance
self.character.health / 100.0,
self.character.ammo / 30.0,
float(self.character.is_in_cover),
float(self.character.can_see_player),
# ... more observations
], dtype=np.float32)
def _execute_action(self, action: np.ndarray):
"""Execute the action chosen by the neural network."""
action_id = int(np.argmax(action))
match action_id:
case 0:
self.character.move_towards_player()
case 1:
self.character.take_cover()
case 2:
self.character.fire_at_player()
case 3:
self.character.reload()
case 4:
self.character.flank()The engine provides a Blender addon for real-time synchronization:
tools/blender/
├── aether_sync/
│ ├── __init__.py
│ ├── operators.py
│ ├── panels.py
│ ├── sync_client.py
│ └── export_utils.py
└── README.md
# tools/blender/aether_sync/__init__.py
bl_info = {
"name": "AetherEngine Sync",
"author": "AIperture Labs",
"version": (0, 1, 0),
"blender": (4, 0, 0),
"category": "Import-Export",
"description": "Real-time sync with AetherEngine",
}
import bpy
from .operators import AETHER_OT_connect, AETHER_OT_sync_scene
from .panels import AETHER_PT_main_panel
classes = [
AETHER_OT_connect,
AETHER_OT_sync_scene,
AETHER_PT_main_panel,
]
def register():
for cls in classes:
bpy.utils.register_class(cls)
def unregister():
for cls in classes:
bpy.utils.unregister_class(cls)tools/vscode/
├── package.json
├── src/
│ ├── extension.ts
│ ├── engine_connection.ts
│ └── script_hot_reload.ts
└── README.md
// tools/vscode/package.json
{
"name": "aether-engine",
"displayName": "AetherEngine",
"description": "AetherEngine development tools for VSCode",
"version": "0.1.0",
"engines": {
"vscode": "^1.85.0"
},
"categories": ["Other"],
"activationEvents": [
"workspaceContains:**/configs/game.toml"
],
"main": "./out/extension.js",
"contributes": {
"commands": [
{
"command": "aether.connectEngine",
"title": "AetherEngine: Connect to Running Instance"
},
{
"command": "aether.hotReloadScript",
"title": "AetherEngine: Hot Reload Current Script"
}
]
}
}// sources/engine/core/public/aether/core/profiler.hpp
#pragma once
#include <tracy/Tracy.hpp>
// Profiling macros
#define AETHER_PROFILE_SCOPE(name) ZoneScopedN(name)
#define AETHER_PROFILE_FUNCTION() ZoneScoped
#define AETHER_PROFILE_FRAME() FrameMark
// GPU profiling (Vulkan)
#define AETHER_PROFILE_GPU_SCOPE(ctx, name) TracyVkZone(ctx, name)// Usage example
void Renderer::render(const Scene& scene) {
AETHER_PROFILE_FUNCTION();
{
AETHER_PROFILE_SCOPE("Shadow Pass");
render_shadows(scene);
}
{
AETHER_PROFILE_SCOPE("Main Pass");
render_main(scene);
}
{
AETHER_PROFILE_SCOPE("Post Process");
apply_post_process();
}
AETHER_PROFILE_FRAME();
}// sources/engine/graphics/private/renderdoc_integration.cpp
#include <aether/graphics/debug.hpp>
#ifdef AETHER_ENABLE_RENDERDOC
#include <renderdoc_app.h>
namespace aether {
class RenderDocCapture {
public:
static void init() {
if (HMODULE mod = GetModuleHandleA("renderdoc.dll")) {
pRENDERDOC_GetAPI getApi;
getApi = (pRENDERDOC_GetAPI)GetProcAddress(mod, "RENDERDOC_GetAPI");
getApi(eRENDERDOC_API_Version_1_1_2, (void**)&s_api);
}
}
static void begin_capture() {
if (s_api) s_api->StartFrameCapture(nullptr, nullptr);
}
static void end_capture() {
if (s_api) s_api->EndFrameCapture(nullptr, nullptr);
}
private:
static inline RENDERDOC_API_1_1_2* s_api = nullptr;
};
} // namespace aether
#endif# Using the helper script
python scripts/create_game.py my_fps_game --template fps --path ~/projects
# Or using justfile
just games-create my_fps_game fps ~/projects| Template | Description | Includes Modules |
|---|---|---|
empty |
Minimal game setup | None |
fps |
First-person shooter | fps_framework |
platformer |
2D/3D platformer | - |
rpg |
Role-playing game |
inventory_system, dialogue_system, quest_system
|
multiplayer |
Online multiplayer | multiplayer |
templates/games/fps/
├── sources/
│ └── game/
│ ├── public/
│ │ └── {{game_name}}/
│ │ ├── game.hpp
│ │ └── player.hpp
│ ├── private/
│ │ ├── game.cpp
│ │ └── player.cpp
│ └── CMakeLists.txt
├── scripts/
│ ├── behaviors/
│ │ ├── player_controller.py
│ │ └── enemy_ai.py
│ ├── systems/
│ │ └── damage_system.py
│ └── ui/
│ └── hud.py
├── assets/
│ ├── models/
│ ├── textures/
│ ├── audio/
│ │ ├── weapons/
│ │ └── ambient/
│ └── levels/
├── configs/
│ ├── game.toml.in
│ ├── input.toml
│ └── weapons.toml
├── runtimes/
│ └── python/
│ └── pyproject.toml.in
├── CMakeLists.txt.in
└── README.md.in
#!/usr/bin/env python3
# scripts/create_game.py
import argparse
import os
import shutil
from pathlib import Path
TEMPLATES_DIR = Path(__file__).parent.parent / "templates" / "games"
GAMES_DIR = Path(__file__).parent.parent / "games"
def get_available_templates() -> list[str]:
return [d.name for d in TEMPLATES_DIR.iterdir() if d.is_dir()]
def to_pascal_case(name: str) -> str:
return "".join(word.capitalize() for word in name.split("_"))
def to_namespace(name: str) -> str:
return name.lower().replace("-", "_")
def create_game(
name: str,
template: str,
output_path: Path,
display_name: str | None = None,
version: str = "0.1.0",
link: bool = True,
) -> Path:
template_path = TEMPLATES_DIR / template
if not template_path.exists():
raise ValueError(f"Template '{template}' not found. Available: {get_available_templates()}")
game_path = output_path / name
if game_path.exists():
raise ValueError(f"Directory already exists: {game_path}")
variables = {
"{{game_name}}": name,
"{{game_namespace}}": to_namespace(name),
"{{game_display_name}}": display_name or to_pascal_case(name),
"{{game_version}}": version,
"{{game_modules}}": "",
}
print(f"Creating game '{name}' from template '{template}'...")
print(f" Output: {game_path}")
shutil.copytree(template_path, game_path)
# Process template files
for root, dirs, files in os.walk(game_path):
root_path = Path(root)
# Rename directories
for dir_name in dirs:
if "{{" in dir_name:
old_path = root_path / dir_name
new_name = dir_name
for var, value in variables.items():
new_name = new_name.replace(var, value)
old_path.rename(root_path / new_name)
# Process files
for file_name in files:
file_path = root_path / file_name
if file_path.suffix in [".png", ".jpg", ".wav", ".ogg", ".gltf", ".glb"]:
continue
new_file_name = file_name
for var, value in variables.items():
new_file_name = new_file_name.replace(var, value)
if new_file_name.endswith(".in"):
new_file_name = new_file_name[:-3]
try:
content = file_path.read_text()
for var, value in variables.items():
content = content.replace(var, value)
new_file_path = file_path.parent / new_file_name
new_file_path.write_text(content)
if file_path != new_file_path:
file_path.unlink()
except UnicodeDecodeError:
if file_name != new_file_name:
file_path.rename(file_path.parent / new_file_name)
# Initialize git
os.system(f"cd {game_path} && git init")
print(f" ✓ Game created: {game_path}")
if link:
link_game(name, game_path)
return game_path
def link_game(name: str, game_path: Path):
symlink_path = GAMES_DIR / name
if symlink_path.exists():
if symlink_path.is_symlink():
symlink_path.unlink()
else:
raise ValueError(f"Cannot create symlink: {symlink_path} exists")
symlink_path.symlink_to(game_path.resolve())
print(f" ✓ Symlink created: {symlink_path} -> {game_path}")
def main():
parser = argparse.ArgumentParser(description="Create a new game from template")
parser.add_argument("name", help="Game name (snake_case)")
parser.add_argument("--template", "-t", default="empty", choices=get_available_templates())
parser.add_argument("--path", "-p", type=Path, default=Path.home() / "projects")
parser.add_argument("--display-name", "-d")
parser.add_argument("--version", "-v", default="0.1.0")
parser.add_argument("--no-link", action="store_true")
args = parser.parse_args()
create_game(
name=args.name,
template=args.template,
output_path=args.path,
display_name=args.display_name,
version=args.version,
link=not args.no_link,
)
if __name__ == "__main__":
main()cmake_minimum_required(VERSION 3.31...4.1)
project(
AetherEngine
VERSION 0.1.0
LANGUAGES C CXX
)
# ============================================================================
# CMake Configuration
# ============================================================================
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/helpers")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
include(CompilerWarnings)
include(CompileCommandsSymlink)
include(Config)
include(Dependencies)
include(AetherHelpers)
# ============================================================================
# Options
# ============================================================================
option(AETHER_BUILD_EDITOR "Build the Editor" ON)
option(AETHER_BUILD_PROGRAMS "Build CLI tools" ON)
option(AETHER_BUILD_TESTS "Build tests" OFF)
option(AETHER_BUILD_DOCS "Build documentation" OFF)
# Profiling
option(AETHER_ENABLE_TRACY "Enable Tracy profiler" ON)
option(AETHER_ENABLE_RENDERDOC "Enable RenderDoc integration" ON)
set(AETHER_ACTIVE_GAME "" CACHE STRING "Game to build (empty = all)")
# ============================================================================
# External Dependencies
# ============================================================================
find_package(Vulkan REQUIRED)
find_package(SDL3 REQUIRED)
find_package(EnTT REQUIRED)
find_package(glm REQUIRED)
if(AETHER_ENABLE_TRACY)
find_package(Tracy REQUIRED)
endif()
# ============================================================================
# Build Targets
# ============================================================================
add_subdirectory(sources/engine)
add_subdirectory(plugins)
add_subdirectory(modules)
add_subdirectory(games)
add_subdirectory(sources/runtime)
if(AETHER_BUILD_EDITOR)
find_package(imgui REQUIRED)
add_subdirectory(sources/editor)
endif()
if(AETHER_BUILD_PROGRAMS)
add_subdirectory(sources/programs)
endif()
if(AETHER_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
if(AETHER_BUILD_DOCS)
add_subdirectory(docs)
endif()# sources/engine/CMakeLists.txt
add_subdirectory(core)
add_subdirectory(window) # SDL3
add_subdirectory(graphics) # Vulkan
add_subdirectory(audio)
add_subdirectory(physics)
add_subdirectory(input)
add_subdirectory(scripting) # Python
add_subdirectory(resources) # glTF/USD
add_subdirectory(ecs) # EnTT
add_subdirectory(network)
add_subdirectory(ai)
# Aggregate library
add_library(AetherEngine INTERFACE)
add_library(AetherEngine::Engine ALIAS AetherEngine)
target_link_libraries(AetherEngine
INTERFACE
AetherEngine::Core
AetherEngine::Window
AetherEngine::Graphics
AetherEngine::Audio
AetherEngine::Physics
AetherEngine::Input
AetherEngine::Scripting
AetherEngine::Resources
AetherEngine::ECS
AetherEngine::Network
AetherEngine::AI
)