Skip to main content

Overview

RenderableManager is the factory and manager for renderables, which are entities that can be drawn. 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.

Key Concepts

  • Renderables: Entities that can be drawn in a Scene
  • Primitives: Individual geometry components within a renderable
  • Instance: A temporary handle to access renderable state

Builder

The Builder class is used to add renderable components to entities.

Constructor

explicit Builder(size_t count) noexcept
Creates a builder for renderable components.
count
size_t
required
The number of primitives that will be supplied to the builder
Example:
// Create renderable with 2 primitives
RenderableManager::Builder builder(2);

Geometry Methods

geometry

Builder& geometry(
    size_t index,
    PrimitiveType type,
    VertexBuffer* vertices,
    IndexBuffer* indices,
    size_t offset,
    size_t minIndex,
    size_t maxIndex,
    size_t count
) noexcept
Specifies the geometry data for a primitive.
index
size_t
required
Zero-based index of the primitive, must be less than count passed to constructor
type
PrimitiveType
required
The topology of the primitive (e.g., TRIANGLES, LINES, POINTS)
vertices
VertexBuffer*
required
The vertex buffer containing vertex attributes
indices
IndexBuffer*
required
The index buffer (either uint16 or uint32)
offset
size_t
required
Where in the index buffer to start reading (expressed as number of indices)
minIndex
size_t
required
Minimum index contained in the index buffer
maxIndex
size_t
required
Maximum index contained in the index buffer
count
size_t
required
Number of indices to read (for triangles, should be multiple of 3)
Returns: Reference to this Builder for chaining calls. PrimitiveType enum values:
  • POINTS - Point primitives
  • LINES - Line primitives
  • LINE_STRIP - Line strip
  • TRIANGLES - Triangle primitives
  • TRIANGLE_STRIP - Triangle strip
Simplified overloads:
Builder& geometry(
    size_t index,
    PrimitiveType type,
    VertexBuffer* vertices,
    IndexBuffer* indices,
    size_t offset,
    size_t count
) noexcept

Builder& geometry(
    size_t index,
    PrimitiveType type,
    VertexBuffer* vertices,
    IndexBuffer* indices
) noexcept

geometryType

Builder& geometryType(GeometryType type) noexcept
Specifies the type of geometry for this renderable.
type
GeometryType
required
Type of geometry
Returns: Reference to this Builder for chaining calls. GeometryType enum values:
  • DYNAMIC - Dynamic geometry has no restrictions (default)
  • STATIC_BOUNDS - Bounds and world-space transform are immutable
  • STATIC - Skinning/morphing not allowed and VertexBuffer/IndexBuffer immutable

material

Builder& material(
    size_t index,
    MaterialInstance const* materialInstance
) noexcept
Binds a material instance to the specified primitive.
index
size_t
required
Zero-based index of the primitive
materialInstance
MaterialInstance const*
required
The material to bind
Returns: Reference to this Builder for chaining calls.
If no material is specified for a primitive, Filament will fall back to a basic default material.

Bounding Box

boundingBox

Builder& boundingBox(const Box& axisAlignedBoundingBox) noexcept
Sets the axis-aligned bounding box of the renderable.
axisAlignedBoundingBox
const Box&
required
Object-space AABB used for frustum culling
Returns: Reference to this Builder for chaining calls.
This is an object-space AABB used for frustum culling. For skinning and morphing, this should encompass all possible vertex positions. It is mandatory unless culling is disabled.
Example:
builder.boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }});

Layer and Visibility

layerMask

Builder& layerMask(uint8_t select, uint8_t values) noexcept
Sets bits in a visibility mask. By default, this is 0x1.
select
uint8_t
required
The set of bits to affect
values
uint8_t
required
The replacement values for the affected bits
Returns: Reference to this Builder for chaining calls. Example:
// Set bit 1 and reset bits 0 and 2 while leaving other bits unaffected
builder.layerMask(7, 2);

Drawing Order

priority

Builder& priority(uint8_t priority) noexcept
Provides coarse-grained control over draw order.
priority
uint8_t
required
Priority value clamped to range [0..7], defaults to 4. Higher values mean lower priority (rendered last)
Returns: Reference to this Builder for chaining calls.
Priority is applied separately for opaque and translucent objects. Opaque objects are always drawn before translucent objects regardless of priority. The Skybox uses priority 7 (lowest).

