Merge branch 'google-deepmind:main' into mjx-warp-segmentation
This commit is contained in:
@@ -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=()
|
||||
|
||||
@@ -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
|
||||
+40
-2
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 <cstring>, and
|
||||
# PostProcessManager.h uses std::optional without including <optional>.
|
||||
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}")
|
||||
|
||||
Vendored
+4
-4
@@ -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"
|
||||
|
||||
Vendored
+4
-4
@@ -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"
|
||||
|
||||
@@ -388,7 +388,7 @@ Defined in `mujoco.h <https://github.com/google-deepmind/mujoco/blob/main/includ
|
||||
- value
|
||||
- description
|
||||
* - ``mjVERSION_HEADER``
|
||||
- 3007001
|
||||
- 3008001
|
||||
- The version of the MuJoCo headers. This is an integer calculated from the version string "S.M.P"
|
||||
using the formula ``(S * 1e6) + (M * 1e3) + P``. For example, version 4.2.1 is represented as 4002001.
|
||||
The API function :ref:`mj_version` returns a number with the same meaning
|
||||
|
||||
+24
-17
@@ -2,34 +2,41 @@
|
||||
Changelog
|
||||
=========
|
||||
|
||||
Upcoming version (not yet released)
|
||||
-----------------------------------
|
||||
Version 3.8.0 (April 24, 2026)
|
||||
------------------------------
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
- Added new :ref:`mj_maxContact<mj_maxContact>` 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<body-flexcomp-cellnum>` 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 <equality-flexstrain-cell>`
|
||||
1. Added support for Python 3.14.
|
||||
2. Added :ref:`multi-cell support<body-flexcomp-cellcount>` 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<flexcomp-edge-equality>` 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 <equality-flexstrain-cell>`
|
||||
4. Added new :ref:`mj_maxContact<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<coMultiCCD>` is now enabled by default. This feature has little performance overhead
|
||||
and gives better contact behavior for stability.
|
||||
6. The :ref:`multiccd<coMultiCCD>` 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<option-flag-multiccd>` must be explicitly disabled.
|
||||
**Migration:** Disable :ref:`multiccd<option-flag-multiccd>` to recover the previous behavior.
|
||||
|
||||
Documentation
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
7. Added :ref:`documentation<exDecoder>` 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)
|
||||
------------------------------
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
Extensions
|
||||
----------
|
||||
|
||||
This section describes MuJoCo's mechanisms for user-authored extensions. At present, extensibility is provided
|
||||
via :ref:`engine plugins<exPlugin>` and :ref:`resource providers<exProvider>`.
|
||||
This section describes MuJoCo's mechanisms for user-authored extensions. At present, extensibility is provided by
|
||||
via :ref:`engine plugins<exPlugin>`, :ref:`decoders<exDecoder>`, and :ref:`resource providers<exProvider>`.
|
||||
|
||||
.. _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<mjPLUGIN_LIB_INIT>`
|
||||
similarly to other MuJoCo plugins.
|
||||
|
||||
MuJoCo ships with two built-in decoders for common mesh formats:
|
||||
|
||||
- **OBJ decoder** (``plugin/obj_decoder``) -- `Wavefront OBJ <https://en.wikipedia.org/wiki/Wavefront_.obj_file>`_.
|
||||
- **STL decoder** (``plugin/stl_decoder``) -- `STL <https://en.wikipedia.org/wiki/STL_(file_format)>`_.
|
||||
|
||||
Additionally, we provide the following optional decoder plugins:
|
||||
|
||||
- **USD decoder** (``plugin/usd_decoder``) -- `Universal Scene Description <https://openusd.org/release/index.html>`_.
|
||||
|
||||
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 <mujoco.h>
|
||||
|
||||
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
|
||||
|
||||
<asset>
|
||||
<mesh file="my_mesh.myf"/>
|
||||
</asset>
|
||||
|
||||
|
||||
.. _exProvider:
|
||||
|
||||
Resource providers
|
||||
|
||||
+2
-2
@@ -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
|
||||
_______
|
||||
|
||||
@@ -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 <stdlib.h>
|
||||
|
||||
+19
-15
@@ -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 \
|
||||
|
||||
+13
-17
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
+111
-48
@@ -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")
|
||||
|
||||
|
||||
###############################################################################
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
+5
-4
@@ -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
|
||||
|
||||
+177
-78
@@ -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
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.mujoco.mjpython</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.7.1</string>
|
||||
<string>3.8.1</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>3.7.1</string>
|
||||
<string>3.8.1</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>3.7.1</string>
|
||||
<string>3.8.1</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.7.1</string>
|
||||
<string>3.8.1</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>mjpython</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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] = {
|
||||
|
||||
@@ -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()
|
||||
|
||||
+47
-43
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -23,12 +23,11 @@
|
||||
#include <imgui.h>
|
||||
#include <math/mat3.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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<SceneView>(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<std::byte*>(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<Mesh>(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<Renderable>(object_mgr_, config));
|
||||
std::make_unique<Renderable>(object_mgr_, params));
|
||||
r->SetCastShadows(false);
|
||||
r->SetReceiveShadows(false);
|
||||
r->SetBlendOrder(static_cast<std::uint16_t>(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();
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -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 <cstdint>
|
||||
#include <memory>
|
||||
@@ -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<SceneView> scene_view_;
|
||||
std::vector<std::unique_ptr<Renderable>> renderables_;
|
||||
std::vector<std::unique_ptr<Mesh>> meshes_;
|
||||
std::unordered_map<uintptr_t, std::unique_ptr<Texture>> 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_
|
||||
+2
-2
@@ -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 <algorithm>
|
||||
#include <any>
|
||||
@@ -34,8 +34,8 @@
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/scalar.h>
|
||||
#include <math/vec3.h>
|
||||
#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 {
|
||||
+4
-4
@@ -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_
|
||||
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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<SceneBridge>(GetObjectManager(), model);
|
||||
imgui_bridge_ = std::make_unique<ImguiBridge>(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<RenderTarget>(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<RenderTarget>(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<uint8_t*>(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
|
||||
@@ -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 <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjrender.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#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<SceneBridge> scene_bridge_;
|
||||
std::unique_ptr<ImguiBridge> imgui_bridge_;
|
||||
};
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_MJR_FILAMENT_RENDERER_H_
|
||||
+30
-30
@@ -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 <array>
|
||||
#include <algorithm>
|
||||
@@ -412,7 +412,7 @@ static std::span<const int> 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<uint16_t>::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<Mesh>(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<Mesh>(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<Mesh>(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<Mesh>(engine_, data);
|
||||
}
|
||||
|
||||
+3
-3
@@ -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 <array>
|
||||
#include <memory>
|
||||
@@ -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_
|
||||
+44
-33
@@ -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 <memory>
|
||||
#include <optional>
|
||||
@@ -36,16 +36,15 @@
|
||||
#include <math/vec4.h>
|
||||
#include <math/TVecHelpers.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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<Texture> CreateFallbackIndirectLightTexture(
|
||||
|
||||
std::unique_ptr<ObjectManager::Asset> 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<Texture> CreateFallbackIndirectLightTexture(
|
||||
|
||||
auto texture = std::make_unique<Texture>(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<ObjectManager::Asset*>(user_data);
|
||||
@@ -87,9 +86,9 @@ static std::unique_ptr<Texture> 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<SceneView>(object_mgr_->GetEngine());
|
||||
model_objects_ =
|
||||
std::make_unique<ModelObjects>(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<Light>(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<Light>(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<Light>(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];
|
||||
+8
-9
@@ -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 <memory>
|
||||
#include <optional>
|
||||
@@ -24,9 +24,8 @@
|
||||
#include <math/vec3.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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<filament::math::float3> ClipFromWorld(
|
||||
const filament::math::float3& pos) const;
|
||||
|
||||
SceneView* scene_view_ = nullptr;
|
||||
std::unique_ptr<SceneView> scene_view_;
|
||||
ObjectManager* object_mgr_ = nullptr;
|
||||
std::unique_ptr<ModelObjects> model_objects_;
|
||||
std::unique_ptr<Light> 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_
|
||||
+40
-39
@@ -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 <cmath>
|
||||
#include <cstdint>
|
||||
@@ -22,20 +22,17 @@
|
||||
|
||||
#include <filament/Material.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/Scene.h>
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/TransformManager.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <utils/Entity.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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<float>(red) / 255.0f;
|
||||
params.segmentation_color.y = static_cast<float>(green) / 255.0f;
|
||||
params.segmentation_color.z = static_cast<float>(blue) / 255.0f;
|
||||
params.segmentation_color[0] = static_cast<float>(red) / 255.0f;
|
||||
params.segmentation_color[1] = static_cast<float>(green) / 255.0f;
|
||||
params.segmentation_color[2] = static_cast<float>(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<float>(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<Renderable> 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<Renderable>(object_mgr, config);
|
||||
mjrRenderableParams params;
|
||||
mjr_defaultRenderableParams(¶ms);
|
||||
params.shading_model = shading_model;
|
||||
auto renderable = std::make_unique<Renderable>(object_mgr, params);
|
||||
|
||||
PrepareGeomMeshes(*renderable, geom, scene, model_objs);
|
||||
UpdateGeomMaterial(*renderable, geom, scene, model_objs, object_mgr, headpos);
|
||||
+4
-5
@@ -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 <memory>
|
||||
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#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<Renderable> CreateGeomRenderable(
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_SCENE_GEOM_UTIL_H_
|
||||
#endif // MUJOCO_SRC_EXPERIMENTAL_FILAMENT_COMPAT_SCENE_GEOM_UTIL_H_
|
||||
@@ -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 <typename T, typename... Args>
|
||||
static std::unique_ptr<Mesh> Create(filament::Engine* engine,
|
||||
Args&&... args) {
|
||||
auto builder = new T(std::forward<Args>(args)...);
|
||||
MeshData* mesh_data = builder->PrepareMeshData();
|
||||
mjrMeshData* mesh_data = builder->PrepareMeshData();
|
||||
mesh_data->release_callback = +[](void* user_data) {
|
||||
delete static_cast<BuiltinBuilder*>(user_data);
|
||||
};
|
||||
@@ -76,13 +76,13 @@ class BuiltinBuilder : MeshData {
|
||||
return std::make_unique<Mesh>(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<const void*>(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<const void*>(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;
|
||||
|
||||
@@ -14,13 +14,11 @@
|
||||
|
||||
#include "experimental/filament/filament/filament_context.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/IndirectLight.h>
|
||||
@@ -35,19 +33,11 @@
|
||||
#include <math/vec4.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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<SceneView>(engine_);
|
||||
scene_bridge_ = std::make_unique<SceneBridge>(object_manager_.get(),
|
||||
scene_view_.get(), model);
|
||||
imgui_bridge_ =
|
||||
std::make_unique<ImguiBridge>(object_manager_.get(), scene_view_.get());
|
||||
FilamentContext::FrameHandle FilamentContext::Render(
|
||||
std::span<const RenderRequest> requests,
|
||||
std::span<const ReadPixelsRequest> 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<RenderTarget>(engine_, config);
|
||||
color_target_->Prepare(width, height);
|
||||
|
||||
config.color_format = mjPIXEL_FORMAT_R32F;
|
||||
config.depth_format = mjPIXEL_FORMAT_DEPTH32F;
|
||||
depth_target_ = std::make_unique<RenderTarget>(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<uint8_t*>(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<filament::Renderer::FrameInfo> frame_info =
|
||||
renderer_->getFrameInfoHistory(1);
|
||||
@@ -306,6 +199,4 @@ double FilamentContext::GetFrameRate() const {
|
||||
return 1.0e9 / static_cast<double>(ns);
|
||||
}
|
||||
|
||||
void FilamentContext::UpdateGui() { DrawGui(scene_bridge_.get()); }
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -15,64 +15,102 @@
|
||||
#ifndef MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_FILAMENT_CONTEXT_H_
|
||||
#define MUJOCO_SRC_EXPERIMENTAL_FILAMENT_FILAMENT_FILAMENT_CONTEXT_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
#include <memory>
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Renderer.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mjrender.h>
|
||||
#include <filament/SwapChain.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#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<const RenderRequest> render_requests,
|
||||
std::span<const ReadPixelsRequest> 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<filament::backend::Platform> 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<RenderTarget> color_target_;
|
||||
std::unique_ptr<RenderTarget> depth_target_;
|
||||
std::unique_ptr<ObjectManager> object_manager_;
|
||||
std::unique_ptr<SceneView> scene_view_;
|
||||
std::unique_ptr<SceneBridge> scene_bridge_;
|
||||
std::unique_ptr<ImguiBridge> imgui_bridge_;
|
||||
int window_width_ = 0;
|
||||
int window_height_ = 0;
|
||||
std::uint64_t frame_counter_ = 0;
|
||||
};
|
||||
|
||||
} // namespace mujoco
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/EntityManager.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,20 +14,59 @@
|
||||
|
||||
#include "experimental/filament/filament/material.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <filament/Color.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "experimental/filament/filament/math_util.h"
|
||||
#include "experimental/filament/filament/texture.h"
|
||||
#include "experimental/filament/filament/object_manager.h"
|
||||
|
||||
namespace mujoco {
|
||||
|
||||
template <int N>
|
||||
static void setf(float (&arr)[N], const std::array<float, N>& 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);
|
||||
|
||||
@@ -17,49 +17,51 @@
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
@@ -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<SharedState>()) {
|
||||
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<Mesh*>(user)->ReleaseResources();
|
||||
auto* state_ptr = static_cast<std::shared_ptr<SharedState>*>(user);
|
||||
auto state = *state_ptr;
|
||||
delete state_ptr;
|
||||
|
||||
std::lock_guard<std::mutex> 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<SharedState>(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<SharedState>(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<uint16_t>(sequence, num_bytes);
|
||||
} else {
|
||||
FillSequence<uint32_t>(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<const float*>(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<const float*>(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<std::mutex> 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 {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
@@ -29,66 +30,67 @@
|
||||
#include <filament/RenderableManager.h>
|
||||
#include <filament/VertexBuffer.h>
|
||||
#include <math/vec4.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
|
||||
// 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<filament::Box> bounds_;
|
||||
std::vector<std::function<void()>> release_callbacks_;
|
||||
std::array<filament::VertexAttribute, kMaxVertexAttributes> attributes_;
|
||||
struct SharedState {
|
||||
std::vector<std::function<void()>> callbacks;
|
||||
std::mutex mutex;
|
||||
bool called = false;
|
||||
};
|
||||
std::shared_ptr<SharedState> shared_state_;
|
||||
std::array<filament::VertexAttribute, mjMAX_VERTEX_ATTRIBUTES> attributes_;
|
||||
int num_attributes_ = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -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<Texture>(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;
|
||||
|
||||
@@ -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<Texture> color_texture_ = nullptr;
|
||||
std::unique_ptr<Texture> depth_texture_ = nullptr;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<Part> parts_;
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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<Renderable*> renderables_;
|
||||
filament::Skybox* skybox_ = nullptr;
|
||||
|
||||
// Custom view for UX.
|
||||
filament::View* ux_view_ = nullptr;
|
||||
filament::Camera* ux_camera_ = nullptr;
|
||||
std::unordered_set<Renderable*> 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<Renderable*> reflectives_;
|
||||
std::vector<std::unique_ptr<RenderTarget>> reflect_targets_;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
#include <filament/Texture.h>
|
||||
#include <math/vec3.h>
|
||||
#include <mujoco/mjmodel.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
#include <mujoco/mjrender.h>
|
||||
#include <mujoco/mjvisualize.h>
|
||||
#include <mujoco/mujoco.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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_);
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -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.
|
||||
|
||||
@@ -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 $<TARGET_FILE:mujoco> -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()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>MuJoCo Studio!</title>
|
||||
<title>MuJoCo Live</title>
|
||||
</head>
|
||||
<body style="margin: 0; overflow: hidden">
|
||||
<div style="position: absolute; top: 3px; right: 3px; z-index: 1;">
|
||||
@@ -16,6 +16,28 @@
|
||||
style="width: 100vw; height: 100vh; display: block"
|
||||
></canvas>
|
||||
<script>
|
||||
// --- Loading overlay (shown while model is compiling) ---
|
||||
var loadingOverlay = document.createElement('div');
|
||||
loadingOverlay.id = 'loadingOverlay';
|
||||
loadingOverlay.style.cssText =
|
||||
'position:fixed;top:0;left:0;width:100%;height:100%;' +
|
||||
'background:rgba(0,0,0,0.6);z-index:10000;display:none;' +
|
||||
'align-items:center;justify-content:center;';
|
||||
loadingOverlay.innerHTML =
|
||||
'<div style="color:#fff;font-size:24px;font-family:sans-serif;' +
|
||||
'text-align:center;">' +
|
||||
'<div style="margin-bottom:16px;">Loading model\u2026</div>' +
|
||||
'<div style="font-size:14px;opacity:0.7;">This may take a moment ' +
|
||||
'for large models.</div></div>';
|
||||
document.body.appendChild(loadingOverlay);
|
||||
|
||||
function showLoading() {
|
||||
loadingOverlay.style.display = 'flex';
|
||||
}
|
||||
function hideLoading() {
|
||||
loadingOverlay.style.display = 'none';
|
||||
}
|
||||
|
||||
var Module = {
|
||||
preRun: [],
|
||||
postRun: [],
|
||||
@@ -46,8 +68,30 @@
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies(left) {},
|
||||
onRuntimeInitialized: () => {
|
||||
// Define assets to prefetch. These paths are relative to the wasm_binary/ directory.
|
||||
// Define assets to prefetch. These paths are relative to the mujoco_live_wasm/ directory.
|
||||
const assetsToPrefetch = [
|
||||
"assets/fontawesome-webfont.ttf",
|
||||
"assets/ibl.ktx",
|
||||
"assets/OpenSans-Regular.ttf",
|
||||
"assets/pbr.filamat",
|
||||
"assets/pbr_packed.filamat",
|
||||
"assets/phong_2d_fade.filamat",
|
||||
"assets/phong_2d.filamat",
|
||||
"assets/phong_2d_reflect.filamat",
|
||||
"assets/phong_2d_uv_fade.filamat",
|
||||
"assets/phong_2d_uv.filamat",
|
||||
"assets/phong_2d_uv_reflect.filamat",
|
||||
"assets/phong_color_fade.filamat",
|
||||
"assets/phong_color.filamat",
|
||||
"assets/phong_color_reflect.filamat",
|
||||
"assets/phong_cube_fade.filamat",
|
||||
"assets/phong_cube.filamat",
|
||||
"assets/phong_cube_reflect.filamat",
|
||||
"assets/unlit_decor.filamat",
|
||||
"assets/unlit_depth.filamat",
|
||||
"assets/unlit_line.filamat",
|
||||
"assets/unlit_segmentation.filamat",
|
||||
"assets/unlit_ui.filamat"
|
||||
];
|
||||
|
||||
const assetPromises = assetsToPrefetch.map(async (relativePath) => {
|
||||
@@ -71,7 +115,31 @@
|
||||
.then(() => {
|
||||
try {
|
||||
Module.init();
|
||||
requestAnimationFrame(Module.animate);
|
||||
|
||||
// Check for a ?model= URL parameter and load from URL.
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const modelUrl = params.get('model');
|
||||
if (modelUrl) {
|
||||
// loadUrl uses ASYNCIFY (via EM_ASYNC_JS fetch), which
|
||||
// suspends the WASM module. We must not start the animation
|
||||
// loop until the load completes, otherwise renderFrame will
|
||||
// hit "Cannot have multiple async operations in flight".
|
||||
showLoading();
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(async () => {
|
||||
try {
|
||||
await Module.loadUrl(modelUrl);
|
||||
} catch (error) {
|
||||
console.error('Failed to load model from URL:', error);
|
||||
} finally {
|
||||
hideLoading();
|
||||
requestAnimationFrame(Module.animate);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
requestAnimationFrame(Module.animate);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to initialize app.', error);
|
||||
}
|
||||
@@ -95,6 +163,31 @@
|
||||
Module.canvas.style.height = window.innerHeight + "px";
|
||||
});
|
||||
|
||||
function handleFile(file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const buffer = e.target.result;
|
||||
// Show the loading overlay, then defer the blocking WASM call by two
|
||||
// animation frames so the browser has a chance to paint the overlay.
|
||||
showLoading();
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
try {
|
||||
Module.loadFile(file.name, buffer);
|
||||
} catch (error) {
|
||||
console.error('Failed to load model from file:', error);
|
||||
} finally {
|
||||
hideLoading();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
reader.onerror = (e) => {
|
||||
console.error('Error reading file:', e);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const uploadButton = document.getElementById('uploadButton');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
@@ -108,23 +201,55 @@
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
handleFile(file);
|
||||
});
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const buffer = e.target.result;
|
||||
try {
|
||||
Module.loadFile(file.name, buffer);
|
||||
} catch (error) {
|
||||
console.error('Failed to load model from file:', error);
|
||||
}
|
||||
};
|
||||
reader.onerror = (e) => {
|
||||
console.error('Error reading file:', e);
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
// --- Drag-and-drop support ---
|
||||
const dropOverlay = document.createElement('div');
|
||||
dropOverlay.id = 'dropOverlay';
|
||||
dropOverlay.style.cssText =
|
||||
'position:fixed;top:0;left:0;width:100%;height:100%;' +
|
||||
'background:rgba(0,0,0,0.5);z-index:9999;display:none;' +
|
||||
'pointer-events:none;align-items:center;justify-content:center;';
|
||||
dropOverlay.innerHTML =
|
||||
'<div style="color:#fff;font-size:24px;font-family:sans-serif;' +
|
||||
'padding:32px 48px;border:3px dashed #fff;border-radius:16px;">' +
|
||||
'Drop model file here</div>';
|
||||
document.body.appendChild(dropOverlay);
|
||||
|
||||
let dragCounter = 0;
|
||||
|
||||
document.addEventListener('dragenter', (e) => {
|
||||
e.preventDefault();
|
||||
dragCounter++;
|
||||
if (dragCounter === 1) {
|
||||
dropOverlay.style.display = 'flex';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('dragleave', (e) => {
|
||||
e.preventDefault();
|
||||
dragCounter--;
|
||||
if (dragCounter === 0) {
|
||||
dropOverlay.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
document.addEventListener('drop', (e) => {
|
||||
e.preventDefault();
|
||||
dragCounter = 0;
|
||||
dropOverlay.style.display = 'none';
|
||||
const files = e.dataTransfer.files;
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
handleFile(files[i]);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script async src="/bin/wasm.js"></script>
|
||||
<script async src="bin/mujoco_live.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -18,7 +18,11 @@
|
||||
#include <emscripten/bind.h>
|
||||
#include <emscripten/val.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
@@ -59,6 +63,84 @@ class AssetRegistry {
|
||||
std::unordered_map<std::string, std::string> 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<char[]>(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 <typename T>
|
||||
using UniquePtrWasm = std::unique_ptr<T, FreeDeleter>;
|
||||
|
||||
struct Entry {
|
||||
UniquePtrWasm<char[]> data;
|
||||
int size;
|
||||
};
|
||||
std::unordered_map<std::string, Entry> 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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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------------------------------------
|
||||
|
||||
+1
-1
@@ -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": {
|
||||
|
||||
Reference in New Issue
Block a user