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:
committed by
Copybara-Service
parent
bf05bb1885
commit
e2180ff744
@@ -146,8 +146,6 @@ void FilamentContext::Render(const mjrRect& viewport, const mjvScene* scene,
|
|||||||
// Draw the GUI. We do this after processing the scene in case there are any
|
// Draw the GUI. We do this after processing the scene in case there are any
|
||||||
// label elements in the scene.
|
// label elements in the scene.
|
||||||
if (gui_view_ && !render_to_texture_) {
|
if (gui_view_ && !render_to_texture_) {
|
||||||
DrawGui(scene_view_.get());
|
|
||||||
|
|
||||||
// Prepare the filament Renderable that contains the GUI draw commands. We
|
// Prepare the filament Renderable that contains the GUI draw commands. We
|
||||||
// must call this function even if we do not plan on rendering the GUI to
|
// must call this function even if we do not plan on rendering the GUI to
|
||||||
// ensure the ImGui state is updated.
|
// ensure the ImGui state is updated.
|
||||||
@@ -339,4 +337,8 @@ double FilamentContext::GetFrameRate() const {
|
|||||||
return 1.0e9 / static_cast<double>(ns);
|
return 1.0e9 / static_cast<double>(ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FilamentContext::UpdateGui() {
|
||||||
|
DrawGui(scene_view_.get());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ class FilamentContext {
|
|||||||
|
|
||||||
double GetFrameRate() const;
|
double GetFrameRate() const;
|
||||||
|
|
||||||
|
void UpdateGui();
|
||||||
|
|
||||||
FilamentContext(const FilamentContext&) = delete;
|
FilamentContext(const FilamentContext&) = delete;
|
||||||
FilamentContext& operator=(const FilamentContext&) = delete;
|
FilamentContext& operator=(const FilamentContext&) = delete;
|
||||||
|
|
||||||
@@ -63,8 +65,6 @@ class FilamentContext {
|
|||||||
void PrepareRenderTargets(int width, int height);
|
void PrepareRenderTargets(int width, int height);
|
||||||
void DestroyRenderTargets();
|
void DestroyRenderTargets();
|
||||||
|
|
||||||
SceneView* GetSceneView(const mjvScene* scene);
|
|
||||||
|
|
||||||
mjrFilamentConfig config_;
|
mjrFilamentConfig config_;
|
||||||
mjrContext* context_ = nullptr;
|
mjrContext* context_ = nullptr;
|
||||||
const mjModel* model_ = nullptr;
|
const mjModel* model_ = nullptr;
|
||||||
|
|||||||
@@ -581,7 +581,7 @@ void DrawLightGui(filament::LightManager& lm,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawSceneViewGui(SceneView* scene_view) {
|
void DrawGui(SceneView* scene_view) {
|
||||||
filament::View* view = scene_view->GetDefaultRenderView();
|
filament::View* view = scene_view->GetDefaultRenderView();
|
||||||
filament::Engine* engine = scene_view->GetEngine();
|
filament::Engine* engine = scene_view->GetEngine();
|
||||||
filament::LightManager& lm = engine->getLightManager();
|
filament::LightManager& lm = engine->getLightManager();
|
||||||
@@ -669,28 +669,4 @@ void DrawSceneViewGui(SceneView* scene_view) {
|
|||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawGui(SceneView* scene_view) {
|
|
||||||
static bool display = false;
|
|
||||||
if (ImGui::BeginMainMenuBar()) {
|
|
||||||
if (ImGui::BeginMenu("View")) {
|
|
||||||
ImGui::Separator();
|
|
||||||
if (ImGui::MenuItem("Filament", "", display)) {
|
|
||||||
display = !display;
|
|
||||||
}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
ImGui::EndMainMenuBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!display) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(400, 300), ImGuiCond_Appearing);
|
|
||||||
ImGui::Begin("Filament");
|
|
||||||
DrawSceneViewGui(scene_view);
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace mujoco
|
} // namespace mujoco
|
||||||
|
|||||||
@@ -113,4 +113,10 @@ double mjr_getFrameRate(const mjrContext* con) {
|
|||||||
return g_filament_context->GetFrameRate();
|
return g_filament_context->GetFrameRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void mjr_updateGui(const mjrContext* con) {
|
||||||
|
if (g_filament_context != nullptr) {
|
||||||
|
g_filament_context->UpdateGui();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ uintptr_t mjr_uploadGuiImage(uintptr_t tex_id, const unsigned char* pixels,
|
|||||||
int width, int height, int bpp,
|
int width, int height, int bpp,
|
||||||
const mjrContext* con);
|
const mjrContext* con);
|
||||||
|
|
||||||
|
void mjr_updateGui(const mjrContext* con);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ target_sources(${MUJOCO_PLATFORM_TARGET_NAME}
|
|||||||
interaction.h
|
interaction.h
|
||||||
picture_gui.h
|
picture_gui.h
|
||||||
picture_gui.cc
|
picture_gui.cc
|
||||||
|
plugin.cc
|
||||||
|
plugin.h
|
||||||
renderer.cc
|
renderer.cc
|
||||||
renderer.h
|
renderer.h
|
||||||
sim_history.cc
|
sim_history.cc
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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_
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#if defined(USE_FILAMENT_OPENGL) || defined(USE_FILAMENT_VULKAN)
|
#if defined(USE_FILAMENT_OPENGL) || defined(USE_FILAMENT_VULKAN)
|
||||||
#include "experimental/filament/render_context_filament.h"
|
#include "experimental/filament/render_context_filament.h"
|
||||||
|
#include "experimental/platform/plugin.h"
|
||||||
#elif defined(USE_CLASSIC_OPENGL)
|
#elif defined(USE_CLASSIC_OPENGL)
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <backends/imgui_impl_opengl3.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_; }
|
double Renderer::GetFps() { return fps_; }
|
||||||
|
|
||||||
} // namespace mujoco::platform
|
} // 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
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#include "experimental/platform/imgui_widgets.h"
|
#include "experimental/platform/imgui_widgets.h"
|
||||||
#include "experimental/platform/interaction.h"
|
#include "experimental/platform/interaction.h"
|
||||||
#include "experimental/platform/picture_gui.h"
|
#include "experimental/platform/picture_gui.h"
|
||||||
|
#include "experimental/platform/plugin.h"
|
||||||
#include "experimental/platform/renderer.h"
|
#include "experimental/platform/renderer.h"
|
||||||
#include "experimental/platform/step_control.h"
|
#include "experimental/platform/step_control.h"
|
||||||
#include "experimental/platform/window.h"
|
#include "experimental/platform/window.h"
|
||||||
@@ -854,6 +855,26 @@ void App::BuildGui() {
|
|||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
platform::ForEachGuiPlugin([](platform::GuiPlugin* plugin) {
|
||||||
|
if (!plugin->update) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ImGui::BeginMainMenuBar()) {
|
||||||
|
if (ImGui::BeginMenu("Plugins")) {
|
||||||
|
if (ImGui::MenuItem(plugin->name, "", plugin->active)) {
|
||||||
|
plugin->active = !plugin->active;
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
}
|
||||||
|
if (plugin->active) {
|
||||||
|
ImGui::Begin(plugin->name);
|
||||||
|
plugin->update(plugin);
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
if (tmp_.first_frame) {
|
if (tmp_.first_frame) {
|
||||||
LoadSettings();
|
LoadSettings();
|
||||||
@@ -1534,6 +1555,11 @@ void App::MainMenuGui() {
|
|||||||
}
|
}
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
if (ImGui::BeginMenu("Plugins")) {
|
||||||
|
// Placeholder menu item that will be populated by plugins later on. We
|
||||||
|
// do this now in so that the menu is present at the right place.
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
if (ImGui::BeginMenu("Help")) {
|
if (ImGui::BeginMenu("Help")) {
|
||||||
if (ImGui::MenuItem("Help", "F1", tmp_.help)) {
|
if (ImGui::MenuItem("Help", "F1", tmp_.help)) {
|
||||||
ToggleWindow(tmp_.help);
|
ToggleWindow(tmp_.help);
|
||||||
|
|||||||
Reference in New Issue
Block a user