IronEngine - Entity-Component System based 3D game engine

Still work in progress!

I'm currently reading through Game Engine Architecture by Jason Gregory, it's a fantastic read. I've been able to review almost all of the ideas I learned in college and get to be formally introduced to game engine development. To help the reading really stick I've started this project along with it.

My initial goal is to make a 3D engine with a simple exposed API that any programmer can utilize without difficulties, similar to how raylib is very easy to use. So, in actuality, this is a framework project for now, not a full-fledged engine.

Here's the source code!

Entity-Component System

It was clear to me that the first thing I wanted in my engine was that it came with its own pre-implemented entity-component system (ECS), so programmers utilizing my framework could easily create entities and attach any functionality to them without worrying about the underlying implementation.

In ECS, an entity is just an unique identifier number (an unsigned 64-bit integer in this case). Normally an entity would cost some larger amount of memory so a programmer would need to worry about the number of entities in his game, with ECS entities are just as light-weight as possible. The memory cost of an entity will only be the 64-bit ID plus its components registered in the systems.

ECS also allows for great flexibility in the game design. For example let's look into how you would set up a simple static model in the world with IronEngine:

    Entity entity = Iron().create_entity(); // Entity is just a number
    Transform tf = {position, orientation, scale};
    Iron().transforms.add(entity, tf); // Register a transform component to the entity
    MeshComponent mesh_component = {PrimitiveMeshes::CUBE};
    Iron().meshes.add(entity, mesh_component); // Register a mesh component to the entity

And with just that, in the next render loop this cube mesh will be rendered with the given transform. One of the powers of ECS is that let's say a wizard wants to control this cube by levitation, all we need to do is to register a LevitatingObject system which will handle the logic of modifying an entity's transform component. Then we simply add a LevitatingObject component to the cube we made above.

Another ECS power is cache locality among components in each system. You can both quickly lookup a component of a given entity ID and iterate over all components in a system because the components are stored next to each other in a simple array.

I try to avoid using templated classes but this is a great use case, all components systems are implemented as such:

template<typename T>
class ComponentManager {
public: // methods for getting/removing/iterating
    ...
private: // all of the memory
    std::vector<T> components;
    std::vector<Entity> index_to_entity;
    std::unordered_map<Entity, size_t> entity_to_index;
};

As you can see, all components are kept in a contiguous array. We keep index_to_entity so we can quickly return all entities that have a component in the system. To get a component of a given entity we have the hash map entity_to_index so we retrieve the index in the components array without having to iterate over index_to_entity. Components are always maintained in a compact array.

Physics

For rigid-body physics, the project is currently using an open-source external library called ReactPhysics3D. It was very easy to setup to work with the entity-component system.

IronEngine API

Here's a taste of the simplicity of the current framework API. The engine API is exposed by methods of a main singleton class IronEngine acquired by the Iron() function.

int main () {
    Iron().init();
    test_physics(); // spawns multiple physics entities
    while (Iron().is_running()) {
        Iron().update();
        Iron().render();
        Iron().handle_inputs();
    }
    Iron().clear();
    return 0;
}

Future

I'll probably be working on this project for a long time given the scope. These are some of the main features I want to tackle: Batch rendering, serialization, network, transform tree, scenes (Godot-like), model loading, animations and skeletons