Skip to main content

Overview

Filament uses an Entity-Component System (ECS) architecture where entities are lightweight identifiers and components add functionality. This design provides flexibility, performance, and clean separation of concerns.

Core Concepts

Entities

Entities are simple integer identifiers managed by the EntityManager. They represent objects in your scene but contain no data themselves:
utils::Entity entity = utils::EntityManager::get().create();
Entities are just IDs. All data and behavior comes from components attached to them.

Components

Components are data containers managed by specialized managers. Filament provides several component types:
  • Renderable - Geometry and materials (RenderableManager)
  • Transform - Position, rotation, scale (TransformManager)
  • Light - Light sources (LightManager)
  • Camera - Viewing parameters (Camera)

The Renderable Component

Creating Renderables

Renderables are created using the Builder pattern:
RenderableManager.h:68-81
auto renderable = utils::EntityManager::get().create();

RenderableManager::Builder(1)
        .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
        .material(0, matInstance)
        .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vertBuffer, indBuffer, 0, 3)
        .receiveShadows(false)
        .build(engine, renderable);

scene->addEntity(renderable);

Primitive-Based Architecture

From RenderableManager.h:62-66:
Renderables are bundles of primitives, each of which has its own geometry and material. All primitives in a particular renderable share a set of rendering attributes, such as whether they cast shadows or use vertex skinning.
RenderableManager::Builder(1)  // One primitive
    .geometry(0, type, vb, ib)
    .material(0, materialInstance)
    .build(engine, entity);

Working with Instances

From RenderableManager.h:83-86:
To modify the state of an existing renderable, clients should first use RenderableManager to get a temporary handle called an instance. The instance can then be used to get or set the renderable’s state. Please note that instances are ephemeral; clients should store entities, not instances.

Getting Component Instances

auto& rcm = engine->getRenderableManager();
RenderableManager::Instance instance = rcm.getInstance(entity);

if (rcm.hasComponent(entity)) {
    // Modify renderable properties
}
Instances are ephemeral. Always store entities, not instances. Instances should be retrieved fresh each time you need them.

Component Managers

Filament provides three main component managers accessible through the Engine:

RenderableManager

Manages geometry and rendering properties:
Engine.h:796-797
RenderableManager& getRenderableManager() noexcept;
Key Operations:
  • Create/destroy renderable components
  • Set geometry (vertex/index buffers)
  • Assign materials
  • Configure shadow casting/receiving
  • Set bounding boxes for culling

TransformManager

Manages spatial transformations:
Engine.h:805-807
TransformManager& getTransformManager() noexcept;
Example:
hellopbr.cpp:151-154
auto& tcm = engine->getTransformManager();
auto ti = tcm.getInstance(app.mesh.renderable);
tcm.setTransform(ti, app.transform * mat4f::rotation(now, float3{ 0, 1, 0 }));

LightManager

Manages light sources:
Engine.h:800-802
LightManager& getLightManager() noexcept;
Example:
hellopbr.cpp:133-141
app.light = em.create();
LightManager::Builder(LightManager::Type::SUN)
        .color(Color::toLinear<ACCURATE>(sRGBColor(0.98f, 0.92f, 0.89f)))
        .intensity(110000)
        .direction({ 0.7, -1, -0.8 })
        .sunAngularRadius(1.9f)
        .castShadows(false)
        .build(*engine, app.light);
scene->addEntity(app.light);

Scene Management

Scenes are flat containers that hold entity references:
Scene.h:38-46
/**
 * A Scene is a flat container of Renderable and Light instances.
 *
 * A Scene doesn't provide a hierarchy of Renderable objects, i.e.: it's not a scene-graph.
 * However, it manages the list of objects to render and the list of lights. Renderable
 * and Light objects can be added or removed from a Scene at any time.
 */

Adding Entities to Scene

Scene.h:115
scene->addEntity(entity);
An entity must have a Renderable or Light component to be useful in a scene. Entities without these components are ignored for rendering.

Scene Operations

scene->addEntity(entity);

Complete Example

Here’s a complete example showing the ECS workflow:
hellopbr.cpp:111-131
auto setup = [config=app.config, &app](Engine* engine, View* view, Scene* scene) {
    auto& tcm = engine->getTransformManager();
    auto& rcm = engine->getRenderableManager();
    auto& em = utils::EntityManager::get();

    // Instantiate material.
    app.material = Material::Builder()
        .package(RESOURCES_AIDEFAULTMAT_DATA, RESOURCES_AIDEFAULTMAT_SIZE).build(*engine);
    auto mi = app.materialInstance = app.material->createInstance();
    mi->setParameter("baseColor", RgbType::LINEAR, float3{0.8});
    mi->setParameter("metallic", 1.0f);
    mi->setParameter("roughness", 0.4f);
    mi->setParameter("reflectance", 0.5f);

    // Add geometry into the scene.
    app.mesh = MeshReader::loadMeshFromBuffer(engine, MONKEY_SUZANNE_DATA, nullptr, nullptr, mi);
    auto ti = tcm.getInstance(app.mesh.renderable);
    app.transform = mat4f{ mat3f(1), float3(0, 0, -4) } * tcm.getWorldTransform(ti);
    rcm.setCastShadows(rcm.getInstance(app.mesh.renderable), false);
    scene->addEntity(app.mesh.renderable);
};

ECS Benefits

Performance

Data-oriented design enables efficient memory access patterns and cache-friendly operations.

Flexibility

Add or remove components dynamically. Entities can have any combination of components.

Clarity

Clear separation between data (components) and identity (entities).

Scalability

Component managers can optimize storage and processing for thousands of entities.

Best Practices

Instances are temporary handles. Always store the utils::Entity and retrieve instances when needed:
// Good
utils::Entity myEntity = createEntity();
auto instance = manager.getInstance(myEntity);

// Bad - instance may become invalid
auto instance = manager.getInstance(entity);
storeForLater(instance);  // Don't do this!
Before getting an instance, verify the component exists:
if (manager.hasComponent(entity)) {
    auto instance = manager.getInstance(entity);
    // Safe to use instance
}
Destroy entities when no longer needed:
engine->destroy(renderable);
utils::EntityManager::get().destroy(entity);
Use batch operations when working with multiple entities:
scene->addEntities(entityArray, count);  // More efficient than loop

Advanced Topics

Geometry Types

From RenderableManager.h:162-166:
enum class GeometryType : uint8_t {
    DYNAMIC,        // dynamic geometry has no restriction
    STATIC_BOUNDS,  // bounds and world space transform are immutable
    STATIC          // skinning/morphing not allowed and Vertex/IndexBuffer immutable
};
Specifying geometry type helps Filament optimize rendering:
RenderableManager::Builder(1)
    .geometryType(GeometryType::STATIC)
    // ... other settings

Layer Masking

Control visibility of groups of renderables:
RenderableManager::Builder(1)
    .layerMask(0xFF, 0x01)  // Set which layers this appears in
    .build(engine, entity);
Then in your view:
view->setVisibleLayers(0x01, 0x01);  // Show only layer 1

Rendering Loop

Learn how entities are rendered each frame

Materials Overview

Understand material assignment to renderables

Build docs developers (and LLMs) love