Add args field to mjResource for decoder and encoder arguments.

Add an optional `const char* args` field to `mjResource` to allow passing
resource arguments/hints (such as requested channel count or encoding options)
to resource decoders and encoders.

PiperOrigin-RevId: 959774058
Change-Id: Icc41a2bb3895fef24f98c5fa9a77cdad092a6bc7
This commit is contained in:
Sam Haves
2026-08-05 11:30:42 -07:00
committed by Copybara-Service
parent 83e621d771
commit 596b6f433d
5 changed files with 74 additions and 0 deletions
+2
View File
@@ -30,6 +30,8 @@ Actuation
- :ref:`mjsActuator` gained ``velrange`` and ``ffrange`` fields, changing its size and layout. The :ref:`mjtGain`
and :ref:`mjtDyn` enums gained ``pid`` members, shifting the values of ``mjGAIN_USER`` and ``mjDYN_USER``.
- :ref:`mjResource` gained an ``args`` field (changing its size and layout), used to hold optional extra encoding and
decoding arguments formatted as URI query parameters (separated by ``&``).
Engine
^^^^^^
+2
View File
@@ -1252,6 +1252,8 @@ typedef struct mjResource_ {
mjVFS* vfs; // pointer to the VFS
char timestamp[512]; // timestamp of the resource
const struct mjpResourceProvider* provider; // pointer to the provider
const char* args; // resource arguments/hints, URI query format key=val&...
// (optional)
} mjResource;
typedef struct mjpResourceProvider {
const char* prefix; // prefix for match against a resource name
+2
View File
@@ -30,6 +30,8 @@ typedef struct mjResource_ {
mjVFS* vfs; // pointer to the VFS
char timestamp[512]; // timestamp of the resource
const struct mjpResourceProvider* provider; // pointer to the provider
const char* args; // resource arguments/hints, URI query format key=val&...
// (optional)
} mjResource;
// callback for opening a resource, returns zero on failure.
+44
View File
@@ -16,6 +16,10 @@
#include <string.h>
#include <cstdlib>
#include <cstring>
#include <string_view>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <mujoco/mjmodel.h>
@@ -118,5 +122,45 @@ TEST_F(DecoderPluginTest, CanDecode) {
mj_deleteSpec(spec);
}
TEST_F(DecoderPluginTest, DecodeWithResourceArgs) {
static auto decode_args_fn =
+[](mjResource* resource, const mjVFS* vfs) -> mjSpec* {
mjSpec* s = MakeSimpleSpec();
if (resource && resource->args) {
std::string_view args_view(resource->args);
size_t pos = args_view.find("size=");
if (pos != std::string_view::npos) {
mjsElement* elem = mjs_firstElement(s, mjOBJ_GEOM);
mjsGeom* geom = mjs_asGeom(elem);
if (geom) {
geom->size[0] = std::atof(args_view.data() + pos + 5);
}
}
}
return s;
};
mjpDecoder decoder;
mjp_defaultDecoder(&decoder);
decoder.content_type = "model/argsformat";
decoder.extension = ".argsformat";
decoder.can_decode = +[](const mjResource* r) -> int { return 1; };
decoder.decode = decode_args_fn;
mjp_registerDecoder(&decoder);
mjResource resource;
std::memset(&resource, 0, sizeof(resource));
resource.name = const_cast<char*>("test.argsformat");
resource.args = "size=42.0&foo=bar";
mjSpec* spec = mju_decodeResource(&resource, "model/argsformat", nullptr);
ASSERT_THAT(spec, testing::NotNull());
mjsElement* elem = mjs_firstElement(spec, mjOBJ_GEOM);
mjsGeom* geom = mjs_asGeom(elem);
ASSERT_THAT(geom, testing::NotNull());
EXPECT_DOUBLE_EQ(geom->size[0], 42.0);
mj_deleteSpec(spec);
}
} // namespace
} // namespace mujoco
+24
View File
@@ -130,5 +130,29 @@ TEST_F(EncoderPluginTest, EncodeModel) {
mj_deleteSpec(spec);
}
TEST_F(EncoderPluginTest, EncodeWithResourceArgs) {
mjSpec* spec = mj_makeSpec();
mjModel* model = mj_compile(spec, nullptr);
ASSERT_THAT(model, testing::NotNull());
const mjpEncoder* found = mjp_findEncoder("output.fakeformat", nullptr);
ASSERT_THAT(found, testing::NotNull());
mjResource resource = {};
resource.name = const_cast<char*>("output.fakeformat");
resource.args = "format=binary&compression=9";
int result = found->encode(spec, model, nullptr, &resource);
EXPECT_GT(result, 0);
auto* output = static_cast<FakeEncoderOutput*>(resource.data);
ASSERT_THAT(output, testing::NotNull());
EXPECT_STREQ(resource.args, "format=binary&compression=9");
found->close_resource(&resource);
mj_deleteModel(model);
mj_deleteSpec(spec);
}
} // namespace
} // namespace mujoco