Skip to content

Commit 644b42b

Browse files
Google DeepMindcopybara-github
Google DeepMind
authored andcommitted
Add mujoco/experimental and move USD work there.
PiperOrigin-RevId: 740730698 Change-Id: If9c68798cda502d6d7632906a034437e2557802e
1 parent 5c955b8 commit 644b42b

File tree

14 files changed

+2228
-0
lines changed

14 files changed

+2228
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
// Copyright 2025 DeepMind Technologies Limited
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "mjcf/mjcf_file_format.h"
16+
17+
#include <array>
18+
#include <memory>
19+
#include <string>
20+
#include <unordered_set>
21+
22+
#include <mujoco/mujoco.h>
23+
#include "mjcf/mujoco_to_usd.h"
24+
#include <pxr/base/tf/diagnostic.h>
25+
#include <pxr/base/tf/enum.h>
26+
#include <pxr/base/tf/pathUtils.h>
27+
#include <pxr/base/tf/registryManager.h>
28+
#include <pxr/base/tf/staticTokens.h>
29+
#include <pxr/base/tf/type.h>
30+
#include <pxr/base/work/loops.h>
31+
#include <pxr/pxr.h>
32+
#include <pxr/usd/ar/asset.h>
33+
#include <pxr/usd/ar/resolvedPath.h>
34+
#include <pxr/usd/ar/resolver.h>
35+
#include <pxr/usd/sdf/declareHandles.h>
36+
#include <pxr/usd/sdf/fileFormat.h>
37+
#include <pxr/usd/sdf/layer.h>
38+
#include <pxr/usd/usd/usdaFileFormat.h>
39+
#include "tinyxml2.h"
40+
41+
PXR_NAMESPACE_OPEN_SCOPE
42+
43+
TF_DEFINE_PUBLIC_TOKENS(UsdMjcfFileFormatTokens, USD_MJCF_FILE_FORMAT_TOKENS);
44+
45+
TF_REGISTRY_FUNCTION(TfType) {
46+
SDF_DEFINE_FILE_FORMAT(UsdMjcfFileFormat, SdfFileFormat);
47+
}
48+
49+
enum ErrorCodes { XmlParsingError };
50+
TF_REGISTRY_FUNCTION(TfEnum) {
51+
TF_ADD_ENUM_NAME(XmlParsingError, "Error when parsing XML.");
52+
};
53+
54+
namespace {
55+
56+
void ResolveMjcfDependencies(const std::string &xml_string,
57+
const std::string &resolved_path);
58+
59+
void AccumulateFilesRecursive(std::unordered_set<std::string> &files,
60+
tinyxml2::XMLElement *elem,
61+
const std::string &resolved_path) {
62+
// get filename
63+
const char *file = elem->Attribute("file");
64+
65+
if (file != nullptr) {
66+
auto identifier = pxr::ArGetResolver().CreateIdentifier(
67+
std::string(file), pxr::ArResolvedPath(resolved_path));
68+
if (!strcasecmp(elem->Value(), "include") ||
69+
!strcasecmp(elem->Value(), "model")) {
70+
auto include_resolved_path = pxr::ArGetResolver().Resolve(identifier);
71+
auto asset = pxr::ArGetResolver().OpenAsset(include_resolved_path);
72+
ResolveMjcfDependencies(asset->GetBuffer().get(), include_resolved_path);
73+
74+
// Neither of these elements should have children.
75+
return;
76+
}
77+
78+
files.insert(identifier);
79+
}
80+
81+
if (!strcasecmp(elem->Value(), "texture")) {
82+
static const char *attributes[] = {"fileright", "fileup", "fileleft",
83+
"filedown", "filefront", "fileback"};
84+
for (const auto &attribute : attributes) {
85+
const char *file = elem->Attribute(attribute);
86+
if (file != nullptr) {
87+
auto identifier = pxr::ArGetResolver().CreateIdentifier(
88+
std::string(file), pxr::ArResolvedPath(resolved_path));
89+
files.insert(identifier);
90+
}
91+
}
92+
}
93+
94+
tinyxml2::XMLElement *child = elem->FirstChildElement();
95+
for (; child; child = child->NextSiblingElement()) {
96+
AccumulateFilesRecursive(files, child, resolved_path);
97+
}
98+
}
99+
100+
void ResolveMjcfDependencies(const std::string &xml_string,
101+
const std::string &resolved_path) {
102+
// load XML file or parse string
103+
tinyxml2::XMLDocument doc;
104+
doc.Parse(xml_string.c_str());
105+
106+
// error checking
107+
if (doc.Error()) {
108+
TF_ERROR(XmlParsingError, "%d:\n%s\n", doc.ErrorID(), doc.ErrorStr());
109+
return;
110+
}
111+
112+
// get top-level element
113+
tinyxml2::XMLElement *root = doc.RootElement();
114+
if (!root) {
115+
TF_ERROR(XmlParsingError, "XML root element not found");
116+
return;
117+
}
118+
119+
// Accumulate file dependencies.
120+
std::unordered_set<std::string> files = {};
121+
AccumulateFilesRecursive(files, root, resolved_path);
122+
123+
auto open_asset = [resolved_path](const std::string &identifier) {
124+
pxr::ArGetResolver().OpenAsset(pxr::ArGetResolver().Resolve(identifier));
125+
};
126+
// Open all assets in parallel.
127+
pxr::WorkParallelForEach(files.begin(), files.end(), open_asset);
128+
}
129+
} // namespace
130+
131+
UsdMjcfFileFormat::UsdMjcfFileFormat()
132+
: SdfFileFormat(
133+
UsdMjcfFileFormatTokens->Id, UsdMjcfFileFormatTokens->Version,
134+
UsdMjcfFileFormatTokens->Target, UsdMjcfFileFormatTokens->Id) {}
135+
136+
UsdMjcfFileFormat::~UsdMjcfFileFormat() {}
137+
138+
bool UsdMjcfFileFormat::CanRead(const std::string &filePath) const {
139+
auto extension = pxr::TfGetExtension(filePath);
140+
if (extension.empty()) {
141+
return false;
142+
}
143+
144+
return extension == this->GetFormatId();
145+
}
146+
147+
bool UsdMjcfFileFormat::ReadImpl(pxr::SdfLayer *layer, mjSpec *spec) const {
148+
auto data = InitData(layer->GetFileFormatArguments());
149+
150+
auto success = mujoco::usd::WriteSpecToData(spec, data);
151+
mj_deleteSpec(spec);
152+
if (!success) {
153+
return false;
154+
}
155+
156+
_SetLayerData(layer, data);
157+
158+
return true;
159+
}
160+
161+
bool UsdMjcfFileFormat::ReadFromString(pxr::SdfLayer *layer,
162+
const std::string &str) const {
163+
std::array<char, 1024> error;
164+
mjSpec *spec =
165+
mj_parseXMLString(str.c_str(), nullptr, error.data(), error.size());
166+
if (spec == nullptr) {
167+
TF_WARN(XmlParsingError, "%s", error.data());
168+
return false;
169+
}
170+
171+
return ReadImpl(layer, spec);
172+
}
173+
174+
bool UsdMjcfFileFormat::Read(pxr::SdfLayer *layer,
175+
const std::string &resolved_path,
176+
bool metadata_only) const {
177+
// Resolved all dependencies so that they are accessible when parsing
178+
// the XML.
179+
std::shared_ptr<pxr::ArAsset> asset =
180+
pxr::ArGetResolver().OpenAsset(pxr::ArResolvedPath(resolved_path));
181+
auto buffer = asset->GetBuffer();
182+
ResolveMjcfDependencies(buffer.get(), resolved_path);
183+
184+
// Parse to USD.
185+
std::array<char, 1024> error;
186+
mjSpec *spec =
187+
mj_parseXML(resolved_path.c_str(), nullptr, error.data(), error.size());
188+
if (spec == nullptr) {
189+
TF_WARN(XmlParsingError, "%s", error.data());
190+
return false;
191+
}
192+
return ReadImpl(layer, spec);
193+
}
194+
195+
bool UsdMjcfFileFormat::WriteToString(const SdfLayer &layer, std::string *str,
196+
const std::string &comment) const {
197+
return SdfFileFormat::FindById(pxr::UsdUsdaFileFormatTokens->Id)
198+
->WriteToString(layer, str, comment);
199+
}
200+
201+
PXR_NAMESPACE_CLOSE_SCOPE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 DeepMind Technologies Limited
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MJCF_FILE_FORMAT_H_
16+
#define MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MJCF_FILE_FORMAT_H_
17+
18+
#include <string>
19+
20+
#include <mujoco/mujoco.h>
21+
#include <pxr/base/tf/declarePtrs.h>
22+
#include <pxr/base/tf/staticTokens.h>
23+
#include <pxr/pxr.h>
24+
#include <pxr/usd/sdf/declareHandles.h>
25+
#include <pxr/usd/sdf/fileFormat.h>
26+
#include <pxr/usd/usd/api.h>
27+
28+
PXR_NAMESPACE_OPEN_SCOPE
29+
30+
// The Id should realistically be mjcf, but the id and extension need to match.
31+
// So near term it just assumes the only .xml file we would import is MJCF.
32+
#define USD_MJCF_FILE_FORMAT_TOKENS \
33+
((Id, "xml"))((Version, "1.0"))((Target, "usd"))
34+
35+
TF_DECLARE_PUBLIC_TOKENS(UsdMjcfFileFormatTokens, USD_MJCF_FILE_FORMAT_TOKENS);
36+
37+
TF_DECLARE_WEAK_AND_REF_PTRS(UsdMjcfFileFormat);
38+
39+
class UsdMjcfFileFormat : public SdfFileFormat {
40+
public:
41+
using SdfFileFormat::FileFormatArguments;
42+
43+
// Returns true if 'file' can be read by this format plugin.
44+
USD_API
45+
bool CanRead(const std::string &file) const override;
46+
47+
// Reads scene description from the asset specified by resolved_path into
48+
// 'layer'.
49+
//
50+
// metadataOnly is a flag that asks for only the layer metadata to be read in,
51+
// which can be much faster if that is all that is required but currently we
52+
// ignore it.
53+
//
54+
// Returns true if the asset is successfully read into layer, false otherwise.
55+
USD_API
56+
bool Read(pxr::SdfLayer *layer, const std::string &resolved_path,
57+
bool metadata_only) const override;
58+
59+
// Reads data in the string 'str' into 'layer'.
60+
//
61+
// If the file is successfully read, this method returns true. Otherwise,
62+
// false is returned and errors are posted.
63+
USD_API
64+
bool ReadFromString(SdfLayer *layer, const std::string &str) const override;
65+
66+
// Writes the contents in 'layer' to 'str'. This just forwards to the usda
67+
// implementation.
68+
USD_API
69+
bool WriteToString(const SdfLayer &layer, std::string *str,
70+
const std::string &comment) const override;
71+
72+
protected:
73+
SDF_FILE_FORMAT_FACTORY_ACCESS;
74+
75+
UsdMjcfFileFormat();
76+
virtual ~UsdMjcfFileFormat();
77+
78+
private:
79+
// Function delegated to by Read and ReadFromString.
80+
bool ReadImpl(SdfLayer *layer, mjSpec *spec) const;
81+
};
82+
83+
PXR_NAMESPACE_CLOSE_SCOPE
84+
85+
#endif // MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MJCF_FILE_FORMAT_H_

0 commit comments

Comments
 (0)