Allow submeshes to be set when adding meshes to a Renderable.

Also add support for blend order.

PiperOrigin-RevId: 900061778
Change-Id: I601757a8672fed23e45defd4cdbe4e9526ea3b70
This commit is contained in:
Haroon Qureshi
2026-04-15 03:25:09 -07:00
committed by Copybara-Service
parent 9289905c9a
commit f24f9ef44d
3 changed files with 135 additions and 71 deletions
@@ -31,11 +31,11 @@ Renderable::Renderable(filament::Engine* engine) : material_(engine) {}
Renderable::~Renderable() noexcept {
while (!entities_.empty()) {
RemoveLast();
RemoveLastEntity();
}
}
void Renderable::RemoveLast() {
void Renderable::RemoveLastEntity() {
if (entities_.empty()) {
return;
}
@@ -53,37 +53,32 @@ void Renderable::RemoveLast() {
meshes_.pop_back();
}
void Renderable::Update(int index, const Mesh* mesh) {
if (index < 0 || index >= entities_.size()) {
mju_error("Invalid index %d for renderable.", index);
}
utils::Entity& entity = entities_[index];
UpdateEntity(entity, mesh);
UpdateMeshes(index, mesh);
void Renderable::UpdateMesh(int index, const Mesh* mesh, int elem_offset,
int elem_count) {
MeshInfo& mesh_info = SetMesh(index, mesh, nullptr, elem_offset, elem_count);
UpdateEntity(index, mesh_info);
}
void Renderable::Update(int index, MeshPtr mesh) {
if (index < 0 || index >= entities_.size()) {
mju_error("Invalid index %d for renderable.", index);
}
utils::Entity& entity = entities_[index];
UpdateEntity(entity, mesh.get());
UpdateMeshes(index, mesh.get(), std::move(mesh));
void Renderable::UpdateMesh(int index, MeshPtr mesh, int elem_offset,
int elem_count) {
MeshInfo& mesh_info =
SetMesh(index, mesh.get(), std::move(mesh), elem_offset, elem_count);
UpdateEntity(index, mesh_info);
}
void Renderable::Append(const Mesh* mesh) {
utils::Entity entity = CreateEntity(mesh);
entities_.push_back(entity);
meshes_.push_back({nullptr, mesh});
void Renderable::AppendMesh(const Mesh* mesh, int elem_offset, int elem_count) {
MeshInfo& mesh_info = SetMesh(-1, mesh, nullptr, elem_offset, elem_count);
AppendEntity(mesh_info);
}
void Renderable::Append(MeshPtr mesh) {
utils::Entity entity = CreateEntity(mesh.get());
entities_.push_back(entity);
meshes_.push_back({std::move(mesh), mesh.get()});
void Renderable::AppendMesh(MeshPtr mesh, int elem_offset, int elem_count) {
MeshInfo& mesh_info =
SetMesh(-1, mesh.get(), std::move(mesh), elem_offset, elem_count);
AppendEntity(mesh_info);
}
utils::Entity Renderable::CreateEntity(const Mesh* mesh) {
void Renderable::AppendEntity(const MeshInfo& mesh_info) {
const Mesh* mesh = mesh_info.mesh;
filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer();
if (vertex_buffer == nullptr) {
mju_error("Invalid (null) vertex buffer.");
@@ -100,7 +95,8 @@ utils::Entity Renderable::CreateEntity(const Mesh* mesh) {
}
filament::RenderableManager::Builder builder(1);
builder.geometry(0, mesh->GetPrimitiveType(), vertex_buffer, index_buffer);
builder.geometry(0, mesh->GetPrimitiveType(), vertex_buffer, index_buffer,
mesh_info.elem_offset, mesh_info.elem_count);
if (mesh->HasBounds()) {
builder.boundingBox(mesh->GetBounds());
} else {
@@ -113,17 +109,23 @@ utils::Entity Renderable::CreateEntity(const Mesh* mesh) {
builder.receiveShadows(receive_shadows_);
builder.layerMask(0xff, layer_mask_);
builder.priority(priority_);
builder.blendOrder(0, blend_order_);
builder.screenSpaceContactShadows(true);
;
builder.build(*GetEngine(), entity);
if (assigned_scene_) {
assigned_scene_->addEntity(entity);
}
return entity;
entities_.push_back(entity);
}
void Renderable::UpdateEntity(utils::Entity entity, const Mesh* mesh) {
void Renderable::UpdateEntity(int index, const MeshInfo& mesh_info) {
if (index < 0 || index >= entities_.size()) {
mju_error("Invalid index %d for renderable.", index);
}
utils::Entity entity = entities_[index];
const Mesh* mesh = mesh_info.mesh;
filament::VertexBuffer* vertex_buffer = mesh->GetFilamentVertexBuffer();
if (vertex_buffer == nullptr) {
mju_error("Invalid (null) vertex buffer.");
@@ -136,16 +138,32 @@ void Renderable::UpdateEntity(utils::Entity entity, const Mesh* mesh) {
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
rm.setGeometryAt(rm.getInstance(entity), 0, mesh->GetPrimitiveType(),
vertex_buffer, index_buffer, 0,
index_buffer->getIndexCount());
vertex_buffer, index_buffer, mesh_info.elem_offset,
mesh_info.elem_count);
}
void Renderable::UpdateMeshes(int index, const Mesh* mesh, MeshPtr owned_mesh) {
if (index < 0 || index >= meshes_.size()) {
Renderable::MeshInfo& Renderable::SetMesh(int index, const Mesh* mesh,
MeshPtr owned_mesh, int elem_offset,
int elem_count) {
if (index == -1) {
index = meshes_.size();
meshes_.emplace_back();
}
if (index < 0 || index >= static_cast<int>(meshes_.size())) {
mju_error("Invalid index %d for renderable.", index);
}
meshes_[index].owned_mesh = std::move(owned_mesh);
meshes_[index].mesh = mesh;
MeshInfo* mesh_info = &meshes_[index];
mesh_info->owned_mesh = std::move(owned_mesh);
mesh_info->mesh = mesh;
mesh_info->elem_offset = elem_offset;
mesh_info->elem_count = elem_count;
if (mesh_info->elem_count == 0) {
const int total =
mesh_info->mesh->GetFilamentIndexBuffer()->getIndexCount();
mesh_info->elem_count = total - mesh_info->elem_offset;
}
return *mesh_info;
}
void Renderable::AddToScene(filament::Scene* scene) {
@@ -209,6 +227,19 @@ std::uint8_t Renderable::SetPriority(std::uint8_t priority) {
return prev;
}
std::uint16_t Renderable::SetBlendOrder(std::uint16_t blend_order) {
std::uint16_t prev = blend_order_;
if (blend_order != blend_order_) {
blend_order_ = blend_order;
filament::RenderableManager& rm = GetEngine()->getRenderableManager();
for (utils::Entity& entity : entities_) {
rm.setBlendOrderAt(rm.getInstance(entity), 0, blend_order_);
}
}
return prev;
}
void Renderable::SetCastShadows(bool cast_shadows) {
if (cast_shadows_ != cast_shadows) {
cast_shadows_ = cast_shadows;
@@ -246,8 +277,8 @@ void Renderable::SetWireframe(bool wireframe) {
filament::IndexBuffer* index_buffer = mesh->GetFilamentIndexBuffer();
rm.setGeometryAt(rm.getInstance(entity), 0,
wireframe_ ? kWireframeType : mesh->GetPrimitiveType(),
vertex_buffer, index_buffer, 0,
index_buffer->getIndexCount());
vertex_buffer, index_buffer, meshes_[i].elem_offset,
meshes_[i].elem_count);
}
}
}
+61 -28
View File
@@ -26,7 +26,14 @@
namespace mujoco {
// Manages a collection of related filament Renderable Entities.
// A collection of meshes and a material that, together, define an object that
// can be rendered in a scene.
//
// Meshes can be added to the Renderable either by unique_ptr or raw pointer.
// This determines whether or not the Renderable takes ownership of the mesh.
//
// Internally, the Renderable creates a filament::Entity for each mesh and
// assigns the same material instance to all of them.
class Renderable {
public:
// Default filament values for priority and layer mask.
@@ -39,42 +46,51 @@ class Renderable {
Renderable(const Renderable&) = delete;
Renderable& operator=(const Renderable&) = delete;
// Appends a new renderable entity built from the given mesh.
void Append(const Mesh* mesh);
void Append(MeshPtr mesh);
// Appends a mesh to the renderable. The elem_offset and elem_count parameters
// can be used to specify a submesh to append. If elem_count is 0, assumes
// the entire mesh should be appended.
void AppendMesh(const Mesh* mesh, int elem_offset = 0, int elem_count = 0);
void AppendMesh(MeshPtr mesh, int elem_offset = 0, int elem_count = 0);
// Updates the entity at the index with new mesh.
void Update(int index, const Mesh* mesh);
void Update(int index, MeshPtr mesh);
// Replaces the mesh at the index with a new mesh. The elem_offset and
// elem_count parameters can be used to specify a submesh to append. If
// elem_count is 0, assumes the entire mesh should be appended.
void UpdateMesh(int index, const Mesh* mesh, int elem_offset = 0,
int elem_count = 0);
void UpdateMesh(int index, MeshPtr mesh, int elem_offset = 0,
int elem_count = 0);
// Removes the last entity.
void RemoveLast();
// Returns the number of meshes that define the renderable.
int GetNumMeshes() const { return meshes_.size(); }
// Returns the entity at the given index.
utils::Entity operator[](int index) { return entities_[index]; }
// Returns the number of Entities that make up this renderable.
int GetNumEntities() const { return entities_.size(); }
// Hides all managed entities.
// Sets the layer mask for the managed filament Entities. Layer masks can be
// used to show/hide the renderable in different views. Returns the previous
// layer mask.
std::uint8_t SetLayerMask(std::uint8_t mask);
// Sets the priority of all managed entities.
// Sets the priority for the managed filament Entities. The priority
// determines the order in which renderables are rendered. Returns the
// previous priority.
std::uint8_t SetPriority(std::uint8_t priority);
// Disables the renderables from casting shadows.
// Sets the blend order of the managed filament entities. This determines the
// order in which renderables are blended together. Returns the previous blend
// order.
std::uint16_t SetBlendOrder(std::uint16_t blend_order);
// Disables the renderable from casting shadows.
void SetCastShadows(bool cast_shadows);
// Disables the renderables from receiving shadows.
// Disables the renderable from receiving shadows.
void SetReceiveShadows(bool receive_shadows);
// If true, forces all entities to be rendered as lines.
// If true, forces all meshes to be rendered using Lines primitives.
void SetWireframe(bool wireframe);
// Adds all managed entities to the given filament Scene.
// Adds the renderable to the given filament Scene.
void AddToScene(filament::Scene* scene);
// Removes all managed entities from the given filament Scene.
// Removes the renderable from the given filament Scene.
void RemoveFromScene(filament::Scene* scene);
// Sets the material instance for all managed entities.
@@ -86,23 +102,40 @@ class Renderable {
// Returns the filament Engine managing the renderables.
filament::Engine* GetEngine();
private:
utils::Entity CreateEntity(const Mesh* mesh);
void UpdateEntity(utils::Entity entity, const Mesh* mesh);
void UpdateMeshes(int index, const Mesh* mesh, MeshPtr owned_mesh = nullptr);
// Returns the underlying filament::entity for the given mesh.
utils::Entity operator[](int index) { return entities_[index]; }
struct MeshWrapper {
private:
struct MeshInfo {
MeshPtr owned_mesh;
const Mesh* mesh = nullptr;
int elem_offset = 0;
int elem_count = 0;
};
// Sets the mesh information for the mesh at the given index. If index is -1,
// a new mesh will be appended to the renderable.
MeshInfo& SetMesh(int index, const Mesh* mesh, MeshPtr owned_mesh,
int elem_offset, int elem_count);
// Appends a new filament::Entity to the renderable, configured to use the
// given mesh.
void AppendEntity(const MeshInfo& mesh_info);
// Updates the filament::Entity at the given index to use the given mesh.
void UpdateEntity(int index, const MeshInfo& mesh_info);
// Removes the last filament::Entity from the renderable.
void RemoveLastEntity();
Material material_;
filament::Scene* assigned_scene_ = nullptr;
filament::MaterialInstance* material_instance_ = nullptr;
std::vector<utils::Entity> entities_;
std::vector<MeshWrapper> meshes_;
std::vector<MeshInfo> meshes_;
std::uint8_t priority_ = kDefaultPriority;
std::uint8_t layer_mask_ = kDefaultLayerMask;
std::uint16_t blend_order_ = 0;
bool wireframe_ = false;
bool cast_shadows_ = true;
bool receive_shadows_ = true;
@@ -93,15 +93,15 @@ static void AddMesh(Renderable& renderable, ModelObjects* model_objs,
if (mesh == nullptr) {
mju_error("Unknown mesh %d", data_id);
}
renderable.Append(mesh);
renderable.AppendMesh(mesh);
}
static void AddGeom(Renderable& renderable, ModelObjects* model_objs,
const mjvScene* scene, const mjvGeom& geom) {
if (geom.type == mjGEOM_FLEX) {
renderable.Append(model_objs->CreateFlexMesh(scene, geom));
renderable.AppendMesh(model_objs->CreateFlexMesh(scene, geom));
} else if (geom.type == mjGEOM_SKIN) {
renderable.Append(model_objs->CreateSkinMesh(scene, geom));
renderable.AppendMesh(model_objs->CreateSkinMesh(scene, geom));
}
}
@@ -111,7 +111,7 @@ static void AddHeightField(Renderable& renderable, ModelObjects* model_objs,
if (mesh == nullptr) {
mju_error("Unknown height field %d", hfield_id);
}
renderable.Append(mesh);
renderable.AppendMesh(mesh);
}
static void AddShape(Renderable& renderable, ModelObjects* model_objs,
@@ -120,7 +120,7 @@ static void AddShape(Renderable& renderable, ModelObjects* model_objs,
if (mesh == nullptr) {
mju_error("Unknown shape %d", shape_type);
}
renderable.Append(mesh);
renderable.AppendMesh(mesh);
}
static void PrepareGeomMeshes(Renderable& renderable, const mjvGeom& geom,
@@ -211,7 +211,7 @@ static void SetGeomTransform(Renderable& renderable, const mjvGeom& geom) {
float3 size = ReadFloat3(geom.size);
filament::TransformManager& tm =
renderable.GetEngine()->getTransformManager();
for (int j = 0; j < renderable.GetNumEntities(); ++j) {
for (int j = 0; j < renderable.GetNumMeshes(); ++j) {
const utils::Entity& entity = renderable[j];
// Update object transform.