A modern, ECS-based 3D game engine built with C++ and Metal for macOS
Features • Architecture • Getting Started • Building • Usage • Documentation
AsterEditor - Full-featured game editor with hierarchy, inspector, and project panels
Real-time 3D scene rendering with transform gizmos and debug visualization
- Pure ECS Architecture - Entity Component System for data-driven game development
- Metal Rendering Backend - High-performance graphics via Apple's Metal API
- Real-time 3D Rendering - Modern rendering pipeline with PBR material support
- Scene Management - Complete scene serialization and deserialization system
- Asset Management - Automatic asset discovery and import system
- Transform Hierarchy - Parent-child entity relationships with world-space transformations
- Camera System - Free-look camera with WASD movement and mouse controls
- Visual Editor - Native macOS editor built with AppKit
- Hierarchy Panel - Tree view of scene entities with drag-and-drop support
- Inspector Panel - Component-based property editing
- Project Panel - Asset browser with import functionality
- Transform Gizmos - Interactive 3D manipulation of entities
- Debug Overlay - Real-time performance metrics and statistics
- Welcome Window - Project management (create, open, recent projects)
- Mesh Import - Universal 3D model loader supporting:
- OBJ format (with Assimp)
- FBX, GLTF, COLLADA (extensible)
- Texture Import - Image loading via STB Image
- Procedural Primitives - Built-in mesh generation:
- Cube, Sphere, Plane
- Cylinder, Cone, Torus, Capsule
- Transform - Position, rotation, scale with hierarchical support
- Mesh - Geometry reference (primitive or imported)
- Material - PBR properties (albedo, metallic, roughness)
- MeshRenderer - Visibility and rendering control
- Name - Entity naming for editor display
AsterEngine follows a modular, layered architecture designed for maintainability and extensibility:
┌─────────────────────────────────────────────────────────┐
│ Applications │
│ ┌────────────────┐ ┌──────────────────┐ │
│ │ AsterEditor │ │ Game Runtime │ │
│ │ (AppKit UI) │ │ (Custom Client) │ │
│ └────────────────┘ └──────────────────┘ │
└──────────────────────┬───────────────────┬──────────────┘
│ │
┌──────────────────────┴───────────────────┴──────────────┐
│ Engine Runtime │
│ ┌──────────────────────────────────────────────────┐ │
│ │ ECS Core (Registry, Components, Systems) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Scene Management & Serialization │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Asset Manager (Meshes, Textures, Materials) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Rendering System (Camera, Debug Draw, Gizmos) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Editor Bridge (C API for UI interop) │ │
│ └──────────────────────────────────────────────────┘ │
└────────────────────────────┬─────────────────────────────┘
│
┌────────────────────────────┴─────────────────────────────┐
│ Rendering Hardware Interface (RHI) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Abstract Interface (Device, Pipeline, Buffer) │ │
│ └──────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Metal Backend (DeviceMetal, BufferMetal, etc.) │ │
│ └──────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
The engine uses a sparse-set based ECS for optimal performance:
// Entities are lightweight IDs
using EntityId = uint32_t;
// Components are pure data (no logic)
struct Transform {
Vec3 position, rotationEuler, scale;
Mat4 worldMatrix;
EntityId parent;
};
// Systems operate on component combinations
for (EntityId entity : registry.GetEntitiesWith<MeshRenderer>()) {
const Transform* transform = registry.Get<Transform>(entity);
const Mesh* mesh = registry.Get<Mesh>(entity);
const Material* material = registry.Get<Material>(entity);
// Render entity...
}Key Benefits:
- O(1) component lookup
- Cache-friendly iteration
- Data-driven design
- Easy to extend with new components
┌──────────────────────────────────────────────────────────┐
│ 1. Scene Query: Get entities with MeshRenderer component │
└────────────────────────────┬─────────────────────────────┘
│
┌────────────────────────────▼─────────────────────────────┐
│ 2. Component Gather: Collect Transform, Mesh, Material │
└────────────────────────────┬─────────────────────────────┘
│
┌────────────────────────────▼─────────────────────────────┐
│ 3. Resource Binding: Load meshes/textures on-demand │
└────────────────────────────┬─────────────────────────────┘
│
┌────────────────────────────▼─────────────────────────────┐
│ 4. Metal Draw Calls: Indexed rendering with instancing │
└────────────────────────────┬─────────────────────────────┘
│
┌────────────────────────────▼─────────────────────────────┐
│ 5. Debug Overlay: Gizmos, grid, statistics │
└──────────────────────────────────────────────────────────┘
The Rendering Hardware Interface (RHI) provides a clean abstraction over graphics APIs:
namespace aster::rhi {
struct Device; // GPU device management
struct Swapchain; // Present surface
struct Pipeline; // Shader program + state
struct Buffer; // GPU buffer (vertex/index/uniform)
struct Texture; // 2D/3D textures
struct CommandList; // Command recording
}Current Implementation: Metal (macOS)
Future Targets: Vulkan (cross-platform), DirectX 12 (Windows)
AsterEngine/
├── src/
│ ├── engine/
│ │ ├── ecs/ # Entity Component System
│ │ │ ├── Entity.h # Entity ID type definitions
│ │ │ ├── Registry.h # Component storage & queries
│ │ │ └── Components.h # Component definitions
│ │ ├── scene/ # Scene management
│ │ │ ├── Scene.h # Scene container
│ │ │ └── SceneSerializer.h # Save/load scenes
│ │ ├── runtime/ # Core engine systems
│ │ │ ├── Engine.h # Main engine class
│ │ │ ├── Camera.h # Camera controller
│ │ │ ├── AssetManager.h # Asset loading
│ │ │ ├── MeshLoader.h # Mesh import (OBJ, etc.)
│ │ │ └── PrimitiveAssets.h # Procedural meshes
│ │ ├── render/ # Rendering utilities
│ │ │ ├── DebugDraw.h # Debug line rendering
│ │ │ └── GizmoAxis.h # Transform gizmos
│ │ ├── rhi/ # Graphics abstraction
│ │ │ └── Pipeline.h # RHI interface
│ │ ├── editor/ # Editor-engine bridge
│ │ │ └── EditorBridge.h # C API for UI
│ │ └── platform_macos/ # Platform layer
│ │ └── AppHost.mm # Window & event handling
│ ├── rhi/
│ │ └── metal/ # Metal implementation
│ │ ├── DeviceMetal.mm
│ │ ├── CommandListMetal.mm
│ │ └── ...
│ ├── apps/
│ │ └── editor/ # Editor application
│ │ ├── EditorApp.mm # Editor entry point
│ │ └── ui/ # UI panels (AppKit)
│ └── main.cpp # Game runtime entry
├── third_party/ # External dependencies
│ ├── stb_image.h # Image loading
│ └── assimp/ # 3D model import
├── resources/ # App resources
├── docs/ # Documentation
└── CMakeLists.txt # Build configuration
- macOS 11.0+ (Big Sur or later)
- Xcode 13+ with Command Line Tools
- CMake 4.0+
- Homebrew (for dependencies)
Install required libraries via Homebrew:
brew install cmake assimp-
Clone the repository:
git clone https://github.com/N3ur0sis/AsterEngine.git cd AsterEngine -
Create build directory:
mkdir build && cd build
-
Configure with CMake:
cmake ..
-
Build the project:
cmake --build . -
Run the editor:
open AsterEditor.app
The project produces three targets:
- AsterEditor - Full-featured game editor
- AsterEngine - Minimal game runtime
- engine_runtime / engine_rhi_metal - Static libraries
# Debug build (default)
cmake -DCMAKE_BUILD_TYPE=Debug ..
cmake --build .
# Release build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .set(CMAKE_CXX_STANDARD 23) # C++23 features
set(CMAKE_CXX_STANDARD_REQUIRED ON)build/
├── AsterEditor.app/ # Editor application bundle
├── AsterEngine.app/ # Game runtime bundle
├── libengine_runtime.a # Core engine library
└── libengine_rhi_metal.a # Metal rendering backend
- Launch AsterEditor.app
- Click "Create New Project"
- Choose a name and location
- The editor creates:
MyProject/ ├── Assets/ │ └── Meshes/ ├── Scenes/ │ └── SampleScene.json └── MyProject.aproj
Method 1: Drag & Drop
- Drag
.obj,.fbx, or image files into the Project Panel
Method 2: Import Menu
- Right-click in Project Panel → "Import Asset..."
- Select files to import
Creating Entities:
- Right-click in Hierarchy → "Create Empty" or "Create Primitive"
- Entities are created with Transform component by default
Adding Components:
- Select an entity
- Click "Add Component" in Inspector
- Choose: Mesh, Material, or MeshRenderer
Transform Manipulation:
- W - Translate gizmo
- E - Rotate gizmo
- R - Scale gizmo
Camera Controls:
- Right-click + drag - Look around
- WASD - Move camera
- Q/E - Move up/down
- Scroll - Adjust speed
Create a custom game client:
#include "runtime/AppHost.h"
#include "runtime/Engine.h"
class MyGame : public AppClient {
aster::Engine engine;
void OnStartup() override {
// Initialize engine
engine.Initialize(surface, 1920, 1080);
engine.LoadScene("Assets/Scenes/MainScene.json");
// Create entities programmatically
aster::Scene* scene = engine.GetScene();
auto entity = scene->CreateEntity("Player");
scene->AddComponent(entity, aster::Mesh{aster::PrimitiveMeshType::Sphere});
scene->AddComponent(entity, aster::MeshRenderer{});
}
void OnUpdate(double dt) override {
engine.Update(dt);
}
void OnRender() override {
engine.Render();
}
};
int main() {
MyGame game;
AppConfig cfg{"My Game", 1920, 1080};
return RunApp(game, cfg);
}- Component System Architecture - ECS design and patterns
- Mesh Import System - Asset pipeline details
class Engine {
bool Initialize(const PlatformSurface& surface, int w, int h);
void Update(double dt);
void Render();
Scene* GetScene();
Camera* GetCamera();
AssetManager* GetAssetManager();
void NewScene(const std::string& name = "New Scene");
bool LoadScene(const std::filesystem::path& path);
bool SaveScene(const std::filesystem::path& path);
};class Scene {
EntityId CreateEntity(const char* name = nullptr);
void DestroyEntity(EntityId entity);
template<typename T>
T& AddComponent(EntityId entity, T&& component);
template<typename T>
T* GetComponent(EntityId entity);
Registry& GetRegistry();
};class Registry {
EntityId CreateEntity();
void DestroyEntity(EntityId entity);
template<typename T>
T& Add(EntityId entity, T&& component);
template<typename T>
T* Get(EntityId entity);
template<typename T>
bool Has(EntityId entity);
template<typename... Components>
std::vector<EntityId> GetEntitiesWith();
};- ✅ ECS architecture with sparse-set storage
- ✅ Metal rendering backend
- ✅ Scene serialization (JSON)
- ✅ OBJ mesh import via Assimp
- ✅ Native macOS editor
- ✅ Transform gizmos
- ✅ Project management
- ✅ Asset browser and import
- 🚧 PBR material system
- 🚧 Texture import and management
- 🚧 FBX/GLTF model support
- 📋 Physics system (RigidBody, Collider components)
- 📋 Lighting system (Point, Directional, Spot lights)
- 📋 Shadow mapping
- 📋 Post-processing stack
- 📋 Audio system (3D spatial audio)
- 📋 Animation system
- 📋 Particle system
- 📋 Scripting (Lua/Python integration)
- 📋 Vulkan backend (Windows/Linux support)
- 📋 Scene runtime (playmode)
- 📋 Build pipeline (game export)
Contributions are welcome! Please feel free to submit a Pull Request.
- Follow the existing code style (C++23, modern practices)
- Keep components as data-only (no logic)
- Use the ECS pattern for new features
- Add documentation for public APIs
- Test on macOS before submitting
- Rendering: Implement PBR shading, shadows, post-processing
- Physics: Integrate a physics library (Bullet, Jolt)
- Animation: Skeletal animation system
- Tools: Additional editor panels and tools
- Platform: Vulkan/DirectX backends
- Documentation: Tutorials, examples, API docs
This project is licensed under the MIT License - see the LICENSE file for details.
- Assimp - Open Asset Import Library
- STB - Single-file public domain libraries
- Apple Metal - High-performance graphics API
- Unity - ECS workflow and editor design
- Unreal Engine - Asset management patterns
- Godot - Scene system architecture
Made with ❤️ using C++23 and Metal
