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.
Single Primitive
Multiple Primitives
RenderableManager :: Builder ( 1 ) // One primitive
. geometry ( 0 , type, vb, ib)
. material ( 0 , materialInstance)
. build (engine, entity);
RenderableManager :: Builder ( 3 ) // Three primitives
. geometry ( 0 , type, vb1, ib1)
. material ( 0 , material1)
. geometry ( 1 , type, vb2, ib2)
. material ( 1 , material2)
. geometry ( 2 , type, vb3, ib3)
. material ( 2 , material3)
. 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:
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
Manages spatial transformations:
TransformManager & getTransformManager () noexcept ;
Example:
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:
LightManager & getLightManager () noexcept ;
Example:
app . light = em . create ();
LightManager :: Builder ( LightManager :: Type ::SUN)
. color ( Color :: toLinear < ACCURATE >( sRGBColor ( 0.98 f , 0.92 f , 0.89 f )))
. intensity ( 110000 )
. direction ({ 0.7 , - 1 , - 0.8 })
. sunAngularRadius ( 1.9 f )
. castShadows ( false )
. build ( * engine, app . light );
scene -> addEntity ( app . light );
Scene Management
Scenes are flat containers that hold entity references:
/**
* 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 -> 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
Add Single Entity
Add Multiple Entities
Remove Entity
Remove All
Check Existence
scene -> addEntity (entity);
Complete Example
Here’s a complete example showing the ECS workflow:
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.0 f );
mi -> setParameter ( "roughness" , 0.4 f );
mi -> setParameter ( "reflectance" , 0.5 f );
// 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
Store Entities, Not Instances
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!
Check Component Existence
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 ( 0x FF , 0x 01 ) // Set which layers this appears in
. build (engine, entity);
Then in your view:
view -> setVisibleLayers ( 0x 01 , 0x 01 ); // Show only layer 1
Rendering Loop Learn how entities are rendered each frame
Materials Overview Understand material assignment to renderables