channel

Builder& channel(uint8_t channel) noexcept
Sets the channel this renderable is associated with.
channel
uint8_t
required
Channel value clamped to range [0..7], defaults to 2
Returns: Reference to this Builder for chaining calls.
All renderables in a given channel are rendered together, regardless of anything else. Channels enforce the strongest ordering. The default channel is 2.
Channels 0 and 1 may not have render primitives using a material with refractionType set to screenspace.

blendOrder

Builder& blendOrder(size_t primitiveIndex, uint16_t order) noexcept
Sets the drawing order for blended primitives.
primitiveIndex
size_t
required
The primitive of interest
order
uint16_t
required
Draw order number (0 by default). Only the lowest 15 bits are used
Returns: Reference to this Builder for chaining calls.

globalBlendOrderEnabled

Builder& globalBlendOrderEnabled(size_t primitiveIndex, bool enabled) noexcept
Sets whether the blend order is global or local to this Renderable.
primitiveIndex
size_t
required
The primitive of interest
enabled
bool
required
true for global, false for local blend ordering (default)
Returns: Reference to this Builder for chaining calls.

Culling and Visibility

culling

Builder& culling(bool enable) noexcept
Controls frustum culling, true by default.
enable
bool
required
Whether to enable frustum culling
Returns: Reference to this Builder for chaining calls.
Do not confuse frustum culling with backface culling. Backface culling is controlled via the material.

lightChannel

Builder& lightChannel(unsigned int channel, bool enable = true) noexcept
Enables or disables a light channel. Light channel 0 is enabled by default.
channel
unsigned int
required
Light channel to enable or disable, between 0 and 7
enable
bool
Whether to enable or disable the light channel (default true)
Returns: Reference to this Builder for chaining calls.

Shadows

castShadows

Builder& castShadows(bool enable) noexcept
Controls if this renderable casts shadows, false by default.
enable
bool
required
Whether to enable shadow casting
Returns: Reference to this Builder for chaining calls.
If the View’s shadow type is set to ShadowType::VSM, castShadows should only be disabled if either receiveShadows is also disabled OR the object is guaranteed to not cast shadows on itself or other objects.

receiveShadows

Builder& receiveShadows(bool enable) noexcept
Controls if this renderable receives shadows, true by default.
enable
bool
required
Whether to enable shadow receiving
Returns: Reference to this Builder for chaining calls.

screenSpaceContactShadows

Builder& screenSpaceContactShadows(bool enable) noexcept
Controls if this renderable uses screen-space contact shadows (off by default).
enable
bool
required
Whether to enable screen-space contact shadows
Returns: Reference to this Builder for chaining calls.
This is more expensive but can improve shadow quality, especially in large scenes.

Skinning

enableSkinningBuffers

Builder& enableSkinningBuffers(bool enabled = true) noexcept
Allows bones to be swapped out and shared using SkinningBuffer.
enabled
bool
If true, enables buffer object mode. False by default
Returns: Reference to this Builder for chaining calls.
If skinning buffer mode is enabled, clients must call setSkinningBuffer() rather than setBones().

skinning (with SkinningBuffer)

Builder& skinning(
    SkinningBuffer* skinningBuffer,
    size_t count,
    size_t offset
) noexcept
Enables GPU vertex skinning for up to 255 bones using a SkinningBuffer.
skinningBuffer
SkinningBuffer*
required
The SkinningBuffer to use
count
size_t
required
Number of bone transforms (up to 255)
offset
size_t
required
Offset in the SkinningBuffer
Returns: Reference to this Builder for chaining calls.
Skinning Buffer mode must be enabled. Each vertex can be affected by up to 4 bones. The VertexBuffer must provide BONE_INDICES (uvec4) and BONE_WEIGHTS (float4).

skinning (with transforms)

Builder& skinning(size_t boneCount, math::mat4f const* transforms) noexcept
Builder& skinning(size_t boneCount, Bone const* bones) noexcept
Builder& skinning(size_t boneCount) noexcept
Enables GPU vertex skinning for up to 255 bones using transform matrices or Bone structures.
boneCount
size_t
required
Number of bone transforms (0 to disable, up to 255)
transforms
math::mat4f const*
The initial set of transforms (one for each bone)
bones
Bone const*
The initial set of bones (quaternion + translation)
Returns: Reference to this Builder for chaining calls. Bone structure:
struct Bone {
    math::quatf unitQuaternion = { 1.f, 0.f, 0.f, 0.f };
    math::float3 translation = { 0.f, 0.f, 0.f };
    float reserved = 0;
};

