From 9b7e7e471dab3683ffe9348697bf494b24d176da Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Mon, 13 Oct 2025 02:58:21 -0700 Subject: [PATCH] Add line and linebox geom primitives. PiperOrigin-RevId: 818570050 Change-Id: If02873dd878a23b442368ce69bc024e0310e9d02 --- .../filament/assets/unlit_line.mat | 29 +++++ .../filament/filament/buffer_util.h | 3 + .../filament/filament/builtins.cc | 119 +++++++++++++++++- src/experimental/filament/filament/builtins.h | 2 + .../filament/filament/drawable.cc | 98 ++++++++------- .../filament/filament/object_manager.cc | 3 + .../filament/filament/object_manager.h | 3 + .../filament/filament/renderables.cc | 7 +- 8 files changed, 214 insertions(+), 50 deletions(-) create mode 100644 src/experimental/filament/assets/unlit_line.mat diff --git a/src/experimental/filament/assets/unlit_line.mat b/src/experimental/filament/assets/unlit_line.mat new file mode 100644 index 00000000..359bb319 --- /dev/null +++ b/src/experimental/filament/assets/unlit_line.mat @@ -0,0 +1,29 @@ +// Copyright 2025 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +material { + name : unlit_segmentation, + shadingModel : unlit, + culling: none, + parameters : [ + { type : float4, name : BaseColorFactor } + ] +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + material.baseColor = materialParams.BaseColorFactor; + } +} diff --git a/src/experimental/filament/filament/buffer_util.h b/src/experimental/filament/filament/buffer_util.h index 8e8a4df6..2e70a106 100644 --- a/src/experimental/filament/filament/buffer_util.h +++ b/src/experimental/filament/filament/buffer_util.h @@ -23,6 +23,7 @@ #include #include #include +#include #include // Functions for creating filament vertex and index buffers. @@ -33,6 +34,8 @@ struct FilamentBuffers { filament::IndexBuffer* index_buffer = nullptr; filament::VertexBuffer* vertex_buffer = nullptr; filament::Box bounds = {{-1, -1, -1}, {1, 1, 1}}; + filament::RenderableManager::PrimitiveType type = + filament::RenderableManager::PrimitiveType::TRIANGLES; }; // Function that fills in the given buffer with actual data. diff --git a/src/experimental/filament/filament/builtins.cc b/src/experimental/filament/filament/builtins.cc index 12660f5d..a47f4f43 100644 --- a/src/experimental/filament/filament/builtins.cc +++ b/src/experimental/filament/filament/builtins.cc @@ -58,10 +58,45 @@ std::size_t NumIndicesPerSide(int num_quads_per_axis) { return kNumIndicesPerQuad * num_quads_per_axis * num_quads_per_axis; } +class LineBuilder { + public: + using VertexType = VertexNoUv; + using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::LINES; + + explicit LineBuilder() {} + + std::size_t NumVertices() const { + return 2; + } + + std::size_t NumIndices() const { + return 2; + } + + void GenerateVertices(VertexType* ptr, size_t num) const { + constexpr float4 kOrientation = {0, 0, 0, 1}; // Unused for lines. + ptr[0] = VertexType({0, 0, 0}, kOrientation); + ptr[1] = VertexType({0, 0, 1}, kOrientation); + } + + void GenerateIndices(IndexType* ptr, size_t num) const { + ptr[0] = 0; + ptr[1] = 1; + } + + filament::Box GetBounds() const { + return {{-0.001, -0.001, 0}, {0.001, 0.001, 1}}; + } +}; + class PlaneBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; explicit PlaneBuilder(int num_quads_per_axis) : num_quads_per_axis_(num_quads_per_axis), @@ -109,10 +144,74 @@ class PlaneBuilder { float4 orientation_; }; +class LineBoxBuilder { + public: + using VertexType = VertexNoUv; + using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::LINES; + + explicit LineBoxBuilder() {} + + std::size_t NumVertices() const { + return 8; + } + + std::size_t NumIndices() const { + return 24; + } + + void GenerateVertices(VertexType* ptr, size_t num) const { + constexpr float4 kOrientation = {0, 0, 0, 1}; // Unused for lines. + ptr[0] = VertexType({-1.0f, -1.0f, -1.0f}, kOrientation); + ptr[1] = VertexType({ 1.0f, -1.0f, -1.0f}, kOrientation); + ptr[2] = VertexType({-1.0f, 1.0f, -1.0f}, kOrientation); + ptr[3] = VertexType({ 1.0f, 1.0f, -1.0f}, kOrientation); + ptr[4] = VertexType({-1.0f, -1.0f, 1.0f}, kOrientation); + ptr[5] = VertexType({ 1.0f, -1.0f, 1.0f}, kOrientation); + ptr[6] = VertexType({-1.0f, 1.0f, 1.0f}, kOrientation); + ptr[7] = VertexType({ 1.0f, 1.0f, 1.0f}, kOrientation); + } + + void GenerateIndices(IndexType* ptr, size_t num) const { + // Bottom square (where z == -1). + ptr[0] = 0; + ptr[1] = 1; + ptr[2] = 1; + ptr[3] = 3; + ptr[4] = 3; + ptr[5] = 2; + ptr[6] = 2; + ptr[7] = 0; + // Top square (where z == 1). + ptr[8] = 4; + ptr[9] = 5; + ptr[10] = 5; + ptr[11] = 7; + ptr[12] = 7; + ptr[13] = 6; + ptr[14] = 6; + ptr[15] = 4; + // Connect edges from bottom to top. + ptr[16] = 2; + ptr[17] = 6; + ptr[18] = 3; + ptr[19] = 7; + ptr[20] = 0; + ptr[21] = 4; + ptr[22] = 1; + ptr[23] = 5; + } + + filament::Box GetBounds() const { return {{-1, -1, -1}, {1, 1, 1}}; } +}; + class BoxBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; static constexpr int kNumSides = 6; @@ -196,6 +295,8 @@ class TubeBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; TubeBuilder(int num_stacks, int num_slices) : num_stacks_(num_stacks), num_slices_(num_slices) {} @@ -252,6 +353,8 @@ class ConeBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; ConeBuilder(int num_stacks, int num_slices) : num_stacks_(num_stacks), num_slices_(num_slices) {} @@ -341,6 +444,8 @@ class DiskBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; explicit DiskBuilder(int num_slices) : num_slices_(num_slices) { orientation_ = CalculateOrientation({0, 0, 1}); @@ -388,6 +493,8 @@ class SphereBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; static constexpr IndexType kNorthPoleIndex = 0; static constexpr IndexType kSouthPoleIndex = 1; @@ -494,6 +601,8 @@ class DomeBuilder { public: using VertexType = VertexNoUv; using IndexType = uint16_t; + static constexpr filament::RenderableManager::PrimitiveType kPrimitiveType = + filament::RenderableManager::PrimitiveType::TRIANGLES; static constexpr IndexType kPoleIndex = 0; @@ -617,7 +726,11 @@ FilamentBuffers CreateFromBuilder(filament::Engine* engine, const T& builder) { auto vb = CreateVertexBuffer(engine, num_vertices, vertices); auto ib = CreateIndexBuffer(engine, num_indices, indices); - return {ib, vb, builder.GetBounds()}; + return {ib, vb, builder.GetBounds(), T::kPrimitiveType}; +} + +FilamentBuffers CreateLine(filament::Engine* engine, const mjModel* model) { + return CreateFromBuilder(engine, LineBuilder()); } FilamentBuffers CreatePlane(filament::Engine* engine, const mjModel* model) { @@ -630,6 +743,10 @@ FilamentBuffers CreateBox(filament::Engine* engine, const mjModel* model) { return CreateFromBuilder(engine, BoxBuilder(num_quads)); } +FilamentBuffers CreateLineBox(filament::Engine* engine, const mjModel* model) { + return CreateFromBuilder(engine, LineBoxBuilder()); +} + FilamentBuffers CreateSphere(filament::Engine* engine, const mjModel* model) { const int num_stacks = model->vis.quality.numstacks; const int num_slices = model->vis.quality.numslices; diff --git a/src/experimental/filament/filament/builtins.h b/src/experimental/filament/filament/builtins.h index e7cfd3c3..e6a8a9ed 100644 --- a/src/experimental/filament/filament/builtins.h +++ b/src/experimental/filament/filament/builtins.h @@ -22,7 +22,9 @@ // Generates buffers for built-in shapes. namespace mujoco { +FilamentBuffers CreateLine(filament::Engine* engine, const mjModel* model); FilamentBuffers CreateBox(filament::Engine* engine, const mjModel* model); +FilamentBuffers CreateLineBox(filament::Engine* engine, const mjModel* model); FilamentBuffers CreatePlane(filament::Engine* engine, const mjModel* model); FilamentBuffers CreateSphere(filament::Engine* engine, const mjModel* model); FilamentBuffers CreateTube(filament::Engine* engine, const mjModel* model); diff --git a/src/experimental/filament/filament/drawable.cc b/src/experimental/filament/filament/drawable.cc index cb1a95e5..98e1eba3 100644 --- a/src/experimental/filament/filament/drawable.cc +++ b/src/experimental/filament/filament/drawable.cc @@ -106,6 +106,10 @@ Drawable::Drawable(ObjectManager* object_mgr, const mjvGeom& geom) AddShape(ObjectManager::kCone); AddShape(ObjectManager::kDisk); AddShape(ObjectManager::kDisk); + } else if (geom.type == mjGEOM_LINE) { + AddShape(ObjectManager::kLine); + } else if (geom.type == mjGEOM_LINEBOX) { + AddShape(ObjectManager::kLineBox); } else if (geom.type == mjGEOM_FLEX || geom.type == mjGEOM_SKIN) { // Flex and skin geometries are dynamically updated every frame. } else { @@ -298,53 +302,57 @@ void Drawable::UpdateMaterial(const mjvGeom& geom) { textures.occlusion = object_mgr->GetTexture(geom.matid, mjTEXROLE_OCCLUSION); } - if (geom.matid >= 0) { - if (textures.orm) { - material_.SetNormalMaterialType(ObjectManager::kPbrPacked); - } else if (textures.metallic) { - material_.SetNormalMaterialType(ObjectManager::kPbr); - } else if (textures.roughness) { - material_.SetNormalMaterialType(ObjectManager::kPbr); - } else if (model->mat_metallic[geom.matid] >= 0) { - material_.SetNormalMaterialType(ObjectManager::kPbr); - } else if (model->mat_roughness[geom.matid] >= 0) { - material_.SetNormalMaterialType(ObjectManager::kPbr); - } - } - - // Check to see if we're dealing with a mesh with texture coordinates. - // `data_id` is the id of the mesh in model (i.e. the geom has mesh geometry) - // and `mesh_texcoordadr` stores the address of the mesh uvs if it has them. - bool has_texcoords = false; - if ((geom.type == mjGEOM_MESH || geom.type == mjGEOM_SDF) && - geom.dataid >= 0 && model->mesh_texcoordadr[geom.dataid / 2] >= 0) { - has_texcoords = true; - } - - if (textures.color == nullptr) { - if (geom.rgba[3] < 1.0f) { - material_.SetNormalMaterialType(ObjectManager::kPhongColorFade); - } else { - material_.SetNormalMaterialType(ObjectManager::kPhongColor); - } - } else if (textures.color->getTarget() == - filament::Texture::Sampler::SAMPLER_CUBEMAP) { - if (geom.rgba[3] < 1.0f) { - material_.SetNormalMaterialType(ObjectManager::kPhongCubeFade); - } else { - material_.SetNormalMaterialType(ObjectManager::kPhongCube); - } - } else if (has_texcoords) { - if (geom.rgba[3] < 1.0f) { - material_.SetNormalMaterialType(ObjectManager::kPhong2dUvFade); - } else { - material_.SetNormalMaterialType(ObjectManager::kPhong2dUv); - } + if (geom.type == mjGEOM_LINE || geom.type == mjGEOM_LINEBOX) { + material_.SetNormalMaterialType(ObjectManager::kUnlitLine); } else { - if (geom.rgba[3] < 1.0f) { - material_.SetNormalMaterialType(ObjectManager::kPhong2dFade); + if (geom.matid >= 0) { + if (textures.orm) { + material_.SetNormalMaterialType(ObjectManager::kPbrPacked); + } else if (textures.metallic) { + material_.SetNormalMaterialType(ObjectManager::kPbr); + } else if (textures.roughness) { + material_.SetNormalMaterialType(ObjectManager::kPbr); + } else if (model->mat_metallic[geom.matid] >= 0) { + material_.SetNormalMaterialType(ObjectManager::kPbr); + } else if (model->mat_roughness[geom.matid] >= 0) { + material_.SetNormalMaterialType(ObjectManager::kPbr); + } + } + + // Check to see if we're dealing with a mesh with texture coordinates. + // `data_id` is the id of the mesh in model (i.e. the geom has mesh geometry) + // and `mesh_texcoordadr` stores the address of the mesh uvs if it has them. + bool has_texcoords = false; + if ((geom.type == mjGEOM_MESH || geom.type == mjGEOM_SDF) && + geom.dataid >= 0 && model->mesh_texcoordadr[geom.dataid / 2] >= 0) { + has_texcoords = true; + } + + if (textures.color == nullptr) { + if (geom.rgba[3] < 1.0f) { + material_.SetNormalMaterialType(ObjectManager::kPhongColorFade); + } else { + material_.SetNormalMaterialType(ObjectManager::kPhongColor); + } + } else if (textures.color->getTarget() == + filament::Texture::Sampler::SAMPLER_CUBEMAP) { + if (geom.rgba[3] < 1.0f) { + material_.SetNormalMaterialType(ObjectManager::kPhongCubeFade); + } else { + material_.SetNormalMaterialType(ObjectManager::kPhongCube); + } + } else if (has_texcoords) { + if (geom.rgba[3] < 1.0f) { + material_.SetNormalMaterialType(ObjectManager::kPhong2dUvFade); + } else { + material_.SetNormalMaterialType(ObjectManager::kPhong2dUv); + } } else { - material_.SetNormalMaterialType(ObjectManager::kPhong2d); + if (geom.rgba[3] < 1.0f) { + material_.SetNormalMaterialType(ObjectManager::kPhong2dFade); + } else { + material_.SetNormalMaterialType(ObjectManager::kPhong2d); + } } } diff --git a/src/experimental/filament/filament/object_manager.cc b/src/experimental/filament/filament/object_manager.cc index baeb1f2c..ab6debca 100644 --- a/src/experimental/filament/filament/object_manager.cc +++ b/src/experimental/filament/filament/object_manager.cc @@ -68,7 +68,9 @@ struct Asset { ObjectManager::ObjectManager(const mjModel* model, filament::Engine* engine, const mjrFilamentConfig* config) : model_(model), engine_(engine), config_(config) { + shapes_[kLine] = CreateLine(engine_, model_); shapes_[kBox] = CreateBox(engine_, model_); + shapes_[kLineBox] = CreateLineBox(engine_, model_); shapes_[kCone] = CreateCone(engine_, model_); shapes_[kDisk] = CreateDisk(engine_, model_); shapes_[kDome] = CreateDome(engine_, model_); @@ -94,6 +96,7 @@ ObjectManager::ObjectManager(const mjModel* model, filament::Engine* engine, materials_[kPhongCube] = LoadMaterial("phong_cube.filamat"); materials_[kPhongCubeFade] = LoadMaterial("phong_cube_fade.filamat"); materials_[kUnlitSegmentation] = LoadMaterial("unlit_segmentation.filamat"); + materials_[kUnlitLine] = LoadMaterial("unlit_line.filamat"); materials_[kUnlitDepth] = LoadMaterial("unlit_depth.filamat"); materials_[kUnlitUi] = LoadMaterial("unlit_ui.filamat"); diff --git a/src/experimental/filament/filament/object_manager.h b/src/experimental/filament/filament/object_manager.h index 48c1bcfe..40c3848a 100644 --- a/src/experimental/filament/filament/object_manager.h +++ b/src/experimental/filament/filament/object_manager.h @@ -51,11 +51,14 @@ class ObjectManager { kPhongCubeFade, kUnlitSegmentation, kUnlitDepth, + kUnlitLine, kUnlitUi, kNumMaterials, }; enum ShapeType { + kLine, + kLineBox, kPlane, kBox, kSphere, diff --git a/src/experimental/filament/filament/renderables.cc b/src/experimental/filament/filament/renderables.cc index 7216a188..daa18faa 100644 --- a/src/experimental/filament/filament/renderables.cc +++ b/src/experimental/filament/filament/renderables.cc @@ -96,8 +96,8 @@ utils::Entity Renderables::CreateEntity(const FilamentBuffers& buffers) { } filament::RenderableManager::Builder builder(1); - builder.geometry(0, filament::RenderableManager::PrimitiveType::TRIANGLES, - buffers.vertex_buffer, buffers.index_buffer); + builder.geometry(0, buffers.type, buffers.vertex_buffer, + buffers.index_buffer); if (material_instance_) { builder.material(0, material_instance_); } @@ -124,8 +124,7 @@ void Renderables::UpdateEntity(utils::Entity entity, mju_error("Invalid (null) index buffer."); } filament::RenderableManager& rm = engine_->getRenderableManager(); - rm.setGeometryAt(rm.getInstance(entity), 0, - filament::RenderableManager::PrimitiveType::TRIANGLES, + rm.setGeometryAt(rm.getInstance(entity), 0, buffers.type, buffers.vertex_buffer, buffers.index_buffer, 0, buffers.index_buffer->getIndexCount()); }