Improve wireframe rendering support in Filament renderer and Studio.

- Draw complete wireframes in the Filament renderer.
- Bind the 'W' key to toggle the wireframe render flag in Studio.

PiperOrigin-RevId: 957230911
Change-Id: Ib583c81ddcfa834686c7cf01d2b12842479993ea
This commit is contained in:
Yuval Tassa
2026-07-31 10:59:46 -07:00
committed by Copybara-Service
parent 6c78c98418
commit e295b31c60
4 changed files with 84 additions and 5 deletions
+4
View File
@@ -811,6 +811,8 @@ void App::HandleKeyboardEvents() {
ToggleFlag(vis_options_.flags[mjVIS_COM]);
} else if (!is_freecam_wasd && ImGui_IsChordJustPressed(ImGuiKey_D)) {
ToggleFlag(vis_options_.flags[mjVIS_STATIC]);
} else if (!is_freecam_wasd && ImGui_IsChordJustPressed(ImGuiKey_W)) {
ToggleFlag(renderer_->GetRenderFlags()[mjRND_WIREFRAME]);
} else if (ImGui_IsChordJustPressed(ImGuiKey_Semicolon)) {
ToggleFlag(vis_options_.flags[mjVIS_SKIN]);
} else if (ImGui_IsChordJustPressed(ImGuiKey_GraveAccent)) {
@@ -2152,6 +2154,7 @@ void App::HelpGui() {
ImGui::Text("Tendon");
ImGui::Text("Texture");
ImGui::Text("Transparent");
ImGui::Text("Wireframe");
ImGui::NextColumn();
ImGui::Indent(indent);
@@ -2181,6 +2184,7 @@ void App::HelpGui() {
ImGui::Text("V");
ImGui::Text("X");
ImGui::Text("T");
ImGui::Text("W");
ImGui::Columns();
}
+56
View File
@@ -146,6 +146,9 @@ Mesh::~Mesh() {
if (index_buffer_) {
engine_->destroy(index_buffer_);
}
if (wireframe_index_buffer_) {
engine_->destroy(wireframe_index_buffer_);
}
if (vertex_buffer_) {
engine_->destroy(vertex_buffer_);
}
@@ -308,6 +311,55 @@ void Mesh::UpdateIndexBuffer(const mjrfMeshData& data) {
// callback for the vertex buffer will call release_callbacks_.
filament::backend::BufferDescriptor desc(indices, num_bytes);
index_buffer_->setBuffer(*engine_, std::move(desc));
UpdateWireframeIndexBuffer(indices);
}
template <typename T>
static void FillWireframeIndices(const void* src, int num_indices, T* out) {
const T* in = static_cast<const T*>(src);
for (int i = 0; i < num_indices - 2; i += 3) {
*out++ = in[i + 0];
*out++ = in[i + 1];
*out++ = in[i + 1];
*out++ = in[i + 2];
*out++ = in[i + 2];
*out++ = in[i + 0];
}
}
void Mesh::UpdateWireframeIndexBuffer(const void* src_indices) {
// Line meshes are their own wireframe. Interior edges shared between two
// triangles are deliberately not deduplicated so that triangle index ranges
// map to line index ranges by doubling; see GetWireframeIndexBuffer().
if (config_.primitive_type != mjMESH_PRIMITIVE_TYPE_TRIANGLES) {
return;
}
const int num_line_indices = 2 * config_.max_indices;
if (wireframe_index_buffer_ == nullptr) {
filament::IndexBuffer::Builder ib_builder;
ib_builder.indexCount(num_line_indices);
ib_builder.bufferType(config_.index_type == mjINDEX_TYPE_U16
? filament::IndexBuffer::IndexType::USHORT
: filament::IndexBuffer::IndexType::UINT);
wireframe_index_buffer_ = ib_builder.build(*engine_);
}
const int element_size = config_.index_type == mjINDEX_TYPE_U16
? sizeof(uint16_t)
: sizeof(uint32_t);
const int num_bytes = num_line_indices * element_size;
std::byte* lines = new std::byte[num_bytes];
shared_state_->callbacks.push_back([=]() { delete[] lines; });
if (config_.index_type == mjINDEX_TYPE_U16) {
FillWireframeIndices(src_indices, config_.max_indices,
reinterpret_cast<uint16_t*>(lines));
} else {
FillWireframeIndices(src_indices, config_.max_indices,
reinterpret_cast<uint32_t*>(lines));
}
filament::backend::BufferDescriptor desc(lines, num_bytes);
wireframe_index_buffer_->setBuffer(*engine_, std::move(desc));
}
void Mesh::UpdateBounds(const mjrfMeshData& data) {
@@ -361,6 +413,10 @@ filament::IndexBuffer* Mesh::GetFilamentIndexBuffer() const {
return index_buffer_;
}
filament::IndexBuffer* Mesh::GetWireframeIndexBuffer() const {
return wireframe_index_buffer_;
}
filament::VertexBuffer* Mesh::GetFilamentVertexBuffer() const {
return vertex_buffer_;
}
+8
View File
@@ -49,6 +49,12 @@ class Mesh : public mjrfMesh {
// Returns the filament IndexBuffer for the mesh.
filament::IndexBuffer* GetFilamentIndexBuffer() const;
// Returns the filament IndexBuffer of line indices for wireframe rendering,
// or nullptr for non-triangle meshes. Each triangle contributes its three
// edges, so a triangle index range [offset, count) maps to the line index
// range [2*offset, 2*count).
filament::IndexBuffer* GetWireframeIndexBuffer() const;
// Returns the filament VertexBuffer for the mesh.
filament::VertexBuffer* GetFilamentVertexBuffer() const;
@@ -75,6 +81,7 @@ class Mesh : public mjrfMesh {
void UpdateVertexBuffer(const mjrfMeshData& data);
void UpdateIndexBuffer(const mjrfMeshData& data);
void UpdateWireframeIndexBuffer(const void* indices);
void UpdateBounds(const mjrfMeshData& data);
filament::math::float4* BuildOrientationsFromNormals(int num_vertices,
@@ -86,6 +93,7 @@ class Mesh : public mjrfMesh {
mjrfMeshConfig config_;
filament::IndexBuffer* index_buffer_ = nullptr;
filament::IndexBuffer* wireframe_index_buffer_ = nullptr;
filament::VertexBuffer* vertex_buffer_ = nullptr;
std::optional<filament::Box> bounds_;
+16 -5
View File
@@ -461,11 +461,22 @@ void Renderable::SetWireframe(bool wireframe) {
for (Part& part : parts_) {
filament::VertexBuffer* vertex_buffer =
part.mesh->GetFilamentVertexBuffer();
filament::IndexBuffer* index_buffer = part.mesh->GetFilamentIndexBuffer();
rm.setGeometryAt(
rm.getInstance(part.entity), 0,
wireframe ? kWireframeType : part.mesh->GetPrimitiveType(),
vertex_buffer, index_buffer, part.elem_offset, part.elem_count);
filament::IndexBuffer* wireframe_index_buffer =
part.mesh->GetWireframeIndexBuffer();
if (wireframe && wireframe_index_buffer) {
// Triangle meshes have a dedicated line index buffer enumerating the
// three edges of each triangle, addressed by doubling the range.
rm.setGeometryAt(rm.getInstance(part.entity), 0, kWireframeType,
vertex_buffer, wireframe_index_buffer,
2 * part.elem_offset, 2 * part.elem_count);
} else {
// Line meshes are drawn as-is in both modes.
rm.setGeometryAt(rm.getInstance(part.entity), 0,
wireframe ? kWireframeType
: part.mesh->GetPrimitiveType(),
vertex_buffer, part.mesh->GetFilamentIndexBuffer(),
part.elem_offset, part.elem_count);
}
}
}
}