boneIndicesAndWeights

Builder& boneIndicesAndWeights(
    size_t primitiveIndex,
    math::float2 const* indicesAndWeights,
    size_t count,
    size_t bonesPerVertex
) noexcept
Defines bone indices and weights pairs for advanced vertex skinning.
primitiveIndex
size_t
required
Zero-based index of the primitive
indicesAndWeights
math::float2 const*
required
Pairs of bone index and bone weight for all vertices sequentially. The x component is the bone index, y is the weight
count
size_t
required
Number of all pairs, must be a multiple of vertexCount. count = vertexCount * bonesPerVertex
bonesPerVertex
size_t
required
Number of bone pairs per vertex (can exceed 4)
Returns: Reference to this Builder for chaining calls.
This substitutes BONE_INDICES and BONE_WEIGHTS defined in VertexBuffer. All bone weights of one vertex should sum to one; otherwise they will be normalized.

Morphing

morphing (legacy)

Builder& morphing(size_t targetCount) noexcept
Enables legacy vertex morphing targets (up to 4 targets).
targetCount
size_t
required
Number of morph targets (0 to disable, up to 4)
Returns: Reference to this Builder for chaining calls.
Legacy morphing only supports up to 4 morph targets and will be deprecated. Use MorphTargetBuffer instead for standard morphing.

morphing (standard)

Builder& morphing(MorphTargetBuffer* morphTargetBuffer) noexcept
Enables standard vertex morphing using a MorphTargetBuffer.
morphTargetBuffer
MorphTargetBuffer*
required
The morph target buffer to use
Returns: Reference to this Builder for chaining calls.
Standard morphing supports up to CONFIG_MAX_MORPH_TARGET_COUNT targets.
Builder& morphing(
    uint8_t level,
    size_t primitiveIndex,
    size_t offset
) noexcept
Specifies the range of the MorphTargetBuffer to use with a primitive.
level
uint8_t
required
The level of detail (only 0 can be specified currently)
primitiveIndex
size_t
required
Zero-based index of the primitive
offset
size_t
required
Offset in the morph target buffer (expressed as number of vertices)
Returns: Reference to this Builder for chaining calls.

Fog

fog

Builder& fog(bool enabled = true) noexcept
Controls if this renderable is affected by large-scale fog.
enabled
bool
If true, enables large-scale fog on this object (default is true)
Returns: Reference to this Builder for chaining calls.

Instancing

instances (count only)

Builder& instances(size_t instanceCount) noexcept
Specifies the number of draw instances of this renderable.
instanceCount
size_t
required
The number of instances, silently clamped between 1 and 32767
Returns: Reference to this Builder for chaining calls.
All instances are culled using the same bounding box. The material must set its instanced parameter to true to use getInstanceIndex() in shaders.

instances (with InstanceBuffer)

Builder& instances(
    size_t instanceCount,
    InstanceBuffer* instanceBuffer
) noexcept
Specifies the number of instances and an InstanceBuffer containing their local transforms.
instanceCount
size_t
required
The number of instances, clamped between 1 and Engine::getMaxAutomaticInstances()
instanceBuffer
InstanceBuffer*
required
An InstanceBuffer containing at least instanceCount transforms
Returns: Reference to this Builder for chaining calls.
Only the VERTEX_DOMAIN_OBJECT vertex domain is supported.

build

Result build(Engine& engine, utils::Entity entity)
Adds the Renderable component to an entity.
engine
Engine&
required
Reference to the filament::Engine to associate this Renderable with
entity
utils::Entity
required
Entity to add the Renderable component to
Returns: Success if the component was created successfully, Error otherwise.
If this component already exists on the given entity and the construction is successful, it is first destroyed. In case of error, the existing component is unmodified.
Example:
utils::Entity entity = utils::EntityManager::get().create();

RenderableManager::Builder(1)
    .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
    .material(0, materialInstance)
    .geometry(0, RenderableManager::PrimitiveType::TRIANGLES,
              vertexBuffer, indexBuffer, 0, 3)
    .receiveShadows(false)
    .build(*engine, entity);

scene->addEntity(entity);

