diff --git a/.github/workflows/build_steps.sh b/.github/workflows/build_steps.sh index c173333f..9e5440f9 100755 --- a/.github/workflows/build_steps.sh +++ b/.github/workflows/build_steps.sh @@ -295,6 +295,32 @@ EOF } +build_mujoco_live() { + echo "Setting up Emscripten SDK..." + source emsdk/emsdk_env.sh + + echo "Building Filament tools, targeting host platform..." + cmake -S . -B build_host -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DUSE_STATIC_LIBCXX=OFF \ + -DMUJOCO_BUILD_STUDIO=ON \ + -DMUJOCO_USE_FILAMENT=ON \ + -DMUJOCO_BUILD_TESTS=OFF \ + -DMUJOCO_BUILD_EXAMPLES=OFF \ + -DMUJOCO_BUILD_SIMULATE=OFF + cmake --build build_host --target matc resgen cmgen mujoco_filament_assets -j$(nproc) + + echo "Building WASM app..." + emcmake cmake -S . -B build_wasm -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DMUJOCO_BUILD_STUDIO=ON \ + -DMUJOCO_USE_FILAMENT=ON \ + -DMUJOCO_BUILD_TESTS_WASM=OFF \ + -DMUJOCO_NATIVE_BUILD_DIR=$(pwd)/build_host + cmake --build build_wasm --target mujoco_live -j$(nproc) +} + + # Discover functions defined in this script by finding identifiers followed by # "()" and capturing the identifier as a valid function name. VALID_FUNCTIONS=() diff --git a/.github/workflows/live.yml b/.github/workflows/live.yml new file mode 100644 index 00000000..49cd5359 --- /dev/null +++ b/.github/workflows/live.yml @@ -0,0 +1,51 @@ +name: live + +on: + push: + branches: + - live + +permissions: + contents: read + pages: write + id-token: write + +jobs: + build-and-upload-artifacts: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Prepare Linux + run: bash ./.github/workflows/build_steps.sh prepare_linux + + - name: Setup Emscripten + run: bash ./.github/workflows/build_steps.sh setup_emsdk + + - name: Build MuJoCo Live + env: + CC: clang-18 + CXX: clang++-18 + run: bash ./.github/workflows/build_steps.sh build_mujoco_live + + - name: Prepare files for GitHub Pages + run: | + mkdir -p dist/bin + cp -r build_wasm/bin/* dist/bin/ + cp src/experimental/studio/index.html dist/index.html + + - name: Upload GitHub Pages artifacts + uses: actions/upload-pages-artifact@v3 + with: + path: dist + + deploy-pages: + needs: build-and-upload-artifacts + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 36a64004..14fb6eec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON) project( mujoco - VERSION 3.7.1 + VERSION 3.8.1 DESCRIPTION "MuJoCo Physics Simulator" HOMEPAGE_URL "https://mujoco.org" ) @@ -53,12 +53,49 @@ endif() if(EMSCRIPTEN) option(MUJOCO_BUILD_TESTS_WASM "Build tests for WASM bindings" ON) + option(MUJOCO_BUILD_STUDIO "Build studio for MuJoCo (WASM)" OFF) + option(MUJOCO_USE_FILAMENT "Use filament rendering" OFF) option(MUJOCO_WASM_THREADS "Build with multi-threading support" ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -O3 -fexceptions") if(MUJOCO_WASM_THREADS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") endif() + + # Filament uses the WEBGL variable (not EMSCRIPTEN) to identify web builds. + # Without this, it falls into the LINUX path and tries to compile with + # futex, X11, Vulkan, etc. + if(MUJOCO_USE_FILAMENT) + set(WEBGL ON CACHE BOOL "Filament WebGL mode" FORCE) + endif() + + # Automatically generate host tool imports for Filament cross-compilation. + # When WEBGL is true, Filament hardcodes the path to: + # ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-Release.cmake + # So we write our file with that exact name and set IMPORT_EXECUTABLES_DIR + # to point at CMAKE_BINARY_DIR (which is ../../ relative to filament-src). + set(MUJOCO_NATIVE_BUILD_DIR "${PROJECT_SOURCE_DIR}/build-host" CACHE PATH "Path to native build directory containing host tools") + find_program(MATC_EXE matc PATHS "${MUJOCO_NATIVE_BUILD_DIR}/bin" NO_DEFAULT_PATH) + find_program(RESGEN_EXE resgen PATHS "${MUJOCO_NATIVE_BUILD_DIR}/bin" NO_DEFAULT_PATH) + find_program(CMGEN_EXE cmgen PATHS "${MUJOCO_NATIVE_BUILD_DIR}/bin" NO_DEFAULT_PATH) + + if(MATC_EXE AND RESGEN_EXE AND CMGEN_EXE) + message(STATUS "Found host tools in ${MUJOCO_NATIVE_BUILD_DIR}/bin") + set(IMPORT_EXECUTABLES_FILE "${CMAKE_BINARY_DIR}/ImportExecutables-Release.cmake") + file(WRITE "${IMPORT_EXECUTABLES_FILE}" + "add_executable(matc IMPORTED)\n" + "set_property(TARGET matc PROPERTY IMPORTED_LOCATION \"${MATC_EXE}\")\n" + "add_executable(resgen IMPORTED)\n" + "set_property(TARGET resgen PROPERTY IMPORTED_LOCATION \"${RESGEN_EXE}\")\n" + "add_executable(cmgen IMPORTED)\n" + "set_property(TARGET cmgen PROPERTY IMPORTED_LOCATION \"${CMGEN_EXE}\")\n" + ) + # Filament's WEBGL path resolves: ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-Release.cmake + # FILAMENT = _deps/filament-src, so ../../ resolves to CMAKE_BINARY_DIR. + set(IMPORT_EXECUTABLES_DIR "../../" CACHE PATH "" FORCE) + else() + message(WARNING "Host tools (matc, resgen, cmgen) not found in ${MUJOCO_NATIVE_BUILD_DIR}/bin. WASM build of Studio might fail.") + endif() endif() if(APPLE AND (MUJOCO_BUILD_EXAMPLES OR MUJOCO_BUILD_SIMULATE)) @@ -125,8 +162,9 @@ if(NOT EMSCRIPTEN AND NOT MUJOCO_USE_FILAMENT_MJR_COMPAT) add_subdirectory(src/render/classic) add_subdirectory(src/ui) endif() +add_subdirectory(src/render/noop) -if(MUJOCO_USE_FILAMENT AND NOT EMSCRIPTEN) +if(MUJOCO_USE_FILAMENT) add_subdirectory(src/experimental/filament) endif() diff --git a/cmake/MujocoOptions.cmake b/cmake/MujocoOptions.cmake index a606220c..74dc340f 100644 --- a/cmake/MujocoOptions.cmake +++ b/cmake/MujocoOptions.cmake @@ -18,7 +18,11 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_C_EXTENSIONS OFF) +if(EMSCRIPTEN) + set(CMAKE_C_EXTENSIONS ON) +else() + set(CMAKE_C_EXTENSIONS OFF) +endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # For LLVM tooling if(NOT CMAKE_CONFIGURATION_TYPES) diff --git a/cmake/third_party_deps/filament.cmake b/cmake/third_party_deps/filament.cmake index 007bc23b..0c94d53f 100644 --- a/cmake/third_party_deps/filament.cmake +++ b/cmake/third_party_deps/filament.cmake @@ -13,7 +13,7 @@ # limitations under the License. set(MUJOCO_DEP_VERSION_filament - a4945939de514d049baeed654efbbdd06bc5bdbf + 06793c4a80dd467025b2db1b3b7ea63bf1a865bb CACHE STRING "Tag/version of `filament` to be fetched." ) mark_as_advanced(MUJOCO_DEP_VERSION_filament) @@ -23,6 +23,15 @@ include(FindOrFetch) set(BUILD_SHARED_LIBS_OLD ${BUILD_SHARED_LIBS}) set(BUILD_SHARED_LIBS OFF) +# Filament's ShaderMinifier.cpp uses strlen without including , and +# PostProcessManager.h uses std::optional without including . +set(CMAKE_CXX_FLAGS_OLD "${CMAKE_CXX_FLAGS}") +if(MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /FI cstring /FI optional") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -include cstring -include optional") +endif() + set(FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT ON) set(FILAMENT_SKIP_SDL2 ON) set(FILAMENT_USE_EXTERNAL_ABSL ON) @@ -39,3 +48,4 @@ fetchpackage( ) set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_OLD}) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_OLD}") diff --git a/dist/mujoco.rc b/dist/mujoco.rc index 2f55a201..6400ab0d 100644 --- a/dist/mujoco.rc +++ b/dist/mujoco.rc @@ -1,6 +1,6 @@ 1 VERSIONINFO -FILEVERSION 3,7,1,0 -PRODUCTVERSION 3,7,1,0 +FILEVERSION 3,8,1,0 +PRODUCTVERSION 3,8,1,0 FILEOS 0x4 FILETYPE 0x1 { @@ -9,9 +9,9 @@ FILETYPE 0x1 BLOCK "040904b0" { VALUE "ProductName", "MuJoCo" - VALUE "ProductVersion", "3.7.1" + VALUE "ProductVersion", "3.8.1" VALUE "FileDescription", "MuJoCo" - VALUE "FileVersion", "3.7.1" + VALUE "FileVersion", "3.8.1" VALUE "InternalName", "mujoco.dll" VALUE "OriginalFilename", "mujoco.dll" VALUE "CompanyName", "Google DeepMind" diff --git a/dist/simulate.rc b/dist/simulate.rc index a1517c13..a7696780 100644 --- a/dist/simulate.rc +++ b/dist/simulate.rc @@ -1,8 +1,8 @@ MUJOCO ICON "mujoco.ico" 1 VERSIONINFO -FILEVERSION 3,7,1,0 -PRODUCTVERSION 3,7,1,0 +FILEVERSION 3,8,1,0 +PRODUCTVERSION 3,8,1,0 FILEOS 0x4 FILETYPE 0x1 { @@ -11,9 +11,9 @@ FILETYPE 0x1 BLOCK "040904b0" { VALUE "ProductName", "MuJoCo" - VALUE "ProductVersion", "3.7.1" + VALUE "ProductVersion", "3.8.1" VALUE "FileDescription", "MuJoCo" - VALUE "FileVersion", "3.7.1" + VALUE "FileVersion", "3.8.1" VALUE "InternalName", "simulate.exe" VALUE "OriginalFilename", "simulate.exe" VALUE "CompanyName", "Google DeepMind" diff --git a/doc/APIreference/APIglobals.rst b/doc/APIreference/APIglobals.rst index 72f8e741..dae8fccd 100644 --- a/doc/APIreference/APIglobals.rst +++ b/doc/APIreference/APIglobals.rst @@ -388,7 +388,7 @@ Defined in `mujoco.h ` function to get the maximum number of possible contacts returned by - two geoms. -- Added ``mj_containsBufferVFS`` and ``mj_containsFileVFS`` to check for existence of buffers and files in VFS. -- Added :ref:`multi-cell support` for trilinear and quadratic flexes. Note that the implicit - integrator uses a dense solver for the flex degrees of freedom, which can be slow for multi-cell flexes. -- Refactored ``flexstrain`` equality constraints to be instantiated per cell instead of per flex object, reducing the - number of degrees of freedom per constraint row. The equality can be associated with a specific cell with the new - attribute ":ref:`cell ` +1. Added support for Python 3.14. +2. Added :ref:`multi-cell support` for trilinear and quadratic flexes. Note that the implicit + integrator uses a dense solver for the flex degrees of freedom, which can be slow for multi-cell flexes. +3. Refactored ``strain`` flex :ref:`equality constraints` to be instantiated per cell instead of + per flex object, reducing the number of degrees of freedom per constraint row. The equality can be associated with a + specific cell with the new attribute :ref:`cell ` +4. Added new :ref:`mj_maxContact` function to get the maximum number of possible contacts returned by + colliding two geoms. +5. Added ``mj_containsBufferVFS`` and ``mj_containsFileVFS`` to check for existence of buffers and files in VFS. - .. admonition:: Breaking API changes +.. admonition:: Breaking API changes :class: attention - - The feature :ref:`multiccd` is now enabled by default. This feature has little performance overhead - and gives better contact behavior for stability. + 6. The :ref:`multiccd` option (multiple contacts returned from the convex collision detection pipeline) + is now enabled by default. The new implementation (as opposed to the legacy pipeline) has little performance + overhead and improves stability. - **Migration:** The flag :ref:`multiccd` must be explicitly disabled. + **Migration:** Disable :ref:`multiccd` to recover the previous behavior. + +Documentation +^^^^^^^^^^^^^ + +7. Added :ref:`documentation` for :ref:`mjpDecoder` plugins. Bug fixes ^^^^^^^^^ -- Asset paths in attached child specs are now resolved relative to the model file directory of the child spec, rather - than the parent spec. This prevents the origin of the parent spec to affect the resolution of asset paths in the child - spec. +8. Asset paths in attached child specs are now resolved relative to the model file directory of the child spec, rather + than the parent spec. This prevents the origin of the parent spec to affect the resolution of asset paths in the + child spec. Version 3.7.0 (April 14, 2026) ------------------------------ diff --git a/doc/programming/extension.rst b/doc/programming/extension.rst index 720f5829..bd2df7e9 100644 --- a/doc/programming/extension.rst +++ b/doc/programming/extension.rst @@ -3,8 +3,8 @@ Extensions ---------- -This section describes MuJoCo's mechanisms for user-authored extensions. At present, extensibility is provided -via :ref:`engine plugins` and :ref:`resource providers`. +This section describes MuJoCo's mechanisms for user-authored extensions. At present, extensibility is provided by +via :ref:`engine plugins`, :ref:`decoders`, and :ref:`resource providers`. .. _exPlugin: @@ -337,6 +337,139 @@ For the sdf plugin, the following methods need to be specified Computes the axis-aligned bounding box in local coordinates. This volume is voxelized uniformly before the call to the marching cubes algorithm. +.. _exDecoder: + +Decoders +~~~~~~~~ + +Decoder plugins extend asset loading capabilities beyond MJCF and URDF. They are :ref:`registered` +similarly to other MuJoCo plugins. + +MuJoCo ships with two built-in decoders for common mesh formats: + +- **OBJ decoder** (``plugin/obj_decoder``) -- `Wavefront OBJ `_. +- **STL decoder** (``plugin/stl_decoder``) -- `STL `_. + +Additionally, we provide the following optional decoder plugins: + +- **USD decoder** (``plugin/usd_decoder``) -- `Universal Scene Description `_. + +These plugins also serve as examples for how to write custom decoders. The obj decoder is perhaps the simplest to +understand, while the USD decoder is more complex due to its support for entire scenes. + +.. _exDecoderInterface: + +Decoder interface +^^^^^^^^^^^^^^^^^ + +A decoder is described by the :ref:`mjpDecoder` struct, which has the following fields: + +``content_type`` + A MIME-like content type string identifying the format. For example, ``"model/obj"``, or ``"model/stl"``. + When a mesh asset specifies a ``content-type`` attribute in MJCF, this string is used + to find the appropriate decoder. + +``extension`` + A file extension string (including the dot) used for matching when no content type is specified. Multiple + extensions can be separated by pipes (`|`) for formats with multiple extensions such as ``.usd|.usda|.usdc|.usdz``. + +``can_decode`` + A callback of type :ref:`mjfCanDecode` that determines whether the decoder can handle a given resource. This is + typically implemented by checking the file extension but may also check the file contents to differentiate between + formats. For example, URDF and MJCF files both have a ``.xml`` extension. Returns nonzero if the decoder can handle + the resource. + +``decode`` + A callback of type :ref:`mjfDecode` that performs the actual decoding. It receives an :ref:`mjResource` and + returns a newly allocated :ref:`mjSpec` containing the decoded asset data. The caller takes + ownership of the returned spec and is responsible for freeing it with :ref:`mj_deleteSpec`. Returns ``NULL`` on + failure. + +When a decoder is invoked for a mesh asset, the compiler will reference the first mesh element in the spec returned +by the ``decode`` callback. + +When a decoder is invoked for a model asset, the spec returned by the ``decode`` callback may contain any number of +elements of any type. + +.. _exDecoderRegistration: + +Registration +^^^^^^^^^^^^ + +Decoders must be registered before they can be used. Registration is performed via +:ref:`mjp_registerDecoder`. The :ref:`mjp_defaultDecoder` function initializes an :ref:`mjpDecoder` struct with +default values. The :ref:`mjPLUGIN_LIB_INIT` macro is used to define the initialization function that registers the +decoder when the library is loaded. + +.. code-block:: C + + mjPLUGIN_LIB_INIT(my_format_decoder) { + mjpDecoder decoder; + mjp_defaultDecoder(&decoder); + decoder.content_type = "model/my-format"; + decoder.extension = ".myf|.myfa|.myfc"; + decoder.decode = MyDecode; + decoder.can_decode = MyCanDecode; + mjp_registerDecoder(&decoder); + } + + +.. _exDecoderExample: + +Example +^^^^^^^ + +Below is a minimal decoder that reads a hypothetical binary mesh format: + +.. code-block:: C + + #include + + static mjSpec* MyDecode(mjResource* resource, const mjVFS* vfs) { + const void* bytes = NULL; + int nbytes = mju_readResource(resource, &bytes); + if (nbytes < 0) { + mju_warning("failed to read resource '%s'", resource->name); + return NULL; + } + + /* ... parse bytes into vertex/face arrays ... */ + + mjSpec* spec = mj_makeSpec(); + mjsMesh* mesh = mjs_addMesh(spec, NULL); + mjs_setString(mesh->file, resource->name); + mjs_setFloat(mesh->uservert, vertices, nvert * 3); + mjs_setInt(mesh->userface, faces, nface * 3); + return spec; + } + + static int MyCanDecode(const mjResource* resource) { + /* check file extension */ + const char* name = resource->name; + int len = strlen(name); + return len > 4 && strcmp(name + len - 4, ".myf") == 0; + } + + mjPLUGIN_LIB_INIT(my_format_decoder) { + mjpDecoder decoder; + mjp_defaultDecoder(&decoder); + decoder.content_type = "model/my-format"; + decoder.extension = ".myf"; + decoder.decode = MyDecode; + decoder.can_decode = MyCanDecode; + mjp_registerDecoder(&decoder); + } + +Once registered, the decoder is used automatically when MuJoCo encounters an asset with a matching file extension +or content type: + +.. code-block:: xml + + + + + + .. _exProvider: Resource providers diff --git a/doc/unity.rst b/doc/unity.rst index 67e09cef..58450440 100644 --- a/doc/unity.rst +++ b/doc/unity.rst @@ -37,14 +37,14 @@ _____ The MuJoCo app needs to be run at least once before the native library can be used, in order to register the library as a trusted binary. Then, copy the dynamic library file from -``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.7.1.dylib`` (it can be +``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.8.1.dylib`` (it can be found by browsing the contents of ``MuJoCo.app``) and rename it as ``mujoco.dylib``. Linux _____ Expand the ``tar.gz`` archive to ``~/.mujoco``. Then copy the dynamic library from -``~/.mujoco/mujoco-3.7.1/lib/libmujoco.so.3.7.1`` and rename it as ``libmujoco.so``. +``~/.mujoco/mujoco-3.8.1/lib/libmujoco.so.3.8.1`` and rename it as ``libmujoco.so``. Windows _______ diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index 2bd0cae8..c8ca6ef5 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -16,7 +16,7 @@ #define MUJOCO_MUJOCO_H_ // header version; should match the library version as returned by mj_version() -#define mjVERSION_HEADER 3007001 +#define mjVERSION_HEADER 3008001 // needed to define size_t, fabs and log10 #include diff --git a/mjx/cuda_requirements.txt b/mjx/cuda_requirements.txt index c8de6c4a..1db7abfb 100644 --- a/mjx/cuda_requirements.txt +++ b/mjx/cuda_requirements.txt @@ -1,21 +1,25 @@ -jax-cuda12-plugin==0.5.3; python_version >= '3.10' \ - --hash=sha256:1862595b2b6d815679d11e0e889e523185ee54a46d46e022689f70fc4554dd91 \ - --hash=sha256:21fec1b56c98783ea0569b747a56751f1f9ff2187b48acc11c700d3bfc5e1a31 \ +jax-cuda12-plugin==0.8.3; python_version >= '3.13' \ + --hash=sha256:11d7cb222cfd4d5f6b7691df61516a5edc9298b7879dd6caaccc6dfc64678c70 \ + --hash=sha256:7bb68f693e16038cad77197aba914375795e5bb7e99f4a4f023b67bb50cad8c2 \ + --hash=sha256:9e3f1335a6812f5b28ace211a17d9ede46b8b46ceb4b8d9c8c697d3b4ada513f \ + --hash=sha256:b38c4bd34062c01d22a8c6a2225254dcd429771e97e024f3a2ccc1d6d24fd856 \ + --hash=sha256:b3f64b5059a39c18ec006c136475f46d088a11164e7e6ee2444e76305da87a1b \ + --hash=sha256:bb20d8b794ce52d644967c7229c5e0a01d3b1fac7efc60fbbfcd046a745a66c6 \ + --hash=sha256:f9a722128e2b423469a5dab8f9f96d77257d153ca7d96850b25a475191efaacc \ + --hash=sha256:fb9d49d43c4447793630079632e6fbb4a5ce30c388955131fe3ba96efec91817 +jax-cuda12-plugin==0.5.3; python_version < '3.13' \ --hash=sha256:2030cf1208ce4ea70ee56cac61ddd239f9798695fc39bb7739c50a25d6e9da44 \ - --hash=sha256:c2517a7c2186f8708894696e26cf96ebd60b7879ceca398b2c46abb28d2c96c8 \ - --hash=sha256:aaa704a5ef547595d022db1c1e4878a0677116412a9360c115d67ff4b64e1596 \ --hash=sha256:298d2d768f1029b74a0b1d01270e549349d2c37dc07658796542cda967eb7bd3 \ + --hash=sha256:6171aed2f4b3bdd5fc13782de1072c6a634fce13731b75d0cb0a6ab8f4e6e650 \ + --hash=sha256:aaa704a5ef547595d022db1c1e4878a0677116412a9360c115d67ff4b64e1596 \ --hash=sha256:ba2555967f9b6c381c8b4ef9fb03d05bc55ec25ecfee5cfe45c5ace34f7d4152 \ - --hash=sha256:6171aed2f4b3bdd5fc13782de1072c6a634fce13731b75d0cb0a6ab8f4e6e650 -jax-cuda12-plugin==0.4.30; python_version == '3.9' \ - --hash=sha256:d8d196241b9253ecb1144a4409b5deacbb9771624f097b2bbf025da3c7d8f4f8 \ - --hash=sha256:cb8edccdce358451205f689e3536272200761c625c8e8059ab10523984cf8b61 -jax-cuda12-pjrt==0.5.3; python_version >= '3.10' \ - --hash=sha256:c5378306568ba0c81b230a779dd3194c9dd10339ab6360ae80928108d37e7f75 \ - --hash=sha256:04ee111eaf5fc2692978ad4a5c84d5925e42eb05c1701849ba3a53f6515400cc -jax-cuda12-pjrt==0.4.30; python_version == '3.9' \ - --hash=sha256:895d0198ad99638fcaf976c47592e2a543eef79ea15fabd24a402d055390c328 \ - --hash=sha256:c36fb1e0c236563bf3a87e70f4d1ab28a31d7cf5d722c9ede30c4172116e8bcb + --hash=sha256:c2517a7c2186f8708894696e26cf96ebd60b7879ceca398b2c46abb28d2c96c8 +jax-cuda12-pjrt==0.8.3; python_version >= '3.13' \ + --hash=sha256:f6d085fa7b2836cd79b14cabf1058ddb50c5161bfca9ede407993fa4f7547b7b \ + --hash=sha256:f740c661dd4064ff45dedf170fd0c4ff1a25d077636ad293307e8d28c78e65d7 +jax-cuda12-pjrt==0.5.3; python_version < '3.13' \ + --hash=sha256:04ee111eaf5fc2692978ad4a5c84d5925e42eb05c1701849ba3a53f6515400cc \ + --hash=sha256:c5378306568ba0c81b230a779dd3194c9dd10339ab6360ae80928108d37e7f75 warp-lang==1.12.1 \ --hash=sha256:98df3533a6c40a33cce961f8efa991006b30c9d286356e4cd77ea8ce86928f1d \ --hash=sha256:6bf01f10509488ba8eacaf4ec7fcf7cfbd503118b22e002ecba407b40a17424e \ diff --git a/mjx/mujoco/mjx/_src/io.py b/mjx/mujoco/mjx/_src/io.py index 74937bc0..918b0fa4 100644 --- a/mjx/mujoco/mjx/_src/io.py +++ b/mjx/mujoco/mjx/_src/io.py @@ -96,16 +96,15 @@ def _resolve_device( return cpu_0 if impl == types.Impl.WARP: - # WARP implementation requires a CUDA GPU. - cuda_gpus = [d for d in jax.devices('cuda')] - if not cuda_gpus: - raise AssertionError( - 'No CUDA GPU devices found in' - f' jax.devices("cuda")={jax.devices("cuda")}.' - ) + # WARP implementation requires a CUDA GPU or CPU. + if has_cuda_gpu_device(): + cuda_gpus = [d for d in jax.devices('cuda')] + if cuda_gpus: + logging.debug('Picking default device: %s', cuda_gpus[0]) + return cuda_gpus[0] - logging.debug('Picking default device: %s', cuda_gpus[0]) - return cuda_gpus[0] + logging.debug('Picking default device for Warp: CPU') + return jax.devices('cpu')[0] raise ValueError(f'Unsupported implementation: {impl}') @@ -121,9 +120,12 @@ def _check_impl_device_compatibility( impl = types.Impl(impl) if impl == types.Impl.WARP: - if not _is_cuda_gpu_device(device): + is_cuda_device = _is_cuda_gpu_device(device) + is_cpu_device = device.platform == 'cpu' + if not (is_cuda_device or is_cpu_device): raise AssertionError( - f'Warp implementation requires a CUDA GPU device, got {device}.' + 'Warp implementation requires a CUDA GPU or CPU device, got ' + f'{device}.' ) _check_warp_installed() @@ -425,8 +427,6 @@ def _put_model_jax( return _strip_weak_type(model) - - def _put_model_warp( m: mujoco.MjModel, graph_mode: mjxw.types.GraphMode, @@ -719,8 +719,6 @@ def _make_data_jax( return d - - def _get_nested_attr(obj: Any, attr_name: str, split: str) -> Any: """Returns the nested attribute from an object.""" for part in attr_name.split(split): @@ -1084,8 +1082,6 @@ def _put_data_jax( return _strip_weak_type(data) - - # TODO(josechenf): Iterate on the keepalive implementation to make it easier to # use before OSS. def _put_data_cpp( diff --git a/mjx/mujoco/mjx/_src/io_test.py b/mjx/mujoco/mjx/_src/io_test.py index dfcb541d..542f0718 100644 --- a/mjx/mujoco/mjx/_src/io_test.py +++ b/mjx/mujoco/mjx/_src/io_test.py @@ -934,8 +934,8 @@ _DEVICE_TEST_CASES = [ ('gpu-nvidia', 'jax', ('gpu', Impl.JAX)), ('tpu', 'jax', ('tpu', Impl.JAX)), # WARP backend specified. - ('cpu', 'warp', ('cpu', 'error')), - ('gpu-notnvidia', 'warp', ('cpu', 'error')), + ('cpu', 'warp', ('cpu', Impl.WARP)), + ('gpu-notnvidia', 'warp', ('gpu', 'error')), ('gpu-nvidia', 'warp', ('gpu', Impl.WARP)), ('tpu', 'warp', ('tpu', 'error')), # CPP backend specified. @@ -962,10 +962,10 @@ _DEFAULT_DEVICE_TEST_CASES = [ ('gpu-nvidia', 'jax', ('gpu', Impl.JAX)), ('tpu', 'jax', ('tpu', Impl.JAX)), # WARP backend impl specified. - ('cpu', 'warp', ('cpu', 'error')), - ('gpu-notnvidia', 'warp', ('cpu', 'error')), + ('cpu', 'warp', ('cpu', Impl.WARP)), + ('gpu-notnvidia', 'warp', ('cpu', Impl.WARP)), ('gpu-nvidia', 'warp', ('gpu', Impl.WARP)), - ('tpu', 'warp', ('tpu', 'error')), + ('tpu', 'warp', ('cpu', Impl.WARP)), # CPP backend impl specified, CPU should always be available. ('cpu', 'cpp', ('cpu', Impl.CPP)), ('gpu-notnvidia', 'cpp', ('cpu', Impl.CPP)), @@ -1140,15 +1140,6 @@ class ResolveImplAndDeviceTest(parameterized.TestCase): self.mock_jax_backends.side_effect = backends_side_effect expected_device, expected_impl = expected - if ( - expected_impl == 'error' - and default_device_str != 'gpu-nvidia' - and impl_str == 'warp' - ): - with self.assertRaisesRegex(RuntimeError, 'cuda backend not supported'): - mjx_io._resolve_impl_and_device(impl=impl_str, device=None) - return - if expected_impl == 'error': with self.assertRaises(AssertionError): mjx_io._resolve_impl_and_device(impl=impl_str, device=None) diff --git a/mjx/mujoco/mjx/_src/types.py b/mjx/mujoco/mjx/_src/types.py index ff08579c..faf47012 100644 --- a/mjx/mujoco/mjx/_src/types.py +++ b/mjx/mujoco/mjx/_src/types.py @@ -776,6 +776,10 @@ class Model(PyTreeNode): flex_vertadr: np.ndarray flex_vertnum: np.ndarray flex_vert0: np.ndarray + flex_nodeadr: np.ndarray + flex_nodenum: np.ndarray + flex_nodebodyid: np.ndarray + flex_node0: np.ndarray hfield_size: np.ndarray hfield_nrow: np.ndarray hfield_ncol: np.ndarray diff --git a/mjx/mujoco/mjx/third_party/warp/_src/jax_experimental/ffi.py b/mjx/mujoco/mjx/third_party/warp/_src/jax_experimental/ffi.py index c3f9e6e9..d931dded 100644 --- a/mjx/mujoco/mjx/third_party/warp/_src/jax_experimental/ffi.py +++ b/mjx/mujoco/mjx/third_party/warp/_src/jax_experimental/ffi.py @@ -210,11 +210,19 @@ class FfiKernel: self.input_output_aliases = input_output_aliases # register the callback - FFI_CCALLFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.POINTER(XLA_FFI_CallFrame)) - self.callback_func = FFI_CCALLFUNC(self.ffi_callback) - ffi_ccall_address = ctypes.cast(self.callback_func, ctypes.c_void_p) - ffi_capsule = jax.ffi.pycapsule(ffi_ccall_address.value) - jax.ffi.register_ffi_target(self.name, ffi_capsule, platform="CUDA") + FFI_CCALLFUNC = ctypes.CFUNCTYPE( + ctypes.c_void_p, ctypes.POINTER(XLA_FFI_CallFrame) + ) + + self.callback_func_cuda = FFI_CCALLFUNC(lambda call_frame: self.ffi_callback(call_frame, platform="CUDA")) + ffi_ccall_address_cuda = ctypes.cast(self.callback_func_cuda, ctypes.c_void_p) + ffi_capsule_cuda = jax.ffi.pycapsule(ffi_ccall_address_cuda.value) + jax.ffi.register_ffi_target(self.name, ffi_capsule_cuda, platform="CUDA") + + self.callback_func_host = FFI_CCALLFUNC(lambda call_frame: self.ffi_callback(call_frame, platform="Host")) + ffi_ccall_address_host = ctypes.cast(self.callback_func_host, ctypes.c_void_p) + ffi_capsule_host = jax.ffi.pycapsule(ffi_ccall_address_host.value) + jax.ffi.register_ffi_target(self.name, ffi_capsule_host, platform="Host") def __call__(self, *args, output_dims=None, launch_dims=None, vmap_method=None): num_inputs = len(args) @@ -241,18 +249,19 @@ class FfiKernel: # check dtype if input_value.dtype != input_arg.jax_scalar_type: raise TypeError( - f"Invalid data type for array argument '{input_arg.name}', expected {input_arg.jax_scalar_type}, got {input_value.dtype}" + f"Invalid data type for array argument '{input_arg.name}'," + f" expected {input_arg.jax_scalar_type}, got {input_value.dtype}" ) # check ndim if input_value.ndim != input_arg.jax_ndim: raise TypeError( - f"Invalid dimensionality for array argument '{input_arg.name}', expected {input_arg.jax_ndim} dimensions, got {input_value.ndim}" + f"Invalid dimensionality for array argument '{input_arg.name}', expected {input_arg.jax_ndim} dimensions, got {input_value.ndim}" ) # check inner dims for d in range(input_arg.dtype_ndim): if input_value.shape[input_arg.type.ndim + d] != input_arg.dtype_shape[d]: raise TypeError( - f"Invalid inner dimensions for array argument '{input_arg.name}', expected {input_arg.dtype_shape}, got {input_value.shape[-input_arg.dtype_ndim :]}" + f"Invalid inner dimensions for array argument '{input_arg.name}', expected {input_arg.dtype_shape}, got {input_value.shape[-input_arg.dtype_ndim :]}" ) else: # make sure scalar is not a traced variable, should be static @@ -328,7 +337,7 @@ class FfiKernel: return call(*args, launch_id=launch_id) - def ffi_callback(self, call_frame): + def ffi_callback(self, call_frame, platform="CUDA"): try: # On the first call, XLA runtime will query the API version and traits # metadata using the |extension| field. Let us respond to that query @@ -340,10 +349,11 @@ class FfiKernel: metadata_ext = ctypes.cast(extension, ctypes.POINTER(XLA_FFI_Metadata_Extension)) metadata_ext.contents.metadata.contents.api_version.major_version = 0 metadata_ext.contents.metadata.contents.api_version.minor_version = 1 - # Turn on CUDA graphs for this handler. - metadata_ext.contents.metadata.contents.traits = ( - XLA_FFI_Handler_TraitsBits.COMMAND_BUFFER_COMPATIBLE - ) + # Turn on CUDA graphs for this handler if on CUDA platform. + if platform == "CUDA": + metadata_ext.contents.metadata.contents.traits = ( + XLA_FFI_Handler_TraitsBits.COMMAND_BUFFER_COMPATIBLE + ) return None # Lock is required to prevent race conditions when callback is invoked @@ -423,29 +433,43 @@ class FfiKernel: kernel_params[0] = ctypes.addressof(launch_bounds) # get device and stream - device = wp.get_cuda_device(get_device_ordinal_from_callframe(call_frame.contents)) - stream = get_stream_from_callframe(call_frame.contents) + if platform == "CUDA": + device = wp.get_cuda_device(get_device_ordinal_from_callframe(call_frame.contents)) + stream = get_stream_from_callframe(call_frame.contents) + else: + device = wp.get_device("cpu") + stream = None # get kernel hooks hooks = self.kernel.module.get_kernel_hooks(self.kernel, device) assert hooks.forward, "Failed to find kernel entry point" # launch the kernel - wp._src.context.runtime.core.wp_cuda_launch_kernel( - device.context, - hooks.forward, - launch_bounds.size, - 0, - 256, - hooks.forward_smem_bytes, - kernel_params, - stream, - ) + if device.is_cuda: + wp._src.context.runtime.core.wp_cuda_launch_kernel( + device.context, + hooks.forward, + launch_bounds.size, + 0, + 256, + hooks.forward_smem_bytes, + kernel_params, + stream, + ) + else: + wp._src.context.runtime.core.wp_cpu_launch_kernel( + device.context, + hooks.forward, + launch_bounds.size, + kernel_params, + ) except Exception as e: print(traceback.format_exc()) return create_ffi_error( - call_frame.contents.api, XLA_FFI_Error_Code.UNKNOWN, f"FFI callback error: {type(e).__name__}: {e}" + call_frame.contents.api, + XLA_FFI_Error_Code.UNKNOWN, + f"FFI callback error: {type(e).__name__}: {e}", ) @@ -594,10 +618,16 @@ class FfiCallable: # register the callback FFI_CCALLFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.POINTER(XLA_FFI_CallFrame)) - self.callback_func = FFI_CCALLFUNC(self.ffi_callback) - ffi_ccall_address = ctypes.cast(self.callback_func, ctypes.c_void_p) - ffi_capsule = jax.ffi.pycapsule(ffi_ccall_address.value) - jax.ffi.register_ffi_target(self.name, ffi_capsule, platform="CUDA") + + self.callback_func_cuda = FFI_CCALLFUNC(lambda call_frame: self.ffi_callback(call_frame, platform="CUDA")) + ffi_ccall_address_cuda = ctypes.cast(self.callback_func_cuda, ctypes.c_void_p) + ffi_capsule_cuda = jax.ffi.pycapsule(ffi_ccall_address_cuda.value) + jax.ffi.register_ffi_target(self.name, ffi_capsule_cuda, platform="CUDA") + + self.callback_func_host = FFI_CCALLFUNC(lambda call_frame: self.ffi_callback(call_frame, platform="Host")) + ffi_ccall_address_host = ctypes.cast(self.callback_func_host, ctypes.c_void_p) + ffi_capsule_host = jax.ffi.pycapsule(ffi_ccall_address_host.value) + jax.ffi.register_ffi_target(self.name, ffi_capsule_host, platform="Host") def __call__(self, *args, output_dims=None, vmap_method=None): num_inputs = len(args) @@ -688,8 +718,7 @@ class FfiCallable: except Exception: # ignore unsupported devices like TPUs pass - # we only support CUDA devices for now - if dev.is_cuda: + if dev.is_cuda or dev.is_cpu: module.load(dev) # save call data to be retrieved by callback @@ -698,7 +727,7 @@ class FfiCallable: self.call_id += 1 return call(*args, call_id=call_id) - def ffi_callback(self, call_frame): + def ffi_callback(self, call_frame, platform="CUDA"): try: # On the first call, XLA runtime will query the API version and traits # metadata using the |extension| field. Let us respond to that query @@ -710,8 +739,8 @@ class FfiCallable: metadata_ext = ctypes.cast(extension, ctypes.POINTER(XLA_FFI_Metadata_Extension)) metadata_ext.contents.metadata.contents.api_version.major_version = 0 metadata_ext.contents.metadata.contents.api_version.minor_version = 1 - # Turn on CUDA graphs for this handler. - if self.graph_mode is GraphMode.JAX: + # Turn on CUDA graphs for this handler if on CUDA platform. + if self.graph_mode is GraphMode.JAX and platform == "CUDA": metadata_ext.contents.metadata.contents.traits = ( XLA_FFI_Handler_TraitsBits.COMMAND_BUFFER_COMPATIBLE ) @@ -738,6 +767,35 @@ class FfiCallable: assert num_inputs == self.num_inputs assert num_outputs == self.num_outputs + if platform == "Host": + device = wp.get_device("cpu") + # reconstruct the argument list + arg_list = [] + + # input and in-out args + for i, arg in enumerate(self.input_args): + if arg.is_array: + buffer = inputs[i].contents + shape = collapse_batch_dims(buffer.dims[: buffer.rank - arg.dtype_ndim], arg.type.ndim) + arr = wp.array(ptr=buffer.data, dtype=arg.type.dtype, shape=shape, device=device) + arg_list.append(arr) + else: + # scalar argument, get stashed value + value = call_desc.static_inputs[arg.name] + arg_list.append(value) + + # pure output args (skip in-out FFI buffers) + for i, arg in enumerate(self.output_args): + buffer = outputs[i + self.num_in_out].contents + shape = collapse_batch_dims(buffer.dims[: buffer.rank - arg.dtype_ndim], arg.type.ndim) + arr = wp.array(ptr=buffer.data, dtype=arg.type.dtype, shape=shape, device=device) + arg_list.append(arr) + + # call the Python function with reconstructed arguments + with wp.ScopedDevice(device): + self.func(*arg_list) + return + cuda_stream = get_stream_from_callframe(call_frame.contents) device_ordinal = get_device_ordinal_from_callframe(call_frame.contents) @@ -870,8 +928,8 @@ class FfiCallable: arg_list.append(arr) # call the Python function with reconstructed arguments - with wp.ScopedStream(stream, sync_enter=False): - if stream.is_capturing: + with wp.ScopedStream(stream, sync_enter=False) if stream else wp.ScopedDevice(device): + if stream and stream.is_capturing: # capturing with JAX with wp.ScopedCapture(external=True) as capture: self.func(*arg_list) @@ -879,7 +937,7 @@ class FfiCallable: # keep a reference to the capture object to prevent required modules getting unloaded call_desc.capture = capture - elif self.graph_mode == GraphMode.WARP: + elif self.graph_mode == GraphMode.WARP and device.is_cuda: # capturing with WARP with wp.ScopedCapture() as capture: self.func(*arg_list) @@ -892,7 +950,7 @@ class FfiCallable: if self._graph_cache_max is not None and len(self.captures) > self._graph_cache_max: self.captures.popitem(last=False) - elif self.graph_mode == GraphMode.WARP_STAGED_EX: + elif self.graph_mode == GraphMode.WARP_STAGED_EX and device.is_cuda: # capturing with WARP using staging buffers and memcopies done outside of the graph wp_memcpy_batch = wp._src.context.runtime.core.wp_memcpy_batch @@ -935,7 +993,7 @@ class FfiCallable: # TODO: we should have a way of freeing this call_desc.capture = capture - elif self.graph_mode == GraphMode.WARP_STAGED: + elif self.graph_mode == GraphMode.WARP_STAGED and device.is_cuda: # capturing with WARP using staging buffers and memcopies done inside of the graph wp_cuda_graph_insert_memcpy_batch = ( wp._src.context.runtime.core.wp_cuda_graph_insert_memcpy_batch @@ -1013,7 +1071,7 @@ class FfiCallable: call_desc.capture = capture else: - # not capturing + # not capturing or on CPU self.func(*arg_list) except Exception as e: @@ -1621,7 +1679,7 @@ def register_ffi_callback(name: str, func: Callable, graph_compatible: bool = Tr # TODO check that the name is not already registered - def ffi_callback(call_frame): + def ffi_callback(call_frame, platform="CUDA"): try: extension = call_frame.contents.extension_start # On the first call, XLA runtime will query the API version and traits @@ -1633,7 +1691,7 @@ def register_ffi_callback(name: str, func: Callable, graph_compatible: bool = Tr metadata_ext = ctypes.cast(extension, ctypes.POINTER(XLA_FFI_Metadata_Extension)) metadata_ext.contents.metadata.contents.api_version.major_version = 0 metadata_ext.contents.metadata.contents.api_version.minor_version = 1 - if graph_compatible: + if graph_compatible and platform == "CUDA": # Turn on CUDA graphs for this handler. metadata_ext.contents.metadata.contents.traits = ( XLA_FFI_Handler_TraitsBits.COMMAND_BUFFER_COMPATIBLE @@ -1666,12 +1724,17 @@ def register_ffi_callback(name: str, func: Callable, graph_compatible: bool = Tr return None FFI_CCALLFUNC = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.POINTER(XLA_FFI_CallFrame)) - callback_func = FFI_CCALLFUNC(ffi_callback) + callback_func_cuda = FFI_CCALLFUNC(lambda call_frame: ffi_callback(call_frame, platform="CUDA")) + callback_func_host = FFI_CCALLFUNC(lambda call_frame: ffi_callback(call_frame, platform="Host")) with _FFI_REGISTRY_LOCK: - _FFI_CALLBACK_REGISTRY[name] = callback_func - ffi_ccall_address = ctypes.cast(callback_func, ctypes.c_void_p) - ffi_capsule = jax.ffi.pycapsule(ffi_ccall_address.value) - jax.ffi.register_ffi_target(name, ffi_capsule, platform="CUDA") + _FFI_CALLBACK_REGISTRY[f"{name}_cuda"] = callback_func_cuda + _FFI_CALLBACK_REGISTRY[f"{name}_host"] = callback_func_host + ffi_ccall_address_cuda = ctypes.cast(callback_func_cuda, ctypes.c_void_p) + ffi_capsule_cuda = jax.ffi.pycapsule(ffi_ccall_address_cuda.value) + jax.ffi.register_ffi_target(name, ffi_capsule_cuda, platform="CUDA") + ffi_ccall_address_host = ctypes.cast(callback_func_host, ctypes.c_void_p) + ffi_capsule_host = jax.ffi.pycapsule(ffi_ccall_address_host.value) + jax.ffi.register_ffi_target(name, ffi_capsule_host, platform="Host") ############################################################################### diff --git a/mjx/mujoco/mjx/warp/forward_test.py b/mjx/mujoco/mjx/warp/forward_test.py index b80c921d..056a0c51 100644 --- a/mjx/mujoco/mjx/warp/forward_test.py +++ b/mjx/mujoco/mjx/warp/forward_test.py @@ -31,6 +31,7 @@ from mujoco.mjx.warp import test_util as tu from mujoco.mjx.warp import warp as wp # pylint: disable=g-importing-member import numpy as np + try: from mujoco.mjx.warp import forward # pylint: disable=g-import-not-at-top except ImportError: @@ -300,6 +301,56 @@ class StepTest(parameterized.TestCase): tu.assert_attr_eq(dx, d, 'mocap_quat') tu.assert_attr_eq(dx, d, 'sensordata') + @parameterized.parameters( + 'humanoid/humanoid.xml', + 'pendula.xml', + ) + def test_step_cpu(self, xml: str): + """Tests step on the CPU device.""" + if not _FORCE_TEST: + if not mjxw.WARP_INSTALLED: + self.skipTest('Warp not installed.') + + batch_size = 1 + m = test_util.load_test_file(xml) + m.opt.iterations = 10 + m.opt.ls_iterations = 10 + + cpu_device = jax.devices('cpu')[0] + mx = mjx.put_model(m, impl='warp', device=cpu_device) + + d = mujoco.MjData(m) + worldids = jp.arange(batch_size) + dx_batch = jax.vmap(functools.partial(tu.make_data, m))(worldids) + dx_batch = jax.device_put(dx_batch, cpu_device) + dx_batch_orig = dx_batch + + for _ in range(10): + dx_batch = jax.vmap(forward.step, in_axes=(None, 0))( + mx, dx_batch + ) + + for i in range(batch_size): + dx = dx_batch[i] + dx_orig = dx_batch_orig[i] + + d.qpos[:] = dx_orig.qpos + d.qvel[:] = dx_orig.qvel + d.ctrl[:] = dx_orig.ctrl + d.mocap_pos[:] = dx_orig.mocap_pos + d.mocap_quat[:] = dx_orig.mocap_quat + d.time = dx_orig.time + mujoco.mj_step(m, d, 10) + + tu.assert_attr_eq(dx, d, 'qpos') + tu.assert_attr_eq(dx, d, 'qvel') + tu.assert_attr_eq(dx, d, 'time') + tu.assert_attr_eq(dx, d, 'ctrl') + tu.assert_attr_eq(dx, d, 'act') + tu.assert_attr_eq(dx, d, 'mocap_pos') + tu.assert_attr_eq(dx, d, 'mocap_quat') + tu.assert_attr_eq(dx, d, 'sensordata') + def test_step_leading_dim_mismatch(self): if not _FORCE_TEST: if not mjxw.WARP_INSTALLED: diff --git a/mjx/mujoco/mjx/warp/smooth_test.py b/mjx/mujoco/mjx/warp/smooth_test.py index e7ffb92b..7c8bb368 100644 --- a/mjx/mujoco/mjx/warp/smooth_test.py +++ b/mjx/mujoco/mjx/warp/smooth_test.py @@ -133,10 +133,11 @@ class SmoothTest(parameterized.TestCase): def test_kinematics_vmap(self): """Tests kinematics with batched data.""" - if not mjxw.WARP_INSTALLED: - self.skipTest('Warp not installed.') - if not io.has_cuda_gpu_device(): - self.skipTest('No CUDA GPU device available.') + if not _FORCE_TEST: + if not mjxw.WARP_INSTALLED: + self.skipTest('Warp not installed.') + if not io.has_cuda_gpu_device(): + self.skipTest('No CUDA GPU device available.') m = tu.load_test_file('pendula.xml') diff --git a/mjx/mujoco/mjx/warp/test_util.py b/mjx/mujoco/mjx/warp/test_util.py index 1752f3c9..78a28671 100644 --- a/mjx/mujoco/mjx/warp/test_util.py +++ b/mjx/mujoco/mjx/warp/test_util.py @@ -153,7 +153,9 @@ def _mjx_efc(dx, worldid: int): efc_pos = select(dx._impl.efc__pos)[:nefc] efc_type = select(dx._impl.efc__type)[:nefc] efc_d = select(dx._impl.efc__D)[:nefc] - keys_sorted = np.lexsort((-efc_pos, efc_type, efc_d)) + keys_sorted = np.lexsort( + (-np.round(efc_pos, 12), efc_type, np.round(efc_d, 12)) + ) keys = keys[keys_sorted] nefc = len(keys) @@ -180,7 +182,9 @@ def _mj_efc(d): else: efc_j = d.efc_J.reshape((-1, d.qvel.shape[0])) - keys = np.lexsort((-d.efc_pos, d.efc_type, d.efc_D)) + keys = np.lexsort( + (-np.round(d.efc_pos, 12), d.efc_type, np.round(d.efc_D, 12)) + ) type_ = d.efc_type[keys] pos = d.efc_pos[keys] efc_j = efc_j[keys] diff --git a/mjx/pyproject.toml b/mjx/pyproject.toml index e6a48dec..65b614b9 100644 --- a/mjx/pyproject.toml +++ b/mjx/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name="mujoco-mjx" -version = "3.7.1" +version = "3.8.1" authors = [ {name = "Google DeepMind", email = "mujoco@deepmind.com"}, ] @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering", ] requires-python = ">=3.10" @@ -29,7 +30,7 @@ dependencies = [ "etils[epath]", "jax", "jaxlib", - "mujoco>=3.7.1.dev0", + "mujoco>=3.8.1.dev0", "scipy", "trimesh", ] @@ -45,9 +46,9 @@ mjx-viewer = "mujoco.mjx.viewer:main" [project.urls] Homepage = "https://github.com/google-deepmind/mujoco/tree/main/mjx" -Documentation = "https://mujoco.readthedocs.io/en/3.7.1" +Documentation = "https://mujoco.readthedocs.io/en/3.8.1" Repository = "https://github.com/google-deepmind/mujoco/tree/main/mjx" -Changelog = "https://mujoco.readthedocs.io/en/3.7.1/changelog.html" +Changelog = "https://mujoco.readthedocs.io/en/3.8.1/changelog.html" [tool.isort] force_single_line = true diff --git a/mjx/requirements.txt b/mjx/requirements.txt index 4e5b56f0..835dbfa8 100644 --- a/mjx/requirements.txt +++ b/mjx/requirements.txt @@ -2,29 +2,48 @@ absl-py==2.1.0 \ --hash=sha256:526a04eadab8b4ee719ce68f204172ead1027549089702d99b9059f129ff1308 etils[epath]==1.10.0; python_version >= '3.10' \ --hash=sha256:0777fe60a234b4c65ca53470fc64f2dd2d0c6bca7fcc623fdaa8d7fa5a317098 -jax==0.5.3; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64') \ +jax==0.8.3; python_version >= '3.13' and (sys_platform != 'darwin' or platform_machine != 'x86_64') \ + --hash=sha256:fb75f4cfee64e990ce6d7a0424cb11eb0520e4de19d7a52d0ca3498bff78261a +jax==0.5.3; python_version >= '3.10' and python_version < '3.13' and (sys_platform != 'darwin' or platform_machine != 'x86_64') \ --hash=sha256:1483dc237b4f47e41755d69429e8c3c138736716147cd43bb2b99b259d4e3c41 \ --hash=sha256:f17fcb0fd61dc289394af6ce4de2dada2312f2689bb0d73642c6f026a95fbb2c -jax==0.4.38; python_version >= '3.10' and sys_platform == 'darwin' and platform_machine == 'x86_64' \ +# 0.4.38 is the last Jax release that provides macOS x86-64 wheels, and only up +# to Python 3.13. Unfortunately, later releases can't readily be built from +# sources because some dependencies, like jaxlib, don't provide tarballs. +# Thus, building with Python 3.14+ on macOS x86-64 is disabled in the build +# scripts; if it's still needed, we'll have to come up with another method. +jax==0.4.38; python_version >= '3.10' and python_version <= '3.13' and sys_platform == 'darwin' and platform_machine == 'x86_64' \ --hash=sha256:78987306f7041ea8500d99df1a17c33ed92620c2268c4c3677fb24e06712be64 -jaxlib==0.5.3; python_version >= '3.10' and (sys_platform != 'darwin' or platform_machine != 'x86_64') \ +jaxlib==0.8.3; python_version >= '3.13' and (sys_platform != 'darwin' or platform_machine != 'x86_64') \ + --hash=sha256:1b3acadba65863254cc5482455d31a41f1fc8d38a449701f13cdbbc45beb240d \ + --hash=sha256:1e5481f9d7df9bfc0ae053d9f9d4f97ee77639a0169b45beb1cced070dc26da6 \ + --hash=sha256:3233198422c7ef49e5340785fc23f61c25ee61a5d767aa6f9183b8015e6ac6e2 \ + --hash=sha256:41067d2ec14a140e1d692fc84bc714f3372e208966b5f29cc309f4fca63c081b \ + --hash=sha256:4be4273edeb2cc75c409e446fd4f59a63d2e14f1311e714a978b5fa67ac16b70 \ + --hash=sha256:4d80575513f351eef4582908039c6e456d4e2ca028a043d2cdcbd059fa55e56c \ + --hash=sha256:56adca7fc1e24972633e2f33c758a28d924e116ba54b2a4e73f8ae4647657cf1 \ + --hash=sha256:831d817fa04218cc91b813920229c8ee0bec076c03ea744bec398e570b699d1c \ + --hash=sha256:92e755030e7862d3ba15929f25c0d7647baa95b083c87ed0c2da297aeb50c48c \ + --hash=sha256:9e76868758330eb63c5e4dd31e4d7c500a7e5523c7f09b34f64780c4a0503c7c \ + --hash=sha256:be237754eead89788264e112b2b5c722ab2a941406d19f1a8da6bf48bfde0bf2 \ + --hash=sha256:c96e562ad771fc81dfbb9a696519caa7e214f9b6dab1ce1df8bcd4759597d4a3 \ + --hash=sha256:ef3376145cc6c768f7847b688b004f37382e952bdc34b9a9404eb28b15ab50f8 \ + --hash=sha256:fe490fe3d81b02d21aaf936475fc4f47171473599e9d3907f6be0823ec4321bb +jaxlib==0.5.3; python_version >= '3.10' and python_version < '3.13' and (sys_platform != 'darwin' or platform_machine != 'x86_64') \ + --hash=sha256:29e1530fc81833216f1e28b578d0c59697654f72ee31c7a44ed7753baf5ac466 \ --hash=sha256:48ff5c89fb8a0fe04d475e9ddc074b4879a91d7ab68a51cec5cd1e87f81e6c47 \ - --hash=sha256:972400db4af6e85270d81db5e6e620d31395f0472e510c50dfcd4cb3f72b7220 \ + --hash=sha256:520665929649f29f7d948d4070dbaf3e032a4c1f7c11f2863eac73320fcee784 \ --hash=sha256:52be6c9775aff738a61170d8c047505c75bb799a45518e66a7a0908127b11785 \ + --hash=sha256:5a5e88ab1cd6fdf78d69abe3544e8f09cce200dd339bb85fbe3c2ea67f2a5e68 \ + --hash=sha256:8eb54e38d789557579f900ea3d70f104a440f8555a9681ed45f4a122dcbfd92e \ + --hash=sha256:972400db4af6e85270d81db5e6e620d31395f0472e510c50dfcd4cb3f72b7220 \ + --hash=sha256:a4666f81d72c060ed3e581ded116a9caa9b0a70a148a54cb12a1d3afca3624b5 \ --hash=sha256:b41a6fcaeb374fabc4ee7e74cfed60843bdab607cd54f60a68b7f7655cde2b66 \ --hash=sha256:b62bd8b29e5a4f9bfaa57c8daf6e04820b2c994f448f3dec602d64255545e9f2 \ - --hash=sha256:a4666f81d72c060ed3e581ded116a9caa9b0a70a148a54cb12a1d3afca3624b5 \ - --hash=sha256:29e1530fc81833216f1e28b578d0c59697654f72ee31c7a44ed7753baf5ac466 \ - --hash=sha256:8eb54e38d789557579f900ea3d70f104a440f8555a9681ed45f4a122dcbfd92e \ - --hash=sha256:d394dbde4a1c6bd67501cfb29d3819a10b900cb534cc0fc603319f7092f24cfa \ --hash=sha256:bddf6360377aa1c792e47fd87f307c342e331e5ff3582f940b1bca00f6b4bc73 \ - --hash=sha256:5a5e88ab1cd6fdf78d69abe3544e8f09cce200dd339bb85fbe3c2ea67f2a5e68 \ - --hash=sha256:520665929649f29f7d948d4070dbaf3e032a4c1f7c11f2863eac73320fcee784 \ - --hash=sha256:31321c25282a06a6dfc940507bc14d0a0ac838d8ced6c07aa00a7fae34ce7b3f \ - --hash=sha256:e904b92dedfbc7e545725a8d7676987030ae9c069001d94701bc109c6dab4100 \ - --hash=sha256:bb7593cb7fffcb13963f22fa5229ed960b8fb4ae5ec3b0820048cbd67f1e8e31 \ - --hash=sha256:8019f73a10b1290f988dd3768c684f3a8a147239091c3b790ce7e47e3bbc00bd -jaxlib==0.4.38; python_version >= '3.10' and sys_platform == 'darwin' and platform_machine == 'x86_64' \ + --hash=sha256:d394dbde4a1c6bd67501cfb29d3819a10b900cb534cc0fc603319f7092f24cfa +# See note above about macOS x86-64 support. +jaxlib==0.4.38; python_version >= '3.10' and python_version <= '3.13' and sys_platform == 'darwin' and platform_machine == 'x86_64' \ --hash=sha256:55c19b9d3f33a6fc59f644aa5a21fba02639ccdd776cb4a9b5526625f57839ff \ --hash=sha256:30b2f52cb50d74734af2f477c2533a7a583e3bb7b2c8acdeb361ee77d940577a \ --hash=sha256:ee19c163a8fdf0839d4c18b88a5fbfb4e731ba7c437416d3e5483e570bb764e4 \ @@ -52,35 +71,68 @@ pytest==8.3.3 \ --hash=sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2 pytest-xdist==3.6.1 \ --hash=sha256:9ed4adfb68a016610848639bb7e02c9352d5d9f03d04809919e2dafc3be4cca7 -scipy==1.14.1; python_version >= '3.10' \ - --hash=sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84 \ - --hash=sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e \ - --hash=sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d \ - --hash=sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e \ - --hash=sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73 \ - --hash=sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e \ - --hash=sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79 \ - --hash=sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f \ - --hash=sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066 \ - --hash=sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310 \ - --hash=sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc \ +scipy==1.17.0; python_version >= '3.13' \ + --hash=sha256:00fb5f8ec8398ad90215008d8b6009c9db9fa924fd4c7d6be307c6f945f9cd73 \ + --hash=sha256:130d12926ae34399d157de777472bf82e9061c60cc081372b3118edacafe1d00 \ + --hash=sha256:13c4096ac6bc31d706018f06a49abe0485f96499deb82066b94d19b02f664209 \ + --hash=sha256:13e861634a2c480bd237deb69333ac79ea1941b94568d4b0efa5db5e263d4fd1 \ + --hash=sha256:1f9586a58039d7229ce77b52f8472c972448cded5736eaf102d5658bbac4c269 \ + --hash=sha256:1ff269abf702f6c7e67a4b7aad981d42871a11b9dd83c58d2d2ea624efbd1088 \ + --hash=sha256:2b531f57e09c946f56ad0b4a3b2abee778789097871fc541e267d2eca081cff1 \ + --hash=sha256:33af70d040e8af9d5e7a38b5ed3b772adddd281e3062ff23fec49e49681c38cf \ + --hash=sha256:3625c631a7acd7cfd929e4e31d2582cf00f42fcf06011f59281271746d77e061 \ + --hash=sha256:363ad4ae2853d88ebcde3ae6ec46ccca903ea9835ee8ba543f12f575e7b07e4e \ + --hash=sha256:423ca1f6584fc03936972b5f7c06961670dbba9f234e71676a7c7ccf938a0d61 \ + --hash=sha256:4e00562e519c09da34c31685f6acc3aa384d4d50604db0f245c14e1b4488bfa2 \ + --hash=sha256:5fb10d17e649e1446410895639f3385fd2bf4c3c7dfc9bea937bddcbc3d7b9ba \ + --hash=sha256:65ec32f3d32dfc48c72df4291345dae4f048749bc8d5203ee0a3f347f96c5ce6 \ + --hash=sha256:6680f2dfd4f6182e7d6db161344537da644d1cf85cf293f015c60a17ecf08752 \ + --hash=sha256:6e886000eb4919eae3a44f035e63f0fd8b651234117e8f6f29bad1cd26e7bc45 \ + --hash=sha256:819fc26862b4b3c73a60d486dbb919202f3d6d98c87cf20c223511429f2d1a97 \ + --hash=sha256:8547e7c57f932e7354a2319fab613981cde910631979f74c9b542bb167a8b9db \ + --hash=sha256:87b411e42b425b84777718cc41516b8a7e0795abfa8e8e1d573bf0ef014f0812 \ + --hash=sha256:979c3a0ff8e5ba254d45d59ebd38cde48fce4f10b5125c680c7a4bfe177aab07 \ + --hash=sha256:9fad7d3578c877d606b1150135c2639e9de9cecd3705caa37b66862977cc3e72 \ + --hash=sha256:a38c3337e00be6fd8a95b4ed66b5d988bac4ec888fd922c2ea9fe5fb1603dd67 \ + --hash=sha256:aabf057c632798832f071a8dde013c2e26284043934f53b00489f1773b33527e \ + --hash=sha256:c17514d11b78be8f7e6331b983a65a7f5ca1fd037b95e27b280921fe5606286a \ + --hash=sha256:c5e8647f60679790c2f5c76be17e2e9247dc6b98ad0d3b065861e082c56e078d \ + --hash=sha256:cacbaddd91fcffde703934897c5cd2c7cb0371fac195d383f4e1f1c5d3f3bd04 \ + --hash=sha256:d7425fcafbc09a03731e1bc05581f5fad988e48c6a861f441b7ab729a49a55ea \ + --hash=sha256:dbf133ced83889583156566d2bdf7a07ff89228fe0c0cb727f777de92092ec6b \ + --hash=sha256:eb2651271135154aa24f6481cbae5cc8af1f0dd46e6533fb7b56aa9727b6a232 \ + --hash=sha256:ec0827aa4d36cb79ff1b81de898e948a51ac0b9b1c43e4a372c0508c38c0f9a3 \ + --hash=sha256:edce1a1cf66298cccdc48a1bdf8fb10a3bf58e8b58d6c3883dd1530e103f87c0 \ + --hash=sha256:eec3842ec9ac9de5917899b277428886042a93db0b227ebbe3a333b64ec7643d \ + --hash=sha256:f2a4942b0f5f7c23c7cd641a0ca1955e2ae83dedcff537e3a0259096635e186b \ + --hash=sha256:f7df7941d71314e60a481e02d5ebcb3f0185b8d799c70d03d8258f6c80f3d467 \ + --hash=sha256:f9eb55bb97d00f8b7ab95cb64f873eb0bf54d9446264d9f3609130381233483f \ + --hash=sha256:fe508b5690e9eaaa9467fc047f833af58f1152ae51a0d0aed67aa5801f4dd7d6 +scipy==1.14.1; python_version >= '3.10' and python_version < '3.13' \ + --hash=sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37 \ --hash=sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5 \ - --hash=sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07 \ + --hash=sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675 \ + --hash=sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d \ + --hash=sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f \ + --hash=sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310 \ + --hash=sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617 \ --hash=sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d \ --hash=sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94 \ - --hash=sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2 \ - --hash=sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37 \ + --hash=sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad \ --hash=sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8 \ - --hash=sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617 \ - --hash=sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2 \ - --hash=sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675 \ - --hash=sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5 \ - --hash=sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69 \ - --hash=sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d \ - --hash=sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3 \ --hash=sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0 \ + --hash=sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69 \ + --hash=sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066 \ + --hash=sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3 \ + --hash=sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5 \ + --hash=sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07 \ + --hash=sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2 \ + --hash=sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389 \ + --hash=sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2 \ --hash=sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3 \ - --hash=sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389 + --hash=sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc \ + --hash=sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1 \ + --hash=sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2 setuptools==78.1.1 \ --hash=sha256:c3a9c4211ff4c309edb8b8c4f1cbfa7ae324c4ba9f91ff254e3d305b9fd54561 \ --hash=sha256:fcc17fd9cd898242f6b4adfaca46137a9edef687f43e6f78469692a5e70d851d @@ -100,56 +152,103 @@ zipp==3.21.0 \ --hash=sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931 # Transitive dependencies of jax and jaxlib -ml-dtypes==0.5.0 \ - --hash=sha256:cb5cc7b25acabd384f75bbd78892d0c724943f3e2e1986254665a1aa10982e07 \ - --hash=sha256:54415257f00eb44fbcc807454efac3356f75644f1cbfc2d4e5522a72ae1dacab \ - --hash=sha256:e04fde367b2fe901b1d47234426fe8819909bd1dd862a5adb630f27789c20599 \ - --hash=sha256:d3b3db9990c3840986a0e70524e122cfa32b91139c3653df76121ba7776e015f \ - --hash=sha256:afa08343069874a30812871d639f9c02b4158ace065601406a493a8511180c02 \ +ml-dtypes==0.5.4; python_version >= '3.13' \ + --hash=sha256:0d2ffd05a2575b1519dc928c0b93c06339eb67173ff53acb00724502cda231cf \ + --hash=sha256:14a4fd3228af936461db66faccef6e4f41c1d82fcc30e9f8d58a08916b1d811f \ + --hash=sha256:2314892cdc3fcf05e373d76d72aaa15fda9fb98625effa73c1d646f331fcecb7 \ + --hash=sha256:2b857d3af6ac0d39db1de7c706e69c7f9791627209c3d6dedbfca8c7e5faec22 \ + --hash=sha256:304ad47faa395415b9ccbcc06a0350800bc50eda70f0e45326796e27c62f18b6 \ + --hash=sha256:4381fe2f2452a2d7589689693d3162e876b3ddb0a832cde7a414f8e1adf7eab1 \ + --hash=sha256:531eff30e4d368cb6255bc2328d070e35836aa4f282a0fb5f3a0cd7260257298 \ + --hash=sha256:533ce891ba774eabf607172254f2e7260ba5f57bdd64030c9a4fcfbd99815d0d \ + --hash=sha256:6a0df4223b514d799b8a1629c65ddc351b3efa833ccf7f8ea0cf654a61d1e35d \ + --hash=sha256:805cef3a38f4eafae3a5bf9ebdcdb741d0bcfd9e1bd90eb54abd24f928cd2465 \ + --hash=sha256:8c6a2dcebd6f3903e05d51960a8058d6e131fe69f952a5397e5dbabc841b6d56 \ + --hash=sha256:8c760d85a2f82e2bed75867079188c9d18dae2ee77c25a54d60e9cc79be1bc48 \ + --hash=sha256:bfc534409c5d4b0bf945af29e5d0ab075eae9eecbb549ff8a29280db822f34f9 \ + --hash=sha256:cb73dccfc991691c444acc8c0012bee8f2470da826a92e3a20bb333b1a7894e6 \ + --hash=sha256:ce756d3a10d0c4067172804c9cc276ba9cc0ff47af9078ad439b075d1abdc29b \ + --hash=sha256:f21c9219ef48ca5ee78402d5cc831bd58ea27ce89beda894428bc67a52da5328 +ml-dtypes==0.5.0; python_version < '3.13' \ + --hash=sha256:2e7534392682c3098bc7341648c650864207169c654aed83143d7a19c67ae06f \ + --hash=sha256:60275f2b51b56834e840c4809fca840565f9bf8e9a73f6d8c94f5b5935701215 \ + --hash=sha256:76942f6aeb5c40766d5ea62386daa4148e6a54322aaf5b53eae9e7553240222f \ + --hash=sha256:8c32138975797e681eb175996d64356bcfa124bdbb6a70460b9768c2b35a6fa4 \ + --hash=sha256:968fede07d1f9b926a63df97d25ac656cac1a57ebd33701734eaf704bc55d8d8 \ --hash=sha256:a38df8df61194aeaae1ab7579075779b4ad32cd1cffd012c28be227fa7f2a70a \ --hash=sha256:a988bac6572630e1e9c2edd9b1277b4eefd1c86209e52b0d061b775ac33902ff \ - --hash=sha256:d4b1a70a3e5219790d6b55b9507606fc4e02911d1497d16c18dd721eb7efe7d0 \ - --hash=sha256:dc74fd9995513d33eac63d64e436240f5494ec74d522a9f0920194942fc3d2d7 \ - --hash=sha256:2e7534392682c3098bc7341648c650864207169c654aed83143d7a19c67ae06f \ - --hash=sha256:76942f6aeb5c40766d5ea62386daa4148e6a54322aaf5b53eae9e7553240222f \ - --hash=sha256:60275f2b51b56834e840c4809fca840565f9bf8e9a73f6d8c94f5b5935701215 \ - --hash=sha256:968fede07d1f9b926a63df97d25ac656cac1a57ebd33701734eaf704bc55d8d8 \ - --hash=sha256:c7a9152f5876fef565516aa5dd1dccd6fc298a5891b2467973905103eb5c7856 \ --hash=sha256:ab046f2ff789b1f11b2491909682c5d089934835f9a760fafc180e47dcb676b8 \ - --hash=sha256:8c32138975797e681eb175996d64356bcfa124bdbb6a70460b9768c2b35a6fa4 \ - --hash=sha256:7ee9c320bb0f9ffdf9f6fa6a696ef2e005d1f66438d6f1c1457338e00a02e8cf \ - --hash=sha256:a03fc861b86cc586728e3d093ba37f0cc05e65330c3ebd7688e7bae8290f8859 \ - --hash=sha256:099e09edd54e676903b4538f3815b5ab96f5b119690514602d96bfdb67172cbe \ - --hash=sha256:5f2b59233a0dbb6a560b3137ed6125433289ccba2f8d9c3695a52423a369ed15 -numpy==2.1.3; python_version >= '3.10' \ - --hash=sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed \ - --hash=sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56 \ - --hash=sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43 \ - --hash=sha256:016d0f6f5e77b0f0d45d77387ffa4bb89816b57c835580c3ce8e099ef830befe \ - --hash=sha256:dc258a761a16daa791081d026f0ed4399b582712e6fc887a95af09df10c5ca57 \ - --hash=sha256:f653490b33e9c3a4c1c01d41bc2aef08f9475af51146e4a7710c450cf9761598 \ - --hash=sha256:96fe52fcdb9345b7cd82ecd34547fca4321f7656d500eca497eb7ea5a926692f \ + --hash=sha256:afa08343069874a30812871d639f9c02b4158ace065601406a493a8511180c02 \ + --hash=sha256:c7a9152f5876fef565516aa5dd1dccd6fc298a5891b2467973905103eb5c7856 \ + --hash=sha256:d4b1a70a3e5219790d6b55b9507606fc4e02911d1497d16c18dd721eb7efe7d0 \ + --hash=sha256:dc74fd9995513d33eac63d64e436240f5494ec74d522a9f0920194942fc3d2d7 +numpy==2.4.4; python_version >= '3.13' \ + --hash=sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50 \ + --hash=sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959 \ + --hash=sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a \ + --hash=sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d \ + --hash=sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e \ + --hash=sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a \ + --hash=sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113 \ + --hash=sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103 \ + --hash=sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93 \ + --hash=sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af \ + --hash=sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5 \ + --hash=sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7 \ + --hash=sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392 \ + --hash=sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40 \ + --hash=sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf \ + --hash=sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b \ + --hash=sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74 \ + --hash=sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d \ + --hash=sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150 \ + --hash=sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8 \ + --hash=sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a \ + --hash=sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed \ + --hash=sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e \ + --hash=sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0 \ + --hash=sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e \ + --hash=sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f \ + --hash=sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83 \ + --hash=sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d \ + --hash=sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c \ + --hash=sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252 \ + --hash=sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115 \ + --hash=sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f \ + --hash=sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0 \ + --hash=sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e +numpy==2.1.3; python_version >= '3.10' and python_version < '3.13' \ + --hash=sha256:02135ade8b8a84011cbb67dc44e07c58f28575cf9ecf8ab304e51c05528c19f0 \ --hash=sha256:0d30c543f02e84e92c4b1f415b7c6b5326cbe45ee7882b6b77db7195fb971e3a \ - --hash=sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b \ - --hash=sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512 \ --hash=sha256:0fa14563cc46422e99daef53d725d0c326e99e468a9320a240affffe87852564 \ - --hash=sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8 \ --hash=sha256:13138eadd4f4da03074851a698ffa7e405f41a0845a6b1ad135b81596e4e9958 \ - --hash=sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e \ - --hash=sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2 \ - --hash=sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b \ - --hash=sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a \ - --hash=sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09 \ - --hash=sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9 \ - --hash=sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41 \ + --hash=sha256:15cb89f39fa6d0bdfb600ea24b250e5f1a3df23f901f51c8debaa6a5d122b2f0 \ + --hash=sha256:17ee83a1f4fef3c94d16dc1802b998668b5419362c8a4f4e8a491de1b41cc3ee \ + --hash=sha256:2312b2aa89e1f43ecea6da6ea9a810d06aae08321609d8dc0d0eda6d946a541b \ + --hash=sha256:3522b0dfe983a575e6a9ab3a4a4dfe156c3e428468ff08ce582b9bb6bd1d71d4 \ --hash=sha256:4d1167c53b93f1f5d8a139a742b3c6f4d429b54e74e6b57d0eff40045187b15d \ - --hash=sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0 \ - --hash=sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098 \ - --hash=sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3 \ + --hash=sha256:4f2015dfe437dfebbfce7c85c7b53d81ba49e71ba7eadbf1df40c915af75979f \ + --hash=sha256:576a1c1d25e9e02ed7fa5477f30a127fe56debd53b8d2c89d5578f9857d03ca9 \ --hash=sha256:6a4825252fcc430a182ac4dee5a505053d262c807f8a924603d411f6718b88fd \ + --hash=sha256:762479be47a4863e261a840e8e01608d124ee1361e48b96916f38b119cfda04a \ + --hash=sha256:78574ac2d1a4a02421f25da9559850d59457bac82f2b8d7a44fe83a64f770098 \ --hash=sha256:825656d0743699c529c5943554d223c021ff0494ff1442152ce887ef4f7561a1 \ + --hash=sha256:8637dcd2caa676e475503d1f8fdb327bc495554e10838019651b76d17b98e512 \ + --hash=sha256:973faafebaae4c0aaa1a1ca1ce02434554d67e628b8d805e61f874b84e136b09 \ + --hash=sha256:a38c19106902bb19351b83802531fea19dee18e5b37b36454f27f11ff956f7fc \ + --hash=sha256:a6b46587b14b888e95e4a24d7b13ae91fa22386c199ee7b418f449032b2fa3b8 \ --hash=sha256:b47fbb433d3260adcd51eb54f92a2ffbc90a4595f8970ee00e064c644ac788f5 \ - --hash=sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff + --hash=sha256:bc6f24b3d1ecc1eebfbf5d6051faa49af40b03be1aaa781ebdadcbc090b4539b \ + --hash=sha256:c006b607a865b07cd981ccb218a04fc86b600411d83d6fc261357f1c0966755d \ + --hash=sha256:c7662f0e3673fe4e832fe07b65c50342ea27d989f92c80355658c7f888fcc83c \ + --hash=sha256:c80e4a09b3d95b4e1cac08643f1152fa71a0a821a2d4277334c88d54b2219a41 \ + --hash=sha256:c894b4305373b9c5576d7a12b473702afdf48ce5369c074ba304cc5ad8730dff \ + --hash=sha256:d89dd2b6da69c4fff5e39c28a382199ddedc3a5be5390115608345dec660b9e2 \ + --hash=sha256:e14e26956e6f1696070788252dcdff11b4aca4c3e8bd166e0df1bb8f315a67cb \ + --hash=sha256:e711e02f49e176a01d0349d82cb5f05ba4db7d5e7e0defd026328e5cfb3226d3 \ + --hash=sha256:ecc76a9ba2911d8d37ac01de72834d8849e55473457558e12995f4cd53e778e0 \ + --hash=sha256:f55ba01150f52b1027829b50d70ef1dafd9821ea82905b63936668403c3b471e \ + --hash=sha256:fa2d1337dc61c8dc417fbccf20f6d1e139896a30721b7f1e832b2bb6ef4eb6c4 opt-einsum==3.4.0 \ --hash=sha256:69bb92469f86a1565195ece4ac0323943e83477171b91d24c35afe028a90d7cd diff --git a/python/build_requirements.txt b/python/build_requirements.txt index 9be02aab..d3d97cc2 100644 --- a/python/build_requirements.txt +++ b/python/build_requirements.txt @@ -15,7 +15,42 @@ glfw==2.9.0 \ --hash=sha256:fcc430cb21984afba74945b7df38a5e1a02b36c0b4a2a2bab42b4a26d7cc51d6 \ --hash=sha256:aef5b555673b9555216e4cd7bc0bdbbb9983f66c620a85ba7310cfcfda5cd38c \ --hash=sha256:183da99152f63469e9263146db2eb1b6cc4ee0c4082b280743e57bd1b0a3bd70 -numpy==2.1.3; python_version >= '3.10' \ +numpy==2.4.4; python_version >= '3.13' \ + --hash=sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50 \ + --hash=sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959 \ + --hash=sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a \ + --hash=sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d \ + --hash=sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e \ + --hash=sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a \ + --hash=sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113 \ + --hash=sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103 \ + --hash=sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93 \ + --hash=sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af \ + --hash=sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5 \ + --hash=sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7 \ + --hash=sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392 \ + --hash=sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40 \ + --hash=sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf \ + --hash=sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b \ + --hash=sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74 \ + --hash=sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d \ + --hash=sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150 \ + --hash=sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8 \ + --hash=sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a \ + --hash=sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed \ + --hash=sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e \ + --hash=sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0 \ + --hash=sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e \ + --hash=sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f \ + --hash=sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83 \ + --hash=sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d \ + --hash=sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c \ + --hash=sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252 \ + --hash=sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115 \ + --hash=sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f \ + --hash=sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0 \ + --hash=sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e +numpy==2.1.3; python_version >= '3.10' and python_version < '3.13' \ --hash=sha256:747641635d3d44bcb380d950679462fae44f54b131be347d5ec2bce47d3df9ed \ --hash=sha256:5641516794ca9e5f8a4d17bb45446998c6554704d888f86df9b200e66bdcce56 \ --hash=sha256:c181ba05ce8299c7aa3125c27b9c2167bca4a4445b7ce73d5febc411ca692e43 \ diff --git a/python/build_requirements_usd.txt b/python/build_requirements_usd.txt index e6d6323c..99329f6c 100644 --- a/python/build_requirements_usd.txt +++ b/python/build_requirements_usd.txt @@ -1,39 +1,95 @@ -pillow==10.4.0 \ - --hash=sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea \ - --hash=sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc \ - --hash=sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0 \ - --hash=sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be \ - --hash=sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70 \ - --hash=sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb \ - --hash=sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3 \ - --hash=sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a \ - --hash=sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a \ - --hash=sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef \ - --hash=sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca \ - --hash=sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80 \ - --hash=sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597 \ - --hash=sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94 \ - --hash=sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91 \ - --hash=sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319 \ - --hash=sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe \ - --hash=sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6 \ - --hash=sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3 \ - --hash=sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be \ - --hash=sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c \ - --hash=sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141 \ - --hash=sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc \ - --hash=sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b \ - --hash=sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f \ - --hash=sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856 \ - --hash=sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d \ - --hash=sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e \ - --hash=sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5 \ - --hash=sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c \ - --hash=sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b \ - --hash=sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126 \ - --hash=sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd \ - --hash=sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b \ - --hash=sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d +pillow==12.1.0 \ + --hash=sha256:00162e9ca6d22b7c3ee8e61faa3c3253cd19b6a37f126cad04f2f88b306f557d \ + --hash=sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc \ + --hash=sha256:0b022eaaf709541b391ee069f0022ee5b36c709df71986e3f7be312e46f42c84 \ + --hash=sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de \ + --hash=sha256:0ddedfaa8b5f0b4ffbc2fa87b556dc59f6bb4ecb14a53b33f9189713ae8053c0 \ + --hash=sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef \ + --hash=sha256:0ed07dca4a8464bada6139ab38f5382f83e5f111698caf3191cb8dbf27d908b4 \ + --hash=sha256:0fde7ec5538ab5095cc02df38ee99b0443ff0e1c847a045554cf5f9af1f4aa82 \ + --hash=sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9 \ + --hash=sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030 \ + --hash=sha256:1f345e7bc9d7f368887c712aa5054558bad44d2a301ddf9248599f4161abc7c0 \ + --hash=sha256:1fcc52d86ce7a34fd17cb04e87cfdb164648a3662a6f20565910a99653d66c18 \ + --hash=sha256:21e686a21078b0f9cb8c8a961d99e6a4ddb88e0fc5ea6e130172ddddc2e5221a \ + --hash=sha256:2415373395a831f53933c23ce051021e79c8cd7979822d8cc478547a3f4da8ef \ + --hash=sha256:277518bf4fe74aa91489e1b20577473b19ee70fb97c374aa50830b279f25841b \ + --hash=sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6 \ + --hash=sha256:29a4cef9cb672363926f0470afc516dbf7305a14d8c54f7abbb5c199cd8f8179 \ + --hash=sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e \ + --hash=sha256:351889afef0f485b84078ea40fe33727a0492b9af3904661b0abbafee0355b72 \ + --hash=sha256:3ffaa2f0659e2f740473bcf03c702c39a8d4b2b7ffc629052028764324842c64 \ + --hash=sha256:40a8e3b9e8773876d6e30daed22f016509e3987bab61b3b7fe309d7019a87451 \ + --hash=sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd \ + --hash=sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924 \ + --hash=sha256:43b4899cfd091a9693a1278c4982f3e50f7fb7cff5153b05174b4afc9593b616 \ + --hash=sha256:461f9dfdafa394c59cd6d818bdfdbab4028b83b02caadaff0ffd433faf4c9a7a \ + --hash=sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94 \ + --hash=sha256:53d8b764726d3af1a138dd353116f774e3862ec7e3794e0c8781e30db0f35dfc \ + --hash=sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8 \ + --hash=sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9 \ + --hash=sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91 \ + --hash=sha256:5da841d81b1a05ef940a8567da92decaa15bc4d7dedb540a8c219ad83d91808a \ + --hash=sha256:5fee4c04aad8932da9f8f710af2c1a15a83582cfb884152a9caa79d4efcdbf9c \ + --hash=sha256:609e89d9f90b581c8d16358c9087df76024cf058fa693dd3e1e1620823f39670 \ + --hash=sha256:6258f3260986990ba2fa8a874f8b6e808cf5abb51a94015ca3dc3c68aa4f30ea \ + --hash=sha256:64efdf00c09e31efd754448a383ea241f55a994fd079866b92d2bbff598aad91 \ + --hash=sha256:65b80c1ee7e14a87d6a068dd3b0aea268ffcabfe0498d38661b00c5b4b22e74c \ + --hash=sha256:6741e6f3074a35e47c77b23a4e4f2d90db3ed905cb1c5e6e0d49bff2045632bc \ + --hash=sha256:681088909d7e8fa9e31b9799aaa59ba5234c58e5e4f1951b4c4d1082a2e980e0 \ + --hash=sha256:6b7a9d1db5dad90e2991645874f708e87d9a3c370c243c2d7684d28f7e133e6b \ + --hash=sha256:7315f9137087c4e0ee73a761b163fc9aa3b19f5f606a7fc08d83fd3e4379af65 \ + --hash=sha256:742aea052cf5ab5034a53c3846165bc3ce88d7c38e954120db0ab867ca242661 \ + --hash=sha256:75af0b4c229ac519b155028fa1be632d812a519abba9b46b20e50c6caa184f19 \ + --hash=sha256:7b5dd7cbae20285cdb597b10eb5a2c13aa9de6cde9bb64a3c1317427b1db1ae1 \ + --hash=sha256:7d6daa89a00b58c37cb1747ec9fb7ac3bc5ffd5949f5888657dfddde6d1312e0 \ + --hash=sha256:800429ac32c9b72909c671aaf17ecd13110f823ddb7db4dfef412a5587c2c24e \ + --hash=sha256:806f3987ffe10e867bab0ddad45df1148a2b98221798457fa097ad85d6e8bc75 \ + --hash=sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4 \ + --hash=sha256:80941e6d573197a0c28f394753de529bb436b1ca990ed6e765cf42426abc39f8 \ + --hash=sha256:84cabc7095dd535ca934d57e9ce2a72ffd216e435a84acb06b2277b1de2689bd \ + --hash=sha256:8637e29d13f478bc4f153d8daa9ffb16455f0a6cb287da1b432fdad2bfbd66c7 \ + --hash=sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61 \ + --hash=sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51 \ + --hash=sha256:907bfa8a9cb790748a9aa4513e37c88c59660da3bcfffbd24a7d9e6abf224551 \ + --hash=sha256:9212d6b86917a2300669511ed094a9406888362e085f2431a7da985a6b124f45 \ + --hash=sha256:92a7fe4225365c5e3a8e598982269c6d6698d3e783b3b1ae979e7819f9cd55c1 \ + --hash=sha256:935b9d1aed48fcfb3f838caac506f38e29621b44ccc4f8a64d575cb1b2a88644 \ + --hash=sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796 \ + --hash=sha256:983976c2ab753166dc66d36af6e8ec15bb511e4a25856e2227e5f7e00a160587 \ + --hash=sha256:9f5fefaca968e700ad1a4a9de98bf0869a94e397fe3524c4c9450c1445252304 \ + --hash=sha256:a332ac4ccb84b6dde65dbace8431f3af08874bf9770719d32a635c4ef411b18b \ + --hash=sha256:a40905599d8079e09f25027423aed94f2823adaf2868940de991e53a449e14a8 \ + --hash=sha256:a6dfc2af5b082b635af6e08e0d1f9f1c4e04d17d4e2ca0ef96131e85eda6eb17 \ + --hash=sha256:a786bf667724d84aa29b5db1c61b7bfdde380202aaca12c3461afd6b71743171 \ + --hash=sha256:a83e0850cb8f5ac975291ebfc4170ba481f41a28065277f7f735c202cd8e0af3 \ + --hash=sha256:aa0c9cc0b82b14766a99fbe6084409972266e82f459821cd26997a488a7261a7 \ + --hash=sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988 \ + --hash=sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a \ + --hash=sha256:b6e53e82ec2db0717eabb276aa56cf4e500c9a7cec2c2e189b55c24f65a3e8c0 \ + --hash=sha256:bb0984b30e973f7e2884362b7d23d0a348c7143ee559f38ef3eaab640144204c \ + --hash=sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2 \ + --hash=sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14 \ + --hash=sha256:bef9768cab184e7ae6e559c032e95ba8d07b3023c289f79a2bd36e8bf85605a5 \ + --hash=sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a \ + --hash=sha256:ca94b6aac0d7af2a10ba08c0f888b3d5114439b6b3ef39968378723622fed377 \ + --hash=sha256:cad302dc10fac357d3467a74a9561c90609768a6f73a1923b0fd851b6486f8b0 \ + --hash=sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5 \ + --hash=sha256:d70347c8a5b7ccd803ec0c85c8709f036e6348f1e6a5bf048ecd9c64d3550b8b \ + --hash=sha256:d70534cea9e7966169ad29a903b99fc507e932069a881d0965a1a84bb57f6c6d \ + --hash=sha256:db44d5c160a90df2d24a24760bbd37607d53da0b34fb546c4c232af7192298ac \ + --hash=sha256:e115c15e3bc727b1ca3e641a909f77f8ca72a64fff150f666fcc85e57701c26c \ + --hash=sha256:e2479c7f02f9d505682dc47df8c0ea1fc5e264c4d1629a5d63fe3e2334b89554 \ + --hash=sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643 \ + --hash=sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13 \ + --hash=sha256:e75d3dba8fc1ddfec0cd752108f93b83b4f8d6ab40e524a95d35f016b9683b09 \ + --hash=sha256:efdc140e7b63b8f739d09a99033aa430accce485ff78e6d311973a67b6bf3208 \ + --hash=sha256:f10c98f49227ed8383d28174ee95155a675c4ed7f85e2e573b04414f7e371bda \ + --hash=sha256:f188028b5af6b8fb2e9a76ac0f841a575bd1bd396e46ef0840d9b88a48fdbcea \ + --hash=sha256:f188d580bd870cda1e15183790d1cc2fa78f666e76077d103edf048eed9c356e \ + --hash=sha256:f45bd71d1fa5e5749587613037b172e0b3b23159d1c00ef2fc920da6f470e6f0 \ + --hash=sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831 \ + --hash=sha256:fb125d860738a09d363a88daa0f59c4533529a90e564785e20fe875b200b6dbd usd-core==24.11; python_version<='3.11' and (platform_machine=='x86_64' or platform_system=='Darwin') \ --hash=sha256:b25bde521bb65497b8bb882e4dd0de03d111dab4937c941ff4ceea6238933d5b \ --hash=sha256:a0416e3f5bc120977028d82dda38bd652478042c228d9a7d053f736bb79cde96 \ diff --git a/python/mujoco/CMakeLists.txt b/python/mujoco/CMakeLists.txt index 6a6e100f..35eeb831 100644 --- a/python/mujoco/CMakeLists.txt +++ b/python/mujoco/CMakeLists.txt @@ -86,7 +86,7 @@ if(NOT TARGET mujoco) if(MUJOCO_FRAMEWORK) message("MuJoCo framework is at ${MUJOCO_FRAMEWORK}/mujoco.framework") set(MUJOCO_LIBRARY - ${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.7.1.dylib + ${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.8.1.dylib ) target_compile_options(mujoco INTERFACE -F${MUJOCO_FRAMEWORK}) endif() @@ -94,7 +94,7 @@ if(NOT TARGET mujoco) if(NOT MUJOCO_FRAMEWORK) find_library( - MUJOCO_LIBRARY mujoco mujoco.3.7.1 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED + MUJOCO_LIBRARY mujoco mujoco.3.8.1 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED ) find_path(MUJOCO_INCLUDE mujoco/mujoco.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED) message("MuJoCo is at ${MUJOCO_LIBRARY}") @@ -193,7 +193,7 @@ findorfetch( GIT_REPO https://github.com/pybind/pybind11 GIT_TAG - d0f1a2168f3335426f544171a3463c36edbd5cc3 # v3.0.3 + c7fb32eea8c92bebeea9f0735041a72aa20c75f5 # v3.0.4 TARGETS pybind11::pybind11_headers EXCLUDE_FROM_ALL diff --git a/python/mujoco/mjpython/Info.plist b/python/mujoco/mjpython/Info.plist index 1c3b5e7c..f18c2a35 100644 --- a/python/mujoco/mjpython/Info.plist +++ b/python/mujoco/mjpython/Info.plist @@ -7,13 +7,13 @@ CFBundleIdentifier org.mujoco.mjpython CFBundleVersion - 3.7.1 + 3.8.1 CFBundleGetInfoString - 3.7.1 + 3.8.1 CFBundleLongVersionString - 3.7.1 + 3.8.1 CFBundleShortVersionString - 3.7.1 + 3.8.1 CFBundleExecutable mjpython CFBundleIconFile diff --git a/python/pyproject.toml b/python/pyproject.toml index 136e7822..8621f35c 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mujoco" -version = "3.7.1" +version = "3.8.1" authors = [ {name = "Google DeepMind", email = "mujoco@deepmind.com"}, ] @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Scientific/Engineering", ] dependencies = [ @@ -34,9 +35,9 @@ dynamic = ["readme", "scripts"] [project.urls] Homepage = "https://github.com/google-deepmind/mujoco" -Documentation = "https://mujoco.readthedocs.io/en/3.7.1" +Documentation = "https://mujoco.readthedocs.io/en/3.8.1" Repository = "https://github.com/google-deepmind/mujoco" -Changelog = "https://mujoco.readthedocs.io/en/3.7.1/changelog.html" +Changelog = "https://mujoco.readthedocs.io/en/3.8.1/changelog.html" [tool.setuptools] include-package-data = false diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 41902f1e..deb9cf63 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -24,7 +24,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON) project( mujoco_samples - VERSION 3.7.1 + VERSION 3.8.1 DESCRIPTION "MuJoCo samples binaries" HOMEPAGE_URL "https://mujoco.org" ) diff --git a/sample/cmake/SampleOptions.cmake b/sample/cmake/SampleOptions.cmake index a606220c..74dc340f 100644 --- a/sample/cmake/SampleOptions.cmake +++ b/sample/cmake/SampleOptions.cmake @@ -18,7 +18,11 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_C_EXTENSIONS OFF) +if(EMSCRIPTEN) + set(CMAKE_C_EXTENSIONS ON) +else() + set(CMAKE_C_EXTENSIONS OFF) +endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # For LLVM tooling if(NOT CMAKE_CONFIGURATION_TYPES) diff --git a/simulate/CMakeLists.txt b/simulate/CMakeLists.txt index cbb2cab8..2e8cee6a 100644 --- a/simulate/CMakeLists.txt +++ b/simulate/CMakeLists.txt @@ -29,7 +29,7 @@ set(MUJOCO_DEP_VERSION_lodepng project( mujoco_simulate - VERSION 3.7.1 + VERSION 3.8.1 DESCRIPTION "MuJoCo simulate binaries" HOMEPAGE_URL "https://mujoco.org" ) diff --git a/simulate/cmake/SimulateOptions.cmake b/simulate/cmake/SimulateOptions.cmake index a606220c..74dc340f 100644 --- a/simulate/cmake/SimulateOptions.cmake +++ b/simulate/cmake/SimulateOptions.cmake @@ -18,7 +18,11 @@ set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_C_EXTENSIONS OFF) +if(EMSCRIPTEN) + set(CMAKE_C_EXTENSIONS ON) +else() + set(CMAKE_C_EXTENSIONS OFF) +endif() set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # For LLVM tooling if(NOT CMAKE_CONFIGURATION_TYPES) diff --git a/src/engine/engine_support.c b/src/engine/engine_support.c index 2cff8056..c68a5d5c 100644 --- a/src/engine/engine_support.c +++ b/src/engine/engine_support.c @@ -43,8 +43,8 @@ //-------------------------- Constants ------------------------------------------------------------- - #define mjVERSION 3007001 -#define mjVERSIONSTRING "3.7.1" + #define mjVERSION 3008001 +#define mjVERSIONSTRING "3.8.1" // names of disable flags const char* mjDISABLESTRING[mjNDISABLE] = { diff --git a/src/experimental/filament/CMakeLists.txt b/src/experimental/filament/CMakeLists.txt index fa2081b5..9e69f162 100644 --- a/src/experimental/filament/CMakeLists.txt +++ b/src/experimental/filament/CMakeLists.txt @@ -32,10 +32,6 @@ target_sources(${MUJOCO_FILAMENT_TARGET_NAME} filament/filament_context.h filament/filament_platform_factory.cc filament/filament_platform_factory.h - filament/imgui_bridge.cc - filament/imgui_bridge.h - filament/imgui_editor.cc - filament/imgui_editor.h filament/light.cc filament/light.h filament/material.cc @@ -44,8 +40,6 @@ target_sources(${MUJOCO_FILAMENT_TARGET_NAME} filament/math_util.h filament/mesh.cc filament/mesh.h - filament/model_objects.cc - filament/model_objects.h filament/model_util.h filament/object_manager.cc filament/object_manager.h @@ -53,14 +47,22 @@ target_sources(${MUJOCO_FILAMENT_TARGET_NAME} filament/render_target.h filament/renderable.cc filament/renderable.h - filament/scene_bridge.cc - filament/scene_bridge.h - filament/scene_geom_util.cc - filament/scene_geom_util.h filament/scene_view.cc filament/scene_view.h filament/texture.cc filament/texture.h + compat/imgui_bridge.cc + compat/imgui_bridge.h + compat/imgui_editor.cc + compat/imgui_editor.h + compat/mjr_filament_renderer.cc + compat/mjr_filament_renderer.h + compat/model_objects.cc + compat/model_objects.h + compat/scene_bridge.cc + compat/scene_bridge.h + compat/scene_geom_util.cc + compat/scene_geom_util.h ) if(MUJOCO_USE_FILAMENT_MJR_COMPAT) target_sources(${MUJOCO_FILAMENT_TARGET_NAME} @@ -127,20 +129,32 @@ foreach(MATERIAL_FILE ${MATERIAL_FILES}) set(INPUT_FILE "${ASSETS_DIR}/${MATERIAL_FILE}") set(OUTPUT_FILE "${OUTPUT_ASSETS_DIR}/${MATERIAL_NAME}.filamat") - add_custom_command( - OUTPUT ${OUTPUT_FILE} - COMMAND ${MATC_EXECUTABLE} - --platform=all - --api=vulkan - --api=opengl - --variant-filter skinning - --optimize-size - --output ${OUTPUT_FILE} - ${INPUT_FILE} - DEPENDS ${INPUT_FILE} - DEPENDS matc - COMMENT "Compiling ${MATERIAL_FILE}" - ) + if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten") + set(PRECOMPILED_FILE "${MUJOCO_NATIVE_BUILD_DIR}/src/experimental/filament/assets/${MATERIAL_NAME}.filamat") + add_custom_command( + OUTPUT ${OUTPUT_FILE} + COMMAND ${CMAKE_COMMAND} -E copy + ${PRECOMPILED_FILE} + ${OUTPUT_FILE} + DEPENDS ${PRECOMPILED_FILE} + COMMENT "Copying precompiled material ${MATERIAL_NAME}.filamat" + ) + else() + add_custom_command( + OUTPUT ${OUTPUT_FILE} + COMMAND ${MATC_EXECUTABLE} + --platform=all + --api=vulkan + --api=opengl + --variant-filter skinning + --optimize-size + --output ${OUTPUT_FILE} + ${INPUT_FILE} + DEPENDS ${INPUT_FILE} + DEPENDS matc + COMMENT "Compiling ${MATERIAL_FILE}" + ) + endif() list(APPEND MUJOCO_FILAMENT_ASSET_FILES ${OUTPUT_FILE}) endforeach() diff --git a/src/experimental/filament/filament/imgui_bridge.cc b/src/experimental/filament/compat/imgui_bridge.cc similarity index 83% rename from src/experimental/filament/filament/imgui_bridge.cc rename to src/experimental/filament/compat/imgui_bridge.cc index 97dd852a..71c9da01 100644 --- a/src/experimental/filament/filament/imgui_bridge.cc +++ b/src/experimental/filament/compat/imgui_bridge.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "experimental/filament/filament/imgui_bridge.h" +#include "experimental/filament/compat/imgui_bridge.h" #include #include @@ -23,12 +23,11 @@ #include #include #include -#include #include #include "experimental/filament/filament/material.h" #include "experimental/filament/filament/mesh.h" -#include "experimental/filament/filament/renderable.h" #include "experimental/filament/filament/object_manager.h" +#include "experimental/filament/filament/renderable.h" #include "experimental/filament/filament/scene_view.h" #include "experimental/filament/filament/texture.h" @@ -37,10 +36,25 @@ namespace mujoco { using filament::math::float3; using filament::math::mat3f; -ImguiBridge::ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view) - : object_mgr_(object_mgr), scene_view_(scene_view) {} +ImguiBridge::ImguiBridge(ObjectManager* object_mgr) : object_mgr_(object_mgr) { + scene_view_ = std::make_unique(object_mgr_->GetEngine()); + scene_view_->DisableShadows(); + scene_view_->DisableReflections(); + scene_view_->DisablePostProcessing(); +} -ImguiBridge::~ImguiBridge() { PrepareRenderables(0); } +ImguiBridge::~ImguiBridge() { + PrepareRenderables(0); + + // Destroy all textures tracked by ImGui. + if (ImGui::GetCurrentContext()) { + for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) { + if (tex->Status != ImTextureStatus_Destroyed) { + DestroyTexture(tex); + } + } + } +} uintptr_t ImguiBridge::UploadImage(uintptr_t tex_id, const uint8_t* pixels, int width, int height, int bpp) { @@ -67,8 +81,8 @@ uintptr_t ImguiBridge::UploadImage(uintptr_t tex_id, const uint8_t* pixels, // new texture. if (texture == nullptr || texture->GetWidth() != width || texture->GetHeight() != height) { - TextureConfig config; - DefaultTextureConfig(&config); + mjrTextureConfig config; + mjr_defaultTextureConfig(&config); config.width = width; config.height = height; config.target = mjTEXTURE_2D; @@ -84,8 +98,8 @@ uintptr_t ImguiBridge::UploadImage(uintptr_t tex_id, const uint8_t* pixels, const auto callback = +[](void* user) { delete[] reinterpret_cast(user); }; - TextureData texture_data; - DefaultTextureData(&texture_data); + mjrTextureData texture_data; + mjr_defaultTextureData(&texture_data); texture_data.bytes = bytes; texture_data.nbytes = num_bytes; texture_data.user_data = bytes; @@ -101,8 +115,8 @@ void ImguiBridge::CreateTexture(ImTextureData* data) { mju_error("Unsupported texture format."); } - TextureConfig config; - DefaultTextureConfig(&config); + mjrTextureConfig config; + mjr_defaultTextureConfig(&config); config.width = data->Width; config.height = data->Height; config.target = mjTEXTURE_2D; @@ -122,8 +136,8 @@ void ImguiBridge::UpdateTexture(ImTextureData* data) { mju_error("Texture not found: %llu", data->TexID); } - TextureData texture_data; - DefaultTextureData(&texture_data); + mjrTextureData texture_data; + mjr_defaultTextureData(&texture_data); texture_data.bytes = data->GetPixels(); texture_data.nbytes = data->Width * data->Height * 4; texture_data.user_data = nullptr; @@ -179,22 +193,10 @@ void ImguiBridge::Update() { if (commands->Textures != nullptr) { for (ImTextureData* tex : *commands->Textures) { - if (tex->Status == ImTextureStatus_OK) { - // ImGui's lifecycle is independent of the filament context lifecycle. - // As such, it is possible to destroy and create a new filament context - // while ImGui is still expecting the "OK" textures to work. In this - // case, we simply recreate the texture. - if (textures_.find(tex->TexID) == textures_.end()) { - CreateTexture(tex); - } - } else if (tex->Status == ImTextureStatus_WantCreate) { + if (tex->Status == ImTextureStatus_WantCreate) { CreateTexture(tex); } else if (tex->Status == ImTextureStatus_WantUpdates) { - if (textures_.find(tex->TexID) == textures_.end()) { - CreateTexture(tex); - } else { - UpdateTexture(tex); - } + UpdateTexture(tex); } else if (tex->Status == ImTextureStatus_WantDestroy && tex->UnusedFrames > 0) { DestroyTexture(tex); @@ -212,24 +214,24 @@ void ImguiBridge::Update() { for (int n = 0; n < commands->CmdListsCount; ++n) { const ImDrawList* cmds = commands->CmdLists[n]; - MeshData data; - DefaultMeshData(&data); + mjrMeshData data; + mjr_defaultMeshData(&data); data.nattributes = 3; - data.attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION; + data.attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION; data.attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2; data.attributes[0].bytes = cmds->VtxBuffer.Data; - data.attributes[1].usage = mjVERTEX_ATTRIBUTE_UV; + data.attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_UV; data.attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2; data.attributes[1].bytes = cmds->VtxBuffer.Data + sizeof(float) * 2; - data.attributes[2].usage = mjVERTEX_ATTRIBUTE_COLOR; + data.attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_COLOR; data.attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_UBYTE4; data.attributes[2].bytes = cmds->VtxBuffer.Data + sizeof(float) * 4; data.interleaved = true; data.nvertices = cmds->VtxBuffer.Size; data.nindices = cmds->IdxBuffer.Size; data.indices = cmds->IdxBuffer.Data; - data.index_type = mjINDEX_TYPE_USHORT; - data.primitive_type = mjPRIM_TYPE_TRIANGLES; + data.index_type = mjINDEX_TYPE_U16; + data.primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES; meshes_.push_back(std::make_unique(scene_view_->GetEngine(), data)); const Mesh* mesh = meshes_.back().get(); @@ -242,10 +244,12 @@ void ImguiBridge::Update() { auto& renderable = renderables_[renderable_index]; renderable->SetMesh(mesh, index_offset, command.ElemCount); - MaterialTextures textures; + mjrMaterialTextures textures; + mjr_defaultMaterialTextures(&textures); textures.color = textures_[command.GetTexID()].get(); - MaterialParams properties; + mjrMaterialParams properties; + mjr_defaultMaterialParams(&properties); properties.scissor[0] = command.ClipRect.x; properties.scissor[1] = height - command.ClipRect.w; properties.scissor[2] = command.ClipRect.z - command.ClipRect.x; @@ -271,18 +275,18 @@ void ImguiBridge::Update() { void ImguiBridge::PrepareRenderables(int count) { while (renderables_.size() < count) { - RenderableParams config; - DefaultRenderableParams(&config); - config.shading_model = ShadingModel::Ux; + mjrRenderableParams params; + mjr_defaultRenderableParams(¶ms); + params.shading_model = mjSHADING_MODEL_UX; auto& r = renderables_.emplace_back( - std::make_unique(object_mgr_, config)); + std::make_unique(object_mgr_, params)); r->SetCastShadows(false); r->SetReceiveShadows(false); r->SetBlendOrder(static_cast(renderables_.size())); - scene_view_->AddToUxScene(r.get()); + scene_view_->AddToScene(r.get()); } while (renderables_.size() > count) { - scene_view_->RemoveFromUxScene(renderables_.back().get()); + scene_view_->RemoveFromScene(renderables_.back().get()); renderables_.pop_back(); } } diff --git a/src/experimental/filament/filament/imgui_bridge.h b/src/experimental/filament/compat/imgui_bridge.h similarity index 83% rename from src/experimental/filament/filament/imgui_bridge.h rename to src/experimental/filament/compat/imgui_bridge.h index 54b5f494..06807f68 100644 --- a/src/experimental/filament/filament/imgui_bridge.h +++ b/src/experimental/filament/compat/imgui_bridge.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_IMGUI_BRIDGE_H_ -#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_IMGUI_BRIDGE_H_ +#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_IMGUI_BRIDGE_H_ +#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_IMGUI_BRIDGE_H_ #include #include @@ -29,10 +29,10 @@ namespace mujoco { -// Manages Renderables that will be added a SceneView's UX scene. +// Creates and manages a SceneView using data read from ImGui. class ImguiBridge { public: - ImguiBridge(ObjectManager* object_mgr, SceneView* scene_view); + explicit ImguiBridge(ObjectManager* object_mgr); ~ImguiBridge(); // Prepares the Renderables using data from the current ImGui state. This @@ -40,6 +40,9 @@ class ImguiBridge { // synced. void Update(); + // Returns the managed UX scene. + SceneView* GetSceneView() const { return scene_view_.get(); } + // Uploads texture to be used with ImGui's Image and ImageButton functions. uintptr_t UploadImage(uintptr_t tex_id, const uint8_t* pixels, int width, int height, int bpp); @@ -57,7 +60,7 @@ class ImguiBridge { void DestroyTexture(ImTextureData* data); ObjectManager* object_mgr_ = nullptr; - SceneView* scene_view_ = nullptr; + std::unique_ptr scene_view_; std::vector> renderables_; std::vector> meshes_; std::unordered_map> textures_; @@ -69,4 +72,4 @@ void DrawTextAt(const char* text, float x, float y, float z); } // namespace mujoco -#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_IMGUI_BRIDGE_H_ +#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_IMGUI_BRIDGE_H_ diff --git a/src/experimental/filament/filament/imgui_editor.cc b/src/experimental/filament/compat/imgui_editor.cc similarity index 99% rename from src/experimental/filament/filament/imgui_editor.cc rename to src/experimental/filament/compat/imgui_editor.cc index bfc773f4..dd707914 100644 --- a/src/experimental/filament/filament/imgui_editor.cc +++ b/src/experimental/filament/compat/imgui_editor.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "experimental/filament/filament/imgui_editor.h" +#include "experimental/filament/compat/imgui_editor.h" #include #include @@ -34,8 +34,8 @@ #include #include #include +#include "experimental/filament/compat/scene_bridge.h" #include "experimental/filament/filament/color_grading_options.h" -#include "experimental/filament/filament/scene_bridge.h" #include "experimental/filament/filament/scene_view.h" namespace mujoco { diff --git a/src/experimental/filament/filament/imgui_editor.h b/src/experimental/filament/compat/imgui_editor.h similarity index 74% rename from src/experimental/filament/filament/imgui_editor.h rename to src/experimental/filament/compat/imgui_editor.h index f073fe45..3457fd26 100644 --- a/src/experimental/filament/filament/imgui_editor.h +++ b/src/experimental/filament/compat/imgui_editor.h @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_IMGUI_EDITOR_H_ -#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_IMGUI_EDITOR_H_ +#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_IMGUI_EDITOR_H_ +#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_IMGUI_EDITOR_H_ -#include "experimental/filament/filament/scene_bridge.h" +#include "experimental/filament/compat/scene_bridge.h" namespace mujoco { @@ -24,4 +24,4 @@ void DrawGui(SceneBridge* scene_bridge); } // namespace mujoco -#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_IMGUI_EDITOR_H_ +#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_IMGUI_EDITOR_H_ diff --git a/src/experimental/filament/compat/mjr_filament_renderer.cc b/src/experimental/filament/compat/mjr_filament_renderer.cc new file mode 100644 index 00000000..48242258 --- /dev/null +++ b/src/experimental/filament/compat/mjr_filament_renderer.cc @@ -0,0 +1,211 @@ +// 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 "experimental/filament/compat/mjr_filament_renderer.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "experimental/filament/compat/imgui_bridge.h" +#include "experimental/filament/compat/imgui_editor.h" +#include "experimental/filament/compat/scene_bridge.h" +#include "experimental/filament/filament/draw_mode.h" +#include "experimental/filament/filament/filament_context.h" +#include "experimental/filament/filament/model_util.h" +#include "experimental/filament/filament/render_target.h" +#include "experimental/filament/filament/texture.h" +#include "experimental/filament/render_context_filament.h" + +namespace mujoco { + +MjrFilamentRenderer::MjrFilamentRenderer(const mjrFilamentConfig* config) + : FilamentContext(config) { +} + +void MjrFilamentRenderer::Init(const mjModel* model) { + scene_bridge_ = std::make_unique(GetObjectManager(), model); + imgui_bridge_ = std::make_unique(GetObjectManager()); + + render_requests_[0].scene = scene_bridge_->GetSceneView(); + render_requests_[0].draw_mode = DrawMode::Color; + + render_requests_[1].scene = imgui_bridge_->GetSceneView(); + render_requests_[1].draw_mode = DrawMode::Color; + + // The UX camera is a fixed orthographic camera. We only need to change the + // width/height based on the viewport per frame. + render_requests_[1].camera.orthographic = true; + render_requests_[1].camera.pos[0] = 0.0f; + render_requests_[1].camera.pos[1] = 0.0f; + render_requests_[1].camera.pos[2] = 1.0f; + render_requests_[1].camera.forward[0] = 0.0f; + render_requests_[1].camera.forward[1] = 0.0f; + render_requests_[1].camera.forward[2] = -1.0f; + render_requests_[1].camera.up[0] = 0.0f; + render_requests_[1].camera.up[1] = 1.0f; + render_requests_[1].camera.up[2] = 0.0f; + render_requests_[1].camera.frustum_top = 0.0f; + render_requests_[1].camera.frustum_near = 0.0f; + render_requests_[1].camera.frustum_far = 1.0f; + + + SetClearColor(ReadElement(model, "filament.clearColor", + filament::math::float4(0, 0, 0, 1))); +} + +void MjrFilamentRenderer::Render(const mjrRect& viewport, const mjvScene* scene) { + scene_bridge_->Update(viewport, scene); + // Update the UX renderable entity after processing the scene in case there + // are any elements in the scene which generate UX draw calls (e.g. labels). + if (mode_ != FrameBufferMode::OffScreen) { + imgui_bridge_->Update(); + } + + if (scene->flags[mjRND_SEGMENT]) { + render_requests_[0].draw_mode = DrawMode::Segmentation; + } else if (scene->flags[mjRND_DEPTH]) { + render_requests_[0].draw_mode = DrawMode::Depth; + } else { + render_requests_[0].draw_mode = DrawMode::Color; + } + + render_requests_[0].width = viewport.width; + render_requests_[0].height = viewport.height; + render_requests_[1].width = viewport.width; + render_requests_[1].height = viewport.height; + + render_requests_[0].camera = mjv_averageCamera(scene->camera, scene->camera + 1); + render_requests_[1].camera.frustum_center = viewport.width / 2.0f; + render_requests_[1].camera.frustum_width = viewport.width / 2.0f; + render_requests_[1].camera.frustum_bottom = viewport.height; + + if (mode_ == FrameBufferMode::Window) { + render_requests_[0].target = nullptr; + render_requests_[1].target = nullptr; + FilamentContext::Render(render_requests_); + } +} + +void MjrFilamentRenderer::SetFrameBuffer(int framebuffer) { + switch (framebuffer) { + case mjFB_WINDOW: + mode_ = FrameBufferMode::Window; + break; + case mjFB_OFFSCREEN: + mode_ = FrameBufferMode::OffScreen; + break; + case 2: // No official constant fo this. + mode_ = FrameBufferMode::OffScreenWithGui; + break; + default: + mju_error("Invalid framebuffer mode: %d", framebuffer); + } +} + +void MjrFilamentRenderer::ReadPixels(mjrRect viewport, unsigned char* rgb, + float* depth) { + if (mode_ == FrameBufferMode::Window) { + mju_error("ReadPixels is only supported for offscreen rendering."); + } + + render_requests_[0].width = viewport.width; + render_requests_[0].height = viewport.height; + render_requests_[1].width = viewport.width; + render_requests_[1].height = viewport.height; + + if (rgb) { + mjrRenderTargetConfig config; + mjr_defaultRenderTargetConfig(&config); + config.color_format = mjPIXEL_FORMAT_RGB8; + config.depth_format = mjPIXEL_FORMAT_DEPTH32F; + auto target = std::make_unique(GetEngine(), config); + target->Prepare(viewport.width, viewport.height); + render_requests_[0].target = target.get(); + render_requests_[1].target = target.get(); + + const size_t num_requests = + (mode_ == FrameBufferMode::OffScreenWithGui) ? 2 : 1; + + ReadPixelsRequest read_request; + read_request.output = rgb; + read_request.num_bytes = viewport.width * viewport.height * 3; + const FrameHandle frame = FilamentContext::Render( + {&render_requests_[0], num_requests}, {&read_request, 1}); + FilamentContext::WaitForFrame(frame); + + render_requests_[0].target = nullptr; + render_requests_[1].target = nullptr; + } + + if (depth) { + mjrRenderTargetConfig config; + mjr_defaultRenderTargetConfig(&config); + config.color_format = mjPIXEL_FORMAT_R32F; + config.depth_format = mjPIXEL_FORMAT_DEPTH32F; + auto target = std::make_unique(GetEngine(), config); + target->Prepare(viewport.width, viewport.height); + render_requests_[0].target = target.get(); + render_requests_[1].target = target.get(); + + DrawMode last_draw_mode = render_requests_[0].draw_mode; + render_requests_[0].draw_mode = DrawMode::Depth; + + ReadPixelsRequest read_request; + read_request.output = reinterpret_cast(depth); + read_request.num_bytes = viewport.width * viewport.height * sizeof(float); + const FrameHandle frame = + FilamentContext::Render({&render_requests_[0], 1}, {&read_request, 1}); + FilamentContext::WaitForFrame(frame); + + render_requests_[0].target = nullptr; + render_requests_[1].target = nullptr; + render_requests_[0].draw_mode = last_draw_mode; + } +} + +void MjrFilamentRenderer::UploadMesh(const mjModel* model, int id) { + if (!scene_bridge_) { + mju_error("SceneBridge is not initialized."); + } + scene_bridge_->UploadMesh(model, id); +} + +void MjrFilamentRenderer::UploadTexture(const mjModel* model, int id) { + if (!scene_bridge_) { + mju_error("SceneBridge is not initialized."); + } + scene_bridge_->UploadTexture(model, id); +} + +void MjrFilamentRenderer::UploadHeightField(const mjModel* model, int id) { + if (!scene_bridge_) { + mju_error("SceneBridge is not initialized."); + } + scene_bridge_->UploadHeightField(model, id); +} + +uintptr_t MjrFilamentRenderer::UploadGuiImage(uintptr_t tex_id, + const uint8_t* pixels, int width, + int height, int bpp) { + return imgui_bridge_->UploadImage(tex_id, pixels, width, height, bpp); +} + +void MjrFilamentRenderer::UpdateGui() { DrawGui(scene_bridge_.get()); } + +} // namespace mujoco diff --git a/src/experimental/filament/compat/mjr_filament_renderer.h b/src/experimental/filament/compat/mjr_filament_renderer.h new file mode 100644 index 00000000..a1c97edc --- /dev/null +++ b/src/experimental/filament/compat/mjr_filament_renderer.h @@ -0,0 +1,86 @@ +// 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_FILAMENT_COMPAT_MJR_FILAMENT_RENDERER_H_ +#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_MJR_FILAMENT_RENDERER_H_ + +#include +#include + +#include +#include +#include +#include "experimental/filament/compat/imgui_bridge.h" +#include "experimental/filament/compat/scene_bridge.h" +#include "experimental/filament/filament/filament_context.h" +#include "experimental/filament/render_context_filament.h" + +namespace mujoco { + +// Subclass of the FilamentContext that implements the legacy mjr API. +class MjrFilamentRenderer : public FilamentContext { + public: + explicit MjrFilamentRenderer(const mjrFilamentConfig* config); + ~MjrFilamentRenderer() = default; + + // Initializes the renderer with the given model. + void Init(const mjModel* model); + + // Renders the given mjvScene to the viewport. + void Render(const mjrRect& viewport, const mjvScene* scene); + + // Configures the renderer to render to the window (0) or an offscreen + // texture (1 or 2). Rendering to the window always includes UX data from + // ImGui. A value of 1 indicates the UX should not be included in the + // offscreen render, whereas 2 indicates that it should. + void SetFrameBuffer(int framebuffer); + + // Renders the scene to a texture if the framebuffer is not 0. + void ReadPixels(mjrRect viewport, unsigned char* rgb, float* depth); + + // Uploads the mesh data from the model to the GPU. + void UploadMesh(const mjModel* model, int id); + + // Uploads the texture data from the model to the GPU. + void UploadTexture(const mjModel* model, int id); + + // Uploads the height field data from the model to the GPU. + void UploadHeightField(const mjModel* model, int id); + + // Uploads a texture that can be used with ImGui to the GPU. + uintptr_t UploadGuiImage(uintptr_t tex_id, const uint8_t* pixels, int width, + int height, int bpp); + + // Renders an ImGui window containing Filament-specific editor UI. + void UpdateGui(); + + MjrFilamentRenderer(const MjrFilamentRenderer&) = delete; + MjrFilamentRenderer& operator=(const MjrFilamentRenderer&) = delete; + + private: + enum class FrameBufferMode { + Window, + OffScreen, + OffScreenWithGui, + }; + + FrameBufferMode mode_ = FrameBufferMode::Window; + RenderRequest render_requests_[2]; + std::unique_ptr scene_bridge_; + std::unique_ptr imgui_bridge_; +}; + +} // namespace mujoco + +#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_MJR_FILAMENT_RENDERER_H_ diff --git a/src/experimental/filament/filament/model_objects.cc b/src/experimental/filament/compat/model_objects.cc similarity index 94% rename from src/experimental/filament/filament/model_objects.cc rename to src/experimental/filament/compat/model_objects.cc index 15121743..1a52b6f1 100644 --- a/src/experimental/filament/filament/model_objects.cc +++ b/src/experimental/filament/compat/model_objects.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "experimental/filament/filament/model_objects.h" +#include "experimental/filament/compat/model_objects.h" #include #include @@ -412,7 +412,7 @@ static std::span GetIndices(const mjModel* model, } } -static void UpdateMeshData(MeshData* data, const mjModel* model, int id, +static void UpdatemjrMeshData(mjrMeshData* data, const mjModel* model, int id, MeshType mesh_type) { if (!IsValidIndex(model, id, mesh_type)) { mju_error("Invalid index %d for type %d", id, mesh_type); @@ -440,22 +440,22 @@ static void UpdateMeshData(MeshData* data, const mjModel* model, int id, break; } - data->primitive_type = mjPRIM_TYPE_TRIANGLES; + data->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES; data->nvertices = num_vertices; data->nindices = data->nvertices; data->indices = nullptr; data->index_type = data->nvertices >= std::numeric_limits::max() - ? mjINDEX_TYPE_UINT - : mjINDEX_TYPE_USHORT; + ? mjINDEX_TYPE_U32 + : mjINDEX_TYPE_U16; data->nattributes = has_uvs ? 3 : 2; - data->attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION; + data->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION; data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3; data->attributes[0].bytes = builder->positions.data(); - data->attributes[1].usage = mjVERTEX_ATTRIBUTE_TANGENTS; + data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS; data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4; data->attributes[1].bytes = builder->orientations.data(); if (has_uvs) { - data->attributes[2].usage = mjVERTEX_ATTRIBUTE_UV; + data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV; data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2; data->attributes[2].bytes = builder->uvs.data(); } @@ -467,7 +467,7 @@ static void UpdateMeshData(MeshData* data, const mjModel* model, int id, data->bounds_max[2] = builder->bounds_max.z; } -void UpdateSkinFlexMeshData(MeshData* data, const mjModel* model, +void UpdateSkinFlexmjrMeshData(mjrMeshData* data, const mjModel* model, const mjvScene* scene, const mjvGeom& geom) { auto positions = GetPositions(model, scene, geom); auto normals = GetNormals(model, scene, geom); @@ -480,20 +480,20 @@ void UpdateSkinFlexMeshData(MeshData* data, const mjModel* model, } data->nattributes = uvs.data() ? 3 : 2; - data->attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION; + data->attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION; data->attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3; data->attributes[0].bytes = positions.data(); - data->attributes[1].usage = mjVERTEX_ATTRIBUTE_NORMAL; + data->attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_NORMAL; data->attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3; data->attributes[1].bytes = normals.data(); - data->attributes[2].usage = mjVERTEX_ATTRIBUTE_UV; + data->attributes[2].usage = mjVERTEX_ATTRIBUTE_USAGE_UV; data->attributes[2].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT2; data->attributes[2].bytes = uvs.data(); data->nvertices = positions.size() / 3; data->nindices = num_indices; data->indices = indices.data(); - data->index_type = mjINDEX_TYPE_UINT; - data->primitive_type = mjPRIM_TYPE_TRIANGLES; + data->index_type = mjINDEX_TYPE_U32; + data->primitive_type = mjMESH_PRIMITIVE_TYPE_TRIANGLES; data->compute_bounds = true; data->release_callback = nullptr; data->user_data = nullptr; @@ -554,15 +554,15 @@ void ModelObjects::UploadMesh(const mjModel* model, int id) { meshes_.erase(id); convex_hulls_.erase(id); - MeshData data; - DefaultMeshData(&data); - UpdateMeshData(&data, model, id, MeshType::kNormal); + mjrMeshData data; + mjr_defaultMeshData(&data); + UpdatemjrMeshData(&data, model, id, MeshType::kNormal); meshes_[id] = std::make_unique(engine_, data); if (model->mesh_graphadr[id] >= 0) { - MeshData convex_hull_data; - DefaultMeshData(&convex_hull_data); - UpdateMeshData(&convex_hull_data, model, id, MeshType::kConvexHull); + mjrMeshData convex_hull_data; + mjr_defaultMeshData(&convex_hull_data); + UpdatemjrMeshData(&convex_hull_data, model, id, MeshType::kConvexHull); convex_hulls_[id] = std::make_unique(engine_, convex_hull_data); } } @@ -575,8 +575,8 @@ void ModelObjects::UploadTexture(const mjModel* model, int id) { mju_error("Invalid texture index: %d", id); } - TextureConfig config; - DefaultTextureConfig(&config); + mjrTextureConfig config; + mjr_defaultTextureConfig(&config); config.width = model->tex_width[id]; config.height = model->tex_height[id]; config.target = (mjtTexture)model->tex_type[id]; @@ -600,8 +600,8 @@ void ModelObjects::UploadTexture(const mjModel* model, int id) { } - TextureData payload; - DefaultTextureData(&payload); + mjrTextureData payload; + mjr_defaultTextureData(&payload); payload.bytes = model->tex_data + model->tex_adr[id]; payload.nbytes = model->tex_width[id] * model->tex_height[id] * model->tex_nchannel[id]; @@ -624,16 +624,16 @@ void ModelObjects::UploadHeightField(const mjModel* model, int id) { height_fields_.erase(id); - MeshData data; - DefaultMeshData(&data); - UpdateMeshData(&data, model, id, MeshType::kHeightField); + mjrMeshData data; + mjr_defaultMeshData(&data); + UpdatemjrMeshData(&data, model, id, MeshType::kHeightField); height_fields_[id] = std::make_unique(engine_, data); } void ModelObjects::CreateSkinFlexMesh(const mjvScene* scene, const mjvGeom& geom) { - MeshData data; - DefaultMeshData(&data); - UpdateSkinFlexMeshData(&data, model_, scene, geom); + mjrMeshData data; + mjr_defaultMeshData(&data); + UpdateSkinFlexmjrMeshData(&data, model_, scene, geom); dynamic_meshes_[geom.objid] = std::make_unique(engine_, data); } diff --git a/src/experimental/filament/filament/model_objects.h b/src/experimental/filament/compat/model_objects.h similarity index 94% rename from src/experimental/filament/filament/model_objects.h rename to src/experimental/filament/compat/model_objects.h index d85693e3..db528d36 100644 --- a/src/experimental/filament/filament/model_objects.h +++ b/src/experimental/filament/compat/model_objects.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_MODEL_OBJECTS_H_ -#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_MODEL_OBJECTS_H_ +#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_MODEL_OBJECTS_H_ +#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_MODEL_OBJECTS_H_ #include #include @@ -100,4 +100,4 @@ class ModelObjects { } // namespace mujoco -#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_MODEL_OBJECTS_H_ +#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_MODEL_OBJECTS_H_ diff --git a/src/experimental/filament/filament/scene_bridge.cc b/src/experimental/filament/compat/scene_bridge.cc similarity index 90% rename from src/experimental/filament/filament/scene_bridge.cc rename to src/experimental/filament/compat/scene_bridge.cc index 3438b8c9..6ab29a36 100644 --- a/src/experimental/filament/filament/scene_bridge.cc +++ b/src/experimental/filament/compat/scene_bridge.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "experimental/filament/filament/scene_bridge.h" +#include "experimental/filament/compat/scene_bridge.h" #include #include @@ -36,16 +36,15 @@ #include #include #include +#include "experimental/filament/compat/imgui_bridge.h" +#include "experimental/filament/compat/model_objects.h" +#include "experimental/filament/compat/scene_geom_util.h" #include "experimental/filament/filament/color_grading_options.h" -#include "experimental/filament/filament/imgui_bridge.h" #include "experimental/filament/filament/light.h" -#include "experimental/filament/filament/material.h" #include "experimental/filament/filament/math_util.h" -#include "experimental/filament/filament/model_objects.h" #include "experimental/filament/filament/model_util.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/renderable.h" -#include "experimental/filament/filament/scene_geom_util.h" #include "experimental/filament/filament/scene_view.h" #include "experimental/filament/filament/texture.h" @@ -64,8 +63,8 @@ static std::unique_ptr CreateFallbackIndirectLightTexture( std::unique_ptr asset = object_mgr->LoadAsset(filename); - TextureConfig config; - DefaultTextureConfig(&config); + mjrTextureConfig config; + mjr_defaultTextureConfig(&config); config.width = 1; config.height = 1; config.target = mjTEXTURE_CUBE; @@ -74,9 +73,9 @@ static std::unique_ptr CreateFallbackIndirectLightTexture( auto texture = std::make_unique(object_mgr->GetEngine(), config); - TextureData payload; - DefaultTextureData(&payload); - payload.bytes = (void*)asset->GetBytes().data(); + mjrTextureData payload; + mjr_defaultTextureData(&payload); + payload.bytes = asset->GetBytes().data(); payload.nbytes = asset->GetBytes().size(); payload.release_callback = +[](void* user_data) { delete static_cast(user_data); @@ -87,9 +86,9 @@ static std::unique_ptr CreateFallbackIndirectLightTexture( return texture; } -SceneBridge::SceneBridge(ObjectManager* object_mgr, SceneView* scene_view, - const mjModel* model) - : scene_view_(scene_view), object_mgr_(object_mgr) { +SceneBridge::SceneBridge(ObjectManager* object_mgr, const mjModel* model) + : object_mgr_(object_mgr) { + scene_view_ = std::make_unique(object_mgr_->GetEngine()); model_objects_ = std::make_unique(model, object_mgr_->GetEngine()); @@ -180,7 +179,10 @@ SceneBridge::~SceneBridge() { scene_view_->RemoveFromScene(iter.get()); } lights_.clear(); - + if (fallback_ibl_) { + scene_view_->RemoveFromScene(fallback_ibl_.get()); + } + fallback_ibl_.reset(); for (auto& iter : renderables_) { scene_view_->RemoveFromScene(iter.get()); } @@ -204,7 +206,8 @@ void SceneBridge::SetEnvironmentLight(std::string_view filename, fallback_ibl_texture_ = CreateFallbackIndirectLightTexture(object_mgr_, filename); - Light::Params params; + mjrLightParams params; + mjr_defaultLightParams(¶ms); params.type = mjLIGHT_IMAGE; params.texture = fallback_ibl_texture_.get(); params.intensity = intensity; @@ -230,7 +233,8 @@ void SceneBridge::PrepareLights() { total_light_intensity += model->light_intensity[i]; if (model->light_type[i] == mjLIGHT_IMAGE) { - Light::Params params; + mjrLightParams params; + mjr_defaultLightParams(¶ms); params.type = mjLIGHT_IMAGE; params.texture = model_objects_->GetTexture(model->light_texid[i]); params.intensity = model->light_intensity[i]; @@ -239,11 +243,14 @@ void SceneBridge::PrepareLights() { lights_.emplace_back(std::move(light_obj)); has_image_based_light = true; } else { - Light::Params params; - params.color = ReadFloat3(model->light_diffuse); + mjrLightParams params; + mjr_defaultLightParams(¶ms); + params.color[0] = model->light_diffuse[0]; + params.color[1] = model->light_diffuse[1]; + params.color[2] = model->light_diffuse[2]; params.type = (mjtLightType)model->light_type[i]; - params.castshadow = model->light_castshadow[i]; - params.bulbradius = model->light_bulbradius[i]; + params.cast_shadows = model->light_castshadow[i]; + params.bulb_radius = model->light_bulbradius[i]; params.range = model->light_range[i]; params.intensity = model->light_intensity[i]; params.shadow_map_size = default_shadow_map_size_; @@ -253,10 +260,7 @@ void SceneBridge::PrepareLights() { } auto light_obj = std::make_unique(engine, params); -#ifndef __EMSCRIPTEN__ - // TODO(b/458045799): Re-enable when lights work on glinux and chromebook. scene_view_->AddToScene(light_obj.get()); -#endif lights_.emplace_back(std::move(light_obj)); } } @@ -264,22 +268,19 @@ void SceneBridge::PrepareLights() { // Add a placeholder (black) headlight as our last light. Going forward, we'll // assume lights_.back() is always the headlight. { - Light::Params params; - params.color = float3(0, 0, 0); + mjrLightParams params; + mjr_defaultLightParams(¶ms); // We break with the spec here slightly and use a spot light for the head // light instead of a directional params. This is because filament only // supports a single directional light, and we'd rather allow a scene // light to be that directional params. It's also a bit odd for a // directional light to move with the camera. params.type = mjLIGHT_SPOT; - params.castshadow = 0; + params.cast_shadows = 0; params.intensity = 0.0f; params.spot_cone_angle = 90.0f; auto light_obj = std::make_unique(engine, params); -#ifndef __EMSCRIPTEN__ - // TODO(b/458045799): Re-enable when lights work on glinux and chromebook. scene_view_->AddToScene(light_obj.get()); -#endif lights_.emplace_back(std::move(light_obj)); } @@ -287,7 +288,8 @@ void SceneBridge::PrepareLights() { // Create a black indirect light to ensure that the skybox is // oriented to respect mujoco's Z-up convention. filament::Engine* engine = object_mgr_->GetEngine(); - Light::Params params; + mjrLightParams params; + mjr_defaultLightParams(¶ms); params.type = mjLIGHT_IMAGE; params.intensity = 10.0f; fallback_ibl_ = std::make_unique(engine, params); @@ -301,7 +303,8 @@ void SceneBridge::PrepareLights() { // Create a fallback environment light. fallback_ibl_texture_ = CreateFallbackIndirectLightTexture(object_mgr_); - Light::Params params; + mjrLightParams params; + mjr_defaultLightParams(¶ms); params.type = mjLIGHT_IMAGE; params.texture = fallback_ibl_texture_.get(); params.intensity = fallback_environment_light_intensity_; @@ -354,8 +357,16 @@ filament::math::mat4 CalculateClipFromWorld(const mjrRect& viewport, } void SceneBridge::Update(const mjrRect& viewport, const mjvScene* scene) { - filament::View* view = scene_view_->GetDefaultRenderView(); - view->setShadowingEnabled(scene->flags[mjRND_SHADOW] ? true : false); + if (scene->flags[mjRND_SHADOW]) { + scene_view_->EnableShadows(); + } else { + scene_view_->DisableShadows(); + } + if (scene->flags[mjRND_REFLECTION]) { + scene_view_->EnableReflections(); + } else { + scene_view_->DisableReflections(); + } mjtNum hpos[3], hfwd[3]; float headpos[3], gazedir[3]; diff --git a/src/experimental/filament/filament/scene_bridge.h b/src/experimental/filament/compat/scene_bridge.h similarity index 84% rename from src/experimental/filament/filament/scene_bridge.h rename to src/experimental/filament/compat/scene_bridge.h index e707d6ed..96e8ab31 100644 --- a/src/experimental/filament/filament/scene_bridge.h +++ b/src/experimental/filament/compat/scene_bridge.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_BRIDGE_H_ -#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_BRIDGE_H_ +#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_BRIDGE_H_ +#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_BRIDGE_H_ #include #include @@ -24,9 +24,8 @@ #include #include #include +#include "experimental/filament/compat/model_objects.h" #include "experimental/filament/filament/light.h" -#include "experimental/filament/filament/material.h" -#include "experimental/filament/filament/model_objects.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/renderable.h" #include "experimental/filament/filament/scene_view.h" @@ -37,8 +36,7 @@ namespace mujoco { // Manages all mjModel data and updates a SceneView using an mjvScene. class SceneBridge { public: - SceneBridge(ObjectManager* object_mgr, SceneView* scene_view, - const mjModel* model); + SceneBridge(ObjectManager* object_mgr, const mjModel* model); ~SceneBridge(); // Updates the environment light using the KTX image at the given path. @@ -56,7 +54,8 @@ class SceneBridge { void UploadTexture(const mjModel* model, int id); void UploadHeightField(const mjModel* model, int id); - SceneView* GetSceneView() const { return scene_view_; } + // Returns the managed scene. + SceneView* GetSceneView() const { return scene_view_.get(); } SceneBridge(const SceneBridge&) = delete; SceneBridge& operator=(const SceneBridge&) = delete; @@ -69,7 +68,7 @@ class SceneBridge { std::optional ClipFromWorld( const filament::math::float3& pos) const; - SceneView* scene_view_ = nullptr; + std::unique_ptr scene_view_; ObjectManager* object_mgr_ = nullptr; std::unique_ptr model_objects_; std::unique_ptr fallback_ibl_; @@ -86,4 +85,4 @@ class SceneBridge { } // namespace mujoco -#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_BRIDGE_H_ +#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_BRIDGE_H_ diff --git a/src/experimental/filament/filament/scene_geom_util.cc b/src/experimental/filament/compat/scene_geom_util.cc similarity index 90% rename from src/experimental/filament/filament/scene_geom_util.cc rename to src/experimental/filament/compat/scene_geom_util.cc index 80c653a7..527fe56f 100644 --- a/src/experimental/filament/filament/scene_geom_util.cc +++ b/src/experimental/filament/compat/scene_geom_util.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "experimental/filament/filament/scene_geom_util.h" +#include "experimental/filament/compat/scene_geom_util.h" #include #include @@ -22,20 +22,17 @@ #include #include -#include #include -#include #include #include #include #include -#include #include #include +#include "experimental/filament/compat/model_objects.h" #include "experimental/filament/filament/material.h" #include "experimental/filament/filament/math_util.h" #include "experimental/filament/filament/mesh.h" -#include "experimental/filament/filament/model_objects.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/renderable.h" #include "experimental/filament/filament/texture.h" @@ -351,9 +348,12 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, const mjModel* model = model_objs->GetModel(); const bool use_segid_color = scene->flags[mjRND_IDCOLOR]; - const bool enable_reflection = scene->flags[mjRND_REFLECTION]; - MaterialParams params; - params.color = ReadFloat4(geom.rgba); + mjrMaterialParams params; + mjr_defaultMaterialParams(¶ms); + params.color[0] = geom.rgba[0]; + params.color[1] = geom.rgba[1]; + params.color[2] = geom.rgba[2]; + params.color[3] = geom.rgba[3]; if (geom.type == mjGEOM_PLANE) { if (IsBehind(headpos, geom.pos, geom.mat)) { params.color[3] *= 0.3; @@ -361,8 +361,7 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, params.reflective = false; } else { renderable.SetReceiveShadows(true); - params.reflective = - enable_reflection && geom.reflectance > 0 && params.color.a == 1.0f; + params.reflective = geom.reflectance > 0 && params.color[3] == 1.0f; } } renderable.SetLayerMask(geom.category); @@ -373,7 +372,8 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, renderable.SetWireframe(scene->flags[mjRND_WIREFRAME]); } - MaterialTextures textures; + mjrMaterialTextures textures; + mjr_defaultMaterialTextures(&textures); if (geom.matid >= 0) { textures.color = model_objs->GetTexture(geom.matid, mjTEXROLE_RGB); textures.normal = model_objs->GetTexture(geom.matid, mjTEXROLE_NORMAL); @@ -394,7 +394,8 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, params.metallic = model->mat_metallic[geom.matid]; params.roughness = model->mat_roughness[geom.matid]; params.tex_uniform = model->mat_texuniform[geom.matid]; - params.tex_repeat = ReadFloat2(model->mat_texrepeat, geom.matid); + params.tex_repeat[0] = model->mat_texrepeat[(geom.matid * 2) + 0]; + params.tex_repeat[1] = model->mat_texrepeat[(geom.matid * 2) + 1]; } if (geom.segid >= 0) { @@ -410,9 +411,9 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, const uint8_t red = (segmentation_color >> 0) & 0xff; const uint8_t green = (segmentation_color >> 8) & 0xff; const uint8_t blue = (segmentation_color >> 16) & 0xff; - params.segmentation_color.x = static_cast(red) / 255.0f; - params.segmentation_color.y = static_cast(green) / 255.0f; - params.segmentation_color.z = static_cast(blue) / 255.0f; + params.segmentation_color[0] = static_cast(red) / 255.0f; + params.segmentation_color[1] = static_cast(green) / 255.0f; + params.segmentation_color[2] = static_cast(blue) / 255.0f; } // UvScale only applies to objects that don't have explicit UV coordinates @@ -428,23 +429,23 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, // For 2D textures, `tex_repeat` specifies how many times the texture // image is repeated. The `tex_uniform` flag determines if the repetition // is applied at in object space (false) or in world space (true). - params.uv_scale.x = params.tex_repeat.x; - params.uv_scale.y = params.tex_repeat.y; + params.uv_scale[0] = params.tex_repeat[0]; + params.uv_scale[1] = params.tex_repeat[1]; if (geom.dataid >= 0 && geom.type != mjGEOM_PLANE) { if (geom.size[0] > mjMINVAL) { - params.uv_scale.x /= geom.size[0]; + params.uv_scale[0] /= geom.size[0]; } if (geom.size[1] > mjMINVAL) { - params.uv_scale.y /= geom.size[1]; + params.uv_scale[1] /= geom.size[1]; } } if (params.tex_uniform) { if (geom.size[0] > 0) { - params.uv_scale.x *= geom.size[0]; + params.uv_scale[0] *= geom.size[0]; } if (geom.size[1] > 0) { - params.uv_scale.y *= geom.size[1]; + params.uv_scale[1] *= geom.size[1]; } } const bool is_infinite_plane = @@ -454,11 +455,11 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, // re-centering in engine_vis_visualize.c. const float plane_scale = static_cast(mjMAXPLANEGRID) / 2.0f; const float tile_size_x = - GetPlaneTileSize(model, geom.matid, params.tex_repeat.x); + GetPlaneTileSize(model, geom.matid, params.tex_repeat[0]); const float tile_size_y = - GetPlaneTileSize(model, geom.matid, params.tex_repeat.y); - params.uv_scale.x = 2.0f * plane_scale / tile_size_x; - params.uv_scale.y = 2.0f * plane_scale / tile_size_y; + GetPlaneTileSize(model, geom.matid, params.tex_repeat[1]); + params.uv_scale[0] = 2.0f * plane_scale / tile_size_x; + params.uv_scale[1] = 2.0f * plane_scale / tile_size_y; } // We want to do the equivalent of: @@ -466,17 +467,17 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, // mjr_setf4(tplane, 0, -0.5 * scl.y, 0, -0.5); // glTexGenfv(GL_S, GL_OBJECT_PLANE, splane); // glTexGenfv(GL_T, GL_OBJECT_PLANE, tplane); - params.uv_scale.x = 0.5f * params.uv_scale.x; - params.uv_scale.y = -0.5f * params.uv_scale.y; - params.uv_offset.x = -0.5f; - params.uv_offset.y = -0.5f; + params.uv_scale[0] = 0.5f * params.uv_scale[0]; + params.uv_scale[1] = -0.5f * params.uv_scale[1]; + params.uv_offset[0] = -0.5f; + params.uv_offset[1] = -0.5f; } else { // For cube maps, if `tex_uniform` is true, then scale the texture so that // it covers a 1x1 area of world space rather than the area of the object. if (params.tex_uniform) { - params.uv_scale.x = 1.0f / (geom.size[0] ? geom.size[0] : 1.0f); - params.uv_scale.y = 1.0f / (geom.size[1] ? geom.size[1] : 1.0f); - params.uv_scale.z = 1.0f / (geom.size[2] ? geom.size[2] : 1.0f); + params.uv_scale[0] = 1.0f / (geom.size[0] ? geom.size[0] : 1.0f); + params.uv_scale[1] = 1.0f / (geom.size[1] ? geom.size[1] : 1.0f); + params.uv_scale[2] = 1.0f / (geom.size[2] ? geom.size[2] : 1.0f); } } } @@ -492,17 +493,17 @@ static void UpdateGeomMaterial(Renderable& renderable, const mjvGeom& geom, std::unique_ptr CreateGeomRenderable( const mjvGeom& geom, const mjvScene* scene, ObjectManager* object_mgr, ModelObjects* model_objs, const float headpos[3]) { - ShadingModel shading_model = ShadingModel::SceneObject; + mjrShadingModel shading_model = mjSHADING_MODEL_SCENE_OBJECT; if (geom.type == mjGEOM_LINE || geom.type == mjGEOM_LINEBOX) { - shading_model = ShadingModel::DecorLines; + shading_model = mjSHADING_MODEL_DECOR_LINES; } else if (geom.category == mjCAT_DECOR) { - shading_model = ShadingModel::Decor; + shading_model = mjSHADING_MODEL_DECOR; } - RenderableParams config; - DefaultRenderableParams(&config); - config.shading_model = shading_model; - auto renderable = std::make_unique(object_mgr, config); + mjrRenderableParams params; + mjr_defaultRenderableParams(¶ms); + params.shading_model = shading_model; + auto renderable = std::make_unique(object_mgr, params); PrepareGeomMeshes(*renderable, geom, scene, model_objs); UpdateGeomMaterial(*renderable, geom, scene, model_objs, object_mgr, headpos); diff --git a/src/experimental/filament/filament/scene_geom_util.h b/src/experimental/filament/compat/scene_geom_util.h similarity index 76% rename from src/experimental/filament/filament/scene_geom_util.h rename to src/experimental/filament/compat/scene_geom_util.h index deef9c58..c702f687 100644 --- a/src/experimental/filament/filament/scene_geom_util.h +++ b/src/experimental/filament/compat/scene_geom_util.h @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_GEOM_UTIL_H_ -#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_GEOM_UTIL_H_ +#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_GEOM_UTIL_H_ +#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_GEOM_UTIL_H_ #include #include -#include "experimental/filament/filament/material.h" -#include "experimental/filament/filament/model_objects.h" +#include "experimental/filament/compat/model_objects.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/renderable.h" @@ -32,4 +31,4 @@ std::unique_ptr CreateGeomRenderable( } // namespace mujoco -#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_GEOM_UTIL_H_ +#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_GEOM_UTIL_H_ diff --git a/src/experimental/filament/filament/builtins.cc b/src/experimental/filament/filament/builtins.cc index 86a6a5c5..f438fa39 100644 --- a/src/experimental/filament/filament/builtins.cc +++ b/src/experimental/filament/filament/builtins.cc @@ -59,16 +59,16 @@ static std::size_t NumIndicesPerSide(int num_quads_per_axis) { return kNumIndicesPerQuad * num_quads_per_axis * num_quads_per_axis; } -class BuiltinBuilder : MeshData { +class BuiltinBuilder : mjrMeshData { public: - BuiltinBuilder() { DefaultMeshData(this); } + BuiltinBuilder() { mjr_defaultMeshData(this); } virtual ~BuiltinBuilder() = default; template static std::unique_ptr Create(filament::Engine* engine, Args&&... args) { auto builder = new T(std::forward(args)...); - MeshData* mesh_data = builder->PrepareMeshData(); + mjrMeshData* mesh_data = builder->PrepareMeshData(); mesh_data->release_callback = +[](void* user_data) { delete static_cast(user_data); }; @@ -76,13 +76,13 @@ class BuiltinBuilder : MeshData { return std::make_unique(engine, *mesh_data); } - MeshData* PrepareMeshData() { - // Update the `MeshData` fields. + mjrMeshData* PrepareMeshData() { + // Update the `mjrMeshData` fields. nattributes = 2; - attributes[0].usage = mjVERTEX_ATTRIBUTE_POSITION; + attributes[0].usage = mjVERTEX_ATTRIBUTE_USAGE_POSITION; attributes[0].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT3; attributes[0].bytes = reinterpret_cast(positions_.data()); - attributes[1].usage = mjVERTEX_ATTRIBUTE_TANGENTS; + attributes[1].usage = mjVERTEX_ATTRIBUTE_USAGE_TANGENTS; attributes[1].type = mjVERTEX_ATTRIBUTE_TYPE_FLOAT4; attributes[1].bytes = reinterpret_cast(orientations_.data()); nvertices = positions_.size(); @@ -91,9 +91,9 @@ class BuiltinBuilder : MeshData { nindices = indices_.size(); primitive_type = primitive_type_ == filament::backend::PrimitiveType::TRIANGLES - ? mjPRIM_TYPE_TRIANGLES - : mjPRIM_TYPE_LINES; - index_type = mjINDEX_TYPE_USHORT; + ? mjMESH_PRIMITIVE_TYPE_TRIANGLES + : mjMESH_PRIMITIVE_TYPE_LINES; + index_type = mjINDEX_TYPE_U16; bounds_min[0] = bounds_.getMin().x; bounds_min[1] = bounds_.getMin().y; bounds_min[2] = bounds_.getMin().z; diff --git a/src/experimental/filament/filament/filament_context.cc b/src/experimental/filament/filament/filament_context.cc index 427306b8..e367b44f 100644 --- a/src/experimental/filament/filament/filament_context.cc +++ b/src/experimental/filament/filament/filament_context.cc @@ -14,13 +14,11 @@ #include "experimental/filament/filament/filament_context.h" -#include #include -#include #include +#include #include -#include #include #include #include @@ -35,19 +33,11 @@ #include #include #include -#include -#include #include -#include "experimental/filament/filament/draw_mode.h" #include "experimental/filament/filament/filament_platform_factory.h" -#include "experimental/filament/filament/imgui_bridge.h" -#include "experimental/filament/filament/imgui_editor.h" -#include "experimental/filament/filament/model_util.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/render_target.h" -#include "experimental/filament/filament/scene_bridge.h" #include "experimental/filament/filament/scene_view.h" -#include "experimental/filament/filament/texture.h" #include "experimental/filament/render_context_filament.h" namespace mujoco { @@ -82,10 +72,6 @@ FilamentContext::FilamentContext(const mjrFilamentConfig* config) } FilamentContext::~FilamentContext() { - DestroyRenderTargets(); - imgui_bridge_.reset(); - scene_bridge_.reset(); - scene_view_.reset(); object_manager_.reset(); engine_->destroy(renderer_); engine_->destroy(window_swap_chain_); @@ -93,209 +79,116 @@ FilamentContext::~FilamentContext() { filament::Engine::destroy(engine_); } -void FilamentContext::Init(const mjModel* model) { - scene_view_ = std::make_unique(engine_); - scene_bridge_ = std::make_unique(object_manager_.get(), - scene_view_.get(), model); - imgui_bridge_ = - std::make_unique(object_manager_.get(), scene_view_.get()); +FilamentContext::FrameHandle FilamentContext::Render( + std::span requests, + std::span read_requests) { + if (read_requests.size() > 1) { + mju_error("Only one read request is supported for now."); + } - // Set clear options. + bool render_began = false; + RenderTarget* current_target = nullptr; + for (const RenderRequest& request : requests) { + if (request.target != current_target && render_began) { + renderer_->endFrame(); + render_began = false; + } + current_target = request.target; + + if (current_target == nullptr) { + if (!read_requests.empty()) { + mju_error("Cannot read pixels from the window."); + } + + if constexpr (UTILS_HAS_THREADING) { + // Wait until previous frame is completed before requesting a new frame. + engine_->flushAndWait(); + } + + // If the window size has changed, we need to reacquire the swap chain. + if (request.width != window_width_ || request.height != window_height_) { + if (window_width_ != 0 && window_height_ != 0) { + engine_->destroy(window_swap_chain_); + window_swap_chain_ = engine_->createSwapChain(config_.native_window); + } + window_width_ = request.width; + window_height_ = request.height; + } + + if (!render_began) { + render_began = renderer_->beginFrame(window_swap_chain_); + } + if (!render_began) { + break; + } + if (render_began) { + SceneView::RenderRequest scene_view_request; + scene_view_request.draw_mode = request.draw_mode; + scene_view_request.viewport = {0, 0, request.width, request.height}; + scene_view_request.camera = request.camera; + request.scene->Render(renderer_, scene_view_request); + } + } else { + if (read_requests.empty()) { + mju_error( + "Rendering to a render target without a read request is pointless."); + } + + const ReadPixelsRequest& read_request = read_requests[0]; + if (read_request.num_bytes == 0) { + mju_error("Output buffer size is zero."); + } + + if (!render_began) { + render_began = renderer_->beginFrame(offscreen_swap_chain_); + } + if (!render_began) { + break; + } + if (render_began) { + SceneView::RenderRequest scene_view_request; + scene_view_request.draw_mode = request.draw_mode; + scene_view_request.viewport = {0, 0, request.width, request.height}; + scene_view_request.camera = request.camera; + scene_view_request.target = request.target; + request.scene->Render(renderer_, scene_view_request); + request.target->ReadColorPixels(renderer_, read_request.output, + read_request.num_bytes); + } + } + } + + if (render_began) { + renderer_->endFrame(); + } + if constexpr (!UTILS_HAS_THREADING) { + engine_->execute(); + } + + if (!read_requests.empty()) { + engine_->flushAndWait(); + if (read_requests[0].read_completed_callback) { + read_requests[0].read_completed_callback(read_requests[0].user_data); + } + } + + return ++frame_counter_; +} + +void FilamentContext::WaitForFrame(FrameHandle frame_handle) { + if (frame_counter_ < frame_handle) { + engine_->flushAndWait(); + } +} + +void FilamentContext::SetClearColor(const filament::math::float4& color) { filament::Renderer::ClearOptions opts; opts.clear = true; opts.discard = true; - opts.clearColor = ReadElement(model, "filament.clearColor", - filament::math::float4(0, 0, 0, 1)); + opts.clearColor = color; renderer_->setClearOptions(opts); } -void FilamentContext::Render(const mjrRect& viewport, const mjvScene* scene) { - // If we're rendering to the window, and the window size has changed, we need - // to reacquire the swap chain. - if (scene_swap_chain_target_ == kWindowSwapChain && - (viewport.width != window_width_ || viewport.height != window_height_)) { - if (window_width_ != 0 && window_height_ != 0) { - if constexpr (UTILS_HAS_THREADING) { - engine_->flushAndWait(); - } - engine_->destroy(window_swap_chain_); - window_swap_chain_ = engine_->createSwapChain(config_.native_window); - } - window_width_ = viewport.width; - window_height_ = viewport.height; - } - - scene_bridge_->Update(viewport, scene); - // Update the UX renderable entity after processing the scene in case there - // are any elements in the scene which generate UX draw calls (e.g. labels). - if (imgui_bridge_ && gui_swap_chain_target_ == scene_swap_chain_target_) { - // Prepare the filament Renderable that contains the GUI draw commands. We - // must call this function even if we do not plan on rendering the GUI to - // ensure the ImGui state is updated. - imgui_bridge_->Update(); - } - - last_render_mode_ = DrawMode::Color; - if (scene->flags[mjRND_SEGMENT]) { - last_render_mode_ = DrawMode::Segmentation; - } else if (scene->flags[mjRND_DEPTH]) { - last_render_mode_ = DrawMode::Depth; - } - last_camera_ = mjv_averageCamera(scene->camera, scene->camera + 1); - - // Render the frame if we're not rendering to a texture. - if (scene_swap_chain_target_ == kWindowSwapChain) { - if constexpr (UTILS_HAS_THREADING) { - // Wait until previous frame is completed before requesting a new frame. - engine_->flushAndWait(); - } - - if (renderer_->beginFrame(window_swap_chain_)) { - SceneView::RenderRequest request; - request.draw_mode = last_render_mode_; - request.viewport = viewport; - request.camera = last_camera_; - request.enable_ux = (gui_swap_chain_target_ == kWindowSwapChain); - scene_view_->Render(renderer_, request); - renderer_->endFrame(); - } - - if constexpr (!UTILS_HAS_THREADING) { - engine_->execute(); - } - } -} - -void FilamentContext::SetFrameBuffer(int framebuffer) { - switch (framebuffer) { - case mjFB_WINDOW: - scene_swap_chain_target_ = kWindowSwapChain; - gui_swap_chain_target_ = kWindowSwapChain; - break; - case mjFB_OFFSCREEN: - scene_swap_chain_target_ = kOffscreenSwapChain; - gui_swap_chain_target_ = kWindowSwapChain; - break; - case 2: // No official constant fo this. - scene_swap_chain_target_ = kOffscreenSwapChain; - gui_swap_chain_target_ = kOffscreenSwapChain; - break; - default: - mju_error("Invalid framebuffer mode: %d", framebuffer); - } - - if (framebuffer == 0) { - DestroyRenderTargets(); - } -} - -void FilamentContext::PrepareRenderTargets(int width, int height) { - RenderTargetConfig config; - DefaultRenderTargetConfig(&config); - - config.color_format = mjPIXEL_FORMAT_RGB8; - config.depth_format = mjPIXEL_FORMAT_DEPTH32F; - color_target_ = std::make_unique(engine_, config); - color_target_->Prepare(width, height); - - config.color_format = mjPIXEL_FORMAT_R32F; - config.depth_format = mjPIXEL_FORMAT_DEPTH32F; - depth_target_ = std::make_unique(engine_, config); - depth_target_->Prepare(width, height); -} - -void FilamentContext::DestroyRenderTargets() { - depth_target_.reset(); - color_target_.reset(); -} - -void FilamentContext::ReadPixels(mjrRect viewport, unsigned char* rgb, - float* depth) { - if (scene_swap_chain_target_ != kOffscreenSwapChain) { - mju_error("Cannot read pixels unless framebuffer is set."); - } - if (color_target_ == nullptr || depth_target_ == nullptr) { - if (viewport.left != 0) { - mju_error("Reading subpixels not supported."); - } - if (viewport.bottom != 0) { - mju_error("Reading subpixels not supported."); - } - PrepareRenderTargets(viewport.width, viewport.height); - } - - if (rgb) { - if (renderer_->beginFrame(offscreen_swap_chain_)) { - SceneView::RenderRequest request; - request.draw_mode = last_render_mode_; - request.viewport = viewport; - request.target = color_target_.get(); - request.camera = last_camera_; - request.enable_ux = (gui_swap_chain_target_ == kOffscreenSwapChain); - scene_view_->Render(renderer_, request); - - const size_t num_bytes = viewport.width * viewport.height * 3; - color_target_->ReadColorPixels(renderer_, rgb, num_bytes); - - renderer_->endFrame(); - } - } - - if (depth) { - if (renderer_->beginFrame(offscreen_swap_chain_)) { - SceneView::RenderRequest request; - request.draw_mode = DrawMode::Depth; - request.viewport = viewport; - request.target = depth_target_.get(); - request.camera = last_camera_; - scene_view_->Render(renderer_, request); - - const size_t num_bytes = viewport.width * viewport.height * sizeof(float); - depth_target_->ReadColorPixels( - renderer_, reinterpret_cast(depth), num_bytes); - - renderer_->endFrame(); - } - } - - if (rgb || depth) { - if constexpr (UTILS_HAS_THREADING) { - // Wait for rendering to copy back to buffer to complete. - engine_->flushAndWait(); - } - } -} - -void FilamentContext::UploadMesh(const mjModel* model, int id) { - if (!scene_bridge_) { - mju_error("SceneBridge is not initialized."); - } - scene_bridge_->UploadMesh(model, id); -} - -void FilamentContext::UploadTexture(const mjModel* model, int id) { - if (!scene_bridge_) { - mju_error("SceneBridge is not initialized."); - } - scene_bridge_->UploadTexture(model, id); -} - -void FilamentContext::UploadHeightField(const mjModel* model, int id) { - if (!scene_bridge_) { - mju_error("SceneBridge is not initialized."); - } - scene_bridge_->UploadHeightField(model, id); -} - -uintptr_t FilamentContext::UploadGuiImage(uintptr_t tex_id, - const uint8_t* pixels, int width, - int height, int bpp) { - if (imgui_bridge_) { - return imgui_bridge_->UploadImage(tex_id, pixels, width, height, bpp); - } - return 0; -} - double FilamentContext::GetFrameRate() const { utils::FixedCapacityVector frame_info = renderer_->getFrameInfoHistory(1); @@ -306,6 +199,4 @@ double FilamentContext::GetFrameRate() const { return 1.0e9 / static_cast(ns); } -void FilamentContext::UpdateGui() { DrawGui(scene_bridge_.get()); } - } // namespace mujoco diff --git a/src/experimental/filament/filament/filament_context.h b/src/experimental/filament/filament/filament_context.h index 3f4eeb7f..391e9818 100644 --- a/src/experimental/filament/filament/filament_context.h +++ b/src/experimental/filament/filament/filament_context.h @@ -15,64 +15,102 @@ #ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_FILAMENT_CONTEXT_H_ #define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_FILAMENT_CONTEXT_H_ +#include #include +#include #include #include #include #include -#include -#include +#include +#include #include #include "experimental/filament/filament/draw_mode.h" -#include "experimental/filament/filament/imgui_bridge.h" +#include "experimental/filament/filament/scene_view.h" #include "experimental/filament/filament/object_manager.h" #include "experimental/filament/filament/render_target.h" -#include "experimental/filament/filament/scene_bridge.h" -#include "experimental/filament/filament/scene_view.h" #include "experimental/filament/render_context_filament.h" namespace mujoco { -// Manages the filament renderer that is exposed via the mjr functions. +// Manages the filament renderer and provides APIs for rendering scenes. class FilamentContext { public: explicit FilamentContext(const mjrFilamentConfig* config); ~FilamentContext(); - void Init(const mjModel* model); + // Information needed to render a single image of a scene. + struct RenderRequest { + // The scene to render. + SceneView* scene = nullptr; - void Render(const mjrRect& viewport, const mjvScene* scene); + // The method (e.g. Color, Depth, Segmentation, etc.) to use for rendering. + DrawMode draw_mode = DrawMode::Color; - void SetFrameBuffer(int framebuffer); + // The camera from which to render the scene. + mjvGLCamera camera; - void ReadPixels(mjrRect viewport, unsigned char* rgb, float* depth); + // The dimensions of the output image. + int width = 0; + int height = 0; - void UploadMesh(const mjModel* model, int id); + // The render target into which to render the image. If nullptr, the image + // will be rendered to the window (as previously configured in + // mjrFilamentConfig::native_window). + RenderTarget* target = nullptr; + }; - void UploadTexture(const mjModel* model, int id); + // Information needed to read pixels from a render target. + struct ReadPixelsRequest { + RenderTarget* target = nullptr; - void UploadHeightField(const mjModel* model, int id); + // The buffer into which the read pixels will be written. + uint8_t* output = nullptr; - uintptr_t UploadGuiImage(uintptr_t tex_id, const uint8_t* pixels, int width, - int height, int bpp); + // The number of bytes in the output buffer. This should match the size of + // the render target texture. + std::size_t num_bytes = 0; + // Callback when the read pixels operation is complete. This will be called + // during WaitForFrame() or in a subsequent call to Render(). This function + // can optionally be used to free the output buffer if needed. + void (*read_completed_callback)(void* user_data) = nullptr; + + // User data to pass to the completion callback. + void* user_data = nullptr; + }; + + // Rendering is asynchronous by nature. Each render request is assigned a + // unique Handle which can be used to query the status of the request. The + // Handle can also be used to block until the request is completed. + using FrameHandle = std::uint64_t; + + // Queues the given render requests for rendering. This function copies the + // necessary data from the requests into the renderer thread and returns + // immediately afterwards. The renderer thread will then perform the actual + // rendering on the GPU. Callers can use WaitForFrame to block until the + // rendering is complete. + FrameHandle Render(std::span render_requests, + std::span read_requests = {}); + + // Blocks until the given frame has completed rendering. + void WaitForFrame(FrameHandle frame_handle); + + // Sets the clear color for the renderer. + void SetClearColor(const filament::math::float4& color); + + // Returns the current frame rate of the renderer. double GetFrameRate() const; - void UpdateGui(); + filament::Engine* GetEngine() const { return engine_; } + + ObjectManager* GetObjectManager() const { return object_manager_.get(); } FilamentContext(const FilamentContext&) = delete; FilamentContext& operator=(const FilamentContext&) = delete; private: - enum SwapChainType { - kWindowSwapChain, - kOffscreenSwapChain, - }; - - void PrepareRenderTargets(int width, int height); - void DestroyRenderTargets(); - mjrFilamentConfig config_; filament::Engine* engine_ = nullptr; @@ -80,19 +118,10 @@ class FilamentContext { filament::SwapChain* window_swap_chain_ = nullptr; filament::SwapChain* offscreen_swap_chain_ = nullptr; std::unique_ptr platform_; - - DrawMode last_render_mode_ = DrawMode::Color; - mjvGLCamera last_camera_; - SwapChainType scene_swap_chain_target_ = kWindowSwapChain; - SwapChainType gui_swap_chain_target_ = kWindowSwapChain; - std::unique_ptr color_target_; - std::unique_ptr depth_target_; std::unique_ptr object_manager_; - std::unique_ptr scene_view_; - std::unique_ptr scene_bridge_; - std::unique_ptr imgui_bridge_; int window_width_ = 0; int window_height_ = 0; + std::uint64_t frame_counter_ = 0; }; } // namespace mujoco diff --git a/src/experimental/filament/filament/filament_platform_factory.cc b/src/experimental/filament/filament/filament_platform_factory.cc index 4b5483fc..a7dc9709 100644 --- a/src/experimental/filament/filament/filament_platform_factory.cc +++ b/src/experimental/filament/filament/filament_platform_factory.cc @@ -31,13 +31,13 @@ static filament::Engine::Backend ResolveBackend(int graphics_api) { #endif switch (graphics_api) { - case mjGFX_DEFAULT: + case mjGRAPHICS_API_DEFAULT: // Use the default based on the platform above. break; - case mjGFX_OPENGL: + case mjGRAPHICS_API_OPENGL: backend = filament::Engine::Backend::OPENGL; break; - case mjGFX_VULKAN: + case mjGRAPHICS_API_VULKAN: backend = filament::Engine::Backend::VULKAN; break; default: diff --git a/src/experimental/filament/filament/light.cc b/src/experimental/filament/filament/light.cc index d00f6097..bdc1f2cd 100644 --- a/src/experimental/filament/filament/light.cc +++ b/src/experimental/filament/filament/light.cc @@ -25,6 +25,7 @@ #include #include #include +#include "experimental/filament/filament/math_util.h" #include "experimental/filament/filament/texture.h" namespace mujoco { @@ -32,7 +33,22 @@ namespace mujoco { using filament::math::float3; using filament::math::mat3f; -Light::Light(filament::Engine* engine, const Params& params) +void mjr_defaultLightParams(mjrLightParams* params) { + params->type = mjLIGHT_POINT; + params->texture = nullptr; + params->color[0] = 0; + params->color[1] = 0; + params->color[2] = 0; + params->intensity = 0.0f; + params->cast_shadows = true; + params->range = 10.0f; + params->spot_cone_angle = 180.f; + params->bulb_radius = 0.0f; + params->shadow_map_size = 2048; + params->vsm_blur_width = 0.0f; +} + +Light::Light(filament::Engine* engine, const mjrLightParams& params) : engine_(engine), params_(params) { // Filament treats image-based lights (IBLs) as separate objects (i.e. // filament::IndirectLight) and so we need to handle IBLs specially. @@ -71,9 +87,9 @@ Light::Light(filament::Engine* engine, const Params& params) } filament::LightManager::Builder builder(type); - builder.color(params.color); + builder.color(ReadFloat3(params.color)); builder.intensityCandela(params.intensity); - builder.castShadows(params.castshadow); + builder.castShadows(params.cast_shadows); if (type == filament::LightManager::Type::FOCUSED_SPOT) { builder.spotLightCone(0, params.spot_cone_angle * std::numbers::pi / 180.0f); @@ -85,7 +101,7 @@ Light::Light(filament::Engine* engine, const Params& params) opts.mapSize = 4096; opts.shadowCascades = type == filament::LightManager::Type::DIRECTIONAL ? 4 : 1; - opts.shadowBulbRadius = params.bulbradius; + opts.shadowBulbRadius = params.bulb_radius; opts.mapSize = params.shadow_map_size; if (params.vsm_blur_width > 0.0f) { opts.vsm.elvsm = true; @@ -141,7 +157,10 @@ void Light::SetTransform(filament::math::float3 position, void Light::SetColor(const filament::math::float3& color) { if (!ibl_) { - params_.color = color; + params_.color[0] = color.r; + params_.color[1] = color.g; + params_.color[2] = color.b; + filament::LightManager& lm = engine_->getLightManager(); const filament::LightManager::Instance li = lm.getInstance(entity_); lm.setColor(li, color); diff --git a/src/experimental/filament/filament/light.h b/src/experimental/filament/filament/light.h index 93974972..93ad0bb8 100644 --- a/src/experimental/filament/filament/light.h +++ b/src/experimental/filament/filament/light.h @@ -24,34 +24,38 @@ namespace mujoco { +typedef mjtLightType mjrLightType; + +// Configuration parameters for a light. +struct mjrLightParams { + // The type of light (e.g. spot, point, directional, etc.) + mjrLightType type; + // The texture to use for image lights. + const Texture* texture; + // The color of the light. + float color[3]; + // The intensity of the light, in candela. + float intensity; + // Whether or not the light casts shadows. + mjtByte cast_shadows; + // The range/distance in which the light is effective, in meters. + float range; + // The angle of the spot light cone, in degrees. + float spot_cone_angle; + // The radius of the bulb used for soft shadows. + float bulb_radius; + // The size of the shadow map. + int shadow_map_size; + // Blur width for EL VSM. + float vsm_blur_width; +}; + +void mjr_defaultLightParams(mjrLightParams* params); + // Manages the filament Entities for a single mjvLight. class Light { public: - // Configuration parameters for a light. - struct Params { - // The type of light (e.g. spot, point, directional, etc.) - mjtLightType type; - // The texture to use for image lights. - const Texture* texture = nullptr; - // The color of the light. - filament::math::float3 color = {0, 0, 0}; - // The intensity of the light, in candela. - float intensity = 0.0f; - // Whether or not the light casts shadows. - bool castshadow = true; - // The range/distance in which the light is effective, in meters. - float range = 10.0f; - // The angle of the spot light cone, in degrees. - float spot_cone_angle = 180.f; - // The radius of the bulb used for soft shadows. - float bulbradius = 0.0f; - // The size of the shadow map. - int shadow_map_size = 2048; - // Blur width for EL VSM. - float vsm_blur_width = 0.0f; - }; - - Light(filament::Engine* engine, const Params& params); + Light(filament::Engine* engine, const mjrLightParams& params); ~Light() noexcept; Light(const Light&) = delete; @@ -85,7 +89,7 @@ class Light { filament::IndirectLight* ibl_ = nullptr; utils::Entity entity_; bool enabled_ = true; - Params params_; + mjrLightParams params_; }; } // namespace mujoco diff --git a/src/experimental/filament/filament/material.cc b/src/experimental/filament/filament/material.cc index 6522bd31..fcdc54fa 100644 --- a/src/experimental/filament/filament/material.cc +++ b/src/experimental/filament/filament/material.cc @@ -14,20 +14,59 @@ #include "experimental/filament/filament/material.h" +#include + #include #include #include #include #include #include +#include "experimental/filament/filament/math_util.h" #include "experimental/filament/filament/texture.h" #include "experimental/filament/filament/object_manager.h" namespace mujoco { +template +static void setf(float (&arr)[N], const std::array& values) { + for (int i = 0; i < N; ++i) { + arr[i] = values[i]; + } +} + +void mjr_defaultMaterialTextures(mjrMaterialTextures* textures) { + textures->color = nullptr; + textures->normal = nullptr; + textures->metallic = nullptr; + textures->roughness = nullptr; + textures->occlusion = nullptr; + textures->orm = nullptr; + textures->emissive = nullptr; + textures->reflection = nullptr; +} + +void mjr_defaultMaterialParams(mjrMaterialParams* params) { + setf(params->color, {1.f, 1.f, 1.f, 1.f}); + setf(params->segmentation_color, {1, 1, 1, 1}); + setf(params->uv_scale, {1, 1, 1}); + setf(params->uv_offset, {0, 0, 0}); + setf(params->scissor, {0, 0, 0, 0}); + + params->emissive = -1.0f; + params->specular = -1.0f; + params->glossiness = -1.0f; + params->metallic = -1.0f; + params->roughness = -1.0f; + params->reflectance = 0.0f; + params->tex_uniform = false; + params->reflective = false; +} + + void UpdateMaterialInstance(filament::MaterialInstance* instance, - const MaterialParams& params, - const MaterialTextures& textures, + const mjrMaterialParams& params, + const mjrMaterialTextures& textures, ObjectManager* object_mgr) { if (params.scissor[2] != 0 && params.scissor[3] != 0) { instance->setScissor(params.scissor[0], params.scissor[1], @@ -37,11 +76,11 @@ void UpdateMaterialInstance(filament::MaterialInstance* instance, const filament::Material* material = instance->getMaterial(); if (material->hasParameter("BaseColorFactor")) { instance->setParameter("BaseColorFactor", filament::RgbaType::sRGB, - params.color); + ReadFloat4(params.color)); } if (material->hasParameter("SegmentationColor")) { instance->setParameter("SegmentationColor", filament::RgbaType::LINEAR, - params.segmentation_color); + ReadFloat4(params.segmentation_color)); } if (material->hasParameter("EmissiveFactor")) { instance->setParameter("EmissiveFactor", params.emissive); @@ -61,10 +100,10 @@ void UpdateMaterialInstance(filament::MaterialInstance* instance, params.roughness >= 0 ? params.roughness : 1.0f); } if (material->hasParameter("UvScale")) { - instance->setParameter("UvScale", params.uv_scale); + instance->setParameter("UvScale", ReadFloat3(params.uv_scale)); } if (material->hasParameter("UvOffset")) { - instance->setParameter("UvOffset", params.uv_offset); + instance->setParameter("UvOffset", ReadFloat3(params.uv_offset)); } if (material->hasParameter("Reflectance")) { instance->setParameter("Reflectance", params.reflectance); diff --git a/src/experimental/filament/filament/material.h b/src/experimental/filament/filament/material.h index 339b5b5b..630f4844 100644 --- a/src/experimental/filament/filament/material.h +++ b/src/experimental/filament/filament/material.h @@ -17,49 +17,51 @@ #include #include -#include -#include -#include +#include #include "experimental/filament/filament/texture.h" #include "experimental/filament/filament/object_manager.h" namespace mujoco { // The textures that can be assigned to the drawable's material. -struct MaterialTextures { - const Texture* color = nullptr; - const Texture* normal = nullptr; - const Texture* metallic = nullptr; - const Texture* roughness = nullptr; - const Texture* occlusion = nullptr; - const Texture* orm = nullptr; - const Texture* emissive = nullptr; - const Texture* reflection = nullptr; +struct mjrMaterialTextures { + const Texture* color; + const Texture* normal; + const Texture* metallic; + const Texture* roughness; + const Texture* occlusion; + const Texture* orm; + const Texture* emissive; + const Texture* reflection; }; +void mjr_defaultMaterialTextures(mjrMaterialTextures* textures); + // The parameters that can be applied to the drawable's material. -struct MaterialParams { - filament::math::float4 color = {1, 1, 1, 1}; - filament::math::float4 segmentation_color = {1, 1, 1, 1}; - filament::math::float2 tex_repeat = {1, 1}; - filament::math::float3 uv_scale = {1, 1, 1}; - filament::math::float3 uv_offset = {0, 0, 0}; - filament::math::float4 scissor = {0, 0, 0, 0}; - float specular = -1.0f; - float glossiness = -1.0f; - float metallic = -1.0f; - float roughness = -1.0f; - float emissive = -1.0f; - float reflectance = 0.0f; - bool tex_uniform = false; - bool reflective = false; +struct mjrMaterialParams { + float color[4]; + float segmentation_color[4]; + float tex_repeat[2]; + float uv_scale[3]; + float uv_offset[3]; + float scissor[4]; + float specular; + float glossiness; + float metallic; + float roughness; + float emissive; + float reflectance; + mjtByte tex_uniform; + mjtByte reflective; }; +void mjr_defaultMaterialParams(mjrMaterialParams* params); + // Updates the material instances based on the currently set parameters and // textures. void UpdateMaterialInstance(filament::MaterialInstance* instance, - const MaterialParams& params, - const MaterialTextures& textures, + const mjrMaterialParams& params, + const mjrMaterialTextures& textures, ObjectManager* object_mgr); } // namespace mujoco diff --git a/src/experimental/filament/filament/mesh.cc b/src/experimental/filament/filament/mesh.cc index 0736cbd9..2d7a74b7 100644 --- a/src/experimental/filament/filament/mesh.cc +++ b/src/experimental/filament/filament/mesh.cc @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include @@ -37,17 +39,17 @@ namespace mujoco { using filament::math::float3; using filament::math::float4; -static filament::VertexAttribute GetUsage(const VertexAttribute& attrib) { +static filament::VertexAttribute GetUsage(const mjrVertexAttribute& attrib) { switch (attrib.usage) { - case mjVERTEX_ATTRIBUTE_POSITION: + case mjVERTEX_ATTRIBUTE_USAGE_POSITION: return filament::VertexAttribute::POSITION; - case mjVERTEX_ATTRIBUTE_NORMAL: + case mjVERTEX_ATTRIBUTE_USAGE_NORMAL: return filament::VertexAttribute::TANGENTS; - case mjVERTEX_ATTRIBUTE_TANGENTS: + case mjVERTEX_ATTRIBUTE_USAGE_TANGENTS: return filament::VertexAttribute::TANGENTS; - case mjVERTEX_ATTRIBUTE_UV: + case mjVERTEX_ATTRIBUTE_USAGE_UV: return filament::VertexAttribute::UV0; - case mjVERTEX_ATTRIBUTE_COLOR: + case mjVERTEX_ATTRIBUTE_USAGE_COLOR: return filament::VertexAttribute::COLOR; default: mju_error("Unsupported vertex attribute usage: %d", attrib.usage); @@ -56,7 +58,7 @@ static filament::VertexAttribute GetUsage(const VertexAttribute& attrib) { } static filament::VertexBuffer::AttributeType GetType( - const VertexAttribute& attrib) { + const mjrVertexAttribute& attrib) { switch (attrib.type) { case mjVERTEX_ATTRIBUTE_TYPE_FLOAT2: return filament::VertexBuffer::AttributeType::FLOAT2; @@ -72,7 +74,7 @@ static filament::VertexBuffer::AttributeType GetType( } } -int VertexAttributeTypeSize(const VertexAttribute& attrib) { +int VertexAttributeTypeSize(const mjrVertexAttribute& attrib) { switch (attrib.type) { case mjVERTEX_ATTRIBUTE_TYPE_FLOAT2: return sizeof(float) * 2; @@ -99,21 +101,21 @@ int FillSequence(std::byte* buffer, std::size_t num_bytes) { return num; } -// Initializes the MeshData to default values. -void DefaultMeshData(MeshData* data) { - std::memset(data, 0, sizeof(MeshData)); +// Initializes the mjrMeshData to default values. +void mjr_defaultMeshData(mjrMeshData* data) { + std::memset(data, 0, sizeof(mjrMeshData)); } -Mesh::Mesh(filament::Engine* engine, const MeshData& data) - : engine_(engine) { - type_ = data.primitive_type == mjPRIM_TYPE_TRIANGLES +Mesh::Mesh(filament::Engine* engine, const mjrMeshData& data) + : engine_(engine), shared_state_(std::make_shared()) { + type_ = data.primitive_type == mjMESH_PRIMITIVE_TYPE_TRIANGLES ? filament::RenderableManager::PrimitiveType::TRIANGLES : filament::RenderableManager::PrimitiveType::LINES; // If the user has provided a release callback, then we need to ensure we // call is when filament is done with the mesh data. if (data.release_callback) { - release_callbacks_.push_back([=]() { + shared_state_->callbacks.push_back([=]() { data.release_callback(data.user_data); }); } @@ -133,40 +135,50 @@ Mesh::~Mesh() { } } -void Mesh::BuildVertexBuffer(const MeshData& data) { +void Mesh::BuildVertexBuffer(const mjrMeshData& data) { if (data.nvertices == 0) { - mju_error("MeshData has no vertices."); + mju_error("mjrMeshData has no vertices."); } - // The filament BufferDescriptor callback for releasing the memory. We assume - // that ReleaseResources() can be called multiple times, so we assign this - // callback to each buffer descriptor. + // The filament BufferDescriptor callback for releasing the memory. + // We pass a heap-allocated shared_ptr to the shared state as the user data. auto callback = +[](void* buffer, size_t size, void* user) { - static_cast(user)->ReleaseResources(); + auto* state_ptr = static_cast*>(user); + auto state = *state_ptr; + delete state_ptr; + + std::lock_guard lock(state->mutex); + if (!state->called) { + for (const auto& cb : state->callbacks) { + cb(); + } + state->callbacks.clear(); + state->called = true; + } }; // Pointers to specific attributes in the mesh data, used for additional // validation and processing. - const VertexAttribute* positions = nullptr; - const VertexAttribute* normals = nullptr; - const VertexAttribute* tangents = nullptr; + const mjrVertexAttribute* positions = nullptr; + const mjrVertexAttribute* normals = nullptr; + const mjrVertexAttribute* tangents = nullptr; for (int i = 0; i < data.nattributes; ++i) { - if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_POSITION) { + if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_POSITION) { positions = &data.attributes[i]; - } else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_NORMAL) { + } else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) { normals = &data.attributes[i]; - } else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_TANGENTS) { + } else if (data.attributes[i].usage == mjVERTEX_ATTRIBUTE_USAGE_TANGENTS) { tangents = &data.attributes[i]; } } if (!positions) { - mju_error("MeshData has no positions."); + mju_error("mjrMeshData has no positions."); } - if (data.attributes[0].usage != mjVERTEX_ATTRIBUTE_POSITION) { + if (data.attributes[0].usage != mjVERTEX_ATTRIBUTE_USAGE_POSITION) { mju_error("Positions must be the first attribute."); } if (normals && tangents) { - mju_error("MeshData has both normals and tangents."); + mju_error("mjrMeshData has both normals and tangents."); } if (normals && data.interleaved) { // We need to build orientations from normals and so we require each @@ -195,7 +207,7 @@ void Mesh::BuildVertexBuffer(const MeshData& data) { // each offset is the sum of the sizes of the preceding attributes. int offset = 0; for (int i = 0; i < data.nattributes; ++i) { - const VertexAttribute& attrib = data.attributes[i]; + const mjrVertexAttribute& attrib = data.attributes[i]; const filament::VertexAttribute usage = GetUsage(attrib); filament::VertexBuffer::AttributeType type = GetType(attrib); vb_builder.attribute(usage, 0, type, offset, total_vertex_size); @@ -206,16 +218,17 @@ void Mesh::BuildVertexBuffer(const MeshData& data) { attributes_[i] = usage; } vertex_buffer_ = vb_builder.build(*engine_); - vertex_buffer_->setBufferAt(*engine_, 0, {bytes, nbytes, callback, this}); + auto* user_data = new std::shared_ptr(shared_state_); + vertex_buffer_->setBufferAt(*engine_, 0, {bytes, nbytes, callback, user_data}); } else { // For a non-interleaved vertex buffer, we assign a separate buffer to each // attribute. vb_builder.bufferCount(data.nattributes); for (int i = 0; i < data.nattributes; ++i) { - const VertexAttribute& attrib = data.attributes[i]; + const mjrVertexAttribute& attrib = data.attributes[i]; const filament::VertexAttribute usage = GetUsage(attrib); filament::VertexBuffer::AttributeType type = GetType(attrib); - if (attrib.usage == mjVERTEX_ATTRIBUTE_NORMAL) { + if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) { // We will replace normals with orientations. type = filament::VertexBuffer::AttributeType::FLOAT4; } @@ -230,25 +243,26 @@ void Mesh::BuildVertexBuffer(const MeshData& data) { // Assign the individual data buffers. for (int i = 0; i < data.nattributes; ++i) { - const VertexAttribute& attrib = data.attributes[i]; + const mjrVertexAttribute& attrib = data.attributes[i]; const void* bytes = attrib.bytes; size_t nbytes = data.nvertices * VertexAttributeTypeSize(attrib); - if (attrib.usage == mjVERTEX_ATTRIBUTE_NORMAL) { + if (attrib.usage == mjVERTEX_ATTRIBUTE_USAGE_NORMAL) { // Replace normals with orientations. nbytes = data.nvertices * sizeof(float4); bytes = BuildOrientationsFromNormals(data.nvertices, attrib); } - vertex_buffer_->setBufferAt(*engine_, i, {bytes, nbytes, callback, this}); + auto* user_data = new std::shared_ptr(shared_state_); + vertex_buffer_->setBufferAt(*engine_, i, {bytes, nbytes, callback, user_data}); } } } -void Mesh::BuildIndexBuffer(const MeshData& data) { +void Mesh::BuildIndexBuffer(const mjrMeshData& data) { if (data.nindices == 0) { return; } - const int element_size = data.index_type == mjINDEX_TYPE_USHORT + const int element_size = data.index_type == mjINDEX_TYPE_U16 ? sizeof(uint16_t) : sizeof(uint32_t); const int num_bytes = data.nindices * element_size; @@ -259,11 +273,11 @@ void Mesh::BuildIndexBuffer(const MeshData& data) { const void* indices = data.indices; if (indices == nullptr) { std::byte* sequence = new std::byte[num_bytes]; - release_callbacks_.push_back([=]() { + shared_state_->callbacks.push_back([=]() { delete[] sequence; }); - if (data.index_type == mjINDEX_TYPE_USHORT) { + if (data.index_type == mjINDEX_TYPE_U16) { FillSequence(sequence, num_bytes); } else { FillSequence(sequence, num_bytes); @@ -273,7 +287,7 @@ void Mesh::BuildIndexBuffer(const MeshData& data) { filament::IndexBuffer::Builder ib_builder; ib_builder.indexCount(data.nindices); - ib_builder.bufferType(data.index_type == mjINDEX_TYPE_USHORT + ib_builder.bufferType(data.index_type == mjINDEX_TYPE_U16 ? filament::IndexBuffer::IndexType::USHORT : filament::IndexBuffer::IndexType::UINT); index_buffer_ = ib_builder.build(*engine_); @@ -283,9 +297,10 @@ void Mesh::BuildIndexBuffer(const MeshData& data) { index_buffer_->setBuffer(*engine_, std::move(desc)); } -float4* Mesh::BuildOrientationsFromNormals(int nvertices, const VertexAttribute& normals) { +float4* Mesh::BuildOrientationsFromNormals(int nvertices, + const mjrVertexAttribute& normals) { float4* orientations = new float4[nvertices]; - release_callbacks_.push_back([=]() { + shared_state_->callbacks.push_back([=]() { delete[] orientations; }); const float* normals_ptr = reinterpret_cast(normals.bytes); @@ -295,7 +310,7 @@ float4* Mesh::BuildOrientationsFromNormals(int nvertices, const VertexAttribute& return orientations; } -void Mesh::UpdateBounds(const MeshData& data) { +void Mesh::UpdateBounds(const mjrMeshData& data) { float3 bounds_min = ReadFloat3(data.bounds_min); float3 bounds_max = ReadFloat3(data.bounds_max); if (bounds_min != bounds_max) { @@ -304,8 +319,8 @@ void Mesh::UpdateBounds(const MeshData& data) { bounds_min = float3(FLT_MAX, FLT_MAX, FLT_MAX); bounds_max = float3(-FLT_MAX, -FLT_MAX, -FLT_MAX); - if (data.attributes[0].usage != mjVERTEX_ATTRIBUTE_POSITION) { - mju_error("MeshData has no positions."); + if (data.attributes[0].usage != mjVERTEX_ATTRIBUTE_USAGE_POSITION) { + mju_error("mjrMeshData has no positions."); } const float* positions = reinterpret_cast(data.attributes[0].bytes); @@ -320,10 +335,14 @@ void Mesh::UpdateBounds(const MeshData& data) { } void Mesh::ReleaseResources() { - for (const auto& callback : release_callbacks_) { - callback(); + std::lock_guard lock(shared_state_->mutex); + if (!shared_state_->called) { + for (const auto& callback : shared_state_->callbacks) { + callback(); + } + shared_state_->callbacks.clear(); + shared_state_->called = true; } - release_callbacks_.clear(); } filament::IndexBuffer* Mesh::GetFilamentIndexBuffer() const { diff --git a/src/experimental/filament/filament/mesh.h b/src/experimental/filament/filament/mesh.h index b75ad6de..3b7fc5ab 100644 --- a/src/experimental/filament/filament/mesh.h +++ b/src/experimental/filament/filament/mesh.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,66 +30,67 @@ #include #include #include +#include // Functions for creating filament vertex and index buffers. namespace mujoco { -// Maximum number of vertex attributes that can be used by a mesh. -static constexpr int kMaxVertexAttributes = 16; - // The type of data stored in an index buffer. -typedef enum mjtIndexType_ { - mjINDEX_TYPE_USHORT = 0, - mjINDEX_TYPE_UINT = 1, -} mjtIndexType; +typedef enum mjrIndexType_ { + mjINDEX_TYPE_U16 = 0, + mjINDEX_TYPE_U32 = 1, +} mjrIndexType; // The type of primitive to be drawn by vertex data. -typedef enum mjtMeshPrimitiveType_ { - mjPRIM_TYPE_TRIANGLES = 0, - mjPRIM_TYPE_LINES = 1, -} mjtMeshPrimitiveType; +typedef enum mjrMeshPrimitiveType_ { + mjMESH_PRIMITIVE_TYPE_TRIANGLES = 0, + mjMESH_PRIMITIVE_TYPE_LINES = 1, +} mjrMeshPrimitiveType; // The usage/purpose of an attribute of a vertex. -typedef enum mjtVertexAttributeUsage_ { - mjVERTEX_ATTRIBUTE_POSITION = 0, - mjVERTEX_ATTRIBUTE_NORMAL = 1, - mjVERTEX_ATTRIBUTE_TANGENTS = 2, - mjVERTEX_ATTRIBUTE_UV = 3, - mjVERTEX_ATTRIBUTE_COLOR = 4, -} mjtVertexAttributeUsage; +typedef enum mjrVertexAttributeUsage_ { + mjVERTEX_ATTRIBUTE_USAGE_POSITION = 0, + mjVERTEX_ATTRIBUTE_USAGE_NORMAL = 1, + mjVERTEX_ATTRIBUTE_USAGE_TANGENTS = 2, + mjVERTEX_ATTRIBUTE_USAGE_UV = 3, + mjVERTEX_ATTRIBUTE_USAGE_COLOR = 4, +} mjrVertexAttributeUsage; // The data format of an attribute of a vertex. -typedef enum mjtVertexAttributeType_ { +typedef enum mjrVertexAttributeType_ { mjVERTEX_ATTRIBUTE_TYPE_FLOAT2 = 0, mjVERTEX_ATTRIBUTE_TYPE_FLOAT3 = 1, mjVERTEX_ATTRIBUTE_TYPE_FLOAT4 = 2, mjVERTEX_ATTRIBUTE_TYPE_UBYTE4 = 3, -} mjtVertexAttributeType; +} mjrVertexAttributeType; + +// Maximum number of vertex attributes that can be used by a mesh. +enum { mjMAX_VERTEX_ATTRIBUTES = 16 }; // Information about a single attribute of a vertex. -struct VertexAttribute { +struct mjrVertexAttribute { // The data for the attribute. const void* bytes; // The usage/purpose of the attribute. - mjtVertexAttributeUsage usage; + mjrVertexAttributeUsage usage; // The data format of the attribute. - mjtVertexAttributeType type; + mjrVertexAttributeType type; }; // The binary contents of a mesh. -struct MeshData { +struct mjrMeshData { // The number of vertices in the mesh. Each of the vertex arrays below is // assumed to have this number of elements. - size_t nvertices; + mjtSize nvertices; // The number of attributes for each vertex in the mesh. int nattributes; // Information about each attribute of a vertex in the mesh. See `interleaved` // for more details. - VertexAttribute attributes[kMaxVertexAttributes]; + mjrVertexAttribute attributes[mjMAX_VERTEX_ATTRIBUTES]; // Whether the vertex attributes are interleaved or not. // @@ -99,24 +101,24 @@ struct MeshData { // // If false, assume each attribute is stored in a separate array as defined // by the `data` field of the attribute. - bool interleaved; + mjtByte interleaved; // The number of indices in the mesh. The indices array is assumed to have // this number of elements. - size_t nindices; + mjtSize nindices; // The indices of the mesh, stored as either ushort or uint depending on the // index type. const void* indices; // The type of data stored in the indices array. - mjtIndexType index_type; + mjrIndexType index_type; // The type of primitive to be drawn by vertex data. - mjtMeshPrimitiveType primitive_type; + mjrMeshPrimitiveType primitive_type; // Whether to compute the bounds of the mesh using the vertex positions. - bool compute_bounds; + mjtByte compute_bounds; // The bounds of the mesh. If bounds_min == bounds_max, then we assume that // that the bounds are not set (i.e. the bounds is empty). @@ -133,13 +135,13 @@ struct MeshData { }; // Initializes the MeshData to default values. -void DefaultMeshData(MeshData* data); +void mjr_defaultMeshData(mjrMeshData* data); // Owns a Vertex and Index buffer representing a geometry mesh. class Mesh { public: // Creates a Mesh from the given MeshData. - Mesh(filament::Engine* engine, const MeshData& data); + Mesh(filament::Engine* engine, const mjrMeshData& data); ~Mesh(); @@ -165,12 +167,12 @@ class Mesh { Mesh& operator=(const Mesh&) = delete; private: - void BuildVertexBuffer(const MeshData& data); - void BuildIndexBuffer(const MeshData& data); - void UpdateBounds(const MeshData& data); + void BuildVertexBuffer(const mjrMeshData& data); + void BuildIndexBuffer(const mjrMeshData& data); + void UpdateBounds(const mjrMeshData& data); filament::math::float4* BuildOrientationsFromNormals( - int nvertices, const VertexAttribute& normals); + int nvertices, const mjrVertexAttribute& normals); void ReleaseResources(); @@ -180,8 +182,13 @@ class Mesh { filament::RenderableManager::PrimitiveType type_ = filament::RenderableManager::PrimitiveType::TRIANGLES; std::optional bounds_; - std::vector> release_callbacks_; - std::array attributes_; + struct SharedState { + std::vector> callbacks; + std::mutex mutex; + bool called = false; + }; + std::shared_ptr shared_state_; + std::array attributes_; int num_attributes_ = 0; }; diff --git a/src/experimental/filament/filament/render_target.cc b/src/experimental/filament/filament/render_target.cc index b01fb94c..3e53ac51 100644 --- a/src/experimental/filament/filament/render_target.cc +++ b/src/experimental/filament/filament/render_target.cc @@ -30,13 +30,13 @@ namespace mujoco { -void DefaultRenderTargetConfig(RenderTargetConfig* config) { +void mjr_defaultRenderTargetConfig(mjrRenderTargetConfig* config) { config->color_format = mjPIXEL_FORMAT_RGBA8; config->depth_format = mjPIXEL_FORMAT_DEPTH32F; } RenderTarget::RenderTarget(filament::Engine* engine, - const RenderTargetConfig& config) + const mjrRenderTargetConfig& config) : engine_(engine), config_(config) {} RenderTarget::~RenderTarget() noexcept { @@ -51,8 +51,8 @@ void RenderTarget::Prepare(int width, int height) { width_ = width; height_ = height; - TextureConfig color_config; - DefaultTextureConfig(&color_config); + mjrTextureConfig color_config; + mjr_defaultTextureConfig(&color_config); Texture::InternalFlags color_flags; color_config.width = width; color_config.height = height; @@ -63,8 +63,8 @@ void RenderTarget::Prepare(int width, int height) { color_flags.color_attachment = true; color_texture_ = std::make_unique(engine_, color_config, color_flags); - TextureConfig depth_config; - DefaultTextureConfig(&depth_config); + mjrTextureConfig depth_config; + mjr_defaultTextureConfig(&depth_config); Texture::InternalFlags depth_flags; depth_config.width = width; depth_config.height = height; diff --git a/src/experimental/filament/filament/render_target.h b/src/experimental/filament/filament/render_target.h index 22a02143..b731a567 100644 --- a/src/experimental/filament/filament/render_target.h +++ b/src/experimental/filament/filament/render_target.h @@ -26,20 +26,20 @@ namespace mujoco { // Defines the basic properties of a render target. -struct RenderTargetConfig { - mjtPixelFormat color_format; - mjtPixelFormat depth_format; +struct mjrRenderTargetConfig { + mjrPixelFormat color_format; + mjrPixelFormat depth_format; }; // Initializes the RenderTargetConfig to default values. -void DefaultRenderTargetConfig(RenderTargetConfig* config); +void mjr_defaultRenderTargetConfig(mjrRenderTargetConfig* config); // Manages a filament RenderTarget and the textures which are bound to it. class RenderTarget { public: // Defines the types of textures to create for the color and depth // attachments. - RenderTarget(filament::Engine* engine, const RenderTargetConfig& config); + RenderTarget(filament::Engine* engine, const mjrRenderTargetConfig& config); ~RenderTarget() noexcept; RenderTarget(const RenderTarget&) = delete; @@ -66,7 +66,7 @@ class RenderTarget { void Destroy(); filament::Engine* engine_ = nullptr; - RenderTargetConfig config_; + mjrRenderTargetConfig config_; filament::RenderTarget* render_target_ = nullptr; std::unique_ptr color_texture_ = nullptr; std::unique_ptr depth_texture_ = nullptr; diff --git a/src/experimental/filament/filament/renderable.cc b/src/experimental/filament/filament/renderable.cc index f1a4e2bc..4381cbd4 100644 --- a/src/experimental/filament/filament/renderable.cc +++ b/src/experimental/filament/filament/renderable.cc @@ -36,12 +36,15 @@ namespace mujoco { using filament::math::mat4f; -void DefaultRenderableParams(RenderableParams* params) { - params->shading_model = ShadingModel::SceneObject; +void mjr_defaultRenderableParams(mjrRenderableParams* params) { + params->shading_model = mjSHADING_MODEL_SCENE_OBJECT; } -Renderable::Renderable(ObjectManager* object_mgr, const RenderableParams& params) - : object_mgr_(object_mgr), params_(params) {} +Renderable::Renderable(ObjectManager* object_mgr, const mjrRenderableParams& params) + : object_mgr_(object_mgr), params_(params) { + mjr_defaultMaterialParams(&material_params_); + mjr_defaultMaterialTextures(&material_textures_); +} Renderable::~Renderable() noexcept { filament::Engine* engine = GetEngine(); @@ -198,13 +201,13 @@ void Renderable::RemoveFromScene(filament::Scene* scene) { assigned_scene_ = nullptr; } -void Renderable::UpdateMaterial(const MaterialParams& params, - const MaterialTextures& textures) { +void Renderable::UpdateMaterial(const mjrMaterialParams& params, + const mjrMaterialTextures& textures) { material_params_ = params; material_textures_ = textures; AssignMaterial(DrawMode::Color, GetColorMaterialType()); - if (params_.shading_model == ShadingModel::SceneObject) { + if (params_.shading_model == mjSHADING_MODEL_SCENE_OBJECT) { AssignMaterial(DrawMode::Depth, ObjectManager::kUnlitDepth); AssignMaterial(DrawMode::Segmentation, ObjectManager::kUnlitSegmentation); } @@ -237,17 +240,17 @@ void Renderable::AssignMaterial(DrawMode mode, } } -const MaterialParams& Renderable::GetMaterialParams() const { +const mjrMaterialParams& Renderable::GetMaterialParams() const { return material_params_; } -const MaterialTextures& Renderable::GetMaterialTextures() const { +const mjrMaterialTextures& Renderable::GetMaterialTextures() const { return material_textures_; } void Renderable::SetDrawMode(DrawMode mode) { // Only SceneObjects support non-color draw modes. - if (params_.shading_model != ShadingModel::SceneObject) { + if (params_.shading_model != mjSHADING_MODEL_SCENE_OBJECT) { mode = DrawMode::Color; } @@ -343,11 +346,11 @@ void Renderable::SetWireframe(bool wireframe) { } ObjectManager::MaterialType Renderable::GetColorMaterialType() const { - if (params_.shading_model == ShadingModel::DecorLines) { + if (params_.shading_model == mjSHADING_MODEL_DECOR_LINES) { return ObjectManager::kUnlitLine; - } else if (params_.shading_model == ShadingModel::Decor) { + } else if (params_.shading_model == mjSHADING_MODEL_DECOR) { return ObjectManager::kUnlitDecor; - } else if (params_.shading_model == ShadingModel::Ux) { + } else if (params_.shading_model == mjSHADING_MODEL_UX) { return ObjectManager::kUnlitUi; } else if (material_textures_.orm) { return ObjectManager::kPbrPacked; @@ -374,7 +377,7 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const { } if (material_textures_.color == nullptr) { - if (material_params_.color.a < 1.0f) { + if (material_params_.color[3] < 1.0f) { return ObjectManager::kPhongColorFade; } else if (material_params_.reflective) { return ObjectManager::kPhongColorReflect; @@ -383,7 +386,7 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const { } } else if (material_textures_.color->GetFilamentTexture()->getTarget() == filament::Texture::Sampler::SAMPLER_CUBEMAP) { - if (material_params_.color.a < 1.0f) { + if (material_params_.color[3] < 1.0f) { return ObjectManager::kPhongCubeFade; } else if (material_params_.reflective) { return ObjectManager::kPhongCubeReflect; @@ -391,7 +394,7 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const { return ObjectManager::kPhongCube; } } else if (has_texcoords) { - if (material_params_.color.a < 1.0f) { + if (material_params_.color[3] < 1.0f) { return ObjectManager::kPhong2dUvFade; } else if (material_params_.reflective) { return ObjectManager::kPhong2dUvReflect; @@ -399,7 +402,7 @@ ObjectManager::MaterialType Renderable::GetColorMaterialType() const { return ObjectManager::kPhong2dUv; } } else { - if (material_params_.color.a < 1.0f) { + if (material_params_.color[3] < 1.0f) { return ObjectManager::kPhong2dFade; } else if (material_params_.reflective) { return ObjectManager::kPhong2dReflect; diff --git a/src/experimental/filament/filament/renderable.h b/src/experimental/filament/filament/renderable.h index 9227e634..a0da8166 100644 --- a/src/experimental/filament/filament/renderable.h +++ b/src/experimental/filament/filament/renderable.h @@ -33,19 +33,19 @@ namespace mujoco { // The shading model (material) for a Renderable. -enum class ShadingModel { - SceneObject, - Decor, - DecorLines, - Ux, -}; +typedef enum mjrShadingModel_ { + mjSHADING_MODEL_SCENE_OBJECT, + mjSHADING_MODEL_DECOR, + mjSHADING_MODEL_DECOR_LINES, + mjSHADING_MODEL_UX, +} mjrShadingModel; // Configuration parameters for a Renderable. -struct RenderableParams { - ShadingModel shading_model; +struct mjrRenderableParams { + mjrShadingModel shading_model; }; -void DefaultRenderableParams(RenderableParams* params); +void mjr_defaultRenderableParams(mjrRenderableParams* params); // A Renderable is effectively two things: a mesh and a material. // @@ -67,7 +67,7 @@ class Renderable { static constexpr std::uint8_t kDefaultPriority = 4; static constexpr std::uint8_t kDefaultLayerMask = 0x01; - Renderable(ObjectManager* object_mgr, const RenderableParams& params); + Renderable(ObjectManager* object_mgr, const mjrRenderableParams& params); ~Renderable() noexcept; Renderable(const Renderable&) = delete; @@ -127,14 +127,14 @@ class Renderable { void SetDrawMode(DrawMode mode); // Updates the parameters for the material. - void UpdateMaterial(const MaterialParams& params, - const MaterialTextures& textures); + void UpdateMaterial(const mjrMaterialParams& params, + const mjrMaterialTextures& textures); // Returns the current material parameters. - const MaterialParams& GetMaterialParams() const; + const mjrMaterialParams& GetMaterialParams() const; // Returns the current material textures. - const MaterialTextures& GetMaterialTextures() const; + const mjrMaterialTextures& GetMaterialTextures() const; // Returns the filament Engine managing the renderables. filament::Engine* GetEngine(); @@ -154,10 +154,10 @@ class Renderable { ObjectManager::MaterialType GetColorMaterialType() const; ObjectManager* object_mgr_; - RenderableParams params_; + mjrRenderableParams params_; filament::MaterialInstance* instances_[kNumDrawModes] = {nullptr}; - MaterialParams material_params_; - MaterialTextures material_textures_; + mjrMaterialParams material_params_; + mjrMaterialTextures material_textures_; DrawMode draw_mode_ = DrawMode::Color; filament::Scene* assigned_scene_ = nullptr; std::vector parts_; diff --git a/src/experimental/filament/filament/scene_view.cc b/src/experimental/filament/filament/scene_view.cc index efcbd76f..997b048f 100644 --- a/src/experimental/filament/filament/scene_view.cc +++ b/src/experimental/filament/filament/scene_view.cc @@ -127,9 +127,7 @@ static void SetupReflectionCamera(const mat4& surface_xform, SceneView::SceneView(filament::Engine* engine) : engine_(engine) { scene_ = engine->createScene(); - ux_scene_ = engine->createScene(); camera_ = engine->createCamera(utils::EntityManager::get().create()); - ux_camera_ = engine->createCamera(utils::EntityManager::get().create()); reflect_camera_ = engine->createCamera(utils::EntityManager::get().create()); for (auto& view : views_) { @@ -139,12 +137,6 @@ SceneView::SceneView(filament::Engine* engine) : engine_(engine) { view->setVisibleLayers(0xff, mjCAT_ALL); } - ux_view_ = engine->createView(); - ux_view_->setScene(ux_scene_); - ux_view_->setCamera(ux_camera_); - ux_view_->setPostProcessingEnabled(false); - ux_view_->setShadowingEnabled(false); - reflect_view_ = engine->createView(); reflect_view_->setScene(scene_); reflect_view_->setCamera(reflect_camera_); @@ -172,22 +164,16 @@ SceneView::~SceneView() { for (auto& renderable : renderables_) { renderable->RemoveFromScene(scene_); } - for (auto& renderable : ux_renderables_) { - renderable->RemoveFromScene(ux_scene_); - } lights_.clear(); renderables_.clear(); reflect_targets_.clear(); engine_->destroyCameraComponent(reflect_camera_->getEntity()); engine_->destroy(reflect_view_); - engine_->destroyCameraComponent(ux_camera_->getEntity()); - engine_->destroy(ux_view_); engine_->destroyCameraComponent(camera_->getEntity()); if (color_grading_) { engine_->destroy(color_grading_); } engine_->destroy(scene_); - engine_->destroy(ux_scene_); for (auto& view : views_) { engine_->destroy(view); } @@ -224,18 +210,6 @@ void SceneView::RemoveFromScene(Renderable* renderable) { } } -void SceneView::AddToUxScene(Renderable* renderable) { - if (ux_renderables_.insert(renderable).second) { - renderable->AddToScene(ux_scene_); - } -} - -void SceneView::RemoveFromUxScene(Renderable* renderable) { - if (ux_renderables_.erase(renderable)) { - renderable->RemoveFromScene(ux_scene_); - } -} - void SceneView::AddToScene(filament::Skybox* skybox) { skybox_ = skybox; scene_->setSkybox(skybox); @@ -255,7 +229,6 @@ void SceneView::Render(filament::Renderer* renderer, for (auto& view : views_) { view->setViewport(viewport); } - ux_view_->setViewport(viewport); reflect_view_->setViewport(viewport); SetupCamera(request.camera, viewport, camera_); @@ -276,7 +249,7 @@ void SceneView::Render(filament::Renderer* renderer, } // Render reflection passes. - if (request.draw_mode == DrawMode::Color) { + if (request.draw_mode == DrawMode::Color && reflections_enabled_) { for (size_t i = 0; i < reflectives_.size(); ++i) { Renderable* renderable = reflectives_[i]; @@ -301,15 +274,6 @@ void SceneView::Render(filament::Renderer* renderer, renderer->render(view); view->setRenderTarget(nullptr); - if (request.enable_ux) { - ux_camera_->setProjection(filament::Camera::Projection::ORTHO, 0.0f, - viewport.width, viewport.height, 0.0f, 0.0f, - 1.0f); - ux_view_->setRenderTarget(render_target); - renderer->render(ux_view_); - ux_view_->setRenderTarget(nullptr); - } - if (request.target) { view->setMultiSampleAntiAliasingOptions(options); } @@ -322,8 +286,8 @@ void SceneView::AddReflectiveRenderable(Renderable* renderable) { // Ensure we have the same number of render targets as we do reflective // renderables. while (reflect_targets_.size() < reflectives_.size()) { - RenderTargetConfig config; - DefaultRenderTargetConfig(&config); + mjrRenderTargetConfig config; + mjr_defaultRenderTargetConfig(&config); config.color_format = mjPIXEL_FORMAT_RGBA8; config.depth_format = mjPIXEL_FORMAT_DEPTH32F; @@ -335,9 +299,11 @@ void SceneView::AddReflectiveRenderable(Renderable* renderable) { auto& target = reflect_targets_[index]; target->Prepare(viewport.width, viewport.height); - MaterialTextures textures = renderable->GetMaterialTextures(); - textures.reflection = target->GetColorTexture(); - renderable->UpdateMaterial(renderable->GetMaterialParams(), textures); + if (reflections_enabled_) { + mjrMaterialTextures textures = renderable->GetMaterialTextures(); + textures.reflection = target->GetColorTexture(); + renderable->UpdateMaterial(renderable->GetMaterialParams(), textures); + } } void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) { @@ -353,6 +319,43 @@ void SceneView::SetColorGradingOptions(const ColorGradingOptions& opts) { color_grading_options_ = opts; } +void SceneView::EnableShadows() { + views_[kNormalIndex]->setShadowingEnabled(true); +} + +void SceneView::DisableShadows() { + views_[kNormalIndex]->setShadowingEnabled(false); +} + +void SceneView::EnableReflections() { + reflections_enabled_ = true; + + for (int i = 0; i < reflectives_.size(); ++i) { + Renderable* renderable = reflectives_[i]; + mjrMaterialTextures textures = renderable->GetMaterialTextures(); + textures.reflection = reflect_targets_[i]->GetColorTexture(); + renderable->UpdateMaterial(renderable->GetMaterialParams(), textures); + } +} + +void SceneView::DisableReflections() { + reflections_enabled_ = false; + for (Renderable* renderable : reflectives_) { + mjrMaterialTextures textures = renderable->GetMaterialTextures(); + textures.reflection = nullptr; + renderable->UpdateMaterial(renderable->GetMaterialParams(), textures); + } + +} + +void SceneView::EnablePostProcessing() { + views_[kNormalIndex]->setPostProcessingEnabled(true); +} + +void SceneView::DisablePostProcessing() { + views_[kNormalIndex]->setPostProcessingEnabled(false); +} + filament::View* SceneView::GetDefaultRenderView() { return views_[kNormalIndex]; } diff --git a/src/experimental/filament/filament/scene_view.h b/src/experimental/filament/filament/scene_view.h index a9a2874b..b6d5dfa6 100644 --- a/src/experimental/filament/filament/scene_view.h +++ b/src/experimental/filament/filament/scene_view.h @@ -38,8 +38,7 @@ namespace mujoco { // // The filament Scene is populated with the objects (e.g. lights, renderables, // skybox, etc.). It manages multiple views to support a variety of draw modes -// (e.g. normal, depth, segmentation, etc.) as well as reflective surfaces. It -// also manages a separate scene and view for UX rendering. +// (e.g. normal, depth, segmentation, etc.) as well as reflective surfaces. class SceneView { public: SceneView(filament::Engine* engine); @@ -53,10 +52,6 @@ class SceneView { void AddToScene(filament::Skybox* skybox); void RemoveFromScene(filament::Skybox* skybox); - // Adds/removes entities from the UX scene, which is rendered separately. - void AddToUxScene(Renderable* renderable); - void RemoveFromUxScene(Renderable* renderable); - // Parameters for rendering the scene. struct RenderRequest { // The draw mode (e.g. normal, depth, segmentation) to render. @@ -67,8 +62,6 @@ class SceneView { mjvGLCamera camera; // An optional render target into which the scene will be rendered. RenderTarget* target = nullptr; - // Whether or not to render the UX as a separate pass. - bool enable_ux = false; }; // Renders the scene. @@ -77,6 +70,18 @@ class SceneView { // Returns the filament Engine managing the scene. filament::Engine* GetEngine() const { return engine_; } + // Enables/disables shadows for the default render view. + void EnableShadows(); + void DisableShadows(); + + // Enables/disables reflections for the default render view. + void EnableReflections(); + void DisableReflections(); + + // Enables/disables post processing for the default render view. + void EnablePostProcessing(); + void DisablePostProcessing(); + // Returns the underlying filament View that is used for normal rendering. // Callers can update rendering settings (e.g. post processing) directly. filament::View* GetDefaultRenderView(); @@ -95,7 +100,6 @@ class SceneView { filament::Engine* engine_ = nullptr; filament::Scene* scene_ = nullptr; - filament::Scene* ux_scene_ = nullptr; filament::Camera* camera_ = nullptr; filament::ColorGrading* color_grading_ = nullptr; ColorGradingOptions color_grading_options_; @@ -106,16 +110,12 @@ class SceneView { std::unordered_set renderables_; filament::Skybox* skybox_ = nullptr; - // Custom view for UX. - filament::View* ux_view_ = nullptr; - filament::Camera* ux_camera_ = nullptr; - std::unordered_set ux_renderables_; - // Custom view and camera for reflective surfaces. filament::View* reflect_view_ = nullptr; filament::Camera* reflect_camera_ = nullptr; // The list of reflective renderables and their corresponding render targets. + bool reflections_enabled_ = true; std::vector reflectives_; std::vector> reflect_targets_; }; diff --git a/src/experimental/filament/filament/texture.cc b/src/experimental/filament/filament/texture.cc index 1277ec44..81b37448 100644 --- a/src/experimental/filament/filament/texture.cc +++ b/src/experimental/filament/filament/texture.cc @@ -29,15 +29,15 @@ namespace mujoco { static constexpr int kNumFacesPerCube = 6; -static bool IsCompressed(const TextureConfig& config) { +static bool IsCompressed(const mjrTextureConfig& config) { return config.format == mjPIXEL_FORMAT_KTX; } -static bool IsCubeMap(const TextureConfig& config) { +static bool IsCubeMap(const mjrTextureConfig& config) { return config.target == mjTEXTURE_CUBE || config.target == mjTEXTURE_SKYBOX; } -static int GetFaceHeight(const TextureConfig& config) { +static int GetFaceHeight(const mjrTextureConfig& config) { int face_height = config.height; if (config.width != config.height) { if (config.width * kNumFacesPerCube != config.height) { @@ -51,7 +51,7 @@ static int GetFaceHeight(const TextureConfig& config) { return face_height; } -static int GetNumChannels(const TextureConfig& config) { +static int GetNumChannels(const mjrTextureConfig& config) { switch (config.format) { case mjPIXEL_FORMAT_R8: return 1; @@ -65,7 +65,7 @@ static int GetNumChannels(const TextureConfig& config) { } } -static filament::Texture::Format GetTextureFormat(const TextureConfig& config) { +static filament::Texture::Format GetTextureFormat(const mjrTextureConfig& config) { switch (config.format) { case mjPIXEL_FORMAT_R8: return filament::Texture::Format::R; @@ -80,7 +80,7 @@ static filament::Texture::Format GetTextureFormat(const TextureConfig& config) { } static filament::Texture::InternalFormat GetTextureInternalFormat( - const TextureConfig& config) { + const mjrTextureConfig& config) { if (config.color_space == mjCOLORSPACE_SRGB) { switch (config.format) { case mjPIXEL_FORMAT_RGB8: @@ -110,15 +110,15 @@ static filament::Texture::InternalFormat GetTextureInternalFormat( } } -void DefaultTextureData(TextureData* data) { - std::memset(data, 0, sizeof(TextureData)); +void mjr_defaultTextureData(mjrTextureData* data) { + std::memset(data, 0, sizeof(mjrTextureData)); } -void DefaultTextureConfig(TextureConfig* config) { - std::memset(config, 0, sizeof(TextureConfig)); +void mjr_defaultTextureConfig(mjrTextureConfig* config) { + std::memset(config, 0, sizeof(mjrTextureConfig)); } -Texture::Texture(filament::Engine* engine, const TextureConfig& config, +Texture::Texture(filament::Engine* engine, const mjrTextureConfig& config, InternalFlags flags) : engine_(engine), config_(config) { if (IsCompressed(config_)) { @@ -166,7 +166,7 @@ Texture::~Texture() { } } -void Texture::Upload(const TextureData& data) { +void Texture::Upload(const mjrTextureData& data) { user_data_ = data.user_data; release_callback_ = data.release_callback; diff --git a/src/experimental/filament/filament/texture.h b/src/experimental/filament/filament/texture.h index b1493470..e51bbfff 100644 --- a/src/experimental/filament/filament/texture.h +++ b/src/experimental/filament/filament/texture.h @@ -21,12 +21,13 @@ #include #include #include +#include // Functions for creating filament textures. namespace mujoco { // Pixel formats for textures. -typedef enum mjtPixelFormat_ { +typedef enum mjrPixelFormat_ { mjPIXEL_FORMAT_UNKNOWN = 0, mjPIXEL_FORMAT_R8, mjPIXEL_FORMAT_RGB8, @@ -34,15 +35,18 @@ typedef enum mjtPixelFormat_ { mjPIXEL_FORMAT_R32F, mjPIXEL_FORMAT_DEPTH32F, mjPIXEL_FORMAT_KTX, -} mjtPixelFormat; +} mjrPixelFormat; + +typedef mjtTexture mjrTextureTarget; +typedef mjtColorSpace mjrColorSpace; // The binary contents of a texture. -struct TextureData { +struct mjrTextureData { // Pointer to the image data. If null, an empty texture will be created. - void* bytes; + const void* bytes; // The number of bytes in the image data. - size_t nbytes; + mjtSize nbytes; // Because rendering may be multithreaded, we cannot make assumptions about // when the image data will finish uploading to the GPU. As such, we will use @@ -54,10 +58,10 @@ struct TextureData { }; // Initializes the TextureData to default values. -void DefaultTextureData(TextureData* data); +void mjr_defaultTextureData(mjrTextureData* data); // Defines the basic properties of a texture. -struct TextureConfig { +struct mjrTextureConfig { // The width of the texture. For compressed textures (e.g. KTX), this is the // number of bytes in the compressed data. int width; @@ -67,17 +71,17 @@ struct TextureConfig { int height; // The target of the texture (e.g. 2D, cube, etc.) - mjtTexture target; + mjrTextureTarget target; // The format of the pixels in the texture (e.g. RGB8, RGBA8, KTX, etc.) - mjtPixelFormat format; + mjrPixelFormat format; // The color space of the texture (e.g. LINEAR, sRGB, etc.) - mjtColorSpace color_space; + mjrColorSpace color_space; }; // Initializes the TextureConfig to default values. -void DefaultTextureConfig(TextureConfig* config); +void mjr_defaultTextureConfig(mjrTextureConfig* config); // Wrapper around a filament::Texture. class Texture { @@ -90,13 +94,13 @@ class Texture { }; // Creates a texture with the given data. - Texture(filament::Engine* engine, const TextureConfig& config, + Texture(filament::Engine* engine, const mjrTextureConfig& config, InternalFlags flags = InternalFlags()); ~Texture(); // Uploads the given data to the texture. - void Upload(const TextureData& data); + void Upload(const mjrTextureData& data); // Returns the width of the texture. int GetWidth() const { return config_.width; } @@ -121,7 +125,7 @@ class Texture { filament::Engine* engine_ = nullptr; filament::Texture* texture_ = nullptr; - TextureConfig config_; + mjrTextureConfig config_; SphericalHarmonics spherical_harmonics_; bool has_spherical_harmonics_ = false; diff --git a/src/experimental/filament/render_context_filament.cc b/src/experimental/filament/render_context_filament.cc index d49069f5..65b4086a 100644 --- a/src/experimental/filament/render_context_filament.cc +++ b/src/experimental/filament/render_context_filament.cc @@ -21,13 +21,13 @@ #include #include #include -#include "experimental/filament/filament/filament_context.h" +#include "experimental/filament/compat/mjr_filament_renderer.h" #if defined(TLS_FILAMENT_CONTEXT) -static thread_local mujoco::FilamentContext* g_filament_context = nullptr; +static thread_local mujoco::MjrFilamentRenderer* g_filament_context = nullptr; #else -static mujoco::FilamentContext* g_filament_context = nullptr; +static mujoco::MjrFilamentRenderer* g_filament_context = nullptr; #endif static void CheckFilamentContext() { @@ -43,13 +43,13 @@ void mjrf_defaultFilamentConfig(mjrFilamentConfig* config) { } void mjrf_makeFilamentContext(const mjModel* m, mjrContext* con, - const mjrFilamentConfig* config) { + const mjrFilamentConfig* config) { // TODO: Support multiple contexts and multiple threads. For now, we'll just // assume a single, global context. if (g_filament_context != nullptr) { mju_error("Context already exists!"); } - g_filament_context = new mujoco::FilamentContext(config); + g_filament_context = new mujoco::MjrFilamentRenderer(config); g_filament_context->Init(m); } diff --git a/src/experimental/filament/render_context_filament.h b/src/experimental/filament/render_context_filament.h index 9db6554f..28183fec 100644 --- a/src/experimental/filament/render_context_filament.h +++ b/src/experimental/filament/render_context_filament.h @@ -29,11 +29,11 @@ extern "C" { // IMPORTANT: This API should still be considered experimental and is likely // change frequently. -typedef enum mjtGraphicsApi_ { // backend graphics API to use - mjGFX_DEFAULT = 0, // default based on platform - mjGFX_OPENGL, // OpenGL (desktop) - mjGFX_VULKAN // Vulkan -} mjtGraphicsApi; +typedef enum mjrGraphicsApi_ { // backend graphics API to use + mjGRAPHICS_API_DEFAULT = 0, // default based on platform + mjGRAPHICS_API_OPENGL, // OpenGL (desktop) / WebGL + mjGRAPHICS_API_VULKAN // Vulkan +} mjrGraphicsApi; struct mjrFilamentConfig { // The native window handle into which we can render directly. diff --git a/src/experimental/platform/hal/renderer.cc b/src/experimental/platform/hal/renderer.cc index 2dc60e18..a023c54e 100644 --- a/src/experimental/platform/hal/renderer.cc +++ b/src/experimental/platform/hal/renderer.cc @@ -90,8 +90,9 @@ void Renderer::Init(const mjModel* model) { render_config.width = model->vis.global.offwidth; render_config.height = model->vis.global.offheight; render_config.force_software_rendering = IsSoftware(gfx_); - render_config.graphics_api = - IsOpenGl(gfx_) || IsWebGl(gfx_) ? mjGFX_OPENGL : mjGFX_VULKAN; + render_config.graphics_api = IsOpenGl(gfx_) || IsWebGl(gfx_) + ? mjGRAPHICS_API_OPENGL + : mjGRAPHICS_API_VULKAN; mjrf_makeFilamentContext(model, &render_context_, &render_config); render_ = [&](mjrRect rect, mjvScene* scene) { mjrf_render(rect, scene, &render_context_); diff --git a/src/experimental/platform/hal/window.cc b/src/experimental/platform/hal/window.cc index faf9a6d1..961e9ab9 100644 --- a/src/experimental/platform/hal/window.cc +++ b/src/experimental/platform/hal/window.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -46,8 +47,8 @@ extern void* GetNativeWindowOsx(void* window); namespace mujoco::platform { -static void InitImGui(SDL_Window* window, float content_scale, bool load_fonts, - bool build_fonts) { +static void InitImGui(SDL_Window* window, float content_scale, + bool load_fonts) { ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); @@ -84,10 +85,6 @@ static void InitImGui(SDL_Window* window, float content_scale, bool load_fonts, constexpr ImWchar icon_ranges[] = {0xf000, 0xf3ff, 0x000}; io.Fonts->AddFontFromMemoryTTF(data, size, 14.f, &icon_cfg, icon_ranges); - if (build_fonts) { - io.Fonts->Build(); - } - // Note: we purposefully do not "close" the font resources as ImGui may // need them again to resize fonts. } @@ -132,9 +129,7 @@ Window::Window(std::string_view title, int width, int height, Config config) mju_error("Error creating window: %s", SDL_GetError()); } - InitImGui(sdl_window_, content_scale, config.load_fonts, - (config_.gfx_mode != GraphicsMode::ClassicOpenGl && - config_.gfx_mode != GraphicsMode::ClassicOpenGlHeadless)); + InitImGui(sdl_window_, content_scale, config.load_fonts); // Filament (except WebGL) manages its own swap chain including when to swap. // In all other cases, we'll use SDL to manage the swap chain. diff --git a/src/experimental/studio/CMakeLists.txt b/src/experimental/studio/CMakeLists.txt index cafa6a66..6dfd90f5 100644 --- a/src/experimental/studio/CMakeLists.txt +++ b/src/experimental/studio/CMakeLists.txt @@ -14,87 +14,124 @@ cmake_minimum_required(VERSION 3.16) -set(MUJOCO_STUDIO_TARGET_NAME mujoco_studio) - -add_executable(${MUJOCO_STUDIO_TARGET_NAME}) - -target_sources(${MUJOCO_STUDIO_TARGET_NAME} - PRIVATE - app.cc - app.h - main.cc -) - -target_include_directories(${MUJOCO_STUDIO_TARGET_NAME} - PUBLIC - ${PROJECT_SOURCE_DIR}/include - ${PROJECT_SOURCE_DIR}/src -) - -if (WIN32) - target_compile_definitions(${MUJOCO_STUDIO_TARGET_NAME} - PRIVATE - -D_USE_MATH_DEFINES - ) - set_target_properties(${MUJOCO_STUDIO_TARGET_NAME} - PROPERTIES - VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" - ) -endif() - include(third_party_deps/dear_imgui) include(third_party_deps/implot) include(third_party_deps/opensans) include(third_party_deps/font_awesome) -target_link_libraries(${MUJOCO_STUDIO_TARGET_NAME} - PRIVATE - absl::flags - absl::flags_parse - dear_imgui - implot - mujoco::mujoco - mujoco::platform -) - -# TODO: re-enable mjz support on Windows builds once DllMain issue is resolved. -if (NOT WIN32) - target_link_libraries(${MUJOCO_STUDIO_TARGET_NAME} +# Common configuration shared between mujoco_studio and mujoco_live. +function(configure_studio_target TARGET_NAME) + target_sources(${TARGET_NAME} PRIVATE - mujoco::mjz + app.cc + app.h ) -endif() -file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets) + target_include_directories(${TARGET_NAME} + PUBLIC + ${PROJECT_SOURCE_DIR}/include + ${PROJECT_SOURCE_DIR}/src + ) -add_custom_command( - TARGET ${MUJOCO_STUDIO_TARGET_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} - -E copy - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../_deps/opensans-src/fonts/ttf/OpenSans-Regular.ttf - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets - COMMENT "Copying OpenSans-Regular.ttf assets to build directory" -) -add_custom_command( - TARGET ${MUJOCO_STUDIO_TARGET_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} - -E copy - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../_deps/font_awesome-src/fonts/fontawesome-webfont.ttf - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets - COMMENT "Copying fontawesome-webfont.ttf to build directory" -) + if (WIN32) + target_compile_definitions(${TARGET_NAME} + PRIVATE + -D_USE_MATH_DEFINES + ) + set_target_properties(${TARGET_NAME} + PROPERTIES + VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}" + ) + endif() + + target_link_libraries(${TARGET_NAME} + PRIVATE + absl::flags + absl::flags_parse + dear_imgui + implot + mujoco::mujoco + mujoco::platform + ) + + # TODO: re-enable mjz support on Windows builds once DllMain issue is resolved. + if (NOT WIN32) + target_link_libraries(${TARGET_NAME} + PRIVATE + mujoco::mjz + ) + endif() + + file(MAKE_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets) -# Filament backend requires additional files to be copied into an "assets" folder. -if(MUJOCO_USE_FILAMENT) add_custom_command( - TARGET ${MUJOCO_STUDIO_TARGET_NAME} + TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} - -E copy_directory - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../src/experimental/filament/assets + -E copy + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../_deps/opensans-src/fonts/ttf/OpenSans-Regular.ttf ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets - COMMENT "Copying Filament assets to build directory" + COMMENT "Copying OpenSans-Regular.ttf assets to build directory" + ) + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} + -E copy + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../_deps/font_awesome-src/fonts/fontawesome-webfont.ttf + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets + COMMENT "Copying fontawesome-webfont.ttf to build directory" + ) + + # Filament backend requires additional files to be copied into an "assets" folder. + if(MUJOCO_USE_FILAMENT) + add_custom_command( + TARGET ${TARGET_NAME} + POST_BUILD + COMMAND ${CMAKE_COMMAND} + -E copy_directory + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../src/experimental/filament/assets + ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/assets + COMMENT "Copying Filament assets to build directory" + ) + endif() +endfunction() + +# Desktop Studio target. +if(NOT EMSCRIPTEN) + add_executable(mujoco_studio) + target_sources(mujoco_studio PRIVATE main.cc) + configure_studio_target(mujoco_studio) +endif() + +# WASM Live target. +if(EMSCRIPTEN) + add_executable(mujoco_live) + target_sources(mujoco_live PRIVATE wasm.cc) + target_compile_options(mujoco_live PRIVATE -g) + configure_studio_target(mujoco_live) + target_link_libraries(mujoco_live PRIVATE mujoco::render_noop) + + # Ensure resource decoder plugins are linked into the WASM binary. + target_link_options(mujoco_live PRIVATE + -Wl,--whole-archive $ -Wl,--no-whole-archive + ) + + # Filament's OpenGL backend requires WebGL2 / full ES3 bindings. + target_link_options(mujoco_live PRIVATE + --bind + -sUSE_WEBGL2=1 + -sFULL_ES3 + -sMIN_WEBGL_VERSION=2 + -sMAX_WEBGL_VERSION=2 + -sALLOW_MEMORY_GROWTH=1 + -sASYNCIFY=1 + -sFETCH=1 + -sGL_PREINITIALIZED_CONTEXT=1 + -sSTACK_SIZE=512mb + -sINITIAL_MEMORY=1024mb + -sASSERTIONS=1 + -fexceptions + -g ) endif() diff --git a/src/experimental/studio/index.html b/src/experimental/studio/index.html index 8eb3190f..ec40e14e 100644 --- a/src/experimental/studio/index.html +++ b/src/experimental/studio/index.html @@ -1,8 +1,8 @@ - + - MuJoCo Studio! + MuJoCo Live
@@ -16,6 +16,28 @@ style="width: 100vw; height: 100vh; display: block" > - + diff --git a/src/experimental/studio/main.cc b/src/experimental/studio/main.cc index 9d48834f..ca84aa4f 100644 --- a/src/experimental/studio/main.cc +++ b/src/experimental/studio/main.cc @@ -75,7 +75,7 @@ class FileResource { int main(int argc, char** argv, char** envp) { absl::ParseCommandLine(argc, argv); - const char* home = getenv("HOME"); + const char* home = std::getenv("HOME"); const std::string ini_path = std::string(home ? home : ".") + "/.mujoco.ini"; mjpResourceProvider resource_provider; @@ -103,6 +103,20 @@ int main(int argc, char** argv, char** envp) { std::string gfx = absl::GetFlag(FLAGS_gfx); + const char* session_type = std::getenv("XDG_SESSION_TYPE"); + const char* wayland_display = std::getenv("WAYLAND_DISPLAY"); + if ((session_type && std::string_view(session_type) == "wayland") || + wayland_display) { + if (gfx.empty()) { + gfx = "opengl_headless"; + } else if (gfx == "classic" || gfx == "opengl") { + mju_error( + "Wayland does not support '%s' graphics mode. " + "Restart with a different graphics mode, or login using X11.", + gfx.c_str()); + } + } + mujoco::platform::GraphicsMode gfx_mode = mujoco::platform::GraphicsModeFromString( gfx, mujoco::platform::GraphicsMode::FilamentOpenGl); diff --git a/src/experimental/studio/wasm.cc b/src/experimental/studio/wasm.cc index 8e5e8768..29da5294 100644 --- a/src/experimental/studio/wasm.cc +++ b/src/experimental/studio/wasm.cc @@ -18,7 +18,11 @@ #include #include +#include +#include +#include #include +#include #include #include #include @@ -59,6 +63,84 @@ class AssetRegistry { std::unordered_map assets_; }; +// --------------------------------------------------------------------------- +// HTTP/HTTPS resource fetching via the JS fetch API (uses ASYNCIFY to yield). +// --------------------------------------------------------------------------- + +// Fetches a URL using the JS fetch API. Returns a malloc'd buffer and its size. +// The caller is responsible for freeing the buffer. Returns 0 on failure. +EM_ASYNC_JS(int, FetchUrl, + (const char* url, char** out_data, std::int32_t* out_size), { + try { + const urlStr = UTF8ToString(url); + const response = await fetch(urlStr); + if (!response.ok) { + console.error('Fetch failed: ' + response.status + ' ' + + urlStr); + return 0; + } + const buffer = await response.arrayBuffer(); + const bytes = new Uint8Array(buffer); + const ptr = _malloc(bytes.length); + HEAPU8.set(bytes, ptr); + setValue(out_data, ptr, '*'); + setValue(out_size, bytes.length, 'i32'); + return 1; + } catch (e) { + console.error('Fetch error:', e); + return 0; + } + }); + +// Cache for data fetched via HTTP/HTTPS. Stores the downloaded bytes keyed by +// the resource name (URL) so that read() can return a pointer to the data. +class FetchCache { + public: + static FetchCache& Instance() { + static FetchCache instance; + return instance; + } + + // Fetches the URL and stores the result. Returns the size (>0) on success. + int Fetch(const char* url) { + char* data = nullptr; + std::int32_t size = 0; + if (!FetchUrl(url, &data, &size)) { + return 0; + } + entries_[url] = Entry{UniquePtrWasm(data), size}; + return size; + } + + // Returns pointer and size for a previously fetched URL. + int Read(const char* url, const void** buffer) { + auto it = entries_.find(url); + if (it == entries_.end()) { + return -1; + } + *buffer = it->second.data.get(); + return it->second.size; + } + + // Frees the data for a URL. + void Close(const char* url) { entries_.erase(url); } + + private: + struct FreeDeleter { + void operator()(void* p) const { std::free(p); } + }; + template + using UniquePtrWasm = std::unique_ptr; + + struct Entry { + UniquePtrWasm data; + int size; + }; + std::unordered_map entries_; +}; + +// --------------------------------------------------------------------------- + // Javascript-facing function to register an asset. void RegisterAsset(std::string filename, std::string contents) { AssetRegistry::Instance().RegisterAsset(std::move(filename), @@ -92,6 +174,27 @@ void Init() { resource_provider.prefix = "filament"; mjp_registerResourceProvider(&resource_provider); + // Register HTTP/HTTPS resource providers so that models loaded from URLs + // can automatically fetch referenced assets (meshes, textures, etc.) over + // the network. + mjpResourceProvider http_provider; + mjp_defaultResourceProvider(&http_provider); + + http_provider.open = [](mjResource* resource) { + return FetchCache::Instance().Fetch(resource->name); + }; + http_provider.read = [](mjResource* resource, const void** buffer) { + return FetchCache::Instance().Read(resource->name, buffer); + }; + http_provider.close = [](mjResource* resource) { + FetchCache::Instance().Close(resource->name); + }; + + http_provider.prefix = "http"; + mjp_registerResourceProvider(&http_provider); + http_provider.prefix = "https"; + mjp_registerResourceProvider(&http_provider); + g_app = new mujoco::studio::App({ .width = width, .height = height, @@ -124,6 +227,17 @@ void LoadFile(const std::string& filename, const std::string& data) { g_app->LoadModelFromBuffer({ptr, ptr + data.size()}, content_type, filename); } +// Javascript-facing function to load a model from a URL. +// The URL is passed directly to LoadModelFromFile, which will use the +// registered HTTP/HTTPS resource providers to fetch the model and any +// referenced assets. +void LoadUrl(const std::string& url) { + if (!g_app) { + return; + } + g_app->LoadModelFromFile(url); +} + // Javascript-facing function to render a single frame. void RenderFrame() { if (g_app) { @@ -144,6 +258,7 @@ EMSCRIPTEN_BINDINGS(studio_bindings) { emscripten::function("registerAsset", &RegisterAsset); emscripten::function("init", &Init); emscripten::function("loadFile", &LoadFile); + emscripten::function("loadUrl", &LoadUrl); emscripten::function("renderFrame", &RenderFrame); emscripten::function("deinit", &Deinit); } diff --git a/src/render/noop/CMakeLists.txt b/src/render/noop/CMakeLists.txt index 782c4858..313ca063 100644 --- a/src/render/noop/CMakeLists.txt +++ b/src/render/noop/CMakeLists.txt @@ -12,4 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -target_sources(mujoco PRIVATE render_noop.c) +add_library(render_noop STATIC render_noop.c) +target_link_libraries(render_noop PUBLIC mujoco::mujoco) +add_library(mujoco::render_noop ALIAS render_noop) diff --git a/unity/Editor/Bindings/MujocoBinaryRetriever.cs b/unity/Editor/Bindings/MujocoBinaryRetriever.cs index b3ee6cee..be5112ab 100644 --- a/unity/Editor/Bindings/MujocoBinaryRetriever.cs +++ b/unity/Editor/Bindings/MujocoBinaryRetriever.cs @@ -37,7 +37,7 @@ public class MujocoBinaryRetriever { if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/mujoco.dylib") == null) { File.Copy( "/Applications/MuJoCo.app/Contents/Frameworks" + - "/mujoco.framework/Versions/Current/libmujoco.3.7.1.dylib", + "/mujoco.framework/Versions/Current/libmujoco.3.8.1.dylib", mujocoPath + "/mujoco.dylib"); AssetDatabase.Refresh(); } @@ -45,7 +45,7 @@ public class MujocoBinaryRetriever { if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/libmujoco.so") == null) { File.Copy( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + - "/.mujoco/mujoco-3.7.1/lib/libmujoco.so.3.7.1", + "/.mujoco/mujoco-3.8.1/lib/libmujoco.so.3.8.1", mujocoPath + "/libmujoco.so"); AssetDatabase.Refresh(); } diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index e3bd9fe9..7f8d625d 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -113,7 +113,7 @@ public const int mjMAXLINEPNT = 1001; public const int mjMAXPLANEGRID = 200; public const bool THIRD_PARTY_MUJOCO_MJXMACRO_H_ = true; public const bool THIRD_PARTY_MUJOCO_MUJOCO_H_ = true; -public const int mjVERSION_HEADER = 3007001; +public const int mjVERSION_HEADER = 3008001; // ------------------------------------Enums------------------------------------ diff --git a/unity/package.json b/unity/package.json index 3f32a5c5..4e9bf364 100644 --- a/unity/package.json +++ b/unity/package.json @@ -1,7 +1,7 @@ { "name": "org.mujoco", "displayName": "MuJoCo", - "version": "3.7.1", + "version": "3.8.1", "description": "MuJoCo importer and runtime plug-in", "dependencies": {}, "author": {