Move mjSpec/mjModel/mjData management into a separate class.
PiperOrigin-RevId: 866327584 Change-Id: I72f6ec656e193f67dee22214fe01c517432f87e5
This commit is contained in:
committed by
Copybara-Service
parent
eb7aaa2868
commit
3e8a81383a
@@ -48,6 +48,8 @@ target_sources(${MUJOCO_PLATFORM_TARGET_NAME}
|
|||||||
imgui_widgets.h
|
imgui_widgets.h
|
||||||
interaction.cc
|
interaction.cc
|
||||||
interaction.h
|
interaction.h
|
||||||
|
model_holder.cc
|
||||||
|
model_holder.h
|
||||||
picture_gui.h
|
picture_gui.h
|
||||||
picture_gui.cc
|
picture_gui.cc
|
||||||
plugin.cc
|
plugin.cc
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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_
|
||||||
+22
-111
@@ -37,12 +37,12 @@
|
|||||||
#include "experimental/platform/helpers.h"
|
#include "experimental/platform/helpers.h"
|
||||||
#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/model_holder.h"
|
||||||
#include "experimental/platform/picture_gui.h"
|
#include "experimental/platform/picture_gui.h"
|
||||||
#include "experimental/platform/plugin.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"
|
||||||
#include "user/user_resource.h"
|
|
||||||
|
|
||||||
namespace mujoco::studio {
|
namespace mujoco::studio {
|
||||||
|
|
||||||
@@ -120,20 +120,8 @@ App::App(Config config)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::ClearModel() {
|
void App::ClearModel() {
|
||||||
if (model_) {
|
model_holder_.reset();
|
||||||
mj_deleteData(data_);
|
|
||||||
data_ = nullptr;
|
|
||||||
mj_deleteModel(model_);
|
|
||||||
model_ = nullptr;
|
|
||||||
|
|
||||||
if (spec_) {
|
|
||||||
mj_deleteSpec(spec_);
|
|
||||||
spec_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window_->SetTitle("MuJoCo Studio");
|
window_->SetTitle("MuJoCo Studio");
|
||||||
|
|
||||||
step_control_.SetSpeed(100.f);
|
step_control_.SetSpeed(100.f);
|
||||||
profiler_.Clear();
|
profiler_.Clear();
|
||||||
tmp_ = UiTempState();
|
tmp_ = UiTempState();
|
||||||
@@ -152,138 +140,61 @@ void App::RequestModelReload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void App::InitEmptyModel() {
|
void App::InitEmptyModel() {
|
||||||
mjSpec* spec = mj_makeSpec();
|
model_holder_ = platform::ModelHolder::FromSpec(mj_makeSpec());
|
||||||
mjModel* model = mj_compile(spec, nullptr);
|
OnModelLoaded("", kEmptyModel);
|
||||||
InitModel(model, spec, nullptr, "", kEmptyModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::LoadModelFromFile(const std::string& filepath) {
|
void App::LoadModelFromFile(const std::string& filepath) {
|
||||||
mjModel* model = nullptr;
|
|
||||||
mjSpec* spec = nullptr;
|
|
||||||
mjVFS vfs;
|
|
||||||
mj_defaultVFS(&vfs);
|
|
||||||
|
|
||||||
const std::string resolved_file =
|
const std::string resolved_file =
|
||||||
platform::ResolveFile(filepath, search_paths_);
|
platform::ResolveFile(filepath, search_paths_);
|
||||||
|
|
||||||
if (resolved_file.empty()) {
|
if (resolved_file.empty()) {
|
||||||
SetLoadError("File not found: " + filepath);
|
SetLoadError("File not found: " + filepath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char err[1000] = "";
|
model_holder_ = platform::ModelHolder::FromFile(resolved_file);
|
||||||
if (resolved_file.ends_with(".mjb")) {
|
if (model_holder_->ok()) {
|
||||||
model = mj_loadModel(resolved_file.c_str(), &vfs);
|
OnModelLoaded(filepath, kModelFromFile);
|
||||||
|
UpdateFilePaths(resolved_file);
|
||||||
|
window_->SetTitle("MuJoCo Studio : " + filepath);
|
||||||
} else {
|
} else {
|
||||||
spec = mj_parse(resolved_file.c_str(), nullptr, &vfs, err, sizeof(err));
|
SetLoadError(std::string(model_holder_->error()));
|
||||||
}
|
}
|
||||||
if (err[0]) {
|
|
||||||
SetLoadError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitModel(model, spec, &vfs, filepath, kModelFromFile);
|
|
||||||
UpdateFilePaths(resolved_file);
|
|
||||||
window_->SetTitle("MuJoCo Studio : " + 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,
|
void App::LoadModelFromBuffer(std::span<const std::byte> buffer,
|
||||||
std::string_view content_type,
|
std::string_view content_type,
|
||||||
std::string_view filename) {
|
std::string_view filename) {
|
||||||
mjModel* model = nullptr;
|
model_holder_ =
|
||||||
mjSpec* spec = nullptr;
|
platform::ModelHolder::FromBuffer(buffer, content_type, filename);
|
||||||
|
if (model_holder_->ok()) {
|
||||||
mjVFS vfs;
|
OnModelLoaded(std::string(filename), kModelFromFile);
|
||||||
mj_defaultVFS(&vfs);
|
|
||||||
|
|
||||||
char err[1000] = "";
|
|
||||||
if (content_type == "text/xml") {
|
|
||||||
const char* ptr = reinterpret_cast<const char*>(buffer.data());
|
|
||||||
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 {
|
} else {
|
||||||
SetLoadError("Unknown content type; expected text/xml or application/mjb");
|
SetLoadError(std::string(model_holder_->error()));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (err[0]) {
|
|
||||||
SetLoadError(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitModel(model, spec, &vfs, std::string(filename), kModelFromBuffer);
|
|
||||||
|
|
||||||
mj_deleteVFS(&vfs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void App::InitModel(mjModel* model, mjSpec* spec, mjVFS* vfs,
|
void App::OnModelLoaded(std::string filename, ModelKind model_kind) {
|
||||||
std::string filename, ModelKind model_kind) {
|
model_path_ = std::move(filename);
|
||||||
|
|
||||||
if (model_kind_ == kEmptyModel) {
|
if (model_kind_ == kEmptyModel) {
|
||||||
step_control_.Unpause();
|
step_control_.Unpause();
|
||||||
}
|
}
|
||||||
ClearModel();
|
|
||||||
|
|
||||||
model_path_ = std::move(filename);
|
|
||||||
model_kind_ = model_kind;
|
model_kind_ = model_kind;
|
||||||
if (model_kind_ == kEmptyModel) {
|
if (model_kind_ == kEmptyModel) {
|
||||||
step_control_.Pause();
|
step_control_.Pause();
|
||||||
}
|
}
|
||||||
|
|
||||||
spec_ = spec;
|
|
||||||
model_ = model;
|
|
||||||
|
|
||||||
// If we have a spec but not a model, we need to compile the model from the spec.
|
|
||||||
if (spec_ && !model_) {
|
|
||||||
model_ = mj_compile(spec_, vfs);
|
|
||||||
if (!model_) {
|
|
||||||
SetLoadError("Error compiling model from spec.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!model_) {
|
|
||||||
mju_error("Error making data for model: %s", model_path_.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
data_ = mj_makeData(model_);
|
|
||||||
if (!data_) {
|
|
||||||
mju_error("Error making data for model: %s", model_path_.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset/reinitialize everything that depends on the new mjModel.
|
// Reset/reinitialize everything that depends on the new mjModel.
|
||||||
renderer_->Init(model_);
|
mjModel* model = model_holder_->model();
|
||||||
const int state_size = mj_stateSize(model_, mjSTATE_INTEGRATION);
|
renderer_->Init(model);
|
||||||
|
const int state_size = mj_stateSize(model, mjSTATE_INTEGRATION);
|
||||||
history_.Init(state_size);
|
history_.Init(state_size);
|
||||||
|
|
||||||
// Initialize the speed based on the model's default real-time setting.
|
// Initialize the speed based on the model's default real-time setting.
|
||||||
float min_error = FLT_MAX;
|
float min_error = FLT_MAX;
|
||||||
const float desired = mju_log(100 * model_->vis.global.realtime);
|
const float desired = mju_log(100 * model->vis.global.realtime);
|
||||||
for (int i = 0; i < kPercentRealTime.size(); ++i) {
|
for (int i = 0; i < kPercentRealTime.size(); ++i) {
|
||||||
const float speed = std::stof(kPercentRealTime[i]);
|
const float speed = std::stof(kPercentRealTime[i]);
|
||||||
const float error = mju_abs(mju_log(speed) - desired);
|
const float error = mju_abs(mju_log(speed) - desired);
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include <mujoco/mujoco.h>
|
#include <mujoco/mujoco.h>
|
||||||
#include "experimental/platform/gui.h"
|
#include "experimental/platform/gui.h"
|
||||||
#include "experimental/platform/interaction.h"
|
#include "experimental/platform/interaction.h"
|
||||||
|
#include "experimental/platform/model_holder.h"
|
||||||
#include "experimental/platform/picture_gui.h"
|
#include "experimental/platform/picture_gui.h"
|
||||||
#include "experimental/platform/renderer.h"
|
#include "experimental/platform/renderer.h"
|
||||||
#include "experimental/platform/sim_history.h"
|
#include "experimental/platform/sim_history.h"
|
||||||
@@ -168,8 +169,7 @@ class App {
|
|||||||
|
|
||||||
// Updates the currently loaded model to the given model. If model is null,
|
// Updates the currently loaded model to the given model. If model is null,
|
||||||
// then compile the spec to a model.
|
// then compile the spec to a model.
|
||||||
void InitModel(mjModel* model, mjSpec* spec, mjVFS* vfs, std::string filename,
|
void OnModelLoaded(std::string filename, ModelKind model_kind);
|
||||||
ModelKind model_kind);
|
|
||||||
|
|
||||||
void SetLoadError(std::string error);
|
void SetLoadError(std::string error);
|
||||||
void UpdateFilePaths(const std::string& resolved_path);
|
void UpdateFilePaths(const std::string& resolved_path);
|
||||||
@@ -207,12 +207,13 @@ class App {
|
|||||||
float GetExpectedLabelWidth();
|
float GetExpectedLabelWidth();
|
||||||
std::vector<const char*> GetCameraNames();
|
std::vector<const char*> GetCameraNames();
|
||||||
|
|
||||||
mjSpec* spec() { return spec_; }
|
mjSpec* spec() { return model_holder_->spec(); }
|
||||||
mjModel* model() { return model_; }
|
mjModel* model() { return model_holder_->model(); }
|
||||||
mjData* data() { return data_; }
|
mjData* data() { return model_holder_->data(); }
|
||||||
bool has_spec() const { return spec_ != nullptr; }
|
bool has_spec() const { return model_holder_ && model_holder_->spec(); }
|
||||||
bool has_model() const { return model_ != nullptr; }
|
bool has_model() const { return model_holder_ && model_holder_->model(); }
|
||||||
bool has_data() const { return data_ != nullptr; }
|
bool has_data() const { return model_holder_ && model_holder_->data(); }
|
||||||
|
|
||||||
|
|
||||||
std::string ini_path_;
|
std::string ini_path_;
|
||||||
std::string model_name_; // Used if model_kind_ is kModelFromBuffer.
|
std::string model_name_; // Used if model_kind_ is kModelFromBuffer.
|
||||||
@@ -224,15 +225,11 @@ class App {
|
|||||||
|
|
||||||
std::unique_ptr<platform::Window> window_;
|
std::unique_ptr<platform::Window> window_;
|
||||||
std::unique_ptr<platform::Renderer> renderer_;
|
std::unique_ptr<platform::Renderer> renderer_;
|
||||||
|
std::unique_ptr<platform::ModelHolder> model_holder_;
|
||||||
platform::StepControl step_control_;
|
platform::StepControl step_control_;
|
||||||
platform::SimProfiler profiler_;
|
platform::SimProfiler profiler_;
|
||||||
platform::SimHistory history_;
|
platform::SimHistory history_;
|
||||||
|
|
||||||
std::vector<std::string> search_paths_;
|
std::vector<std::string> search_paths_;
|
||||||
|
|
||||||
mjSpec* spec_ = nullptr;
|
|
||||||
mjModel* model_ = nullptr;
|
|
||||||
mjData* data_ = nullptr;
|
|
||||||
std::vector<std::byte> pixels_;
|
std::vector<std::byte> pixels_;
|
||||||
|
|
||||||
mjvCamera camera_;
|
mjvCamera camera_;
|
||||||
|
|||||||
Reference in New Issue
Block a user