From fe1625560293c8724a689bf20a0e510d6c084a4e Mon Sep 17 00:00:00 2001 From: Haroon Qureshi Date: Tue, 27 Jan 2026 03:22:27 -0800 Subject: [PATCH] Add mjz support to studio. PiperOrigin-RevId: 861627355 Change-Id: Iafd291965ca78d03fdc1efe0e40dabcd4f2dacc4 --- src/experimental/studio/app.cc | 25 +++++++++++++++++++++++++ src/experimental/studio/app.h | 6 ++++-- src/experimental/studio/index.html | 2 +- src/experimental/studio/wasm.cc | 4 ++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/experimental/studio/app.cc b/src/experimental/studio/app.cc index 6e20914a..8075338d 100644 --- a/src/experimental/studio/app.cc +++ b/src/experimental/studio/app.cc @@ -41,6 +41,7 @@ #include "experimental/platform/renderer.h" #include "experimental/platform/step_control.h" #include "experimental/platform/window.h" +#include "user/user_resource.h" namespace mujoco::studio { @@ -202,6 +203,22 @@ void App::LoadModelFromFile(const std::string& filepath) { mj_deleteVFS(&vfs); } +struct BufferProvider : public mjpResourceProvider { + BufferProvider(std::span buffer) : buffer(buffer) { + mjp_defaultResourceProvider(this); + open = [](mjResource* resource) { + return 1; + }; + read = [](mjResource* resource, const void** buffer) { + BufferProvider* self = (BufferProvider*)resource->provider; + *buffer = self->buffer.data(); + return static_cast(self->buffer.size()); + }; + close = [](mjResource* resource) {}; + } + std::span buffer; +}; + void App::LoadModelFromBuffer(std::span buffer, std::string_view content_type, std::string_view filename) { @@ -217,6 +234,14 @@ void App::LoadModelFromBuffer(std::span buffer, spec = mj_parseXMLString(ptr, nullptr, err, sizeof(err)); } else if (content_type == "application/mjb") { model = mj_loadModelBuffer(buffer.data(), buffer.size()); + } else if (content_type == "application/zip") { + BufferProvider provider(buffer); + mjResource resource; + memset(&resource, 0, sizeof(mjResource)); + resource.vfs = &vfs; + resource.provider = &provider; + resource.name = (char*)filename.data(); + spec = mju_decodeResource(&resource, content_type.data(), &vfs); } else { SetLoadError( "Unknown content type; expected text/xml or application/mjb"); diff --git a/src/experimental/studio/app.h b/src/experimental/studio/app.h index b1cad2a8..85dcf136 100644 --- a/src/experimental/studio/app.h +++ b/src/experimental/studio/app.h @@ -45,11 +45,13 @@ class App { void InitEmptyModel(); // Loads an mjModel from the given file. This extension should be one of: - // .xml or .mjb. + // .xml, .mjb, or .mjz. void LoadModelFromFile(const std::string& filepath); // Loads an mjModel from the given memory buffer. The content_type should be - // one of: "text/xml" or "application/mjb". + // one of: "text/xml", "application/mjb", or "application/mjz". For zip files, + // a name is required in order to uniquely identify the model within the + // archive. void LoadModelFromBuffer(std::span buffer, std::string_view content_type, std::string_view name); diff --git a/src/experimental/studio/index.html b/src/experimental/studio/index.html index c34d4619..ee22448d 100644 --- a/src/experimental/studio/index.html +++ b/src/experimental/studio/index.html @@ -7,7 +7,7 @@
- +