Create mjpDecoder, mj_parse, and mju_decodeResource

- plugin system similar to mjpResourceProvider, but instead of loading a resource it converts an existing mjResource into an mjSpec.
  - The returned spec is then composed into the referencing spec.
  - This enables different file types to generate arbitrary specs, and allows us to separate format parsing from compilation code.

Follow up CLs will move some of the logic in src/engine for PNG, USD, KTX, OBJ loading into decoders.

The mj_parse function MjSpec from a given file, it's a more generic version of mj_parseXML.

In it's implementation, mj_parse as opposed to mj_parseXML will look for any registered decoder and
not assume we are striclty dealing with MJCF.

PiperOrigin-RevId: 826149497
Change-Id: I0ece26904280cb94bd5ded6dd5a565c539d60254
This commit is contained in:
Sam Haves
2025-10-30 12:53:40 -07:00
committed by Copybara-Service
parent f2badc05ac
commit 57f7145806
16 changed files with 2063 additions and 1555 deletions
+18
View File
@@ -31,6 +31,7 @@
#include "user/user_cache.h"
#include "user/user_model.h"
#include "user/user_objects.h"
#include "user/user_resource.h"
#include "user/user_util.h"
namespace {
@@ -63,7 +64,24 @@ mjSpec* mj_copySpec(const mjSpec* s) {
return &modelC->spec;
}
// parse file into spec
mjSpec* mj_parse(const char* filename, const char* content_type,
const mjVFS* vfs, char* error, int error_sz) {
// early exit for existing XML workflow
auto filepath = mujoco::user::FilePath(filename);
if (filepath.Ext() == ".xml" || (content_type && std::strcmp(content_type, "text/xml") == 0)) {
return mj_parseXML(filename, vfs, error, error_sz);
}
mjResource* resource = mju_openResource("", filename, vfs, error, error_sz);
if (!resource) {
mju_error("Could not load resource %s", filename);
}
mjSpec* spec = mju_decodeResource(resource, content_type);
mju_closeResource(resource);
return spec;
}
// compile model
mjModel* mj_compile(mjSpec* s, const mjVFS* vfs) {