RenderableManager Methods

Component Management

hasComponent

bool hasComponent(utils::Entity e) const noexcept
Checks if the given entity already has a renderable component.
e
utils::Entity
required
Entity to check
Returns: true if the entity has a renderable component.

getInstance

Instance getInstance(utils::Entity e) const noexcept
Gets a temporary handle that can be used to access the renderable state.
e
utils::Entity
required
Entity to get instance for
Returns: Non-zero Instance handle if the entity has a renderable component, 0 otherwise.

getEntity

utils::Entity getEntity(Instance i) const noexcept
Retrieves the Entity of the component from its Instance.
i
Instance
required
Instance of the component obtained from getInstance()
Returns: The entity associated with the instance.

destroy

void destroy(utils::Entity e) noexcept
Destroys the renderable component in the given entity.
e
utils::Entity
required
Entity to remove renderable from

Bounding Box

setAxisAlignedBoundingBox

void setAxisAlignedBoundingBox(Instance instance, const Box& aabb)
Changes the bounding box used for frustum culling.
instance
Instance
required
Instance of the component
aabb
const Box&
required
New axis-aligned bounding box
The renderable must not have static geometry enabled.

getAxisAlignedBoundingBox

const Box& getAxisAlignedBoundingBox(Instance instance) const noexcept
Gets the bounding box used for frustum culling.
instance
Instance
required
Instance of the component
Returns: The current axis-aligned bounding box.

Layer Mask and Priority

setLayerMask

void setLayerMask(Instance instance, uint8_t select, uint8_t values) noexcept
Changes the visibility bits.
instance
Instance
required
Instance of the component
select
uint8_t
required
Bits to affect
values
uint8_t
required
New values for selected bits

getLayerMask

uint8_t getLayerMask(Instance instance) const noexcept
Gets the visibility bits.
instance
Instance
required
Instance of the component
Returns: The current layer mask.

setPriority

void setPriority(Instance instance, uint8_t priority) noexcept
Changes the coarse-level draw ordering.
instance
Instance
required
Instance of the component
priority
uint8_t
required
New priority value (0-7)

getPriority

uint8_t getPriority(Instance instance) const noexcept
Gets the coarse-level draw ordering.
instance
Instance
required
Instance of the component
Returns: The current priority.

setChannel

void setChannel(Instance instance, uint8_t channel) noexcept
Changes the channel a renderable is associated with.
instance
Instance
required
Instance of the component
channel
uint8_t
required
New channel value (0-7)

getChannel

uint8_t getChannel(Instance instance) const noexcept
Gets the channel a renderable is associated with.
instance
Instance
required
Instance of the component
Returns: The current channel.

Culling and Visibility

setCulling

void setCulling(Instance instance, bool enable) noexcept
Changes whether frustum culling is on.
instance
Instance
required
Instance of the component
enable
bool
required
Whether to enable culling

isCullingEnabled

bool isCullingEnabled(Instance instance) const noexcept
Gets whether frustum culling is on.
instance
Instance
required
Instance of the component
Returns: true if culling is enabled.

setFogEnabled

void setFogEnabled(Instance instance, bool enable) noexcept
Changes whether large-scale fog is applied to this renderable.
instance
Instance
required
Instance of the component
enable
bool
required
Whether to enable fog

getFogEnabled

bool getFogEnabled(Instance instance) const noexcept
Returns whether large-scale fog is enabled for this renderable.
instance
Instance
required
Instance of the component
Returns: true if fog is enabled.

setLightChannel

void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept
Enables or disables a light channel.
instance
Instance
required
Instance of the component
channel
unsigned int
required
Light channel (0-7)
enable
bool
required
Whether to enable the channel

getLightChannel

bool getLightChannel(Instance instance, unsigned int channel) const noexcept
Returns whether a light channel is enabled.
instance
Instance
required
Instance of the component
channel
unsigned int
required
Light channel to query (0-7)
Returns: true if the light channel is enabled.

Shadow Methods

setCastShadows

void setCastShadows(Instance instance, bool enable) noexcept
Changes whether the renderable casts shadows.
instance
Instance
required
Instance of the component
enable
bool
required
Whether to enable shadow casting

setReceiveShadows

void setReceiveShadows(Instance instance, bool enable) noexcept
Changes whether the renderable can receive shadows.
instance
Instance
required
Instance of the component
enable
bool
required
Whether to enable shadow receiving

