Loading
Personal Project
Game Engine: Eurekiel

Eurekiel is a custom 3D game engine written in C++17, targeting DirectX 12 with a focus on voxel worlds and deferred rendering. The engine uses a modular subsystem architecture: major systems (graphics, resources, audio, input) register as independent subsystems managed by an Engine singleton, keeping modules loosely coupled.

The rendering backend features SM6.6 bindless resource indexing, a deferred shading pipeline compatible with the Iris shader specification, and a Shader Bundle system with multi-layer fallback chains. The voxel module provides chunk management, biome generation, fluid simulation, and block lighting.

Engine architecture

Eurekiel follows a subsystem-based architecture. The Engine class acts as a central registry: game applications instantiate and register subsystems during startup, and the engine manages their lifecycle (startup, per-frame update, shutdown) in registration order.

// Subsystem registration during application startup
GEngine->RegisterSubsystem(std::make_unique<LoggerSubsystem>());
GEngine->RegisterSubsystem(std::make_unique<EventSubsystem>(eventConfig));
GEngine->RegisterSubsystem(std::make_unique<ResourceSubsystem>(resourceConfig));
GEngine->RegisterSubsystem(std::make_unique<RendererSubsystem>(renderConfig));
GEngine->RegisterSubsystem(std::make_unique<ShaderBundleSubsystem>(bundleConfig));
GEngine->RegisterSubsystem(std::make_unique<ImGuiSubsystem>(imguiConfig));

GEngine->Startup(); // initializes all subsystems in registration order

The main loop runs through a BeginFrame -> Update -> Render -> EndFrame cycle. Subsystems hook into the phases they need, while the application layer drives scene logic and render pass execution through public engine APIs.

Engine modules

Core

The foundation layer hosts the Engine singleton, the EngineSubsystem base class, the system Clock, error handlers, and configuration parsers:

  • Logger: Category-based structured logging with severity filtering
  • Console: Developer console with command parsing and history
  • Event: Publish-subscribe event bus for inter-system messaging
  • Schedule: YAML-configured task scheduling
  • ImGui: Dear ImGui integration for debug overlays and editor tooling
  • Command: Command registration and invocation framework

Graphics (DirectX 12)

The DirectX 12 rendering backend is organized into several modules:

  • DX12 Core: Wraps device creation, command queues, swap chain, and resource transitions in D3D12RenderSystem
  • Resource management: Typed GPU resources (D12Buffer, D12Texture, D12RenderTarget, D12DepthTexture) inheriting from a shared D12Resource base
  • Bindless resources: SM6.6 bindless architecture with a global descriptor heap, index allocator, and a 128-byte root signature
  • Camera system: Strategy-pattern cameras (PerspectiveCamera, OrthographicCamera, ShadowCamera, UICamera) implementing ICamera
  • Shader bundles: Shader packaging and fallback system compatible with Iris shader packs, supporting a three-tier fallback chain (Current, Program, Engine Default)
  • Render targets: Centralized management for up to 16 color textures and depth buffers (colortex0-15, depthtex0-2)
  • Shadow system: Shadow map generation and sampling
  • Command lists: CommandListManager for recording and submitting across multi-queue graphics pipelines

The deferred pipeline supports a 24-phase rendering schedule compatible with Iris’s WorldRenderingPhase, spanning geometry passes (terrain, cutout, translucent, sky), shadow generation, deferred lighting, composite passes, and final presentation.

Math

A self-contained vector and geometry math library:

  • Vectors (Vec2/3/4, IntVec2/3/4), 4x4 matrices, Euler angles, and rotation utilities
  • Geometric primitives: AABB, OBB, spheres, planes, capsules, cylinders, and convex hulls
  • Curves: Cubic Bezier, Hermite splines, and easing functions
  • Procedural noise: Raw and smooth noise generators
  • Raycasting and intersection tests

Resource

A namespaced resource manager supporting multiple asset roots:

  • Resource discovery and directory indexing
  • Typed loaders for textures, meshes, audio, and block definitions
  • Texture atlas packing and UV management
  • Asset metadata tracking and hot reloading during development
blog placeholder
Generated Block Atlas

Voxel

A voxel engine implementation featuring:

  • Block system: Block states, properties, placement context, and voxel collision shapes
  • Chunk system: Chunk data arrays, chunk management, and multithreaded mesh construction (BuildMeshJob)
  • World generation: Multi-noise biome systems, procedural terrain, and feature decorators
  • Fluid simulation: Block-level fluid spreading and leveling
  • Lighting: Block and sky light propagation algorithms
  • In-game time: Day and night cycle clocks

Window

Win32 window creation, event dispatch, and input routing configured via WindowConfig.

Renderer (legacy)

An immediate-mode DirectX 11 rendering layer providing IRenderer, bitmap font rendering, sprite drawing, and debug primitives. It remains available for 2D UI and debug visualization while the DX12 Graphics backend handles modern passes.

