Move mjSpec/mjModel/mjData management into a separate class.

PiperOrigin-RevId: 866327584
Change-Id: I72f6ec656e193f67dee22214fe01c517432f87e5
This commit is contained in:
Haroon Qureshi
2026-02-06 00:50:18 -08:00
committed by Copybara-Service
parent eb7aaa2868
commit 3e8a81383a
5 changed files with 251 additions and 124 deletions
+2
View File
@@ -48,6 +48,8 @@ target_sources(${MUJOCO_PLATFORM_TARGET_NAME}
imgui_widgets.h
interaction.cc
interaction.h
model_holder.cc
model_holder.h
picture_gui.h
picture_gui.cc
plugin.cc
+140
View File
@@ -0,0 +1,140 @@
// 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 "experimental/platform/model_holder.h"
#include <cstddef>
#include <cstring>
#include <memory>
#include <span>
#include <string_view>
#include <mujoco/mujoco.h>
#include "user/user_resource.h"
namespace mujoco::platform {
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;
};
std::unique_ptr<ModelHolder> ModelHolder::FromSpec(mjSpec* spec) {
auto mh = std::unique_ptr<ModelHolder>(new ModelHolder());
mh->InitFromSpec(spec);
return mh;
}
std::unique_ptr<ModelHolder> ModelHolder::FromFile(std::string_view filepath) {
auto mh = std::unique_ptr<ModelHolder>(new ModelHolder());
mh->InitFromFile(filepath);
return mh;
}
std::unique_ptr<ModelHolder> ModelHolder::FromBuffer(
std::span<const std::byte> buffer, std::string_view content_type,
std::string_view filename) {
auto mh = std::unique_ptr<ModelHolder>(new ModelHolder());
mh->InitFromBuffer(buffer, content_type, filename);
return mh;
}
ModelHolder::~ModelHolder() {
if (data_) {
mj_deleteData(data_);
}
if (model_) {
mj_deleteModel(model_);
}
if (spec_) {
mj_deleteSpec(spec_);
}
mj_deleteVFS(&vfs_);
}
void ModelHolder::PostInit() {
if (spec_ && !model_) {
model_ = mj_compile(spec_, &vfs_);
if (!model_) {
SetLoadError("Error compiling model from spec.");
return;
}
}
data_ = mj_makeData(model_);
if (!data_) {
SetLoadError("Error making data for model.");
}
}
void ModelHolder::InitFromSpec(mjSpec* spec) {
spec_ = spec;
PostInit();
}
void ModelHolder::InitFromFile(std::string_view filepath) {
mj_defaultVFS(&vfs_);
if (filepath.ends_with(".mjb")) {
model_ = mj_loadModel(filepath.data(), &vfs_);
} else {
spec_ = mj_parse(filepath.data(), nullptr, &vfs_, error_, sizeof(error_));
}
if (error_[0] == 0) {
PostInit();
}
}
void ModelHolder::InitFromBuffer(std::span<const std::byte> buffer,
std::string_view content_type,
std::string_view filename) {
mj_defaultVFS(&vfs_);
if (content_type == "text/xml") {
const char* ptr = reinterpret_cast<const char*>(buffer.data());
spec_ = mj_parseXMLString(ptr, nullptr, error_, sizeof(error_));
} else if (content_type == "application/mjb") {
model_ = mj_loadModelBuffer(buffer.data(), buffer.size());
} else if (content_type == "application/zip") {
BufferProvider provider(buffer);
mjResource resource;
std::memset(&resource, 0, sizeof(mjResource));
resource.vfs = &vfs_;
resource.provider = &provider;
resource.name = const_cast<char*>(filename.data());
spec_ = mju_decodeResource(&resource, content_type.data(), &vfs_);
} else {
SetLoadError(
"Unknown content type; expected text/xml or application/mjb");
}
if (error_[0] == 0) {
PostInit();
}
}
void ModelHolder::SetLoadError(std::string_view error) {
strncpy(error_, error.data(), sizeof(error_) - 1);
error_[sizeof(error_) - 1] = 0;
}
} // namespace mujoco::platform
+77
View File
@@ -0,0 +1,77 @@
// 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_MODEL_HOLDER_H_
#define MUJOCO_SRC_EXPERIMENTAL_PLATFORM_MODEL_HOLDER_H_
#include <memory>
#include <cstddef>
#include <span>
#include <string_view>
#include <mujoco/mujoco.h>
namespace mujoco::platform {
// Container for storing an mjModel as well as its associated mjData. Also, if
// applicable, stores the mjSpec and mjVFS from which the model was constructed.
class ModelHolder {
public:
ModelHolder(const ModelHolder&) = delete;
ModelHolder& operator=(const ModelHolder&) = delete;
~ModelHolder();
// Creates an mjModel from an existing mjSpec. This class takes ownership of
// the spec and will `mj_deleteSpec` it on destruction.
static std::unique_ptr<ModelHolder> FromSpec(mjSpec* spec);
// Creates an mjModel by attempting to open and parse the give file.
static std::unique_ptr<ModelHolder> FromFile(std::string_view filepath);
// Creates an mjModel by attempting to decode the given buffer.
static std::unique_ptr<ModelHolder> FromBuffer(
std::span<const std::byte> buffer, std::string_view content_type,
std::string_view filename);
// Accessors to the MuJoCo structures managed by this object.
mjVFS* vfs() { return &vfs_; }
mjSpec* spec() { return spec_; }
mjData* data() { return data_; }
mjModel* model() { return model_; }
// Returns true if the holder holds a valid model (and data).
bool ok() const { return error_[0] == 0 && model_ && data_; }
// Returns the error message if the model failed to load.
std::string_view error() const { return error_; }
private:
ModelHolder() = default;
void InitFromSpec(mjSpec* spec);
void InitFromFile(std::string_view filepath);
void InitFromBuffer(std::span<const std::byte> buffer,
std::string_view content_type,
std::string_view filename);
void PostInit();
void SetLoadError(std::string_view error);
mjVFS vfs_;
mjSpec* spec_ = nullptr;
mjModel* model_ = nullptr;
mjData* data_ = nullptr;
char error_[1000] = "";
};
} // namespace mujoco::platform
#endif // MUJOCO_SRC_EXPERIMENTAL_PLATFORM_MODEL_HOLDER_H_