Move OpenUSD parsing to usd_decoder
Add usd_decoder to mujoco/plugin. - Previously was src/experimental/usd/usd_to_mjspec.cc, now that same spec is returned by the plugin. PiperOrigin-RevId: 854350962 Change-Id: Ia980190a557c50ce8197980922a2594b613859b4
This commit is contained in:
committed by
Copybara-Service
parent
64a2345c07
commit
a5dc57c0c3
@@ -243,6 +243,7 @@ if(MUJOCO_WITH_USD)
|
||||
endif()
|
||||
|
||||
add_subdirectory(src/experimental/usd)
|
||||
add_subdirectory(plugin/usd_decoder)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING AND MUJOCO_BUILD_TESTS)
|
||||
|
||||
@@ -13,42 +13,39 @@
|
||||
# limitations under the License.
|
||||
cmake_minimum_required(VERSION "3.16")
|
||||
|
||||
option(BUILD_USD OFF "Build OpenUSD")
|
||||
project(openusd-dependency)
|
||||
|
||||
if (BUILD_USD)
|
||||
include(ExternalProject)
|
||||
include(ExternalProject)
|
||||
|
||||
set(DEPS_DIR ${CMAKE_BINARY_DIR}/../../../../build/_deps)
|
||||
set(DEPS_DIR ${CMAKE_BINARY_DIR}/../../../../build/_deps)
|
||||
|
||||
ExternalProject_Add(openusd
|
||||
GIT_REPOSITORY https://github.com/PixarAnimationStudios/USD.git
|
||||
GIT_TAG v25.11
|
||||
PREFIX openusd
|
||||
BUILD_IN_SOURCE 0
|
||||
UPDATE_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
SOURCE_DIR ${DEPS_DIR}/openusd-src
|
||||
BINARY_DIR ${DEPS_DIR}/openusd-build
|
||||
INSTALL_DIR ${DEPS_DIR}/openusd-build
|
||||
BUILD_COMMAND python3 ${DEPS_DIR}/openusd-src/build_scripts/build_usd.py
|
||||
--build-shared
|
||||
--no-examples
|
||||
--no-tools
|
||||
--no-ptex
|
||||
--no-prman
|
||||
--no-openimageio
|
||||
--no-opencolorio
|
||||
--no-alembic
|
||||
--no-draco
|
||||
--no-materialx
|
||||
--no-tutorials
|
||||
--no-tests
|
||||
--no-docs
|
||||
--no-imaging
|
||||
--no-python
|
||||
--no-usdValidation
|
||||
<INSTALL_DIR>
|
||||
)
|
||||
endif()
|
||||
ExternalProject_Add(openusd
|
||||
GIT_REPOSITORY https://github.com/PixarAnimationStudios/USD.git
|
||||
GIT_TAG v25.11
|
||||
PREFIX openusd
|
||||
BUILD_IN_SOURCE 0
|
||||
UPDATE_COMMAND ""
|
||||
CONFIGURE_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
SOURCE_DIR ${DEPS_DIR}/openusd-src
|
||||
BINARY_DIR ${DEPS_DIR}/openusd-build
|
||||
INSTALL_DIR ${DEPS_DIR}/openusd-build
|
||||
BUILD_COMMAND python3 ${DEPS_DIR}/openusd-src/build_scripts/build_usd.py
|
||||
--build-shared
|
||||
--no-examples
|
||||
--no-tools
|
||||
--no-ptex
|
||||
--no-prman
|
||||
--no-openimageio
|
||||
--no-opencolorio
|
||||
--no-alembic
|
||||
--no-draco
|
||||
--no-materialx
|
||||
--no-tutorials
|
||||
--no-tests
|
||||
--no-docs
|
||||
--no-imaging
|
||||
--no-python
|
||||
--no-usdValidation
|
||||
<INSTALL_DIR>
|
||||
)
|
||||
|
||||
+35
-29
@@ -3,55 +3,61 @@ Building
|
||||
|
||||
.. WARNING:: OpenUSD support is currently experimental and subject to frequent change.
|
||||
|
||||
Advanced users can start testing out USD support by building against their own USD libraries or USD built from source.
|
||||
MuJoCo must be built against a pre-built USD library, we provide a utility to do so but you may also bring your own USD
|
||||
libraries.
|
||||
|
||||
This assumes that you have built MuJoCo in ``~/mujoco`` and have a build directory at ``~/mujoco/build``
|
||||
The following instructions assume that you have cloned MuJoCo into ``~/mujoco`` and have a build directory at
|
||||
``~/mujoco/build``.
|
||||
|
||||
.. _usdBuildingUSD:
|
||||
|
||||
Building USD
|
||||
------------
|
||||
|
||||
USD has a pretty streamlined installation via their ``build_usd.py`` script. It's recommended to use a separate
|
||||
installation directory that exists outside of the cloned repository directory.
|
||||
If you have a pre-built USD library, you can skip this section.
|
||||
|
||||
MuJoCo provides a CMake project that simplifies the process of building USD. It will download and build USD with only
|
||||
the necessary features enabled.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd ~/mujoco
|
||||
cmake -Bcmake/third_party_deps/openusd/build cmake/third_party_deps/openusd
|
||||
cmake --build cmake/third_party_deps/openusd/build
|
||||
|
||||
If you want to customize the build process, you can use USD's ``build_usd.py`` script. It's recommended to use a
|
||||
separate installation directory that exists outside of the cloned repository directory.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
git clone https://github.com/PixarAnimationStudios/OpenUSD
|
||||
python OpenUSD/build_scripts/build_usd.py /path/to/my_usd_install_dir
|
||||
|
||||
.. _usdEnablingUSD:
|
||||
|
||||
Enabling USD
|
||||
------------
|
||||
|
||||
USD is comprised of many plugins. When USD enabled application starts up it looks for an environment variable called
|
||||
``PXR_PLUGINPATH_NAME``. Below is an example where we build MuJoCo with USD enabled and set this variable.
|
||||
If USD was built with the third_party_deps/openusd CMake project, you can enable USD support with the MUJOCO_WITH_USD
|
||||
flag.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd ~/mujoco/build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DUSD_DIR=/path/to/my_usd_install_dir
|
||||
cmake --build . -j 30; sudo cmake --install .
|
||||
export PXR_PLUGINPATH_NAME=/usr/local/lib/mujocoUsd/resources/*/plugInfo.json
|
||||
cd ~/mujoco
|
||||
cmake -Bbuild -S. -DMUJOCO_WITH_USD=True
|
||||
cmake --build build -j 64
|
||||
|
||||
Otherwise, if you have a pre-built USD library, you must also pass the pxr_DIR flag.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd ~/mujoco
|
||||
cmake -Bbuild -S. -DMUJOCO_WITH_USD=True -Dpxr_DIR=/path/to/my_usd_install_dir
|
||||
cmake --build build -j 64
|
||||
|
||||
|
||||
If we now run :ref:`simulate.cc <saSimulate>`, we will be able to drag and drop USD files.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
simulate
|
||||
|
||||
Enabling plugins in Houdini
|
||||
---------------------------
|
||||
|
||||
Houdini is a procedural content authoring tool with extensive support for USD workflows via their Solaris context. It's
|
||||
highly popular in the VFX industry, and it's easy to imagine procedural generation tools for simulation ready assets and
|
||||
scenes.
|
||||
|
||||
To allow support for loading MJCF files in Solaris, and usage of the mjcPhysics schemas you can build against Houdini's
|
||||
USD libraries. To do so, simply run `source ./houdini_setup` as descrived in the `SideFX documentation
|
||||
<https://www.sidefx.com/faq/question/how-do-i-set-up-the-houdini-environment-for-command-line-tools>`__.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
cd ~/mujoco/build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release -DHOUDINI_HFS_DIR=$HFS
|
||||
cmake --build . -j 30; sudo cmake --install .
|
||||
export PXR_PLUGINPATH_NAME=/usr/local/lib/mujocoUsd/resources/*/plugInfo.json
|
||||
houdini
|
||||
|
||||
@@ -39,6 +39,13 @@ General
|
||||
a silent implicit cast, now negativity will trigger an error.
|
||||
- Added a :ref:`depth<mjtRndFlag>` rendering flag.
|
||||
|
||||
- :doc:`OpenUSD <OpenUSD/index>`:
|
||||
|
||||
- Parsing has been moved out of experimental into a mjpDecoder plugin. (documentation pending)
|
||||
- OpenUSD can now be built with the `third_party_deps/openusd` <https://github.com/google-deepmind/mujoco/tree/main/cmake/third_party_deps/openusd> CMake utility project.
|
||||
- ``USD_DIR`` is no longer used by the MuJoCo CMake project, instead use ``pxr_DIR`` if you have a pre-built USD library.
|
||||
- Users no longer have to set ``PXR_PLUGINPATH_NAME`` environment variable, MuJoCo should load USD plugins automatically.
|
||||
|
||||
MJX
|
||||
^^^
|
||||
- Added ``actuator_length``, ``cdof`` and ``cdof_dof`` fields to ``mjx.Data``.
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// 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_EXPERIMENTAL_SRC_USD_USD_TO_MJSPEC_H_
|
||||
#define MUJOCO_EXPERIMENTAL_SRC_USD_USD_TO_MJSPEC_H_
|
||||
|
||||
#include <mujoco/mujoco.h>
|
||||
#include <pxr/usd/usd/common.h>
|
||||
|
||||
// Given a USD file parse to a spec, then compile and return the low-level
|
||||
// model.
|
||||
//
|
||||
// Particular care is taken for physics data to be lossless but visual
|
||||
// data such as materials may be lossy.
|
||||
MJAPI mjModel* mj_loadUSD(const char* filename, const mjVFS* vfs, char* error,
|
||||
int error_sz);
|
||||
|
||||
// Given a USD layer identifier, this function will do a best effort conversion
|
||||
// from the composed stage to mjSpec.
|
||||
//
|
||||
// Particular care is taken for physics data to be lossless but visual
|
||||
// data such as materials may be lossy.
|
||||
MJAPI mjSpec* mj_parseUSD(const char* identifier, const mjVFS* vfs, char* error,
|
||||
int error_sz);
|
||||
|
||||
// Given a USD stage, this function will do a best effort conversion to
|
||||
// mjSpec.
|
||||
//
|
||||
// Particular care is taken for physics data to be lossless but visual
|
||||
// data such as materials may be lossy.
|
||||
MJAPI mjSpec* mj_parseUSDStage(pxr::UsdStageRefPtr stage);
|
||||
|
||||
#endif // MUJOCO_EXPERIMENTAL_SRC_USD_USD_TO_MJSPEC_H_
|
||||
@@ -0,0 +1,53 @@
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
find_package(pxr REQUIRED)
|
||||
|
||||
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set(USD_DECODER_SRCS
|
||||
usd_decoder.cc
|
||||
kinematic_tree.cc
|
||||
kinematic_tree.h
|
||||
material_parsing.cc
|
||||
material_parsing.h
|
||||
utils.h
|
||||
)
|
||||
|
||||
add_library(usd_decoder_plugin SHARED ${USD_DECODER_SRCS})
|
||||
target_include_directories(usd_decoder_plugin PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../..
|
||||
)
|
||||
target_link_libraries(usd_decoder_plugin PRIVATE
|
||||
mujoco
|
||||
mjcPhysics
|
||||
)
|
||||
target_compile_options(usd_decoder_plugin PRIVATE
|
||||
${AVX_COMPILE_OPTIONS}
|
||||
${MUJOCO_MACOS_COMPILE_OPTIONS}
|
||||
${EXTRA_COMPILE_OPTIONS}
|
||||
${MUJOCO_CXX_FLAGS}
|
||||
-Wno-deprecated # pxr Tf lib uses deprecated header
|
||||
)
|
||||
target_link_options(usd_decoder_plugin PRIVATE
|
||||
${MUJOCO_MACOS_LINK_OPTIONS}
|
||||
${EXTRA_LINK_OPTIONS}
|
||||
)
|
||||
|
||||
# Install to mujoco_plugin directory in bin location so that it is picked up by simulate
|
||||
# on startup.
|
||||
install(
|
||||
TARGETS usd_decoder_plugin
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_BINDIR}/mujoco_plugin"
|
||||
)
|
||||
|
||||
target_link_libraries(usd_decoder_plugin PRIVATE usd usdGeom usdPhysics usdShade gf tf ar vt kind)
|
||||
@@ -12,7 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "experimental/usd/kinematic_tree.h"
|
||||
#include "kinematic_tree.h"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -34,9 +34,6 @@
|
||||
#include <pxr/usd/usdPhysics/rigidBodyAPI.h>
|
||||
#include <pxr/usd/usdPhysics/scene.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
|
||||
bool GetJointBodies(const pxr::UsdPhysicsJoint& joint, pxr::SdfPath* from,
|
||||
pxr::SdfPath* to) {
|
||||
// Grab the default prim path
|
||||
@@ -274,6 +271,3 @@ std::unique_ptr<Node> BuildKinematicTree(const pxr::UsdStageRefPtr stage) {
|
||||
}
|
||||
return world_root;
|
||||
}
|
||||
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef MUJOCO_SRC_EXPERIMENTAL_USD_KINEMATIC_TREE_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_USD_KINEMATIC_TREE_H_
|
||||
#ifndef MUJOCO_PLUGIN_USD_DECODER_KINEMATIC_TREE_H_
|
||||
#define MUJOCO_PLUGIN_USD_DECODER_KINEMATIC_TREE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@@ -21,9 +21,6 @@
|
||||
#include <pxr/usd/sdf/path.h>
|
||||
#include <pxr/usd/usdPhysics/joint.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
|
||||
// A struct to represent a node in the kinematic tree.
|
||||
// Using a struct with a vector of children preserves the order of bodies,
|
||||
// which is important for things like keyframes and policy compatibility.
|
||||
@@ -51,7 +48,4 @@ using JointVec = std::vector<pxr::UsdPhysicsJoint>;
|
||||
// Returns the root of the kinematic tree, or `nullptr` for invalid structures.
|
||||
std::unique_ptr<Node> BuildKinematicTree(const pxr::UsdStageRefPtr stage);
|
||||
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_USD_KINEMATIC_TREE_H_
|
||||
#endif // MUJOCO_PLUGIN_USD_DECODER_KINEMATIC_TREE_H_
|
||||
@@ -12,17 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "experimental/usd/material_parsing.h"
|
||||
#include "material_parsing.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lodepng.h"
|
||||
#include <mujoco/mujoco.h>
|
||||
#include <pxr/base/gf/vec3f.h>
|
||||
#include <pxr/base/gf/vec4f.h>
|
||||
@@ -39,9 +34,6 @@
|
||||
#include <pxr/usd/usdShade/types.h>
|
||||
#include <pxr/usd/usdShade/udimUtils.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
|
||||
// Using to satisfy TF_DEFINE_PRIVATE_TOKENS macro below and avoid operating in
|
||||
// PXR_NS.
|
||||
using pxr::TfToken;
|
||||
@@ -146,53 +138,16 @@ ResolvedShaderInput<T> ReadUsdUVTexture(mjSpec* spec,
|
||||
}
|
||||
|
||||
auto extension = resolver.GetExtension(resolved_texture_path);
|
||||
if (extension != "png") {
|
||||
mju_error("MuJoCo USD Parsing only supports PNG textures: %s",
|
||||
resolved_texture_path.c_str());
|
||||
return out;
|
||||
}
|
||||
|
||||
FILE* fp = fopen(resolved_texture_path.c_str(), "r");
|
||||
if (fp) {
|
||||
mjs_setString(texture->content_type, "image/png");
|
||||
mjs_setString(texture->file, resolved_texture_path.c_str());
|
||||
out.sampler = texture;
|
||||
if (fp == nullptr) {
|
||||
mju_error(
|
||||
"USD decoder only supports assets that are available on the file "
|
||||
"system");
|
||||
return out;
|
||||
}
|
||||
|
||||
std::shared_ptr<pxr::ArAsset> texture_asset =
|
||||
resolver.OpenAsset(pxr::ArResolvedPath(resolved_texture_path));
|
||||
|
||||
size_t texture_size = texture_asset->GetSize();
|
||||
|
||||
uint32_t width, height;
|
||||
std::vector<unsigned char> image;
|
||||
lodepng::State state;
|
||||
if (nchannels == 3) {
|
||||
state.info_raw.colortype = LCT_RGB;
|
||||
} else if (nchannels == 1) {
|
||||
state.info_raw.colortype = LCT_GREY;
|
||||
} else {
|
||||
mju_error("MuJoCo USD Parsing only supports 1 or 3 channel textures.");
|
||||
return out;
|
||||
}
|
||||
|
||||
unsigned error = lodepng::decode(
|
||||
image, width, height, state,
|
||||
reinterpret_cast<const unsigned char*>(texture_asset->GetBuffer().get()),
|
||||
texture_size);
|
||||
|
||||
// check for errors
|
||||
if (error) {
|
||||
mju_error("LodePNG error %u: %s", error, lodepng_error_text(error));
|
||||
return out;
|
||||
}
|
||||
|
||||
texture->width = width;
|
||||
texture->height = height;
|
||||
texture->nchannel = nchannels;
|
||||
mjs_setBuffer(texture->data, image.data(), image.size());
|
||||
|
||||
mjs_setString(texture->file, resolved_texture_path.c_str());
|
||||
out.sampler = texture;
|
||||
return out;
|
||||
}
|
||||
@@ -311,5 +266,3 @@ mjsMaterial* ParseMaterial(mjSpec* spec,
|
||||
|
||||
return mj_mat;
|
||||
}
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
@@ -12,16 +12,12 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef MUJOCO_SRC_EXPERIMENTAL_USD_MATERIAL_PARSING_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_USD_MATERIAL_PARSING_H_
|
||||
#ifndef MUJOCO_PLUGIN_USD_DECODER_MATERIAL_PARSING_H_
|
||||
#define MUJOCO_PLUGIN_USD_DECODER_MATERIAL_PARSING_H_
|
||||
|
||||
#include <pxr/usd/usdShade/material.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
mjsMaterial* ParseMaterial(mjSpec* spec, const pxr::UsdShadeMaterial &material);
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_USD_MATERIAL_PARSING_H_
|
||||
#endif // MUJOCO_PLUGIN_USD_DECODER_MATERIAL_PARSING_H_
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <numbers>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <mujoco/experimental/usd/mjcPhysics/actuator.h>
|
||||
@@ -33,11 +34,9 @@
|
||||
#include <mujoco/experimental/usd/mjcPhysics/siteAPI.h>
|
||||
#include <mujoco/experimental/usd/mjcPhysics/tendon.h>
|
||||
#include <mujoco/experimental/usd/mjcPhysics/tokens.h>
|
||||
#include <mujoco/experimental/usd/usd.h>
|
||||
#include <mujoco/experimental/usd/utils.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/usd/kinematic_tree.h"
|
||||
#include "experimental/usd/material_parsing.h"
|
||||
#include "kinematic_tree.h"
|
||||
#include "material_parsing.h"
|
||||
#include <pxr/base/gf/declare.h>
|
||||
#include <pxr/base/gf/matrix4d.h>
|
||||
#include <pxr/base/gf/rotation.h>
|
||||
@@ -74,9 +73,6 @@
|
||||
#include <pxr/usd/usdShade/material.h>
|
||||
#include <pxr/usd/usdShade/materialBindingAPI.h>
|
||||
|
||||
namespace mujoco {
|
||||
namespace usd {
|
||||
|
||||
using pxr::MjcPhysicsTokens;
|
||||
using pxr::TfToken;
|
||||
|
||||
@@ -87,6 +83,26 @@ struct UsdCaches {
|
||||
std::map<pxr::SdfPath, mjsMaterial*> parsed_materials;
|
||||
};
|
||||
|
||||
constexpr const char* kUsdPrimPathKey = "usd_primpath";
|
||||
|
||||
void SetUsdPrimPathUserValue(mjsElement* element,
|
||||
const pxr::SdfPath& prim_path) {
|
||||
// The value is a pointer to a newly allocated SdfPath, which will be deleted
|
||||
// when the mjsElement is deleted.
|
||||
const pxr::SdfPath* usd_primpath = new pxr::SdfPath(prim_path);
|
||||
mjs_setUserValueWithCleanup(
|
||||
element, kUsdPrimPathKey, usd_primpath,
|
||||
[](const void* data) { delete static_cast<const pxr::SdfPath*>(data); });
|
||||
}
|
||||
|
||||
pxr::SdfPath GetUsdPrimPathUserValue(mjsElement* element) {
|
||||
const void* user_data = mjs_getUserValue(element, kUsdPrimPathKey);
|
||||
if (user_data) {
|
||||
return *static_cast<const pxr::SdfPath*>(user_data);
|
||||
}
|
||||
return pxr::SdfPath();
|
||||
}
|
||||
|
||||
void SetDoubleArrFromGfVec3d(double* to, const pxr::GfVec3d& from) {
|
||||
to[0] = from[0];
|
||||
to[1] = from[1];
|
||||
@@ -279,7 +295,7 @@ mjsMesh* ParseUsdMesh(mjSpec* spec, const pxr::UsdPrim& prim, mjsGeom* geom,
|
||||
}
|
||||
mjsMesh* mesh = mjs_addMesh(spec, nullptr);
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(mesh->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(mesh->element, prim.GetPath());
|
||||
|
||||
geom->type = mjGEOM_MESH;
|
||||
pxr::UsdGeomMesh usd_mesh(prim);
|
||||
@@ -924,14 +940,13 @@ void ParseMjcPhysicsMeshCollisionAPI(
|
||||
}
|
||||
}
|
||||
|
||||
void ParseMjcPhysicsTendon(mjSpec* spec,
|
||||
const pxr::MjcPhysicsTendon& tendon) {
|
||||
void ParseMjcPhysicsTendon(mjSpec* spec, const pxr::MjcPhysicsTendon& tendon) {
|
||||
pxr::UsdPrim prim = tendon.GetPrim();
|
||||
pxr::UsdStageRefPtr stage = prim.GetStage();
|
||||
mjsTendon* mj_tendon = mjs_addTendon(spec, nullptr);
|
||||
mjs_setName(mj_tendon->element, prim.GetPath().GetAsString().c_str());
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(mj_tendon->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(mj_tendon->element, prim.GetPath());
|
||||
|
||||
pxr::TfToken type;
|
||||
tendon.GetTypeAttr().Get(&type);
|
||||
@@ -963,25 +978,31 @@ void ParseMjcPhysicsTendon(mjSpec* spec,
|
||||
wrap_targets.size());
|
||||
return;
|
||||
}
|
||||
// Check that if we have >1 segments that the user has specified how much each segment
|
||||
// contributes to the total segment length.
|
||||
// Check that if we have >1 segments that the user has specified how much
|
||||
// each segment contributes to the total segment length.
|
||||
if (!segments.empty() && divisors.empty()) {
|
||||
mju_warning(
|
||||
"Spatial tendon %s has >1 segments (%d) but does not specify divisors, skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), *std::max_element(segments.begin(), segments.end()) + 1);
|
||||
"Spatial tendon %s has >1 segments (%d) but does not specify "
|
||||
"divisors, skipping.",
|
||||
prim.GetPath().GetAsString().c_str(),
|
||||
*std::max_element(segments.begin(), segments.end()) + 1);
|
||||
return;
|
||||
}
|
||||
// Check that if we side site indices that we have N of them.
|
||||
if (!side_site_indices.empty() && side_site_indices.size() != wrap_targets.size()) {
|
||||
if (!side_site_indices.empty() &&
|
||||
side_site_indices.size() != wrap_targets.size()) {
|
||||
mju_warning(
|
||||
"Spatial tendon %s has %lu sideSite indices but %lu wrap targets, skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), side_site_indices.size(), wrap_targets.size());
|
||||
"Spatial tendon %s has %lu sideSite indices but %lu wrap targets, "
|
||||
"skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), side_site_indices.size(),
|
||||
wrap_targets.size());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!side_site_indices.empty() && side_site_paths.empty()) {
|
||||
mju_warning(
|
||||
"Spatial tendon %s has %lu sideSite indices but no side sites, skipping.",
|
||||
"Spatial tendon %s has %lu sideSite indices but no side sites, "
|
||||
"skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), side_site_indices.size());
|
||||
return;
|
||||
}
|
||||
@@ -990,7 +1011,8 @@ void ParseMjcPhysicsTendon(mjSpec* spec,
|
||||
if (!coefs.empty() && coefs.size() != wrap_targets.size()) {
|
||||
mju_warning(
|
||||
"Spatial tendon %s has %lu coefs but %lu wrap targets, skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), coefs.size(), wrap_targets.size());
|
||||
prim.GetPath().GetAsString().c_str(), coefs.size(),
|
||||
wrap_targets.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,21 +1020,25 @@ void ParseMjcPhysicsTendon(mjSpec* spec,
|
||||
for (int i = 0; i < wrap_targets.size(); ++i) {
|
||||
auto wrap_target = wrap_targets[i];
|
||||
auto wrap_prim = stage->GetPrimAtPath(wrap_target);
|
||||
// Important to check site before Imageable here because some Imageable prims are sites.
|
||||
// Important to check site before Imageable here because some Imageable
|
||||
// prims are sites.
|
||||
|
||||
if (!segments.empty()) {
|
||||
int segment = segments[i];
|
||||
if (segment >= divisors.size()) {
|
||||
mju_warning("Tendon %s has at least %d segments but only %lu divisors, skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), segment + 1, divisors.size());
|
||||
mju_warning(
|
||||
"Tendon %s has at least %d segments but only %lu divisors, "
|
||||
"skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), segment + 1, divisors.size());
|
||||
return;
|
||||
}
|
||||
|
||||
if (segment > last_segment) {
|
||||
mjsWrap* pulley_wrap = mjs_wrapPulley(mj_tendon, divisors[segment]);
|
||||
mjs_setString(pulley_wrap->info, ("Pulley between segments: " +
|
||||
std::to_string(last_segment) + " and " +
|
||||
std::to_string(segment)).c_str());
|
||||
std::to_string(last_segment) +
|
||||
" and " + std::to_string(segment))
|
||||
.c_str());
|
||||
}
|
||||
last_segment = segment;
|
||||
}
|
||||
@@ -1031,13 +1057,17 @@ void ParseMjcPhysicsTendon(mjSpec* spec,
|
||||
if (!side_site_indices.empty()) {
|
||||
int side_site_index = side_site_indices[i];
|
||||
if (side_site_index >= side_site_paths.size()) {
|
||||
mju_warning("Tendon %s has side site index %d but only %lu side sites, skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), side_site_index, side_site_paths.size());
|
||||
mju_warning(
|
||||
"Tendon %s has side site index %d but only %lu side sites, "
|
||||
"skipping.",
|
||||
prim.GetPath().GetAsString().c_str(), side_site_index,
|
||||
side_site_paths.size());
|
||||
return;
|
||||
}
|
||||
side_site_name = side_site_paths[side_site_index].GetAsString();
|
||||
}
|
||||
wrap = mjs_wrapGeom(mj_tendon, wrap_target.GetAsString().c_str(), side_site_name.c_str());
|
||||
wrap = mjs_wrapGeom(mj_tendon, wrap_target.GetAsString().c_str(),
|
||||
side_site_name.c_str());
|
||||
} else {
|
||||
mju_warning("Tendon %s has an invalid wrap target type, skipping.",
|
||||
prim.GetPath().GetAsString().c_str());
|
||||
@@ -1226,7 +1256,7 @@ void ParseMjcPhysicsActuator(mjSpec* spec,
|
||||
mjsActuator* mj_act = mjs_addActuator(spec, nullptr);
|
||||
mjs_setName(mj_act->element, prim.GetPath().GetAsString().c_str());
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(mj_act->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(mj_act->element, prim.GetPath());
|
||||
|
||||
auto group_attr = tran.GetGroupAttr();
|
||||
if (group_attr.HasAuthoredValue()) {
|
||||
@@ -1671,7 +1701,7 @@ void ParseUsdGeomGprim(mjSpec* spec, const pxr::UsdPrim& gprim,
|
||||
geom->contype = 0;
|
||||
geom->conaffinity = 0;
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(geom->element, gprim.GetPath());
|
||||
SetUsdPrimPathUserValue(geom->element, gprim.GetPath());
|
||||
|
||||
ParseDisplayColorAndOpacity(gprim, geom);
|
||||
SetLocalPoseFromPrim(gprim, body_prim, geom, caches.xform_cache);
|
||||
@@ -1728,7 +1758,7 @@ void ParseUsdPhysicsCollider(mjSpec* spec,
|
||||
geom->contype = 1;
|
||||
geom->conaffinity = 1;
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(geom->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(geom->element, prim.GetPath());
|
||||
|
||||
if (prim.HasAPI<pxr::MjcPhysicsCollisionAPI>()) {
|
||||
ParseMjcPhysicsCollisionAPI(geom, pxr::MjcPhysicsCollisionAPI(prim));
|
||||
@@ -1810,7 +1840,7 @@ void ParseUsdPhysicsJoint(mjSpec* spec, const pxr::UsdPrim& prim, mjsBody* body,
|
||||
mj_joint->type = type;
|
||||
mjs_setName(mj_joint->element, prim.GetPath().GetAsString().c_str());
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(mj_joint->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(mj_joint->element, prim.GetPath());
|
||||
|
||||
if (prim.IsA<pxr::UsdPhysicsRevoluteJoint>()) {
|
||||
pxr::UsdPhysicsRevoluteJoint revolute(prim);
|
||||
@@ -1901,7 +1931,7 @@ void ParseMjcPhysicsSite(mjSpec* spec, const pxr::MjcPhysicsSiteAPI& site_api,
|
||||
site_api.GetPrim().GetPath().GetAsString().c_str());
|
||||
SetLocalPoseFromPrim(site_api.GetPrim(), parent_prim, site, xform_cache);
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(site->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(site->element, prim.GetPath());
|
||||
|
||||
auto group_attr = site_api.GetGroupAttr();
|
||||
if (group_attr.HasAuthoredValue()) {
|
||||
@@ -1949,7 +1979,7 @@ void ParseMjcPhysicsKeyframe(mjSpec* spec,
|
||||
// If no time samples, we create a single keyframe.
|
||||
mjsKey* key = mjs_addKey(spec);
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(key->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(key->element, prim.GetPath());
|
||||
|
||||
mjs_setName(key->element, prim.GetName().GetString().c_str());
|
||||
setKeyframeData(key, qpos_attr, &key->qpos);
|
||||
@@ -1966,7 +1996,7 @@ void ParseMjcPhysicsKeyframe(mjSpec* spec,
|
||||
for (double time : times) {
|
||||
mjsKey* key = mjs_addKey(spec);
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(key->element, prim.GetPath());
|
||||
SetUsdPrimPathUserValue(key->element, prim.GetPath());
|
||||
|
||||
std::string key_name =
|
||||
prim.GetName().GetString() + "_" + std::to_string(keyframe_id++);
|
||||
@@ -1995,7 +2025,12 @@ mjsBody* ParseUsdPhysicsRigidbody(
|
||||
ParseUsdPhysicsMassAPIForBody(body, pxr::UsdPhysicsMassAPI(prim));
|
||||
}
|
||||
|
||||
mujoco::usd::SetUsdPrimPathUserValue(body->element, prim.GetPath());
|
||||
// The value is a pointer to a newly allocated SdfPath, which will be deleted
|
||||
// when the mjsElement is deleted.
|
||||
const pxr::SdfPath* usd_primpath = new pxr::SdfPath(prim.GetPath());
|
||||
mjs_setUserValueWithCleanup(
|
||||
body->element, kUsdPrimPathKey, usd_primpath,
|
||||
[](const void* data) { delete static_cast<const pxr::SdfPath*>(data); });
|
||||
|
||||
return body;
|
||||
}
|
||||
@@ -2080,49 +2115,45 @@ void PopulateSpecFromTree(pxr::UsdStageRefPtr stage, mjSpec* spec,
|
||||
child_node.get(), caches);
|
||||
}
|
||||
}
|
||||
} // namespace usd
|
||||
} // namespace mujoco
|
||||
|
||||
mjSpec* mj_parseUSDStage(const pxr::UsdStageRefPtr stage) {
|
||||
mjSpec* ParseStage(const pxr::UsdStageRefPtr stage) {
|
||||
mjSpec* spec = mj_makeSpec();
|
||||
|
||||
std::unique_ptr<mujoco::usd::Node> root =
|
||||
mujoco::usd::BuildKinematicTree(stage);
|
||||
std::unique_ptr<Node> root = BuildKinematicTree(stage);
|
||||
|
||||
// First parse the physics scene and other root elements such as keyframes
|
||||
// and actuators.
|
||||
if (!root->physics_scene.IsEmpty()) {
|
||||
mujoco::usd::ParseUsdPhysicsScene(
|
||||
spec, pxr::UsdPhysicsScene::Get(stage, root->physics_scene));
|
||||
ParseUsdPhysicsScene(spec,
|
||||
pxr::UsdPhysicsScene::Get(stage, root->physics_scene));
|
||||
} else {
|
||||
// If there is no physics scene we still need to infer the gravity vector
|
||||
// from the stage up axis and units per meter metadata.
|
||||
mujoco::usd::SetGravityAttributes(spec, stage);
|
||||
SetGravityAttributes(spec, stage);
|
||||
}
|
||||
|
||||
if (!root->keyframes.empty()) {
|
||||
for (const auto& keyframe : root->keyframes) {
|
||||
mujoco::usd::ParseMjcPhysicsKeyframe(
|
||||
spec, pxr::MjcPhysicsKeyframe::Get(stage, keyframe));
|
||||
ParseMjcPhysicsKeyframe(spec,
|
||||
pxr::MjcPhysicsKeyframe::Get(stage, keyframe));
|
||||
}
|
||||
}
|
||||
|
||||
if (!root->actuators.empty()) {
|
||||
for (const auto& actuator : root->actuators) {
|
||||
mujoco::usd::ParseMjcPhysicsActuator(
|
||||
spec, pxr::MjcPhysicsActuator::Get(stage, actuator));
|
||||
ParseMjcPhysicsActuator(spec,
|
||||
pxr::MjcPhysicsActuator::Get(stage, actuator));
|
||||
}
|
||||
}
|
||||
|
||||
if (!root->tendons.empty()) {
|
||||
for (const auto& tendon : root->tendons) {
|
||||
mujoco::usd::ParseMjcPhysicsTendon(
|
||||
spec, pxr::MjcPhysicsTendon::Get(stage, tendon));
|
||||
ParseMjcPhysicsTendon(spec, pxr::MjcPhysicsTendon::Get(stage, tendon));
|
||||
}
|
||||
}
|
||||
|
||||
// Set of caches to use for all queries when parsing.
|
||||
mujoco::usd::UsdCaches caches;
|
||||
UsdCaches caches;
|
||||
// Then populate the kinematic tree.
|
||||
PopulateSpecFromTree(stage, spec, /*parent_mj_body=*/nullptr,
|
||||
/*parent_node=*/nullptr, root.get(), caches);
|
||||
@@ -2130,8 +2161,27 @@ mjSpec* mj_parseUSDStage(const pxr::UsdStageRefPtr stage) {
|
||||
return spec;
|
||||
}
|
||||
|
||||
mjSpec* mj_parseUSD(const char* identifier, const mjVFS* vfs, char* error,
|
||||
int error_sz) {
|
||||
auto stage = pxr::UsdStage::Open(identifier);
|
||||
return mj_parseUSDStage(stage);
|
||||
namespace {
|
||||
// load 2D
|
||||
mjSpec* Decode(mjResource* resource, const mjVFS* vfs) {
|
||||
auto stage = pxr::UsdStage::Open(resource->name);
|
||||
return ParseStage(stage);
|
||||
}
|
||||
|
||||
int CanDecode(const mjResource* resource) {
|
||||
std::string_view name(resource->name);
|
||||
return name.ends_with(".usd") || name.ends_with(".usda") ||
|
||||
name.ends_with(".usdc") || name.ends_with(".usdz");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// clang-format off
|
||||
mjPLUGIN_LIB_INIT {
|
||||
mjpDecoder decoder;
|
||||
mjp_defaultDecoder(&decoder);
|
||||
decoder.content_type = "model/usd";
|
||||
decoder.extension = ".usd|.usda|.usdc|.usdz";
|
||||
decoder.decode = Decode;
|
||||
decoder.can_decode = CanDecode;
|
||||
mjp_registerDecoder(&decoder);
|
||||
}
|
||||
@@ -196,6 +196,7 @@ if(SIMULATE_BUILD_EXECUTABLE)
|
||||
target_link_libraries(
|
||||
simulate
|
||||
mujoco::usd::mjcf
|
||||
usd_decoder_plugin
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
+17
-21
@@ -25,9 +25,6 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#if defined(mjUSEUSD)
|
||||
#include <mujoco/experimental/usd/usd.h>
|
||||
#endif
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "glfw_adapter.h"
|
||||
#include "simulate.h"
|
||||
@@ -241,22 +238,26 @@ mjModel* LoadModel(const char* file, mj::Simulate& sim) {
|
||||
if (!mnew) {
|
||||
mju::strcpy_arr(loadError, "could not load binary model");
|
||||
}
|
||||
#if defined(mjUSEUSD)
|
||||
} else if (extension == ".usda" || extension == ".usd" ||
|
||||
extension == ".usdc" || extension == ".usdz" ) {
|
||||
mnew = mj_loadUSD(filename, nullptr, loadError, kErrorLength);
|
||||
#endif
|
||||
} else {
|
||||
} else if (extension == ".xml") {
|
||||
mnew = mj_loadXML(filename, nullptr, loadError, kErrorLength);
|
||||
|
||||
// remove trailing newline character from loadError
|
||||
if (loadError[0]) {
|
||||
int error_length = mju::strlen_arr(loadError);
|
||||
if (loadError[error_length-1] == '\n') {
|
||||
loadError[error_length-1] = '\0';
|
||||
}
|
||||
} else {
|
||||
mjSpec* spec = mj_parse(filename, nullptr, nullptr, loadError, kErrorLength);
|
||||
if (!spec) {
|
||||
mju::strcpy_arr(loadError, "could not parse model");
|
||||
} else {
|
||||
mnew = mj_compile(spec, nullptr);
|
||||
mj_deleteSpec(spec);
|
||||
}
|
||||
}
|
||||
|
||||
// remove trailing newline character from loadError
|
||||
if (loadError[0]) {
|
||||
int error_length = mju::strlen_arr(loadError);
|
||||
if (loadError[error_length-1] == '\n') {
|
||||
loadError[error_length-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
auto load_interval = mj::Simulate::Clock::now() - load_start;
|
||||
double load_seconds = Seconds(load_interval).count();
|
||||
|
||||
@@ -516,11 +517,6 @@ int main(int argc, char** argv) {
|
||||
// scan for libraries in the plugin directory to load additional plugins
|
||||
scanPluginLibraries();
|
||||
|
||||
#if defined(mjUSEUSD)
|
||||
// If USD is used, print the version.
|
||||
std::printf("OpenUSD version v%d.%02d\n", PXR_MINOR_VERSION, PXR_PATCH_VERSION);
|
||||
#endif
|
||||
|
||||
mjvCamera cam;
|
||||
mjv_defaultCamera(&cam);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# --- Global Configuration ---
|
||||
|
||||
# Plugin target names (used for library and plugInfo.json)
|
||||
set(MJCF_PLUGIN_TARGET_NAME usdMjcf)
|
||||
set(MJC_PHYSICS_PLUGIN_TARGET_NAME mjcPhysics)
|
||||
@@ -147,12 +148,7 @@ target_include_directories(${MJC_PHYSICS_PLUGIN_TARGET_NAME} PUBLIC
|
||||
## ----- mujoco USD utils -----
|
||||
|
||||
target_sources(mujoco PRIVATE
|
||||
kinematic_tree.cc
|
||||
kinematic_tree.h
|
||||
layer_sink.cc
|
||||
material_parsing.cc
|
||||
material_parsing.h
|
||||
usd_to_mjspec.cc
|
||||
utils.cc
|
||||
writer.cc
|
||||
)
|
||||
|
||||
@@ -31,12 +31,6 @@
|
||||
#include "xml/xml_global.h"
|
||||
#include "xml/xml_native_reader.h"
|
||||
#include "xml/xml_util.h"
|
||||
#if defined(mjUSEUSD)
|
||||
#include <mujoco/experimental/usd/usd.h>
|
||||
#include <pxr/usd/usd/stage.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//---------------------------------- Functions -----------------------------------------------------
|
||||
|
||||
@@ -75,36 +69,6 @@ mjModel* mj_loadXML(const char* filename, const mjVFS* vfs,
|
||||
return m;
|
||||
}
|
||||
|
||||
#if defined(mjUSEUSD)
|
||||
// parse USD file, compile it, and return low-level model.
|
||||
// if vfs is not NULL, look up files in vfs before reading from disk
|
||||
// error can be NULL; otherwise assumed to have size error_sz
|
||||
mjModel* mj_loadUSD(const char* filename, const mjVFS* vfs, char* error, int error_sz) {
|
||||
auto stage = pxr::UsdStage::Open(filename);
|
||||
|
||||
if (stage == nullptr) {
|
||||
mjCopyError(error, "Failed to load USD stage from file.", error_sz);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parse USD into mjSpec.
|
||||
std::unique_ptr<mjSpec, std::function<void(mjSpec*)> > spec(
|
||||
mj_parseUSDStage(stage),
|
||||
[](mjSpec* s) {
|
||||
mj_deleteSpec(s);
|
||||
});
|
||||
|
||||
// Compile new model.
|
||||
mjModel* m = mj_compile(spec.get(), vfs);
|
||||
if (!m) {
|
||||
mjCopyError(error, mjs_getError(spec.get()), error_sz);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetGlobalXmlSpec(spec.release());
|
||||
return m;
|
||||
}
|
||||
#endif
|
||||
|
||||
// update XML data structures with info from low-level model, save as MJCF
|
||||
// returns 1 if successful, 0 otherwise
|
||||
|
||||
@@ -45,9 +45,6 @@
|
||||
#include "xml/xml_base.h"
|
||||
#include "xml/xml_util.h"
|
||||
#include "tinyxml2.h"
|
||||
#ifdef mjUSEUSD
|
||||
#include <mujoco/experimental/usd/usd.h>
|
||||
#endif // mjUSEUSD
|
||||
|
||||
namespace {
|
||||
using std::string;
|
||||
@@ -3446,21 +3443,11 @@ void mjXReader::Asset(XMLElement* section, const mjVFS* vfs) {
|
||||
ReadAttrTxt(elem, "content_type", content_type);
|
||||
|
||||
// parse the child
|
||||
mjSpec* child = nullptr;
|
||||
std::array<char, 1024> error;
|
||||
auto filename = modelfiledir_ + ReadAttrFile(elem, "file", vfs).value();
|
||||
|
||||
#ifdef mjUSEUSD
|
||||
if (content_type == "text/usd") {
|
||||
child = mj_parseUSD(filename.c_str(), vfs, error.data(), error.size());
|
||||
} else {
|
||||
#endif // mjUSEUSD
|
||||
child = mj_parse(filename.c_str(), content_type.c_str(), vfs,
|
||||
mjSpec* child = mj_parse(filename.c_str(), content_type.c_str(), vfs,
|
||||
error.data(), error.size());
|
||||
#ifdef mjUSEUSD
|
||||
}
|
||||
#endif // mjUSEUSD
|
||||
|
||||
if (!child) {
|
||||
throw mjXError(elem, "could not parse model file with error: %s", error.data());
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <array>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <mujoco/experimental/usd/mjcPhysics/siteAPI.h>
|
||||
#include <mujoco/experimental/usd/usd.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "test/fixture.h"
|
||||
#include <pxr/base/tf/token.h>
|
||||
@@ -79,7 +79,9 @@ TEST_F(MjcSiteApiTest, TestApply) {
|
||||
auto box = pxr::UsdGeomCube::Define(stage, test_box_site_path);
|
||||
MjcPhysicsSiteAPI::Apply(box.GetPrim());
|
||||
|
||||
mjSpec* spec = mj_parseUSDStage(stage);
|
||||
std::array<char, 1024> error;
|
||||
mjSpec* spec = mj_parse(stage->GetRootLayer()->GetIdentifier().c_str(),
|
||||
"model/usd", nullptr, error.data(), error.size());
|
||||
mjModel* default_model = mj_compile(spec, nullptr);
|
||||
EXPECT_THAT(default_model, NotNull()) << mjs_getError(spec);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user