From f24f9ef44d040837b33bde5420f8c9d7b6dde54b Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Wed, 15 Apr 2026 03:25:09 -0700 Subject: [PATCH] Allow submeshes to be set when adding meshes to a Renderable. Also add support for blend order. PiperOrigin-RevId: 900061778 Change-Id: I601757a8672fed23e45defd4cdbe4e9526ea3b70 --- .../filament/filament/renderable.cc | 105 ++++++++++++------ .../filament/filament/renderable.h | 89 ++++++++++----- .../filament/filament/scene_geom_util.cc | 12 +- 3 files changed, 135 insertions(+), 71 deletions(-) diff --git a/src/experimental/filament/filament/renderable.cc b/src/experimental/filament/filament/renderable.cc index 32fd66d9..ea39df1a 100644 --- a/src/experimental/filament/filament/renderable.cc +++ b/src/experimental/filament/filament/renderable.cc @@ -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(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); } } } diff --git a/src/experimental/filament/filament/renderable.h b/src/experimental/filament/filament/renderable.h index 74394980..32577beb 100644 --- a/src/experimental/filament/filament/renderable.h +++ b/src/experimental/filament/filament/renderable.h @@ -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 entities_; - std::vector meshes_; + std::vector 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; diff --git a/src/experimental/filament/filament/scene_geom_util.cc b/src/experimental/filament/filament/scene_geom_util.cc index a62d3749..f8bf04f5 100644 --- a/src/experimental/filament/filament/scene_geom_util.cc +++ b/src/experimental/filament/filament/scene_geom_util.cc @@ -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.