Initial implementation of toolbox-based "Studio" app.
PiperOrigin-RevId: 809966748 Change-Id: Ic6333259ca1d112a2e42d47ccc44326bfcea3bdd
This commit is contained in:
committed by
Copybara-Service
parent
7fc6b29afd
commit
6eea3d8ec0
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,194 @@
|
||||
// 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.
|
||||
|
||||
#ifndef MUJOCO_SRC_EXPERIMENTAL_STUDIO_APP_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_STUDIO_APP_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/toolbox/helpers.h"
|
||||
#include "experimental/toolbox/physics.h"
|
||||
#include "experimental/toolbox/renderer.h"
|
||||
#include "experimental/toolbox/window.h"
|
||||
|
||||
namespace mujoco::studio {
|
||||
|
||||
// Owns, updates, and renders a MuJoCo simulation.
|
||||
class App {
|
||||
public:
|
||||
using Clock = std::chrono::steady_clock;
|
||||
using TimePoint = std::chrono::time_point<Clock>;
|
||||
using Seconds = std::chrono::duration<double>;
|
||||
using Milliseconds = std::chrono::duration<double, std::milli>;
|
||||
|
||||
App(int width, int height, std::string ini_path,
|
||||
const toolbox::LoadAssetFn& load_asset_fn);
|
||||
|
||||
// Loads a model into the simulation.
|
||||
void LoadModel(std::string model_file);
|
||||
|
||||
// Processes window events and advances the state of the simulation.
|
||||
bool Update();
|
||||
|
||||
// Syncs the state of the simulation with the renderer.
|
||||
void Sync();
|
||||
|
||||
// Builds the GUI. We do this after Sync() to ensure we have the latest data
|
||||
// for building the GUI.
|
||||
void BuildGui();
|
||||
|
||||
// Renders everything (e.g. the simulation and the GUI).
|
||||
void Render();
|
||||
|
||||
private:
|
||||
static int LoadAssetCallback(const char* path, void* user_data,
|
||||
unsigned char** out, std::uint64_t* out_size);
|
||||
|
||||
// UI state that is persisted across application runs
|
||||
struct UiState {
|
||||
bool classic_ui = true;
|
||||
|
||||
char watch_field[1000] = "qpos";
|
||||
int watch_index = 0;
|
||||
int camera_idx = 0;
|
||||
int key_idx = 0;
|
||||
int scrub_idx = 0;
|
||||
bool dark_mode = true;
|
||||
|
||||
// UI visibility.
|
||||
bool simulation = false;
|
||||
bool physics = false;
|
||||
bool rendering = false;
|
||||
bool watch = false;
|
||||
bool visualization = false;
|
||||
bool groups = false;
|
||||
bool joints = false;
|
||||
bool controls = false;
|
||||
bool profiler = false;
|
||||
bool sensor = false;
|
||||
|
||||
using Dict = std::unordered_map<std::string, std::string>;
|
||||
Dict ToDict() const;
|
||||
void FromDict(const Dict& dict);
|
||||
};
|
||||
|
||||
// UI state that is transient and only needed while the application runs
|
||||
// TODO(matijak): Combine this with UiState and identify the list of transient
|
||||
// variables with a comment
|
||||
struct UiTempState {
|
||||
// bool paused = false; // Application starts with the simulation running.
|
||||
bool help = false;
|
||||
bool info = false;
|
||||
bool modal_open = false;
|
||||
bool load_popup = false;
|
||||
bool save_xml_popup = false;
|
||||
bool save_mjb_popup = false;
|
||||
bool save_screenshot_popup = false;
|
||||
bool print_model_popup = false;
|
||||
bool print_data_popup = false;
|
||||
bool should_exit = false;
|
||||
// bool should_reload = false;
|
||||
bool style_editor = false;
|
||||
bool imgui_demo = false;
|
||||
bool perturb_active = false;
|
||||
|
||||
int speed_index = 0;
|
||||
|
||||
// Visibility, position and size of the left and right UI panels
|
||||
bool show_ui_lhs = true;
|
||||
bool show_ui_rhs = true;
|
||||
float pos_ui_lhs[2];
|
||||
float pos_ui_rhs[2];
|
||||
float size_ui_lhs[2];
|
||||
float size_ui_rhs[2];
|
||||
|
||||
char filename[1000] = "";
|
||||
};
|
||||
|
||||
void OnModelLoaded(std::string_view model_file);
|
||||
|
||||
void LoadSettings();
|
||||
void SaveSettings();
|
||||
|
||||
void SetCamera(int idx);
|
||||
|
||||
void ChangeSpeed(int delta);
|
||||
|
||||
void HandleMouseEvents();
|
||||
void HandleKeyboardEvents();
|
||||
|
||||
void ClearProfilerData();
|
||||
void UpdateProfilerData();
|
||||
|
||||
void BuildGuiWithWindows();
|
||||
void BuildGuiWithSections();
|
||||
|
||||
void InfoGui();
|
||||
void HelpGui();
|
||||
void MainMenuGui();
|
||||
void FileDialogGui();
|
||||
void SimulationGui();
|
||||
void PhysicsGui();
|
||||
void RenderingGui();
|
||||
void VisualizationGui();
|
||||
void GroupsGui();
|
||||
void WatchGui();
|
||||
void SensorGui();
|
||||
void ProfilerGui();
|
||||
void JointsGui();
|
||||
void ControlsGui();
|
||||
|
||||
mjModel* Model() { return physics_->GetModel(); };
|
||||
mjData* Data() { return physics_->GetData(); };
|
||||
|
||||
std::string ini_path_;
|
||||
std::string model_file_;
|
||||
|
||||
std::unique_ptr<toolbox::Window> window_;
|
||||
std::unique_ptr<toolbox::Renderer> renderer_;
|
||||
std::unique_ptr<toolbox::Physics> physics_;
|
||||
toolbox::LoadAssetFn load_asset_fn_;
|
||||
|
||||
mjvCamera camera_;
|
||||
mjvPerturb perturb_;
|
||||
mjvOption vis_options_;
|
||||
|
||||
// profiler data
|
||||
std::vector<float> cpu_total_;
|
||||
std::vector<float> cpu_collision_;
|
||||
std::vector<float> cpu_prepare_;
|
||||
std::vector<float> cpu_solve_;
|
||||
std::vector<float> cpu_other_;
|
||||
std::vector<float> dim_dof_;
|
||||
std::vector<float> dim_body_;
|
||||
std::vector<float> dim_constraint_;
|
||||
std::vector<float> dim_sqrt_nnz_;
|
||||
std::vector<float> dim_contact_;
|
||||
std::vector<float> dim_iteration_;
|
||||
|
||||
UiState ui_;
|
||||
UiTempState tmp_;
|
||||
};
|
||||
|
||||
} // namespace mujoco::studio
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_STUDIO_APP_H_
|
||||
@@ -0,0 +1,66 @@
|
||||
// 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.
|
||||
|
||||
// Main entry point for the Filament-based MuJoCo renderer.
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "third_party/absl/flags/flag.h"
|
||||
#include "third_party/mujoco/google/runfiles/runfiles.h"
|
||||
#include "experimental/studio/app.h"
|
||||
|
||||
ABSL_FLAG(int, window_width, 1400, "Window width");
|
||||
ABSL_FLAG(int, window_height, 700, "Window height");
|
||||
ABSL_FLAG(std::string, model_file, "", "MuJoCo model file.");
|
||||
|
||||
constexpr char kDataPath[] = "third_party/mujoco/google/filament/assets/data/";
|
||||
|
||||
static std::vector<std::byte> LoadAsset(std::string_view path) {
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
return {};
|
||||
}
|
||||
std::streampos file_size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
std::vector<std::byte> buffer(file_size);
|
||||
if (!file.read(reinterpret_cast<char*>(buffer.data()), file_size)) {
|
||||
return {};
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
||||
const int width = absl::GetFlag(FLAGS_window_width);
|
||||
const int height = absl::GetFlag(FLAGS_window_height);
|
||||
const std::string ini_path = file::JoinPath(getenv("HOME"), ".mujoco.ini");
|
||||
mujoco::studio::App app(width, height, ini_path, LoadAsset);
|
||||
|
||||
// If the model file is not specified, try to load it from the first argument
|
||||
std::string model_file = absl::GetFlag(FLAGS_model_file);
|
||||
if (model_file.empty() && argc > 1 && argv[1][0] != '-') model_file = argv[1];
|
||||
app.LoadModel(model_file);
|
||||
while (app.Update()) {
|
||||
app.Sync();
|
||||
app.BuildGui();
|
||||
app.Render();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user