Add mujoco/experimental and move USD work there.
PiperOrigin-RevId: 740730698 Change-Id: If9c68798cda502d6d7632906a034437e2557802e
This commit is contained in:
committed by
Copybara-Service
parent
5c955b8fe2
commit
644b42bee3
@@ -0,0 +1,201 @@
|
||||
// Copyright 2025 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 "mjcf/mjcf_file_format.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "mjcf/mujoco_to_usd.h"
|
||||
#include <pxr/base/tf/diagnostic.h>
|
||||
#include <pxr/base/tf/enum.h>
|
||||
#include <pxr/base/tf/pathUtils.h>
|
||||
#include <pxr/base/tf/registryManager.h>
|
||||
#include <pxr/base/tf/staticTokens.h>
|
||||
#include <pxr/base/tf/type.h>
|
||||
#include <pxr/base/work/loops.h>
|
||||
#include <pxr/pxr.h>
|
||||
#include <pxr/usd/ar/asset.h>
|
||||
#include <pxr/usd/ar/resolvedPath.h>
|
||||
#include <pxr/usd/ar/resolver.h>
|
||||
#include <pxr/usd/sdf/declareHandles.h>
|
||||
#include <pxr/usd/sdf/fileFormat.h>
|
||||
#include <pxr/usd/sdf/layer.h>
|
||||
#include <pxr/usd/usd/usdaFileFormat.h>
|
||||
#include "tinyxml2.h"
|
||||
|
||||
PXR_NAMESPACE_OPEN_SCOPE
|
||||
|
||||
TF_DEFINE_PUBLIC_TOKENS(UsdMjcfFileFormatTokens, USD_MJCF_FILE_FORMAT_TOKENS);
|
||||
|
||||
TF_REGISTRY_FUNCTION(TfType) {
|
||||
SDF_DEFINE_FILE_FORMAT(UsdMjcfFileFormat, SdfFileFormat);
|
||||
}
|
||||
|
||||
enum ErrorCodes { XmlParsingError };
|
||||
TF_REGISTRY_FUNCTION(TfEnum) {
|
||||
TF_ADD_ENUM_NAME(XmlParsingError, "Error when parsing XML.");
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
void ResolveMjcfDependencies(const std::string &xml_string,
|
||||
const std::string &resolved_path);
|
||||
|
||||
void AccumulateFilesRecursive(std::unordered_set<std::string> &files,
|
||||
tinyxml2::XMLElement *elem,
|
||||
const std::string &resolved_path) {
|
||||
// get filename
|
||||
const char *file = elem->Attribute("file");
|
||||
|
||||
if (file != nullptr) {
|
||||
auto identifier = pxr::ArGetResolver().CreateIdentifier(
|
||||
std::string(file), pxr::ArResolvedPath(resolved_path));
|
||||
if (!strcasecmp(elem->Value(), "include") ||
|
||||
!strcasecmp(elem->Value(), "model")) {
|
||||
auto include_resolved_path = pxr::ArGetResolver().Resolve(identifier);
|
||||
auto asset = pxr::ArGetResolver().OpenAsset(include_resolved_path);
|
||||
ResolveMjcfDependencies(asset->GetBuffer().get(), include_resolved_path);
|
||||
|
||||
// Neither of these elements should have children.
|
||||
return;
|
||||
}
|
||||
|
||||
files.insert(identifier);
|
||||
}
|
||||
|
||||
if (!strcasecmp(elem->Value(), "texture")) {
|
||||
static const char *attributes[] = {"fileright", "fileup", "fileleft",
|
||||
"filedown", "filefront", "fileback"};
|
||||
for (const auto &attribute : attributes) {
|
||||
const char *file = elem->Attribute(attribute);
|
||||
if (file != nullptr) {
|
||||
auto identifier = pxr::ArGetResolver().CreateIdentifier(
|
||||
std::string(file), pxr::ArResolvedPath(resolved_path));
|
||||
files.insert(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tinyxml2::XMLElement *child = elem->FirstChildElement();
|
||||
for (; child; child = child->NextSiblingElement()) {
|
||||
AccumulateFilesRecursive(files, child, resolved_path);
|
||||
}
|
||||
}
|
||||
|
||||
void ResolveMjcfDependencies(const std::string &xml_string,
|
||||
const std::string &resolved_path) {
|
||||
// load XML file or parse string
|
||||
tinyxml2::XMLDocument doc;
|
||||
doc.Parse(xml_string.c_str());
|
||||
|
||||
// error checking
|
||||
if (doc.Error()) {
|
||||
TF_ERROR(XmlParsingError, "%d:\n%s\n", doc.ErrorID(), doc.ErrorStr());
|
||||
return;
|
||||
}
|
||||
|
||||
// get top-level element
|
||||
tinyxml2::XMLElement *root = doc.RootElement();
|
||||
if (!root) {
|
||||
TF_ERROR(XmlParsingError, "XML root element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Accumulate file dependencies.
|
||||
std::unordered_set<std::string> files = {};
|
||||
AccumulateFilesRecursive(files, root, resolved_path);
|
||||
|
||||
auto open_asset = [resolved_path](const std::string &identifier) {
|
||||
pxr::ArGetResolver().OpenAsset(pxr::ArGetResolver().Resolve(identifier));
|
||||
};
|
||||
// Open all assets in parallel.
|
||||
pxr::WorkParallelForEach(files.begin(), files.end(), open_asset);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
UsdMjcfFileFormat::UsdMjcfFileFormat()
|
||||
: SdfFileFormat(
|
||||
UsdMjcfFileFormatTokens->Id, UsdMjcfFileFormatTokens->Version,
|
||||
UsdMjcfFileFormatTokens->Target, UsdMjcfFileFormatTokens->Id) {}
|
||||
|
||||
UsdMjcfFileFormat::~UsdMjcfFileFormat() {}
|
||||
|
||||
bool UsdMjcfFileFormat::CanRead(const std::string &filePath) const {
|
||||
auto extension = pxr::TfGetExtension(filePath);
|
||||
if (extension.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return extension == this->GetFormatId();
|
||||
}
|
||||
|
||||
bool UsdMjcfFileFormat::ReadImpl(pxr::SdfLayer *layer, mjSpec *spec) const {
|
||||
auto data = InitData(layer->GetFileFormatArguments());
|
||||
|
||||
auto success = mujoco::usd::WriteSpecToData(spec, data);
|
||||
mj_deleteSpec(spec);
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_SetLayerData(layer, data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UsdMjcfFileFormat::ReadFromString(pxr::SdfLayer *layer,
|
||||
const std::string &str) const {
|
||||
std::array<char, 1024> error;
|
||||
mjSpec *spec =
|
||||
mj_parseXMLString(str.c_str(), nullptr, error.data(), error.size());
|
||||
if (spec == nullptr) {
|
||||
TF_WARN(XmlParsingError, "%s", error.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
return ReadImpl(layer, spec);
|
||||
}
|
||||
|
||||
bool UsdMjcfFileFormat::Read(pxr::SdfLayer *layer,
|
||||
const std::string &resolved_path,
|
||||
bool metadata_only) const {
|
||||
// Resolved all dependencies so that they are accessible when parsing
|
||||
// the XML.
|
||||
std::shared_ptr<pxr::ArAsset> asset =
|
||||
pxr::ArGetResolver().OpenAsset(pxr::ArResolvedPath(resolved_path));
|
||||
auto buffer = asset->GetBuffer();
|
||||
ResolveMjcfDependencies(buffer.get(), resolved_path);
|
||||
|
||||
// Parse to USD.
|
||||
std::array<char, 1024> error;
|
||||
mjSpec *spec =
|
||||
mj_parseXML(resolved_path.c_str(), nullptr, error.data(), error.size());
|
||||
if (spec == nullptr) {
|
||||
TF_WARN(XmlParsingError, "%s", error.data());
|
||||
return false;
|
||||
}
|
||||
return ReadImpl(layer, spec);
|
||||
}
|
||||
|
||||
bool UsdMjcfFileFormat::WriteToString(const SdfLayer &layer, std::string *str,
|
||||
const std::string &comment) const {
|
||||
return SdfFileFormat::FindById(pxr::UsdUsdaFileFormatTokens->Id)
|
||||
->WriteToString(layer, str, comment);
|
||||
}
|
||||
|
||||
PXR_NAMESPACE_CLOSE_SCOPE
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2025 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_USD_PLUGINS_MJCF_MJCF_FILE_FORMAT_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MJCF_FILE_FORMAT_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include <pxr/base/tf/declarePtrs.h>
|
||||
#include <pxr/base/tf/staticTokens.h>
|
||||
#include <pxr/pxr.h>
|
||||
#include <pxr/usd/sdf/declareHandles.h>
|
||||
#include <pxr/usd/sdf/fileFormat.h>
|
||||
#include <pxr/usd/usd/api.h>
|
||||
|
||||
PXR_NAMESPACE_OPEN_SCOPE
|
||||
|
||||
// The Id should realistically be mjcf, but the id and extension need to match.
|
||||
// So near term it just assumes the only .xml file we would import is MJCF.
|
||||
#define USD_MJCF_FILE_FORMAT_TOKENS \
|
||||
((Id, "xml"))((Version, "1.0"))((Target, "usd"))
|
||||
|
||||
TF_DECLARE_PUBLIC_TOKENS(UsdMjcfFileFormatTokens, USD_MJCF_FILE_FORMAT_TOKENS);
|
||||
|
||||
TF_DECLARE_WEAK_AND_REF_PTRS(UsdMjcfFileFormat);
|
||||
|
||||
class UsdMjcfFileFormat : public SdfFileFormat {
|
||||
public:
|
||||
using SdfFileFormat::FileFormatArguments;
|
||||
|
||||
// Returns true if 'file' can be read by this format plugin.
|
||||
USD_API
|
||||
bool CanRead(const std::string &file) const override;
|
||||
|
||||
// Reads scene description from the asset specified by resolved_path into
|
||||
// 'layer'.
|
||||
//
|
||||
// metadataOnly is a flag that asks for only the layer metadata to be read in,
|
||||
// which can be much faster if that is all that is required but currently we
|
||||
// ignore it.
|
||||
//
|
||||
// Returns true if the asset is successfully read into layer, false otherwise.
|
||||
USD_API
|
||||
bool Read(pxr::SdfLayer *layer, const std::string &resolved_path,
|
||||
bool metadata_only) const override;
|
||||
|
||||
// Reads data in the string 'str' into 'layer'.
|
||||
//
|
||||
// If the file is successfully read, this method returns true. Otherwise,
|
||||
// false is returned and errors are posted.
|
||||
USD_API
|
||||
bool ReadFromString(SdfLayer *layer, const std::string &str) const override;
|
||||
|
||||
// Writes the contents in 'layer' to 'str'. This just forwards to the usda
|
||||
// implementation.
|
||||
USD_API
|
||||
bool WriteToString(const SdfLayer &layer, std::string *str,
|
||||
const std::string &comment) const override;
|
||||
|
||||
protected:
|
||||
SDF_FILE_FORMAT_FACTORY_ACCESS;
|
||||
|
||||
UsdMjcfFileFormat();
|
||||
virtual ~UsdMjcfFileFormat();
|
||||
|
||||
private:
|
||||
// Function delegated to by Read and ReadFromString.
|
||||
bool ReadImpl(SdfLayer *layer, mjSpec *spec) const;
|
||||
};
|
||||
|
||||
PXR_NAMESPACE_CLOSE_SCOPE
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MJCF_FILE_FORMAT_H_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
// Copyright 2025 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_USD_PLUGINS_MJCF_MUJOCO_TO_USD_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MUJOCO_TO_USD_H_
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include <pxr/usd/sdf/abstractData.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
// Given an mjSpec, write it to a SdfAbstractData.
|
||||
//
|
||||
// Args:
|
||||
// spec: mjSpec built programmatically or via parsed XML.
|
||||
// data: SdfAbstractDataRefPtr that will be written to.
|
||||
bool WriteSpecToData(mjSpec* spec, pxr::SdfAbstractDataRefPtr& data);
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_MUJOCO_TO_USD_H_
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"Plugins": [
|
||||
{
|
||||
"Info": {
|
||||
"Types": {
|
||||
"UsdMjcfFileFormat": {
|
||||
"bases": ["SdfFileFormat"],
|
||||
"displayName": "MJCF USD File Format",
|
||||
"extensions": ["xml"],
|
||||
"formatId": "xml",
|
||||
"primary": true,
|
||||
"supportsReading": true,
|
||||
"supportsWriting": false,
|
||||
"target": "usd"
|
||||
}
|
||||
}
|
||||
},
|
||||
"LibraryPath": "",
|
||||
"Name": "usdMjcf",
|
||||
"ResourcePath": "",
|
||||
"Root": ".",
|
||||
"Type": "library"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright 2025 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 "mjcf/utils.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <pxr/base/tf/token.h>
|
||||
#include <pxr/usd/sdf/abstractData.h>
|
||||
#include <pxr/usd/sdf/listOp.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/reference.h>
|
||||
#include <pxr/usd/sdf/schema.h>
|
||||
#include <pxr/usd/sdf/types.h>
|
||||
#include <pxr/usd/sdf/valueTypeName.h>
|
||||
#include <pxr/usd/usd/tokens.h>
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
void AppendChild(pxr::SdfAbstractDataRefPtr& data, const pxr::SdfPath& specPath,
|
||||
const pxr::TfToken& childKey, const T& child) {
|
||||
// Get existing children.
|
||||
std::vector<T> children;
|
||||
pxr::SdfAbstractDataTypedValue<std::vector<T>> getter(&children);
|
||||
data->Has(specPath, childKey, &getter);
|
||||
|
||||
children.push_back(child);
|
||||
data->Set(specPath, childKey,
|
||||
pxr::SdfAbstractDataConstTypedValue<std::vector<T>>(&children));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AppendListOp(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& spec_path, const pxr::TfToken& field,
|
||||
const T& item) {
|
||||
// Get existing list op.
|
||||
pxr::SdfListOp<T> list_op;
|
||||
pxr::SdfAbstractDataTypedValue<pxr::SdfListOp<T>> getter(&list_op);
|
||||
data->Has(spec_path, field, &getter);
|
||||
|
||||
auto items = list_op.GetExplicitItems();
|
||||
items.push_back(item);
|
||||
list_op.SetExplicitItems(items);
|
||||
data->Set(spec_path, field,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::SdfListOp<T>>(&list_op));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void PrependListOp(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& spec_path, const pxr::TfToken& field,
|
||||
const T& item) {
|
||||
// Get existing list op.
|
||||
pxr::SdfListOp<T> listOp;
|
||||
pxr::SdfAbstractDataTypedValue<pxr::SdfListOp<T>> getter(&listOp);
|
||||
data->Has(spec_path, field, &getter);
|
||||
|
||||
auto prependedItems = listOp.GetPrependedItems();
|
||||
prependedItems.insert(prependedItems.begin(), item);
|
||||
listOp.SetPrependedItems(prependedItems);
|
||||
data->Set(spec_path, field,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::SdfListOp<T>>(&listOp));
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
|
||||
pxr::SdfPath CreatePrimSpec(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& parent_path,
|
||||
const pxr::TfToken& name, const pxr::TfToken& type,
|
||||
pxr::SdfSpecifier specifier) {
|
||||
const pxr::SdfPath prim_path = parent_path.AppendChild(name);
|
||||
data->CreateSpec(prim_path, pxr::SdfSpecTypePrim);
|
||||
data->Set(prim_path, pxr::SdfFieldKeys->Specifier,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::SdfSpecifier>(&specifier));
|
||||
if (!type.IsEmpty()) {
|
||||
data->Set(prim_path, pxr::SdfFieldKeys->TypeName,
|
||||
pxr::SdfAbstractDataConstTypedValue<const pxr::TfToken>(&type));
|
||||
}
|
||||
|
||||
AppendChild(data, parent_path, pxr::SdfChildrenKeys->PrimChildren, name);
|
||||
|
||||
return prim_path;
|
||||
}
|
||||
|
||||
pxr::SdfPath CreateAttributeSpec(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& name,
|
||||
const pxr::SdfValueTypeName& type_name,
|
||||
pxr::SdfVariability variability) {
|
||||
const pxr::SdfPath propertyPath = prim_path.AppendProperty(name);
|
||||
data->CreateSpec(propertyPath, pxr::SdfSpecTypeAttribute);
|
||||
|
||||
pxr::TfToken typeNameToken = type_name.GetAsToken();
|
||||
data->Set(propertyPath, pxr::SdfFieldKeys->TypeName,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::TfToken>(&typeNameToken));
|
||||
if (variability != pxr::SdfVariabilityVarying) {
|
||||
data->Set(
|
||||
propertyPath, pxr::SdfFieldKeys->Variability,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::SdfVariability>(&variability));
|
||||
}
|
||||
|
||||
AppendChild(data, prim_path, pxr::SdfChildrenKeys->PropertyChildren, name);
|
||||
|
||||
return propertyPath;
|
||||
}
|
||||
|
||||
pxr::SdfPath CreateRelationshipSpec(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& relationship_name,
|
||||
const pxr::SdfPath& relationship_path,
|
||||
pxr::SdfVariability variability) {
|
||||
pxr::SdfPath prop_path = prim_path.AppendProperty(relationship_name);
|
||||
data->CreateSpec(prop_path, pxr::SdfSpecTypeRelationship);
|
||||
if (variability != pxr::SdfVariabilityVarying) {
|
||||
data->Set(
|
||||
prop_path, pxr::SdfFieldKeys->Variability,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::SdfVariability>(&variability));
|
||||
}
|
||||
|
||||
AppendChild(data, prim_path, pxr::SdfChildrenKeys->PropertyChildren,
|
||||
relationship_name);
|
||||
|
||||
AppendChild(data, prop_path, pxr::SdfChildrenKeys->RelationshipTargetChildren,
|
||||
relationship_path);
|
||||
AppendListOp(data, prop_path, pxr::SdfFieldKeys->TargetPaths,
|
||||
relationship_path);
|
||||
|
||||
pxr::SdfPath target_path = prop_path.AppendTarget(relationship_path);
|
||||
data->CreateSpec(target_path, pxr::SdfSpecTypeRelationshipTarget);
|
||||
|
||||
return prop_path;
|
||||
}
|
||||
|
||||
pxr::SdfPath CreateClassSpec(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& class_name) {
|
||||
pxr::SdfPath class_path = prim_path.AppendChild(class_name);
|
||||
pxr::SdfSpecifier class_specifier = pxr::SdfSpecifier::SdfSpecifierClass;
|
||||
data->CreateSpec(class_path, pxr::SdfSpecTypePrim);
|
||||
data->Set(
|
||||
class_path, pxr::SdfFieldKeys->Specifier,
|
||||
pxr::SdfAbstractDataConstTypedValue<pxr::SdfSpecifier>(&class_specifier));
|
||||
|
||||
AppendChild(data, prim_path, pxr::SdfChildrenKeys->PrimChildren, class_name);
|
||||
|
||||
return class_path;
|
||||
}
|
||||
|
||||
void AddAttributeConnection(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& attribute_path,
|
||||
const pxr::SdfPath& target_attribute_path) {
|
||||
AppendChild(data, attribute_path, pxr::SdfChildrenKeys->ConnectionChildren,
|
||||
target_attribute_path);
|
||||
AppendListOp(data, attribute_path, pxr::SdfFieldKeys->ConnectionPaths,
|
||||
target_attribute_path);
|
||||
|
||||
data->CreateSpec(attribute_path.AppendTarget(target_attribute_path),
|
||||
pxr::SdfSpecTypeConnection);
|
||||
}
|
||||
|
||||
void AddPrimReference(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::SdfPath& referenced_prim_path) {
|
||||
PrependListOp(data, prim_path, pxr::SdfFieldKeys->References,
|
||||
pxr::SdfReference("", referenced_prim_path));
|
||||
}
|
||||
|
||||
void AddPrimInherit(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::SdfPath& class_path) {
|
||||
PrependListOp(data, prim_path, pxr::SdfFieldKeys->InheritPaths, class_path);
|
||||
}
|
||||
|
||||
void ApplyApiSchema(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& schema_name) {
|
||||
PrependListOp(data, prim_path, pxr::UsdTokens->apiSchemas, schema_name);
|
||||
}
|
||||
|
||||
void SetPrimKind(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path, pxr::TfToken kind) {
|
||||
SetPrimMetadata(data, prim_path, pxr::TfToken("kind"), kind);
|
||||
}
|
||||
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
@@ -0,0 +1,127 @@
|
||||
// Copyright 2025 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_USD_PLUGINS_MJCF_UTILS_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_UTILS_H_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <pxr/base/tf/token.h>
|
||||
#include <pxr/imaging/hd/primTypeIndex.h>
|
||||
#include <pxr/usd/sdf/abstractData.h>
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/sdf/schema.h>
|
||||
#include <pxr/usd/sdf/types.h>
|
||||
#include <pxr/usd/sdf/valueTypeName.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
|
||||
// Create a prim spec and append it as a child of parent_path.
|
||||
pxr::SdfPath CreatePrimSpec(
|
||||
pxr::SdfAbstractDataRefPtr& data, const pxr::SdfPath& parent_path,
|
||||
const pxr::TfToken& name, const pxr::TfToken& type = pxr::TfToken(),
|
||||
pxr::SdfSpecifier specifier = pxr::SdfSpecifier::SdfSpecifierDef);
|
||||
|
||||
// Create an attribute spec and append it as a child of parent_path.
|
||||
// By default the attribute will be varying.
|
||||
pxr::SdfPath CreateAttributeSpec(
|
||||
pxr::SdfAbstractDataRefPtr& data, const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& name, const pxr::SdfValueTypeName& type_name,
|
||||
pxr::SdfVariability variability = pxr::SdfVariabilityVarying);
|
||||
|
||||
// Create a relationship spec and append it as a child of prim_path.
|
||||
pxr::SdfPath CreateRelationshipSpec(
|
||||
pxr::SdfAbstractDataRefPtr& data, const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& relationship_name,
|
||||
const pxr::SdfPath& relationship_path,
|
||||
pxr::SdfVariability variability = pxr::SdfVariabilityVarying);
|
||||
|
||||
pxr::SdfPath CreateClassSpec(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& class_name);
|
||||
|
||||
void AddAttributeConnection(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& attribute_path,
|
||||
const pxr::SdfPath& target_attribute_path);
|
||||
|
||||
void AddPrimReference(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::SdfPath& referenced_prim_path);
|
||||
|
||||
void AddPrimInherit(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::SdfPath& class_path);
|
||||
|
||||
void ApplyApiSchema(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path,
|
||||
const pxr::TfToken& schema_name);
|
||||
|
||||
void SetPrimKind(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path, pxr::TfToken kind);
|
||||
|
||||
// Set the value specified by key on any field at field_path.
|
||||
template <typename T>
|
||||
void SetField(pxr::SdfAbstractDataRefPtr& data, const pxr::SdfPath& field_path,
|
||||
const pxr::TfToken key, T&& value) {
|
||||
using Deduced = typename std::remove_reference_t<T>;
|
||||
const auto typed_val = pxr::SdfAbstractDataConstTypedValue<Deduced>(&value);
|
||||
const pxr::SdfAbstractDataConstValue& untyped_val = typed_val;
|
||||
|
||||
data->Set(field_path, key, untyped_val);
|
||||
}
|
||||
|
||||
// Set the value specified by key on an attribute spec at attribute_path.
|
||||
template <typename T>
|
||||
void SetAttribute(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& attribute_path, const pxr::TfToken key,
|
||||
T&& value) {
|
||||
SetField(data, attribute_path, key, value);
|
||||
}
|
||||
|
||||
// Set the value specified by key on a prim spec at prim_path.
|
||||
template <typename T>
|
||||
void SetPrimMetadata(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& prim_path, const pxr::TfToken key,
|
||||
T&& value) {
|
||||
SetAttribute(data, prim_path, key, value);
|
||||
}
|
||||
|
||||
// Set the value specified by key on an attribute spec at attribute_path.
|
||||
template <typename T>
|
||||
void SetAttributeMetadata(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& attribute_path,
|
||||
const pxr::TfToken key, T&& value) {
|
||||
SetAttribute(data, attribute_path, key, value);
|
||||
}
|
||||
|
||||
// Set the default value on an attribute spec at attribute_path.
|
||||
template <typename T>
|
||||
void SetAttributeDefault(pxr::SdfAbstractDataRefPtr& data,
|
||||
const pxr::SdfPath& attribute_path,
|
||||
T&& default_value) {
|
||||
SetAttribute(data, attribute_path, pxr::SdfFieldKeys->Default, default_value);
|
||||
}
|
||||
|
||||
// Set the value specified by key on the root layer.
|
||||
template <typename T>
|
||||
void SetLayerMetadata(pxr::SdfAbstractDataRefPtr& data, const pxr::TfToken& key,
|
||||
T&& value) {
|
||||
SetAttribute(data, pxr::SdfPath::AbsoluteRootPath(), key, value);
|
||||
}
|
||||
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_USD_PLUGINS_MJCF_UTILS_H_
|
||||
Reference in New Issue
Block a user