Add mjz support to studio.

PiperOrigin-RevId: 861627355
Change-Id: Iafd291965ca78d03fdc1efe0e40dabcd4f2dacc4
This commit is contained in:
Haroon Qureshi
2026-01-27 03:22:27 -08:00
committed by Copybara-Service
parent 32a4412e2b
commit fe16255602
4 changed files with 34 additions and 3 deletions
+25
View File
@@ -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<const std::byte> 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<int>(self->buffer.size());
};
close = [](mjResource* resource) {};
}
std::span<const std::byte> buffer;
};
void App::LoadModelFromBuffer(std::span<const std::byte> buffer,
std::string_view content_type,
std::string_view filename) {
@@ -217,6 +234,14 @@ void App::LoadModelFromBuffer(std::span<const std::byte> 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");
+4 -2
View File
@@ -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<const std::byte> buffer,
std::string_view content_type,
std::string_view name);
+1 -1
View File
@@ -7,7 +7,7 @@
<body style="margin: 0; overflow: hidden">
<div style="position: absolute; top: 3px; right: 3px; z-index: 1;">
<button id="uploadButton">Upload Model</button>
<input type="file" id="fileInput" accept=".xml,.mjb" style="display: none;">
<input type="file" id="fileInput" accept=".xml,.mjb,.mjz,.zip" style="display: none;">
</div>
<canvas
class="emscripten"
+4
View File
@@ -104,6 +104,10 @@ void LoadFile(const std::string& filename, const std::string& data) {
std::string content_type;
if (filename.ends_with(".mjb")) {
content_type = "application/mjb";
} else if (filename.ends_with(".mjz")) {
content_type = "application/zip";
} else if (filename.ends_with(".zip")) {
content_type = "application/zip";
} else if (filename.ends_with(".xml")) {
content_type = "text/xml";
} else {