Application integration

To build a project with Eurekiel, the game application defines an App class that owns the engine lifecycle and a Game class for scene logic. The application controls the entry point, treating the engine as a linked library.

Entry point

The Win32 entry point creates the App, runs the message loop, and handles shutdown:

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    g_theApp = new App();
    g_theApp->Startup();

    while (!g_theApp->IsQuitting())
    {
        RunMessagePump();   // process OS messages (input, focus, resize, etc.)
        g_theApp->RunFrame();
    }

    g_theApp->Shutdown();
    delete g_theApp;
    return 0;
}

Startup and subsystem registration

In App::Startup(), the application creates an Engine instance and registers subsystems in order of dependency. The engine assumes ownership through std::unique_ptr:

void App::Startup()
{
    Engine::CreateInstance();

    // Register subsystems in dependency order
    GEngine->RegisterSubsystem(std::make_unique<LoggerSubsystem>());
    GEngine->RegisterSubsystem(std::make_unique<EventSubsystem>(eventConfig));
    GEngine->RegisterSubsystem(std::make_unique<ResourceSubsystem>(resourceConfig));

    // Window and input are created before the renderer (it needs a target window)
    g_theWindow = new Window(windowConfig);
    g_theWindow->Startup();

    // Renderer subsystem configured via YAML with programmatic overrides
    auto renderConfig    = RendererSubsystemConfig::ParseFromYaml(".enigma/config/engine/renderer.yml")
                               .value_or(RendererSubsystemConfig::GetDefault());
    renderConfig.targetWindow = g_theWindow;
    GEngine->RegisterSubsystem(std::make_unique<RendererSubsystem>(renderConfig));

    // Shader bundle, ImGui, etc.
    GEngine->RegisterSubsystem(std::make_unique<ShaderBundleSubsystem>(bundleConfig));
    GEngine->RegisterSubsystem(std::make_unique<ImGuiSubsystem>(imguiConfig));

    GEngine->Startup();  // initializes all registered subsystems

    // Create the game object: your scene logic lives here
    m_game = std::make_unique<Game>();
}

Subsystems can be queried anywhere by type:

auto* renderer = GEngine->GetSubsystem<RendererSubsystem>();

Frame loop

RunFrame() runs a four-phase cycle where engine subsystems handle setup and cleanup, leaving gameplay to update and render:

void App::RunFrame()
{
    BeginFrame();  // tick the clock, call GEngine->BeginFrame() + input begin
    Update();      // GEngine->Update(dt), then game logic
    Render();      // game draws the scene via render passes
    EndFrame();    // input end, GEngine->EndFrame() (present, flip, cleanup)
}

The engine manages swap chains, descriptor allocations, and barriers internally, allowing game code to work through render passes.

Shutdown

Shutdown cleans up in reverse registration order:

void App::Shutdown()
{
    m_game.reset();          // destroy game first
    GEngine->Shutdown();     // shuts down all subsystems in reverse order
    g_theWindow->Shutdown();
    delete g_theWindow;
    Engine::DestroyInstance();
}

Build system and integration

The engine and game live in separate Visual Studio projects. Engine.vcxproj compiles as a static library (.lib) referenced by the game project via <ProjectReference>.

Include paths

The game project configures two include roots:

$(SolutionDir)Code/           (game source, e.g. #include "Game/Framework/App.hpp")
$(SolutionDir)../Engine/Code/ (engine source, e.g. #include "Engine/Core/Engine.hpp")

Prefixing headers with Engine/ keeps boundaries clear across modules.

Library linking

The linker includes:

  • Temporary/Engine_x64_Debug/ (or Release) for the engine static library
  • Engine/Code/ThirdParty/DXC/lib/x64 for the DirectX Shader Compiler import library
  • Run/ for runtime DLLs

The explicit link dependency is dxcompiler.lib, with remaining Windows APIs resolved through the engine static library and SDK defaults.

Post-build asset syncing

After compilation, a post-build command copies the game binary to Run/ and executes CopyEngineResources.bat to sync default shaders, fonts, and YAML templates from .enigma/ into the working directory.

Third-party dependencies

Third-party dependencies reside in Engine/Code/ThirdParty/ and compile directly into the engine static library:

LibraryPurpose
d3dx12Direct3D 12 helper headers for barriers, descriptors, and resource descriptions
DXCDirectX Shader Compiler for runtime HLSL compilation targeting Shader Model 6.x
imguiImmediate-mode debug UI with DX11, DX12, and Win32 backends
jsonnlohmann/json single-header parser for configurations
TinyXML2Lightweight XML parser for legacy configs
stbSingle-header image loaders
tinygltfglTF 2.0 asset loader
yaml-cppYAML parser for engine and shader bundle configs
fmodAudio middleware linked dynamically via fmod64.dll
googletestUnit testing framework