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