setScreenSpaceContactShadows

void setScreenSpaceContactShadows(Instance instance, bool enable) noexcept
Changes whether the renderable can use screen-space contact shadows.
instance
Instance
required
Instance of the component
enable
bool
required
Whether to enable screen-space contact shadows

isShadowCaster

bool isShadowCaster(Instance instance) const noexcept
Checks if the renderable can cast shadows.
instance
Instance
required
Instance of the component
Returns: true if shadow casting is enabled.

isShadowReceiver

bool isShadowReceiver(Instance instance) const noexcept
Checks if the renderable can receive shadows.
instance
Instance
required
Instance of the component
Returns: true if shadow receiving is enabled.

isScreenSpaceContactShadowsEnabled

bool isScreenSpaceContactShadowsEnabled(Instance instance) const noexcept
Checks if the renderable can use screen-space contact shadows.
instance
Instance
required
Instance of the component
Returns: true if screen-space contact shadows are enabled.

Skinning Methods

setBones

void setBones(
    Instance instance,
    Bone const* transforms,
    size_t boneCount = 1,
    size_t offset = 0
)

void setBones(
    Instance instance,
    math::mat4f const* transforms,
    size_t boneCount = 1,
    size_t offset = 0
)
Updates the bone transforms in the range [offset, offset + boneCount).
instance
Instance
required
Instance of the component
transforms
Bone const* or math::mat4f const*
required
Array of bone transforms
boneCount
size_t
Number of bones to update (default 1)
offset
size_t
Starting offset for update (default 0)
The bones must be pre-allocated using Builder::skinning().

setSkinningBuffer

void setSkinningBuffer(
    Instance instance,
    SkinningBuffer* skinningBuffer,
    size_t count,
    size_t offset
)
Associates a region of a SkinningBuffer to a renderable instance.
instance
Instance
required
Instance of the component
skinningBuffer
SkinningBuffer*
required
Skinning buffer to associate
count
size_t
required
Size of the region in bones (must be less than or equal to 256)
offset
size_t
required
Start offset of the region in bones
Due to hardware limitations, offset + 256 must be smaller or equal to skinningBuffer->getBoneCount().

Morphing Methods

setMorphWeights

void setMorphWeights(
    Instance instance,
    float const* weights,
    size_t count,
    size_t offset = 0
)
Updates the vertex morphing weights on a renderable.
instance
Instance
required
Instance of the component
weights
float const*
required
Pointer to morph target weights to update
count
size_t
required
Number of morph target weights
offset
size_t
Index of the first morph target weight to set (default 0)
The renderable must be built with morphing enabled. In legacy morphing mode, only the first 4 weights are considered.

setMorphTargetBufferOffsetAt

void setMorphTargetBufferOffsetAt(
    Instance instance,
    uint8_t level,
    size_t primitiveIndex,
    size_t offset
)
Associates a MorphTargetBuffer to the given primitive.
instance
Instance
required
Instance of the component
level
uint8_t
required
Level of detail (only 0 is currently supported)
primitiveIndex
size_t
required
Index of the primitive
offset
size_t
required
Offset in the buffer

getMorphTargetBuffer

MorphTargetBuffer* getMorphTargetBuffer(Instance instance) const noexcept
Gets the MorphTargetBuffer for the given renderable or null if it doesn’t exist.
instance
Instance
required
Instance of the component
Returns: Pointer to MorphTargetBuffer or nullptr.

getMorphTargetCount

size_t getMorphTargetCount(Instance instance) const noexcept
Gets the number of morphing targets in the given entity.
instance
Instance
required
Instance of the component
Returns: Number of morph targets.

Primitive Methods

getPrimitiveCount

size_t getPrimitiveCount(Instance instance) const noexcept
Gets the immutable number of primitives in the given renderable.
instance
Instance
required
Instance of the component
Returns: Number of primitives.

setMaterialInstanceAt

void setMaterialInstanceAt(
    Instance instance,
    size_t primitiveIndex,
    MaterialInstance const* materialInstance
)
Changes the material instance binding for the given primitive.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
materialInstance
MaterialInstance const*
required
New material instance to bind

getMaterialInstanceAt

MaterialInstance* getMaterialInstanceAt(
    Instance instance,
    size_t primitiveIndex
) const noexcept
Retrieves the material instance that is bound to the given primitive.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
Returns: Pointer to the MaterialInstance or nullptr.

