First-pass of a plugin system for GUI elements.

Refactor filament's debug drawing to be one such plugin.

PiperOrigin-RevId: 859002002
Change-Id: Ibe9000dec19fce648eef6d57170ce435b9a8c6ff
This commit is contained in:
Haroon Qureshi
2026-01-21 02:46:40 -08:00
committed by Copybara-Service
parent bf05bb1885
commit e2180ff744
10 changed files with 176 additions and 29 deletions
+2
View File
@@ -47,6 +47,8 @@ target_sources(${MUJOCO_PLATFORM_TARGET_NAME}
interaction.h
picture_gui.h
picture_gui.cc
plugin.cc
plugin.h
renderer.cc
renderer.h
sim_history.cc
+63
View File
@@ -0,0 +1,63 @@
// Copyright 2026 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.
#include <functional>
#include <string_view>
#include <mujoco/mujoco.h>
#include "experimental/platform/plugin.h"
#include "engine/engine_global_table.h"
namespace mujoco::platform {
void RegisterGuiPlugin(const GuiPlugin* plugin) {
if (plugin->name == nullptr || plugin->name[0] == '\0') {
mju_error("Plugin name must not be empty or null.");
}
GlobalTable<GuiPlugin>::GetSingleton().AppendIfUnique(*plugin);
}
void ForEachGuiPlugin(const std::function<void(GuiPlugin*)>& fn) {
auto& table = GlobalTable<GuiPlugin>::GetSingleton();
for (int i = 0; i < table.count(); ++i) {
const GuiPlugin* plugin = table.GetAtSlot(i);
fn(const_cast<GuiPlugin*>(plugin));
}
}
} // namespace mujoco::platform
using mujoco::GlobalTable;
using GuiPlugin = mujoco::platform::GuiPlugin;
template <>
const char* GlobalTable<GuiPlugin>::HumanReadableTypeName() {
return "gui plugin";
}
template <>
std::string_view GlobalTable<GuiPlugin>::ObjectKey(const GuiPlugin& plugin) {
return std::string_view(plugin.name);
}
template <>
bool GlobalTable<GuiPlugin>::ObjectEqual(const GuiPlugin& p1, const GuiPlugin& p2) {
return CaseInsensitiveEqual(p1.name, p2.name);
}
template <>
bool GlobalTable<GuiPlugin>::CopyObject(GuiPlugin& dst, const GuiPlugin& src, ErrorMessage& err) {
dst = src;
return true;
}
+58
View File
@@ -0,0 +1,58 @@
// Copyright 2026 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.
#ifndef MUJOCO_SRC_EXPERIMENTAL_PLATFORM_PLUGIN_H_
#define MUJOCO_SRC_EXPERIMENTAL_PLATFORM_PLUGIN_H_
#include <functional>
namespace mujoco::platform {
// Important: Do not inherit from these plugin structs. They are copied by value
// and therefore any derived classes will be sliced. We assume plugins are
// effectively globals and so any pointers will be valid for the lifetime
// of the process.
// Plugin for processing custom UI windows. The plugin will be listed in the
// "Plugins" main menu and, when selected, an ImGui window will be opened with
// the name of the plugin as the title. The `update` function can then be used
// to process the GUI. All functions will be called by passing `this` as the
// first argument.
struct GuiPlugin final {
using UpdateFn = void (*)(GuiPlugin* self);
// Whether or not to display the plugin window.
bool active = false;
// The name of the plugin; must be unique.
const char* name = "";
// The function that will update the plugin's window. Plugin GUI updates
// happen when the window is `active` and after all other Studio GUI updates.
UpdateFn update = nullptr;
// Optional data pointer.
void* data = nullptr;
};
// Registers a plugin with a global registry. The plugin must have a
// case-insensitive unique name.
void RegisterGuiPlugin(const GuiPlugin* plugin);
// Executes the given function for each registered plugin.
void ForEachGuiPlugin(const std::function<void(GuiPlugin*)>& fn);
} // namespace mujoco::platform
#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_PLUGIN_H_
+12
View File
@@ -20,6 +20,7 @@
#if defined(USE_FILAMENT_OPENGL) || defined(USE_FILAMENT_VULKAN)
#include "experimental/filament/render_context_filament.h"
#include "experimental/platform/plugin.h"
#elif defined(USE_CLASSIC_OPENGL)
#include <imgui.h>
#include <backends/imgui_impl_opengl3.h>
@@ -136,3 +137,14 @@ int Renderer::UploadImage(int texture_id, const std::byte* pixels, int width,
double Renderer::GetFps() { return fps_; }
} // namespace mujoco::platform
#if !defined(USE_CLASSIC_OPENGL)
mjPLUGIN_LIB_INIT {
mujoco::platform::GuiPlugin plugin;
plugin.name = "Filament";
plugin.update = [](mujoco::platform::GuiPlugin* self) {
mjr_updateGui(nullptr);
};
mujoco::platform::RegisterGuiPlugin(&plugin);
}
#endif