Expose ability to go from USD stage to MjSpec.
PiperOrigin-RevId: 776184388 Change-Id: I0325b347c9896183d148a196897e1e5d0c4bbde2
This commit is contained in:
committed by
Copybara-Service
parent
672746f509
commit
afc35451a0
+21
-9
@@ -42,14 +42,26 @@ option(MUJOCO_BUILD_EXAMPLES "Build samples for MuJoCo" ON)
|
|||||||
option(MUJOCO_BUILD_SIMULATE "Build simulate library for MuJoCo" ON)
|
option(MUJOCO_BUILD_SIMULATE "Build simulate library for MuJoCo" ON)
|
||||||
option(MUJOCO_BUILD_TESTS "Build tests for MuJoCo" ON)
|
option(MUJOCO_BUILD_TESTS "Build tests for MuJoCo" ON)
|
||||||
option(MUJOCO_TEST_PYTHON_UTIL "Build and test utility libraries for Python bindings" ON)
|
option(MUJOCO_TEST_PYTHON_UTIL "Build and test utility libraries for Python bindings" ON)
|
||||||
option(MUJOCO_BUILD_USD_PLUGINS "Build OpenUSD plugins" OFF)
|
option(MUJOCO_WITH_USD "Build with OpenUSD" OFF)
|
||||||
|
|
||||||
# USD libs to compile against.
|
# Option to provide a path to an existing USD build directory or to Houdini HFS directory.
|
||||||
set(MUJOCO_USD_ALLOWED_TARGET_VALUES "USD" "Houdini")
|
set(USD_DIR "" CACHE PATH "Path to an existing USD build directory.")
|
||||||
set(MUJOCO_USD_TARGET "USD" CACHE STRING "Select the USD target for the project.")
|
set(HOUDINI_HFS_DIR "" CACHE PATH "Path to Houdini HFS directory to build USD plugins against.")
|
||||||
set_property(CACHE MUJOCO_USD_TARGET
|
if(USD_DIR)
|
||||||
PROPERTY STRINGS ${MUJOCO_USD_ALLOWED_TARGET_VALUES}
|
if(EXISTS "${USD_DIR}")
|
||||||
)
|
# If the path is provided, set MUJOCO_WITH_USD to ON and add the path to CMAKE_PREFIX_PATH.
|
||||||
|
set(MUJOCO_WITH_USD ON CACHE BOOL "Build with OpenUSD" FORCE)
|
||||||
|
list(PREPEND CMAKE_PREFIX_PATH "${USD_DIR}")
|
||||||
|
message(STATUS "Using custom USD build directory: ${USD_DIR}")
|
||||||
|
else()
|
||||||
|
message(WARNING "Invalid path provided for USD_DIR: ${USD_DIR}")
|
||||||
|
endif()
|
||||||
|
elseif(HOUDINI_HFS_DIR)
|
||||||
|
if(EXISTS "${HOUDINI_HFS_DIR}")
|
||||||
|
set(MUJOCO_WITH_USD ON CACHE BOOL "Build with OpenUSD" FORCE)
|
||||||
|
message(STATUS "Building Mujoco USD against Houdini HFS: ${HOUDINI_HFS_DIR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
if(APPLE AND (MUJOCO_BUILD_EXAMPLES OR MUJOCO_BUILD_SIMULATE))
|
if(APPLE AND (MUJOCO_BUILD_EXAMPLES OR MUJOCO_BUILD_SIMULATE))
|
||||||
enable_language(OBJC)
|
enable_language(OBJC)
|
||||||
@@ -189,8 +201,8 @@ if(MUJOCO_BUILD_EXAMPLES)
|
|||||||
add_subdirectory(sample)
|
add_subdirectory(sample)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MUJOCO_BUILD_USD_PLUGINS)
|
if(MUJOCO_WITH_USD)
|
||||||
add_subdirectory(src/experimental/usd/plugins)
|
add_subdirectory(src/experimental/usd)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BUILD_TESTING AND MUJOCO_BUILD_TESTS)
|
if(BUILD_TESTING AND MUJOCO_BUILD_TESTS)
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
// 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 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,268 @@
|
|||||||
|
# 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
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# --- Global Configuration ---
|
||||||
|
# Plugin target names (used for library and plugInfo.json)
|
||||||
|
set(MJCF_PLUGIN_TARGET_NAME usdMjcf)
|
||||||
|
set(MJC_PHYSICS_PLUGIN_TARGET_NAME mjcPhysics)
|
||||||
|
set(MJ_USD_TARGET_NAME mj_usd)
|
||||||
|
|
||||||
|
# Installation directory for USD plugins
|
||||||
|
set(MJ_USD_INSTALL_DIR_LIB ${CMAKE_INSTALL_LIBDIR}/mujocoUsd)
|
||||||
|
|
||||||
|
# Common OpenUSD libraries
|
||||||
|
set(OPENUSD_CORE_LIBS
|
||||||
|
usd ar kind tf gf vt usdShade usdLux usdGeom usdImaging usdPhysics
|
||||||
|
)
|
||||||
|
|
||||||
|
# We need to set the visibility to default until core type symbol visibility
|
||||||
|
# is resolved in OpenUSD https://github.com/PixarAnimationStudios/OpenUSD/issues/1475
|
||||||
|
set(DEFAULT_CXX_VISIBILITY_PROPS
|
||||||
|
CXX_VISIBILITY_PRESET default
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- Helper Functions ---
|
||||||
|
function(configure_and_install_usd_plugin_info plugin_name source_subpath install_base_dir)
|
||||||
|
# Determine shared library prefix
|
||||||
|
if(CMAKE_SHARED_LIBRARY_PREFIX)
|
||||||
|
set(LIB_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX})
|
||||||
|
else()
|
||||||
|
set(LIB_PREFIX "")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(PLUG_INFO_LIBRARY_PATH "../../${LIB_PREFIX}${plugin_name}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||||
|
set(OUTPUT_DIR "${CMAKE_BINARY_DIR}/${install_base_dir}/resources/${plugin_name}")
|
||||||
|
set(OUTPUT_FILE "${OUTPUT_DIR}/plugInfo.json")
|
||||||
|
|
||||||
|
# Create the output directory if it doesn't exist
|
||||||
|
file(MAKE_DIRECTORY "${OUTPUT_DIR}")
|
||||||
|
|
||||||
|
# Configure the plugInfo.json file to replace @PLUG_INFO_LIBRARY_PATH@ instances.
|
||||||
|
configure_file(
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/${source_subpath}/plugInfo.json"
|
||||||
|
"${OUTPUT_FILE}"
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
install(FILES "${OUTPUT_FILE}"
|
||||||
|
DESTINATION "${install_base_dir}/resources/${plugin_name}"
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Function to apply Houdini-specific link directories and include paths
|
||||||
|
function(configure_houdini_target target_name link_scope include_scope)
|
||||||
|
set(HOUDINI_LIBS_DIR "${HOUDINI_HFS_DIR}/../Libraries")
|
||||||
|
get_filename_component(HOUDINI_LIBS_DIR "${HOUDINI_LIBS_DIR}" ABSOLUTE) # Normalize the path
|
||||||
|
|
||||||
|
# Assume everyone using Houdini on 3.11 for now.
|
||||||
|
set(USD_MJCF_PYTHON_LIB python3.11)
|
||||||
|
set(USD_MJCF_PYTHON_LIB_NUMBER python311) # For boost library names
|
||||||
|
set(PYTHON_LIB "${HOUDINI_HFS_DIR}/Frameworks/Python.framework/Versions/3.11/Python")
|
||||||
|
set(PXR_LIB_PREFIX "pxr_")
|
||||||
|
|
||||||
|
# Houdini-specific PXR and other libraries
|
||||||
|
set(HOUDINI_SPECIFIC_LIBS
|
||||||
|
${PXR_LIB_PREFIX}usd
|
||||||
|
${PXR_LIB_PREFIX}ar
|
||||||
|
${PXR_LIB_PREFIX}kind
|
||||||
|
${PXR_LIB_PREFIX}tf
|
||||||
|
${PXR_LIB_PREFIX}gf
|
||||||
|
${PXR_LIB_PREFIX}vt
|
||||||
|
${PXR_LIB_PREFIX}sdf
|
||||||
|
${PXR_LIB_PREFIX}usdShade
|
||||||
|
${PXR_LIB_PREFIX}usdLux
|
||||||
|
${PXR_LIB_PREFIX}usdGeom
|
||||||
|
${PXR_LIB_PREFIX}usdImaging
|
||||||
|
${PXR_LIB_PREFIX}usdPhysics
|
||||||
|
tbb
|
||||||
|
hboost_${USD_MJCF_PYTHON_LIB_NUMBER}
|
||||||
|
${PYTHON_LIB}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_directories(${target_name} ${link_scope} ${HOUDINI_LIBS_DIR})
|
||||||
|
target_include_directories(${target_name} ${include_scope}
|
||||||
|
"${HOUDINI_HFS_DIR}/toolkit/include"
|
||||||
|
"${HOUDINI_HFS_DIR}/toolkit/include/python3.11"
|
||||||
|
)
|
||||||
|
target_link_libraries(${target_name} ${link_scope}
|
||||||
|
${HOUDINI_SPECIFIC_LIBS}
|
||||||
|
mujoco
|
||||||
|
tinyxml2
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
## usdMjcf
|
||||||
|
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
|
||||||
|
# The list of RPATHs:
|
||||||
|
# 1. $ORIGIN: For libmjcPhysics.so (since it's in the same install dir)
|
||||||
|
# 2. Path to OpenUSD libraries: Typically ${USD_DIR}/lib
|
||||||
|
# 3. Path to MuJoCo library: Assuming it's in ${MUJOCO_INSTALL_DIR}/lib
|
||||||
|
if(APPLE)
|
||||||
|
set(RPATH_LIST "@loader_path")
|
||||||
|
# Add OpenUSD lib to RPATH for macOS
|
||||||
|
if(DEFINED USD_DIR AND EXISTS "${USD_DIR}/lib")
|
||||||
|
list(APPEND RPATH_LIST "${USD_DIR}/lib")
|
||||||
|
endif()
|
||||||
|
# Add MuJoCo lib to RPATH for macOS
|
||||||
|
if(DEFINED MUJOCO_INSTALL_DIR AND EXISTS "${MUJOCO_INSTALL_DIR}/lib")
|
||||||
|
list(APPEND RPATH_LIST "${MUJOCO_INSTALL_DIR}/lib")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(JOIN ":" CMAKE_INSTALL_RPATH "${RPATH_LIST}") # macOS uses colon for RPATH separator
|
||||||
|
message(STATUS "Final CMAKE_INSTALL_RPATH: ${CMAKE_INSTALL_RPATH}")
|
||||||
|
elseif(UNIX)
|
||||||
|
|
||||||
|
# For adjacent libs.
|
||||||
|
set(RPATH_LIST "\$ORIGIN")
|
||||||
|
|
||||||
|
# Add OpenUSD library directory
|
||||||
|
if(DEFINED USD_DIR AND EXISTS "${USD_DIR}/lib")
|
||||||
|
list(APPEND RPATH_LIST "${USD_DIR}/lib")
|
||||||
|
message(STATUS "Adding OpenUSD lib to RPATH: ${USD_DIR}/lib")
|
||||||
|
else()
|
||||||
|
message(WARNING "USD_DIR is not defined or ${USD_DIR}/lib does not exist. OpenUSD libraries might not be found via RPATH.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add MuJoCo library directory
|
||||||
|
if(DEFINED MUJOCO_INSTALL_DIR AND EXISTS "${MUJOCO_INSTALL_DIR}/lib")
|
||||||
|
list(APPEND RPATH_LIST "${MUJOCO_INSTALL_DIR}/lib")
|
||||||
|
message(STATUS "Adding MuJoCo lib to RPATH: ${MUJOCO_INSTALL_DIR}/lib")
|
||||||
|
elseif(DEFINED MUJOCO_INSTALL_DIR AND EXISTS "${MUJOCO_INSTALL_DIR}/bin")
|
||||||
|
list(APPEND RPATH_LIST "${MUJOCO_INSTALL_DIR}/bin")
|
||||||
|
message(STATUS "Adding MuJoCo bin to RPATH: ${MUJOCO_INSTALL_DIR}/bin")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set the final RPATH for installation
|
||||||
|
string(JOIN ";" CMAKE_INSTALL_RPATH "${RPATH_LIST}")
|
||||||
|
message(STATUS "Final CMAKE_INSTALL_RPATH: ${CMAKE_INSTALL_RPATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
|
||||||
|
|
||||||
|
add_library(${MJCF_PLUGIN_TARGET_NAME} SHARED)
|
||||||
|
target_sources(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
||||||
|
plugins/mjcf/mjcf_file_format.cc
|
||||||
|
plugins/mjcf/mjcf_file_format.h
|
||||||
|
plugins/mjcf/mujoco_to_usd.cc
|
||||||
|
plugins/mjcf/mujoco_to_usd.h
|
||||||
|
plugins/mjcf/utils.cc
|
||||||
|
plugins/mjcf/utils.h
|
||||||
|
)
|
||||||
|
set_target_properties(${MJCF_PLUGIN_TARGET_NAME} PROPERTIES
|
||||||
|
OUTPUT_NAME ${MJCF_PLUGIN_TARGET_NAME}
|
||||||
|
${DEFAULT_CXX_VISIBILITY_PROPS}
|
||||||
|
)
|
||||||
|
target_include_directories(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/plugins"
|
||||||
|
)
|
||||||
|
|
||||||
|
## mjcPhysics
|
||||||
|
|
||||||
|
add_library(${MJC_PHYSICS_PLUGIN_TARGET_NAME} SHARED)
|
||||||
|
target_sources(${MJC_PHYSICS_PLUGIN_TARGET_NAME} PRIVATE
|
||||||
|
mjcPhysics/actuatorAPI.cpp
|
||||||
|
mjcPhysics/collisionAPI.cpp
|
||||||
|
mjcPhysics/meshCollisionAPI.cpp
|
||||||
|
mjcPhysics/sceneAPI.cpp
|
||||||
|
mjcPhysics/siteAPI.cpp
|
||||||
|
mjcPhysics/tokens.cpp
|
||||||
|
)
|
||||||
|
set_target_properties(${MJC_PHYSICS_PLUGIN_TARGET_NAME} PROPERTIES
|
||||||
|
OUTPUT_NAME ${MJC_PHYSICS_PLUGIN_TARGET_NAME}
|
||||||
|
${DEFAULT_CXX_VISIBILITY_PROPS}
|
||||||
|
)
|
||||||
|
target_include_directories(${MJC_PHYSICS_PLUGIN_TARGET_NAME} PUBLIC
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/mjcPhysics"
|
||||||
|
)
|
||||||
|
|
||||||
|
## mj_usd
|
||||||
|
|
||||||
|
add_library(${MJ_USD_TARGET_NAME} SHARED)
|
||||||
|
target_sources(${MJ_USD_TARGET_NAME} PRIVATE
|
||||||
|
writer.cc
|
||||||
|
layer_sink.cc
|
||||||
|
usd_to_mjspec.cc
|
||||||
|
utils.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if (USD_DIR)
|
||||||
|
find_package(pxr REQUIRED)
|
||||||
|
|
||||||
|
target_link_libraries(${MJC_PHYSICS_PLUGIN_TARGET_NAME} PRIVATE
|
||||||
|
${OPENUSD_CORE_LIBS}
|
||||||
|
mujoco
|
||||||
|
tinyxml2
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
||||||
|
${OPENUSD_CORE_LIBS}
|
||||||
|
mujoco
|
||||||
|
tinyxml2
|
||||||
|
${MJC_PHYSICS_PLUGIN_TARGET_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${MJ_USD_TARGET_NAME} PUBLIC
|
||||||
|
${OPENUSD_CORE_LIBS}
|
||||||
|
mujoco
|
||||||
|
tinyxml2
|
||||||
|
${MJC_PHYSICS_PLUGIN_TARGET_NAME}
|
||||||
|
)
|
||||||
|
elseif (HOUDINI_HFS_DIR)
|
||||||
|
message(STATUS "Configuring for Houdini target...")
|
||||||
|
|
||||||
|
# In Houdini, the Houdini package we would typically use via find_package
|
||||||
|
# does not have all the USD dependencies that we need (namely UsdPhysics)
|
||||||
|
# so we need to manually link all the required libraries.
|
||||||
|
configure_houdini_target(${MJC_PHYSICS_PLUGIN_TARGET_NAME} PRIVATE PRIVATE)
|
||||||
|
configure_houdini_target(${MJCF_PLUGIN_TARGET_NAME} PRIVATE PRIVATE)
|
||||||
|
configure_houdini_target(${MJ_USD_TARGET_NAME} PUBLIC PUBLIC)
|
||||||
|
|
||||||
|
# Re-add inter-plugin dependencies after Houdini-specific linking
|
||||||
|
target_link_libraries(${MJCF_PLUGIN_TARGET_NAME} PRIVATE ${MJC_PHYSICS_PLUGIN_TARGET_NAME})
|
||||||
|
target_link_libraries(${MJ_USD_TARGET_NAME} PUBLIC ${MJC_PHYSICS_PLUGIN_TARGET_NAME})
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
message(STATUS "Mujoco USD plugins will be installed to: ${CMAKE_INSTALL_PREFIX}/${MJ_USD_INSTALL_DIR_LIB}")
|
||||||
|
message(STATUS "Make sure PXR_PLUGINPATH_NAME includes: ${CMAKE_INSTALL_PREFIX}/${MJ_USD_INSTALL_DIR_LIB}/*/resources")
|
||||||
|
|
||||||
|
# Generate and install plugInfo.json for each plugin
|
||||||
|
configure_and_install_usd_plugin_info(
|
||||||
|
${MJCF_PLUGIN_TARGET_NAME}
|
||||||
|
plugins/mjcf
|
||||||
|
${MJ_USD_INSTALL_DIR_LIB}
|
||||||
|
)
|
||||||
|
configure_and_install_usd_plugin_info(
|
||||||
|
${MJC_PHYSICS_PLUGIN_TARGET_NAME}
|
||||||
|
mjcPhysics
|
||||||
|
${MJ_USD_INSTALL_DIR_LIB}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install specific resources that are not plugInfo.json (like generatedSchema.usda)
|
||||||
|
install(FILES
|
||||||
|
mjcPhysics/generatedSchema.usda
|
||||||
|
DESTINATION ${MJ_USD_INSTALL_DIR_LIB}/resources/mjcPhysics
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install shared libraries
|
||||||
|
install(TARGETS
|
||||||
|
${MJCF_PLUGIN_TARGET_NAME}
|
||||||
|
${MJC_PHYSICS_PLUGIN_TARGET_NAME}
|
||||||
|
${MJ_USD_TARGET_NAME}
|
||||||
|
LIBRARY DESTINATION ${MJ_USD_INSTALL_DIR_LIB}
|
||||||
|
)
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
// 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 <mujoco/experimental/usd/layer_sink.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
|
#include <pxr/base/gf/declare.h>
|
||||||
|
#include <pxr/base/gf/matrix4d.h>
|
||||||
|
#include <pxr/base/gf/quaternion.h>
|
||||||
|
#include <pxr/base/gf/rotation.h>
|
||||||
|
#include <pxr/base/gf/transform.h>
|
||||||
|
#include <pxr/base/tf/staticTokens.h>
|
||||||
|
#include <pxr/base/tf/token.h>
|
||||||
|
#include <pxr/base/vt/types.h>
|
||||||
|
#include <pxr/base/vt/value.h>
|
||||||
|
#include <pxr/pxr.h>
|
||||||
|
#include <pxr/usd/sdf/attributeSpec.h>
|
||||||
|
#include <pxr/usd/sdf/changeBlock.h>
|
||||||
|
#include <pxr/usd/sdf/childrenPolicies.h>
|
||||||
|
#include <pxr/usd/sdf/primSpec.h>
|
||||||
|
#include <pxr/usd/sdf/types.h>
|
||||||
|
#include <pxr/usd/sdf/valueTypeName.h>
|
||||||
|
#include <pxr/usd/usd/common.h>
|
||||||
|
#include <pxr/usd/usd/stage.h>
|
||||||
|
#include <pxr/usd/usd/timeCode.h>
|
||||||
|
#include <pxr/usd/usdGeom/tokens.h>
|
||||||
|
#include <pxr/usd/usdGeom/xformCache.h>
|
||||||
|
|
||||||
|
PXR_NAMESPACE_OPEN_SCOPE
|
||||||
|
TF_DEFINE_PRIVATE_TOKENS(_layer_sink_tokens,
|
||||||
|
((xformOpTransform,
|
||||||
|
"xformOp:transform:mujoco"))(Xform));
|
||||||
|
PXR_NAMESPACE_CLOSE_SCOPE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
pxr::SdfAttributeSpecHandle GetOrCreateAttribute(
|
||||||
|
pxr::SdfLayerHandle layer, pxr::SdfPrimSpecHandle prim_spec,
|
||||||
|
const pxr::SdfPath& path, const pxr::TfToken& token,
|
||||||
|
const pxr::SdfValueTypeName& type, pxr::SdfVariability variability) {
|
||||||
|
// Find or create transform attribute.
|
||||||
|
pxr::SdfAttributeSpecHandle attr =
|
||||||
|
layer->GetAttributeAtPath(path.AppendProperty(token));
|
||||||
|
|
||||||
|
if (!attr) {
|
||||||
|
attr = pxr::SdfAttributeSpec::New(prim_spec, token, type, variability);
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace mujoco {
|
||||||
|
namespace usd {
|
||||||
|
LayerSink::LayerSink(pxr::UsdStageRefPtr stage) : stage_(stage) {}
|
||||||
|
|
||||||
|
void LayerSink::Update(const mjData* const data,
|
||||||
|
std::vector<pxr::SdfPath> body_paths) {
|
||||||
|
pxr::SdfLayerHandle layer = stage_->GetEditTarget().GetLayer();
|
||||||
|
|
||||||
|
pxr::UsdTimeCode timecode = data->time * layer->GetTimeCodesPerSecond();
|
||||||
|
pxr::UsdGeomXformCache xform_cache(timecode);
|
||||||
|
|
||||||
|
pxr::SdfChangeBlock change_block;
|
||||||
|
// TODO(shaves) this can be parallelised.
|
||||||
|
for (int i = 0; i < body_paths.size(); i++) {
|
||||||
|
const pxr::SdfPath& path = body_paths[i];
|
||||||
|
|
||||||
|
// Author transform timesample
|
||||||
|
// Convert pose data.
|
||||||
|
mjtNum* xpos = &data->xpos[i * 3];
|
||||||
|
mjtNum* xquat = &data->xquat[i * 4];
|
||||||
|
pxr::GfVec3d translation(xpos[0], xpos[1], xpos[2]);
|
||||||
|
pxr::GfVec3d imag_part(xquat[1], xquat[2], xquat[3]);
|
||||||
|
// Some bodies such as the world body don't have mapped USD prims to
|
||||||
|
// transform.
|
||||||
|
if (path.IsEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pxr::SdfPrimSpecHandle prim_spec = pxr::SdfCreatePrimInLayer(layer, path);
|
||||||
|
|
||||||
|
// Find or create transform attribute.
|
||||||
|
pxr::SdfAttributeSpecHandle xform_attr = GetOrCreateAttribute(
|
||||||
|
layer, prim_spec, path, pxr::_layer_sink_tokens->xformOpTransform,
|
||||||
|
pxr::SdfValueTypeNames->Matrix4d, pxr::SdfVariabilityVarying);
|
||||||
|
|
||||||
|
pxr::GfQuaternion quat(xquat[0], imag_part);
|
||||||
|
pxr::GfRotation rotation(quat);
|
||||||
|
|
||||||
|
// Identities
|
||||||
|
pxr::GfVec3d scale(1.0);
|
||||||
|
pxr::GfVec3d pivot_position(0);
|
||||||
|
pxr::GfRotation pivot_rotation(pxr::GfQuaternion::GetIdentity());
|
||||||
|
|
||||||
|
// Set time sample
|
||||||
|
pxr::GfTransform xform(translation, rotation, scale, pivot_position,
|
||||||
|
pivot_rotation);
|
||||||
|
pxr::GfMatrix4d parent_xform =
|
||||||
|
xform_cache.GetParentToWorldTransform(stage_->GetPrimAtPath(path));
|
||||||
|
pxr::GfMatrix4d relative_xform =
|
||||||
|
xform.GetMatrix() * parent_xform.GetInverse();
|
||||||
|
layer->SetTimeSample(xform_attr->GetPath(), timecode.GetValue(),
|
||||||
|
relative_xform);
|
||||||
|
|
||||||
|
// Create xformop_order attribute if missing.
|
||||||
|
pxr::SdfAttributeSpecHandle op_order_attr = GetOrCreateAttribute(
|
||||||
|
layer, prim_spec, path, pxr::UsdGeomTokens->xformOpOrder,
|
||||||
|
pxr::SdfValueTypeNames->TokenArray, pxr::SdfVariabilityUniform);
|
||||||
|
|
||||||
|
// Override all local transforms so that this is the absolute position.
|
||||||
|
// We don't reset the xform stack here, so parent transforms will still
|
||||||
|
// affect the world pose.
|
||||||
|
const pxr::VtTokenArray xformop_order(
|
||||||
|
{pxr::_layer_sink_tokens->xformOpTransform});
|
||||||
|
|
||||||
|
op_order_attr->SetDefaultValue(pxr::VtValue(xformop_order));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace usd
|
||||||
|
} // namespace mujoco
|
||||||
@@ -1,137 +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
|
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
|
|
||||||
# Plugin target name (used for library and plugInfo.json)
|
|
||||||
set(MJCF_PLUGIN_TARGET_NAME usdMjcf)
|
|
||||||
|
|
||||||
add_library(${MJCF_PLUGIN_TARGET_NAME} SHARED
|
|
||||||
mjcf/mjcf_file_format.cc
|
|
||||||
mjcf/mjcf_file_format.h
|
|
||||||
mjcf/mujoco_to_usd.cc
|
|
||||||
mjcf/mujoco_to_usd.h
|
|
||||||
mjcf/utils.cc
|
|
||||||
mjcf/utils.h
|
|
||||||
)
|
|
||||||
|
|
||||||
# We need to set the visibility to default until core type symbol visibility
|
|
||||||
# is resolved in OpenUSD https://github.com/PixarAnimationStudios/OpenUSD/issues/1475
|
|
||||||
# Otherwise we will run into issues during composition on MacOS due to std::type_info
|
|
||||||
# comparisons failing for pxr::TfTokenVector and the like that we place in SdfAbstractData.
|
|
||||||
set_target_properties(${MJCF_PLUGIN_TARGET_NAME} PROPERTIES
|
|
||||||
OUTPUT_NAME ${MJCF_PLUGIN_TARGET_NAME}
|
|
||||||
CXX_VISIBILITY_PRESET default
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if (MUJOCO_USD_TARGET STREQUAL "USD")
|
|
||||||
find_package(pxr REQUIRED)
|
|
||||||
|
|
||||||
# --- Link Dependencies ---
|
|
||||||
# Link against the necessary OpenUSD components
|
|
||||||
target_link_libraries(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
|
||||||
usd
|
|
||||||
ar
|
|
||||||
kind
|
|
||||||
tf
|
|
||||||
gf
|
|
||||||
vt
|
|
||||||
usdShade
|
|
||||||
usdLux
|
|
||||||
usdGeom
|
|
||||||
usdImaging
|
|
||||||
usdPhysics
|
|
||||||
mujoco
|
|
||||||
tinyxml2
|
|
||||||
)
|
|
||||||
elseif (MUJOCO_USD_TARGET STREQUAL "Houdini")
|
|
||||||
|
|
||||||
if (NOT DEFINED ENV{HFS})
|
|
||||||
message(FATAL_ERROR "Environment variable 'HFS' is not defined: $ENV{HFS}. Please run houdini_setup.")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# In Houdini, the Houdini package we would typically use via find_package
|
|
||||||
# does not have all the USD dependencies that we need (namely UsdPhysics)
|
|
||||||
# so we need to manually link all the required libraries.
|
|
||||||
|
|
||||||
set(HFS_ENV "$ENV{HFS}")
|
|
||||||
set(HOUDINI_LIBS "${HFS_ENV}/../Libraries")
|
|
||||||
get_filename_component(HOUDINI_LIBS "${HOUDINI_LIBS}" ABSOLUTE) # Normalize the path
|
|
||||||
target_link_directories(${MJCF_PLUGIN_TARGET_NAME} PRIVATE ${HOUDINI_LIBS})
|
|
||||||
|
|
||||||
target_include_directories(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
|
||||||
"${HFS_ENV}/toolkit/include"
|
|
||||||
"${HFS_ENV}/toolkit/include/python3.11"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Assume everyone using Houdini on 3.11 for now.
|
|
||||||
set(USD_MJCF_PYTHON_LIB python3.11)
|
|
||||||
set(USD_MJCF_PYTHON_LIB_NUMBER python311)
|
|
||||||
set(PYTHON_LIB "${HFS_ENV}/Frameworks/Python.framework/Versions/3.11/Python")
|
|
||||||
set(PXR_LIB_PREFIX "pxr_")
|
|
||||||
|
|
||||||
# --- Link Dependencies ---
|
|
||||||
# Link against the necessary OpenUSD components
|
|
||||||
target_link_libraries(${MJCF_PLUGIN_TARGET_NAME} PRIVATE
|
|
||||||
${PXR_LIB_PREFIX}usd
|
|
||||||
${PXR_LIB_PREFIX}ar
|
|
||||||
${PXR_LIB_PREFIX}kind
|
|
||||||
${PXR_LIB_PREFIX}tf
|
|
||||||
${PXR_LIB_PREFIX}gf
|
|
||||||
${PXR_LIB_PREFIX}vt
|
|
||||||
${PXR_LIB_PREFIX}sdf
|
|
||||||
${PXR_LIB_PREFIX}usdShade
|
|
||||||
${PXR_LIB_PREFIX}usdLux
|
|
||||||
${PXR_LIB_PREFIX}usdGeom
|
|
||||||
${PXR_LIB_PREFIX}usdImaging
|
|
||||||
${PXR_LIB_PREFIX}usdPhysics
|
|
||||||
tbb
|
|
||||||
hboost_${USD_MJCF_PYTHON_LIB_NUMBER}
|
|
||||||
${PYTHON_LIB}
|
|
||||||
mujoco
|
|
||||||
tinyxml2
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# --- Generate plugInfo.json ---
|
|
||||||
if(CMAKE_SHARED_LIBRARY_PREFIX)
|
|
||||||
set(LIB_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX}) # Usually "lib" on Unix
|
|
||||||
else()
|
|
||||||
set(LIB_PREFIX "")
|
|
||||||
endif()
|
|
||||||
set(PLUG_INFO_LIBRARY_PATH "${LIB_PREFIX}${MJCF_PLUGIN_TARGET_NAME}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
|
||||||
|
|
||||||
# --- Installation ---
|
|
||||||
|
|
||||||
set(USD_PLUGIN_INSTALL_DIR_LIB ${CMAKE_INSTALL_LIBDIR}/usdMjcf)
|
|
||||||
|
|
||||||
message(STATUS "Copying plugInfo.json to ${CMAKE_BINARY_DIR}/${USD_PLUGIN_INSTALL_DIR_LIB}/plugInfo.json")
|
|
||||||
configure_file(
|
|
||||||
mjcf/plugInfo.json
|
|
||||||
${CMAKE_BINARY_DIR}/${USD_PLUGIN_INSTALL_DIR_LIB}/plugInfo.json
|
|
||||||
)
|
|
||||||
|
|
||||||
install(FILES ${CMAKE_BINARY_DIR}/${USD_PLUGIN_INSTALL_DIR_LIB}/plugInfo.json DESTINATION ${USD_PLUGIN_INSTALL_DIR_LIB})
|
|
||||||
|
|
||||||
# Install shared lib and plugInfo to same location for simplicity.
|
|
||||||
install(TARGETS ${MJCF_PLUGIN_TARGET_NAME}
|
|
||||||
LIBRARY DESTINATION ${USD_PLUGIN_INSTALL_DIR_LIB}
|
|
||||||
)
|
|
||||||
|
|
||||||
message(STATUS "USD MJCF Plugin will be installed to: ${CMAKE_INSTALL_PREFIX}/${USD_PLUGIN_INSTALL_DIR_LIB}")
|
|
||||||
message(STATUS "Make sure PXR_PLUGINPATH_NAME includes: ${CMAKE_INSTALL_PREFIX}/${USD_PLUGIN_INSTALL_DIR_LIB}")
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
|||||||
|
// 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 <mujoco/experimental/usd/writer.h>
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <mujoco/experimental/usd/utils.h>
|
||||||
|
#include <mujoco/mujoco.h>
|
||||||
|
#include <pxr/usd/sdf/path.h>
|
||||||
|
#include <pxr/usd/usd/common.h>
|
||||||
|
|
||||||
|
namespace mujoco {
|
||||||
|
namespace usd {
|
||||||
|
|
||||||
|
// Writer is the main entry point for mj_usd. It is responsible
|
||||||
|
// for converting the USD scene into a Mujoco model and exposing stepping
|
||||||
|
// API.
|
||||||
|
Writer::Writer(pxr::UsdStageRefPtr stage, mjSpec* spec, mjModel_* model)
|
||||||
|
: stage_(stage), spec_(spec), model_(model) {
|
||||||
|
BuildMjUsdMapping();
|
||||||
|
}
|
||||||
|
|
||||||
|
Writer::~Writer() = default;
|
||||||
|
|
||||||
|
void Writer::BuildMjUsdMapping() {
|
||||||
|
body_id_to_path_.assign(model_->nbody, pxr::SdfPath());
|
||||||
|
|
||||||
|
// World body (ID 0) maps to an empty SdfPath.
|
||||||
|
if (model_->nbody > 0) {
|
||||||
|
body_id_to_path_[0] = pxr::SdfPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over MuJoCo body IDs from the mjModel.
|
||||||
|
for (int body_idx = 1; body_idx < model_->nbody; ++body_idx) {
|
||||||
|
const char* body_name = mj_id2name(model_, mjOBJ_BODY, body_idx);
|
||||||
|
if (body_name) {
|
||||||
|
mjsBody* spec_body = mjs_findBody(spec_, body_name);
|
||||||
|
body_id_to_path_[body_idx] =
|
||||||
|
mujoco::usd::GetUsdPrimPathUserValue(spec_body->element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Writer::Update(const mjData* const data) {
|
||||||
|
for (const auto& sink_fn : pose_sinks_) {
|
||||||
|
sink_fn(data, body_id_to_path_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Writer::AddSink(PoseSinkFn sink_fn) {
|
||||||
|
pose_sinks_.push_back(std::move(sink_fn));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace usd
|
||||||
|
} // namespace mujoco
|
||||||
@@ -12,7 +12,22 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
if(MUJOCO_BUILD_USD_PLUGINS AND MUJOCO_USD_TARGET STREQUAL "USD")
|
# Only support testing when building against a standalone version of USD.
|
||||||
|
if(MUJOCO_WITH_USD AND USD_DIR)
|
||||||
|
find_package(pxr REQUIRED)
|
||||||
|
|
||||||
|
add_library(usd_test_utils STATIC usd/test_utils.h usd/test_utils.cc)
|
||||||
|
target_include_directories(usd_test_utils PUBLIC ${MUJOCO_TEST_INCLUDE})
|
||||||
|
target_compile_definitions(usd_test_utils PUBLIC MJSTATIC)
|
||||||
|
target_link_libraries(
|
||||||
|
usd_test_utils
|
||||||
|
PUBLIC usd
|
||||||
|
tf
|
||||||
|
gtest
|
||||||
|
gmock
|
||||||
|
mujoco
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(usd/plugins/mjcf)
|
add_subdirectory(usd/plugins/mjcf)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -12,19 +12,13 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
find_package(pxr REQUIRED)
|
mujoco_test(mjcf_file_format_test ADDITIONAL_LINK_LIBRARIES
|
||||||
|
usd
|
||||||
add_library(usd_fixture STATIC fixture.h fixture.cc)
|
tf
|
||||||
target_include_directories(usd_fixture PUBLIC ${MUJOCO_TEST_INCLUDE})
|
usdGeom
|
||||||
target_compile_definitions(usd_fixture PUBLIC MJSTATIC)
|
usdImaging
|
||||||
|
usdPhysics
|
||||||
target_link_libraries(
|
usdShade
|
||||||
usd_fixture
|
usd_test_utils
|
||||||
PUBLIC usd
|
mjcPhysics
|
||||||
tf
|
)
|
||||||
gtest
|
|
||||||
gmock
|
|
||||||
mujoco
|
|
||||||
)
|
|
||||||
|
|
||||||
mujoco_test(mjcf_file_format_test ADDITIONAL_LINK_LIBRARIES usd tf usdGeom usdImaging usdPhysics usdShade usd_fixture)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user