setGeometryAt

void setGeometryAt(
    Instance instance,
    size_t primitiveIndex,
    PrimitiveType type,
    VertexBuffer* vertices,
    IndexBuffer* indices,
    size_t offset,
    size_t count
) noexcept
Changes the geometry for the given primitive.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
type
PrimitiveType
required
Primitive topology type
vertices
VertexBuffer*
required
New vertex buffer
indices
IndexBuffer*
required
New index buffer
offset
size_t
required
Offset into index buffer
count
size_t
required
Number of indices

setBlendOrderAt

void setBlendOrderAt(
    Instance instance,
    size_t primitiveIndex,
    uint16_t order
) noexcept
Changes the drawing order for blended primitives.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
order
uint16_t
required
Draw order number (only lowest 15 bits used)

getBlendOrderAt

uint16_t getBlendOrderAt(
    Instance instance,
    size_t primitiveIndex
) const noexcept
Gets the drawing order for blended primitives.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
Returns: The blend order value.

setGlobalBlendOrderEnabledAt

void setGlobalBlendOrderEnabledAt(
    Instance instance,
    size_t primitiveIndex,
    bool enabled
) noexcept
Changes whether the blend order is global or local to this Renderable.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
enabled
bool
required
true for global, false for local blend ordering

getEnabledAttributesAt

AttributeBitset getEnabledAttributesAt(
    Instance instance,
    size_t primitiveIndex
) const noexcept
Retrieves the set of enabled attribute slots in the given primitive’s VertexBuffer.
instance
Instance
required
Instance of the component
primitiveIndex
size_t
required
Index of the primitive
Returns: Bitset of enabled attributes.

Utility Methods

computeAABB

template<typename VECTOR, typename INDEX>
static Box computeAABB(
    VECTOR const* vertices,
    INDEX const* indices,
    size_t count,
    size_t stride = sizeof(VECTOR)
) noexcept
Utility method that computes the axis-aligned bounding box from a set of vertices.
vertices
VECTOR const*
required
Pointer to vertex data (float3, float4, half3, or half4)
indices
INDEX const*
required
Pointer to index data (uint16_t or uint32_t)
count
size_t
required
Number of indices
stride
size_t
Stride between vertices in bytes (default is sizeof(VECTOR))
Returns: Computed axis-aligned bounding box. Example:
math::float3 vertices[] = { /* ... */ };
uint16_t indices[] = { /* ... */ };

Box aabb = RenderableManager::computeAABB(
    vertices, indices, sizeof(indices) / sizeof(uint16_t)
);

Example Usage

#include <filament/Engine.h>
#include <filament/RenderableManager.h>
#include <filament/VertexBuffer.h>
#include <filament/IndexBuffer.h>
#include <filament/Material.h>
#include <filament/Scene.h>
#include <utils/EntityManager.h>

// Create entity
utils::Entity entity = utils::EntityManager::get().create();

// Create vertex and index buffers
VertexBuffer* vb = VertexBuffer::Builder()
    .vertexCount(3)
    .bufferCount(1)
    .attribute(VertexAttribute::POSITION, 0,
               VertexBuffer::AttributeType::FLOAT2, 0, 12)
    .attribute(VertexAttribute::COLOR, 0,
               VertexBuffer::AttributeType::UBYTE4, 8, 12)
    .normalized(VertexAttribute::COLOR)
    .build(*engine);

IndexBuffer* ib = IndexBuffer::Builder()
    .indexCount(3)
    .bufferType(IndexBuffer::IndexType::USHORT)
    .build(*engine);

// Build renderable
RenderableManager::Builder(1)
    .boundingBox({{ -1, -1, -1 }, { 1, 1, 1 }})
    .material(0, materialInstance)
    .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, vb, ib, 0, 3)
    .receiveShadows(false)
    .castShadows(false)
    .culling(false)
    .build(*engine, entity);

// Add to scene
scene->addEntity(entity);

// Get instance and modify at runtime
auto& rcm = engine->getRenderableManager();
auto instance = rcm.getInstance(entity);

rcm.setCastShadows(instance, true);
rcm.setPriority(instance, 6);

// Cleanup
rcm.destroy(entity);
utils::EntityManager::get().destroy(entity);

Build docs developers (and LLMs) love