diff --git a/.clang-format b/.clang-format index 7606a58a..da5f07f3 100644 --- a/.clang-format +++ b/.clang-format @@ -5,6 +5,13 @@ # CoreUObject/Public/VerseVM/.clang-format), with ONE deliberate deviation: # # SortIncludes: false +# LineEnding: LF +# +# Epic's config leaves the line ending to whatever the file already has, which +# on Windows means clang-format preserves the CRLF that core.autocrlf wrote. +# Pinning it makes the formatter normalise, so a file formatted on Windows and +# one formatted on Linux are the same bytes. `.gitattributes` pins the same +# thing for the repository; this is the half that runs before a commit exists. # # Epic ships SortIncludes: true, but those engine subsystems have no # reflection/generated headers. URLab's UPROPERTY modules require the @@ -62,6 +69,7 @@ IndentCaseLabels: true IndentPPDirectives: None IndentWidth: 4 Language: Cpp +LineEnding: LF NamespaceIndentation: None PackConstructorInitializers: CurrentLine PointerAlignment: Left diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 8416b890..ade140d6 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -6,3 +6,6 @@ # style: adopt Epic clang-format + codegen format-step + repo reformat 1a57be12debd08eb60a2db0a5503b0a64c8ad678 + +# style: reformat hand-written sources and pin line endings to LF +3381d8597bbb3106d826d6ffb369a82fce531ffb diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..f7edfed4 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,35 @@ +# Line endings are LF in the repository and LF on checkout, on every platform. +# +# Without this, `core.autocrlf=true` (the Windows default) commits CRLF, and a +# file touched on Windows shows up as changed in its entirety on Linux and in +# review. Pinning it here rather than asking every contributor to configure +# their client is what keeps that out of the history. +* text=auto eol=lf + +# Binary, and never to be sniffed for it. Content sniffing gets these right +# today; saying so outright means it cannot get them wrong after an edit that +# happens to remove every NUL byte from a header. +*.png binary +*.uasset binary +*.umap binary +*.stl binary +*.obj binary +*.mjb binary + +# The emitted MuJoCo document profile. +# +# LF-pinned because the drift gate compares generated bytes: on Windows, +# core.autocrlf would rewrite these on checkout and the next `--check` would +# report a difference that is nothing but line endings. Marked generated so +# review tools collapse them and language statistics ignore them. +Source/URLab/Public/MuJoCo/Gen/** text eol=lf linguist-generated=true +Source/URLab/Private/MuJoCo/Gen/** text eol=lf linguist-generated=true +Source/URLabEditor/Private/Tests/MjGenCoverage.gen.cpp text eol=lf linguist-generated=true +Scripts/regen_ue_profile.ps1 text eol=lf +Scripts/regen_ue_profile.sh text eol=lf + +# Recorded models. These are compared field for field after being loaded back, +# so any rewriting on checkout would surface as a corrupt golden rather than as +# the line-ending change it actually was. Pinned rather than left to content +# sniffing, because a golden is only worth what its bytes are. +Content/TestData/goldens/*.mjb binary diff --git a/.gitignore b/.gitignore index 8dd8005c..f2bc7f79 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,7 @@ ThirdParty *.lo *.o *.obj +!Content/TestData/**/*.obj # Precompiled Headers *.gch @@ -119,6 +120,9 @@ third_party/install/ # submodule checkout itself is tracked via the submodule pointer; only the # per-build scratch directories inside each submodule are ignored. third_party/*/src/build/ +# Per-dep build scratch dirs that sit beside src/ rather than inside it +# (e.g. third_party/MjShim/build/). +third_party/*/build/ # Python cache __pycache__/ diff --git a/.gitmodules b/.gitmodules index 08f5c1a2..65127c63 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,7 @@ [submodule "third_party/MuJoCo/src"] path = third_party/MuJoCo/src + # Upstream MuJoCo. The engine is built from it, and the ProtoSpec generator + # reads src/xml/mjcf.schema and doc/generate/mjcf_schema.py from it. url = https://github.com/google-deepmind/mujoco [submodule "third_party/CoACD/src"] path = third_party/CoACD/src diff --git a/Content/Materials/M_MuJoCo_Master.uasset b/Content/Materials/M_MuJoCo_Master.uasset index f4ee2af3..714a784c 100644 Binary files a/Content/Materials/M_MuJoCo_Master.uasset and b/Content/Materials/M_MuJoCo_Master.uasset differ diff --git a/Content/TestData/boundary/plugins.xml b/Content/TestData/boundary/plugins.xml new file mode 100644 index 00000000..30ec0a17 --- /dev/null +++ b/Content/TestData/boundary/plugins.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/CAPTURE.json b/Content/TestData/goldens/CAPTURE.json new file mode 100644 index 00000000..546d0130 --- /dev/null +++ b/Content/TestData/goldens/CAPTURE.json @@ -0,0 +1,6 @@ +{ + "mujoco_version": "3.11.1", + "mujoco_submodule": "b6683ac13dd353c8d66788a3beea6e84b2eb2dc2", + "captured_commit": "8028138e0e5f68fafd2b9b53ff958ad29cb27195", + "captured_utc": "2026-08-09" +} diff --git a/Content/TestData/goldens/actuators_all.parity.mjb b/Content/TestData/goldens/actuators_all.parity.mjb new file mode 100644 index 00000000..fee7f6bb Binary files /dev/null and b/Content/TestData/goldens/actuators_all.parity.mjb differ diff --git a/Content/TestData/goldens/actuators_all.roundtrip.xml b/Content/TestData/goldens/actuators_all.roundtrip.xml new file mode 100644 index 00000000..1f8ad48d --- /dev/null +++ b/Content/TestData/goldens/actuators_all.roundtrip.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/angle_degrees.parity.mjb b/Content/TestData/goldens/angle_degrees.parity.mjb new file mode 100644 index 00000000..da79c8fc Binary files /dev/null and b/Content/TestData/goldens/angle_degrees.parity.mjb differ diff --git a/Content/TestData/goldens/angle_degrees.roundtrip.xml b/Content/TestData/goldens/angle_degrees.roundtrip.xml new file mode 100644 index 00000000..8b9cdfd5 --- /dev/null +++ b/Content/TestData/goldens/angle_degrees.roundtrip.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/assets_meshes.parity.mjb b/Content/TestData/goldens/assets_meshes.parity.mjb new file mode 100644 index 00000000..0ff61f06 Binary files /dev/null and b/Content/TestData/goldens/assets_meshes.parity.mjb differ diff --git a/Content/TestData/goldens/assets_meshes.roundtrip.xml b/Content/TestData/goldens/assets_meshes.roundtrip.xml new file mode 100644 index 00000000..21295176 --- /dev/null +++ b/Content/TestData/goldens/assets_meshes.roundtrip.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/assets_meshes.scene2.parity.mjb b/Content/TestData/goldens/assets_meshes.scene2.parity.mjb new file mode 100644 index 00000000..70b8fc8d Binary files /dev/null and b/Content/TestData/goldens/assets_meshes.scene2.parity.mjb differ diff --git a/Content/TestData/goldens/defaults_nested.parity.mjb b/Content/TestData/goldens/defaults_nested.parity.mjb new file mode 100644 index 00000000..b0521689 Binary files /dev/null and b/Content/TestData/goldens/defaults_nested.parity.mjb differ diff --git a/Content/TestData/goldens/defaults_nested.roundtrip.xml b/Content/TestData/goldens/defaults_nested.roundtrip.xml new file mode 100644 index 00000000..2d4e3d2f --- /dev/null +++ b/Content/TestData/goldens/defaults_nested.roundtrip.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/keyframes_options.parity.mjb b/Content/TestData/goldens/keyframes_options.parity.mjb new file mode 100644 index 00000000..f80294fb Binary files /dev/null and b/Content/TestData/goldens/keyframes_options.parity.mjb differ diff --git a/Content/TestData/goldens/keyframes_options.roundtrip.xml b/Content/TestData/goldens/keyframes_options.roundtrip.xml new file mode 100644 index 00000000..d9ed125f --- /dev/null +++ b/Content/TestData/goldens/keyframes_options.roundtrip.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/macro_composite.parity.mjb b/Content/TestData/goldens/macro_composite.parity.mjb new file mode 100644 index 00000000..4b931d03 Binary files /dev/null and b/Content/TestData/goldens/macro_composite.parity.mjb differ diff --git a/Content/TestData/goldens/macro_composite.roundtrip.xml b/Content/TestData/goldens/macro_composite.roundtrip.xml new file mode 100644 index 00000000..eec657c6 --- /dev/null +++ b/Content/TestData/goldens/macro_composite.roundtrip.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/macro_flexcomp.parity.mjb b/Content/TestData/goldens/macro_flexcomp.parity.mjb new file mode 100644 index 00000000..fd1d1ef3 Binary files /dev/null and b/Content/TestData/goldens/macro_flexcomp.parity.mjb differ diff --git a/Content/TestData/goldens/macro_flexcomp.roundtrip.xml b/Content/TestData/goldens/macro_flexcomp.roundtrip.xml new file mode 100644 index 00000000..87cc4b4d --- /dev/null +++ b/Content/TestData/goldens/macro_flexcomp.roundtrip.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/macro_replicate.parity.mjb b/Content/TestData/goldens/macro_replicate.parity.mjb new file mode 100644 index 00000000..c9c22772 Binary files /dev/null and b/Content/TestData/goldens/macro_replicate.parity.mjb differ diff --git a/Content/TestData/goldens/macro_replicate.roundtrip.xml b/Content/TestData/goldens/macro_replicate.roundtrip.xml new file mode 100644 index 00000000..c9077af9 --- /dev/null +++ b/Content/TestData/goldens/macro_replicate.roundtrip.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/plugins.roundtrip.xml b/Content/TestData/goldens/plugins.roundtrip.xml new file mode 100644 index 00000000..66375188 --- /dev/null +++ b/Content/TestData/goldens/plugins.roundtrip.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/tendons_equality_sensors.parity.mjb b/Content/TestData/goldens/tendons_equality_sensors.parity.mjb new file mode 100644 index 00000000..d54d911b Binary files /dev/null and b/Content/TestData/goldens/tendons_equality_sensors.parity.mjb differ diff --git a/Content/TestData/goldens/tendons_equality_sensors.roundtrip.xml b/Content/TestData/goldens/tendons_equality_sensors.roundtrip.xml new file mode 100644 index 00000000..05db83d3 --- /dev/null +++ b/Content/TestData/goldens/tendons_equality_sensors.roundtrip.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/goldens/vx300s.parity.mjb b/Content/TestData/goldens/vx300s.parity.mjb new file mode 100644 index 00000000..d395fa1c Binary files /dev/null and b/Content/TestData/goldens/vx300s.parity.mjb differ diff --git a/Content/TestData/goldens/vx300s.roundtrip.xml b/Content/TestData/goldens/vx300s.roundtrip.xml new file mode 100644 index 00000000..2a5a3721 --- /dev/null +++ b/Content/TestData/goldens/vx300s.roundtrip.xml @@ -0,0 +1,167 @@ + + + diff --git a/Content/TestData/humanoid.xml b/Content/TestData/humanoid.xml new file mode 100644 index 00000000..a2c4a16f --- /dev/null +++ b/Content/TestData/humanoid.xml @@ -0,0 +1,267 @@ + + + + diff --git a/Content/TestData/parity/actuators_all.xml b/Content/TestData/parity/actuators_all.xml new file mode 100644 index 00000000..310cabfa --- /dev/null +++ b/Content/TestData/parity/actuators_all.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/angle_degrees.xml b/Content/TestData/parity/angle_degrees.xml new file mode 100644 index 00000000..dcdb3808 --- /dev/null +++ b/Content/TestData/parity/angle_degrees.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/assets_meshes.xml b/Content/TestData/parity/assets_meshes.xml new file mode 100644 index 00000000..d58e107e --- /dev/null +++ b/Content/TestData/parity/assets_meshes.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/checker.png b/Content/TestData/parity/checker.png new file mode 100644 index 00000000..60912e1b Binary files /dev/null and b/Content/TestData/parity/checker.png differ diff --git a/Content/TestData/parity/defaults_nested.xml b/Content/TestData/parity/defaults_nested.xml new file mode 100644 index 00000000..bb0a279d --- /dev/null +++ b/Content/TestData/parity/defaults_nested.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/keyframes_options.xml b/Content/TestData/parity/keyframes_options.xml new file mode 100644 index 00000000..e0e17482 --- /dev/null +++ b/Content/TestData/parity/keyframes_options.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/macro_composite.xml b/Content/TestData/parity/macro_composite.xml new file mode 100644 index 00000000..1586830a --- /dev/null +++ b/Content/TestData/parity/macro_composite.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/macro_flexcomp.xml b/Content/TestData/parity/macro_flexcomp.xml new file mode 100644 index 00000000..41bd13c8 --- /dev/null +++ b/Content/TestData/parity/macro_flexcomp.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/macro_replicate.xml b/Content/TestData/parity/macro_replicate.xml new file mode 100644 index 00000000..89a7f536 --- /dev/null +++ b/Content/TestData/parity/macro_replicate.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/meshA/base.obj b/Content/TestData/parity/meshA/base.obj new file mode 100644 index 00000000..9cfdae89 --- /dev/null +++ b/Content/TestData/parity/meshA/base.obj @@ -0,0 +1,8 @@ +v 0 0 0 +v 0.2 0 0 +v 0.1 0.2 0 +v 0.1 0.07 0.2 +f 1 3 2 +f 1 2 4 +f 2 3 4 +f 3 1 4 diff --git a/Content/TestData/parity/meshB/base.obj b/Content/TestData/parity/meshB/base.obj new file mode 100644 index 00000000..bc09bfca --- /dev/null +++ b/Content/TestData/parity/meshB/base.obj @@ -0,0 +1,20 @@ +v 0 0 0 +v 0.3 0 0 +v 0.3 0.3 0 +v 0 0.3 0 +v 0 0 0.3 +v 0.3 0 0.3 +v 0.3 0.3 0.3 +v 0 0.3 0.3 +f 1 4 3 +f 1 3 2 +f 5 6 7 +f 5 7 8 +f 1 2 6 +f 1 6 5 +f 2 3 7 +f 2 7 6 +f 3 4 8 +f 3 8 7 +f 4 1 5 +f 4 5 8 diff --git a/Content/TestData/parity/tendons_equality_sensors.xml b/Content/TestData/parity/tendons_equality_sensors.xml new file mode 100644 index 00000000..6066fe86 --- /dev/null +++ b/Content/TestData/parity/tendons_equality_sensors.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/parity/trossen_vx300s/LICENSE b/Content/TestData/parity/trossen_vx300s/LICENSE new file mode 100644 index 00000000..8df4a42c --- /dev/null +++ b/Content/TestData/parity/trossen_vx300s/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2023, Trossen Robotics +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Content/TestData/parity/trossen_vx300s/assets/interbotix_black.png b/Content/TestData/parity/trossen_vx300s/assets/interbotix_black.png new file mode 100644 index 00000000..e7443eb5 Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/interbotix_black.png differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_10_gripper_finger.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_10_gripper_finger.stl new file mode 100644 index 00000000..d6df86be Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_10_gripper_finger.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_1_base.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_1_base.stl new file mode 100644 index 00000000..5a7efda2 Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_1_base.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_2_shoulder.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_2_shoulder.stl new file mode 100644 index 00000000..dc22aa7e Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_2_shoulder.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_3_upper_arm.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_3_upper_arm.stl new file mode 100644 index 00000000..111c586e Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_3_upper_arm.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_4_upper_forearm.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_4_upper_forearm.stl new file mode 100644 index 00000000..8170d21c Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_4_upper_forearm.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_5_lower_forearm.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_5_lower_forearm.stl new file mode 100644 index 00000000..39581f83 Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_5_lower_forearm.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_6_wrist.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_6_wrist.stl new file mode 100644 index 00000000..ab8423e9 Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_6_wrist.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_7_gripper.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_7_gripper.stl new file mode 100644 index 00000000..043db9ca Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_7_gripper.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_8_gripper_prop.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_8_gripper_prop.stl new file mode 100644 index 00000000..36099b42 Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_8_gripper_prop.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/assets/vx300s_9_gripper_bar.stl b/Content/TestData/parity/trossen_vx300s/assets/vx300s_9_gripper_bar.stl new file mode 100644 index 00000000..eba3caa2 Binary files /dev/null and b/Content/TestData/parity/trossen_vx300s/assets/vx300s_9_gripper_bar.stl differ diff --git a/Content/TestData/parity/trossen_vx300s/vx300s.xml b/Content/TestData/parity/trossen_vx300s/vx300s.xml new file mode 100644 index 00000000..1f9a9064 --- /dev/null +++ b/Content/TestData/parity/trossen_vx300s/vx300s.xml @@ -0,0 +1,202 @@ + + + + diff --git a/Content/TestData/ros_maps_test.xml b/Content/TestData/ros_maps_test.xml new file mode 100644 index 00000000..9d81ea2f --- /dev/null +++ b/Content/TestData/ros_maps_test.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Content/TestData/ros_maps_test_ue.xml b/Content/TestData/ros_maps_test_ue.xml new file mode 100644 index 00000000..cbe95a4a --- /dev/null +++ b/Content/TestData/ros_maps_test_ue.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 9b425d53..c2c99a94 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,27 @@ ![URLab](docs/images/hero.png) +> ## ⚠️ Beta: this release breaks existing MjArticulation assets +> +> `main` is now the **beta**. The component model changed: components no longer +> mirror the MuJoCo model, they **are** the model, and every element is +> generated from MuJoCo's own `mjcf.schema`. +> +> **Updating the plugin will break MjArticulations you already have.** Their +> components are the previous generation's classes, and nothing migrates them. +> Expect to re-import your MJCF rather than to open an existing Blueprint. Back +> up your project first. +> +> **If you are not ready for that, stay on the `alpha` branch**, which is the +> final pre-beta state and is not going to move under you: +> +> ``` +> git checkout alpha # or the v0.5.0-alpha tag, which is frozen +> ``` +> +> Re-importing gets you a model that compiles to exactly what stock MuJoCo's +> own `mj_loadXML` produces, which the previous generation could not promise. + UnrealRoboticsLab (URLab) is an Unreal Engine 5 plugin that embeds the [MuJoCo](https://github.com/google-deepmind/mujoco) physics engine directly into the editor and runtime. You simulate robots with MuJoCo's accurate contact @@ -87,7 +108,8 @@ ROS 2 bridging. Contributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md), and the [Contributing docs](https://urlab-sim.github.io/UnrealRoboticsLab/contributing/building/) -for building from source, the codegen, and bumping MuJoCo. Since Unreal projects +for building from source, regenerating the MuJoCo profile, and bumping +MuJoCo. Since Unreal projects cannot use standard CI, each PR should include proof of a local build and passing tests. diff --git a/Scripts/build_and_test.ps1 b/Scripts/build_and_test.ps1 index d2d139fc..0ffd7c77 100644 --- a/Scripts/build_and_test.ps1 +++ b/Scripts/build_and_test.ps1 @@ -39,7 +39,7 @@ -Target CustomEditorTargetName .NOTES - Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args. + Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args, 4 generated-profile drift. #> [CmdletBinding()] @@ -48,7 +48,10 @@ param( [Parameter(Mandatory = $true)] [string] $Project, [string] $Target = '', [string] $Filter = 'URLab', - [string] $Log = (Join-Path $env:TEMP 'urlab_test.log') + # Unique per run. Two runs sharing one path collide on the file lock: the + # loser dies outright, and the winner can be left with a truncated or empty + # log, which greps to zero failures and reads exactly like a clean pass. + [string] $Log = (Join-Path $env:TEMP ('urlab_test_{0}_{1}.log' -f (Get-Date -Format 'yyyyMMdd-HHmmss'), $PID)) ) $ErrorActionPreference = 'Stop' @@ -66,19 +69,27 @@ $cmd = Join-Path $Engine 'Engine\Binaries\Win64\UnrealEditor-Cmd.exe' if (-not (Test-Path $bat)) { Write-Error "Build.bat not found: $bat"; exit 3 } if (-not (Test-Path $cmd)) { Write-Error "UnrealEditor-Cmd not found: $cmd"; exit 3 } -# --- Codegen drift gate ---------------------------------------------------- -# Refuse to build if codegen output drifted from the committed Source/, or -# if any drift diagnostic (--strict) fires, or if the clang-AST introspect -# snapshot is missing (--require-introspect). Skipped silently when Python -# isn't on PATH — the gate is best-effort locally; CI enforces it strictly. +# --- Generated-profile drift gate ------------------------------------------ +# Refuse to build if the emitted MuJoCo document profile drifted from what the +# schema now says. The gate needs the generator and `uv`; when either is absent +# it cannot run, and it says so rather than passing quietly. A skipped gate and +# a passing gate are the same exit code, so silence here reads as a green gate +# in every log and every CI summary that only checks the code. $pluginRoot = Split-Path -Parent $PSScriptRoot -$generator = Join-Path $pluginRoot 'Scripts/codegen/generate_ue_components.py' -$py = (Get-Command python -ErrorAction SilentlyContinue) -if ($py -and (Test-Path $generator)) { - Write-Host '>>> Codegen drift gate: python generate_ue_components.py --check --strict --require-introspect' - & $py.Source $generator --check --strict --require-introspect +$regen = Join-Path $pluginRoot 'Scripts/regen_ue_profile.ps1' +$protospec = Join-Path $pluginRoot 'protospec/protospec_gen' +$gateMissing = @() +if (-not (Test-Path $regen)) { $gateMissing += "no $regen" } +if (-not (Test-Path $protospec)) { $gateMissing += "no ProtoSpec generator at $protospec" } +if (-not (Get-Command uv -ErrorAction SilentlyContinue)) { $gateMissing += 'uv is not on PATH' } + +if ($gateMissing.Count -gt 0) { + Write-Warning ("Profile drift gate SKIPPED ({0}). The generated MuJoCo profile is NOT checked in this run." -f ($gateMissing -join '; ')) +} else { + Write-Host '>>> Profile drift gate: regen_ue_profile.ps1 -Check' + & $regen -Check if ($LASTEXITCODE -ne 0) { - Write-Error "Codegen drift detected (exit $LASTEXITCODE). Re-run 'python Scripts/codegen/generate_ue_components.py' to regenerate, then re-run this script." + Write-Error "Generated profile drift detected (exit $LASTEXITCODE). Re-run 'Scripts/regen_ue_profile.ps1', then re-run this script." exit 4 } } @@ -95,7 +106,21 @@ Write-Host ">>> Building $Target (Win64 Development)..." # Result: Failed (ActionGraphInvalid). Just the positional target is enough. $buildArgs = @($Target, 'Win64', 'Development', "-Project=$Project", '-Progress') $buildOut = & $bat @buildArgs 2>&1 -$buildOut | Select-Object -Last 10 | ForEach-Object { Write-Host $_ } + +# The whole build output goes to a file, and what reaches the console is the +# diagnostics rather than the last ten lines. A failing build is explained by +# its FIRST error; the tail is whatever the toolchain happened to say +# afterwards, which on a large generated translation unit is usually a summary +# that names no file at all. +$buildLog = "$Log.build.txt" +$buildOut | Out-File -FilePath $buildLog -Encoding utf8 +$diagnostics = $buildOut | Select-String -Pattern 'error [A-Z]+[0-9]+|error:|fatal error|Result: ' +if ($diagnostics) { + $diagnostics | Select-Object -First 40 | ForEach-Object { Write-Host $_ } +} else { + $buildOut | Select-Object -Last 10 | ForEach-Object { Write-Host $_ } +} +Write-Host ">>> Full build output: $buildLog" $buildStatus = if ($buildOut -match 'Result: Succeeded') { 'Succeeded' } else { 'Failed' } # --- Test ------------------------------------------------------------------ @@ -132,10 +157,15 @@ if ($buildStatus -eq 'Succeeded') { # runs. The SHA-256 of the log is sufficient for reviewers to verify content # integrity when they re-run the suite themselves (#37). $ts = (Get-Date).ToUniversalTime().ToString('yyyy-MM-dd HH:mm:ss') + ' UTC' -try { - $gitSha = (git rev-parse --short=8 HEAD 2>$null).Trim() - $gitBranch = (git rev-parse --abbrev-ref HEAD 2>$null).Trim() -} catch { $gitSha = 'unknown'; $gitBranch = 'unknown' } +# Asked of the PLUGIN repository explicitly, not of whatever directory the +# script happened to be invoked from. The SHA in this block is what ties a gate +# result to a commit in the record, so it must not depend on the caller's +# working directory: run from the host project root, a bare `git rev-parse` +# answers for the wrong repository or for none. +$gitSha = (git -C $PSScriptRoot rev-parse --short=8 HEAD 2>$null) +$gitBranch = (git -C $PSScriptRoot rev-parse --abbrev-ref HEAD 2>$null) +if (-not $gitSha) { $gitSha = 'unknown' } else { $gitSha = $gitSha.Trim() } +if (-not $gitBranch) { $gitBranch = 'unknown' } else { $gitBranch = $gitBranch.Trim() } # Strip the engine path down to its last segment (e.g. 'UE_5.7'). The full # path can contain the user's install root on a non-standard layout. diff --git a/Scripts/build_and_test.sh b/Scripts/build_and_test.sh index 8cd8f60d..d8b968d2 100644 --- a/Scripts/build_and_test.sh +++ b/Scripts/build_and_test.sh @@ -36,13 +36,16 @@ # --target defaults to Editor derived from the .uproject filename. # Only override for projects that don't follow UE's naming convention. # -# Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args. +# Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args, 4 generated-profile drift. set -eu TARGET="" FILTER="URLab" -LOG="/tmp/urlab_test.log" +# Unique per run. Two runs sharing one path collide on the file lock: the loser +# dies outright, and the winner can be left with a truncated or empty log, which +# greps to zero failures and reads exactly like a clean pass. +LOG="/tmp/urlab_test_$(date +%Y%m%d-%H%M%S)_$$.log" ENGINE="" PROJECT="" @@ -60,7 +63,7 @@ Usage: --target defaults to Editor derived from the .uproject filename. -Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args. +Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args, 4 generated-profile drift. HELP exit 3 } @@ -94,6 +97,32 @@ CMD="$ENGINE/Engine/Binaries/Win64/UnrealEditor-Cmd.exe" [[ -f "$BAT" ]] || { echo "Build.bat not found: $BAT" >&2; exit 3; } [[ -x "$CMD" ]] || { echo "UnrealEditor-Cmd not found: $CMD" >&2; exit 3; } +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +PLUGIN_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) + +# --- Generated-profile drift gate ------------------------------------------ +# Refuse to build if the emitted MuJoCo document profile drifted from what the +# schema now says. The gate needs the generator and `uv`; when either is absent +# it cannot run, and it says so rather than passing quietly. A skipped gate and +# a passing gate are the same exit code, so silence here reads as a green gate +# in every log and every CI summary that only checks the code. +REGEN="$PLUGIN_ROOT/Scripts/regen_ue_profile.sh" +PROTOSPEC_GEN="$PLUGIN_ROOT/protospec/protospec_gen" +GATE_MISSING="" +[[ -f "$REGEN" ]] || GATE_MISSING="no $REGEN" +[[ -d "$PROTOSPEC_GEN" ]] || GATE_MISSING="${GATE_MISSING:+$GATE_MISSING; }no ProtoSpec generator at $PROTOSPEC_GEN" +command -v uv >/dev/null 2>&1 || GATE_MISSING="${GATE_MISSING:+$GATE_MISSING; }uv is not on PATH" + +if [[ -n "$GATE_MISSING" ]]; then + echo "WARNING: profile drift gate SKIPPED ($GATE_MISSING). The generated MuJoCo profile is NOT checked in this run." >&2 +else + echo ">>> Profile drift gate: regen_ue_profile.sh --check" + if ! bash "$REGEN" --check; then + echo "ERROR: generated profile drift detected. Re-run 'Scripts/regen_ue_profile.sh', then re-run this script." >&2 + exit 4 + fi +fi + # Truncate the test log up-front so a build failure (or any early exit # before UnrealEditor-Cmd writes to it) doesn't leave the SHA-256 in the # summary pointing at a previous run's file. @@ -105,7 +134,20 @@ echo ">>> Building $TARGET (Win64 Development)..." # target name it makes UBT's two action-graph passes ambiguous and bails with # Result: Failed (ActionGraphInvalid). Just the positional target is enough. BUILD_OUT=$("$BAT" "$TARGET" Win64 Development "-Project=$PROJECT" -Progress 2>&1 || true) -echo "$BUILD_OUT" | tail -10 + +# The whole build output goes to a file, and what reaches the console is the +# diagnostics rather than the last ten lines. A failing build is explained by +# its FIRST error; the tail is whatever the toolchain happened to say +# afterwards, which on a large generated translation unit is usually a summary +# that names no file at all. +BUILD_LOG="$LOG.build.txt" +printf '%s\n' "$BUILD_OUT" > "$BUILD_LOG" +if printf '%s\n' "$BUILD_OUT" | grep -qE 'error [A-Z]+[0-9]+|error:|fatal error'; then + printf '%s\n' "$BUILD_OUT" | grep -E 'error [A-Z]+[0-9]+|error:|fatal error|Result: ' | head -40 +else + printf '%s\n' "$BUILD_OUT" | tail -10 +fi +echo ">>> Full build output: $BUILD_LOG" if echo "$BUILD_OUT" | grep -q "Result: Succeeded"; then BUILD_STATUS="Succeeded" else @@ -142,8 +184,13 @@ fi # The SHA-256 of the log is sufficient for reviewers to verify content # integrity when they re-run the suite themselves (#37). TS=$(date -u '+%Y-%m-%d %H:%M:%S UTC') -GIT_SHA=$(git rev-parse --short=8 HEAD 2>/dev/null || echo "unknown") -GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") +# Asked of the PLUGIN repository explicitly, not of whatever directory the +# script happened to be invoked from. The SHA in this block is what ties a gate +# result to a commit in the record, so it must not depend on the caller's +# working directory: run from the host project root, a bare `git rev-parse` +# answers for the wrong repository or for none. +GIT_SHA=$(git -C "$SCRIPT_DIR" rev-parse --short=8 HEAD 2>/dev/null || echo "unknown") +GIT_BRANCH=$(git -C "$SCRIPT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown") # Strip the engine path down to its last segment (e.g. 'UE_5.7'). The full # path can contain the user's install root on a non-standard layout. @@ -153,8 +200,6 @@ ENGINE_LABEL=$(basename "$ENGINE") # Third-party dep SHAs from third_party/install//INSTALLED_SHA.txt # (written by the CMake build scripts). Lets a reviewer verify they're # comparing against the same binary toolchain. Silent skip if missing. -SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) -PLUGIN_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) DEPS_LINE="" for pair in "mj:MuJoCo" "coacd:CoACD" "zmq:libzmq"; do key="${pair%%:*}" diff --git a/Scripts/build_and_test_linux.sh b/Scripts/build_and_test_linux.sh index 496ff029..fddb1f9b 100755 --- a/Scripts/build_and_test_linux.sh +++ b/Scripts/build_and_test_linux.sh @@ -40,7 +40,7 @@ # --target defaults to Editor derived from the .uproject filename. # Only override for projects that don't follow UE's naming convention. # -# Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args. +# Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args, 4 generated-profile drift. set -eu @@ -64,7 +64,7 @@ Usage: --target defaults to Editor derived from the .uproject filename. -Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args. +Exit codes: 0 ok, 1 build failed, 2 tests failed, 3 bad args, 4 generated-profile drift. HELP exit 3 } @@ -106,6 +106,24 @@ if pgrep -fa "/UnrealEditor( |$)" >/dev/null 2>&1; then exit 3 fi +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +PLUGIN_ROOT=$(cd "$SCRIPT_DIR/.." && pwd) + +# --- Generated-profile drift gate ------------------------------------------ +# Refuse to build if the emitted MuJoCo document profile drifted from what the +# schema now says. The generator ships inside the MuJoCo submodule, so this is +# skipped when the submodule is not checked out: the gate is best-effort +# locally and enforced in CI. +REGEN="$PLUGIN_ROOT/Scripts/regen_ue_profile.sh" +PROTOSPEC_GEN="$PLUGIN_ROOT/protospec/protospec_gen" +if [[ -f "$REGEN" && -d "$PROTOSPEC_GEN" ]] && command -v uv >/dev/null 2>&1; then + echo ">>> Profile drift gate: regen_ue_profile.sh --check" + if ! bash "$REGEN" --check; then + echo "ERROR: generated profile drift detected. Re-run 'Scripts/regen_ue_profile.sh', then re-run this script." >&2 + exit 4 + fi +fi + : > "$LOG" # --- Build ----------------------------------------------------------------- diff --git a/Scripts/clean_meshes.py b/Scripts/clean_meshes.py index 33d7c0da..9ac1e693 100644 --- a/Scripts/clean_meshes.py +++ b/Scripts/clean_meshes.py @@ -31,22 +31,196 @@ pip install trimesh numpy scipy Pillow networkx Usage: - python clean_meshes_trimesh.py + python clean_meshes.py [--out-dir DIR] [--allow-external-includes] + +Without --out-dir the prepared XML is written beside the input. With it, the +prepared XML goes to DIR and every asset path inside is re-expressed so it +still resolves from there, which is how Unreal keeps its prepared copy out of +the model author's own folders. + + fragments are flattened into one document on the way through, and an +include whose target is outside the model's own folder is REFUSED unless +--allow-external-includes is passed. That is the same boundary the Unreal +reader enforces, asked here because this step runs first: a prepared import +whose includes were followed by this script would otherwise have its security +answer decided by a script nobody asked. + +Exit status is 0 only when every referenced mesh was prepared. A conversion +that failed, a source file that was missing, a refused include, or an +unreadable document exits nonzero with the reason on stderr, because a caller +that continued anyway would import the unprepared meshes at the wrong scale +without saying so. Example: - python clean_meshes_trimesh.py "path/to/mujoco_menagerie/franka_emika_panda/panda.xml" + python clean_meshes.py "path/to/mujoco_menagerie/franka_emika_panda/panda.xml" """ import trimesh import numpy as np from pathlib import Path from xml.etree import ElementTree as ET +import argparse import os import sys import copy -def clean_mesh(mesh): +# MuJoCo's own crease threshold: a face whose normal is more than acos(0.8) +# ~= 36.9 degrees off the vertex normal is not part of that vertex's smooth +# group (mjCMesh::MakeNormal, src/user/user_mesh.cc). Reused here so the +# decision about what counts as an edge matches the simulator, even though the +# representation below is sharper than MuJoCo's own. +CREASE_DOT = 0.8 + + +def split_normals_by_crease(mesh, dot_threshold: float = CREASE_DOT): + """Split `mesh`'s vertices into smooth groups, so hard edges shade hard. + + The split is geometric rather than a normal override: a vertex whose faces + disagree by more than the threshold is duplicated, once per group of faces + that agree. Averaging then happens within a group and never across one, so + a box corner stays sharp while a finely tessellated cylinder stays round. + + Done this way on purpose. Assigning `mesh.vertex_normals` directly does hold + in memory, but trimesh recomputes normals during GLB export and the override + is silently lost -- which looks like a crease split right up until you read + the accessor back. + + MuJoCo cannot express this at all: it keeps one normal per vertex and merely + subtracts the outlying contributions, so its corners are a compromise. The + threshold is still MuJoCo's, so the decision about what counts as an edge + matches the simulator even though the representation is sharper. + """ + faces = mesh.faces + verts = mesh.vertices + raw = np.cross(verts[faces[:, 1]] - verts[faces[:, 0]], + verts[faces[:, 2]] - verts[faces[:, 0]]) + unit = raw / np.maximum(np.linalg.norm(raw, axis=1)[:, None], 1e-20) + + # Faces meeting at each vertex. + incident = [[] for _ in range(len(verts))] + for fi, tri in enumerate(faces): + for vi in tri: + incident[vi].append(fi) + + new_verts = [] + # (vertex, face) -> index into new_verts + remap = {} + for vi, face_ids in enumerate(incident): + if not face_ids: + continue + # Greedy clustering: a face joins the first group whose running mean it + # agrees with, otherwise it starts one. Enough for the shapes a mesh + # asset actually has, and it never merges across a real edge. + groups = [] # list of [summed_normal, [face ids]] + for fi in face_ids: + n = unit[fi] + for g in groups: + mean = g[0] / max(np.linalg.norm(g[0]), 1e-20) + if float(np.dot(n, mean)) >= dot_threshold: + g[0] = g[0] + n + g[1].append(fi) + break + else: + groups.append([n.copy(), [fi]]) + for g in groups: + index = len(new_verts) + new_verts.append(verts[vi]) + for fi in g[1]: + remap[(vi, fi)] = index + + new_faces = np.empty_like(faces) + for fi, tri in enumerate(faces): + for corner, vi in enumerate(tri): + new_faces[fi, corner] = remap[(vi, fi)] + + # No custom normals: trimesh's own area-weighted average over the split + # geometry is the crease result, and it survives export because nothing + # had to be overridden. + return trimesh.Trimesh(vertices=np.asarray(new_verts), faces=new_faces, + process=False) + + +def collision_mesh_names(root): + """Mesh names any COLLIDING geom uses, resolved through the class chain. + + Splitting a vertex so a hard edge shades hard duplicates it. That is what a + renderer wants and it is invisible to the physics -- no vertex moves, so the + shape and its convex hull are unchanged -- but it does change `nmeshvert`, + and a collision mesh has no shading to improve. So the split is confined to + meshes only visual geoms use, and everything else is left exactly as MuJoCo + would have loaded it. + + Whether a geom collides is `contype`/`conaffinity`, both 1 unless something + says otherwise, and in practice a visual geom gets its zeroes from a + `` class rather than from the geom. So the chain has to be walked: + the geom's own attribute wins, then its class, then that class's parent, up + to `main`. + + Conservative on purpose. A mesh whose class cannot be resolved counts as + collision, because the cost of being wrong that way is a mesh that shades a + little softer, and the cost the other way is a model that no longer matches + what MuJoCo would have compiled. + """ + # class name -> (parent class name, geom attributes that class supplies) + classes = {} + + def read_defaults(element, parent_name): + for default in element.findall("default"): + name = default.get("class") + if name is None: + # An unnamed at the top is MuJoCo's `main`. + name = "main" if parent_name is None else parent_name + geom = default.find("geom") + classes[name] = (parent_name, dict(geom.attrib) if geom is not None else {}) + read_defaults(default, name) + + read_defaults(root, None) + + def resolve(attr, geom_class, own): + """`attr` for a geom in `geom_class`, its own attributes taking priority.""" + if attr in own: + return own[attr] + seen = set() + name = geom_class + while name is not None and name not in seen: + seen.add(name) + entry = classes.get(name) + if entry is None: + return None + parent, attrs = entry + if attr in attrs: + return attrs[attr] + name = parent + return None + + collision = set() + + def walk(element, inherited_class): + # `childclass` applies to every descendant that does not name its own. + current = element.get("childclass", inherited_class) + for child in element: + if child.tag == "geom": + mesh = child.get("mesh") + if mesh: + geom_class = child.get("class", current) + contype = resolve("contype", geom_class, child.attrib) + conaffinity = resolve("conaffinity", geom_class, child.attrib) + # Unset means MuJoCo's own 1, which collides. + collides = (contype is None or contype.strip() != "0") or ( + conaffinity is None or conaffinity.strip() != "0" + ) + if collides: + collision.add(mesh) + walk(child, current) + + for worldbody in root.findall("worldbody"): + walk(worldbody, None) + + return collision + + +def clean_mesh(mesh, source_path=None, smooth_normal=False): """Clean up a mesh using trimesh.""" print(f" Original: {len(mesh.vertices)} vertices, {len(mesh.faces)} faces") @@ -71,6 +245,18 @@ def clean_mesh(mesh): rotation_matrix = trimesh.transformations.rotation_matrix(-np.radians(90), [1, 0, 0]) mesh.apply_transform(rotation_matrix) + # Every mesh, not just the ones whose file states no normals. An OBJ that + # ships split vertices loses them here regardless: the welding above and the + # GLB export both collapse them, so "the file already answered this" is not + # a state that survives conversion. Measured on Spot, whose OBJs are flat + # and whose GLBs came out averaged across every corner. + # + # After the transform, never before: applying one invalidates trimesh's + # cached normals. + if not smooth_normal: + mesh = split_normals_by_crease(mesh) + print(f" Creased: {len(mesh.vertices)} vertices after splitting hard edges") + print(f" Cleaned: {len(mesh.vertices)} vertices, {len(mesh.faces)} faces") return mesh @@ -88,7 +274,7 @@ def glb_up_to_date(output_glb: Path, source_path: Path) -> bool: return mtime > source_path.stat().st_mtime and mtime > _SCRIPT_MTIME -def convert_mesh(input_path: Path, output_path: Path) -> bool: +def convert_mesh(input_path: Path, output_path: Path, smooth_normal: bool = False) -> bool: """Convert a single mesh file to GLB.""" print(f"\n Converting: {input_path.name} -> {output_path.name}") @@ -99,7 +285,7 @@ def convert_mesh(input_path: Path, output_path: Path) -> bool: print(f" x Not a valid mesh: {input_path.name}") return False - cleaned_mesh = clean_mesh(mesh) + cleaned_mesh = clean_mesh(mesh, input_path, smooth_normal) # Strip embedded materials/textures to prevent Unreal's Interchange importer # from creating a Texture2D instead of a StaticMesh. @@ -145,6 +331,37 @@ def _parse_floats(s: str) -> list: _MODEL_ROOT_TAGS = ("mujoco", "mujocoinclude") +class ExternalIncludeError(Exception): + """An ```` reaching outside the model's own folder. + + The importer asks before following one of those, because an MJCF document + can be someone else's and ```` is a + file read the author of the scene never asked for. This preparation step + runs BEFORE the reader and flattens the includes itself, so it has to ask + the same question or the answer the user gave is decided by a script that + never heard it. + """ + + def __init__(self, include: str, resolved: Path, boundary: Path): + super().__init__( + f"refused resolving to {resolved}: " + f"outside the model's folder {boundary}. Re-run with " + "--allow-external-includes to allow it.") + self.include = include + self.resolved = resolved + self.boundary = boundary + + +def _inside(path: Path, boundary: Path) -> bool: + """True when ``path`` is ``boundary`` or below it, symlinks resolved.""" + try: + return path.resolve().is_relative_to(boundary.resolve()) + except (OSError, ValueError): + # A path on another drive, or one the OS will not resolve, is outside + # by definition; failing closed is the whole point of the check. + return False + + def _compiler_dirs(root) -> tuple: """Return (meshdir, texturedir, assetdir) declared by any directly under ``root``. MuJoCo allows several compiler elements; later ones win for a @@ -184,15 +401,21 @@ def _rewrite_asset_path(elem, src_dir: Path, root_dir: Path, def _append_expanded(out_parent, elem, src_dir: Path, root_dir: Path, - meshdir: str, texturedir: str, assetdir: str, visited: set): + meshdir: str, texturedir: str, assetdir: str, visited: set, + boundary: Path = None): """Copy ``elem`` into ``out_parent``, recursively expanding any descendants in place. Handles both and include roots - and rewrites asset file= paths to stay valid from root_dir.""" + and rewrites asset file= paths to stay valid from root_dir. + + ``boundary``, when given, is the directory tree an include may not leave; + one that does raises ExternalIncludeError naming it.""" if elem.tag == "include": inc_file = elem.get("file") if not inc_file: return inc_path = (src_dir / inc_file).resolve() + if boundary is not None and not _inside(inc_path, boundary): + raise ExternalIncludeError(inc_file, inc_path, boundary) if inc_path in visited: print(f" [include] cycle/duplicate skipped: {inc_path}") return @@ -205,7 +428,8 @@ def _append_expanded(out_parent, elem, src_dir: Path, root_dir: Path, imd, itxd, iad = _compiler_dirs(inc_root) # Splice the included root's children directly into the current parent. for child in list(inc_root): - _append_expanded(out_parent, child, inc_dir, root_dir, imd, itxd, iad, visited) + _append_expanded(out_parent, child, inc_dir, root_dir, imd, itxd, iad, + visited, boundary) return # Regular element: shallow-copy attributes, then recurse into children. @@ -222,10 +446,11 @@ def _append_expanded(out_parent, elem, src_dir: Path, root_dir: Path, for child in list(elem): _append_expanded(new_elem, child, src_dir, root_dir, - meshdir, texturedir, assetdir, visited) + meshdir, texturedir, assetdir, visited, boundary) -def flatten_includes(root, root_dir: Path): +def flatten_includes(root, src_dir: Path, root_dir: Path = None, + boundary: Path = None): """Resolve every into a single self-contained tree. gym-aloha (and many MJCF models) split a robot across fragments @@ -233,15 +458,39 @@ def flatten_includes(root, root_dir: Path): Unreal's importer must see one flat document so all //body content is visible and there is exactly one worldbody. Returns the new root element (a ). No-op-equivalent for files without includes. + + ``src_dir`` is where the document being flattened lives; ``root_dir`` is + where the flattened document will be written, and defaults to ``src_dir``. + ``boundary`` is the directory tree includes may not leave, or None to follow + them anywhere. """ + if root_dir is None: + root_dir = src_dir md, txd, ad = _compiler_dirs(root) visited = set() new_root = ET.Element("mujoco", dict(root.attrib)) for child in list(root): - _append_expanded(new_root, child, root_dir, root_dir, md, txd, ad, visited) + _append_expanded(new_root, child, src_dir, root_dir, md, txd, ad, visited, + boundary) return new_root +def _rebase_assets(root, src_dir: Path, root_dir: Path): + """Re-express every asset ``file=`` so it resolves from ``root_dir``. + + What ``flatten_includes`` does on the way through, for a document that has + no includes to flatten. The dir attributes are cleared once the + paths carry their own prefixes, so nothing double-prefixes them. + """ + md, txd, ad = _compiler_dirs(root) + for elem in root.iter(): + if elem.tag in _ASSET_FILE_TAGS: + _rewrite_asset_path(elem, src_dir, root_dir, md, txd, ad) + for comp in root.findall("compiler"): + for attr in ("meshdir", "texturedir", "assetdir"): + comp.attrib.pop(attr, None) + + def materialize_inline_meshes(root, mesh_base: Path) -> int: """Rewrite ```` (MuJoCo's inline-data form) to ```` by emitting an OBJ next to the rest of the @@ -315,17 +564,27 @@ def materialize_inline_meshes(root, mesh_base: Path) -> int: return materialized -def process_xml(xml_path: Path): - """Parse MJCF XML, convert meshes, resolve conflicts, write updated XML.""" +def process_xml(xml_path: Path, out_dir: Path = None, + allow_external_includes: bool = False) -> bool: + """Parse MJCF XML, convert meshes, resolve conflicts, write updated XML. + + Returns True only when every mesh the document references was prepared, and + only when every ```` stayed inside the model's own folder unless + ``allow_external_includes`` says otherwise. + """ if not xml_path.exists(): - print(f"Error: XML file not found: {xml_path}") - return + print(f"Error: XML file not found: {xml_path}", file=sys.stderr) + return False - xml_dir = xml_path.parent + xml_dir = xml_path.parent.resolve() + root_dir = out_dir.resolve() if out_dir is not None else xml_dir + root_dir.mkdir(parents=True, exist_ok=True) print(f"XML: {xml_path}") print(f"Dir: {xml_dir}") + if root_dir != xml_dir: + print(f"Out: {root_dir}") print("=" * 60) # Parse XML @@ -339,10 +598,20 @@ def process_xml(xml_path: Path): include_count = sum(1 for _ in root.iter("include")) if include_count: print(f"Flattening {include_count} fragment(s)...") - root = flatten_includes(root, xml_dir.resolve()) + # The model's own folder is the boundary, matching the reader's rule. + # Off only when the caller passed the same option the import dialog + # shows, so the gate is decided in one place rather than twice. + boundary = None if allow_external_includes else xml_dir + try: + root = flatten_includes(root, xml_dir, root_dir, boundary) + except ExternalIncludeError as refused: + print(f"Error: {refused}", file=sys.stderr) + return False tree = ET.ElementTree(root) remaining = sum(1 for _ in root.iter("include")) print(f" -> {remaining} include(s) remain after flatten") + elif root_dir != xml_dir: + _rebase_assets(root, xml_dir, root_dir) # Find meshdir from compiler meshdir = "" @@ -351,9 +620,13 @@ def process_xml(xml_path: Path): if md: meshdir = md - mesh_base = xml_dir / meshdir if meshdir else xml_dir + mesh_base = root_dir / meshdir if meshdir else root_dir print(f"Mesh directory: {mesh_base}") + # Every mesh this run could not prepare. Non-empty means the caller must + # not use the output: the unconverted source would import at its own scale. + failures = [] + # Phase 0: Materialize inline ```` entries. # These get rewritten to file= entries before Phase 1 runs, so the rest # of the pipeline treats them like any other on-disk mesh. @@ -364,6 +637,11 @@ def process_xml(xml_path: Path): # Collect all mesh elements mesh_elements = list(root.iter("mesh")) + # Which of them a colliding geom uses. Those keep MuJoCo's own vertices. + collision_meshes = collision_mesh_names(root) + if collision_meshes: + print(f"Collision meshes (normals left as authored): {len(collision_meshes)}") + # Also convert meshes referenced by for flexcomp in root.iter("flexcomp"): file_attr = flexcomp.get("file") @@ -373,11 +651,15 @@ def process_xml(xml_path: Path): output_glb = source_path.with_suffix(".glb") if not glb_up_to_date(output_glb, source_path): print(f"\n[flexcomp] Converting mesh: {source_path.name} -> {output_glb.name}") - if not convert_mesh(source_path, output_glb) and output_glb.exists(): - output_glb.unlink() - print(f"[flexcomp] Removed stale GLB: {output_glb.name}") + if not convert_mesh(source_path, output_glb): + failures.append(f"flexcomp mesh '{file_attr}' failed to convert") + if output_glb.exists(): + output_glb.unlink() + print(f"[flexcomp] Removed stale GLB: {output_glb.name}") else: print(f"\n[flexcomp] Mesh up to date: {output_glb.name}") + else: + failures.append(f"flexcomp mesh not found: {source_path}") print(f"Found {len(mesh_elements)} mesh assets in XML\n") @@ -453,7 +735,8 @@ def process_xml(xml_path: Path): # Copy the source file to the new name if not source_path.exists(): - print(f" x Source file missing: {source_path} — skipping") + print(f" x Source file missing: {source_path}") + failures.append(f"mesh source not found: {source_path}") continue if not renamed_source.exists() or renamed_source.stat().st_mtime < source_path.stat().st_mtime: shutil.copy2(str(source_path), str(renamed_source)) @@ -471,6 +754,7 @@ def process_xml(xml_path: Path): if not actual_source.exists(): print(f"\n x Source not found: {actual_source}") + failures.append(f"mesh source not found: {actual_source}") continue if glb_up_to_date(output_glb, actual_source): @@ -479,10 +763,22 @@ def process_xml(xml_path: Path): continue print(f"\n[{mesh_name}] {actual_source.name} -> {output_glb.name}") - if convert_mesh(actual_source, output_glb): + # MJCF's own opt-in to one averaged normal per vertex. It defaults to + # false, so a model that says nothing gets the crease split. + smooth_normal = mesh_el.get("smoothnormal", "false").strip().lower() in ("true", "1") + + # A mesh a colliding geom uses is not split, whatever the document says + # about its normals: the split is a shading improvement, and paying for + # it with a model that no longer matches MuJoCo's is the wrong trade on + # geometry nobody looks at. + if mesh_name in collision_meshes: + smooth_normal = True + + if convert_mesh(actual_source, output_glb, smooth_normal): success_count += 1 else: print(f" x FAILED to convert {actual_source.name}") + failures.append(f"mesh '{mesh_name}' failed to convert: {actual_source}") # Never leave a stale or partial GLB behind: the importer # prefers .glb over the source mesh, so a leftover here would # silently ship the very data the conversion just refused to @@ -491,32 +787,46 @@ def process_xml(xml_path: Path): output_glb.unlink() print(f" -> Removed stale GLB: {output_glb.name}") - # Phase 4: Write updated XML - output_xml = xml_path.parent / f"{xml_path.stem}_ue.xml" + # Phase 4: Write updated XML. Written even when something failed, so the + # partial result can be inspected; the exit status is what decides whether + # a caller may use it. + output_xml = root_dir / f"{xml_path.stem}_ue.xml" tree.write(str(output_xml), encoding="unicode", xml_declaration=True) print("\n" + "=" * 60) print(f"Processed {success_count}/{len(output_plan)} meshes successfully") print(f"Conflicts resolved: {conflicts_found}") print(f"Updated XML: {output_xml}") + + if failures: + print(f"\n{len(failures)} mesh(es) could not be prepared:", file=sys.stderr) + for reason in failures: + print(f" - {reason}", file=sys.stderr) + return False + print(f"\nDrag '{output_xml.name}' into Unreal Content Browser to import.") + return True def main(): - if len(sys.argv) < 2: - print("Error: No XML file specified") - print("Usage: python clean_meshes_trimesh.py ") - print('Example: python clean_meshes_trimesh.py "C:/mujoco_menagerie/franka_emika_panda/panda.xml"') - return + parser = argparse.ArgumentParser( + description="Prepare MuJoCo MJCF meshes for Unreal import.") + parser.add_argument("xml", type=Path, help="path to the MJCF .xml to prepare") + parser.add_argument("--out-dir", type=Path, default=None, dest="out_dir", + help="directory to write the prepared _ue.xml into " + "(default: beside the input)") + parser.add_argument("--allow-external-includes", action="store_true", + dest="allow_external_includes", + help="follow an whose target is outside the " + "model's own folder (refused by default)") + args = parser.parse_args() - xml_path = Path(sys.argv[1]) + if args.xml.suffix.lower() != ".xml": + parser.error(f"expected an .xml file, got '{args.xml.suffix}'") - if xml_path.suffix.lower() == ".xml": - process_xml(xml_path) - else: - print(f"Error: Expected an .xml file, got '{xml_path.suffix}'") - print("Usage: python clean_meshes_trimesh.py ") + return 0 if process_xml(args.xml, args.out_dir, + args.allow_external_includes) else 1 if __name__ == "__main__": - main() + sys.exit(main()) diff --git a/Scripts/codegen/_codegen_checks.py b/Scripts/codegen/_codegen_checks.py deleted file mode 100644 index e425cbcf..00000000 --- a/Scripts/codegen/_codegen_checks.py +++ /dev/null @@ -1,934 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""URLab codegen — drift checks. - -Every ``_check_*`` function that scans rules + snapshots for drift -lives here. The orchestrator imports the dispatcher functions -(``_emit_drift_diagnostics``, ``_check_*``) and wires them into -``_phase_diagnostics``. - -Dependencies are strictly downward: imports from ``_codegen_core`` -only. No emit logic, no .cpp injection — drift checks read the -rules + snapshots and route diagnostics through ``_diag_add``. -""" - -from __future__ import annotations - -import os -import re -from typing import Any, Dict, List, Optional, Sequence, Set, Tuple - -from _codegen_core import ( - _diag_add, - _UE_TYPE_INFO, _UE_TYPE_FAMILY, _ue_type_accepts_units_meta, _KNOWN_UE_UNITS, - _classify_c_type, _shapes_compatible, _VEC3_CONVERT_FMT, - schema_attrs, schema_subtype_attrs, - compute_canon_absorbed, - _resolve_mjs_field, _resolve_value_map, _value_map_pair, -) - - -# --------------------------------------------------------------------------- -# Hand-enum scrape (regex fallback when the libclang scrape didn't run) -# --------------------------------------------------------------------------- - -_HAND_ENUM_RE = re.compile( - r"enum\s+class\s+(EMj\w+)\s*:\s*uint8\s*\{([^}]*)\}", - re.DOTALL, -) -_HAND_ENUM_MEMBER_RE = re.compile(r"\A\s*(\w+)") - - -def _scan_hand_enums(public_root: str) -> Dict[str, List[str]]: - """Walk ``public_root`` for every ``.h`` file declaring - ``enum class EMj* : uint8 { ... }`` and return - ``{enum_name: [members]}``. Members are stripped of inline - initialisers, UMETA macros, and comments.""" - out: Dict[str, List[str]] = {} - for root, _dirs, files in os.walk(public_root): - for name in files: - if not name.endswith(".h"): - continue - with open(os.path.join(root, name), "r", encoding="utf-8") as f: - text = f.read() - for m in _HAND_ENUM_RE.finditer(text): - enum_name = m.group(1) - body = m.group(2) - body = re.sub(r"UMETA\([^)]*\)", "", body) - body = re.sub(r"//[^\n]*", "", body) - body = re.sub(r"/\*.*?\*/", "", body, flags=re.DOTALL) - members: List[str] = [] - for line in body.split(","): - mm = _HAND_ENUM_MEMBER_RE.match(line) - if mm: - members.append(mm.group(1)) - if members: - out[enum_name] = members - return out - - -def _hand_enums_from_snapshot(mjspec: Optional[Dict[str, Any]]) -> Dict[str, List[str]]: - """Pull URLab's hand-rolled ``EMj*`` enums out of the merged - introspect snapshot when present. Snapshot path produces real - ENUM_CONSTANT_DECL members from libclang. Regex fallback covers - the local-dev case where the snapshot wasn't regenerated.""" - if not mjspec: - return {} - introspect = mjspec.get("introspect", {}) or {} - hand_enums = introspect.get("hand_enums") or {} - out: Dict[str, List[str]] = {} - for name, entry in hand_enums.items(): - if not isinstance(entry, dict): - continue - members = entry.get("members", {}) - if isinstance(members, dict): - out[name] = list(members.keys()) - return out - - -# --------------------------------------------------------------------------- -# Drift checks -# --------------------------------------------------------------------------- - - -def _check_hand_enum_drift( - schema: Dict[str, Any], - rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], - public_root: str, -) -> None: - """Cross-check rule-declared UE enum members against the actual - hand enums AND against MuJoCo's mjt* enums.""" - hand_enums = _hand_enums_from_snapshot(mjspec) - if not hand_enums: - hand_enums = _scan_hand_enums(public_root) - if not hand_enums: - return - - for cat_name, cat_rules in rules.get("categories", {}).items(): - type_enum = cat_rules.get("type_enum_name") - if not type_enum or type_enum not in hand_enums: - continue - members = set(hand_enums[type_enum]) - for sub in cat_rules.get("subtypes", []): - ev = sub.get("enum_value") - if ev and ev not in members: - _diag_add( - f"[diagnostic] category '{cat_name}' subtype '{sub.get('key')}' " - f"references enum_value '{type_enum}::{ev}' that does not " - f"exist in {type_enum} members {sorted(members)}.", - source="hand_enum_drift", - ) - - element_rules = rules.get("element_rules", {}) - mjspec_enums = (mjspec or {}).get("enums", {}) - intentional_skips = rules.get("intentionally_unmapped_mj_enum_values", {}) - for elem, elem_rules in element_rules.items(): - for attr, enum_def in elem_rules.get("xml_enum_attrs", {}).items(): - ue_enum_type = enum_def.get("ue_enum_type") or enum_def.get("ue_enum") - value_map = enum_def.get("value_map", {}) - if not ue_enum_type or ue_enum_type not in hand_enums: - continue - members = set(hand_enums[ue_enum_type]) - for xml_val, mapping in value_map.items(): - pair = _value_map_pair(mapping) - if pair is None: - continue - ue_member, _ = pair - if ue_member not in members: - _diag_add( - f"[diagnostic] element '{elem}' xml_enum '{attr}' " - f"value_map entry '{xml_val}' maps to " - f"'{ue_enum_type}::{ue_member}' that does not exist " - f"in members {sorted(members)}.", - source="hand_enum_drift", - ) - from_mj_enum = enum_def.get("from_mj_enum") - if not from_mj_enum: - mjs_cast = enum_def.get("mjs_cast", "") - if mjs_cast.startswith("mjt"): - from_mj_enum = mjs_cast - if from_mj_enum and from_mj_enum in mjspec_enums: - mj_values = mjspec_enums[from_mj_enum] - covered_mj: set = set() - for mapping in value_map.values(): - pair = _value_map_pair(mapping) - if pair is not None: - covered_mj.add(pair[1]) - skips = set(intentional_skips.get(from_mj_enum, [])) - per_elem = intentional_skips.get("__per_element__", {}) - skips |= set(per_elem.get(f"{elem}.{attr}", [])) - for mj_const in mj_values: - if mj_const in covered_mj or mj_const in skips: - continue - _diag_add( - f"[diagnostic] {from_mj_enum}::{mj_const} (from " - f"mjspec snapshot) has no mapping in element " - f"'{elem}' xml_enum_attrs.{attr}.value_map; new " - f"MuJoCo enum values silently fall through to the " - f"default branch at runtime. Add a value_map entry " - f"or list under 'intentionally_unmapped_mj_enum_values" - f".{from_mj_enum}'.", - source="hand_enum_drift", - ) - - -def _check_type_shape_drift( - schema: Dict[str, Any], - rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], -) -> None: - """Cross-check ``type_mappings[attr]`` against the actual C-type - shape of the corresponding mjs struct field.""" - if not mjspec: - return - struct_table = (mjspec.get("introspect") or {}).get("structs", {}) - if not struct_table: - return - type_map = rules.get("type_mappings", {}) - unmodeled_mjs = rules.get("intentionally_unmodeled_mjs_fields", {}) - element_rules = rules.get("element_rules", {}) - for cat_name, cat_rules in rules.get("categories", {}).items(): - mjs_struct = cat_rules.get("mjs_struct") - if not mjs_struct or mjs_struct not in struct_table: - continue - fields = { - f["name"]: (f.get("c_type", ""), f.get("array_dim")) - for f in struct_table[mjs_struct].get("fields", []) - } - skip_fields = set(unmodeled_mjs.get(mjs_struct, [])) - skip_fields |= set(element_rules.get(cat_name, {}).get("exclude_attrs", [])) - for attr, ue_type in type_map.items(): - if attr not in fields or attr in skip_fields: - continue - ue_shape = _UE_TYPE_FAMILY.get(ue_type) - if not ue_shape: - continue - c_type, array_dim = fields[attr] - c_shape = _classify_c_type(c_type, array_dim) - if c_shape is None or c_shape[0] == "opaque": - continue - if not _shapes_compatible(ue_shape, c_shape): - _diag_add( - f"[diagnostic] type_mappings['{attr}'] = '{ue_type}' " - f"(category '{cat_name}') but {mjs_struct}.{attr} is " - f"'{c_type}' (shape={c_shape}); rule expects " - f"shape={ue_shape}. UE/C shape mismatch — emitted " - f"code may overflow, truncate, or fail to compile.", - source="type_shape_drift", - ) - - -def _check_new_attr_typing( - schema: Dict[str, Any], - rules: Dict[str, Any], -) -> None: - """Every schema attr that lands as a UPROPERTY but has no explicit - type_mappings entry silently inherits ``default_type`` ('float'). - Also surfaces type_mappings entries whose UE type does not resolve - to a known shape — the codegen would otherwise fall back to a - default emit and hide the typo (e.g. ``TArray``).""" - type_map = rules.get("type_mappings", {}) - default_type = rules.get("default_type", "float") - global_excl = set(rules.get("global_exclusions", [])) - intentional_float = set(rules.get("intentionally_default_typed_attrs", [])) - element_rules = rules.get("element_rules", {}) - canonicalizations = rules.get("canonicalizations", {}) - - def _ue_type_known(ue_type: str) -> bool: - if ue_type in _UE_TYPE_INFO: - return True - if ue_type.startswith("EMj"): - return True - if ue_type.startswith("TArray<") and ue_type.endswith(">"): - inner = ue_type[len("TArray<"):-1] - return inner in _UE_TYPE_INFO or inner.startswith("EMj") or inner.startswith("U") - return False - - bad_typed: set = set() - for attr, ue_type in type_map.items(): - if attr.startswith("_"): - continue - if not _ue_type_known(ue_type): - bad_typed.add(attr) - _diag_add( - f"[diagnostic] type_mappings['{attr}'] = '{ue_type}' is " - f"not a recognised UE type. The codegen will fall back " - f"to default_type ('{default_type}') for downstream " - f"emits, hiding the typo. Fix the type or add the entry " - f"to _UE_TYPE_INFO.", - source="new_attr_typing", - ) - - def _collect_attrs(cat_rules: Dict[str, Any]) -> set: - out: set = set(schema_attrs(schema, cat_rules.get("schema_common_block", ""))) - out |= set(cat_rules.get("schema_common_extra_attrs", [])) - sub_block = cat_rules.get("schema_subtypes_block") - if sub_block and sub_block in schema: - block = schema[sub_block] - if isinstance(block, dict): - for sub_attrs in block.values(): - if isinstance(sub_attrs, list): - out |= set(sub_attrs) - elif isinstance(sub_attrs, dict): - out |= set(sub_attrs.get("attrs", [])) - elif isinstance(block, list): - for entry in block: - if isinstance(entry, dict): - out |= set(entry.get("attrs", [])) - return out - - flagged: set = set() - for cat_name, cat_rules in rules.get("categories", {}).items(): - elem_rules = element_rules.get(cat_name, {}) - elem_excl = set(elem_rules.get("exclude_attrs", [])) - xml_enum_attrs = set(elem_rules.get("xml_enum_attrs", {}).keys()) - renames = set(elem_rules.get("property_renames", {}).keys()) - collations = set(elem_rules.get("target_collations", {}).keys()) - export_skip = set(elem_rules.get("export_skip_attrs", [])) - common_imports = set() - for entry in elem_rules.get("common_imports", []): - if isinstance(entry, str): - common_imports.add(entry) - elif isinstance(entry, dict): - common_imports.add(entry.get("attr")) - canon_absorbed: set = compute_canon_absorbed( - elem_rules.get("applies_canonicalizations", []), - canonicalizations, - ) - for attr in _collect_attrs(cat_rules): - if attr in global_excl or attr in elem_excl: - continue - if attr in xml_enum_attrs or attr in renames or attr in collations: - continue - if attr in canon_absorbed or attr in export_skip or attr in common_imports: - continue - if attr in type_map: - continue - if attr in flagged or attr in intentional_float: - continue - flagged.add(attr) - _diag_add( - f"[diagnostic] attr '{attr}' (used by {cat_name}) has no " - f"type_mappings entry and would fall back to default_type " - f"('{default_type}'). Pin its UE type in type_mappings, or " - f"list it under 'intentionally_default_typed_attrs' to " - f"acknowledge the fallback.", - source="new_attr_typing", - ) - - -_ATTR_NAME_RE = re.compile(r"^[a-z_][a-z0-9_]*$") - - -def _all_schema_attrs(schema: Dict[str, Any], rules: Dict[str, Any]) -> set: - """Union of every schema attr observed anywhere in the snapshot.""" - out: set = set() - - def _walk(node: Any) -> None: - if isinstance(node, list): - for item in node: - if isinstance(item, str) and _ATTR_NAME_RE.match(item): - out.add(item) - else: - _walk(item) - elif isinstance(node, dict): - for k, v in node.items(): - if k in ("obj_attr", "ref_attr") and isinstance(v, str): - if _ATTR_NAME_RE.match(v): - out.add(v) - _walk(v) - - _walk(schema) - for cat_rules in rules.get("categories", {}).values(): - out |= set(cat_rules.get("schema_common_extra_attrs", [])) - return out - - -def _check_orphan_rule_entries( - schema: Dict[str, Any], - rules: Dict[str, Any], -) -> None: - """Diagnose rule entries that reference attrs no schema element - uses. Catches stale rules left over from MuJoCo renaming/removing - an attr.""" - known_attrs = _all_schema_attrs(schema, rules) - if not known_attrs: - return - type_map = rules.get("type_mappings", {}) - for attr in type_map.keys(): - if attr not in known_attrs: - _diag_add( - f"[diagnostic] type_mappings['{attr}'] is set but no " - f"schema element exposes an attr by that name. Dead rule " - f"entry — remove, or check whether MuJoCo renamed/removed " - f"the attr.", - source="orphan_rule", - ) - PER_ELEM_KEYS = ( - "exclude_attrs", - "vec3_convert", - "property_renames", - "property_meta", - "property_defaults", - "export_skip_attrs", - "mjs_data_packed_attrs", - ) - for elem, elem_rules in rules.get("element_rules", {}).items(): - for key in PER_ELEM_KEYS: - block = elem_rules.get(key, {}) - if isinstance(block, dict): - rule_attrs = list(block.keys()) - elif isinstance(block, list): - rule_attrs = [ - a if isinstance(a, str) else a.get("attr") - for a in block - ] - else: - continue - for attr in rule_attrs: - if not attr or attr in known_attrs: - continue - _diag_add( - f"[diagnostic] element_rules['{elem}']['{key}'] " - f"references attr '{attr}', which is not present in " - f"ANY schema element. Stale rule.", - source="orphan_rule", - ) - - -def _check_mjxmacro_block_coverage( - rules: Dict[str, Any], - mjxmacro: Dict[str, Any], -) -> None: - """Bi-directional check between mjxmacro snapshot blocks and - ``synthetic_categories`` entries. Forward catches new blocks - upstream URLab doesn't model; reverse catches rule entries - pointing at vanished blocks.""" - blocks = mjxmacro.get("struct_fields", {}) - synthetic = rules.get("synthetic_categories", {}) - if not blocks and not synthetic: - return - block_to_rule: Dict[str, str] = {} - for key, entry in synthetic.items(): - if not isinstance(entry, dict): - continue - mb = entry.get("mjxmacro_block") - if mb: - block_to_rule[mb] = key - for block in blocks: - if block not in block_to_rule: - _diag_add( - f"[diagnostic] mjxmacro struct_fields block '{block}' has " - f"no synthetic_categories rule entry; the {len(blocks[block])} " - f"field(s) it declares will not be emitted as a UE struct.", - source="mjxmacro_block_coverage", - ) - for block, rule_key in block_to_rule.items(): - if block not in blocks: - _diag_add( - f"[diagnostic] synthetic_categories '{rule_key}' references " - f"mjxmacro_block '{block}' that does not exist in the current " - f"mjxmacro snapshot — block renamed or removed upstream.", - source="mjxmacro_block_coverage", - ) - - -# Apply-mode values _emit_synth_apply_methods_cpp understands. Anything -# else lands as a silent passthrough to vec3_y_flip / unconditional — -# the diagnostic surfaces typos so a rule author's intent is real. -_VALID_APPLY_MODES = ( - {"unconditional", "guarded", "pos"} - | set(_VEC3_CONVERT_FMT.keys()) - | {f"guarded_{k}" for k in _VEC3_CONVERT_FMT.keys()} - | {"guarded_pos"} -) - - -def _check_apply_mode_validity(rules: Dict[str, Any]) -> None: - """Diagnose ``field_apply_to_spec_mode`` values the codegen would - silently fall back on.""" - synth = rules.get("synthetic_categories", {}) - for cat_name, def_ in synth.items(): - if cat_name.startswith("_") or not isinstance(def_, dict): - continue - modes = def_.get("field_apply_to_spec_mode", {}) - if not isinstance(modes, dict): - continue - for field, mode in modes.items(): - if mode not in _VALID_APPLY_MODES: - _diag_add( - f"[diagnostic] synthetic_categories['{cat_name}']" - f".field_apply_to_spec_mode['{field}'] = '{mode}' " - f"is not a recognised apply mode. Expected one of " - f"{sorted(_VALID_APPLY_MODES)}.", - source="apply_mode", - ) - - -def _check_embedded_cpp_references( - rules: Dict[str, Any], mjspec: Optional[Dict[str, Any]], -) -> None: - """Diagnose rule entries that name a MuJoCo C symbol the mjspec - snapshot doesn't carry.""" - if not mjspec or "enums" not in mjspec: - return - known_consts: Set[str] = set() - for members in mjspec["enums"].values(): - if isinstance(members, dict): - known_consts.update(members.keys()) - if not known_consts: - return - for elem, elem_rules in rules.get("element_rules", {}).items(): - for attr, enum_def in elem_rules.get("xml_enum_attrs", {}).items(): - for xml_val, mapping in enum_def.get("value_map", {}).items(): - pair = _value_map_pair(mapping) - if pair is None: - continue - mj_const = pair[1] - if not isinstance(mj_const, str): - continue - if not mj_const.startswith("mj"): - continue - if mj_const not in known_consts: - _diag_add( - f"[diagnostic] element_rules['{elem}']" - f".xml_enum_attrs['{attr}'].value_map['{xml_val}'][1]" - f" = '{mj_const}' is not present in any mjspec enum. " - f"Likely typo — UE compile would fail with " - f"'undeclared identifier'.", - source="embedded_cpp_refs", - ) - for cat_name, cat_rules in rules.get("categories", {}).items(): - final = cat_rules.get("geom_final_type") - if not final: - continue - fallback = final.get("default_fallback") - if isinstance(fallback, str) and fallback.startswith("mj") and fallback not in known_consts: - _diag_add( - f"[diagnostic] categories['{cat_name}'].geom_final_type" - f".default_fallback = '{fallback}' is not present in any " - f"mjspec enum.", - source="embedded_cpp_refs", - ) - - -def _collect_every_schema_attr(schema: Dict[str, Any]) -> Set[str]: - """Walk the entire schema tree returning every attr name found. - Used by ``_check_allowlist_staleness``.""" - out: Set[str] = set() - - def walk(node: Any) -> None: - if isinstance(node, dict): - for v in node.values(): - if isinstance(v, list) and all(isinstance(s, str) for s in v): - out.update(v) - else: - walk(v) - elif isinstance(node, list): - for entry in node: - walk(entry) - - walk(schema) - return out - - -def _check_allowlist_staleness( - schema: Dict[str, Any], rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], -) -> None: - """Surface allowlist entries the codebase no longer needs. Covers - 4 of the 5 ``intentionally_*`` allowlists; the fifth - (intentionally_unmodeled_compiler_attrs) is policed indirectly via - _check_compiler_attrs_coverage.""" - unmodeled_elems: Dict[str, str] = rules.get("intentionally_unmodeled_elements", {}) - schema_keys = set(schema.keys()) - for key in unmodeled_elems: - if key not in schema_keys: - _diag_add( - f"[diagnostic] intentionally_unmodeled_elements lists '{key}' " - f"but the schema no longer carries that top-level element. " - f"Drop the entry.", - source="allowlist_staleness", - ) - - unmodeled_fields: Dict[str, List[str]] = rules.get("intentionally_unmodeled_mjs_fields", {}) - structs = (mjspec or {}).get("structs", {}) if mjspec else {} - if structs: - for struct, fields in unmodeled_fields.items(): - if struct not in structs: - _diag_add( - f"[diagnostic] intentionally_unmodeled_mjs_fields names " - f"struct '{struct}' that is missing from the current " - f"mjspec snapshot. Drop the entry.", - source="allowlist_staleness", - ) - continue - struct_field_set = set(structs[struct]) - for field in fields or []: - if field not in struct_field_set: - _diag_add( - f"[diagnostic] intentionally_unmodeled_mjs_fields" - f"['{struct}'] lists '{field}' but the field no " - f"longer exists on the mjs struct. Drop the entry.", - source="allowlist_staleness", - ) - - default_typed: List[str] = rules.get("intentionally_default_typed_attrs", []) - every_attr: Set[str] = _collect_every_schema_attr(schema) - for attr in default_typed: - if attr not in every_attr: - _diag_add( - f"[diagnostic] intentionally_default_typed_attrs lists " - f"'{attr}' but no schema element carries that attr. " - f"Drop the entry.", - source="allowlist_staleness", - ) - - unmapped: Dict[str, Any] = rules.get("intentionally_unmapped_mj_enum_values", {}) - mj_enums = (mjspec or {}).get("enums", {}) if mjspec else {} - if mj_enums: - for key in unmapped: - if key.startswith("_") or key == "__per_element__": - continue - if key not in mj_enums: - _diag_add( - f"[diagnostic] intentionally_unmapped_mj_enum_values " - f"names mjt enum '{key}' that is missing from the " - f"current mjspec snapshot. Drop the entry.", - source="allowlist_staleness", - ) - - -def _check_property_units_validity( - rules: Dict[str, Any], -) -> None: - """Diagnose property_units entries that use an unrecognised UE - display unit, or target a UE type that UHT rejects Units meta on.""" - for elem, elem_rules in rules.get("element_rules", {}).items(): - units = elem_rules.get("property_units", {}) - if not isinstance(units, dict): - continue - for attr, unit in units.items(): - if unit not in _KNOWN_UE_UNITS: - _diag_add( - f"[diagnostic] element_rules['{elem}'].property_units" - f"['{attr}'] = '{unit}' is not a UE-recognised display " - f"unit. Use one of {sorted(_KNOWN_UE_UNITS)} or extend " - f"_KNOWN_UE_UNITS in _codegen_core.", - source="property_units", - ) - - -def _check_compiler_attrs_coverage( - schema: Dict[str, Any], - rules: Dict[str, Any], -) -> None: - """Diagnose any attribute the MJCF schema lists that - URLab neither models in FMjCompilerSettings nor explicitly skips.""" - compiler_attrs = schema.get("compiler", {}).get("attrs", []) - if not compiler_attrs: - return - mapped = set(rules.get("compiler_attr_field_map", {}).keys()) - skipped = set(rules.get("intentionally_unmodeled_compiler_attrs", [])) - for attr in compiler_attrs: - if attr in mapped or attr in skipped: - continue - _diag_add( - f"[diagnostic] is in the MJCF schema " - f"but URLab does not handle it. Either add a field to " - f"FMjCompilerSettings + an entry in 'compiler_attr_field_map', " - f"or add to 'intentionally_unmodeled_compiler_attrs' if the " - f"flag has no UE-side effect.", - source="compiler_coverage", - ) - - -def _check_value_map_stale_mj_consts( - rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], -) -> None: - """Every value_map[*][1] is a literal mj* C constant emitted into - the generated .cpp; the snapshot cross-check surfaces removed/ - renamed constants before they break the C++ compile.""" - if not mjspec: - return - enums = mjspec.get("enums", {}) - if not enums: - return - known: set = set() - for members in enums.values(): - if isinstance(members, dict): - known.update(members.keys()) - if not known: - return - for elem, elem_rules in rules.get("element_rules", {}).items(): - for attr, enum_def in elem_rules.get("xml_enum_attrs", {}).items(): - value_map = enum_def.get("value_map", {}) - for xml_val, mapping in value_map.items(): - pair = _value_map_pair(mapping) - if pair is None: - continue - mj_const = pair[1] - if not isinstance(mj_const, str) or not mj_const.startswith("mj"): - continue - if mj_const not in known: - _diag_add( - f"[diagnostic] element '{elem}' xml_enum '{attr}' " - f"value_map entry '{xml_val}' references " - f"'{mj_const}', which is NOT in the mjspec " - f"snapshot's enum members. The generated .cpp " - f"will fail to compile when the MuJoCo headers " - f"update.", - source="value_map_stale", - ) - - -def _check_top_level_elements_coverage( - schema: Dict[str, Any], rules: Dict[str, Any], -) -> None: - """Flag top-level schema elements with no category in rules.""" - categories = rules.get("categories", {}) - known_blocks = {c.get("schema_common_block", "").split(".", 1)[0] - for c in categories.values()} - known_blocks.discard("") - container_keys = { - "_meta", "actuator_common", "actuator_types", "sensor_common", - "sensor_types", "sensor_per_type", "sensor_extras", - "tendon", "equality", "contact", - "asset", "compiler", "option", "keyframe", "default", - } - unmodeled: Dict[str, str] = rules.get("intentionally_unmodeled_elements", {}) - for key in schema: - if key.startswith("_") or key in container_keys or key in known_blocks: - continue - if key in unmodeled: - continue - _diag_add( - f"[diagnostic] schema has top-level element '{key}' but no " - f"category in codegen_rules.json (rules['categories']). " - f"Either add a category entry, list '{key}' in " - f"container_keys, or add an " - f"'intentionally_unmodeled_elements' entry explaining why " - f"it's skipped.", - source="top_level_coverage", - ) - - -def _check_actuator_subtypes_coverage( - schema: Dict[str, Any], rules: Dict[str, Any], -) -> None: - act_cat = rules.get("categories", {}).get("actuator", {}) - known_act_subs = {s.get("key") for s in act_cat.get("subtypes", [])} - for name in schema.get("actuator_types", {}): - if name not in known_act_subs: - _diag_add( - f"[diagnostic] schema actuator subtype '{name}' has no " - f"entry in categories.actuator.subtypes. Add a subtype " - f"+ subtype_setto rule pair so codegen emits the UMj" - f"{name.title()}Actuator class.", - source="actuator_subtypes_coverage", - ) - - -def _check_sensor_subtypes_coverage( - schema: Dict[str, Any], rules: Dict[str, Any], -) -> None: - sens_cat = rules.get("categories", {}).get("sensor", {}) - known_sens_subs = {s.get("key") for s in sens_cat.get("subtypes", [])} - for name in schema.get("sensor_types", []): - if name not in known_sens_subs: - _diag_add( - f"[diagnostic] schema sensor type '{name}' has no entry " - f"in categories.sensor.subtypes. Add it so the per-sensor " - f"UMjXSensor subclass is emitted.", - source="sensor_subtypes_coverage", - ) - - -def _check_setto_param_coverage( - schema: Dict[str, Any], rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], -) -> None: - """Flag mjs_setTo* parameters with no UE-side mapping AND no - explicit sentinel default.""" - if not mjspec or "setto_functions" not in mjspec: - return - categories = rules.get("categories", {}) - setto_defaults = rules.get("setto_param_defaults", {}) - for cat_name, cat_rules in categories.items(): - setto_block = cat_rules.get("subtype_setto") or {} - common_attrs = set(schema_attrs( - schema, cat_rules.get("schema_common_block", ""))) - for sub_key, setto_def in setto_block.items(): - if not isinstance(setto_def, dict): - continue - fn_name = setto_def.get("call") - if not fn_name or fn_name not in mjspec["setto_functions"]: - continue - sig = mjspec["setto_functions"][fn_name] - renames = setto_def.get("param_renames", {}) - fn_defaults = set(setto_defaults.get(fn_name, {}).keys()) - nullable = set(setto_def.get("nullable_params", [])) - inherited = set(setto_def.get("inherit_fields", {}).keys()) - sub_attrs = set(schema_subtype_attrs(schema, cat_name, sub_key)) - schema_attr_set = common_attrs | sub_attrs - for p in sig.get("params", []): - pname = p["name"] - ue_prop = renames.get(pname, pname) - if (ue_prop in schema_attr_set or pname in fn_defaults - or pname in nullable or pname in inherited): - continue - _diag_add( - f"[diagnostic] {fn_name} param '{pname}' is not " - f"in the {cat_name}.{sub_key} schema attrs and " - f"has no param_renames / setto_param_defaults / " - f"nullable_params / inherit_fields entry. Codegen " - f"passes the generic sentinel for it. If MuJoCo added " - f"this param, decide whether to expose it as a UE " - f"UPROPERTY (schema attr + type_mapping), pin its " - f"sentinel, or mark it nullable/inherited.", - source="setto_param_coverage", - ) - - -def _check_mjs_struct_field_drift( - schema: Dict[str, Any], rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], -) -> None: - """Flag mjs struct fields with no schema-attr coverage or rule - binding.""" - if not mjspec or "structs" not in mjspec: - return - categories = rules.get("categories", {}) - unmodeled_by_struct: Dict[str, set] = { - struct: set(fields) for struct, fields in - rules.get("intentionally_unmodeled_mjs_fields", {}).items() - } - for cat_name, cat_rules in categories.items(): - mjs_struct = cat_rules.get("mjs_struct") - if not mjs_struct or mjs_struct not in mjspec["structs"]: - continue - block = cat_rules.get("schema_common_block", "") - schema_attrs_set = set(schema_attrs(schema, block)) - for sub in cat_rules.get("subtypes", []): - schema_attrs_set.update( - schema_subtype_attrs(schema, cat_name, sub.get("key", "")) - ) - mjs_field_set = set(mjspec["structs"][mjs_struct]) - elem_rules = rules.get("element_rules", {}).get(cat_name, {}) - attr_to_mjs = elem_rules.get("attr_to_mjs_field", {}) - mapped_fields = set(attr_to_mjs.values()) - for coll_def in elem_rules.get("target_collations", {}).values(): - if isinstance(coll_def, dict) and coll_def.get("mjs_field"): - mapped_fields.add(coll_def["mjs_field"]) - for enum_def in elem_rules.get("xml_enum_attrs", {}).values(): - if isinstance(enum_def, dict) and enum_def.get("mjs_field"): - mapped_fields.add(enum_def["mjs_field"]) - for packed in elem_rules.get("mjs_data_packed_attrs", {}).values(): - if isinstance(packed, dict): - mapped_fields.add(packed.get("target") or "data") - for da in elem_rules.get("mjs_double_vec_attrs", []): - mapped_fields.add(da) - if cat_rules.get("type_enum_name"): - mapped_fields.add("type") - mapped_fields.add("objtype") - for conv_list in elem_rules.get("unit_conversion", {}).values(): - for entry in conv_list if isinstance(conv_list, list) else []: - if isinstance(entry, dict) and entry.get("mjs_field"): - mapped_fields.add(entry["mjs_field"]) - # Canonicalisation name -> set of mjs struct fields its export - # block writes. Lets the drift check treat canon-owned fields as - # covered. Inlined here because this is the only consumer; kept - # in lock-step with the inline canon emitters in - # generate_ue_components (_canon_export_*). - canon_mjs_fields = { - "body_sleep_policy": {"sleep"}, - "actuator_transmission": {"target", "trntype", "slidersite", "refsite"}, - "fromto_decompose": set(), - "orientation": {"quat"}, - "spatial_pose": {"pos"}, - } - for canon in elem_rules.get("applies_canonicalizations", []): - mapped_fields |= canon_mjs_fields.get(canon, set()) - structural = { - "element", "info", "userdata", "user", "name", "classname", - "parent", "frame", "explicitinertial", - } - structural |= unmodeled_by_struct.get(mjs_struct, set()) - structural |= mapped_fields - normalised_schema = {a.replace("_", "").lower() - for a in schema_attrs_set} - for mjs_field in mjs_field_set: - if mjs_field in structural: - continue - candidate = mjs_field.replace("_", "").lower() - if candidate in normalised_schema: - continue - stripped = candidate.replace("name", "") - if stripped in normalised_schema: - continue - if candidate.startswith("act") and ("actuator" + candidate[3:]) in normalised_schema: - continue - _diag_add( - f"[diagnostic] {mjs_struct}.{mjs_field} (in mjspec_snapshot) " - f"has no corresponding schema attr in '{cat_name}'. Either " - f"add a schema canonicalisation, list it under " - f"intentionally_unmodeled_mjs_fields, or accept that codegen " - f"silently skips it.", - source="mjs_field_drift", - ) - - -def _check_generated_enum_coverage( - rules: Dict[str, Any], mjspec: Optional[Dict[str, Any]], -) -> None: - """Flag generated_enums entries that map fewer mj enum members - than the snapshot carries.""" - if not mjspec or "enums" not in mjspec: - return - for gen_name, gen_def in rules.get("generated_enums", {}).items(): - if gen_name.startswith("_") or not isinstance(gen_def, dict): - continue - if gen_def.get("disabled"): - continue - for entry in gen_def.get("enums", []): - if entry.get("disabled"): - continue - mj_enum_name = entry.get("from_mj_enum") - ue_member_from_mj = entry.get("ue_member_from_mj", {}) - exclude = set(entry.get("exclude_mj_members", [])) - if not mj_enum_name: - continue - c_enum = mjspec["enums"].get(mj_enum_name, {}) - if not c_enum: - _diag_add( - f"[diagnostic] generated_enums['{gen_name}'].{entry.get('ue_name')}: " - f"from_mj_enum '{mj_enum_name}' not found in the projected mjspec. " - f"Rebuild the introspect snapshot, or correct the enum name.", - source="enum_drift", - ) - continue - mapped = set(ue_member_from_mj) | exclude - missing = [m for m in c_enum if m not in mapped] - if missing: - _diag_add( - f"[diagnostic] generated_enums['{gen_name}'].{entry.get('ue_name')}: " - f"C enum '{mj_enum_name}' has {len(missing)} member(s) with no UE " - f"mapping: {missing[:5]}{'...' if len(missing) > 5 else ''}. Add to " - f"ue_member_from_mj or exclude_mj_members.", - source="enum_drift", - ) - - -def _emit_drift_diagnostics(schema: Dict[str, Any], rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]]) -> None: - """Run every drift-coverage check. Each helper surfaces one class - of drift. All diagnostics route through the module-level - ``_DIAGS_BUFFER.pending`` collector and flush once at the end of - ``collect_all_writes``.""" - _check_top_level_elements_coverage(schema, rules) - _check_actuator_subtypes_coverage(schema, rules) - _check_sensor_subtypes_coverage(schema, rules) - _check_setto_param_coverage(schema, rules, mjspec) - _check_mjs_struct_field_drift(schema, rules, mjspec) - _check_generated_enum_coverage(rules, mjspec) diff --git a/Scripts/codegen/_codegen_core.py b/Scripts/codegen/_codegen_core.py deleted file mode 100644 index 0e4cf0f3..00000000 --- a/Scripts/codegen/_codegen_core.py +++ /dev/null @@ -1,487 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""URLab codegen — core shared data + helpers. - -Dependency root. Owns: - -- ``DiagBuffer`` — the diagnostics buffer the orchestrator + checks - share. -- ``_UE_TYPE_INFO`` — single source of truth for every UE type - codegen can emit; ``_UE_TYPE_FAMILY`` and - ``_UE_TYPES_ACCEPTING_UNITS_META`` are derived views. -- Naming helpers, schema readers, canon-iteration helpers. -- The 5-strategy ``_resolve_mjs_field`` chain — the bridge between - MJCF attr names and the C struct field names MuJoCo picks. -- ``_resolve_value_map`` — the xml_enum value-map reader. -- ``FileWrite`` — the data class every emitter produces. - -Read-only against rules / schema / snapshot. No file writes, no -UPROPERTY emission, no .cpp injection. ``generate_ue_components`` -and ``_codegen_checks`` both import from here. -""" - -from __future__ import annotations - -import re -import sys -from dataclasses import dataclass -from typing import Any, Dict, Iterable, Iterator, List, Optional, Sequence, Set, Tuple - - -# --------------------------------------------------------------------------- -# Diagnostics -# --------------------------------------------------------------------------- - -@dataclass -class Diagnostic: - """A non-fatal codegen warning. Collected by ``_diag_add`` during a - run and flushed once at the end via ``_diag_flush``.""" - message: str - source: str = "" # short tag for grouping (e.g. "schema_drift") - - -class DiagBuffer: - """Collects codegen diagnostics during a run. ``pending`` is the - queue of unflushed entries; ``fired_count`` survives the flush so - ``main()`` can check whether ``--strict`` should exit non-zero.""" - - def __init__(self) -> None: - self.pending: List[Diagnostic] = [] - self.fired_count: int = 0 - - def add(self, message: str, *, source: str = "") -> None: - self.pending.append(Diagnostic(message=message, source=source)) - - def flush(self) -> None: - """Print collected diagnostics to stderr, advance ``fired_count`` - by the number flushed, and clear the pending list.""" - if not self.pending: - return - print(f"\n--- codegen diagnostics ({len(self.pending)}) ---", - file=sys.stderr) - for d in self.pending: - print(d.message, file=sys.stderr) - print( - "(diagnostics are non-fatal; the codegen has emitted what it " - "can — fix the rules to silence them.)", - file=sys.stderr, - ) - self.fired_count += len(self.pending) - self.pending.clear() - - def reset(self) -> None: - self.pending.clear() - self.fired_count = 0 - - -_DIAGS_BUFFER = DiagBuffer() - - -def _diag_add(message: str, *, source: str = "") -> None: - _DIAGS_BUFFER.add(message, source=source) - - -def _diag_flush() -> None: - _DIAGS_BUFFER.flush() - - -def _reset_diags() -> None: - """Reset the diag buffer between runs so back-to-back invocations - in the same Python process (test runners, integration shells) - don't carry stale state.""" - _DIAGS_BUFFER.reset() - - -# --------------------------------------------------------------------------- -# UE type registry -# --------------------------------------------------------------------------- - -@dataclass(frozen=True) -class _UEType: - """Single source of truth for a UE type codegen can emit. Every per- - type dispatch (default initializer, drift-check shape, UHT Units-meta - gate) reads this row instead of redeclaring the type universe.""" - default_init: str # C++ initializer (e.g. ``"0.0f"``) - shape: Tuple[str, Any] # drift-check shape (see _classify_c_type) - accepts_units_meta: bool # UHT allows Units="..." on this type - - -# Every UE type codegen can target. ``_attr_default_value``, -# ``_UE_TYPE_FAMILY``, and ``_UE_TYPES_ACCEPTING_UNITS_META`` below are -# thin views over this dict. -_UE_TYPE_INFO: Dict[str, _UEType] = { - "float": _UEType("0.0f", ("scalar", "num"), True), - "double": _UEType("0.0", ("scalar", "num"), True), - "int32": _UEType("0", ("scalar", "int"), True), - "uint8": _UEType("0", ("scalar", "int"), True), # synth-mirror only - "bool": _UEType("false", ("scalar", "bool"), False), - "FString": _UEType('TEXT("")', ("scalar", "string"), False), - "FVector": _UEType("FVector::ZeroVector", ("vec", 3), True), - "FQuat": _UEType("FQuat::Identity", ("vec", 4), False), - "FLinearColor": _UEType("FLinearColor::White", ("vec", 4), False), - "TArray": _UEType("{}", ("array", None), False), - "TArray": _UEType("{}", ("array", None), False), -} - - -def _attr_default_value(ue_type: str) -> str: - info = _UE_TYPE_INFO.get(ue_type) - if info is not None: - return info.default_init - # Unknown types fall through to the default-construct literal — same - # for novel TArray<...> entries (e.g. TArray) the registry - # doesn't enumerate. - return "{}" - - -# UE type → shape for ``_check_type_shape_drift``. Derived from -# ``_UE_TYPE_INFO`` so the type universe is declared once. -_UE_TYPE_FAMILY = {k: v.shape for k, v in _UE_TYPE_INFO.items()} - - -_UE_TYPES_ACCEPTING_UNITS_META = { - k for k, v in _UE_TYPE_INFO.items() if v.accepts_units_meta -} - - -def _ue_type_accepts_units_meta(ue_type: str) -> bool: - """UHT enforces 'Units' meta only on numeric scalars + numeric - structs. Returns False for TArray<...>, FString, FLinearColor, - FQuat — codegen silently drops the Units meta for those types so - the build succeeds.""" - info = _UE_TYPE_INFO.get(ue_type) - return bool(info and info.accepts_units_meta) - - -_DYNAMIC_VEC_TYPES = { - "mjFloatVec", "mjDoubleVec", "mjIntVec", "mjStringVec", - "mjFloatVecVec", "mjDoubleVecVec", "mjIntVecVec", -} - - -_KNOWN_UE_UNITS = { - # UE5 numeric units the editor knows how to render + (optionally) - # convert via Display Units preferences. - "m", "cm", "mm", "km", - "rad", "deg", -} - - -@dataclass -class FileWrite: - """One file write the codegen would perform. Collected during the - phase walk; ``apply_writes`` either flushes them to disk, prints - diffs, or returns the count under ``--check``.""" - path: str - content: str - - -def override_toggle_name(prop_name: str) -> str: - """``bOverride_`` is the codegen-emitted toggle field that - gates whether the corresponding UPROPERTY's value gets written - back to the mjs struct.""" - return f"bOverride_{prop_name}" - - -# --------------------------------------------------------------------------- -# Rule-iteration helpers -# --------------------------------------------------------------------------- - - -def iter_canon_absorbed(elem_canons: Iterable[str], - canonicalizations: Dict[str, Any]) -> Iterator[str]: - """Yield every attr absorbed by any canonicalisation in - ``elem_canons``. Skips unknown canon keys silently — diagnostics - for those live in the rule-validity drift checks.""" - if not canonicalizations: - return - for c in elem_canons: - cd = canonicalizations.get(c) - if cd: - yield from cd.get("absorbs_attrs", ()) - - -def compute_canon_absorbed(elem_canons: Iterable[str], - canonicalizations: Dict[str, Any]) -> Set[str]: - return set(iter_canon_absorbed(elem_canons, canonicalizations)) - - -def iter_category_attrs(attrs: Iterable[str], *, - global_excl: Set[str], - elem_excl: Set[str], - canon_absorbed: Set[str], - extra_excl: Optional[Set[str]] = None) -> Iterator[str]: - """Yield attrs that pass the standard codegen exclusion gates plus - any site-specific ``extra_excl`` set the caller assembles - (xml_enum_attrs, target-collation-absorbed, etc — these vary per - emission stage).""" - for attr in attrs: - if attr in global_excl or attr in elem_excl or attr in canon_absorbed: - continue - if extra_excl is not None and attr in extra_excl: - continue - yield attr - - -# --------------------------------------------------------------------------- -# Schema readers -# --------------------------------------------------------------------------- - - -def schema_attrs(schema: Dict[str, Any], path: str) -> List[str]: - """Read an attribute list at a dotted path, e.g. - ``actuator_common.attrs``. Returns [] if missing.""" - cur: Any = schema - for piece in path.split("."): - if isinstance(cur, dict) and piece in cur: - cur = cur[piece] - else: - return [] - if isinstance(cur, list): - if cur and isinstance(cur[0], str): - return list(cur) - attrs: List[str] = [] - for entry in cur: - if isinstance(entry, dict): - if "attrs" in entry and isinstance(entry["attrs"], list): - attrs.extend([str(x) for x in entry["attrs"]]) - else: - for v in entry.values(): - if isinstance(v, list): - attrs.extend([str(x) for x in v]) - seen = set() - out = [] - for a in attrs: - if a not in seen: - seen.add(a) - out.append(a) - return out - if isinstance(cur, dict): - attrs2: List[str] = [] - for v in cur.values(): - if isinstance(v, list): - attrs2.extend([str(x) for x in v if isinstance(x, str)]) - return attrs2 - return [] - - -def schema_subtype_attrs(schema: Dict[str, Any], category: str, - subtype_key: str) -> List[str]: - """Look up per-subtype attrs from schema['actuator_types'][key] / - schema['sensor_types'] (list-of-dict). Returns [] if not found.""" - if category == "actuator": - block = schema.get("actuator_types", {}) - if isinstance(block, dict): - entry = block.get(subtype_key) - if isinstance(entry, list): - return [str(x) for x in entry] - if isinstance(entry, dict) and "attrs" in entry: - return [str(x) for x in entry["attrs"]] - return [] - if category == "sensor": - block = schema.get("sensor_types", []) - if isinstance(block, list): - for entry in block: - if isinstance(entry, dict) and entry.get("name") == subtype_key: - attrs = entry.get("attrs", []) - return [str(x) for x in attrs] if isinstance(attrs, list) else [] - if isinstance(entry, str) and entry == subtype_key: - return [] - return [] - return [] - - -# --------------------------------------------------------------------------- -# C-type classifier (drift-check inputs) -# --------------------------------------------------------------------------- - - -def _classify_c_type(c_type: str, array_dim) -> Optional[tuple]: - """Reduce an introspect c_type + array_dim down to a comparable - (shape, dim_or_family) tuple. Shapes: - ``("scalar", fam)`` — single value, fam ∈ {num,int,bool,string} - ``("vec", N)`` — fixed-size C array of length N - ``("array", None)`` — dynamic-length C vec (mjFloatVec, etc.) - ``("opaque", _)`` — mjsElement* and other spec-internal - pointers URLab never exposes; returning a - distinct tag lets the check skip them - without silently accepting "scalar" - matches. - Returns None for genuinely unknown bases so the check stays quiet - on novel types instead of producing noise. - """ - ct = c_type.strip() - base_no_arr = re.sub(r"\s*\[[^\]]*\]\s*", "", ct).strip() - base_no_ptr = base_no_arr.rstrip(" *").strip() - if base_no_ptr in _DYNAMIC_VEC_TYPES: - return ("array", None) - if base_no_ptr.startswith("mjsElement") or base_no_ptr.endswith("Element"): - return ("opaque", None) - if "*" in ct or "mjString" in ct or "std::string" in ct: - return ("scalar", "string") - is_array = array_dim is not None and array_dim != 1 - base = base_no_arr - fam: Optional[str] = None - if base in ("mjtNum", "float", "double"): - fam = "num" - elif base in ("int", "mjtByte", "unsigned char", "uint8_t", "uchar"): - fam = "int" - elif base in ("bool", "_Bool", "mjtBool"): - fam = "bool" - else: - return None - if is_array: - if array_dim in (3, 4): - return ("vec", array_dim) - return ("array", None) - return ("scalar", fam) - - -def _shapes_compatible(ue_shape: tuple, c_shape: tuple) -> bool: - """True when ``ue_shape`` can legitimately back ``c_shape``. - - A ``TArray`` (array) is shape-compatible with a fixed-size C - array (vec) because the codegen writes ``prop.Num()`` elements - into the C array. - Vec-to-vec requires the dim to match exactly. - Scalar↔array always mismatches. Opaque C shapes are skipped.""" - if c_shape[0] == "opaque": - return True - if ue_shape[0] == c_shape[0]: - if ue_shape[0] == "vec": - return ue_shape[1] == c_shape[1] - return True - if ue_shape[0] == "array" and c_shape[0] == "vec": - return True - return False - - -# --------------------------------------------------------------------------- -# Vec3 convert format table -# --------------------------------------------------------------------------- -# -# Maps the per-field ``apply_to_spec_mode`` rule values for FVector -# UPROPERTYs to the per-element write template the synth-struct emitter -# uses. Each entry is a Python format string with two slots: ``{c}`` -# (the destination C-array expression) and ``{ue}`` (the source UE -# FVector expression). - -_VEC3_CONVERT_FMT = { - "pos": "{c}[0] = {ue}.X / 100.0; {c}[1] = -{ue}.Y / 100.0; {c}[2] = {ue}.Z / 100.0;", - "vec3_cm": "{c}[0] = {ue}.X / 100.0; {c}[1] = -{ue}.Y / 100.0; {c}[2] = {ue}.Z / 100.0;", - "vec3_y_flip": "{c}[0] = {ue}.X; {c}[1] = -{ue}.Y; {c}[2] = {ue}.Z;", - "passthrough": "{c}[0] = {ue}.X; {c}[1] = {ue}.Y; {c}[2] = {ue}.Z;", -} - - -# --------------------------------------------------------------------------- -# mjs-field resolver chain (the 5-strategy bridge) -# --------------------------------------------------------------------------- - - -def _resolve_direct(attr: str, mjs_fields: Set[str]) -> Optional[str]: - """``attr`` matches a struct field verbatim (condim -> condim).""" - return attr if attr in mjs_fields else None - - -def _resolve_name_suffix(attr: str, mjs_fields: Set[str]) -> Optional[str]: - """``attr + "name"`` matches (mesh -> meshname). MuJoCo's name- - ref fields uniformly append ``name`` to the schema attr.""" - cand = attr + "name" - return cand if cand in mjs_fields else None - - -def _resolve_root_name_digits(attr: str, mjs_fields: Set[str]) -> Optional[str]: - """```` -> ``name`` (body1 -> - bodyname1).""" - m = re.match(r"^(.*?)(\d+)$", attr) - if not m: - return None - root, digits = m.group(1), m.group(2) - for candidate in (f"{root}name{digits}", f"{root}_name{digits}"): - if candidate in mjs_fields: - return candidate - return None - - -def _resolve_underscore_norm(attr: str, mjs_fields: Set[str]) -> Optional[str]: - """Strip all underscores and look for matching letters - (solreflimit -> solref_limit). Lossy — only used when nothing - cleaner fits.""" - norm = attr.replace("_", "") - for f in mjs_fields: - if f.replace("_", "") == norm: - return f - return None - - -def _resolve_actuator_prefix(attr: str, mjs_fields: Set[str]) -> Optional[str]: - """Schema ``actuator*`` -> mjs ``act*`` (actuatorfrclimited -> - actfrclimited). MuJoCo abbreviates the prefix on its struct - fields even though the XML attr spells it out.""" - if not attr.startswith("actuator"): - return None - cand = "act" + attr[len("actuator"):] - if cand in mjs_fields: - return cand - cand_norm = cand.replace("_", "") - for f in mjs_fields: - if f.replace("_", "") == cand_norm: - return f - return None - - -_MJS_FIELD_RESOLVERS = ( - _resolve_direct, - _resolve_name_suffix, - _resolve_root_name_digits, - _resolve_underscore_norm, - _resolve_actuator_prefix, -) - - -def _resolve_mjs_field(attr: str, mjs_fields: set, - attr_to_mjs_field: Optional[Dict[str, str]] = None) -> str: - """Pick the correct C field name on a category's mjs struct for a - given schema attribute. Per-element ``attr_to_mjs_field`` - overrides apply first; otherwise the ``_MJS_FIELD_RESOLVERS`` - chain captures MuJoCo's own naming conventions in priority order. - Falls back to ``attr`` if nothing matches — the build then - surfaces the mismatch loudly when the export line tries to access - ``Element->attr``.""" - if attr_to_mjs_field and attr in attr_to_mjs_field: - return attr_to_mjs_field[attr] - if not mjs_fields: - return attr - for resolver in _MJS_FIELD_RESOLVERS: - hit = resolver(attr, mjs_fields) - if hit is not None: - return hit - return attr - - -# --------------------------------------------------------------------------- -# xml_enum value-map resolver -# --------------------------------------------------------------------------- - - -def _resolve_value_map(attr: str, enum_def: Dict[str, Any]) -> Dict[str, Sequence[str]]: - """Return the ``{xml_val -> [UE_enum_member, mj_const]}`` table for - an xml_enum attr. The rule must carry an explicit ``value_map``.""" - if "value_map" in enum_def: - return enum_def["value_map"] - raise RuntimeError(f"xml_enum {attr}: rule is missing value_map") - - -def _value_map_pair(mapping: Any) -> Optional[Tuple[str, str]]: - """Unpack a ``value_map`` entry into ``(ue_member, mj_const)`` or - return None when the shape is malformed. The 4 check sites that - used to inline ``isinstance(mapping, (list, tuple)) and len(mapping) - >= 2`` route through here so one shape contract covers all of - them. The corresponding emit-side path raises (see - ``_emit_xml_enum_export``) — the rule shape contract test - (test_rule_shape_contract.py::test_xml_enum_attr_value_maps_have_ - correct_shape) is the canonical gate; this helper is the - runtime fallback for the drift checks.""" - if not isinstance(mapping, (list, tuple)) or len(mapping) < 2: - return None - return mapping[0], mapping[1] diff --git a/Scripts/codegen/_codegen_inject.py b/Scripts/codegen/_codegen_inject.py deleted file mode 100644 index f11d70d1..00000000 --- a/Scripts/codegen/_codegen_inject.py +++ /dev/null @@ -1,196 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""URLab codegen — text-injection machinery. - -Pure text operations: locate ``// --- CODEGEN__START/END ---`` -marker pairs in a source file, splice a body between them, and emit -diagnostics when the body has unbalanced braces or the markers are -missing. - -The brace-aware helpers (``_find_matching_brace``, -``_strip_cpp_braces_in_strings``, ``_check_brace_balance``) live -here too because the banner-overwrite audit + the brace-balance -gate both consume them.""" - -from __future__ import annotations - -import os -import re -from typing import Dict, List, Optional, Sequence, Tuple - -from _codegen_core import _diag_add, FileWrite - - -def _make_tag_pair(tag: str) -> Tuple[str, str]: - return (f"// --- CODEGEN_{tag}_START ---", f"// --- CODEGEN_{tag}_END ---") - - -def _strip_cpp_braces_in_strings(text: str) -> str: - """Remove characters that look like braces but live inside a - string/char literal or comment. Used by ``_check_brace_balance`` - so a diagnostic message containing ``"{"`` doesn't trip the - balance gate.""" - text = re.sub(r"//[^\n]*", "", text) - text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL) - text = re.sub(r'"(?:\\.|[^"\\])*"', '""', text) - text = re.sub(r"'(?:\\.|[^'\\])*'", "''", text) - return text - - -def _find_matching_brace(text: str, open_idx: int) -> Optional[int]: - """Return the index of the ``}`` matching the ``{`` at - ``open_idx``. Tracks brace depth across string/char literals and - line/block comments so a nested ``{`` inside a TEXT(...) literal - doesn't shift the balance. Returns None on EOF / unmatched.""" - if open_idx < 0 or open_idx >= len(text) or text[open_idx] != "{": - return None - depth = 1 - i = open_idx + 1 - in_str: Optional[str] = None - while i < len(text) and depth > 0: - ch = text[i] - if in_str is not None: - if ch == "\\" and i + 1 < len(text): - i += 2 - continue - if ch == in_str: - in_str = None - i += 1 - continue - if ch == "/" and i + 1 < len(text) and text[i + 1] == "/": - nl = text.find("\n", i) - i = nl if nl != -1 else len(text) - continue - if ch == "/" and i + 1 < len(text) and text[i + 1] == "*": - end = text.find("*/", i + 2) - i = end + 2 if end != -1 else len(text) - continue - if ch in ('"', "'"): - in_str = ch - elif ch == "{": - depth += 1 - elif ch == "}": - depth -= 1 - if depth == 0: - return i - i += 1 - return None - - -def _check_brace_balance(body: str, tag: str, source: str) -> None: - """Emit a diagnostic when an injected block has an obvious brace - imbalance.""" - stripped = _strip_cpp_braces_in_strings(body) - opens = stripped.count("{") - closes = stripped.count("}") - if opens != closes: - _diag_add( - f"[diagnostic] {source}: injected block for tag " - f"CODEGEN_{tag} has unbalanced braces (open={opens}, " - f"close={closes}). The generated .cpp will not compile.", - source=source, - ) - - -_TAG_PATTERN_CACHE: Dict[str, "re.Pattern[str]"] = {} - - -def _tag_pattern(tag: str) -> "re.Pattern[str]": - pat = _TAG_PATTERN_CACHE.get(tag) - if pat is None: - start, end = _make_tag_pair(tag) - pat = re.compile( - r"([ \t]*)(" + re.escape(start) + r")(.*?)([ \t]*)(" + re.escape(end) + r")", - re.DOTALL, - ) - _TAG_PATTERN_CACHE[tag] = pat - return pat - - -def inject_between_tags(file_text: str, tag: str, new_inner: str, - *, balance_source: Optional[str] = None) -> Tuple[str, bool]: - """Replace the content between ``// --- CODEGEN__START ---`` - and ``// --- CODEGEN__END ---`` with ``new_inner``. Returns - (new_text, was_found). - - The END marker's leading whitespace is rewritten to match the - START marker's indent. When ``new_inner`` is empty or - whitespace-only the block collapses so START + END sit on - adjacent lines with no stray blank line between them. - - When ``balance_source`` is set, emit a diagnostic if the injected - block has unbalanced braces.""" - pattern = _tag_pattern(tag) - matched = [False] - - def repl(m: re.Match) -> str: - matched[0] = True - start_indent = m.group(1) - trimmed = new_inner.rstrip() - if trimmed: - body = "\n" + trimmed + "\n" + start_indent - else: - # Empty body: collapse to one newline so START + END sit - # adjacent (no stray blank line between them). - body = "\n" + start_indent - return m.group(1) + m.group(2) + body + m.group(5) - - new_text = pattern.sub(repl, file_text) - if matched[0] and balance_source: - _check_brace_balance(new_inner, tag, balance_source) - return new_text, matched[0] - - -def _inject_tags_into_cpp( - cpp_path: str, - tagged_bodies: Sequence[Tuple[str, str]], - writes: List[FileWrite], - *, - diag_source: str, -) -> None: - """Open ``cpp_path`` once, inject every (tag, body) pair, surface - a diagnostic for each missing marker pair, append (or update) a - single FileWrite if any tag actually changed the text. - - Phase ordering: earlier phases (e.g. ``_phase_categories``) may have - queued a FileWrite for the same path. Reading from disk in that case - would be stale — the later phase would inject into the PRE-cleanup - text and the post-cleanup IMPORT/EXPORT block would be lost on - apply. Use the pending write's content when present, and replace - it in-place so duplicate FileWrites for the same path don't show - up in ``apply_writes``.""" - pending_idx: Optional[int] = None - for i in range(len(writes) - 1, -1, -1): - if writes[i].path == cpp_path: - pending_idx = i - break - - if pending_idx is not None: - text = writes[pending_idx].content - elif os.path.exists(cpp_path): - with open(cpp_path, "r", encoding="utf-8") as f: - text = f.read() - else: - _diag_add( - f"[diagnostic] {diag_source}: host .cpp '{cpp_path}' does " - f"not exist; no blocks injected.", - source=diag_source, - ) - return - - new_text = text - for tag, body in tagged_bodies: - new_text, ok = inject_between_tags( - new_text, tag, body, balance_source=diag_source, - ) - if not ok: - _diag_add( - f"[diagnostic] {diag_source}: '{cpp_path}' is missing " - f"the CODEGEN_{tag}_START/END marker pair.", - source=diag_source, - ) - if new_text == text: - return - if pending_idx is not None: - writes[pending_idx] = FileWrite(path=cpp_path, content=new_text) - else: - writes.append(FileWrite(path=cpp_path, content=new_text)) diff --git a/Scripts/codegen/_vendored/__init__.py b/Scripts/codegen/_vendored/__init__.py deleted file mode 100644 index 0f5b8bcf..00000000 --- a/Scripts/codegen/_vendored/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Local support modules for URLab codegen. - -Currently holds ``ast_nodes.py`` (clang-AST node dataclasses used by -``build_introspect_snapshot.py``). MuJoCo headers are no longer vendored -here — ``mjspecmacro.h`` ships in the installed MuJoCo headers and is read -from ``third_party/install`` like the other headers. -""" diff --git a/Scripts/codegen/_vendored/ast_nodes.py b/Scripts/codegen/_vendored/ast_nodes.py deleted file mode 100644 index f85cc9a7..00000000 --- a/Scripts/codegen/_vendored/ast_nodes.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Lightweight AST-node dataclasses used by ``build_introspect_snapshot.py`` -to project clang.cindex AST cursors into a serialisable shape. - -URLab-side rather than pulled from upstream because (a) the upstream -``clang.cindex`` types don't pickle cleanly across libclang versions and -(b) the snapshot only needs a small subset of node info. Keeping the -projection in one place means the schema is auditable from JSON, and -test fixtures can be hand-built without needing a real libclang install. -""" - -from __future__ import annotations - -from dataclasses import dataclass, field, asdict -from typing import Any, Dict, List, Optional - - -@dataclass -class CFunctionParam: - name: str - c_type: str - array_dim: Optional[int] = None # None for scalar / pointer - - def to_dict(self) -> Dict[str, Any]: - return asdict(self) - - -@dataclass -class CFunction: - name: str - return_type: str - params: List[CFunctionParam] = field(default_factory=list) - doc: str = "" - file: str = "" # source path - line: int = 0 - - def to_dict(self) -> Dict[str, Any]: - return { - "name": self.name, - "return_type": self.return_type, - "params": [p.to_dict() for p in self.params], - "doc": self.doc, - "file": self.file, - "line": self.line, - } - - -@dataclass -class CEnumMember: - name: str - value: int - doc: str = "" - - -@dataclass -class CEnum: - name: str - members: List[CEnumMember] = field(default_factory=list) - underlying_type: Optional[str] = None - doc: str = "" - file: str = "" - line: int = 0 - - def to_dict(self) -> Dict[str, Any]: - return { - "name": self.name, - "members": {m.name: m.value for m in self.members}, - "member_docs": {m.name: m.doc for m in self.members if m.doc}, - "underlying_type": self.underlying_type, - "doc": self.doc, - "file": self.file, - "line": self.line, - } - - -@dataclass -class CStructField: - name: str - c_type: str - array_dim: Optional[int] = None - is_pointer: bool = False - doc: str = "" - - -@dataclass -class CStruct: - name: str - fields: List[CStructField] = field(default_factory=list) - doc: str = "" - file: str = "" - line: int = 0 - - def to_dict(self) -> Dict[str, Any]: - return { - "name": self.name, - "fields": [ - { - "name": f.name, - "c_type": f.c_type, - "array_dim": f.array_dim, - "is_pointer": f.is_pointer, - "doc": f.doc, - } - for f in self.fields - ], - "doc": self.doc, - "file": self.file, - "line": self.line, - } - - -@dataclass -class CDefine: - name: str - value: str - doc: str = "" - file: str = "" - line: int = 0 - - -@dataclass -class IntrospectSnapshot: - """Top-level container for the clang-AST scrape output. Serialised - to ``Scripts/introspect_snapshot.json``. snapshot_version bumps on - schema-incompatible changes. - - ``hand_enums`` mirrors ``enums`` but covers URLab's hand-rolled - ``EMj*`` enums under ``Source/URLab/Public``. They live in a - separate field so the MuJoCo mjt* lookup (value_map.mj_const drift - checks) doesn't accidentally collide with a future EMj* name.""" - snapshot_version: int = 1 - header_hash: str = "" - functions: Dict[str, CFunction] = field(default_factory=dict) - enums: Dict[str, CEnum] = field(default_factory=dict) - structs: Dict[str, CStruct] = field(default_factory=dict) - defines: Dict[str, CDefine] = field(default_factory=dict) - hand_enums: Dict[str, CEnum] = field(default_factory=dict) - - def to_dict(self) -> Dict[str, Any]: - return { - "_meta": { - "snapshot_version": self.snapshot_version, - "header_hash": self.header_hash, - }, - "functions": {n: f.to_dict() for n, f in self.functions.items()}, - "enums": {n: e.to_dict() for n, e in self.enums.items()}, - "structs": {n: s.to_dict() for n, s in self.structs.items()}, - "defines": { - n: {"value": d.value, "doc": d.doc, "file": d.file, "line": d.line} - for n, d in self.defines.items() - }, - "hand_enums": {n: e.to_dict() for n, e in self.hand_enums.items()}, - } diff --git a/Scripts/codegen/build_introspect_snapshot.py b/Scripts/codegen/build_introspect_snapshot.py deleted file mode 100644 index b9497567..00000000 --- a/Scripts/codegen/build_introspect_snapshot.py +++ /dev/null @@ -1,486 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -clang.cindex-based scrape of MuJoCo headers (mjspec.h + mjmodel.h) into -``Scripts/codegen/snapshots/introspect_snapshot.json``. The codegen -consumes this snapshot as its single source of truth for the MuJoCo C -API surface. - -Captures: -- function signatures (every MJAPI-tagged declaration in the headers) -- enum decls with values + per-member doc comments -- struct decls with fields + per-field doc comments + array dims -- ``#define`` constants (after preprocessor evaluation when literal) - -Usage: - python Scripts/codegen/build_introspect_snapshot.py - [--mujoco-include /path/to/mujoco/include] - [--output Scripts/codegen/snapshots/introspect_snapshot.json] - [--libclang /path/to/libclang.dll] (only if not auto-found) - -libclang resolution: respects ``$LIBCLANG_LIBRARY_FILE`` env var; falls -back to clang.cindex's default search. Set ``--libclang`` to override. -""" - -from __future__ import annotations - -import argparse -import hashlib -import json -import os -import re -import sys -from typing import Dict, List - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_PLUGIN_ROOT = os.path.normpath(os.path.join(_HERE, "..", "..")) - -# Use the local vendored ast_nodes copy. -sys.path.insert(0, os.path.join(_HERE, "_vendored")) -from ast_nodes import ( # noqa: E402 - CFunction, - CFunctionParam, - CEnum, - CEnumMember, - CStruct, - CStructField, - CDefine, - IntrospectSnapshot, -) - - -def _header_hash(paths: List[str]) -> str: - """SHA-256 of the concatenated header content. Lets callers skip - re-running the snapshot when nothing upstream changed.""" - h = hashlib.sha256() - for p in sorted(paths): - if not os.path.exists(p): - continue - with open(p, "rb") as f: - h.update(f.read()) - return h.hexdigest() - - -def _strip_doc(raw: str) -> str: - """Collapse a libclang raw doc comment into one line, stripping - leading ``//`` / ``/*`` decoration.""" - if not raw: - return "" - out_lines: List[str] = [] - for line in raw.splitlines(): - s = line.strip() - for prefix in ("///", "//!", "//", "/**", "*/", "*", "/*"): - if s.startswith(prefix): - s = s[len(prefix):].strip() - break - if s.endswith("*/"): - s = s[:-2].strip() - if s: - out_lines.append(s) - return " ".join(out_lines) - - -def _walk(tu, mujoco_include: str, snapshot: IntrospectSnapshot) -> None: - """Visit every cursor in the translation unit. ``mujoco_include`` is - the include root; we only capture decls whose file lives under it - (so libc / Windows SDK / TinyXML noise is filtered out).""" - import clang.cindex as cx # noqa: E402, local-only - - plugin_root_norm = os.path.normcase(os.path.abspath(_PLUGIN_ROOT)) - - def _normalised_path(loc) -> str: - """Return the location's file as a forward-slash path relative to - the plugin root, so the snapshot is byte-identical across - developers (no absolute ``c:\\users\\...`` strings).""" - if not loc or not loc.file: - return "" - abs_path = os.path.normcase(os.path.abspath(loc.file.name)) - if abs_path.startswith(plugin_root_norm): - rel = os.path.relpath(abs_path, plugin_root_norm) - return rel.replace("\\", "/") - return abs_path.replace("\\", "/") - - # Filter root mirrors _normalised_path: relative-to-plugin-root with - # forward slashes. - mujoco_abs = os.path.normcase(os.path.abspath(mujoco_include)) - if mujoco_abs.startswith(plugin_root_norm): - norm_root = os.path.relpath(mujoco_abs, plugin_root_norm).replace("\\", "/") - else: - norm_root = mujoco_abs.replace("\\", "/") - - for cursor in tu.cursor.get_children(): - fp = _normalised_path(cursor.location) - if not fp.startswith(norm_root): - continue - - kind = cursor.kind - - if kind == cx.CursorKind.FUNCTION_DECL: - params: List[CFunctionParam] = [] - for arg in cursor.get_arguments(): - t = arg.type.spelling - dim = None - if "[" in t and "]" in t: - # e.g. "double [4]" -> dim=4 - try: - dim_str = t[t.index("[") + 1:t.index("]")] - if dim_str.strip().isdigit(): - dim = int(dim_str) - except ValueError: - pass - params.append(CFunctionParam( - name=arg.spelling, c_type=t, array_dim=dim, - )) - fn = CFunction( - name=cursor.spelling, - return_type=cursor.result_type.spelling, - params=params, - doc=_strip_doc(cursor.raw_comment or ""), - file=fp, - line=cursor.location.line if cursor.location else 0, - ) - snapshot.functions[fn.name] = fn - - elif kind == cx.CursorKind.ENUM_DECL: - # Anonymous enums get spelling = "" — skip; we only want - # named typedef enums. The C tag name is ``mjtJoint_`` but - # callers reference the typedef ``mjtJoint``; normalise here. - name = cursor.spelling or cursor.type.spelling - if not name or name.startswith("(anonymous"): - continue - if name.endswith("_"): - name = name[:-1] - members: List[CEnumMember] = [] - for child in cursor.get_children(): - if child.kind == cx.CursorKind.ENUM_CONSTANT_DECL: - members.append(CEnumMember( - name=child.spelling, - value=child.enum_value, - doc=_strip_doc(child.raw_comment or ""), - )) - underlying = cursor.enum_type.spelling if cursor.enum_type else None - snapshot.enums[name] = CEnum( - name=name, members=members, - underlying_type=underlying, - doc=_strip_doc(cursor.raw_comment or ""), - file=fp, - line=cursor.location.line if cursor.location else 0, - ) - - elif kind == cx.CursorKind.STRUCT_DECL: - name = cursor.spelling or cursor.type.spelling - if not name: - continue - # Strip trailing underscore from C tag names ("mjsBody_" -> "mjsBody"). - if name.endswith("_"): - name = name[:-1] - fields: List[CStructField] = [] - for child in cursor.get_children(): - if child.kind != cx.CursorKind.FIELD_DECL: - continue - t = child.type.spelling - dim = None - is_ptr = "*" in t - if "[" in t and "]" in t: - try: - dim_str = t[t.index("[") + 1:t.index("]")] - if dim_str.strip().isdigit(): - dim = int(dim_str) - except ValueError: - pass - fields.append(CStructField( - name=child.spelling, - c_type=t, - array_dim=dim, - is_pointer=is_ptr, - doc=_strip_doc(child.raw_comment or ""), - )) - snapshot.structs[name] = CStruct( - name=name, fields=fields, - doc=_strip_doc(cursor.raw_comment or ""), - file=fp, - line=cursor.location.line if cursor.location else 0, - ) - - elif kind == cx.CursorKind.MACRO_DEFINITION: - name = cursor.spelling - # Skip function-like macros; libclang doesn't expand them. - # Skip header guards. - if name.endswith("_H_") or name.startswith("__"): - continue - # Extract tokens between the cursor's extent to get the value. - tokens = list(tu.get_tokens(extent=cursor.extent)) - if len(tokens) < 2: - continue - value = " ".join(t.spelling for t in tokens[1:]) - snapshot.defines[name] = CDefine( - name=name, value=value, - doc=_strip_doc(cursor.raw_comment or ""), - file=fp, - line=cursor.location.line if cursor.location else 0, - ) - - -# --------------------------------------------------------------------------- -# URLab hand-enum scrape -# -# UE hand-rolled ``enum class EMj* : uint8 { ... }`` declarations live in -# C++ headers that #include CoreMinimal.h + lots of UE machinery libclang -# can't parse standalone. Extract each enum block via regex (the -# delimiters are stable), strip the UE-macro surface (UMETA/UENUM/UMETA), -# and hand the cleaned single-enum source to libclang for AST-level -# member extraction. That gives us libclang's accuracy on member values -# without the UE-include hell, and is byte-comparable to the regex -# scraper that used to live in generate_ue_components.py. -# --------------------------------------------------------------------------- - -_HAND_ENUM_BLOCK_RE = re.compile( - r"(enum\s+class\s+EMj\w+\s*:\s*uint8\s*\{[^}]*\}\s*;)", - re.DOTALL, -) -_HAND_ENUM_NAME_RE = re.compile(r"enum\s+class\s+(EMj\w+)") - - -_UE_FN_MACRO_RE = re.compile(r"\b(UMETA|UENUM)\s*\(") - - -def _strip_balanced_call(text: str, start: int) -> int: - """Return the index just past the matching ``)`` for a function-call - opener at ``start``. Handles nested parens — naive ``[^)]*`` would - bail on ``UMETA(DisplayName = "Track (Centre of Mass)")``.""" - depth = 1 - i = start + 1 - in_str = None - while i < len(text) and depth > 0: - ch = text[i] - if in_str: - if ch == "\\" and i + 1 < len(text): - i += 2 - continue - if ch == in_str: - in_str = None - elif ch in ('"', "'"): - in_str = ch - elif ch == "(": - depth += 1 - elif ch == ")": - depth -= 1 - i += 1 - return i - - -def _strip_ue_macros_from_enum_block(text: str) -> str: - """Erase ``UMETA(...)`` and ``UENUM(...)`` function-like macros so a - plain libclang pass can compile the enum block. Each macro turns - into the empty string; the surrounding member identifiers + comma - separators stay intact. Handles nested parens inside the macro - argument — ``UMETA(DisplayName = "Track (Centre of Mass)")`` etc.""" - out: List[str] = [] - i = 0 - while i < len(text): - m = _UE_FN_MACRO_RE.search(text, i) - if not m: - out.append(text[i:]) - break - out.append(text[i:m.start()]) - i = _strip_balanced_call(text, m.end() - 1) - stripped = "".join(out) - stripped = re.sub(r"//[^\n]*", "", stripped) - stripped = re.sub(r"/\*.*?\*/", "", stripped, flags=re.DOTALL) - return stripped - - -def _scrape_hand_enums_via_libclang( - urlab_source_root: str, -) -> Dict[str, CEnum]: - """Walk every .h under ``urlab_source_root`` looking for ``enum class - EMj* : uint8`` blocks; hand each cleaned block to libclang for - member + value extraction. - - Returns ``{enum_name: CEnum}``. The plugin_root-relative path is - stamped on each entry so the snapshot stays byte-identical across - developer machines (no absolute ``c:\\users\\...`` strings).""" - import clang.cindex as cx # noqa: E402, local-only - - plugin_root_norm = os.path.normcase(os.path.abspath(_PLUGIN_ROOT)) - out: Dict[str, CEnum] = {} - idx = cx.Index.create() - - for root, _dirs, files in os.walk(urlab_source_root): - for name in files: - if not name.endswith(".h"): - continue - path = os.path.join(root, name) - try: - with open(path, "r", encoding="utf-8") as f: - text = f.read() - except OSError: - continue - rel = os.path.relpath( - os.path.normcase(os.path.abspath(path)), - plugin_root_norm, - ).replace("\\", "/") - for block_match in _HAND_ENUM_BLOCK_RE.finditer(text): - block = block_match.group(1) - name_m = _HAND_ENUM_NAME_RE.search(block) - if not name_m: - continue - enum_name = name_m.group(1) - cleaned = _strip_ue_macros_from_enum_block(block) - # Compute 1-based source line of the enum's opening - # ``enum`` keyword (the block_match is anchored on the - # whole declaration, so its start offset is also the - # enum's start). - start_line = text[:block_match.start()].count("\n") + 1 - # UE typedefs uint8 to unsigned char; provide our own - # typedef so libclang doesn't need any system headers - # (which on Windows pull in MSVC STL that triggers a - # "Unexpected compiler version" static_assert). - tu = idx.parse( - path=".cpp", - args=["-x", "c++", "-std=c++17", "-nostdinc", - "-nostdinc++", "-ferror-limit=0"], - unsaved_files=[( - ".cpp", - "typedef unsigned char uint8;\n" + cleaned + "\n", - )], - options=cx.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES, - ) - # Don't bail on diagnostics — even with errors libclang - # produces usable ENUM_DECL cursors. The downstream - # match-by-name + member iteration covers correctness. - for cursor in tu.cursor.walk_preorder(): - if cursor.kind != cx.CursorKind.ENUM_DECL: - continue - if cursor.spelling != enum_name: - continue - members: List[CEnumMember] = [] - for child in cursor.get_children(): - if child.kind == cx.CursorKind.ENUM_CONSTANT_DECL: - members.append(CEnumMember( - name=child.spelling, - value=child.enum_value, - doc="", - )) - if not members: - continue - out[enum_name] = CEnum( - name=enum_name, - members=members, - underlying_type="uint8", - doc="", - file=rel, - line=start_line, - ) - break - return out - - -def main(argv: List[str] | None = None) -> int: - ap = argparse.ArgumentParser(description=__doc__) - ap.add_argument("--mujoco-include", default=os.path.join( - _PLUGIN_ROOT, "third_party", "install", "MuJoCo", "include", - )) - ap.add_argument("--output", default=os.path.join( - _PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "introspect_snapshot.json", - )) - ap.add_argument("--libclang", default=None, - help="path to libclang.dll/.so/.dylib; overrides auto-detect") - ap.add_argument("--urlab-source-root", default=os.path.join( - _PLUGIN_ROOT, "Source", "URLab", "Public", - ), help="root for the EMj* hand-enum libclang scrape; skipped if empty") - args = ap.parse_args(argv) - - try: - import clang.cindex as cx - except ImportError: - print("ERROR: clang.cindex not installed. Run " - "`uv run --with libclang python build_introspect_snapshot.py` " - "or `pip install libclang`.", file=sys.stderr) - return 1 - - if args.libclang: - cx.Config.set_library_file(args.libclang) - elif os.environ.get("LIBCLANG_LIBRARY_FILE"): - cx.Config.set_library_file(os.environ["LIBCLANG_LIBRARY_FILE"]) - - mujoco_include = args.mujoco_include - if not os.path.exists(mujoco_include): - print(f"ERROR: --mujoco-include not found: {mujoco_include}", file=sys.stderr) - return 2 - - mjspec_h = os.path.join(mujoco_include, "mujoco", "mjspec.h") - mjmodel_h = os.path.join(mujoco_include, "mujoco", "mjmodel.h") - mjxmacro_h = os.path.join(mujoco_include, "mujoco", "mjxmacro.h") - - # mjspecmacro.h ships in the installed MuJoCo headers (post-3.9.0); hashed - # alongside the others so the snapshot invalidates when it changes. - mjspecmacro_h = os.path.join(mujoco_include, "mujoco", "mjspecmacro.h") - - # Synthesize a small dispatch source that #includes the headers we - # care about. Letting clang chew on a real .c lets it resolve types - # without us threading -include flags. - dispatch_c = "\n".join([ - "#include ", - "#include ", - "#include ", - "#include ", - ]) - args_clang = [ - "-x", "c", - "-std=c11", - f"-I{mujoco_include}", - "-DMJ_STATIC", # avoid MJAPI dllimport/export linkage attrs - ] - - try: - idx = cx.Index.create() - except cx.LibclangError as exc: - print(f"ERROR: libclang failed to load: {exc}", file=sys.stderr) - return 3 - - tu = idx.parse( - path=".c", - args=args_clang, - unsaved_files=[(".c", dispatch_c)], - options=(cx.TranslationUnit.PARSE_DETAILED_PROCESSING_RECORD - | cx.TranslationUnit.PARSE_SKIP_FUNCTION_BODIES), - ) - fatal_diags = [d for d in tu.diagnostics if d.severity >= cx.Diagnostic.Error] - if fatal_diags: - print(f"libclang errors:", file=sys.stderr) - for d in fatal_diags[:5]: - print(f" {d.spelling} ({d.location})", file=sys.stderr) - return 4 - - snapshot = IntrospectSnapshot( - snapshot_version=1, - header_hash=_header_hash([mjspec_h, mjmodel_h, mjxmacro_h, - mjspecmacro_h]), - ) - _walk(tu, mujoco_include, snapshot) - - if args.urlab_source_root and os.path.isdir(args.urlab_source_root): - try: - snapshot.hand_enums = _scrape_hand_enums_via_libclang( - args.urlab_source_root, - ) - except cx.LibclangError as exc: - print(f"warning: hand-enum libclang scrape skipped: {exc}", - file=sys.stderr) - - out_dict = snapshot.to_dict() - os.makedirs(os.path.dirname(args.output), exist_ok=True) - with open(args.output, "w", encoding="utf-8") as f: - json.dump(out_dict, f, indent=2) - print(f"wrote {args.output}: " - f"{len(snapshot.functions)} functions, " - f"{len(snapshot.enums)} enums, " - f"{len(snapshot.structs)} structs, " - f"{len(snapshot.defines)} defines, " - f"{len(snapshot.hand_enums)} hand_enums, " - f"header_hash={snapshot.header_hash[:12]}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/Scripts/codegen/build_mjcf_schema_snapshot.py b/Scripts/codegen/build_mjcf_schema_snapshot.py deleted file mode 100644 index 634d4484..00000000 --- a/Scripts/codegen/build_mjcf_schema_snapshot.py +++ /dev/null @@ -1,594 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Parse the MJCF schema declaration in MuJoCo's ``xml_native_reader.cc`` -and emit ``Scripts/mjcf_schema_snapshot.json`` in URLab's curated shape. - -The schema source lives in a single C++ initializer-list array -``std::vector MJCF[nMJCF]`` and looks like: - - {"body", "*", "name", "childclass", "pos", ...}, - {"<"}, - {"inertial", "?", "pos", "mass", ...}, - {"joint", "*", "name", "type", ...}, - ... - {">"}, - -This script tokenises that file into (element, occurrence, attrs) tuples -with parent/child relationships derived from the ``"<"`` / ``">"`` markers, -then projects the tree onto URLab's hand-curated snapshot shape (the same -shape codegen_rules.json consumes). - -Usage: - python Scripts/codegen/build_mjcf_schema_snapshot.py - [--src PATH] (default: third_party/MuJoCo/src/src/xml/xml_native_reader.cc) - [--output PATH] (default: Scripts/mjcf_schema_snapshot.json) -""" -from __future__ import annotations - -import argparse -import datetime as _dt -import json -import os -import re -import sys -from dataclasses import dataclass, field -from typing import Dict, List, Optional - - -# ---------------------------------------------------------------------------- -# Parsing -# ---------------------------------------------------------------------------- - -# Matches one C++ initializer block on a line, capturing the comma-separated -# string list inside the outer braces. We deliberately tolerate any whitespace, -# trailing commas, and continuation across lines. -_BLOCK_RE = re.compile(r"\{((?:[^{}]|\{[^}]*\})*?)\}\s*,?", re.DOTALL) -_STRING_RE = re.compile(r'"([^"\\]*)"') - - -@dataclass -class SchemaNode: - name: str - occurrence: str = "*" - attrs: List[str] = field(default_factory=list) - children: List["SchemaNode"] = field(default_factory=list) - - -def _extract_mjcf_block(src_text: str) -> str: - """Pull out the body of ``std::vector MJCF[nMJCF] = { ... };``.""" - start_marker = "MJCF[nMJCF]" - i = src_text.find(start_marker) - if i < 0: - raise SystemExit(f"could not find '{start_marker}' in source") - # find the '=' then the opening '{' - eq = src_text.find("=", i) - open_brace = src_text.find("{", eq) - # walk braces to find the matching close - depth = 0 - j = open_brace - n = len(src_text) - while j < n: - c = src_text[j] - if c == "{": - depth += 1 - elif c == "}": - depth -= 1 - if depth == 0: - # strip the outermost braces — return body - return src_text[open_brace + 1:j] - j += 1 - raise SystemExit("unbalanced braces in MJCF declaration") - - -def _tokenise_blocks(body: str) -> List[List[str]]: - """Return a flat list of ``[strings...]`` for each ``{...}`` block in the - MJCF body, in source order. Comments are stripped (``//`` line comments - and ``/* ... */`` block comments).""" - # Strip /* ... */ - body = re.sub(r"/\*.*?\*/", "", body, flags=re.DOTALL) - # Strip // ... to end-of-line - body = re.sub(r"//[^\n]*", "", body) - - blocks: List[List[str]] = [] - for m in _BLOCK_RE.finditer(body): - inner = m.group(1) - strings = _STRING_RE.findall(inner) - if strings: - blocks.append(strings) - return blocks - - -def _parse_tree(blocks: List[List[str]]) -> SchemaNode: - """Build a tree from the flat block list using ``"<"`` / ``">"`` markers.""" - root = SchemaNode(name="__root__") - # Each entry on the stack is (parent_node, last_added_child_under_it). - # When we see ``"<"`` we recurse INTO the last child of the current top. - # When we see ``">"`` we pop back out. - stack: List[SchemaNode] = [root] - for block in blocks: - head = block[0] - if head == "<": - # Open scope on the last-added child of the current top. - top = stack[-1] - if not top.children: - # Defensive: malformed input. Fall back to treating the - # current top itself as the scope. - stack.append(top) - else: - stack.append(top.children[-1]) - continue - if head == ">": - stack.pop() - continue - # Element declaration: ("name", "occurrence", "attr1", "attr2", ...) - occ = block[1] if len(block) > 1 else "*" - attrs = block[2:] - node = SchemaNode(name=head, occurrence=occ, attrs=list(attrs)) - stack[-1].children.append(node) - return root - - -def _find_child(parent: SchemaNode, name: str) -> Optional[SchemaNode]: - for c in parent.children: - if c.name == name: - return c - return None - - -# ---------------------------------------------------------------------------- -# Bucketing into URLab's curated snapshot shape -# ---------------------------------------------------------------------------- - -# Top-level worldbody children whose attrs we expose directly. The schema also -# has these under ; we use the worldbody copy because it carries -# `name`/`class` which the default copy doesn't. -_WORLDBODY_DIRECT = [ - "body", "inertial", "joint", "freejoint", "geom", "site", - "camera", "light", -] - - -# URLab convention for splitting per-subtype attr lists into "common" (lives -# on the base UMjActuator / UMjSensor) vs "extras" (lives on the subtype). -# These are NOT strict intersections — they're the URLab-side curation of -# "what makes sense as a shared base attr". Without baking them in, new -# MuJoCo subtypes that don't carry every common attr would shrink the -# intersection and force the codegen to re-emit common attrs on every -# subtype. -# -# When MuJoCo adds a brand-new attr that should be considered "common" (e.g. -# a new lifecycle hook on every actuator), extend the list below. The -# extractor's "added attrs to known elements" warning will surface the -# candidate. -_ACTUATOR_COMMON_ATTRS = [ - "name", "class", "group", "nsample", "interp", "delay", - "ctrllimited", "forcelimited", "actlimited", - "ctrlrange", "forcerange", "actrange", "lengthrange", - "gear", "damping", "armature", "cranklength", "user", -] -_ACTUATOR_TARGETS = [ - "joint", "jointinparent", "tendon", - "slidersite", "cranksite", "site", "refsite", -] -_SENSOR_COMMON_ATTRS = [ - "name", "nsample", "interp", "delay", "interval", - "cutoff", "noise", "user", -] - - -# ---------------------------------------------------------------------------- -# Per-sensor objtype/reftype extraction -# ---------------------------------------------------------------------------- -# -# The static MJCF[nMJCF] schema only carries attr lists — not the per-sensor -# mapping between the XML element name and the mjsSensor objtype/reftype the -# compiler expects. That logic lives in xml_native_reader.cc's `Sensor()` -# method (a giant if/else cascade, one branch per sensor type). The codegen -# scrapes those branches here so rules don't have to hand-list them. - -# Inside Sensor(), each "regular" branch follows: -# if (type == "") { -# sensor->type = mjSENS_X; -# sensor->objtype = mjOBJ_Y; // OPTIONAL: literal default -# ReadAttrTxt(elem, "", objname, true); // OPTIONAL: name source -# sensor->reftype = mjOBJ_Z; // OPTIONAL -# ReadAttrTxt(elem, "", refname, true); // OPTIONAL: ref name source -# } -# -# "Computed" branches (rangefinder, distance/normal/fromto, contact, user, -# plugin) derive objtype/reftype from attribute presence at parse time; -# those get marked computed=True so codegen rules keep their explicit -# overrides for them. -# Matches the open of a sensor branch, capturing the parenthesised condition -# string. We post-process the condition to pull out every ``type == "NAME"`` -# clause so ``else if (type == "distance" || type == "normal" || type == -# "fromto") { ... }`` produces three entries that share the branch body. -_SENSOR_BRANCH_RE = re.compile( - r'(?:if|else if)\s*\((?P[^{]*?type\s*==\s*"[^"]+"[^{]*)\)\s*\{', - re.DOTALL, -) -_SENSOR_NAMES_IN_COND_RE = re.compile(r'type\s*==\s*"([^"]+)"') -_TYPE_LITERAL_RE = re.compile(r'sensor->type\s*=\s*(mjSENS_\w+)\s*;') -_OBJTYPE_LITERAL_RE = re.compile(r'sensor->objtype\s*=\s*(mjOBJ_\w+)\s*;') -_REFTYPE_LITERAL_RE = re.compile(r'sensor->reftype\s*=\s*(mjOBJ_\w+)\s*;') -_OBJTYPE_FROM_XML_RE = re.compile( - r'ReadAttrTxt\s*\(\s*elem\s*,\s*"objtype"\s*,\s*text' -) -_NAME_READ_RE = re.compile( - r'ReadAttrTxt\s*\(\s*elem\s*,\s*"([^"]+)"\s*,\s*(objname|refname)\b' -) - - -def _slice_balanced_braces(src: str, open_idx: int) -> str: - """Given an index pointing AT '{', return the substring up to the - matching close (exclusive of both braces). Tracks string literals and - line/block comments so braces inside ``"..."``, ``//`` or ``/* */`` are - ignored. Returns "" if no matching close found.""" - assert src[open_idx] == "{" - n = len(src) - depth = 0 - i = open_idx - in_str = False - str_q = "" - in_line_comment = False - in_block_comment = False - while i < n: - c = src[i] - c2 = src[i:i+2] - if in_line_comment: - if c == "\n": - in_line_comment = False - i += 1 - continue - if in_block_comment: - if c2 == "*/": - in_block_comment = False - i += 2 - continue - i += 1 - continue - if in_str: - if c == "\\": - i += 2 - continue - if c == str_q: - in_str = False - i += 1 - continue - if c2 == "//": - in_line_comment = True - i += 2 - continue - if c2 == "/*": - in_block_comment = True - i += 2 - continue - if c in ('"', "'"): - in_str = True - str_q = c - i += 1 - continue - if c == "{": - depth += 1 - elif c == "}": - depth -= 1 - if depth == 0: - return src[open_idx+1:i] - i += 1 - return "" - - -# Branches where objtype/reftype are derived from attribute presence at -# parse time (multiple ReadAttrTxt + sensor->objtype = X ? mjOBJ_A : mjOBJ_B -# pattern). Codegen rules keep explicit overrides for these; the extractor -# marks them computed=True so the generated codegen doesn't pretend to know. -_COMPUTED_OBJTYPE_SENSORS = { - "rangefinder", "distance", "normal", "fromto", "contact", - "user", "plugin", -} - - -def _extract_sensor_per_type(src_text: str) -> Dict[str, Dict[str, object]]: - """Scrape ``Sensor()``'s per-type if/else cascade. Returns a dict - keyed by XML element name; each entry has: - - objtype: "mjOBJ_X" | "from_xml" | "computed" | None - reftype: "mjOBJ_Y" | "computed" | None - obj_attr: "site"/"joint"/.../None (XML attr name carrying objname) - ref_attr: "camera"/"site"/.../None (XML attr name carrying refname) - computed: True iff objtype/reftype are derived from attr presence - """ - # Slice out the Sensor() method body so we don't pick up branches from - # other methods that happen to share regex shapes. - sig_re = re.compile(r"void\s+mjXReader::Sensor\s*\(") - m = sig_re.search(src_text) - if not m: - return {} - open_idx = src_text.find("{", m.end()) - if open_idx < 0: - return {} - body = _slice_balanced_braces(src_text, open_idx) - if not body: - return {} - - result: Dict[str, Dict[str, object]] = {} - # Some outer branches (distance/normal/fromto, contact, plugin) contain - # an inner ``if (type == "X")`` that toggles ->type. Those inner matches - # would otherwise overwrite the outer-branch entry with a stripped-down - # body. Track the [start, end) of each outer-branch body and skip - # matches whose '{' falls inside. - consumed_ranges: List[tuple] = [] - for bm in _SENSOR_BRANCH_RE.finditer(body): - names = _SENSOR_NAMES_IN_COND_RE.findall(bm.group("cond")) - if not names: - continue - brace_idx = bm.end() - 1 - if brace_idx < 0 or body[brace_idx] != "{": - continue - if any(s < brace_idx < e for s, e in consumed_ranges): - continue - branch = _slice_balanced_braces(body, brace_idx) - consumed_ranges.append((brace_idx, brace_idx + 1 + len(branch))) - - obj_literal = _OBJTYPE_LITERAL_RE.search(branch) - ref_literal = _REFTYPE_LITERAL_RE.search(branch) - obj_from_xml = bool(_OBJTYPE_FROM_XML_RE.search(branch)) and not obj_literal - - obj_attr: Optional[str] = None - ref_attr: Optional[str] = None - for nm in _NAME_READ_RE.finditer(branch): - attr, target = nm.group(1), nm.group(2) - if attr == "objtype": - continue # the literal "objtype" attr selector, not a name source - if target == "objname" and obj_attr is None: - obj_attr = attr - elif target == "refname" and ref_attr is None: - ref_attr = attr - - # Capture the mjSENS_X assigned in this branch. Multi-name branches - # (distance/normal/fromto) carry an inner positional switch with - # one ->type per name. Single-name branches (touch, framepos, ...) - # have exactly one mjSENS_X. - mj_types = [m.group(1) for m in _TYPE_LITERAL_RE.finditer(branch)] - if len(mj_types) == len(names): - name_to_mj_type = dict(zip(names, mj_types)) - elif mj_types: - # Single-type branch — every name shares it. - name_to_mj_type = {n: mj_types[0] for n in names} - else: - name_to_mj_type = {n: None for n in names} - - # Multi-name branches (distance/normal/fromto share one block, - # routed inside by `if (type == ...)` to set ->type). All share - # the same objtype/reftype derivation, so emit one entry per name. - for name in names: - computed = name in _COMPUTED_OBJTYPE_SENSORS - result[name] = { - "mj_type": name_to_mj_type.get(name), - "objtype": ("computed" if computed - else ("from_xml" if obj_from_xml - else (obj_literal.group(1) if obj_literal else None))), - "reftype": ("computed" if computed - else (ref_literal.group(1) if ref_literal else None)), - "obj_attr": obj_attr, - "ref_attr": ref_attr, - "computed": computed, - } - return result - - -def _build_snapshot(root: SchemaNode, mujoco_version: str, *, - src_text: str = "") -> Dict: - """Walk the parsed MJCF tree and project it onto URLab's curated shape.""" - out: Dict = { - "_meta": { - "mujoco_version": mujoco_version, - "snapshot_date": _dt.date.today().isoformat(), - "source": "https://github.com/google-deepmind/mujoco/blob/main/src/xml/xml_native_reader.cc", - "description": ( - "MJCF schema snapshot, auto-extracted by " - "Scripts/codegen/build_mjcf_schema_snapshot.py from the " - "MuJoCo submodule's xml_native_reader.cc." - ), - } - } - - mujoco = _find_child(root, "mujoco") - if mujoco is None: - raise SystemExit("schema source: no root element") - - # There is no explicit ```` declaration in the MJCF[] schema — - # ```` sits directly under ```` with occurrence "R" - # (recursive). That body's child list is what the runtime parser uses - # for both worldbody contents and nested-body contents. - body = _find_child(mujoco, "body") - - # Top-level worldbody-like elements (attrs + children). The body itself - # is the worldbody schema; geom/joint/inertial/site/camera/light/etc. - # live underneath it. - for name in _WORLDBODY_DIRECT: - node = body if name == "body" else (_find_child(body, name) if body else None) - if node is None: - continue - entry: Dict = {"attrs": list(node.attrs)} - if node.children: - entry["children"] = [c.name for c in node.children] - out[name] = entry - - # Actuator section. Each subtype's full attr list lives directly under - # . URLab's curated shape splits this into: - # actuator_common.attrs (URLab convention, _ACTUATOR_COMMON_ATTRS above) - # actuator_common.targets (transmission target attrs, _ACTUATOR_TARGETS) - # actuator_types[name] (per-subtype EXTRAS only) - actuator = _find_child(mujoco, "actuator") - if actuator is not None: - type_attrs = {c.name: list(c.attrs) for c in actuator.children - if c.name != "plugin"} - common_set = set(_ACTUATOR_COMMON_ATTRS) - targets_set = set(_ACTUATOR_TARGETS) - out["actuator_common"] = { - "attrs": list(_ACTUATOR_COMMON_ATTRS), - "targets": list(_ACTUATOR_TARGETS), - } - actuator_types: Dict[str, List[str]] = {} - for name, attrs in type_attrs.items(): - extras = [a for a in attrs - if a not in common_set and a not in targets_set] - actuator_types[name] = extras - out["actuator_types"] = actuator_types - - # Sensor section: per-type attr declarations. URLab tracks: - # sensor_types = list of names - # sensor_common = shared attrs (_SENSOR_COMMON_ATTRS, URLab convention) - # sensor_per_type = per-sensor objtype/reftype/attr defaults scraped - # from xml_native_reader.cc's Sensor() method body - sensor = _find_child(mujoco, "sensor") - if sensor is not None: - sensor_attrs = {c.name: list(c.attrs) for c in sensor.children} - out["sensor_types"] = list(sensor_attrs.keys()) - out["sensor_common"] = {"attrs": list(_SENSOR_COMMON_ATTRS)} - if src_text: - per_type = _extract_sensor_per_type(src_text) - # Only emit per-type rows for sensors that exist in the static - # MJCF schema — keeps the snapshot in sync if MuJoCo adds a - # parser branch but not a schema entry (or vice versa). - out["sensor_per_type"] = { - name: per_type[name] for name in sensor_attrs - if name in per_type - } - - # Tendon section: spatial vs fixed, plus wrap types. - tendon = _find_child(mujoco, "tendon") - if tendon is not None: - tdict: Dict[str, Dict] = {} - for sub in tendon.children: - entry = {"attrs": list(sub.attrs)} - if sub.children: - entry["wrap_types"] = [c.name for c in sub.children] - tdict[sub.name] = entry - out["tendon"] = tdict - - # Equality section. - equality = _find_child(mujoco, "equality") - if equality is not None: - out["equality"] = {c.name: list(c.attrs) for c in equality.children} - - # Contact section (pair + exclude). - contact = _find_child(mujoco, "contact") - if contact is not None: - out["contact"] = {c.name: list(c.attrs) for c in contact.children} - - # Asset section (mesh / hfield / texture / material / etc.). - asset = _find_child(mujoco, "asset") - if asset is not None: - out["asset"] = {c.name: list(c.attrs) for c in asset.children} - - # Compiler section. - compiler = _find_child(mujoco, "compiler") - if compiler is not None: - out["compiler"] = {"attrs": list(compiler.attrs)} - - # Option section (top-level attrs + sub-element). - option = _find_child(mujoco, "option") - if option is not None: - opt_entry: Dict[str, List[str]] = {"attrs": list(option.attrs)} - flag = _find_child(option, "flag") - if flag is not None: - opt_entry["flag"] = list(flag.attrs) - out["option"] = opt_entry - - # Keyframe section: -> . - keyframe = _find_child(mujoco, "keyframe") - if keyframe is not None: - key = _find_child(keyframe, "key") - if key is not None: - out["keyframe"] = {"key": list(key.attrs)} - - # Default section: just the child element names the codegen needs to - # know about so it can wire UMjDefault sub-component creation. - default = _find_child(mujoco, "default") - if default is not None: - # Filter out nested defaults — only direct element children. - kids = [c.name for c in default.children - if c.name not in ("default", "config")] - out["default"] = {"child_elements": kids} - - # Deformable/flexcomp/flex — capture top-level attrs for downstream rules. - deformable = _find_child(mujoco, "deformable") - if deformable is not None: - for c in deformable.children: - out[c.name] = {"attrs": list(c.attrs)} - if c.children: - out[c.name]["children"] = [cc.name for cc in c.children] - # flexcomp lives under body, not . - flexcomp = _find_child(body, "flexcomp") if body else None - if flexcomp is not None: - out["flexcomp"] = {"attrs": list(flexcomp.attrs)} - if flexcomp.children: - out["flexcomp"]["children"] = [c.name for c in flexcomp.children] - - return out - - -# ---------------------------------------------------------------------------- -# Main -# ---------------------------------------------------------------------------- - -def _detect_mujoco_version(src_path: str) -> str: - """Read the version from include/mujoco/mujoco.h next to xml_native_reader.cc.""" - # src_path is .../mujoco/src/xml/xml_native_reader.cc - repo_root = os.path.normpath(os.path.join(os.path.dirname(src_path), "..", "..")) - header = os.path.join(repo_root, "include", "mujoco", "mujoco.h") - if not os.path.exists(header): - return "unknown" - with open(header, "r", encoding="utf-8") as f: - head = f.read(4096) - # MuJoCo encodes the version as MMmmpp (e.g. 3008001 = 3.8.1) — 7 digits - # for 3.10+, 6 digits before. Parse the last 3 digit pairs as patch/minor/major. - m = re.search(r"#define\s+mjVERSION_HEADER\s+(\d+)", head) - if m: - v = int(m.group(1)) - patch = v % 1000 - minor = (v // 1000) % 1000 - major = v // 1000000 - return f"{major}.{minor}.{patch}" - return "unknown" - - -def main(argv: Optional[List[str]] = None) -> int: - here = os.path.dirname(os.path.abspath(__file__)) - plugin_root = os.path.normpath(os.path.join(here, "..", "..")) - default_src = os.path.join( - plugin_root, "third_party", "MuJoCo", "src", "src", "xml", - "xml_native_reader.cc", - ) - default_output = os.path.join(plugin_root, "Scripts", "codegen", "snapshots", "mjcf_schema_snapshot.json") - - ap = argparse.ArgumentParser() - ap.add_argument("--src", default=default_src, - help=f"MuJoCo xml_native_reader.cc (default: {default_src})") - ap.add_argument("--output", default=default_output, - help=f"Output JSON path (default: {default_output})") - args = ap.parse_args(argv) - - if not os.path.exists(args.src): - print(f"error: schema source not found: {args.src}", file=sys.stderr) - return 2 - - with open(args.src, "r", encoding="utf-8") as f: - src_text = f.read() - body = _extract_mjcf_block(src_text) - blocks = _tokenise_blocks(body) - root = _parse_tree(blocks) - version = _detect_mujoco_version(args.src) - snapshot = _build_snapshot(root, mujoco_version=version, src_text=src_text) - - os.makedirs(os.path.dirname(args.output), exist_ok=True) - with open(args.output, "w", encoding="utf-8") as f: - json.dump(snapshot, f, indent=2) - f.write("\n") - print(f"wrote {args.output}: mujoco_version={version}, top-level keys={len(snapshot) - 1}") - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/Scripts/codegen/build_mjxmacro_snapshot.py b/Scripts/codegen/build_mjxmacro_snapshot.py deleted file mode 100644 index f226da2a..00000000 --- a/Scripts/codegen/build_mjxmacro_snapshot.py +++ /dev/null @@ -1,382 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -# -# 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 -""" -Parse MuJoCo's mjxmacro.h into a machine-readable JSON snapshot. - -Output: Scripts/mjxmacro_snapshot.json with mjModel/mjData pointer -fields grouped by category, plus mjOption / mjStatistic / mjVisual -field lists for top-level struct codegen. - -Each entry carries the C type, field name, outer dimension (e.g. -``njnt``, ``nv``), and inner stride (e.g. ``1``, ``3``, ``mjNREF``, -``MJ_M(nuser_jnt)`` for runtime-dimensioned arrays). -""" - -from __future__ import annotations - -import json -import os -import re -import sys -from typing import Any, Dict, List, Optional - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -PLUGIN_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "..")) -DEFAULT_MJXMACRO = os.path.join( - PLUGIN_ROOT, "third_party", "install", "MuJoCo", "include", "mujoco", "mjxmacro.h" -) -DEFAULT_OUTPUT = os.path.join(PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "mjxmacro_snapshot.json") - -# Block names we care about. Categorized so the consumer knows the -# kind of data (per-element pointer table vs flat struct fields). -POINTER_BLOCKS_MJMODEL = [ - "MJMODEL_POINTERS_BODY", - "MJMODEL_POINTERS_JOINT", - "MJMODEL_POINTERS_DOF", - "MJMODEL_POINTERS_TREE", - "MJMODEL_POINTERS_GEOM", - "MJMODEL_POINTERS_SITE", - "MJMODEL_POINTERS_CAMERA", - "MJMODEL_POINTERS_LIGHT", - "MJMODEL_POINTERS_FLEX", - "MJMODEL_POINTERS_FLEXVERT", - "MJMODEL_POINTERS_FLEXEDGE", - "MJMODEL_POINTERS_FLEXELEM", - "MJMODEL_POINTERS_FLEXTEXCOORD", - "MJMODEL_POINTERS_MESH", - "MJMODEL_POINTERS_SKIN", - "MJMODEL_POINTERS_HFIELD", - "MJMODEL_POINTERS_TEXTURE", - "MJMODEL_POINTERS_MATERIAL", - "MJMODEL_POINTERS_PAIR", - "MJMODEL_POINTERS_EXCLUDE", - "MJMODEL_POINTERS_EQUALITY", - "MJMODEL_POINTERS_TENDON", - "MJMODEL_POINTERS_ACTUATOR", - "MJMODEL_POINTERS_SENSOR", - "MJMODEL_POINTERS_NUMERIC", - "MJMODEL_POINTERS_TEXT", - "MJMODEL_POINTERS_TUPLE", - "MJMODEL_POINTERS_KEY", - "MJMODEL_POINTERS_PLUGIN", - "MJMODEL_POINTERS_NAMES", - "MJMODEL_POINTERS_PATHS", -] - -POINTER_BLOCKS_MJDATA = [ - "MJDATA_POINTERS_PREAMBLE", - "MJDATA_POINTERS", - "MJDATA_ARENA_POINTERS", - "MJDATA_ARENA_POINTERS_PRIMAL", - "MJDATA_ARENA_POINTERS_DUAL", - "MJDATA_ARENA_POINTERS_ISLAND", - "MJDATA_ARENA_POINTERS_SOLVER", - "MJDATA_ARENA_POINTERS_CONTACT", -] - -# Flat struct field blocks (no outer dimension — just type + name + size). -# Lives across both mjxmacro.h (the mjmodel-side struct fields) and -# mjspecmacro.h (the mjspec-side struct fields). -STRUCT_FIELD_BLOCKS = [ - "MJOPTION_FIELDS", - "MJSTATISTIC_FIELDS", - "MJVISUAL_GLOBAL_FIELDS", - "MJVISUAL_QUALITY_FIELDS", - "MJVISUAL_HEADLIGHT_FIELDS", - "MJVISUAL_MAP_FIELDS", - "MJVISUAL_SCALE_FIELDS", - "MJVISUAL_RGBA_FIELDS", - "MJSCOMPILER_FIELDS", - "MJSPEC_FIELDS", -] - -# mjspecmacro.h ships in the installed MuJoCo headers (added upstream after -# 3.9.0; present from our main-pinned build onward), so we read it from the -# install like mjxmacro.h — no longer vendored. -DEFAULT_MJSPECMACRO = os.path.join( - PLUGIN_ROOT, "third_party", "install", "MuJoCo", "include", "mujoco", "mjspecmacro.h" -) - -# Regexes -_BLOCK_START_RE = re.compile(r"^\s*#define\s+(\w+)\s*(?:\(\s*\w+\s*\))?\s*\\?\s*$") -# Single source of truth for the X-macro tag set. Accepts X / XVEC / XNV. -# XNV ("X No Vec") is structurally identical to X (same 4-tuple shape); -# MuJoCo uses the tag so its own codegen knows to skip the vectorised -# emission path. URLab doesn't care about that distinction — we just want -# the entry shape. Add new tags here whenever MuJoCo introduces one; -# duplicating the list in a sibling regex is the bug class that bit XNV. -_X_TAG_RE = r"X(?:VEC|NV)?" -# X-macro entry. Tolerates 4 or 3-tuple forms (pointer vs flat field). -# Captures parenthesised contents; field split below trims/normalizes. -_X_ENTRY_RE = re.compile(rf"^\s*{_X_TAG_RE}\s*\(\s*(.*?)\s*\)\s*\\?\s*$") - - -def _strip_comments(text: str) -> str: - # Drop //... comments + /* ... */ blocks. - text = re.sub(r"//[^\n]*", "", text) - text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL) - return text - - -_BODY_ENTRY_RE = re.compile(rf"^\s*{_X_TAG_RE}\s*\(") - - -def _parse_block_body(lines: List[str], start_idx: int) -> List[str]: - """Collect the entries of a block starting at ``start_idx``. Returns - the entry lines as raw strings (each looks like ``X ( ... )`` or - ``XVEC ( ... )``). Stops at the first non-entry, non-empty line.""" - out: List[str] = [] - i = start_idx - while i < len(lines): - line = lines[i] - stripped = line.strip() - if not stripped: - break - if _BODY_ENTRY_RE.match(stripped): - out.append(stripped.rstrip("\\").rstrip()) - i += 1 - continue - break - return out - - -def _split_csv(tup: str) -> List[str]: - """Split a comma-separated tuple body that may contain MJ_M(...) calls.""" - parts: List[str] = [] - depth = 0 - cur: List[str] = [] - for ch in tup: - if ch == "," and depth == 0: - parts.append("".join(cur).strip()) - cur = [] - continue - if ch == "(": - depth += 1 - elif ch == ")": - depth -= 1 - cur.append(ch) - if cur: - parts.append("".join(cur).strip()) - return parts - - -def _parse_pointer_entry(raw: str) -> Optional[Dict[str, Any]]: - """Parse `X(type, name, outer_dim, stride)` or `XVEC(...)`.""" - m = _X_ENTRY_RE.match(raw) - if not m: - return None - fields = _split_csv(m.group(1)) - if len(fields) == 4: - ctype, name, outer_dim, stride = fields - return { - "type": ctype, - "name": name, - "outer_dim": outer_dim, - "stride": stride, - } - return None - - -def _parse_struct_field_entry(raw: str) -> Optional[Dict[str, Any]]: - """Parse `X(type, name, count)` or `X(name, count)` or `X(name)` or `XVEC(...)`. - - mjxmacro flat-struct blocks have a few shapes: - - `X(type, name, count)` (mjOption) - - `X(name, count)` (mjStatistic — no type, defaults to mjtNum) - - `X(type, name)` (mjVisual fields — no explicit count, treated as scalar) - - `X(name)` (mjVisualQuality/Map/Scale/Rgba — bare field name; type is - whatever the corresponding ``struct mjVisualX`` member is. URLab - defaults to mjtNum since these structs are float/int in practice and - consumers can override via ``field_types``.) - - `XVEC(type, name, count)` (vec3 / vec5 / etc.) - """ - m = _X_ENTRY_RE.match(raw) - if not m: - return None - fields = _split_csv(m.group(1)) - is_vec = raw.lstrip().startswith("XVEC") - if len(fields) == 3: - ctype, name, count = fields - elif len(fields) == 2: - # Could be (type, name) or (name, count). If first token looks - # like a C type keep it as type; else treat as (name, count) and - # default type to mjtNum. - if fields[0] in ("int", "float", "mjtNum", "mjtByte", "char"): - ctype, name, count = fields[0], fields[1], "1" - else: - ctype, name, count = "mjtNum", fields[0], fields[1] - elif len(fields) == 1: - # Bare field name — type defaults to mjtNum, scalar. - ctype, name, count = "mjtNum", fields[0], "1" - else: - return None - return { - "type": ctype, - "name": name, - "count": count, - "is_vec": is_vec, - } - - -# Block names from MuJoCo's X-macro headers that we KNOWINGLY do not -# consume — additions are still flagged at parse time so a new block -# can't disappear silently, but listed names produce no warning. -KNOWN_UNCONSUMED_BLOCKS = { - # Sentinels / size constants — defined as #define X foo without a body. - "MJMODEL_POINTERS_PREAMBLE", - "MJMODEL_POINTERS", # meta-aggregator (re-includes all POINTERS_* blocks) - "MJMODEL_SIZES", # int field counts - "MJMODEL_INTS", - "MJMODEL_INTS_PREAMBLE", - "MJMODEL_INTS_NUMERIC", - "MJMODEL_FLOATS", - "MJDATA_VECTOR", - "MJDATA_SCALAR", - "MJDATA_SCALAR_PREAMBLE", -} - - -def parse_mjxmacro(path: str) -> Dict[str, Any]: - with open(path, "r", encoding="utf-8") as f: - raw_text = f.read() - text = _strip_comments(raw_text) - lines = text.split("\n") - - out: Dict[str, Any] = { - "_meta": { - "source": os.path.relpath(path, PLUGIN_ROOT), - "schema_kind": "mjxmacro", - }, - "mjmodel_pointers": {}, # block_name -> list of entries - "mjdata_pointers": {}, - "struct_fields": {}, - } - - expected = ( - set(POINTER_BLOCKS_MJMODEL) | set(POINTER_BLOCKS_MJDATA) - | set(STRUCT_FIELD_BLOCKS) | KNOWN_UNCONSUMED_BLOCKS - ) - seen_blocks: set = set() - - i = 0 - while i < len(lines): - line = lines[i] - m = _BLOCK_START_RE.match(line) - if not m: - i += 1 - continue - block_name = m.group(1) - seen_blocks.add(block_name) - # Collect block body — entries on subsequent lines. - entries = _parse_block_body(lines, i + 1) - # Defensive sentinel: a non-entry, non-empty line ended body - # collection inside an EXPECTED block. The XNV regression was - # exactly this — surface it so the next new X-tag bumps cause a - # loud warning rather than silently truncating the snapshot. - end_idx = i + 1 + len(entries) - if (block_name in expected and end_idx < len(lines) - and lines[end_idx].strip() - and not _BODY_ENTRY_RE.match(lines[end_idx].strip())): - # If the very next line was also an entry-shaped token we - # missed (i.e. it has parens), warn loudly. - tail = lines[end_idx].strip() - if "(" in tail and tail.endswith(")") or tail.endswith(") \\"): - print( - f"warning: body collector stopped INSIDE block " - f"{block_name} at line {end_idx + 1}: '{tail[:80]}' — " - f"new X-macro tag? Update _X_TAG_RE in this script.", - file=sys.stderr, - ) - if block_name in POINTER_BLOCKS_MJMODEL: - parsed = [e for e in (_parse_pointer_entry(r) for r in entries) if e] - out["mjmodel_pointers"][block_name] = parsed - elif block_name in POINTER_BLOCKS_MJDATA: - parsed = [e for e in (_parse_pointer_entry(r) for r in entries) if e] - out["mjdata_pointers"][block_name] = parsed - elif block_name in STRUCT_FIELD_BLOCKS: - parsed = [e for e in (_parse_struct_field_entry(r) for r in entries) if e] - out["struct_fields"][block_name] = parsed - i += 1 - - # New-block tripwire: surface any #define block whose name we have - # never seen before. Catches scenarios like a new - # MJMODEL_POINTERS_PLUGIN_SUB landing upstream — without this, the - # snapshot silently drops the block. - unexpected = { - b for b in seen_blocks - if b not in expected and (b.startswith("MJMODEL_") or - b.startswith("MJDATA_") or - b.startswith("MJOPTION_") or - b.startswith("MJSTATISTIC_") or - b.startswith("MJVISUAL_") or - b.startswith("MJSCOMPILER_") or - b.startswith("MJSPEC_")) - } - if unexpected: - print( - f"warning: parsed {len(unexpected)} unrecognised X-macro " - f"block(s) — add to POINTER_BLOCKS_MJMODEL/MJDATA, " - f"STRUCT_FIELD_BLOCKS, or KNOWN_UNCONSUMED_BLOCKS in this " - f"script: {sorted(unexpected)}", - file=sys.stderr, - ) - return out - - -def main() -> int: - src = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_MJXMACRO - dst = sys.argv[2] if len(sys.argv) > 2 else DEFAULT_OUTPUT - if not os.path.isfile(src): - print(f"error: mjxmacro.h not found at {src}", file=sys.stderr) - return 1 - snapshot = parse_mjxmacro(src) - # Track which source each struct-field block came from so a snapshot - # reader can tell mjmodel-side (mjxmacro.h) blocks apart from mjspec-side - # (mjspecmacro.h) blocks without re-parsing the headers. - block_sources: Dict[str, str] = {} - for block_name in snapshot["struct_fields"]: - block_sources[block_name] = snapshot["_meta"]["source"] - # Also fold in mjspecmacro.h (MJSCOMPILER_FIELDS + MJSPEC_FIELDS) from the - # installed MuJoCo headers — both live in the same ``struct_fields`` dict so - # synthetic_categories rules can reference either by block name. - if os.path.isfile(DEFAULT_MJSPECMACRO): - spec_snapshot = parse_mjxmacro(DEFAULT_MJSPECMACRO) - spec_rel = os.path.relpath(DEFAULT_MJSPECMACRO, PLUGIN_ROOT) - for block_name, entries in spec_snapshot["struct_fields"].items(): - snapshot["struct_fields"][block_name] = entries - block_sources[block_name] = spec_rel - else: - # Loud rather than silent — MJSCOMPILER_FIELDS / MJSPEC_FIELDS blocks - # WILL be missing if the installed headers predate mjspecmacro.h, and - # that surfaces only as a downstream KeyError much later in - # generate_ue_components.py. - print( - f"warning: mjspecmacro.h not found at {DEFAULT_MJSPECMACRO}; " - f"MJSCOMPILER_FIELDS + MJSPEC_FIELDS will be absent from the snapshot. " - f"Rebuild the MuJoCo install (needs a version that ships mjspecmacro.h).", - file=sys.stderr, - ) - snapshot["_meta"]["struct_field_sources"] = block_sources - # Stats for sanity. - total_model = sum(len(v) for v in snapshot["mjmodel_pointers"].values()) - total_data = sum(len(v) for v in snapshot["mjdata_pointers"].values()) - total_structs = sum(len(v) for v in snapshot["struct_fields"].values()) - print( - f"parsed mjxmacro.h: {len(snapshot['mjmodel_pointers'])} model blocks " - f"({total_model} entries), " - f"{len(snapshot['mjdata_pointers'])} data blocks ({total_data} entries), " - f"{len(snapshot['struct_fields'])} struct blocks ({total_structs} entries)" - ) - with open(dst, "w", encoding="utf-8") as f: - json.dump(snapshot, f, indent=2) - print(f"wrote {dst}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/Scripts/codegen/codegen_rules.json b/Scripts/codegen/codegen_rules.json deleted file mode 100644 index ee017f23..00000000 --- a/Scripts/codegen/codegen_rules.json +++ /dev/null @@ -1,1533 +0,0 @@ -{ - "_meta": { - "description": "Definitive rules for URLab Unreal codegen. Drives generate_ue_components.py: which schema attrs to emit, how to type-map them, which canonicalisations absorb multi-attr clusters, which categories use single-UCLASS or multi-UCLASS or no-subclass layouts, and which mjxmacro pointer fields belong on which view struct.", - "version": "5.0" - }, - - "_doc_transform_widget_policy": "Categories whose Pos/Quat are authored via the UE Transform widget (geom/site/camera/frame/inertial) hide the canon Pos/Quat UPROPERTYs from the Details panel. The UPROPERTYs still exist for the import seed + BP read access, but EditConditionHides keeps them out of the user-facing surface so authoring stays in one place. Per-element _note_hidden entries point at this policy instead of restating it.", - - "global_exclusions": [ - "user", "name", "class", "type", "objtype", "reftype" - ], - "default_type": "float", - "_note_property_renames": "Schema attr -> UPROPERTY identifier. Used when the UE-side identifier must differ from the verbatim schema attr (typically to avoid C++ keyword collisions or to keep PascalCase for legacy consumers). qpos/qvel/etc are PascalCase on UMjKeyframe because BP graphs / runtime accessors reference them that way.", - "property_renames": { - "qpos": "Qpos", - "qvel": "Qvel", - "act": "Act", - "ctrl": "Ctrl", - "mpos": "Mpos", - "mquat": "Mquat", - "time": "Time", - "actuatorfrclimited": "ActFrcLimited", - "actuatorfrcrange": "ActFrcRange", - "axis": "Axis" - }, - - "_note_setto_param_defaults": "Per-(function,param) 'not overridden' values, used ONLY for params MuJoCo treats as genuinely optional — i.e. it guards them with `>= 0` (muscle) or a null pointer, so the sentinel means 'leave the default alone'. Params MuJoCo writes unconditionally by value must NOT appear here (a sentinel would clobber the inherited value); use `inherit_fields` in subtype_setto instead. Values are strings so the codegen drops them verbatim into the generated C++ call without forcing a 1 vs 1.0 vs 1.0f format choice.", - "setto_param_defaults": { - "mjs_setToPosition": {"inheritrange": "0.0"}, - "mjs_setToIntVelocity": {"inheritrange": "0.0"}, - "mjs_setToMuscle": {"tausmooth": "0.0"}, - "mjs_setToDCMotor": {"resistance": "0.0", "input_mode": "0"} - }, - - "_note_unmodeled_elements": "Top-level MJCF elements that the codegen drift-diagnostic silences. Each entry is the reason it's skipped — change the value to '' to make the diagnostic fire again if you want to revisit.", - "intentionally_unmodeled_elements": { - "freejoint": "Handled as a joint subtype (UMjFreeJoint) — the freejoint XML tag maps to the joint category via the parser, not as a top-level category.", - "light": "URLab doesn't model MJCF as a UE component — UE has its own light primitives that override MJCF light placement.", - "flex": "Children of . URLab generates flexes via UMjFlexcomp (which expands to a flex at parse time); the direct element is not a UE component.", - "skin": "Children of . URLab handles skinning via UE skeletal meshes; in MJCF would need a dedicated asset pipeline." - }, - - "_note_hand_vs_generated_enums": "URLab keeps every EMj*Type enum hand-rolled. Subtype-driving enums (EMjJointType, EMjGeomType, etc.) must stay hand-rolled because each member drives a UClass subclass — adding one is a product decision, not mechanical. Pure-mapping enums (EMjGainType, EMjBiasType, ...) are mechanically codegenable but live next to their parent UClass; moving them to Generated/ would require include reordering for no functional gain. The hand_enum_drift phase in generate_ue_components.py provides the equivalent forward-maintainability guarantee: any new mjt* value upstream surfaces as a diagnostic instead of a silent runtime fallthrough.", - - "_note_compiler_attr_field_map": "MJCF XML attrs URLab honours, mapped to their FMjCompilerSettings field name. The field is hand-rolled in MuJoCo/Utils/MjOrientationUtils.h; the drift check uses this map purely as an allowlist. New MuJoCo compiler flags surface as a diagnostic until added here or to intentionally_unmodeled_compiler_attrs.", - "compiler_attr_field_map": { - "angle": "bAngleInDegrees", - "eulerseq": "EulerSeq", - "meshdir": "MeshDir", - "assetdir": "AssetDir", - "autolimits": "bAutoLimits" - }, - - "_note_intentionally_unmodeled_compiler_attrs": " XML attrs that don't affect URLab's UE-side parsing — physics tuning knobs, compiler-only optimisations, paths URLab supersedes with UE asset routing. Listed explicitly so the compiler-coverage diagnostic stays clean while remaining loud on additions.", - "intentionally_unmodeled_compiler_attrs": [ - "boundmass", "boundinertia", "settotalmass", "balanceinertia", - "strippath", "coordinate", "fitaabb", "discardvisual", - "usethread", "fusestatic", "inertiafromgeom", "inertiagrouprange", - "saveinertial", "texturedir", "alignfree" - ], - - "_note_intentionally_default_typed_attrs": "Attrs that legitimately want default_type ('float') — single scalar floats whose UE shape happens to match the rule fallback. Listed explicitly so the new_attr_typing diagnostic stays clean. Any new MuJoCo XML attr added in a future version that ISN'T a scalar float will surface as a diagnostic.", - "intentionally_default_typed_attrs": [ - "active", "actuatorgravcomp", "area", "armature", "bias", - "cranklength", "cutoff", "dampratio", "density", "diameter", - "force", "fovy", "fpmax", "frictionloss", "fvmax", - "gain", "gap", "gravcomp", "inheritrange", "ipd", - "kp", "kv", "lmax", "lmin", "margin", - "mass", "noise", "output", "radius", "ref", - "resistance", "solmix", "springref", "tausmooth", "torquescale", - "vmax", "width" - ], - - "_note_intentionally_unmapped_mj_enum_values": "MuJoCo enum constants the codegen knowingly does NOT expose as URLab enum members. Silences the hand_enum_drift diagnostic. Two scopes: top-level keys are mjt* enum names whose entries apply to EVERY element using that enum (visualizer-only types, sentinel counts, alternative-class subsumption); '__per_element__' carries per-(element, attr) overrides for cases where the same mjt* enum admits a different subset depending on the host (e.g. rejects mjGEOM_PLANE/HFIELD/MESH but accepts them).", - "intentionally_unmapped_mj_enum_values": { - "mjtGain": ["mjGAIN_DCMOTOR"], - "mjtBias": ["mjBIAS_DCMOTOR"], - "mjtDyn": ["mjDYN_DCMOTOR"], - "mjtGeom": [ - "mjNGEOMTYPES", - "mjGEOM_ARROW", "mjGEOM_ARROW1", "mjGEOM_ARROW2", - "mjGEOM_LINE", "mjGEOM_LINEBOX", "mjGEOM_LABEL", "mjGEOM_TRIANGLE", - "mjGEOM_FLEX", "mjGEOM_SKIN", "mjGEOM_SDF", "mjGEOM_NONE" - ], - "__per_element__": { - "_note_site_type": "Site exposes Mesh + Hfield. Plane stays omitted because a site is a point/extent surface, not an infinite plane; SDF stays omitted because it carries heavy resource data the spawn-surface UX doesn't need.", - "site.type": ["mjGEOM_PLANE"] - } - }, - - "_note_intentionally_unmodeled_mjs_fields": "Per-mjsX-struct list of fields URLab knows about but doesn't model via codegen. Silences the mjsX-field-drift diagnostic. Add entries for fields handled by a different UE component, by canon, or that we deliberately skip.", - "intentionally_unmodeled_mjs_fields": { - "mjsBody": [ - "inertia", "ipos", "ialt", "mass", "iquat", "fullinertia", "alt", - "plugin" - ], - "mjsFrame": [ - "pos", "quat", "childclass", "alt" - ], - "mjsFlex": [ - "solimp", "solmix", "contype", "activelayers", "edgedamping", "elem", - "size", "passive", "condim", "order", "nodebody", - "vertbody", "elemtexcoord", "selfcollide", "poisson", "vert", "margin", - "conaffinity", "damping", "priority", "edgestiffness", "node", - "solref", "gap", "elastic2d", "young", "friction", "thickness", "internal" - ], - "mjsCamera": [ - "intrinsic", "alt" - ], - "mjsGeom": [ - "alt", "plugin" - ], - "mjsSite": [ - "alt" - ], - "mjsActuator": [ - "plugin" - ], - "mjsJoint": [ - "align" - ], - "mjsSensor": [ - "datatype", "dim", "intprm", "needstage", "objname", "plugin", - "refname", "reftype" - ] - }, - - "type_mappings": { - "axis": "FVector", - "dir": "FVector", - "size": "TArray", - "principal": "TArray", - "focal": "TArray", - "sensorsize": "TArray", - "fluidcoef": "TArray", - "count": "TArray", - "cellcount": "TArray", - "spacing": "TArray", - "scale": "TArray", - "point": "TArray", - "element": "TArray", - "texcoord": "TArray", - "inertiabox": "float", - "rigid": "bool", - "flatskin": "bool", - "origin": "TArray", - "dim": "int32", - "rgba": "FLinearColor", - "range": "TArray", - "actuatorfrcrange": "TArray", - "ctrlrange": "TArray", - "forcerange": "TArray", - "actrange": "TArray", - "lengthrange": "TArray", - "gear": "TArray", - "springdamper": "TArray", - "springlength": "TArray", - "timeconst": "TArray", - "motorconst": "TArray", - "nominal": "TArray", - "saturation": "TArray", - "inductance": "TArray", - "cogging": "TArray", - "controller": "TArray", - "thermal": "TArray", - "lugre": "TArray", - "stiffness": "TArray", - "damping": "TArray", - "friction": "TArray", - "fullinertia": "TArray", - "diaginertia": "TArray", - "resolution": "TArray", - "focalpixel": "TArray", - "principalpixel": "TArray", - "solref": "TArray", - "solimp": "TArray", - "solreflimit": "TArray", - "solimplimit": "TArray", - "solreffriction": "TArray", - "solimpfriction": "TArray", - "gainprm": "TArray", - "biasprm": "TArray", - "dynprm": "TArray", - "polycoef": "TArray", - "limited": "bool", - "mocap": "bool", - "actlimited": "bool", - "ctrllimited": "bool", - "forcelimited": "bool", - "actuatorfrclimited":"bool", - "actearly": "bool", - "shellinertia": "bool", - "fitscale": "double", - "fitaabb": "bool", - "discardvisual": "bool", - "objname": "FString", - "refname": "FString", - "gaintype": "FString", - "biastype": "FString", - "dyntype": "FString", - "fluidshape": "FString", - "site": "FString", - "joint": "FString", - "tendon": "FString", - "actuator": "FString", - "geom": "FString", - "geom1": "FString", - "geom2": "FString", - "body": "FString", - "body1": "FString", - "body2": "FString", - "site1": "FString", - "site2": "FString", - "joint1": "FString", - "joint2": "FString", - "tendon1": "FString", - "tendon2": "FString", - "mesh": "FString", - "material": "FString", - "hfield": "FString", - "childclass": "FString", - "texture": "FString", - "file": "FString", - "target": "FString", - "mode": "FString", - "anchor": "TArray", - "relpose": "TArray", - "ctrl": "TArray", - "qpos": "TArray", - "qvel": "TArray", - "act": "TArray", - "mpos": "TArray", - "mquat": "TArray", - "time": "float", - "group": "int32", - "contype": "int32", - "conaffinity": "int32", - "condim": "int32", - "priority": "int32", - "actdim": "int32", - "nsample": "int32", - "interp": "int32", - "interval": "TArray", - "delay": "float", - "input": "EMjDcMotorInput" - }, - - "canonicalizations": { - "spatial_pose": { - "absorbs_attrs": ["pos"], - "emits_property": {"name": "Pos", "type": "FVector"}, - "import_helper": "MjUtils::ReadVec3InMeters", - "export_helper": "MjUtils::UEToMjPosition" - }, - "orientation": { - "absorbs_attrs": ["quat", "euler", "axisangle", "xyaxes", "zaxis"], - "emits_property": {"name": "Quat", "type": "FQuat"}, - "import_helper": "MjOrientationUtils::OrientationToMjQuat", - "export_helper": "MjUtils::UEToMjRotation" - }, - "actuator_transmission": { - "_note": "MJCF actuators encode their transmission via one of {joint, tendon, site, body, jointinparent} (the target name) plus optional {slidersite, refsite}. URLab collapses this to a single EMjActuatorTrnType enum + TargetName/SliderSite/RefSite strings. Codegen emits the parse+write blocks inline (no rule-side helper); the UPROPERTY declarations stay hand-side on UMjActuator.", - "absorbs_attrs": ["joint", "tendon", "site", "body", "jointinparent", "slidersite", "refsite"], - "emits_property": null - }, - "body_sleep_policy": { - "_note": "MJCF body.sleep is one of {'never','allowed','init'} or absent; mjsBody.sleep is the mjtSleepPolicy enum. URLab exposes EMjBodySleepPolicy SleepPolicy (with a Default entry that means 'let the global option decide'). Codegen emits the string<->enum mapping on both sides inline (no rule-side helper); the SleepPolicy UPROPERTY itself stays declared hand-side on UMjBody.", - "absorbs_attrs": ["sleep"], - "emits_property": null - }, - "fromto_decompose": { - "_note": "MJCF's `fromto` is an alternative form for capsule/cylinder/box/ellipsoid (an XY-XY segment). Decompose at import into Pos/Quat plus a half-length scalar written into size[1] (Y slot, e.g. capsule half-length) or size[2] (Z slot, e.g. box half-height) depending on shape Type. The codegen-owned size TArray is grown as needed.", - "absorbs_attrs": ["fromto"], - "emits_property": null, - "import_helper": "MjUtils::DecomposeFromTo", - "category_specifics": { - "geom": { - "z_slot_check": "(Type == EMjGeomType::Box || Type == EMjGeomType::Ellipsoid)" - }, - "site": { - "z_slot_check": "(Type == EMjSiteType::Box || Type == EMjSiteType::Ellipsoid)" - } - } - } - }, - - "element_rules": { - "actuator": { - "_note_enums": "gaintype/biastype/dyntype are mjtGain/mjtBias/mjtDyn enums on mjsActuator. URLab declares EMjGainType/EMjBiasType/EMjDynType hand-side on UMjActuator; codegen owns the XML<->enum map via xml_enum_attrs. dcmotor 'input' is per-subtype (only on UMjDcMotorActuator) — emitter gates on attr-in-subtype-schema for that case.", - "common_imports": ["class"], - "applies_canonicalizations": ["actuator_transmission"], - "xml_enum_attrs": { - "gaintype": { - "ue_property": "GainType", - "ue_enum_type": "EMjGainType", - "mjs_field": "gaintype", - "mjs_cast": "mjtGain", - "emit_property_decl": true, - "property_default": "Fixed", - "value_map": { - "fixed": ["Fixed", "mjGAIN_FIXED"], - "affine": ["Affine", "mjGAIN_AFFINE"], - "muscle": ["Muscle", "mjGAIN_MUSCLE"], - "user": ["User", "mjGAIN_USER"] - } - }, - "biastype": { - "ue_property": "BiasType", - "ue_enum_type": "EMjBiasType", - "mjs_field": "biastype", - "mjs_cast": "mjtBias", - "emit_property_decl": true, - "property_default": "None", - "value_map": { - "none": ["None", "mjBIAS_NONE"], - "affine": ["Affine", "mjBIAS_AFFINE"], - "muscle": ["Muscle", "mjBIAS_MUSCLE"], - "user": ["User", "mjBIAS_USER"] - } - }, - "dyntype": { - "ue_property": "DynType", - "ue_enum_type": "EMjDynType", - "mjs_field": "dyntype", - "mjs_cast": "mjtDyn", - "emit_property_decl": true, - "property_default": "None", - "value_map": { - "none": ["None", "mjDYN_NONE"], - "integrator": ["Integrator", "mjDYN_INTEGRATOR"], - "filter": ["Filter", "mjDYN_FILTER"], - "filterexact": ["FilterExact", "mjDYN_FILTEREXACT"], - "muscle": ["Muscle", "mjDYN_MUSCLE"], - "user": ["User", "mjDYN_USER"] - } - }, - "input": { - "ue_property": "input", - "ue_enum_type": "EMjDcMotorInput", - "mjs_field": null, - "value_map": { - "voltage": ["Voltage", "0"], - "position": ["Position", "1"], - "velocity": ["Velocity", "2"] - } - } - } - }, - "sensor": { - "_note": "Sensor reads its target name from one of several xor attrs depending on subtype (site for touch/accel/gyro, joint for jointpos, etc.). target_collations collapses them into UE FString TargetName. ReferenceName is direct from 'refname'. ObjType / RefType are EMjObjType enums with their own string<->enum mapping (xml_enum_attrs).", - "common_imports": ["class"], - "target_collations": { - "TargetName": { - "mjs_field": "objname", - "absorbs_attrs": ["site", "joint", "tendon", "actuator", "body", "geom", "objname", "camera", "mesh"] - }, - "ReferenceName": { - "mjs_field": "refname", - "absorbs_attrs": ["refname"] - } - }, - "xml_enum_attrs": { - "objtype": { - "ue_property": "ObjType", - "ue_enum_type": "EMjObjType", - "has_override_toggle": false, - "value_map": { - "body": ["Body", "mjOBJ_BODY"], - "xbody": ["XBody", "mjOBJ_XBODY"], - "joint": ["Joint", "mjOBJ_JOINT"], - "dof": ["DoF", "mjOBJ_DOF"], - "geom": ["Geom", "mjOBJ_GEOM"], - "site": ["Site", "mjOBJ_SITE"], - "camera": ["Camera", "mjOBJ_CAMERA"], - "light": ["Light", "mjOBJ_LIGHT"], - "mesh": ["Mesh", "mjOBJ_MESH"], - "hfield": ["HField", "mjOBJ_HFIELD"], - "texture": ["Texture", "mjOBJ_TEXTURE"], - "material": ["Material", "mjOBJ_MATERIAL"], - "pair": ["Pair", "mjOBJ_PAIR"], - "exclude": ["Exclude", "mjOBJ_EXCLUDE"], - "equality": ["Equality", "mjOBJ_EQUALITY"], - "tendon": ["Tendon", "mjOBJ_TENDON"], - "actuator": ["Actuator", "mjOBJ_ACTUATOR"] - } - }, - "reftype": { - "ue_property": "RefType", - "ue_enum_type": "EMjObjType", - "has_override_toggle": false, - "value_map": { - "body": ["Body", "mjOBJ_BODY"], - "xbody": ["XBody", "mjOBJ_XBODY"], - "joint": ["Joint", "mjOBJ_JOINT"], - "geom": ["Geom", "mjOBJ_GEOM"], - "site": ["Site", "mjOBJ_SITE"], - "camera": ["Camera", "mjOBJ_CAMERA"] - } - } - } - }, - "joint": { - "_note_axis_pos": "axis: MuJoCo direction vector with opposite Y handedness vs UE; vec3_convert.axis = y_negate flips on import + export. pos: spatial_pose canon emits the Pos UPROPERTY pair + drives SetRelativeLocation on import.", - "common_imports": ["class"], - "applies_canonicalizations": ["spatial_pose"], - "vec3_convert": {"axis": "y_negate"}, - "property_defaults": {"axis": "FVector(0.0f, 0.0f, 1.0f)"}, - "_note_hidden": "Pos is the joint anchor offset — derived from the UE component's RelativeLocation. Axis stays visible as a direction (not a position) and is the user-authored joint hinge direction.", - "hidden_canon_properties": ["spatial_pose"], - "_note_property_meta": "Joint fields whose unit semantics shift with the joint Type get a tooltip that spells out the per-type interpretation, so a user reading the Details panel knows whether the number is metres or degrees. UE's Units meta is intentionally NOT used here — a static label would be wrong for the other Type.", - "property_meta": { - "axis": "ToolTip=\"Joint axis in the parent body's local frame. Hinge: rotation axis. Slide: translation direction. Ignored for Ball and Free.\"", - "range": "ToolTip=\"Joint position bounds [min, max]. Hinge/Ball: degrees. Slide: centimetres. Ignored when 'limited' is false.\"", - "ref": "ToolTip=\"Reference (zero) position. Hinge/Ball: degrees. Slide: centimetres.\"", - "springref": "ToolTip=\"Equilibrium position for the joint spring. Hinge/Ball: degrees. Slide: centimetres.\"", - "stiffness": "ToolTip=\"Per-axis spring stiffness. Hinge/Ball: N·m/rad. Slide: N/m.\"", - "damping": "ToolTip=\"Per-axis damping. Hinge/Ball: N·m·s/rad. Slide: N·s/m.\"", - "springdamper": "ToolTip=\"Compact spring spec [stiffness, damping]; populated when MJCF used . Overrides stiffness + damping when set.\"", - "armature": "ToolTip=\"Added rotor inertia for hinges (kg·m²) or added mass for slides (kg). Models reflected inertia of the actuator.\"", - "frictionloss": "ToolTip=\"Dry-friction loss. Hinge/Ball: N·m. Slide: N.\"", - "actuatorgravcomp": "ToolTip=\"Gravity compensation factor passed to actuators on this joint. 0 = no compensation, 1 = full gravity cancelled.\"", - "actuatorfrcrange": "ToolTip=\"Actuator force bounds [min, max] when 'ActFrcLimited' is true. Hinge/Ball: N·m. Slide: N.\"", - "margin": "ToolTip=\"Contact-detection margin (m). Joints inherit this when computing limit constraints.\"", - "group": "ToolTip=\"Visualisation group 0–5. MuJoCo viewer uses this to toggle visibility.\"" - }, - "xml_enum_attrs": { - "type": { - "ue_property": "Type", - "ue_enum_type": "EMjJointType", - "mjs_field": "type", - "mjs_cast": "mjtJoint", - "value_map": { - "hinge": ["Hinge", "mjJNT_HINGE"], - "slide": ["Slide", "mjJNT_SLIDE"], - "ball": ["Ball", "mjJNT_BALL"], - "free": ["Free", "mjJNT_FREE"] - } - } - }, - "_note_unit_conversion": "URLab stores joint range/ref/springref in UE conventions (degrees for hinge/ball, centimetres for slide). MJCF XML carries them in the compiler.angle unit (deg/rad) for angular joints and metres for linear; URLab's spec wrapper sets compiler.degree=false so mjsJoint.range[] must be radians on the wire. The conversions below normalize at the IO boundary so the rest of URLab can treat the UPROPERTYs as UE-native values.", - "unit_conversion": { - "range": [ - {"condition": "Type == EMjJointType::Slide", - "import_op": "m_to_cm", "export_op": "cm_to_m"}, - {"condition": "(Type == EMjJointType::Hinge) || (Type == EMjJointType::Ball)", - "import_op": "rad_to_deg_if_xml_radians", "export_op": "deg_to_rad"} - ], - "ref": [ - {"condition": "Type == EMjJointType::Slide", - "import_op": "m_to_cm", "export_op": "cm_to_m"}, - {"condition": "(Type == EMjJointType::Hinge) || (Type == EMjJointType::Ball)", - "import_op": "rad_to_deg_if_xml_radians", "export_op": "deg_to_rad"} - ], - "springref": [ - {"condition": "Type == EMjJointType::Slide", - "import_op": "m_to_cm", "export_op": "cm_to_m"}, - {"condition": "(Type == EMjJointType::Hinge) || (Type == EMjJointType::Ball)", - "import_op": "rad_to_deg_if_xml_radians", "export_op": "deg_to_rad"} - ] - } - }, - "geom": { - "_note": "size is codegen-owned TArray; fromto-decomposed half-length is written into size[1]/[2] by the canon. material is codegen-owned for IMPORT (FString -> UE material lookup) but SKIPPED on export because URLab handles materials as UE assets and does not register them in the mjsSpec (mjs_setString on Element->material would reference a non-existent spec entry and fail mj_compile). Orientation cluster absorbed by `orientation`; fromto absorbed by `fromto_decompose`. type is an URLab enum (EMjGeomType); codegen owns the XML<->enum string map via xml_enum_attrs.", - "common_imports": ["class"], - "_note_hfield": "hfield references URLab's own hfield actor system, not a mjsHField spec entry. Codegen still imports the string for editor visibility but skips the spec write to avoid 'hfield not found' on compile.", - "export_skip_attrs": ["material", "hfield"], - "applies_canonicalizations": ["spatial_pose", "orientation", "fromto_decompose"], - "_note_sentinel": "fromto-only XML inputs leave the radius slot unset; the canon writes -1.0f as the sentinel and the per-attr size export skips those slots so the parent default's radius is preserved.", - "sentinel_skip_export": {"size": "-1.0f"}, - "_note_property_units": "Scalar lengths exposed in mjsGeom carry meta=(Units=\"m\") so the editor shows a 'm' suffix or auto-converts to cm/mm when 'Display Units' is enabled. Storage stays raw MJ metres; export path unaffected. size is hidden (Scale3D is canonical) — see hidden_attrs.", - "property_units": { - "margin": "m", - "gap": "m" - }, - "_note_hidden": "See _doc_transform_widget_policy. size is hidden too — Scale3D is canonical.", - "hidden_canon_properties": ["spatial_pose", "orientation"], - "hidden_attrs": ["size"], - "_note_attr_to_mjs_field": "MuJoCo renames fluidcoef -> fluid_coefs on the mjsGeom struct. shellinertia and fluidshape are handled by xml_enum_attrs below (they need string<->enum + field-rename together).", - "attr_to_mjs_field": { - "fluidcoef": "fluid_coefs" - }, - "xml_enum_attrs": { - "type": { - "ue_property": "Type", - "ue_enum_type": "EMjGeomType", - "mjs_field": "type", - "mjs_cast": "mjtGeom", - "value_map": { - "plane": ["Plane", "mjGEOM_PLANE"], - "hfield": ["Hfield", "mjGEOM_HFIELD"], - "sphere": ["Sphere", "mjGEOM_SPHERE"], - "capsule": ["Capsule", "mjGEOM_CAPSULE"], - "ellipsoid": ["Ellipsoid", "mjGEOM_ELLIPSOID"], - "cylinder": ["Cylinder", "mjGEOM_CYLINDER"], - "box": ["Box", "mjGEOM_BOX"], - "mesh": ["Mesh", "mjGEOM_MESH"], - "sdf": ["SDF", "mjGEOM_SDF"] - } - }, - "shellinertia": { - "_note": "XML attr is bool-ish (true/false). MuJoCo stores it as mjtGeomInertia (mjINERTIA_VOLUME=0 default, mjINERTIA_SHELL=1) in mjsGeom.typeinertia.", - "ue_property": "ShellInertia", - "ue_enum_type": "EMjGeomInertia", - "mjs_field": "typeinertia", - "mjs_cast": "mjtGeomInertia", - "value_map": { - "false": ["Volume", "mjINERTIA_VOLUME"], - "true": ["Shell", "mjINERTIA_SHELL"] - } - }, - "fluidshape": { - "_note": "XML attr is none/ellipsoid. MuJoCo stores 0.0/1.0 in mjsGeom.fluid_ellipsoid (mjtNum).", - "ue_property": "FluidShape", - "ue_enum_type": "EMjFluidShape", - "mjs_field": "fluid_ellipsoid", - "mjs_cast": "mjtNum", - "value_map": { - "none": ["None", "0.0"], - "ellipsoid": ["Ellipsoid", "1.0"] - } - } - } - }, - "site": { - "_note": "Same as geom: size codegen-owned TArray; material codegen-owned for IMPORT but export-skipped (UE-side materials); orientation + fromto canonicalized. mjsSite.type uses mjtGeom (sites are visual 'geoms'). Site has no bOverride_Type — Type always carries a value (default Sphere).", - "export_skip_attrs": ["material"], - "applies_canonicalizations": ["spatial_pose", "orientation", "fromto_decompose"], - "sentinel_skip_export": {"size": "-1.0f"}, - "_note_hidden": "See _doc_transform_widget_policy. size hidden too — Scale3D is canonical.", - "hidden_canon_properties": ["spatial_pose", "orientation"], - "hidden_attrs": ["size"], - "xml_enum_attrs": { - "type": { - "ue_property": "Type", - "ue_enum_type": "EMjSiteType", - "mjs_field": "type", - "mjs_cast": "mjtGeom", - "has_override_toggle": false, - "value_map": { - "sphere": ["Sphere", "mjGEOM_SPHERE"], - "capsule": ["Capsule", "mjGEOM_CAPSULE"], - "ellipsoid": ["Ellipsoid", "mjGEOM_ELLIPSOID"], - "cylinder": ["Cylinder", "mjGEOM_CYLINDER"], - "box": ["Box", "mjGEOM_BOX"], - "mesh": ["Mesh", "mjGEOM_MESH"], - "hfield": ["Hfield", "mjGEOM_HFIELD"] - } - } - } - }, - "tendon": { - "common_imports": ["class"], - "_note_material": "material codegen-owned for IMPORT but export-skipped — URLab handles materials as UE assets, not mjsMaterial spec entries.", - "export_skip_attrs": ["material"], - "_note_property_units": "springlength is unambiguously a length in metres. range is intentionally NOT tagged — it can be a length (slide-mode tendon) or an angle (rare hinge-coupled tendon) and labelling it with one unit would be wrong as often as right.", - "property_units": { - "springlength": "m" - } - }, - "equality": { - "_note_target_collation": "MJCF equality elements address one or two of {body, site, joint, tendon, flex} per the equality kind. URLab collapses to FString Obj1/Obj2 (hand-declared UPROPERTYs with dynamic GetOptions based on EqualityType). Excluded from codegen so they don't appear as separate UPROPERTYs; target_collations rule emits the import collation + mjs_setString export. site1/site2 stay as their own UPROPERTYs (codegen-owned) so the objtype switch in ExportTo can detect site-mode connects/welds and pick mjOBJ_SITE.", - "exclude_attrs": ["body1", "body2", "joint1", "joint2", "tendon1", "tendon2", "flex"], - "target_collations": { - "Obj1": { - "absorbs_attrs": ["body1", "site1", "joint1", "tendon1", "flex"], - "mjs_field": "name1" - }, - "Obj2": { - "absorbs_attrs": ["body2", "site2", "joint2", "tendon2"], - "mjs_field": "name2" - } - }, - "_note_data_packed": "mjsEquality has no per-attr fields for anchor/relpose/polycoef/torquescale; they all pack into data[] per the connect/weld/joint/tendon layout (verified via probe_eq_spec.py against the installed MuJoCo). Codegen-emitted exports replace the default per-attr emission with the packed write under the right EqualityType condition. anchor carries a cm_to_m export op so the URLab-side cm convention round-trips into spec metres. relpose is stored opaquely (raw MJ format: metres for the first 3 floats, MuJoCo quat for the last 4) — weld constraints are rare enough that the inconsistency is worth the simpler emitter.", - "_note_unit_conversion": "anchor is a position offset in body1's local frame; URLab stores it in centimetres (matching every other position UPROPERTY). The unit_conversion below handles the import side (XML m -> UPROPERTY cm); the data-packed export carries its own cm_to_m op so it converts in flight before writing data[0..2].", - "unit_conversion": { - "anchor": [ - {"condition": "(EqualityType == EMjEqualityType::Connect) || (EqualityType == EMjEqualityType::Weld)", - "import_op": "m_to_cm"} - ] - }, - "_note_property_units": "anchor is stored in UE centimetres (post unit_conversion). Tagging the field with Units=\"cm\" so the editor shows the suffix that matches the actual stored value — no auto-conversion needed.", - "property_units": { - "anchor": "cm" - }, - "mjs_data_packed_attrs": { - "anchor": { - "slot_range": [0, 3], - "condition": "(EqualityType == EMjEqualityType::Connect) || (EqualityType == EMjEqualityType::Weld)", - "export_op": "cm_to_m" - }, - "relpose": { - "slot_range": [3, 10], - "condition": "EqualityType == EMjEqualityType::Weld" - }, - "polycoef": { - "_note": "polycoef is a 5-element MJCF attribute (default '0 1 0 0 0'). mjsEquality.data layout reserves slots 0..4 for it on joint/tendon equalities; slots 5..10 hold other fields (e.g. weld's torquescale at 10). Capping the slot_range here keeps a TArray longer than 5 entries from overflowing into those slots.", - "slot_range": [0, 5], - "condition": "(EqualityType == EMjEqualityType::Joint) || (EqualityType == EMjEqualityType::Tendon)" - }, - "torquescale": { - "slot": 10, - "condition": "EqualityType == EMjEqualityType::Weld" - } - } - }, - "camera": { - "_note": "MJCF camera.mode is a tracking mode (mjtCamLight subset). URLab declares EMjCameraTrackingMode hand-side; codegen owns the XML<->enum map. NOT to be confused with URLab's EMjCameraMode which is a UE-side render-mode selector (RGB/Depth/Segmentation).", - "_note_common_imports": "Camera also reads name; the hand-written ImportFromXml has a fallback `if MjName empty -> 'Camera'` that runs AFTER this read.", - "common_imports": ["name"], - "applies_canonicalizations": ["spatial_pose", "orientation"], - "_note_hidden": "See _doc_transform_widget_policy. Intrinsics (fovy, ipd, focal, etc.) stay visible.", - "hidden_canon_properties": ["spatial_pose", "orientation"], - "_note_attr_to_mjs_field": "MuJoCo renames a handful of camera attrs between the XML schema and the mjsCamera struct: target -> targetbody (mjString*), projection -> proj (mjtProjection enum), focal -> focal_length, principal -> principal_length. sensorsize auto-resolves to sensor_size via the underscore-normalise heuristic. Without this map the auto-resolver in _resolve_mjs_field can't bridge target/focal/principal and they were silently dropped on export.", - "attr_to_mjs_field": { - "target": "targetbody", - "focal": "focal_length", - "principal": "principal_length" - }, - "_note_property_units": "Camera lengths (ipd, focal, principal, sensorsize) stay in raw MJ metres on the UE side; tagging them with Units=\"m\" gives the editor a suffix and lets Display Units convert to mm/cm. fovy is degrees per the MJCF spec. focalpixel / principalpixel are pixel counts (not lengths) and are intentionally untagged.", - "property_units": { - "ipd": "m", - "focal": "m", - "principal": "m", - "sensorsize": "m", - "fovy": "deg" - }, - "xml_enum_attrs": { - "mode": { - "ue_property": "TrackingMode", - "ue_enum_type": "EMjCameraTrackingMode", - "mjs_field": "mode", - "mjs_cast": "mjtCamLight", - "value_map": { - "fixed": ["Fixed", "mjCAMLIGHT_FIXED"], - "track": ["Track", "mjCAMLIGHT_TRACK"], - "trackcom": ["TrackCom", "mjCAMLIGHT_TRACKCOM"], - "targetbody": ["TargetBody", "mjCAMLIGHT_TARGETBODY"], - "targetbodycom": ["TargetBodyCom", "mjCAMLIGHT_TARGETBODYCOM"] - } - }, - "projection": { - "ue_property": "Projection", - "ue_enum_type": "EMjCameraProjection", - "mjs_field": "proj", - "mjs_cast": "mjtProjection", - "value_map": { - "orthographic": ["Orthographic", "mjPROJ_ORTHOGRAPHIC"], - "perspective": ["Perspective", "mjPROJ_PERSPECTIVE"] - } - } - } - }, - "body": { - "common_imports": ["name"], - "applies_canonicalizations": ["spatial_pose", "orientation", "body_sleep_policy"], - "_note_canon_export_skip": "Body's pos/quat are written by MjSpecWrapper::CreateBody from the UE component transform (the source of truth for body placement). The codegen-emitted spatial_pose/orientation export would overwrite those with the Pos/Quat UPROPERTYs, which are stale for any body not freshly imported from XML. Skip the export side for these two canons.", - "canon_export_skip": ["spatial_pose", "orientation"], - "_note_hidden": "See _doc_transform_widget_policy. canon_export_skip routes the write through the UE transform.", - "hidden_canon_properties": ["spatial_pose", "orientation"], - "property_meta": { - "mocap": "DisplayName=\"Driven By Unreal\"", - "childclass": "GetOptions=\"GetChildClassOptions\"" - } - }, - "inertial": { - "applies_canonicalizations": ["spatial_pose", "orientation"], - "_note_hidden": "See _doc_transform_widget_policy. UMjInertial::RegisterToSpec (hand-rolled) writes ParentBody->ipos/iquat from the UE transform.", - "hidden_canon_properties": ["spatial_pose", "orientation"] - }, - "frame": { - "_note": "Like body: the UE component transform is the source of truth for placement. Codegen imports XML pos/quat into Pos/Quat UPROPERTYs (UE units / UE frame) and the cpp drives SetRelativeLocation/SetRelativeRotation from them. Spec export is skipped for the canon Pos/Quat because MjSpecWrapper::CreateFrame writes pos/quat from the UE transform.", - "applies_canonicalizations": ["spatial_pose", "orientation"], - "canon_export_skip": ["spatial_pose", "orientation"], - "hidden_canon_properties": ["spatial_pose", "orientation"] - }, - "flexcomp": { - "_note": "Flexcomp doesn't register via mjs_addX — at spec time UMjFlexcomp builds a tiny standalone MJCF fragment and re-parses it via mj_parseXMLString + mjs_attach (the flexcomp macro is expanded by MuJoCo's XML parser, not the spec API). Codegen still owns the UPROPERTY surface so new MJCF attrs auto-appear; BuildFlexcompXml in MjFlexcomp.cpp reads from those codegen-owned fields. Sub-elements (///) remain hand-rolled because the codegen doesn't have per-sub-element UPROPERTY support yet.", - "_note_material": "material codegen-owned for IMPORT but export-skipped — URLab handles materials as UE assets, not mjsMaterial spec entries.", - "export_skip_attrs": ["material"], - "applies_canonicalizations": ["spatial_pose", "orientation"], - "canon_export_skip": ["spatial_pose", "orientation"], - "hidden_canon_properties": ["spatial_pose", "orientation"], - "property_units": { - "radius": "m", - "spacing": "m" - }, - "xml_enum_attrs": { - "type": { - "ue_property": "FlexcompType", - "ue_enum_type": "EMjFlexcompType", - "mjs_field": "type", - "mjs_cast": "int", - "value_map": { - "grid": ["Grid", "0"], - "box": ["Box", "1"], - "cylinder": ["Cylinder", "2"], - "ellipsoid": ["Ellipsoid", "3"], - "square": ["Square", "4"], - "disc": ["Disc", "5"], - "circle": ["Circle", "6"], - "mesh": ["Mesh", "7"], - "gmsh": ["Gmsh", "8"], - "direct": ["Direct", "9"] - } - }, - "dof": { - "ue_property": "FlexcompDof", - "ue_enum_type": "EMjFlexcompDof", - "mjs_field": "dof", - "mjs_cast": "int", - "value_map": { - "full": ["Full", "0"], - "radial": ["Radial", "1"], - "trilinear": ["Trilinear", "2"], - "quadratic": ["Quadratic", "3"], - "2d": ["TwoD", "4"] - } - } - } - }, - "freejoint": { - }, - "contact_pair": { - "common_imports": [{"attr": "name", "ue_field": "Name"}], - "property_units": { - "margin": "m", - "gap": "m" - } - }, - "contact_exclude": { - "common_imports": [{"attr": "name", "ue_field": "Name"}] - }, - "keyframe": { - "_note": "qpos/qvel/act/ctrl/mpos/mquat target mjsKey's mjDoubleVec* fields (std::vector*) — require ->clear()+->push_back() rather than direct assignment. Codegen emits the import (TArray read) + the mjs_double_vec-style export. RegisterToSpec hand-rolls freejoint-padding for qpos/qvel.", - "mjs_double_vec_attrs": ["qpos", "qvel", "act", "ctrl", "mpos", "mquat"] - } - }, - - "categories": { - "actuator": { - "layout": "single_uclass_per_file", - "base_class_name": "UMjActuator", - "base_class_header": "MuJoCo/Components/Actuators/MjActuator.h", - "subclass_directory_pub": "MuJoCo/Components/Actuators", - "subclass_directory_priv": "MuJoCo/Components/Actuators", - "mjs_struct": "mjsActuator", - "schema_common_block": "actuator_common.attrs", - "schema_common_extra_attrs": ["gainprm", "biasprm", "dynprm", "gaintype", "biastype", "dyntype", "actdim"], - "schema_subtypes_block": "actuator_types", - "type_enum_name": "EMjActuatorType", - "subtypes": [ - {"key": "general", "enum_value": "Motor", "class_name": "UMjGeneralActuator", "header": "MjGeneralActuator.h"}, - {"key": "motor", "enum_value": "Motor", "class_name": "UMjMotorActuator", "header": "MjMotorActuator.h"}, - {"key": "position", "enum_value": "Position", "class_name": "UMjPositionActuator", "header": "MjPositionActuator.h"}, - {"key": "velocity", "enum_value": "Velocity", "class_name": "UMjVelocityActuator", "header": "MjVelocityActuator.h"}, - {"key": "intvelocity", "enum_value": "IntVelocity","class_name": "UMjIntVelocityActuator", "header": "MjIntVelocityActuator.h"}, - {"key": "damper", "enum_value": "Damper", "class_name": "UMjDamperActuator", "header": "MjDamperActuator.h"}, - {"key": "cylinder", "enum_value": "Cylinder", "class_name": "UMjCylinderActuator", "header": "MjCylinderActuator.h"}, - {"key": "muscle", "enum_value": "Muscle", "class_name": "UMjMuscleActuator", "header": "MjMuscleActuator.h"}, - {"key": "adhesion", "enum_value": "Adhesion", "class_name": "UMjAdhesionActuator", "header": "MjAdhesionActuator.h"}, - {"key": "dcmotor", "enum_value": "DcMotor", "class_name": "UMjDcMotorActuator", "header": "MjDcMotorActuator.h"} - ], - "_note_subtype_setto": "Codegen-driven mjs_setTo* call config. Function signatures come from the clang-AST introspect snapshot, projected by _mjspec_from_introspect; the generator emits the call from the signature + the per-subtype config below. NO hand-written C++ literals in this file.", - "_note_inherit_fields": "MuJoCo's mjs_setTo* write some params UNCONDITIONALLY by value (e.g. gainprm[0] = kp), so there is no sentinel meaning 'unset' — passing one clobbers the value the default class already put on the element via mjs_addActuator. For those params, `inherit_fields` names the C++ expression the generator re-asserts when the UPROPERTY isn't overridden, preserving the inherited value. `nullable_params` are pointer args (double x[1]) MuJoCo skips on nullptr; passing nullptr when unset is MuJoCo's actual 'leave default alone' convention (its own Python binding maps -1 -> nullptr). Muscle/dcmotor are unaffected: muscle guards every param with `>= 0` so the -1.0 sentinel is correct, and dcmotor already passes nullptr for unset arrays.", - "subtype_setto": { - "general": null, - "motor": {"call": "mjs_setToMotor"}, - "position": {"call": "mjs_setToPosition", - "inherit_fields": {"kp": "Element->gainprm[0]"}, - "nullable_params": ["kv", "dampratio", "timeconst"]}, - "velocity": {"call": "mjs_setToVelocity", - "inherit_fields": {"kv": "Element->gainprm[0]"}}, - "intvelocity": {"call": "mjs_setToIntVelocity", - "inherit_fields": {"kp": "Element->gainprm[0]"}, - "nullable_params": ["kv", "dampratio", "timeconst"]}, - "_todo_damper_inherit": "TODO(jonat): decide fidelity vs correctness for damper's inherited kv. MuJoCo's xml_native_reader.cc OneActuator() seeds `double kv = 0` for the damper case (all sibling types seed from the inherited field, e.g. position `kp = actuator->gainprm[0]`), so mj_loadXML SILENTLY DROPS a damper's class-inherited kv to 0 — verified a bug, present in upstream main. URLab's inherit_fields below re-asserts -gainprm[2], so URLab is MORE correct (keeps kv) but DIVERGES from mj_loadXML, which can break UE-vs-bridge sim2sim. Revisit: either match MuJoCo bug-for-bug for fidelity, or upstream a fix (seed `kv = -actuator->gainprm[2]`). See MjActuatorInheritanceTests InheritedDamperKv_Preserved.", - "damper": {"call": "mjs_setToDamper", - "inherit_fields": {"kv": "-Element->gainprm[2]"}}, - "cylinder": {"call": "mjs_setToCylinder", - "inherit_fields": {"timeconst": "Element->dynprm[0]", "bias": "Element->biasprm[0]", "area": "Element->gainprm[0]"}}, - "muscle": {"call": "mjs_setToMuscle", - "projections": {"scale": "X"}}, - "adhesion": {"call": "mjs_setToAdhesion", - "inherit_fields": {"gain": "Element->gainprm[0]"}}, - "dcmotor": {"call": "mjs_setToDCMotor", - "nullable_arrays": true, - "use_filldouble": true, - "param_renames": {"input_mode": "input"}} - } - }, - - "sensor": { - "layout": "single_uclass_per_file", - "base_class_name": "UMjSensor", - "base_class_header": "MuJoCo/Components/Sensors/MjSensor.h", - "subclass_directory_pub": "MuJoCo/Components/Sensors", - "subclass_directory_priv": "MuJoCo/Components/Sensors", - "mjs_struct": "mjsSensor", - "schema_common_block": "sensor_common.attrs", - "schema_subtypes_block": "sensor_types", - "type_enum_name": "EMjSensorType", - "subtypes": [ - {"key": "touch", "enum_value": "Touch", "class_name": "UMjTouchSensor", "header": "MjTouchSensor.h", "fully_emitted": true}, - {"key": "accelerometer", "enum_value": "Accelerometer", "class_name": "UMjAccelerometer", "header": "MjAccelerometer.h", "fully_emitted": true}, - {"key": "velocimeter", "enum_value": "Velocimeter", "class_name": "UMjVelocimeter", "header": "MjVelocimeter.h", "fully_emitted": true}, - {"key": "gyro", "enum_value": "Gyro", "class_name": "UMjGyro", "header": "MjGyro.h", "fully_emitted": true}, - {"key": "force", "enum_value": "Force", "class_name": "UMjForceSensor", "header": "MjForceSensor.h", "fully_emitted": true}, - {"key": "torque", "enum_value": "Torque", "class_name": "UMjTorqueSensor", "header": "MjTorqueSensor.h", "fully_emitted": true}, - {"key": "magnetometer", "enum_value": "Magnetometer", "class_name": "UMjMagnetometer", "header": "MjMagnetometer.h", "fully_emitted": true}, - {"key": "camprojection", "enum_value": "CamProjection", "class_name": "UMjCamProjectionSensor", "header": "MjCamProjectionSensor.h", "fully_emitted": true}, - {"key": "rangefinder", "enum_value": "RangeFinder", "class_name": "UMjRangeFinderSensor", "header": "MjRangeFinderSensor.h", - "case_body_override": "Element->type = mjSENS_RANGEFINDER; Element->objtype = (ObjType == EMjObjType::Camera) ? mjOBJ_CAMERA : mjOBJ_SITE;", - "fully_emitted": true}, - {"key": "jointpos", "enum_value": "JointPos", "class_name": "UMjJointPosSensor", "header": "MjJointPosSensor.h"}, - {"key": "jointvel", "enum_value": "JointVel", "class_name": "UMjJointVelSensor", "header": "MjJointVelSensor.h", "fully_emitted": true}, - {"key": "ballquat", "enum_value": "BallQuat", "class_name": "UMjBallQuatSensor", "header": "MjBallQuatSensor.h", "fully_emitted": true}, - {"key": "ballangvel", "enum_value": "BallAngVel", "class_name": "UMjBallAngVelSensor", "header": "MjBallAngVelSensor.h", "fully_emitted": true}, - {"key": "jointlimitpos", "enum_value": "JointLimitPos", "class_name": "UMjJointLimitPosSensor", "header": "MjJointLimitPosSensor.h", "fully_emitted": true}, - {"key": "jointlimitvel", "enum_value": "JointLimitVel", "class_name": "UMjJointLimitVelSensor", "header": "MjJointLimitVelSensor.h", "fully_emitted": true}, - {"key": "jointlimitfrc", "enum_value": "JointLimitFrc", "class_name": "UMjJointLimitFrcSensor", "header": "MjJointLimitFrcSensor.h", "fully_emitted": true}, - {"key": "tendonpos", "enum_value": "TendonPos", "class_name": "UMjTendonPosSensor", "header": "MjTendonPosSensor.h", "fully_emitted": true}, - {"key": "tendonvel", "enum_value": "TendonVel", "class_name": "UMjTendonVelSensor", "header": "MjTendonVelSensor.h", "fully_emitted": true}, - {"key": "tendonlimitpos", "enum_value": "TendonLimitPos", "class_name": "UMjTendonLimitPosSensor", "header": "MjTendonLimitPosSensor.h", "fully_emitted": true}, - {"key": "tendonlimitvel", "enum_value": "TendonLimitVel", "class_name": "UMjTendonLimitVelSensor", "header": "MjTendonLimitVelSensor.h", "fully_emitted": true}, - {"key": "tendonlimitfrc", "enum_value": "TendonLimitFrc", "class_name": "UMjTendonLimitFrcSensor", "header": "MjTendonLimitFrcSensor.h", "fully_emitted": true}, - {"key": "actuatorpos", "enum_value": "ActuatorPos", "class_name": "UMjActuatorPosSensor", "header": "MjActuatorPosSensor.h", "fully_emitted": true}, - {"key": "actuatorvel", "enum_value": "ActuatorVel", "class_name": "UMjActuatorVelSensor", "header": "MjActuatorVelSensor.h", "fully_emitted": true}, - {"key": "actuatorfrc", "enum_value": "ActuatorFrc", "class_name": "UMjActuatorFrcSensor", "header": "MjActuatorFrcSensor.h", "fully_emitted": true}, - {"key": "jointactuatorfrc", "enum_value": "JointActFrc", "class_name": "UMjJointActFrcSensor", "header": "MjJointActFrcSensor.h", "fully_emitted": true}, - {"key": "tendonactuatorfrc","enum_value": "TendonActFrc", "class_name": "UMjTendonActFrcSensor", "header": "MjTendonActFrcSensor.h", "fully_emitted": true}, - {"key": "framepos", "enum_value": "FramePos", "class_name": "UMjFramePosSensor", "header": "MjFramePosSensor.h", "fully_emitted": true}, - {"key": "framequat", "enum_value": "FrameQuat", "class_name": "UMjFrameQuatSensor", "header": "MjFrameQuatSensor.h", "fully_emitted": true}, - {"key": "framexaxis", "enum_value": "FrameXAxis", "class_name": "UMjFrameXAxisSensor", "header": "MjFrameXAxisSensor.h", "fully_emitted": true}, - {"key": "frameyaxis", "enum_value": "FrameYAxis", "class_name": "UMjFrameYAxisSensor", "header": "MjFrameYAxisSensor.h", "fully_emitted": true}, - {"key": "framezaxis", "enum_value": "FrameZAxis", "class_name": "UMjFrameZAxisSensor", "header": "MjFrameZAxisSensor.h", "fully_emitted": true}, - {"key": "framelinvel", "enum_value": "FrameLinVel", "class_name": "UMjFrameLinVelSensor", "header": "MjFrameLinVelSensor.h", "fully_emitted": true}, - {"key": "frameangvel", "enum_value": "FrameAngVel", "class_name": "UMjFrameAngVelSensor", "header": "MjFrameAngVelSensor.h", "fully_emitted": true}, - {"key": "framelinacc", "enum_value": "FrameLinAcc", "class_name": "UMjFrameLinAccSensor", "header": "MjFrameLinAccSensor.h", "fully_emitted": true}, - {"key": "frameangacc", "enum_value": "FrameAngAcc", "class_name": "UMjFrameAngAccSensor", "header": "MjFrameAngAccSensor.h", "fully_emitted": true}, - {"key": "subtreecom", "enum_value": "SubtreeCom", "class_name": "UMjSubtreeComSensor", "header": "MjSubtreeComSensor.h", "fully_emitted": true}, - {"key": "subtreelinvel", "enum_value": "SubtreeLinVel", "class_name": "UMjSubtreeLinVelSensor", "header": "MjSubtreeLinVelSensor.h", "fully_emitted": true}, - {"key": "subtreeangmom", "enum_value": "SubtreeAngMom", "class_name": "UMjSubtreeAngMomSensor", "header": "MjSubtreeAngMomSensor.h", "fully_emitted": true}, - {"key": "insidesite", "enum_value": "InsideSite", "class_name": "UMjInsideSiteSensor", "header": "MjInsideSiteSensor.h", "fully_emitted": true}, - {"key": "distance", "enum_value": "GeomDist", "class_name": "UMjGeomDistSensor", "header": "MjGeomDistSensor.h", "fully_emitted": true}, - {"key": "normal", "enum_value": "GeomNormal", "class_name": "UMjGeomNormalSensor", "header": "MjGeomNormalSensor.h", "fully_emitted": true}, - {"key": "fromto", "enum_value": "GeomFromTo", "class_name": "UMjGeomFromToSensor", "header": "MjGeomFromToSensor.h", "fully_emitted": true}, - {"key": "contact", "enum_value": "Contact", "class_name": "UMjContactSensor", "header": "MjContactSensor.h", "fully_emitted": true}, - {"key": "e_potential", "enum_value": "EPotential", "class_name": "UMjEPotentialSensor", "header": "MjEPotentialSensor.h", "fully_emitted": true}, - {"key": "e_kinetic", "enum_value": "EKinetic", "class_name": "UMjEKineticSensor", "header": "MjEKineticSensor.h", "fully_emitted": true}, - {"key": "clock", "enum_value": "Clock", "class_name": "UMjClockSensor", "header": "MjClockSensor.h", "fully_emitted": true}, - {"key": "tactile", "enum_value": "Tactile", "class_name": "UMjTactileSensor", "header": "MjTactileSensor.h"}, - {"key": "user", "enum_value": "User", "class_name": "UMjUserSensor", "header": "MjUserSensor.h", "fully_emitted": true}, - {"key": "plugin", "enum_value": "Plugin", "class_name": "UMjPluginSensor", "header": "MjPluginSensor.h", "fully_emitted": true} - ] - }, - - "joint": { - "layout": "single_uclass_per_file", - "base_class_name": "UMjJoint", - "base_class_header": "MuJoCo/Components/Joints/MjJoint.h", - "subclass_directory_pub": "MuJoCo/Components/Joints", - "subclass_directory_priv": "MuJoCo/Components/Joints", - "mjs_struct": "mjsJoint", - "schema_common_block": "joint.attrs", - "type_enum_name": "EMjJointType", - "subtypes": [ - {"key": "hinge", "enum_value": "Hinge", "class_name": "UMjHingeJoint", "header": "MjHingeJoint.h", "fully_emitted": true, - "_note_extra_ctor": "UMjJoint::ExportTo only writes Element->type when bOverride_Type is true. Without this flag set in the subclass ctor, BP-spawned joints fall back to the mjsJoint default (mjJNT_HINGE), silently substituting Ball/Slide joints for Hinge.", - "extra_constructor": "bOverride_Type = true;"}, - {"key": "slide", "enum_value": "Slide", "class_name": "UMjSlideJoint", "header": "MjSlideJoint.h", "fully_emitted": true, - "extra_constructor": "bOverride_Type = true;"}, - {"key": "ball", "enum_value": "Ball", "class_name": "UMjBallJoint", "header": "MjBallJoint.h", "fully_emitted": true, - "extra_constructor": "bOverride_Type = true;"}, - {"key": "free", "enum_value": "Free", "class_name": "UMjFreeJoint", "header": "MjFreeJoint.h"} - ] - }, - - "site": { - "layout": "multi_uclass", - "base_class_name": "UMjSite", - "base_class_header": "MuJoCo/Components/Geometry/MjSite.h", - "mjs_struct": "mjsSite", - "schema_common_block": "site.attrs", - "type_enum_name": "EMjSiteType", - "subtypes": [ - {"key": "box", "enum_value": "Box", "class_name": "UMjBoxSite"}, - {"key": "sphere", "enum_value": "Sphere", "class_name": "UMjSphereSite"}, - {"key": "capsule", "enum_value": "Capsule", "class_name": "UMjCapsuleSite"}, - {"key": "cylinder", "enum_value": "Cylinder", "class_name": "UMjCylinderSite"}, - {"key": "ellipsoid", "enum_value": "Ellipsoid", "class_name": "UMjEllipsoidSite"} - ] - }, - - "tendon": { - "layout": "multi_uclass", - "base_class_name": "UMjTendon", - "base_class_header": "MuJoCo/Components/Tendons/MjTendon.h", - "mjs_struct": "mjsTendon", - "schema_common_block": "tendon.spatial.attrs", - "type_enum_name": "EMjTendonType", - "subtypes": [ - {"key": "spatial", "enum_value": "Spatial", "class_name": "UMjSpatialTendon"}, - {"key": "fixed", "enum_value": "Fixed", "class_name": "UMjFixedTendon"} - ] - }, - - "equality": { - "layout": "multi_uclass", - "base_class_name": "UMjEquality", - "base_class_header": "MuJoCo/Components/Constraints/MjEquality.h", - "mjs_struct": "mjsEquality", - "schema_common_block": "equality.weld", - "schema_common_extra_attrs": ["polycoef"], - "type_enum_name": "EMjEqualityType", - "_note_objtype_dispatch": "mjsEquality.objtype tells MuJoCo what kind of object name1/name2 refer to. Connect/Weld are dual-mode: site-to-site if site1 was specified, else body-to-body. Codegen emits a switch on EqualityType into the CODEGEN_OBJTYPE_DISPATCH markers in MjEquality.cpp.", - "objtype_dispatch": { - "discriminator": "EqualityType", - "target": "Element->objtype", - "default": "mjOBJ_UNKNOWN", - "cases": [ - {"keys": ["Connect", "Weld"], "expr": "!site1.IsEmpty() ? mjOBJ_SITE : mjOBJ_BODY"}, - {"keys": ["Joint"], "expr": "mjOBJ_JOINT"}, - {"keys": ["Tendon"], "expr": "mjOBJ_TENDON"}, - {"keys": ["Flex", "FlexVert", "FlexStrain"], "expr": "mjOBJ_FLEX"} - ] - }, - "subtypes": [ - {"key": "connect", "enum_value": "Connect", "class_name": "UMjConnectEquality"}, - {"key": "weld", "enum_value": "Weld", "class_name": "UMjWeldEquality"}, - {"key": "joint", "enum_value": "Joint", "class_name": "UMjJointEquality"}, - {"key": "tendon", "enum_value": "Tendon", "class_name": "UMjTendonEquality"}, - {"key": "flex", "enum_value": "Flex", "class_name": "UMjFlexEquality"}, - {"key": "flexvert", "enum_value": "FlexVert", "class_name": "UMjFlexVertEquality"}, - {"key": "flexstrain", "enum_value": "FlexStrain", "class_name": "UMjFlexStrainEquality"} - ] - }, - - "geom": { - "layout": "single_uclass_per_file", - "base_class_name": "UMjGeom", - "base_class_header": "MuJoCo/Components/Geometry/MjGeom.h", - "subclass_directory_pub": "MuJoCo/Components/Geometry/Primitives", - "subclass_directory_priv": "MuJoCo/Components/Geometry/Primitives", - "mjs_struct": "mjsGeom", - "schema_common_block": "geom.attrs", - "type_enum_name": "EMjGeomType", - "_note_geom_final_type": "Mesh-name export depends on the COMPILE-TIME geom type, which is either the UPROPERTY Type (if overridden) or the Default geom's type (otherwise). Codegen emits the resolution + the conditional mesh-name write into the CODEGEN_GEOM_FINAL_TYPE markers in MjGeom.cpp. The value_map is pulled from element_rules.geom.xml_enum_attrs.type so there's only one source of truth for the XML/UE/mj triplet.", - "geom_final_type": { - "xml_enum_ref": "type", - "override_field": "bOverride_Type", - "default_lookup": "Default->geom->type", - "default_fallback": "mjGEOM_MESH", - "name_field": "MeshName", - "name_export_for": "mjGEOM_MESH", - "name_target": "Element->meshname", - "name_setter": "MjSetStringRaw" - }, - "subtypes": [ - {"key": "box", "enum_value": "Box", "class_name": "UMjBox", "header": "MjBox.h"}, - {"key": "sphere", "enum_value": "Sphere", "class_name": "UMjSphere", "header": "MjSphere.h"}, - {"key": "capsule", "enum_value": "Capsule", "class_name": "UMjCapsule", "header": "MjCapsule.h"}, - {"key": "cylinder", "enum_value": "Cylinder", "class_name": "UMjCylinder", "header": "MjCylinder.h"}, - {"key": "ellipsoid", "enum_value": "Ellipsoid", "class_name": "UMjEllipsoid", "header": "MjEllipsoid.h", "fully_emitted": true}, - {"key": "plane", "enum_value": "Plane", "class_name": "UMjPlane", "header": "MjPlane.h", "fully_emitted": true}, - {"key": "sdf", "enum_value": "SDF", "class_name": "UMjSdf", "header": "MjSdf.h", "fully_emitted": true} - ], - "_note_skipped_subtypes": ["hfield (URLab has its own hfield actor system)", "mesh (existing UMjMeshGeom)"] - }, - - "camera": { - "layout": "no_subclasses", - "base_class_name": "UMjCamera", - "base_class_header": "MuJoCo/Components/Sensors/MjCamera.h", - "mjs_struct": "mjsCamera", - "schema_common_block": "camera.attrs" - }, - - "flexcomp": { - "_note": "flexcomp doesn't register via mjs_addX — at spec time UMjFlexcomp builds a tiny standalone MJCF fragment and re-parses it via mj_parseXMLString + mjs_attach (the flexcomp macro is expanded by MuJoCo's XML parser, not the spec API). xml_passthrough_emission flips the export side from mjs writes to MJCF attribute-string writes (BuildSchemaAttrsXml), so any new schema attr flows end-to-end without a hand-edit.", - "layout": "no_subclasses", - "base_class_name": "UMjFlexcomp", - "base_class_header": "MuJoCo/Components/Deformable/MjFlexcomp.h", - "mjs_struct": "mjsFlex", - "schema_common_block": "flexcomp.attrs", - "xml_passthrough_emission": true - }, - - "body": { - "layout": "no_subclasses", - "base_class_name": "UMjBody", - "base_class_header": "MuJoCo/Components/Bodies/MjBody.h", - "mjs_struct": "mjsBody", - "schema_common_block": "body.attrs" - }, - - "frame": { - "_note": "MJCF nests inside / and supplies a coordinate offset that the MuJoCo compiler bakes into the pos/quat of its child elements. UE-side the frame is a USceneComponent with a relative transform; MjFrame::Setup walks children and dispatches RegisterToSpec. The codegen-emitted Import populates Pos/Quat UPROPERTYs from XML — but the source of truth for placement is the UE transform (set via SetRelativeLocation/Rotation in ImportFromXml), so canon export is skipped for spatial_pose/orientation. childclass is codegen-owned (FString -> mjs_setString).", - "layout": "no_subclasses", - "base_class_name": "UMjFrame", - "base_class_header": "MuJoCo/Components/Bodies/MjFrame.h", - "mjs_struct": "mjsFrame", - "schema_common_block": "frame.attrs", - "schema_common_extra_attrs": ["pos", "quat", "axisangle", "euler", "xyaxes", "zaxis", "childclass"] - }, - - "inertial": { - "_note_export": "MJCF has no corresponding mjsInertial struct; the export path writes to ParentBody->{mass,inertia,fullinertia,ipos,iquat} in a hand-rolled UMjInertial::RegisterToSpec. disable_mjs_export_emission suppresses the codegen-emitted Element->* export block so the hand-rolled path stays canonical.", - "layout": "no_subclasses", - "base_class_name": "UMjInertial", - "base_class_header": "MuJoCo/Components/Physics/MjInertial.h", - "mjs_struct": "mjsInertial", - "schema_common_block": "inertial.attrs", - "disable_mjs_export_emission": true - }, - - "contact_pair": { - "layout": "no_subclasses", - "base_class_name": "UMjContactPair", - "base_class_header": "MuJoCo/Components/Physics/MjContactPair.h", - "mjs_struct": "mjsPair", - "schema_common_block": "contact.pair" - }, - - "contact_exclude": { - "layout": "no_subclasses", - "base_class_name": "UMjContactExclude", - "base_class_header": "MuJoCo/Components/Physics/MjContactExclude.h", - "mjs_struct": "mjsExclude", - "schema_common_block": "contact.exclude" - }, - - "keyframe": { - "layout": "no_subclasses", - "_note": "qpos/qvel/etc are mjDoubleVec* on mjsKey, handled by the mjs_double_vec_attrs rule in element_rules.keyframe. Time + the override toggles are codegen-owned. RegisterToSpec hand-rolls the freejoint padding (which post-mutates Element->qpos/qvel after ExportTo).", - "base_class_name": "UMjKeyframe", - "base_class_header": "MuJoCo/Components/Keyframes/MjKeyframe.h", - "mjs_struct": "mjsKey", - "schema_common_block": "keyframe.key" - } - }, - - "views": { - "BodyView": { - "include_blocks": ["MJMODEL_POINTERS_BODY", "MJMODEL_POINTERS_TREE"], - "data_fields": { - "xpos": {"index_var": "id", "stride": "3"}, - "xquat": {"index_var": "id", "stride": "4"}, - "xmat": {"index_var": "id", "stride": "9"}, - "cvel": {"index_var": "id", "stride": "6"}, - "cinert": {"index_var": "id", "stride": "10"}, - "xfrc_applied": {"index_var": "id", "stride": "6"} - } - }, - "JointView": { - "include_blocks": ["MJMODEL_POINTERS_JOINT", "MJMODEL_POINTERS_DOF"], - "derived_index_vars": { - "qpos_adr": "m->jnt_qposadr[id]", - "dof_adr": "m->jnt_dofadr[id]" - }, - "block_index_var": { - "MJMODEL_POINTERS_JOINT": "id", - "MJMODEL_POINTERS_DOF": "dof_adr" - }, - "data_fields": { - "qpos": {"index_var": "qpos_adr", "stride": "1"}, - "qvel": {"index_var": "dof_adr", "stride": "1"}, - "qacc": {"index_var": "dof_adr", "stride": "1"}, - "xanchor": {"index_var": "id", "stride": "3"}, - "xaxis": {"index_var": "id", "stride": "3"} - } - }, - "GeomView": { - "include_blocks": ["MJMODEL_POINTERS_GEOM"], - "data_fields": { - "geom_xpos": {"index_var": "id", "stride": "3"}, - "geom_xmat": {"index_var": "id", "stride": "9"} - } - }, - "SiteView": { - "include_blocks": ["MJMODEL_POINTERS_SITE"], - "data_fields": { - "site_xpos": {"index_var": "id", "stride": "3"}, - "site_xmat": {"index_var": "id", "stride": "9"} - } - }, - "ActuatorView": { - "include_blocks": ["MJMODEL_POINTERS_ACTUATOR"], - "data_fields": { - "ctrl": {"index_var": "id", "stride": "1"}, - "actuator_force": {"index_var": "id", "stride": "1"}, - "actuator_length":{"index_var": "id", "stride": "1"}, - "actuator_velocity":{"index_var": "id", "stride": "1"}, - "_note_moment": "Per-actuator row of the actuator_moment (nu x nv) matrix. Stride is m->nv, which is a valid stride expression — no bind_override needed.", - "actuator_moment": {"index_var": "id", "stride": "m->nv"}, - "_note_act": "act lives in d->act indexed by m->actuator_actadr[id]. The adr can be -1 for actuators without an internal activation state; bind_override emits the nullable lookup so consumers can null-check ``act`` directly.", - "act": {"bind_override": "act = (m->actuator_actadr[id] >= 0) ? d->act + m->actuator_actadr[id] : nullptr;"} - } - }, - "TendonView": { - "include_blocks": ["MJMODEL_POINTERS_TENDON"], - "data_fields": { - "ten_length": {"index_var": "id", "stride": "1"}, - "ten_velocity": {"index_var": "id", "stride": "1"} - } - }, - "SensorView": { - "include_blocks": ["MJMODEL_POINTERS_SENSOR"], - "data_fields": { - "_note_sensordata": "Bounds-checked slice — the default base+id*stride formula is wrong for sensordata (it would double-multiply adr*dim). bind_override is a verbatim C++ statement the codegen drops inside the BIND marker.", - "sensordata": {"bind_override": "sensordata = (sensor_adr >= 0 && sensor_adr < m->nsensordata) ? d->sensordata + sensor_adr : nullptr;"} - } - } - }, - - "_note_editor_option_helpers": "Codegens trivial UMjX::GetYOptions() editor wrappers, injecting them between CODEGEN_EDITOR_OPTIONS_START/END markers inside each host .cpp's #if WITH_EDITOR block. Wrappers with real logic (TransmissionType / EqualityType / GetClassForObjType switches) stay hand-written in the same #if block.", - "editor_option_helpers": { - "wrappers": [ - {"class": "UMjActuator", "host_cpp": "MuJoCo/Components/Actuators/MjActuator.cpp", - "method": "GetSliderSiteOptions", "filter_class": "UMjSite"}, - {"class": "UMjActuator", "host_cpp": "MuJoCo/Components/Actuators/MjActuator.cpp", - "method": "GetRefSiteOptions", "filter_class": "UMjSite"}, - {"class": "UMjActuator", "host_cpp": "MuJoCo/Components/Actuators/MjActuator.cpp", - "method": "GetDefaultClassOptions", "filter_class": "UMjDefault", "include_defaults": true}, - {"class": "UMjBody", "host_cpp": "MuJoCo/Components/Bodies/MjBody.cpp", - "method": "GetChildClassOptions", "filter_class": "UMjDefault", "include_defaults": true}, - {"class": "UMjGeom", "host_cpp": "MuJoCo/Components/Geometry/MjGeom.cpp", - "method": "GetDefaultClassOptions", "filter_class": "UMjDefault", "include_defaults": true}, - {"class": "UMjSite", "host_cpp": "MuJoCo/Components/Geometry/MjSite.cpp", - "method": "GetDefaultClassOptions", "filter_class": "UMjDefault", "include_defaults": true}, - {"class": "UMjJoint", "host_cpp": "MuJoCo/Components/Joints/MjJoint.cpp", - "method": "GetDefaultClassOptions", "filter_class": "UMjDefault", "include_defaults": true}, - {"class": "UMjContactExclude","host_cpp": "MuJoCo/Components/Physics/MjContactExclude.cpp", - "method": "GetBodyOptions", "filter_class": "UMjBody"}, - {"class": "UMjContactPair", "host_cpp": "MuJoCo/Components/Physics/MjContactPair.cpp", - "method": "GetGeomOptions", "filter_class": "UMjGeom"}, - {"class": "UMjSensor", "host_cpp": "MuJoCo/Components/Sensors/MjSensor.cpp", - "method": "GetDefaultClassOptions", "filter_class": "UMjDefault", "include_defaults": true}, - {"class": "UMjTendon", "host_cpp": "MuJoCo/Components/Tendons/MjTendon.cpp", - "method": "GetDefaultClassOptions", "filter_class": "UMjDefault", "include_defaults": true} - ] - }, - - "_note_generated_enums": "Banner-mode .h with one or more UENUMs derived from the introspect snapshot's mjt* enums (projected into mjspec.enums by _mjspec_from_introspect). Hand-edits get clobbered on next regen. ``extra_members`` appends URLab-specific values that have no mjt counterpart (e.g. DcMotor).", - "generated_enums": { - "MjOptionEnums": { - "public_header": "MuJoCo/Generated/MjOptionEnums.h", - "enums": [ - { - "ue_name": "EMjIntegrator", - "from_mj_enum": "mjtIntegrator", - "ue_member_from_mj": { - "mjINT_EULER": "Euler", - "mjINT_RK4": "RK4", - "mjINT_IMPLICIT": "Implicit", - "mjINT_IMPLICITFAST": "ImplicitFast" - } - }, - { - "ue_name": "EMjCone", - "from_mj_enum": "mjtCone", - "ue_member_from_mj": { - "mjCONE_PYRAMIDAL": "Pyramidal", - "mjCONE_ELLIPTIC": "Elliptic" - } - }, - { - "ue_name": "EMjSolver", - "from_mj_enum": "mjtSolver", - "ue_member_from_mj": { - "mjSOL_PGS": "PGS", - "mjSOL_CG": "CG", - "mjSOL_NEWTON": "Newton" - } - } - ] - }, - "_note_MjArticulationEnums": "Pure value-mapping enums emitted as MuJoCo/Generated/MjArticulationEnums.h. Components that need them (MjActuator.h, MjGeom.h, MjCameraTypes.h, MjSensor.h, MjFlexcomp.h) include this header.", - "MjArticulationEnums": { - "public_header": "MuJoCo/Generated/MjArticulationEnums.h", - "enums": [ - { - "ue_name": "EMjGainType", - "from_mj_enum": "mjtGain", - "ue_member_from_mj": { - "mjGAIN_FIXED": "Fixed", - "mjGAIN_AFFINE": "Affine", - "mjGAIN_MUSCLE": "Muscle", - "mjGAIN_DCMOTOR": "DcMotor", - "mjGAIN_USER": "User" - } - }, - { - "ue_name": "EMjBiasType", - "from_mj_enum": "mjtBias", - "ue_member_from_mj": { - "mjBIAS_NONE": "None", - "mjBIAS_AFFINE": "Affine", - "mjBIAS_MUSCLE": "Muscle", - "mjBIAS_DCMOTOR": "DcMotor", - "mjBIAS_USER": "User" - } - }, - { - "ue_name": "EMjDynType", - "from_mj_enum": "mjtDyn", - "ue_member_from_mj": { - "mjDYN_NONE": "None", - "mjDYN_INTEGRATOR": "Integrator", - "mjDYN_FILTER": "Filter", - "mjDYN_FILTEREXACT": "FilterExact", - "mjDYN_MUSCLE": "Muscle", - "mjDYN_DCMOTOR": "DcMotor", - "mjDYN_USER": "User" - } - }, - { - "_note_EMjGeomInertia": "No mjt counterpart; MJCF geom inertia kind is parser-side enum string-mapped on import. Pure extra_members.", - "ue_name": "EMjGeomInertia", - "extra_members": [ - {"name": "Volume", "value": 0}, - {"name": "Shell", "value": 1} - ] - }, - { - "_note_EMjFluidShape": "URLab-only; MuJoCo fluid shape is a runtime computation, not a public enum. Pure extra_members.", - "ue_name": "EMjFluidShape", - "extra_members": [ - {"name": "None", "value": 0}, - {"name": "Ellipsoid", "value": 1} - ] - }, - { - "_note_EMjCameraTrackingMode": "Mirrors mjtCamLight but only the camera-side subset; values come from mjCAMLIGHT_* keyed by URLab member name.", - "ue_name": "EMjCameraTrackingMode", - "from_mj_enum": "mjtCamLight", - "ue_member_from_mj": { - "mjCAMLIGHT_FIXED": "Fixed", - "mjCAMLIGHT_TRACK": "Track", - "mjCAMLIGHT_TRACKCOM": "TrackCom", - "mjCAMLIGHT_TARGETBODY": "TargetBody", - "mjCAMLIGHT_TARGETBODYCOM": "TargetBodyCom" - } - }, - { - "ue_name": "EMjCameraProjection", - "from_mj_enum": "mjtProjection", - "ue_member_from_mj": { - "mjPROJ_PERSPECTIVE": "Perspective", - "mjPROJ_ORTHOGRAPHIC": "Orthographic" - } - }, - { - "ue_name": "EMjObjType", - "from_mj_enum": "mjtObj", - "exclude_mj_members": ["mjNOBJECT", "mjOBJ_DEFAULT", "mjOBJ_MODEL"], - "ue_member_from_mj": { - "mjOBJ_UNKNOWN": "Unknown", - "mjOBJ_BODY": "Body", - "mjOBJ_XBODY": "XBody", - "mjOBJ_JOINT": "Joint", - "mjOBJ_DOF": "DoF", - "mjOBJ_GEOM": "Geom", - "mjOBJ_SITE": "Site", - "mjOBJ_CAMERA": "Camera", - "mjOBJ_LIGHT": "Light", - "mjOBJ_FLEX": "Flex", - "mjOBJ_MESH": "Mesh", - "mjOBJ_SKIN": "Skin", - "mjOBJ_HFIELD": "HField", - "mjOBJ_TEXTURE": "Texture", - "mjOBJ_MATERIAL": "Material", - "mjOBJ_PAIR": "Pair", - "mjOBJ_EXCLUDE": "Exclude", - "mjOBJ_EQUALITY": "Equality", - "mjOBJ_TENDON": "Tendon", - "mjOBJ_ACTUATOR": "Actuator", - "mjOBJ_SENSOR": "Sensor", - "mjOBJ_NUMERIC": "Numeric", - "mjOBJ_TEXT": "Text", - "mjOBJ_TUPLE": "Tuple", - "mjOBJ_KEY": "Key", - "mjOBJ_PLUGIN": "Plugin", - "mjOBJ_FRAME": "Frame" - } - }, - { - "_note_EMjActuatorTrnType": "mjTRN_UNDEFINED has C value 1000 (way out of uint8 range), so it can't be a direct ue_member_from_mj entry. Excluded from the snapshot pass and reintroduced as an extra_member with the next contiguous slot (6).", - "ue_name": "EMjActuatorTrnType", - "from_mj_enum": "mjtTrn", - "exclude_mj_members": ["mjTRN_UNDEFINED"], - "ue_member_from_mj": { - "mjTRN_JOINT": "Joint", - "mjTRN_JOINTINPARENT": "JointInParent", - "mjTRN_SLIDERCRANK": "SliderCrank", - "mjTRN_TENDON": "Tendon", - "mjTRN_SITE": "Site", - "mjTRN_BODY": "Body" - }, - "extra_members": [ - {"name": "Undefined", "value": 6} - ] - }, - { - "_note_EMjFlexcompDof": "URLab-only flex-component DoF reduction kind; no mjt counterpart (the MJCF attr is `dof` and parsed string-side). Pure extra_members; matches the pre-migration hand-rolled set.", - "ue_name": "EMjFlexcompDof", - "extra_members": [ - {"name": "Full", "value": 0}, - {"name": "Radial", "value": 1}, - {"name": "Trilinear", "value": 2}, - {"name": "Quadratic", "value": 3}, - {"name": "TwoD", "value": 4} - ] - } - ] - } - }, - - "_note_synthetic_categories": "Whole-file banner-mode emission for USTRUCTs mirroring C structs from mjxmacro_block. Hand-edits to generated files get clobbered on next regen. See generate_ue_components.py::_phase_synthetic_categories.", - "synthetic_categories": { - "MjStatistic": { - "ue_struct_name": "FMjStatistic", - "mjxmacro_block": "MJSTATISTIC_FIELDS", - "c_struct_type": "mjStatistic", - "public_header": "MuJoCo/Generated/MjStatistic.h", - "private_source": "MuJoCo/Generated/MjStatistic.cpp", - "category_label": "Statistic" - }, - - "_note_MjOption": "Codegen-emitted FMjOptionGenerated mirrors mjOption (MJOPTION_FIELDS) with URLab extras (MemoryMB, bEnableMultiCCD, bEnableSleep, SleepTolerance). AMjArticulation::SimOptions and UMjPhysicsEngine::SimOptions point at this struct directly.", - "MjOption": { - "ue_struct_name": "FMjOptionGenerated", - "mjxmacro_block": "MJOPTION_FIELDS", - "c_struct_type": "mjOption", - "public_header": "MuJoCo/Generated/MjOptionGenerated.h", - "private_source": "MuJoCo/Generated/MjOptionGenerated.cpp", - "category_label": "MjOption", - "include_directives": [ - "mujoco/mujoco.h", - "MuJoCo/Generated/MjOptionEnums.h" - ], - "field_renames": { - "timestep": "Timestep", - "impratio": "Impratio", - "tolerance": "Tolerance", - "ls_tolerance": "LsTolerance", - "noslip_tolerance": "NoslipTolerance", - "ccd_tolerance": "CCD_Tolerance", - "sdf_initpoints": "SdfInitPoints", - "sdf_iterations": "SdfIterations", - "iterations": "Iterations", - "ls_iterations": "LsIterations", - "noslip_iterations": "NoslipIterations", - "ccd_iterations": "CCD_Iterations", - "disableflags": "DisableFlags", - "enableflags": "EnableFlags", - "disableactuator": "DisableActuator", - "apirate": "ApiRate", - "sleep_tolerance": "SleepToleranceMj", - "rebuild_interval": "RebuildInterval" - }, - "field_types": { - "timestep": "float", - "apirate": "float", - "integrator": "EMjIntegrator", - "cone": "EMjCone", - "solver": "EMjSolver" - }, - "field_defaults": { - "Timestep": "0.002", - "Impratio": "1.0", - "Tolerance": "1e-8", - "Iterations": "100", - "LsIterations": "50", - "NoslipTolerance": "1e-6", - "CCD_Tolerance": "1e-6", - "Gravity": "FVector(0.0f, 0.0f, -981.0f)", - "Magnetic": "FVector(0.0f, -0.5f, 0.0f)", - "Integrator": "EMjIntegrator::Euler", - "Cone": "EMjCone::Pyramidal", - "Solver": "EMjSolver::Newton" - }, - "field_apply_to_spec_mode": { - "gravity": "vec3_cm", - "wind": "vec3_cm", - "magnetic": "vec3_y_flip", - "integrator": "guarded", - "cone": "guarded", - "solver": "guarded", - "noslip_iterations": "guarded", - "noslip_tolerance": "guarded", - "ccd_iterations": "guarded", - "ccd_tolerance": "guarded" - }, - "_note_skip_vs_exclude": "field_skip_apply: emitted as UPROPERTY but not written to mjOption (apply body skips). exclude_fields: omitted from the struct entirely. exclude_fields takes precedence in iteration, so entries listed in both would be dead — keep them apart.", - "field_skip_apply": [ - "disableflags", "enableflags", "disableactuator", - "sdf_initpoints", "sdf_iterations", "sleep_tolerance" - ], - "exclude_fields": ["apirate", "rebuild_interval", "o_solref", "o_solimp", "o_friction"], - "urlab_extra_fields": [ - { - "name": "MemoryMB", - "type": "int32", - "default": "100", - "meta": "ClampMin=\"1\"", - "has_override": true - }, - { - "name": "bEnableMultiCCD", - "type": "bool", - "default": "false", - "sub_category": "Collision", - "meta": "ToolTip=\"Enable multi-contact convex collision detection. Useful for flat surfaces (e.g. mesh-mesh) where single-point contact causes sliding or wobbling.\"", - "has_override": false - }, - { - "name": "bEnableSleep", - "type": "bool", - "default": "false", - "sub_category": "Sleep", - "has_override": false - }, - { - "name": "SleepTolerance", - "type": "float", - "default": "1e-4f", - "sub_category": "Sleep", - "edit_condition": "bEnableSleep", - "has_override": false - } - ], - "apply_extras_function": "ApplyMjOptionExtras", - "emit_apply_methods": true - }, - - "_note_MjVisualStructs": "Codegen-emitted FMjVisual{Global,Headlight,Quality,Map,Scale,Rgba} parallel to mjVisual sub-blocks.", - "MjVisualGlobal": { - "ue_struct_name": "FMjVisualGlobal", - "mjxmacro_block": "MJVISUAL_GLOBAL_FIELDS", - "c_struct_type": "mjVisualGlobal", - "public_header": "MuJoCo/Generated/MjVisualGlobal.h", - "private_source": "MuJoCo/Generated/MjVisualGlobal.cpp", - "category_label": "Visual|Global", - "field_defaults": { - "Fovy": "45.0f", - "Ipd": "0.068f", - "Realtime": "1.0f" - } - }, - "MjVisualHeadlight": { - "ue_struct_name": "FMjVisualHeadlight", - "mjxmacro_block": "MJVISUAL_HEADLIGHT_FIELDS", - "c_struct_type": "mjVisualHeadlight", - "public_header": "MuJoCo/Generated/MjVisualHeadlight.h", - "private_source": "MuJoCo/Generated/MjVisualHeadlight.cpp", - "category_label": "Visual|Headlight", - "field_defaults": { - "Ambient": "FVector(0.1f, 0.1f, 0.1f)", - "Diffuse": "FVector(0.4f, 0.4f, 0.4f)", - "Specular": "FVector(0.5f, 0.5f, 0.5f)", - "Active": "1" - } - }, - "MjVisualQuality": { - "ue_struct_name": "FMjVisualQuality", - "mjxmacro_block": "MJVISUAL_QUALITY_FIELDS", - "c_struct_type": "mjVisualQuality", - "public_header": "MuJoCo/Generated/MjVisualQuality.h", - "category_label": "Visual|Quality", - "_note": "All quality fields are int in mjmodel.h (shadowsize=1024, offsamples=4, numslices=28, numstacks=16, numquads=4). mjxmacro carries no type info for these (bare X(name) form); explicit field_types override.", - "field_types": { - "shadowsize": "int32", "offsamples": "int32", "numslices": "int32", - "numstacks": "int32", "numquads": "int32" - }, - "field_defaults": { - "Shadowsize": "1024", "Offsamples": "4", "Numslices": "28", - "Numstacks": "16", "Numquads": "4" - } - }, - "MjVisualMap": { - "ue_struct_name": "FMjVisualMap", - "mjxmacro_block": "MJVISUAL_MAP_FIELDS", - "c_struct_type": "mjVisualMap", - "public_header": "MuJoCo/Generated/MjVisualMap.h", - "category_label": "Visual|Map", - "_note": "All map fields are float scalars in mjVisualMap (force, torque, stiffness, etc.). mjxmacro defaults them to mjtNum (double) which compiles cleanly via implicit conversion." - }, - "MjVisualScale": { - "ue_struct_name": "FMjVisualScale", - "mjxmacro_block": "MJVISUAL_SCALE_FIELDS", - "c_struct_type": "mjVisualScale", - "public_header": "MuJoCo/Generated/MjVisualScale.h", - "category_label": "Visual|Scale", - "_note": "Same as MjVisualMap — float scalars in mjmodel.h, mjxmacro defaults to mjtNum which compiles fine." - }, - "MjVisualRgba": { - "ue_struct_name": "FMjVisualRgba", - "mjxmacro_block": "MJVISUAL_RGBA_FIELDS", - "c_struct_type": "mjVisualRgba", - "public_header": "MuJoCo/Generated/MjVisualRgba.h", - "category_label": "Visual|Rgba", - "_note": "Every field is float[4] color in mjVisualRgba. mjxmacro's bare X(name) form drops that info; all_fields_type forces FLinearColor for the UPROPERTY + flips the ApplyTo emit to .R/.G/.B/.A writes.", - "all_fields_type": "FLinearColor" - }, - "_note_MjsCompilerOptions": "Mirror of mjsCompiler (MJSCOMPILER_FIELDS in mjspecmacro.h, read from the installed MuJoCo headers). Header-only USTRUCT — no .cpp emission today since URLab parses compiler settings via the legacy FMjCompilerSettings struct in MjOrientationUtils.h. Future migration: route MjArticulation through this struct + an ApplyToSpec method.", - "MjsCompilerOptions": { - "ue_struct_name": "FMjsCompilerOptions", - "mjxmacro_block": "MJSCOMPILER_FIELDS", - "c_struct_type": "mjsCompiler", - "public_header": "MuJoCo/Generated/MjsCompilerOptions.h", - "category_label": "Spec|Compiler", - "_note_exclude": "LRopt is mjLROpt (nested struct, not mirror-able as a UPROPERTY); meshdir/texturedir are mjString* (different shape — handled by the existing MeshDir/AssetDir on FMjCompilerSettings). eulerseq is a char[3] vec — mirror as FString instead, but skip in ApplyTo since FString -> char[3] needs a copy not a cast.", - "exclude_fields": ["LRopt", "meshdir", "texturedir"], - "field_skip_apply": ["eulerseq"], - "field_types": {"eulerseq": "FString"}, - "field_defaults": {"Eulerseq": "TEXT(\"xyz\")"} - }, - "_note_MjsSpec": "Mirror of mjSpec top-level fields (MJSPEC_FIELDS in mjspecmacro.h, read from the installed MuJoCo headers). Many entries are NESTED structs (mjsCompiler, mjOption, mjVisual, mjStatistic) — those are exposed via the dedicated FMjsCompilerOptions / FMjOptionGenerated / FMjVisual* / FMjStatistic structs. We only mirror the scalar + string fields here.", - "MjsSpec": { - "ue_struct_name": "FMjsSpec", - "mjxmacro_block": "MJSPEC_FIELDS", - "c_struct_type": "mjSpec", - "public_header": "MuJoCo/Generated/MjsSpec.h", - "category_label": "Spec", - "_note_exclude": "element/compiler/option/visual/stat are nested structs already exposed via their own synthetic_categories. modelname / comment / modelfiledir / meshdir / texturedir are mjString* — distinct shape. memory is mjtSize (handle separately).", - "exclude_fields": [ - "element", "compiler", "option", "visual", "stat", - "modelname", "comment", "modelfiledir", "meshdir", "texturedir", - "memory", "strippath" - ] - } - } -} diff --git a/Scripts/codegen/generate_ue_components.py b/Scripts/codegen/generate_ue_components.py deleted file mode 100644 index b09d7b74..00000000 --- a/Scripts/codegen/generate_ue_components.py +++ /dev/null @@ -1,4318 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -# -# 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 -""" -URLab UE component codegen, schema-driven. - -Inputs: - - Scripts/codegen/snapshots/mjcf_schema_snapshot.json (MJCF schema attrs) - - Scripts/codegen/snapshots/mjxmacro_snapshot.json (mjModel/mjData pointer table) - - Scripts/codegen/snapshots/introspect_snapshot.json (libclang-scraped C API: - mjsX struct fields, mjt* - enums, mjs_setTo* signatures) - - Scripts/codegen/codegen_rules.json (per-category layout, rules) - -Outputs: - - Component .h / .cpp files in Source/URLab/Public|Private/MuJoCo/Components/ - (single-UCLASS or multi-UCLASS depending on layout) - - View structs injected into Source/URLab/Public/MuJoCo/Utils/MjBind.h - - Schema-completeness header at - Source/URLabEditor/Private/Tests/MjSchemaForTests.h - -All emitted text lives between explicit ``// --- CODEGEN_*_START/END ---`` -tags. The generator refuses to inject into a file that does not have -the right tag pairs. There is no auto-insert fallback. - -Usage: - python generate_ue_components.py # write mode - python generate_ue_components.py --dry-run # print diffs only - python generate_ue_components.py --check # exit non-zero if writes would change anything -""" - -from __future__ import annotations - -import argparse -import difflib -import json -import os -import re -import shutil -import subprocess -import sys -from dataclasses import dataclass, field -from typing import Any, Callable, Dict, List, Optional, Sequence, Set, Tuple - -# Sibling modules. Only names actually consumed by this orchestrator's -# body are imported here. Test files import private helpers (e.g. -# _resolve_direct, _classify_c_type, _check_brace_balance) directly from -# their owning module — the orchestrator is not a re-export surface. -from _codegen_core import ( - _DIAGS_BUFFER, _diag_add, _diag_flush, _reset_diags, - _ue_type_accepts_units_meta, _attr_default_value, - override_toggle_name, - schema_attrs, schema_subtype_attrs, - compute_canon_absorbed, iter_category_attrs, - _VEC3_CONVERT_FMT, - _resolve_mjs_field, _resolve_value_map, - FileWrite, -) -from _codegen_inject import ( - _find_matching_brace, - inject_between_tags, _inject_tags_into_cpp, -) -from _codegen_checks import ( - _check_hand_enum_drift, _check_type_shape_drift, - _check_new_attr_typing, _check_orphan_rule_entries, - _check_mjxmacro_block_coverage, _check_apply_mode_validity, - _check_embedded_cpp_references, - _check_allowlist_staleness, - _check_property_units_validity, _check_compiler_attrs_coverage, - _check_value_map_stale_mj_consts, - _emit_drift_diagnostics, -) - -SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) -PLUGIN_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "..")) - -_SNAPSHOT_DIR = os.path.join(PLUGIN_ROOT, "Scripts", "codegen", "snapshots") -DEFAULT_SCHEMA = os.path.join(_SNAPSHOT_DIR, "mjcf_schema_snapshot.json") -DEFAULT_MJXMACRO = os.path.join(_SNAPSHOT_DIR, "mjxmacro_snapshot.json") -DEFAULT_INTROSPECT = os.path.join(_SNAPSHOT_DIR, "introspect_snapshot.json") -DEFAULT_RULES = os.path.join(SCRIPT_DIR, "codegen_rules.json") -DEFAULT_PUBLIC_ROOT = os.path.join(PLUGIN_ROOT, "Source", "URLab", "Public") -DEFAULT_PRIVATE_ROOT= os.path.join(PLUGIN_ROOT, "Source", "URLab", "Private") -DEFAULT_BIND_H = os.path.join(DEFAULT_PUBLIC_ROOT, "MuJoCo", "Utils", "MjBind.h") -DEFAULT_TESTS_DIR = os.path.join(PLUGIN_ROOT, "Source", "URLabEditor", "Private", "Tests") - -COPYRIGHT_BLOCK = """// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -// -// 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. -// -// --- LEGAL DISCLAIMER --- -// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, -// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are -// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. -// -// This plugin incorporates third-party software: MuJoCo (Apache 2.0), -// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. -// -// AUTOGENERATED by Scripts/codegen/generate_ue_components.py -// Do not edit by hand. Re-run the generator to regenerate. -""" - - -def _should_emit_xml_enum_at_this_layer( - attr: str, - attrs: Sequence[str], - global_excl: Set[str], - *, - apply_canonicalizations: bool, -) -> bool: - """Whether this codegen layer owns the xml_enum import/export for - ``attr``. Two cases fire: - - - The attr appears in this layer's attrs list — subtype-specific - case, e.g. ``dcmotor.input`` fires on the DCMotor subtype's - emission. - - The base-class layer (apply_canonicalizations=True) AND the - attr is in global_exclusions — always-emit-at-base case, e.g. - ``joint.type`` is globally excluded from per-attr UPROPERTY - emission but xml_enum_attrs still owns its import/export at - the base. - """ - if attr in attrs: - return True - return apply_canonicalizations and attr in global_excl - - -# `schema_attrs` and `schema_subtype_attrs` live in `_codegen_core`. - - -# --------------------------------------------------------------------------- -# Property emission -# --------------------------------------------------------------------------- - -@dataclass -class EmittedSchema: - """Output of property-set emission for a given attr list.""" - properties_h: str # UPROPERTY block for header - imports_cpp: str # ImportFromXml body (between codegen tags) - exports_cpp: str # ExportTo body (between codegen tags) - - -@dataclass -class EmissionContext: - """Cross-cutting rules / element / call-site state threaded through - ``emit_schema_for_attrs`` and its helpers. Built once via ``from_call``. - - Carries every shared computation the property/import/export helpers - need (canon_absorbed, xml_enum_attr_names, elem_canons, the various - rule dicts) so the helpers take one ``ctx`` arg instead of 15 - positional parameters.""" - # --- caller-provided --- - attrs: Sequence[str] - rules: Dict[str, Any] - element_name: str - apply_canonicalizations: bool = True - mjs_fields: Optional[set] = None - consumed_by_setto: Optional[set] = None - mjs_array_caps: Optional[Dict[str, int]] = None - # --- derived from rules (cached on construct) --- - global_excl: set = field(default_factory=set) - type_map: Dict[str, str] = field(default_factory=dict) - default_type: str = "float" - renames: Dict[str, str] = field(default_factory=dict) - elem_rules: Dict[str, Any] = field(default_factory=dict) - elem_excl: set = field(default_factory=set) - canonicalizations: Dict[str, Any] = field(default_factory=dict) - attr_to_mjs_field: Dict[str, str] = field(default_factory=dict) - # --- derived in from_call but parameterised by apply_canonicalizations --- - elem_canons: List[str] = field(default_factory=list) - canon_absorbed: set = field(default_factory=set) - xml_enum_attr_names: set = field(default_factory=set) - - @classmethod - def from_call(cls, - attrs: Sequence[str], - rules: Dict[str, Any], - element_name: str, - *, - apply_canonicalizations: bool = True, - mjs_fields: Optional[set] = None, - consumed_by_setto: Optional[set] = None, - mjs_array_caps: Optional[Dict[str, int]] = None) -> "EmissionContext": - elem_rules = rules.get("element_rules", {}).get(element_name, {}) - canonicalizations = rules.get("canonicalizations", {}) - # ``elem_canons`` is the raw applied-canon list; the helpers - # clear it when apply_canonicalizations is False, but - # ``canon_absorbed`` keeps the raw absorbed set so per-attr - # emission still excludes them at the subtype layer. - elem_canons: List[str] = list( - elem_rules.get("applies_canonicalizations", []) - ) - canon_absorbed = compute_canon_absorbed(elem_canons, canonicalizations) - if not apply_canonicalizations: - elem_canons = [] - return cls( - attrs=attrs, - rules=rules, - element_name=element_name, - apply_canonicalizations=apply_canonicalizations, - mjs_fields=mjs_fields, - consumed_by_setto=consumed_by_setto, - mjs_array_caps=mjs_array_caps, - global_excl=set(rules.get("global_exclusions", [])), - type_map=rules.get("type_mappings", {}), - default_type=rules.get("default_type", "float"), - renames=rules.get("property_renames", {}), - elem_rules=elem_rules, - elem_excl=set(elem_rules.get("exclude_attrs", [])), - canonicalizations=canonicalizations, - attr_to_mjs_field=elem_rules.get("attr_to_mjs_field", {}), - elem_canons=elem_canons, - canon_absorbed=canon_absorbed, - xml_enum_attr_names=set(elem_rules.get("xml_enum_attrs", {}).keys()), - ) - - - -def _inject_or_diag( - text: str, - tag: str, - body: str, - *, - host_path: str, - host_kind: str, - block_label: str, - diag_source: str, -) -> Tuple[str, bool]: - """Inject ``body`` between the ``CODEGEN_`` markers in ``text``. - When the markers are missing AND the body is non-empty, fire a - diagnostic that names the host kind (e.g. 'subclass header'), the - path, the tag, and what didn't land. Returns (new_text, ok). - - This collapses ~12 near-identical try-inject-then-diag stanzas - that used to live in the emit_*_class_injection callers.""" - new_text, ok = inject_between_tags(text, tag, body, balance_source=diag_source) - if not ok and body.strip(): - _diag_add( - f"[diagnostic] {host_kind} '{host_path}' is missing the " - f"CODEGEN_{tag}_START/END marker pair; {block_label} was " - f"NOT injected.", - source=diag_source, - ) - return new_text, ok - - -def _guarded_export(toggle: str, body: str, - extra_cond: Optional[str] = None, - extra_cond_first: bool = False, - multiline: bool = False) -> str: - """Wrap ``body`` in an ``if ({toggle})`` guard and return the indented - emission. - - ``extra_cond=None``: ``if ({toggle}) {body}`` - ``extra_cond`` + first=False: ``if ({toggle} && {extra_cond}) {body}`` - ``extra_cond`` + first=True: ``if (({extra_cond}) && {toggle}) {body}`` - - ``multiline=True``: emit a braced block. ``body`` is dropped verbatim - inside the braces (caller controls inner indent — the helper does - NOT auto-indent each line so existing multi-line bodies migrate - byte-identically). Use for blocks that were previously hand-rolled - as ``if ({toggle})\\n{{\\n\\n}}\\n``. - """ - if extra_cond is None: - cond = toggle - elif extra_cond_first: - cond = f"({extra_cond}) && {toggle}" - else: - cond = f"{toggle} && {extra_cond}" - if multiline: - return f" if ({cond})\n {{\n{body} }}\n" - return f" if ({cond}) {body}\n" - - -# `_UEType`, `_UE_TYPE_INFO`, and `_attr_default_value` live in -# `_codegen_core` (re-exported at the top of this file). - - -def _emit_uproperty(prop_name: str, ue_type: str, category_label: str, - sub_category: Optional[str] = None, - extra_meta: Optional[str] = None, - default_override: Optional[str] = None, - hide_from_details: bool = False) -> str: - """Emit the toggle + value UPROPERTY pair for a schema-driven attr. - - ``extra_meta`` appends to the value UPROPERTY's meta=() block — used to - inject ``DisplayName``, ``GetOptions``, etc. via per-attr rules in - ``codegen_rules.json#element_rules..property_meta.``. - - ``hide_from_details=True`` keeps the field as a C++ member (so codegen - import paths can still seed it and BPs can still read/write it) but - suppresses the Details-panel widget via - ``EditCondition="false", EditConditionHides``. Used for properties whose - canonical authoring surface is the UE Transform widget (Pos/Quat -> - RelativeLocation/Rotation, size -> RelativeScale3D). - """ - cat = f"MuJoCo|{category_label}" - if sub_category: - cat = f"{cat}|{sub_category}" - toggle = override_toggle_name(prop_name) - default = default_override if default_override else _attr_default_value(ue_type) - if hide_from_details: - # EditConditionHides on the value also collapses the InlineEditConditionToggle - # toggle bool above it because UE renders them as a single merged row. - meta_inner = 'EditCondition="false", EditConditionHides' - else: - meta_inner = f'EditCondition="{toggle}"' - if extra_meta: - meta_inner = f'{meta_inner}, {extra_meta}' - return ( - f' UPROPERTY(EditAnywhere, Category = "{cat}", meta=(InlineEditConditionToggle))\n' - f' bool {toggle} = false;\n' - f'\n' - f' UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "{cat}", meta=({meta_inner}))\n' - f' {ue_type} {prop_name} = {default};\n\n' - ) - - -def _emit_import_line(attr: str, prop_name: str, ue_type: str, - vec3_convert: Optional[str] = None) -> str: - toggle = override_toggle_name(prop_name) - key = f'TEXT("{attr}")' - if ue_type == "float": - return f' MjXmlUtils::ReadAttrFloat(Node, {key}, {prop_name}, {toggle});\n' - if ue_type == "double": - return f' MjXmlUtils::ReadAttrDouble(Node, {key}, {prop_name}, {toggle});\n' - if ue_type == "int32": - return f' MjXmlUtils::ReadAttrInt(Node, {key}, {prop_name}, {toggle});\n' - if ue_type == "bool": - return f' MjXmlUtils::ReadAttrBool(Node, {key}, {prop_name}, {toggle});\n' - if ue_type == "FString": - return (f' if (MjXmlUtils::ReadAttrString(Node, {key}, {prop_name}))' - f' {toggle} = true;\n') - if ue_type == "FVector": - out = f' MjXmlUtils::ReadAttrVec3(Node, {key}, {prop_name}, {toggle});\n' - # MuJoCo direction vectors don't carry units but DO use opposite Y - # handedness vs UE. Apply Y-negate post-read when configured. - if vec3_convert == "y_negate": - out += f' if ({toggle}) {prop_name}.Y = -{prop_name}.Y;\n' - return out - if ue_type == "FLinearColor": - return f' MjXmlUtils::ReadAttrColor(Node, {key}, {prop_name}, {toggle});\n' - if ue_type.startswith("TArray mjs-field name mapper. -# Each takes (attr, mjs_fields) and returns ``Optional[str]`` — the -# winning field name, or None to fall through to the next strategy. -# Order in _MJS_FIELD_RESOLVERS matters: more specific rules first. - - -# The 5-strategy mjs-field resolver chain (+ ``_resolve_mjs_field``) -# lives in `_codegen_core`. - - -def _emit_export_line(attr: str, prop_name: str, ue_type: str, - mjs_fields: set | None = None, - slot_sentinel: str | None = None, - attr_to_mjs_field: Dict[str, str] | None = None, - consumed_by_setto: set | None = None, - vec3_convert: Optional[str] = None, - array_cap: Optional[int] = None) -> str: - toggle = override_toggle_name(prop_name) - # If we have an mjs field set and the attr can't be resolved to one of - # them, this attr isn't directly assignable on the mjs struct (e.g. - # actuator subtype params like `kp` live in gainprm[], not Element->kp). - # Emit either: - # - nothing (silent) when the attr is consumed by an mjs_setTo* call - # above us in the same CODEGEN_EXPORT block (no need for noise) - # - a one-line "(skipped)" marker otherwise (genuinely unhandled, - # reviewers should know) - if mjs_fields: - field = _resolve_mjs_field(attr, mjs_fields, attr_to_mjs_field) - if field == attr and attr not in mjs_fields and not ( - attr_to_mjs_field and attr in attr_to_mjs_field - ): - if consumed_by_setto and attr in consumed_by_setto: - return "" - return f' // (skipped: mjs has no field for "{attr}"; hand-roll via mjs_setTo)\n' - else: - field = attr_to_mjs_field.get(attr, attr) if attr_to_mjs_field else attr - if ue_type.startswith("TArray gainprm[6]) doesn't memory-overrun. Dynamic - # vec destinations (mjFloatVec*, mjDoubleVec*) pass array_cap=None - # and use the raw prop.Num() — the dynamic vec resizes itself. - upper = f"FMath::Min({prop_name}.Num(), {array_cap})" if array_cap else f"{prop_name}.Num()" - if slot_sentinel is not None: - # Per-slot sentinel guard: skip slots whose value equals the - # sentinel (used for `size` so fromto-only imports don't - # clobber the inherited default radius / earlier slots). - return _guarded_export(toggle, - f'{{ for (int32 i = 0; i < {upper}; ++i)' - f' {{ if ({prop_name}[i] != {slot_sentinel}) Element->{field}[i] = {prop_name}[i]; }} }}', - ) - return _guarded_export(toggle, - f'{{ for (int32 i = 0; i < {upper}; ++i)' - f' Element->{field}[i] = {prop_name}[i]; }}', - ) - if ue_type == "FVector": - if vec3_convert == "y_negate": - return _guarded_export(toggle, - f'{{ Element->{field}[0] = {prop_name}.X;' - f' Element->{field}[1] = -{prop_name}.Y;' - f' Element->{field}[2] = {prop_name}.Z; }}', - ) - return _guarded_export(toggle, - f'{{ Element->{field}[0] = {prop_name}.X;' - f' Element->{field}[1] = {prop_name}.Y;' - f' Element->{field}[2] = {prop_name}.Z; }}', - ) - if ue_type == "FLinearColor": - return _guarded_export(toggle, - f'{{ Element->{field}[0] = {prop_name}.R;' - f' Element->{field}[1] = {prop_name}.G;' - f' Element->{field}[2] = {prop_name}.B;' - f' Element->{field}[3] = {prop_name}.A; }}', - ) - if ue_type == "FString": - # MjSetString (Public/MuJoCo/Utils/MjUtils.h) internally skips empty - # FStrings, so the previous `!X.IsEmpty()` guard collapses into the - # helper. Toggle-guard stays. - return _guarded_export(toggle, f'MjSetString(Element->{field}, {prop_name});') - if ue_type == "bool": - return _guarded_export(toggle, f'Element->{field} = {prop_name} ? 1 : 0;') - return _guarded_export(toggle, f'Element->{field} = {prop_name};') - - -# --- unit conversion ---------------------------------------------------------- -# Per-element conversion ops applied to TArray / float UPROPERTYs at -# the import/export boundary. URLab stores values in UE conventions -# (degrees for angles, cm for lengths); MuJoCo wants radians / metres. -# -# To keep the rules JSON data-only, every operation is a key into this -# registry. Adding a new conversion means one dict entry — no rules edit. -# The "if guard" form is used when the conversion only fires conditional on -# the compiler.angle setting (XML in radians needs rad->deg; XML already in -# degrees needs nothing). -_UNIT_CONVERT_OPS = { - "m_to_cm": {"per_elem": "V *= 100.0f;"}, - "cm_to_m": {"per_elem": "V *= 0.01f;"}, - "deg_to_rad": {"per_elem": "V = FMath::DegreesToRadians(V);"}, - "rad_to_deg": {"per_elem": "V = FMath::RadiansToDegrees(V);"}, - "rad_to_deg_if_xml_radians": { - "per_elem": "V = FMath::RadiansToDegrees(V);", - "guard": "!CompilerSettings.bAngleInDegrees", - }, -} - - -def _emit_unit_conversion_export( - prop_name: str, mjs_field: str, conversions: Sequence[Dict[str, Any]], - is_array: bool, -) -> str: - """Emit the EXPORT side of a unit_conversion rule: build a temp value - per-element, apply the conversion, then write to ``Element->mjs_field[i]`` - (array) or ``Element->mjs_field`` (scalar). Replaces the default per-attr - export emission for attrs that have unit conversion attached. - - Array pattern: - if (bOverride_X) - { - for (int32 i = 0; i < X.Num(); ++i) - { - float V = X[i]; - if () { } - else if () { /* optional guard */ } - Element->{mjs_field}[i] = (mjtNum)V; - } - } - - Scalar pattern: - if (bOverride_X) - { - float V = X; - if () { } - ... - Element->{mjs_field} = (mjtNum)V; - } - """ - toggle = override_toggle_name(prop_name) - branches: List[str] = [] - indent = " " if is_array else " " - for entry in conversions: - op_name = entry.get("export_op") - if not op_name: - continue - op = _UNIT_CONVERT_OPS.get(op_name) - if op is None: - raise RuntimeError(f"unit_conversion: unknown export op '{op_name}' (for {prop_name})") - body = op["per_elem"] - guard = op.get("guard") - if guard: - body = f"if ({guard}) {{ {body} }}" - condition = entry.get("condition") - if condition: - prefix = "else if" if branches else "if " - branches.append(f"{indent}{prefix} ({condition}) {{ {body} }}") - else: - branches.append(f"{indent}{body}") - if not branches: - return "" - inner = "\n".join(branches) - if is_array: - body = ( - f" for (int32 i = 0; i < {prop_name}.Num(); ++i)\n" - f" {{\n" - f" float V = {prop_name}[i];\n" - f"{inner}\n" - f" Element->{mjs_field}[i] = (mjtNum)V;\n" - f" }}\n" - ) - else: - body = ( - f" float V = {prop_name};\n" - f"{inner}\n" - f" Element->{mjs_field} = (mjtNum)V;\n" - ) - return _guarded_export(toggle, body, multiline=True) - - -def _emit_unit_conversion_block( - prop_name: str, conversions: Sequence[Dict[str, Any]], side: str, - is_array: bool, -) -> str: - """Emit a guarded loop / scalar block that applies per-element - conversions to a UPROPERTY at import time. ``side`` is "import" or - "export"; only the matching ``*_op`` is used. ``is_array`` selects - between ``for (float& V : X)`` and direct ``X`` mutation. - - Pattern (array): - if (bOverride_X) - { - if () { for (float& V : X) ; } - else if () { /* optional guard */ for (float& V : X) ; } - } - """ - op_key = "import_op" if side == "import" else "export_op" - toggle = override_toggle_name(prop_name) - - def _apply(per_elem: str) -> str: - if is_array: - return f"for (float& V : {prop_name}) {{ {per_elem} }}" - # Scalar: mutate the UPROPERTY directly. The ops are written in - # terms of `V`; substitute with the prop name. - return per_elem.replace("V ", f"{prop_name} ").replace("(V)", f"({prop_name})") - - branches: List[str] = [] - for entry in conversions: - op_name = entry.get(op_key) - if not op_name: - continue - op = _UNIT_CONVERT_OPS.get(op_name) - if op is None: - raise RuntimeError(f"unit_conversion: unknown op '{op_name}' (for {prop_name})") - guard = op.get("guard") - body = _apply(op["per_elem"]) - if guard: - body = f"if ({guard}) {{ {body} }}" - condition = entry.get("condition") - if condition: - prefix = "else if" if branches else "if " - branches.append(f" {prefix} ({condition}) {{ {body} }}") - else: - branches.append(f" {body}") - if not branches: - return "" - body_lines = "\n".join(branches) - return _guarded_export(toggle, f"{body_lines}\n", multiline=True) - - -def _emit_target_collation_import(prop_name: str, absorbs_attrs: Sequence[str]) -> str: - """Emit import for an mjs name-string field that the schema spells out - across several xor attrs (e.g. mjsEquality.name1 is one of body1/joint1/ - tendon1/flex depending on the equality type). Sets the UPROPERTY to the - first non-empty attr. - - No bOverride toggle — Obj1/Obj2 are hand-declared UPROPERTYs with - dynamic GetOptions; their "set-ness" is captured by emptiness. - """ - lines: List[str] = [f" // target_collation: -> {prop_name}"] - first = True - for a in absorbs_attrs: - if first: - lines.append(f' {prop_name} = Node->GetAttribute(TEXT("{a}"));') - first = False - else: - lines.append( - f' if ({prop_name}.IsEmpty()) {prop_name} = Node->GetAttribute(TEXT("{a}"));' - ) - return "\n".join(lines) + "\n" - - -def _emit_target_collation_export(prop_name: str, mjs_field: str) -> str: - # MjSetString skips empty FStrings internally; preserves the existing - # "only write when set" semantics without the explicit guard. - return f' MjSetString(Element->{mjs_field}, {prop_name});\n' - - -def _emit_double_vec_export(attr: str, prop_name: str, mjs_field: str) -> str: - """Emit export for an attr whose mjs counterpart is a ``mjDoubleVec*`` - (std::vector*) — used by mjsKey for qpos/qvel/act/ctrl/mpos/mquat. - - Delegates to ``MjSetDoubleVec`` (Public/MuJoCo/Utils/MjUtils.h) which - handles the ``clear() + push_back`` boilerplate. The helper accepts - a null Dest so we don't need the ``&& Element->X`` guard at the - call site. - """ - toggle = override_toggle_name(prop_name) - return f" if ({toggle}) MjSetDoubleVec(Element->{mjs_field}, {prop_name});\n" - - -def _emit_data_packed_export(attr: str, packed_def: Dict[str, Any], - ue_type: str, prop_name: str) -> str: - """Emit ``Element->data[N]`` packed export for an attr that the mjs - struct can't represent directly (e.g. mjsEquality polycoef -> data[0..10], - torquescale -> data[10], anchor -> data[0..2]). - - packed_def shape: - { - "target": "data", // optional, defaults to "data" - "slot": 7, // single-slot pack (scalar UE prop) - "slot_range": [0, 11], // range-pack [start, end) (TArray) - "condition": "EqualityType == ..." // optional gating - "export_op": "cm_to_m" // optional per-elem unit conversion - // applied to each element / scalar - // before writing into data[] - } - """ - target = packed_def.get("target", "data") - condition = packed_def.get("condition") - toggle = override_toggle_name(prop_name) - - export_op_name = packed_def.get("export_op") - op_body = "" - if export_op_name: - op = _UNIT_CONVERT_OPS.get(export_op_name) - if op is None: - raise RuntimeError( - f"mjs_data_packed_attrs '{attr}': unknown export_op '{export_op_name}'" - ) - op_body = op["per_elem"] - - def _wrap(body: str) -> str: - return _guarded_export(toggle, body, - extra_cond=condition, - extra_cond_first=True, - multiline=True) - - if "slot" in packed_def: - slot = packed_def["slot"] - if op_body: - body = ( - f" float V = (float){prop_name};\n" - f" {op_body}\n" - f" Element->{target}[{slot}] = (mjtNum)V;\n" - ) - return _wrap(body) - body = f" Element->{target}[{slot}] = (mjtNum){prop_name};\n" - return _wrap(body) - if "slot_range" in packed_def: - start, end = packed_def["slot_range"] - count = end - start - if not ue_type.startswith("TArray"): - raise RuntimeError( - f"mjs_data_packed_attrs '{attr}': slot_range requires UE TArray; got {ue_type}" - ) - if op_body: - body = ( - f" for (int32 i = 0; i < {prop_name}.Num() && i < {count}; ++i)\n" - f" {{\n" - f" float V = {prop_name}[i];\n" - f" {op_body}\n" - f" Element->{target}[{start} + i] = (mjtNum)V;\n" - f" }}\n" - ) - return _wrap(body) - body = ( - f" for (int32 i = 0; i < {prop_name}.Num() && i < {count}; ++i)\n" - f" Element->{target}[{start} + i] = (mjtNum){prop_name}[i];\n" - ) - return _wrap(body) - raise RuntimeError(f"mjs_data_packed_attrs '{attr}': must specify either 'slot' or 'slot_range'") - - -# `_resolve_value_map` lives in `_codegen_core`. - - -def _emit_xml_enum_import(attr: str, enum_def: Dict[str, Any]) -> str: - """Emit the XML-string -> URLab-enum mapping inside CODEGEN_IMPORT. - - enum_def shape: - { - "ue_property": "Type", - "ue_enum_type": "EMjJointType", - "value_map": { "hinge": ["Hinge", "mjJNT_HINGE"], ... }, - "has_override_toggle": true // optional, default true; when false the - // import skips the bOverride_X=true line - // and the export fires unconditionally - } - """ - ue_prop = enum_def["ue_property"] - value_map: Dict[str, Sequence[str]] = _resolve_value_map(attr, enum_def) - has_toggle = enum_def.get("has_override_toggle", True) - ue_enum_type = enum_def.get("ue_enum_type", "") - lines: List[str] = [] - lines.append(f' {{ // xml_enum: {attr} -> {ue_enum_type or ue_prop}') - lines.append(f' FString S = Node->GetAttribute(TEXT("{attr}"));') - lines.append(" S = S.ToLower();") - # Toggle fires only when a known XML value matched. A typo'd value - # (e.g. ``type="hindge"``) used to silently set the toggle on the - # UE-default enum value — misleadingly signalling "user overrode this" - # while actually masking the typo. - if has_toggle: - lines.append(" bool bMatched = false;") - branches: List[str] = [] - for xml_val, mapping in value_map.items(): - ue_enum_member = mapping[0] - prefix = "else if " if branches else "if " - match_assign = " bMatched = true;" if has_toggle else "" - branches.append( - f' {prefix}(S == TEXT("{xml_val}")) ' - f'{{ {ue_prop} = {ue_enum_type}::{ue_enum_member};{match_assign} }}' - ) - lines.extend(branches) - if has_toggle: - toggle = override_toggle_name(ue_prop) - lines.append(f" if (bMatched) {toggle} = true;") - lines.append(" }") - return "\n".join(lines) + "\n" - - -def emit_enum_switch( - discriminator: str, - enum_name: str, - cases: Sequence[Tuple[Any, str]], - *, - indent: str = " ", - default_break: bool = False, - default_body: Optional[str] = None, -) -> str: - """Emit ``switch (discriminator) { case Enum::M: ; break; ... }``. - - ``cases`` is a sequence of ``(member_or_members, body)`` pairs: - - When ``member_or_members`` is a ``str``, the case label + body + - break collapse onto one line: ``case Enum::M: ; break;``. - - When it is a sequence of strings (multi-key group, e.g. objtype - dispatch's BODY/XBODY → ``mjOBJ_BODY``), the case labels stack - and the body / break go on separate indented lines: - ``case Enum::A:`` - ``case Enum::B:`` - `` ;`` - `` break;`` - - ``default_break=True`` appends a single-line ``default: break;``. - ``default_body`` (when set) appends a multi-line ``default:`` arm - with that body. ``default_break`` is ignored if ``default_body`` is - given. - - All lines are prefixed with ``indent``; case labels use - ``indent + 4 spaces``; multi-line bodies use ``indent + 8 spaces``. - Result ends with a trailing newline. - """ - inner = indent + " " - body_indent = inner + " " - lines: List[str] = [] - lines.append(f"{indent}switch ({discriminator})") - lines.append(f"{indent}{{") - for members, body in cases: - if isinstance(members, str): - lines.append(f"{inner}case {enum_name}::{members}: {body}; break;") - continue - for m in members: - lines.append(f"{inner}case {enum_name}::{m}:") - lines.append(f"{body_indent}{body};") - lines.append(f"{body_indent}break;") - if default_body is not None: - lines.append(f"{inner}default:") - lines.append(f"{body_indent}{default_body};") - lines.append(f"{body_indent}break;") - elif default_break: - lines.append(f"{inner}default: break;") - lines.append(f"{indent}}}") - return "\n".join(lines) + "\n" - - -def _emit_xml_enum_export(attr: str, enum_def: Dict[str, Any]) -> str: - """Emit the URLab-enum -> mjt* / mjsField mapping inside CODEGEN_EXPORT. - - Gated on ``bOverride_X`` when ``has_override_toggle`` is true (default). - Unconditional otherwise — needed for categories like site where Type - always carries a meaningful value with no inheritance semantics. - - If ``mjs_field`` is null / missing, the export is empty — used for enums - that don't have a direct mjs* field counterpart (e.g. dcmotor.input is - carried to MuJoCo via ``mjs_setToDCMotor``'s ``input_mode`` param, not a - direct ``Element->input`` write). - """ - ue_prop = enum_def["ue_property"] - ue_enum_type = enum_def.get("ue_enum_type", "") - value_map: Dict[str, Sequence[str]] = _resolve_value_map(attr, enum_def) - mjs_field = enum_def.get("mjs_field") - if not mjs_field: - return "" - mjs_cast = enum_def.get("mjs_cast", "") - has_toggle = enum_def.get("has_override_toggle", True) - indent = " " if has_toggle else " " - cases: List[Tuple[Any, str]] = [] - for _, mapping in value_map.items(): - if not isinstance(mapping, list) or len(mapping) < 2: - raise RuntimeError( - f"xml_enum {attr}: value_map entries must be [UE_enum, mj_const]; got {mapping!r}" - ) - ue_enum_member, mj_const = mapping[0], mapping[1] - cast_expr = f"({mjs_cast}){mj_const}" if mjs_cast else mj_const - cases.append((ue_enum_member, f"Element->{mjs_field} = {cast_expr}")) - switch_body = emit_enum_switch( - ue_prop, ue_enum_type, cases, - indent=indent, default_break=True, - ) - if has_toggle: - toggle = override_toggle_name(ue_prop) - return _guarded_export(toggle, switch_body, multiline=True) - return switch_body - - -def _canon_property_block(canon_name: str, canon_def: Dict[str, Any], - category_label: str, - hide_from_details: bool = False) -> str: - prop = canon_def.get("emits_property") - if not prop: - return "" - return _emit_uproperty(prop["name"], prop["type"], category_label, - sub_category=canon_name.replace("_", " ").title(), - hide_from_details=hide_from_details) - - -# --------------------------------------------------------------------------- -# Canonicalisation registry -# -# Five canonicalisations collapse multiple raw MJCF attrs into one URLab -# property: spatial_pose (pos -> Pos), orientation (quat/euler/etc -> Quat), -# fromto_decompose (fromto -> Pos/Quat/size), body_sleep_policy (sleep -> -# SleepPolicy enum), actuator_transmission (joint/tendon/site/... -> -# TransmissionType + names). The previous design hard-coded each as an -# if-branch in _canon_import_block / _canon_export_block; a new rule -# entry without a matching branch silently dropped its emission. The -# registry below is the single source of truth. -# --------------------------------------------------------------------------- - - -def _canon_import_body_sleep_policy(canon_def, element_name) -> str: - return ( - ' { // canonicalize body.sleep -> EMjBodySleepPolicy + bOverride_SleepPolicy\n' - ' FString S = Node->GetAttribute(TEXT("sleep"));\n' - ' S = S.ToLower();\n' - ' if (S == TEXT("never")) { SleepPolicy = EMjBodySleepPolicy::Never; bOverride_SleepPolicy = true; }\n' - ' else if (S == TEXT("allowed")) { SleepPolicy = EMjBodySleepPolicy::Allowed; bOverride_SleepPolicy = true; }\n' - ' else if (S == TEXT("init")) { SleepPolicy = EMjBodySleepPolicy::InitAsleep; bOverride_SleepPolicy = true; }\n' - ' }\n' - ) - - -def _canon_import_actuator_transmission(canon_def, element_name) -> str: - return ( - ' { // canonicalize actuator transmission attrs\n' - ' FString TrnTarget;\n' - ' if (MjXmlUtils::ReadAttrString(Node, TEXT("joint"), TrnTarget))\n' - ' {\n' - ' TransmissionType = EMjActuatorTrnType::Joint;\n' - ' TargetName = TrnTarget;\n' - ' }\n' - ' if (MjXmlUtils::ReadAttrString(Node, TEXT("jointinparent"), TrnTarget))\n' - ' {\n' - ' TransmissionType = EMjActuatorTrnType::JointInParent;\n' - ' TargetName = TrnTarget;\n' - ' }\n' - ' if (MjXmlUtils::ReadAttrString(Node, TEXT("tendon"), TrnTarget))\n' - ' {\n' - ' TransmissionType = EMjActuatorTrnType::Tendon;\n' - ' TargetName = TrnTarget;\n' - ' }\n' - ' if (MjXmlUtils::ReadAttrString(Node, TEXT("site"), TrnTarget))\n' - ' {\n' - ' TransmissionType = EMjActuatorTrnType::Site;\n' - ' TargetName = TrnTarget;\n' - ' }\n' - ' if (MjXmlUtils::ReadAttrString(Node, TEXT("body"), TrnTarget))\n' - ' {\n' - ' TransmissionType = EMjActuatorTrnType::Body;\n' - ' TargetName = TrnTarget;\n' - ' }\n' - ' MjXmlUtils::ReadAttrString(Node, TEXT("refsite"), RefSite);\n' - ' // SliderSite must run AFTER the main-target reads so it\n' - ' // overrides the transmission type while preserving the\n' - ' // TargetName captured above (per MJCF: slider-crank carries\n' - ' // a main target attr PLUS slidersite).\n' - ' if (MjXmlUtils::ReadAttrString(Node, TEXT("slidersite"), SliderSite))\n' - ' {\n' - ' TransmissionType = EMjActuatorTrnType::SliderCrank;\n' - ' }\n' - ' }\n' - ) - - -def _canon_import_fromto_decompose(canon_def, element_name) -> str: - helper = canon_def["import_helper"] - cat_spec = canon_def.get("category_specifics", {}).get(element_name, {}) - z_slot_check = cat_spec.get("z_slot_check", "false") - return ( - f' {{ // canonicalize fromto -> Pos/Quat/size half-length\n' - f' FVector FTPos; FQuat FTQuat; float FTHalf = 0.f;\n' - f' if ({helper}(Node, FTPos, FTQuat, FTHalf))\n' - f' {{\n' - f' Pos = FTPos; bOverride_Pos = true;\n' - f' Quat = FTQuat; bOverride_Quat = true;\n' - f' const bool bFTZSlot = {z_slot_check};\n' - f' const int32 FTSlot = bFTZSlot ? 2 : 1;\n' - f' while (size.Num() <= FTSlot) {{ size.Add(-1.0f); }}\n' - f' size[FTSlot] = FTHalf;\n' - f' bOverride_size = true;\n' - f' }}\n' - f' }}\n' - ) - - -def _canon_import_orientation(canon_def, element_name) -> str: - prop = canon_def.get("emits_property") - helper = canon_def["import_helper"] - return ( - f' {{ // canonicalize orientation (quat/euler/axisangle/xyaxes/zaxis)\n' - f' double TmpQuat[4] = {{1.0, 0.0, 0.0, 0.0}};\n' - f' if ({helper}(Node, CompilerSettings, TmpQuat))\n' - f' {{\n' - f' {prop["name"]} = MjUtils::MjToUERotation(TmpQuat);\n' - f' {override_toggle_name(prop["name"])} = true;\n' - f' }}\n' - f' }}\n' - ) - - -def _canon_import_spatial_pose(canon_def, element_name) -> str: - prop = canon_def.get("emits_property") - helper = canon_def["import_helper"] - return ( - f' {helper}(Node, TEXT("pos"), {prop["name"]}, {override_toggle_name(prop["name"])});\n' - ) - - -def _canon_export_body_sleep_policy(canon_def) -> str: - return _guarded_export( - "bOverride_SleepPolicy", - " Element->sleep = static_cast(static_cast(SleepPolicy));\n", - multiline=True, - ) - - -def _canon_export_actuator_transmission(canon_def) -> str: - return ( - ' MjSetString(Element->target, TargetName);\n' - ' switch (TransmissionType)\n' - ' {\n' - ' case EMjActuatorTrnType::Joint: Element->trntype = mjTRN_JOINT; break;\n' - ' case EMjActuatorTrnType::JointInParent: Element->trntype = mjTRN_JOINTINPARENT; break;\n' - ' case EMjActuatorTrnType::SliderCrank: Element->trntype = mjTRN_SLIDERCRANK; break;\n' - ' case EMjActuatorTrnType::Tendon: Element->trntype = mjTRN_TENDON; break;\n' - ' case EMjActuatorTrnType::Site: Element->trntype = mjTRN_SITE; break;\n' - ' case EMjActuatorTrnType::Body: Element->trntype = mjTRN_BODY; break;\n' - ' default: Element->trntype = mjTRN_UNDEFINED; break;\n' - ' }\n' - ' if (TransmissionType == EMjActuatorTrnType::SliderCrank && !SliderSite.IsEmpty())\n' - ' {\n' - ' MjSetStringRaw(Element->slidersite, SliderSite);\n' - ' }\n' - ' if (TransmissionType == EMjActuatorTrnType::Site && !RefSite.IsEmpty())\n' - ' {\n' - ' MjSetStringRaw(Element->refsite, RefSite);\n' - ' }\n' - ) - - -def _canon_export_fromto_decompose(canon_def) -> str: - # fromto resolves to Pos/Quat/Size on import — those properties carry - # the values through to export via their own canonicalisations. - return "" - - -def _canon_export_orientation(canon_def) -> str: - prop = canon_def.get("emits_property") - helper = canon_def.get("export_helper") - return _guarded_export( - override_toggle_name(prop["name"]), - f' double TmpQuat[4];\n' - f' {helper}({prop["name"]}, TmpQuat);\n' - f' Element->quat[0] = TmpQuat[0]; Element->quat[1] = TmpQuat[1];\n' - f' Element->quat[2] = TmpQuat[2]; Element->quat[3] = TmpQuat[3];\n', - multiline=True, - ) - - -def _canon_export_spatial_pose(canon_def) -> str: - prop = canon_def.get("emits_property") - helper = canon_def.get("export_helper") - return _guarded_export( - override_toggle_name(prop["name"]), - f' double TmpPos[3];\n' - f' {helper}({prop["name"]}, TmpPos);\n' - f' Element->pos[0] = TmpPos[0]; Element->pos[1] = TmpPos[1]; Element->pos[2] = TmpPos[2];\n', - multiline=True, - ) - - -@dataclass(frozen=True) -class Canonicalisation: - """One canonicalisation's import + export emitters. Looked up by - canon name from `codegen_rules.json[canonicalizations]`. The - importer takes (canon_def, element_name) and returns C++ text; - the exporter takes (canon_def) and returns C++ text.""" - name: str - import_emitter: Callable[[Dict[str, Any], str], str] - export_emitter: Callable[[Dict[str, Any]], str] - - -_CANONICALISATIONS: Dict[str, Canonicalisation] = { - c.name: c for c in [ - Canonicalisation("body_sleep_policy", - _canon_import_body_sleep_policy, - _canon_export_body_sleep_policy), - Canonicalisation("actuator_transmission", - _canon_import_actuator_transmission, - _canon_export_actuator_transmission), - Canonicalisation("fromto_decompose", - _canon_import_fromto_decompose, - _canon_export_fromto_decompose), - Canonicalisation("orientation", - _canon_import_orientation, - _canon_export_orientation), - Canonicalisation("spatial_pose", - _canon_import_spatial_pose, - _canon_export_spatial_pose), - ] -} - - -def _canon_import_block(canon_name: str, canon_def: Dict[str, Any], element_name: str = "") -> str: - entry = _CANONICALISATIONS.get(canon_name) - if entry is None: - _diag_add( - f"[diagnostic] canonicalization '{canon_name}' has a rule entry " - f"but no registry entry in _CANONICALISATIONS; per-element XML " - f"import for absorbed attrs will be silently dropped.", - source="canon_dispatch", - ) - return "" - return entry.import_emitter(canon_def, element_name) - - -def _canon_export_block(canon_name: str, canon_def: Dict[str, Any]) -> str: - entry = _CANONICALISATIONS.get(canon_name) - if entry is None: - _diag_add( - f"[diagnostic] canonicalization '{canon_name}' has a rule entry " - f"but no registry entry in _CANONICALISATIONS; per-element mjs " - f"export for absorbed attrs will be silently dropped.", - source="canon_dispatch", - ) - return "" - return entry.export_emitter(canon_def) - - -# --------------------------------------------------------------------------- -# Property-block emitters -# -# emit_schema_for_attrs used to inline both the canonicalisation property -# loop and the per-attr property loop. They're factored out here so the -# orchestration in emit_schema_for_attrs reads as "props + imports + -# exports" instead of one 400-line function. -# --------------------------------------------------------------------------- - - -def _emit_canon_property_block( - elem_canons: List[str], - canonicalizations: Dict[str, Any], - category_label: str, - hidden_canons: Set[str], -) -> str: - """Emit canonicalisation-owned UPROPERTY blocks at the top of the - component so the canonical authoring surface (Pos / Quat / etc) - appears first in the Details panel.""" - out = "" - for c in elem_canons: - cd = canonicalizations.get(c) - if not cd: - continue - out += _canon_property_block( - c, cd, category_label, - hide_from_details=(c in hidden_canons), - ) - return out - - -def _emit_per_attr_property_block( - attrs: Sequence[str], - elem_rules: Dict[str, Any], - type_map: Dict[str, str], - default_type: str, - renames: Dict[str, str], - category_label: str, - *, - global_excl: Set[str], - elem_excl: Set[str], - canon_absorbed: Set[str], - xml_enum_attr_names: Set[str], - hidden_attrs: Set[str], -) -> str: - """Emit per-attr UPROPERTY blocks (the bulk of the component's - Details panel). Attrs handled by xml_enum_attrs are skipped — those - use a hand-declared URLab enum UPROPERTY; codegen owns only the - import/export mapping. - - ``property_units`` lowers to UE-native ``Units="m"`` meta on numeric - scalars/structs; on TArray/FString/FLinearColor it lowers to a - custom ``MjUnit="m"`` meta tag the URLab detail customization reads - at runtime (UHT rejects Units on those types).""" - prop_meta: Dict[str, str] = elem_rules.get("property_meta", {}) - property_defaults: Dict[str, str] = elem_rules.get("property_defaults", {}) - property_units: Dict[str, str] = elem_rules.get("property_units", {}) - out = "" - for attr in iter_category_attrs(attrs, global_excl=global_excl, - elem_excl=elem_excl, - canon_absorbed=canon_absorbed, - extra_excl=xml_enum_attr_names): - ue_type = type_map.get(attr, default_type) - prop_name = renames.get(attr, attr) - attr_meta_parts: List[str] = [] - if prop_meta.get(attr): - attr_meta_parts.append(prop_meta[attr]) - unit = property_units.get(attr) or property_units.get(prop_name) - if unit: - if _ue_type_accepts_units_meta(ue_type): - attr_meta_parts.append(f'Units="{unit}"') - else: - attr_meta_parts.append(f'MjUnit="{unit}"') - out += _emit_uproperty( - prop_name, ue_type, category_label, - extra_meta=", ".join(attr_meta_parts) if attr_meta_parts else None, - default_override=property_defaults.get(attr) or property_defaults.get(prop_name), - hide_from_details=(attr in hidden_attrs or prop_name in hidden_attrs), - ) - return out - - -def _emit_xml_enum_property_decls( - elem_rules: Dict[str, Any], - category_label: str, - *, - apply_canonicalizations: bool, -) -> str: - """Emit UPROPERTY decls for xml_enum_attrs entries that opt in via - ``emit_property_decl: true``. URLab usually hand-declares the - UE-side enum properties (e.g. ``EMjJointType Type`` in MjJoint.h); - the codegen-owned path is reserved for cases where the rule author - wants the decl + initializer to live in the rule JSON. Only fires - on the BASE class emission (subclasses would shadow the base decl - and fail UHT). - - Per-rule ``property_default`` lets the enum decl take a real - initializer like ``EMjGainType::Fixed`` instead of the ``{}`` - fallback. A typo'd default (``"Fxed"``) surfaces as a codegen - diagnostic, not a misleading UHT compile error far downstream.""" - if not apply_canonicalizations: - return "" - out = "" - for attr, enum_def in elem_rules.get("xml_enum_attrs", {}).items(): - if not enum_def.get("emit_property_decl"): - continue - ue_prop = enum_def.get("ue_property") - ue_enum_type = enum_def.get("ue_enum_type") - if not ue_prop or not ue_enum_type: - continue - prop_default = enum_def.get("property_default") - if prop_default: - value_map = enum_def.get("value_map", {}) - valid_members = {m[0] for m in value_map.values() - if isinstance(m, (list, tuple)) and m} - if valid_members and prop_default not in valid_members: - _diag_add( - f"[diagnostic] xml_enum '{attr}' property_default " - f"'{prop_default}' is not in value_map UE members " - f"({sorted(valid_members)}); UPROPERTY default will " - f"fail UHT compile.", - source="property_default", - ) - full_default = ( - f"{ue_enum_type}::{prop_default}" if prop_default else None - ) - out += _emit_uproperty( - ue_prop, ue_enum_type, category_label, - extra_meta=enum_def.get("property_meta"), - default_override=full_default, - ) - return out - - -def _emit_imports_block(ctx: EmissionContext) -> str: - """Emit the ``ImportFromXml`` body — every read-from-XML line. Order: - common imports prefix (name/class), xml_enum imports, per-attr - imports, canon imports, SetRelativeTransform driver, target- - collation imports, unit-conversion imports.""" - attrs = ctx.attrs - elem_rules = ctx.elem_rules - type_map = ctx.type_map - default_type = ctx.default_type - renames = ctx.renames - global_excl = ctx.global_excl - elem_excl = ctx.elem_excl - canon_absorbed = ctx.canon_absorbed - elem_canons = ctx.elem_canons - canonicalizations = ctx.canonicalizations - apply_canon = ctx.apply_canonicalizations - xml_enum_attr_names = ctx.xml_enum_attr_names - - imports = "" - - # 2a) Common imports prefix (name / class) — emits at the top. - _COMMON_IMPORT_FIELDS = {"name": "MjName", "class": "MjClassName"} - common_imports_list = elem_rules.get("common_imports", []) if apply_canon else [] - if common_imports_list: - common_block = "" - for entry in common_imports_list: - if isinstance(entry, str): - attr = entry - ue_field = _COMMON_IMPORT_FIELDS.get(attr, attr) - else: - attr = entry["attr"] - ue_field = entry.get("ue_field", _COMMON_IMPORT_FIELDS.get(attr, attr)) - common_block += ( - f' MjXmlUtils::ReadAttrString(Node, TEXT("{attr}"), {ue_field});\n' - ) - imports = common_block + imports - - # 3a) XML enum imports first (downstream emission can depend on the - # resolved enum value, e.g. fromto_decompose reads Type). - xml_enum_attrs: Dict[str, Any] = elem_rules.get("xml_enum_attrs", {}) - for attr, enum_def in xml_enum_attrs.items(): - if _should_emit_xml_enum_at_this_layer( - attr, attrs, global_excl, apply_canonicalizations=apply_canon, - ): - imports += _emit_xml_enum_import(attr, enum_def) - - # 3b) Per-attr imports. - vec3_convert_map: Dict[str, str] = elem_rules.get("vec3_convert", {}) - for attr in iter_category_attrs(attrs, global_excl=global_excl, - elem_excl=elem_excl, - canon_absorbed=canon_absorbed, - extra_excl=xml_enum_attr_names): - ue_type = type_map.get(attr, default_type) - prop_name = renames.get(attr, attr) - imports += _emit_import_line(attr, prop_name, ue_type, - vec3_convert=vec3_convert_map.get(attr)) - - # 3c) Canon imports run AFTER per-attr so canons (fromto) can - # override individual slots. - for c in elem_canons: - cd = canonicalizations.get(c) - if cd: - imports += _canon_import_block(c, cd, element_name=ctx.element_name) - - # 3d) Drive the UE component transform from the codegen-owned - # Pos/Quat UPROPERTYs. - if apply_canon: - if "spatial_pose" in elem_canons: - imports += " if (bOverride_Pos) SetRelativeLocation(Pos);\n" - if "orientation" in elem_canons: - imports += " if (bOverride_Quat) SetRelativeRotation(Quat);\n" - - # 4c) Target-collation imports. - target_collations: Dict[str, Any] = elem_rules.get("target_collations", {}) - if apply_canon: - for prop_name, coll_def in target_collations.items(): - imports += _emit_target_collation_import( - prop_name, coll_def.get("absorbs_attrs", []) - ) - - # 4d) Unit-conversion imports. - unit_conversions: Dict[str, Any] = elem_rules.get("unit_conversion", {}) - for attr, conv_list in unit_conversions.items(): - if attr not in attrs and not apply_canon: - continue - prop_name = renames.get(attr, attr) - ue_type = type_map.get(attr, default_type) - is_array = ue_type.startswith("TArray") - imports += _emit_unit_conversion_block( - prop_name, conv_list, side="import", is_array=is_array, - ) - - return imports - - -def _emit_exports_block(ctx: EmissionContext) -> str: - """Emit the ``ExportTo`` body — every write-back-to-mjs line. Order: - canon exports, xml_enum exports, per-attr exports, data-packed - exports, target-collation exports, unit-conversion exports, double- - vec exports.""" - attrs = ctx.attrs - elem_rules = ctx.elem_rules - type_map = ctx.type_map - default_type = ctx.default_type - renames = ctx.renames - global_excl = ctx.global_excl - elem_excl = ctx.elem_excl - canon_absorbed = ctx.canon_absorbed - elem_canons = ctx.elem_canons - canonicalizations = ctx.canonicalizations - apply_canon = ctx.apply_canonicalizations - xml_enum_attr_names = ctx.xml_enum_attr_names - mjs_fields = ctx.mjs_fields - mjs_array_caps = ctx.mjs_array_caps or {} - consumed_by_setto = ctx.consumed_by_setto - attr_to_mjs_field = ctx.attr_to_mjs_field - - exports = "" - target_collations: Dict[str, Any] = elem_rules.get("target_collations", {}) - unit_conversions: Dict[str, Any] = elem_rules.get("unit_conversion", {}) - vec3_convert_map: Dict[str, str] = elem_rules.get("vec3_convert", {}) - - # 5) Canon exports first (set canonical mjs fields before per-attr - # tweaks). canon_export_skip opts an element out per-canon (e.g. - # body's pos/quat are written by MjSpecWrapper from the transform). - canon_export_skip = set(elem_rules.get("canon_export_skip", [])) - for c in elem_canons: - if c in canon_export_skip: - continue - cd = canonicalizations.get(c) - if cd: - exports += _canon_export_block(c, cd) - - # 5b) XML enum exports — same layer policy as imports (3a). - xml_enum_attrs: Dict[str, Any] = elem_rules.get("xml_enum_attrs", {}) - for attr, enum_def in xml_enum_attrs.items(): - if _should_emit_xml_enum_at_this_layer( - attr, attrs, global_excl, apply_canonicalizations=apply_canon, - ): - exports += _emit_xml_enum_export(attr, enum_def) - - # 6) Per-attr exports. - data_packed_attrs: Dict[str, Any] = elem_rules.get("mjs_data_packed_attrs", {}) - double_vec_attrs: List[str] = elem_rules.get("mjs_double_vec_attrs", []) - double_vec_set: set = set(double_vec_attrs) - sentinel_skip: Dict[str, str] = elem_rules.get("sentinel_skip_export", {}) - export_skip_attrs: set = set(elem_rules.get("export_skip_attrs", [])) - unit_convert_export_attrs: set = { - a for a, convs in unit_conversions.items() - if any(cv.get("export_op") for cv in convs) - } - target_collation_absorbed: set = set() - if apply_canon: - for prop_name, coll_def in target_collations.items(): - target_collation_absorbed.update(coll_def.get("absorbs_attrs", [])) - export_extra_excl: set = set(xml_enum_attr_names) - export_extra_excl |= double_vec_set - export_extra_excl |= unit_convert_export_attrs - export_extra_excl |= export_skip_attrs - export_extra_excl |= target_collation_absorbed - if apply_canon: - export_extra_excl |= set(data_packed_attrs.keys()) - for attr in iter_category_attrs(attrs, global_excl=global_excl, - elem_excl=elem_excl, - canon_absorbed=canon_absorbed, - extra_excl=export_extra_excl): - ue_type = type_map.get(attr, default_type) - prop_name = renames.get(attr, attr) - # Cap export loops against the destination C array size so a - # narrower mjs field can't overrun. - resolved_field = _resolve_mjs_field( - attr, mjs_fields, attr_to_mjs_field, - ) if mjs_fields else (attr_to_mjs_field or {}).get(attr, attr) - array_cap = mjs_array_caps.get(resolved_field) - exports += _emit_export_line( - attr, prop_name, ue_type, - mjs_fields=mjs_fields, - slot_sentinel=sentinel_skip.get(attr), - attr_to_mjs_field=attr_to_mjs_field, - consumed_by_setto=consumed_by_setto, - vec3_convert=vec3_convert_map.get(attr), - array_cap=array_cap, - ) - - # 6b) Data-packed exports. - if apply_canon: - for attr, packed_def in data_packed_attrs.items(): - ue_type = type_map.get(attr, default_type) - prop_name = renames.get(attr, attr) - exports += _emit_data_packed_export( - attr, packed_def, ue_type, prop_name, - ) - - # 6d) Target-collation exports. - if apply_canon: - for prop_name, coll_def in target_collations.items(): - exports += _emit_target_collation_export( - prop_name, coll_def["mjs_field"], - ) - - # 6e) Unit-conversion exports. - for attr, conv_list in unit_conversions.items(): - if attr not in attrs and not apply_canon: - continue - prop_name = renames.get(attr, attr) - ue_type = type_map.get(attr, default_type) - is_array = ue_type.startswith("TArray") - if mjs_fields: - mjs_field = _resolve_mjs_field(attr, mjs_fields, attr_to_mjs_field) - else: - mjs_field = (attr_to_mjs_field or {}).get(attr, attr) - exports += _emit_unit_conversion_export( - prop_name, mjs_field, conv_list, is_array=is_array, - ) - - # 6c) Double-vec exports. - for attr in double_vec_attrs: - prop_name = renames.get(attr, attr) - if mjs_fields: - mjs_field = _resolve_mjs_field(attr, mjs_fields, attr_to_mjs_field) - else: - mjs_field = (attr_to_mjs_field or {}).get(attr, attr) - exports += _emit_double_vec_export(attr, prop_name, mjs_field) - - return exports - - -def emit_schema_for_attrs( - attrs: Sequence[str], - rules: Dict[str, Any], - element_name: str, - category_label: str, - *, - apply_canonicalizations: bool = True, - mjs_fields: set | None = None, - consumed_by_setto: set | None = None, - mjs_array_caps: Dict[str, int] | None = None, -) -> EmittedSchema: - """Emit UPROPERTY + import/export blocks for a list of schema attrs. - - Applies global exclusions, element exclusions, canonicalisation - absorption, and type mappings from `codegen_rules.json`. - - Pass ``apply_canonicalizations=False`` when emitting for a subclass - in a category whose base class already owns the canonicalised - properties (e.g. site/tendon/equality subtypes in multi_uclass - layout, or geom subtypes that share base canonicalisations). - Raw canonicalised attrs (pos/quat/euler/etc.) are still excluded - so they don't re-appear as per-attr UPROPERTYs. - - ``consumed_by_setto`` is the set of schema attrs that will be packed - into the mjs spec by an ``mjs_setTo*`` call prepended to the export - block (see ``_emit_setto_call``). For those attrs, we suppress the - fallback "(skipped: mjs has no field)" comment to keep the emitted - file uncluttered. - """ - ctx = EmissionContext.from_call( - attrs, rules, element_name, - apply_canonicalizations=apply_canonicalizations, - mjs_fields=mjs_fields, - consumed_by_setto=consumed_by_setto, - mjs_array_caps=mjs_array_caps, - ) - elem_rules = ctx.elem_rules - hidden_canons: set = set(elem_rules.get("hidden_canon_properties", [])) - hidden_attrs: set = set(elem_rules.get("hidden_attrs", [])) - - props = ( - _emit_canon_property_block( - ctx.elem_canons, ctx.canonicalizations, - category_label, hidden_canons, - ) - + _emit_per_attr_property_block( - attrs, elem_rules, ctx.type_map, ctx.default_type, ctx.renames, - category_label, - global_excl=ctx.global_excl, elem_excl=ctx.elem_excl, - canon_absorbed=ctx.canon_absorbed, - xml_enum_attr_names=ctx.xml_enum_attr_names, - hidden_attrs=hidden_attrs, - ) - + _emit_xml_enum_property_decls( - elem_rules, category_label, - apply_canonicalizations=apply_canonicalizations, - ) - ) - - imports = _emit_imports_block(ctx) - exports = _emit_exports_block(ctx) - - return EmittedSchema(properties_h=props, imports_cpp=imports, exports_cpp=exports) - - -def _emit_passthrough_canon_lines( - elem_canons: List[str], - canonicalizations: Dict[str, Any], -) -> List[str]: - """Emit ``pos="x y z"`` / ``quat="w x y z"`` formatters for the - canonicalisation-owned UPROPERTYs. Round-trips through the same - UEToMjPosition / UEToMjRotation helpers the canon export blocks - use, so the XML matches the spec write exactly.""" - lines: List[str] = [] - for c in elem_canons: - cd = canonicalizations.get(c) - if not cd: - continue - emits = cd.get("emits_property") - if not emits: - continue - prop = emits["name"] - ue_t = emits["type"] - toggle = override_toggle_name(prop) - if ue_t == "FVector" and c == "spatial_pose": - lines.append(f" if ({toggle})") - lines.append(" {") - lines.append(" double Tmp[3];") - lines.append(f" MjUtils::UEToMjPosition({prop}, Tmp);") - lines.append(' Out += FString::Printf(TEXT(" pos=\\"%f %f %f\\""), Tmp[0], Tmp[1], Tmp[2]);') - lines.append(" }") - elif ue_t == "FQuat" and c == "orientation": - lines.append(f" if ({toggle})") - lines.append(" {") - lines.append(" double Tmp[4];") - lines.append(f" MjUtils::UEToMjRotation({prop}, Tmp);") - lines.append(' Out += FString::Printf(TEXT(" quat=\\"%f %f %f %f\\""), Tmp[0], Tmp[1], Tmp[2], Tmp[3]);') - lines.append(" }") - return lines - - -def _emit_passthrough_xml_enum_lines( - xml_enum_attrs: Dict[str, Any], -) -> List[str]: - """Emit the per-attr if/else-if chain that picks the XML string for - the chosen enum value.""" - lines: List[str] = [] - for attr, enum_def in xml_enum_attrs.items(): - ue_prop = enum_def["ue_property"] - ue_enum = enum_def["ue_enum_type"] - value_map = _resolve_value_map(attr, enum_def) - toggle = override_toggle_name(ue_prop) - lines.append(f" if ({toggle})") - lines.append(" {") - first = True - for xml_val, mapping in value_map.items(): - ue_member = mapping[0] - prefix = "if " if first else "else if " - first = False - lines.append(f' {prefix}({ue_prop} == {ue_enum}::{ue_member}) ' - f'Out += TEXT(" {attr}=\\"{xml_val}\\"");') - lines.append(" }") - return lines - - -def _emit_passthrough_attr_line( - attr: str, prop_name: str, ue_type: str, toggle: str, -) -> List[str]: - """Emit the per-type XML-attr formatter line(s) for a single attr. - Returns a list of indented C++ lines (one for scalars, multiple - for TArray loops). Unknown types fall through with a TODO comment - so the build still compiles.""" - if ue_type == "FString": - return [f' if ({toggle} && !{prop_name}.IsEmpty()) ' - f'Out += FString::Printf(TEXT(" {attr}=\\"%s\\""), *{prop_name});'] - if ue_type == "bool": - return [f' if ({toggle}) ' - f'Out += FString::Printf(TEXT(" {attr}=\\"%s\\""), ' - f'{prop_name} ? TEXT("true") : TEXT("false"));'] - if ue_type == "int32": - return [f' if ({toggle}) ' - f'Out += FString::Printf(TEXT(" {attr}=\\"%d\\""), {prop_name});'] - if ue_type == "float": - return [f' if ({toggle}) ' - f'Out += FString::Printf(TEXT(" {attr}=\\"%f\\""), {prop_name});'] - if ue_type == "FLinearColor": - return [f" if ({toggle}) " - f'Out += FString::Printf(TEXT(" {attr}=\\"%f %f %f %f\\""), ' - f'{prop_name}.R, {prop_name}.G, {prop_name}.B, {prop_name}.A);'] - if ue_type.startswith("TArray 0)", - " {", - f' Out += TEXT(" {attr}=\\"");', - f" for (int32 i = 0; i < {prop_name}.Num(); ++i)", - " {", - ' if (i > 0) Out += TEXT(" ");', - f' Out += FString::Printf(TEXT("%f"), {prop_name}[i]);', - " }", - ' Out += TEXT("\\"");', - " }", - ] - if ue_type.startswith("TArray 0)", - " {", - f' Out += TEXT(" {attr}=\\"");', - f" for (int32 i = 0; i < {prop_name}.Num(); ++i)", - " {", - ' if (i > 0) Out += TEXT(" ");', - f' Out += FString::Printf(TEXT("%d"), {prop_name}[i]);', - " }", - ' Out += TEXT("\\"");', - " }", - ] - return [f' // TODO(xml_passthrough): unsupported UE type "{ue_type}" for attr "{attr}"'] - - -def emit_xml_passthrough_body( - attrs: Sequence[str], - rules: Dict[str, Any], - element_name: str, -) -> str: - """Emit the body of a `BuildSchemaAttrsXml() const -> FString` method. - - For categories with ``xml_passthrough_emission: true`` in their codegen - rule, the spec-registration path goes through an MJCF re-parse - (mj_parseXMLString + mjs_attach) instead of the mjs_addX API — flexcomp - is the canonical example because MuJoCo expands the flexcomp macro in - the XML parser, not the spec compiler. To keep new schema attrs - flowing end-to-end without hand-edits, the codegen emits a single - method that walks every override-tracked UPROPERTY and writes its - value as an ``name="value"`` XML attribute string. The hand-rolled - BuildFlexcompXml just calls this helper for the main attrs and - handles sub-elements (which the codegen doesn't model yet) itself. - - Output is the indented C++ statements (no signature, no braces) — - the caller wraps them in a method body. Per-type emission lives in - ``_emit_passthrough_attr_line``; canon + xml_enum live in their own - helpers. - """ - global_excl = set(rules.get("global_exclusions", [])) - elem_rules = rules.get("element_rules", {}).get(element_name, {}) - elem_excl = set(elem_rules.get("exclude_attrs", [])) - elem_canons: List[str] = elem_rules.get("applies_canonicalizations", []) - canonicalizations = rules.get("canonicalizations", {}) - canon_absorbed: set = compute_canon_absorbed(elem_canons, canonicalizations) - xml_enum_attrs: Dict[str, Any] = elem_rules.get("xml_enum_attrs", {}) - type_map: Dict[str, str] = rules.get("type_mappings", {}) - default_type: str = rules.get("default_type", "float") - renames: Dict[str, str] = rules.get("property_renames", {}) - - lines: List[str] = [" FString Out;"] - lines.extend(_emit_passthrough_canon_lines(elem_canons, canonicalizations)) - lines.extend(_emit_passthrough_xml_enum_lines(xml_enum_attrs)) - - # ``name`` is written by the caller (it knows the prefix / MjName - # logic). - passthrough_extra_excl = set(xml_enum_attrs) | {"name"} - for attr in iter_category_attrs(attrs, global_excl=global_excl, - elem_excl=elem_excl, - canon_absorbed=canon_absorbed, - extra_excl=passthrough_extra_excl): - prop_name = renames.get(attr, attr) - ue_type = type_map.get(attr, default_type) - toggle = override_toggle_name(prop_name) - lines.extend(_emit_passthrough_attr_line(attr, prop_name, ue_type, toggle)) - - lines.append(" return Out;") - return "\n".join(lines) + "\n" - - -# --------------------------------------------------------------------------- -# File templates -# --------------------------------------------------------------------------- - -SUBCLASS_HEADER_TEMPLATE = """{Copyright} -#pragma once - -#include "CoreMinimal.h" -#include "{BaseInclude}" -#include "{ClassFileStem}.generated.h" - -/** - * @class {ClassName} - * @brief Autogenerated component representing the MuJoCo '{ElementName}' element. - * - * Subclass of {BaseClassName}. Sets Type = {TypeEnumValue} in the constructor. - */ -UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) -class URLAB_API {ClassName} : public {BaseClassName} -{{ - GENERATED_BODY() - -public: - {ClassName}(); - - // --- CODEGEN_PROPERTIES_START --- -{Properties} // --- CODEGEN_PROPERTIES_END --- - - virtual void ImportFromXml(const class FXmlNode* Node, const FMjCompilerSettings& CompilerSettings = FMjCompilerSettings{{}}) override; - virtual void ExportTo({MjsStruct}* Element, mjsDefault* Default = nullptr) override; -}}; -""" - - -SUBCLASS_SOURCE_TEMPLATE = """{Copyright} -#include "{IncludePath}" -#include "MuJoCo/Utils/MjXmlUtils.h" -#include "MuJoCo/Utils/MjOrientationUtils.h" -#include "MuJoCo/Utils/MjUtils.h" -#include "XmlNode.h" -#include "mujoco/mjspec.h" - -{ClassName}::{ClassName}() -{{ - {ConstructorBody} -}} - -void {ClassName}::ImportFromXml(const FXmlNode* Node, const FMjCompilerSettings& CompilerSettings) -{{ - Super::ImportFromXml(Node, CompilerSettings); - if (!Node) return; - - // --- CODEGEN_IMPORT_START --- -{Imports} // --- CODEGEN_IMPORT_END --- -}} - -void {ClassName}::ExportTo({MjsStruct}* Element, mjsDefault* Default) -{{ - if (!Element) return; - - Super::ExportTo(Element, Default); - - // --- CODEGEN_EXPORT_START --- -{Exports} // --- CODEGEN_EXPORT_END --- -}} -""" - - -# --------------------------------------------------------------------------- -# Boundary-tag injection -# --------------------------------------------------------------------------- - -# Text-injection machinery lives in `_codegen_inject`. The symbols -# (`_make_tag_pair`, `_strip_cpp_braces_in_strings`, -# `_find_matching_brace`, `_check_brace_balance`, -# `inject_between_tags`, `_inject_tags_into_cpp`) are re-exported -# through the import block at the top of this file. - - -# --------------------------------------------------------------------------- -# Owned-name computation (used for the schema-for-tests header). -# The generator no longer migrates / strips existing files. Hand-prep -# each component to the codegen-canonical shape (see docs/plan_codegen.md): -# explicit CODEGEN tag pairs, ExportTo param named ``Element``, type-enum -# field named ``Type``, ExtractSpecifics/ApplyRawOverrides/ParseSpecifics -# deleted, schema-owned UPROPERTYs removed. The generator's only job is -# to fill the tag slots. -# --------------------------------------------------------------------------- - - -def codegen_owned_property_names(category: str, attrs: List[str], rules: Dict[str, Any]) -> set: - """Return the set of property names this category's codegen will - emit for the given attribute list. Includes canonicalisation - outputs.""" - global_excl = set(rules.get("global_exclusions", [])) - renames: Dict[str, str] = rules.get("property_renames", {}) - elem_rules = rules.get("element_rules", {}).get(category, {}) - elem_excl = set(elem_rules.get("exclude_attrs", [])) - elem_canons: List[str] = elem_rules.get("applies_canonicalizations", []) - canonicalizations: Dict[str, Any] = rules.get("canonicalizations", {}) - canon_absorbed: set = compute_canon_absorbed(elem_canons, canonicalizations) - owned: set = set() - for c in elem_canons: - cdef = canonicalizations.get(c) - if cdef: - prop = cdef.get("emits_property") - if prop: - owned.add(prop["name"]) - for a in iter_category_attrs(attrs, global_excl=global_excl, - elem_excl=elem_excl, - canon_absorbed=canon_absorbed): - owned.add(renames.get(a, a)) - return owned - - - - -# --------------------------------------------------------------------------- -# Pipeline: emit subclass files -# --------------------------------------------------------------------------- - -# `FileWrite` lives in `_codegen_core`. - - -def _resolve_include_path(public_root: str, file_relpath: str) -> str: - """Convert ``Public/MuJoCo/Components/Actuators/MjMotorActuator.h`` to - the include path ``MuJoCo/Components/Actuators/MjMotorActuator.h``.""" - # All emitted headers are under /MuJoCo/Components/... so the - # include strips the leading public_root. - rel = os.path.relpath(file_relpath, public_root) - return rel.replace(os.sep, "/") - - -_BANNER_TEMPLATE_INCLUDES = { - "MuJoCo/Utils/MjXmlUtils.h", - "MuJoCo/Utils/MjOrientationUtils.h", - "MuJoCo/Utils/MjUtils.h", - "XmlNode.h", - "mujoco/mjspec.h", -} - - -def _audit_banner_safety(source_path: str, subtype: Dict[str, Any]) -> None: - """Scan an existing source file the codegen is ABOUT to overwrite via - ``fully_emitted: true`` and emit a diagnostic if it carries content - the template wouldn't reproduce (extra includes beyond the template - set, extra method definitions, ctor extras not in ``extra_constructor``). - - Non-fatal — the rewrite still proceeds. The diagnostic exists so a - rule author who flips ``fully_emitted: true`` without running the - audit helper sees a loud warning the next time the codegen runs. - """ - try: - with open(source_path, "r", encoding="utf-8") as f: - text = f.read() - except FileNotFoundError: - return - except OSError as e: - _diag_add( - f"[diagnostic] banner_overwrite: could not read '{source_path}' " - f"({e}) — skipping the pre-overwrite audit. The rewrite still " - f"proceeds, but any hand-edits in the existing file may be lost " - f"unnoticed.", - source="banner_overwrite", - ) - return - - rel = os.path.basename(source_path) - own_h = os.path.splitext(rel)[0] + ".h" - - # Extra includes beyond the template set. - extras = [] - for inc in re.findall(r'#include\s+"([^"]+)"', text): - if inc.endswith(own_h) or inc in _BANNER_TEMPLATE_INCLUDES: - continue - extras.append(inc) - if extras: - _diag_add( - f"[diagnostic] fully_emitted: true on '{subtype.get('class_name', rel)}' " - f"is about to OVERWRITE {rel} which has non-template includes " - f"{extras}. Run Scripts/codegen/_audit_banner_candidates.py " - f"before flipping, or revert fully_emitted on this subtype.", - source="banner_overwrite", - ) - - # Extra ctor lines beyond what the rule provides via extra_constructor. - # The ctor body can contain its own brace-bearing statements (range-for - # loops, init lists, nested ifs); use the brace-aware extractor so the - # diagnostic doesn't truncate at the first inner ``}`` and report a - # spurious "non-template ctor content". - expected = {subtype.get("extra_constructor", "").strip()} - expected.discard("") - class_name = subtype.get("class_name", "") - if class_name: - sig = re.search( - rf'{re.escape(class_name)}::{re.escape(class_name)}\(\)\s*\{{', - text, - ) - if sig: - open_idx = sig.end() - 1 - close_idx = _find_matching_brace(text, open_idx) - if close_idx is not None: - body = text[open_idx + 1:close_idx] - stripped = re.sub(r"(Type\s*=\s*EMj\w+::\w+;|//[^\n]*|\s+)", "", body) - for e in expected: - # The expected extras may themselves contain whitespace — - # strip to compare. - stripped = stripped.replace(re.sub(r"\s+", "", e), "") - if stripped: - _diag_add( - f"[diagnostic] fully_emitted: true on " - f"'{class_name}' is about to OVERWRITE the ctor in " - f"{rel} — non-template ctor content remains after " - f"stripping Type+extra_constructor: {stripped!r}.", - source="banner_overwrite", - ) - - -def _subclass_constructor_body( - category: str, - subtype: Dict[str, Any], - type_enum_name: Optional[str], -) -> str: - pieces: List[str] = [] - if type_enum_name and subtype.get("enum_value"): - pieces.append(f"Type = {type_enum_name}::{subtype['enum_value']};") - extra = subtype.get("extra_constructor") - if extra: - pieces.extend(extra.strip().splitlines()) - if not pieces: - return "// (no per-subclass constructor body)" - return ("\n ").join(pieces) - - -def _setto_consumed_attrs( - setto_rules: Dict[str, Any] | None, - mjspec: Dict[str, Any] | None, -) -> set: - """Return the set of schema attr names that an ``mjs_setTo*`` call - consumes for the given subtype. These attrs are packed into the mjs - spec via the setTo function (e.g. position actuator's kp/kv/dampratio - go into gainprm/biasprm via mjs_setToPosition); the codegen should - NOT also emit a "skipped" fallback comment for them in the export - block (the setTo call already handles them).""" - if not setto_rules or not mjspec: - return set() - fn_name = setto_rules.get("call") - if not fn_name or fn_name not in mjspec.get("setto_functions", {}): - return set() - sig = mjspec["setto_functions"][fn_name] - param_renames: Dict[str, str] = setto_rules.get("param_renames", {}) - return {param_renames.get(p["name"], p["name"]) for p in sig.get("params", [])} - - -def _emit_setto_call( - subtype_key: str, - setto_rules: Dict[str, Any], - rules: Dict[str, Any], - mjspec: Dict[str, Any] | None, - subtype_schema_attrs: List[str], - base_schema_attrs: Sequence[str], -) -> str: - """Generate the ``mjs_setToX(Element, ...)`` call inside CODEGEN_EXPORT - from the function's signature in the mjspec snapshot. Replaces the hand- - written multi-line C++ literals that used to live in ``codegen_rules.json``. - - Marshaling rules per function parameter ``p`` (``c_type``, ``array_dim``): - - ``double`` scalar + UE ``float`` -> ``bOverride_p ? (double)p : `` - - ``double`` scalar + UE ``FVector`` + projection ``X`` - -> ``bOverride_p ? (double)p.X : `` - - ``double[N]`` array + UE ``TArray`` - -> ``double pBuf[N] = { ... }; pass pBuf`` - - ``double[1]`` array + UE ``float`` (scalar-in-buffer) - -> ``double pBuf[1] = { ... }; pass pBuf`` - - ``int`` scalar -> ``bOverride_p ? (int)p : `` - - Params NOT present in subtype/base schema -> force sentinel. - - Sentinels: defaults come from ``setto_param_defaults`` in the rules JSON, - keyed by ``(function_name, param_name)``; otherwise ``-1.0`` for doubles, - ``0`` for ints. The dcmotor pattern (``nullable_arrays``) emits - ``bOverride_X ? Buf : nullptr`` and uses the ``FillDouble`` helper that - lives file-local in ``MjDcMotorActuator.cpp``. - """ - if setto_rules is None: - return "" - fn_name = setto_rules.get("call") - if not fn_name: - return "" - if not mjspec or fn_name not in mjspec.get("setto_functions", {}): - raise RuntimeError( - f"mjs_setTo* function '{fn_name}' not found in the projected " - f"mjspec. Rebuild the introspect snapshot via " - f"`python Scripts/codegen/build_introspect_snapshot.py`." - ) - sig = mjspec["setto_functions"][fn_name] - if not sig["params"]: - return ( - f" {{\n" - f" const char* SetToErr = {fn_name}(Element);\n" - f" if (SetToErr && *SetToErr)\n" - f" {{\n" - f" UE_LOG(LogURLabBind, Warning, TEXT(\"{fn_name} on '%s': %s\"), " - f"*GetName(), UTF8_TO_TCHAR(SetToErr));\n" - f" }}\n" - f" }}\n" - ) - - type_mappings: Dict[str, str] = rules.get("type_mappings", {}) - default_type: str = rules.get("default_type", "float") - fn_defaults: Dict[str, str] = rules.get("setto_param_defaults", {}).get(fn_name, {}) - param_renames: Dict[str, str] = setto_rules.get("param_renames", {}) - projections: Dict[str, str] = setto_rules.get("projections", {}) - nullable_arrays: bool = setto_rules.get("nullable_arrays", False) - use_filldouble: bool = setto_rules.get("use_filldouble", False) - # Params that MuJoCo writes UNCONDITIONALLY by value (e.g. position kp -> - # gainprm[0] = kp): there is no sentinel that means "unset", so when the - # UPROPERTY isn't overridden we must re-pass the value already inherited - # onto the element (via mjs_addActuator's default class), not a sentinel - # that would clobber it. Value is the verbatim C++ expression to re-assert. - inherit_fields: Dict[str, str] = setto_rules.get("inherit_fields", {}) - # Pointer params (double x[1]) that MuJoCo skips when passed nullptr - # (if (kv) { ... }): pass nullptr when not overridden so the inherited / - # type-derived default survives, instead of a non-null pointer to a - # sentinel (which trips MuJoCo's validation and gets written literally). - nullable_params: set = set(setto_rules.get("nullable_params", [])) - - schema_attr_set = set(subtype_schema_attrs) | set(base_schema_attrs) - - setup_lines: List[str] = [] - call_args: List[str] = [] - for p in sig["params"]: - pname = p["name"] - ue_prop = param_renames.get(pname, pname) - param_in_schema = ue_prop in schema_attr_set - ue_type = type_mappings.get(ue_prop, default_type) - c_is_int = p["c_type"] == "int" - default_sentinel = "0" if c_is_int else "-1.0" - sentinel = fn_defaults.get(pname, default_sentinel) - proj = projections.get(pname) - # What to pass when the UPROPERTY is not overridden: re-assert the - # inherited element field for unconditional-write params, else the - # sentinel (correct only for params MuJoCo guards with `>= 0` / null). - notset = inherit_fields.get(pname, sentinel) - is_nullable = pname in nullable_params - - if p["array_dim"] is None: - # Scalar parameter. - if not param_in_schema: - call_args.append(notset) - continue - if ue_type == "FVector" and proj: - expr = f"bOverride_{ue_prop} ? (double){ue_prop}.{proj} : {notset}" - elif ue_type.startswith("TArray"): - # UE side is a TArray (e.g. cylinder.timeconst, where some - # actuator subtypes share the TArray mapping but - # mjs_setToCylinder takes a scalar). Take element [0]. - expr = ( - f"(bOverride_{ue_prop} && {ue_prop}.Num() > 0) ? " - f"(double){ue_prop}[0] : {notset}" - ) - elif c_is_int: - expr = f"bOverride_{ue_prop} ? (int){ue_prop} : {notset}" - else: - expr = f"bOverride_{ue_prop} ? (double){ue_prop} : {notset}" - call_args.append(expr) - continue - - # Fixed-array parameter. - dim = p["array_dim"] - buf_name = f"{pname}Buf" - - if nullable_arrays: - # DC-motor pattern: pass nullptr when the UPROPERTY is not overridden. - if use_filldouble and param_in_schema: - setup_lines.append( - f" double {buf_name}[{dim}]; FillDouble({buf_name}, {ue_prop});" - ) - call_args.append(f"bOverride_{ue_prop} ? {buf_name} : nullptr") - elif param_in_schema: - # No FillDouble helper — inline element-wise init. - inits = ",".join( - f" (bOverride_{ue_prop} && {ue_prop}.Num() > {i}) ? (double){ue_prop}[{i}] : {sentinel}" - for i in range(dim) - ) - setup_lines.append(f" double {buf_name}[{dim}] = {{{inits} }};") - call_args.append(f"bOverride_{ue_prop} ? {buf_name} : nullptr") - else: - # Not even declared on the URLab side — always nullptr. - call_args.append("nullptr") - continue - - if not param_in_schema: - if is_nullable: - # Not authored on the URLab side and MuJoCo skips it on null: - # pass nullptr so the inherited / type-derived default survives. - call_args.append("nullptr") - continue - # Force-sentinel array. - inits = ", ".join(sentinel for _ in range(dim)) - setup_lines.append(f" double {buf_name}[{dim}] = {{ {inits} }};") - call_args.append(buf_name) - continue - - if ue_type.startswith("TArray"): - if dim == 1: - setup_lines.append( - f" double {buf_name}[1] = {{ (bOverride_{ue_prop} && " - f"{ue_prop}.Num() > 0) ? (double){ue_prop}[0] : {sentinel} }};" - ) - else: - inits = ", ".join( - f"(bOverride_{ue_prop} && {ue_prop}.Num() > {i}) ? " - f"(double){ue_prop}[{i}] : {sentinel}" - for i in range(dim) - ) - setup_lines.append(f" double {buf_name}[{dim}] = {{ {inits} }};") - else: - # Scalar UE type wrapped in a 1-element buffer (e.g. position.kv:float -> double[1]). - if dim != 1: - raise RuntimeError( - f"non-1 array dim {dim} with scalar UE type for {fn_name}.{pname}" - ) - setup_lines.append( - f" double {buf_name}[1] = {{ bOverride_{ue_prop} ? " - f"(double){ue_prop} : {sentinel} }};" - ) - # Nullable pointer param: pass the buffer only when overridden, else - # nullptr so MuJoCo leaves the inherited value untouched. For a TArray - # source, an authored-but-empty array is also treated as unset — passing - # a sentinel buffer would trip MuJoCo's range check and short-circuit the - # setTo (e.g. skipping biastype) instead of just inheriting. - if is_nullable: - nn = f"bOverride_{ue_prop}" - if ue_type.startswith("TArray"): - nn = f"(bOverride_{ue_prop} && {ue_prop}.Num() > 0)" - call_args.append(f"{nn} ? {buf_name} : nullptr") - else: - call_args.append(buf_name) - - args_str = ", ".join(call_args) - body = "\n".join(setup_lines) - if body: - body += "\n" - # MuJoCo's mjs_setTo* return a diagnostic string on misuse (e.g. a param - # out of range). Surface it instead of silently discarding it. - call = ( - f" const char* SetToErr = {fn_name}(Element, {args_str});\n" - f" if (SetToErr && *SetToErr)\n" - f" {{\n" - f" UE_LOG(LogURLabBind, Warning, TEXT(\"{fn_name} on '%s': %s\"), " - f"*GetName(), UTF8_TO_TCHAR(SetToErr));\n" - f" }}\n" - ) - return f" {{\n{body}{call} }}\n" - - -def _mjs_fields_for(cat_rules: Dict[str, Any], mjspec: Dict[str, Any] | None) -> set: - """Pull the field-name set for a category's mjs_struct out of the - mjspec snapshot. Returns empty set if snapshot is missing — codegen - then falls back to using the raw schema attr name.""" - if not mjspec: - return set() - struct_name = cat_rules.get("mjs_struct") - if not struct_name: - return set() - return set(mjspec.get("structs", {}).get(struct_name, [])) - - -def _mjs_array_caps_for( - cat_rules: Dict[str, Any], - mjspec: Dict[str, Any] | None, -) -> Dict[str, int]: - """Pull {field_name: c_array_dim} for a category's mjs_struct from the - introspect snapshot, scoped to fixed-size C arrays only (skips scalars, - dynamic vectors, pointers). The TArray export loop uses these caps to - emit ``FMath::Min(prop.Num(), cap)`` so a future MuJoCo array-shrink - (e.g. ``gainprm[10]`` → ``gainprm[6]``) doesn't overrun memory. - """ - if not mjspec: - return {} - struct_name = cat_rules.get("mjs_struct") - if not struct_name: - return {} - introspect = mjspec.get("introspect") or {} - struct_table = introspect.get("structs", {}) - fields = struct_table.get(struct_name, {}).get("fields", []) - caps: Dict[str, int] = {} - for f in fields: - dim = f.get("array_dim") - if isinstance(dim, int) and dim > 1: - caps[f["name"]] = dim - return caps - - -def emit_subclass_files( - category: str, - cat_rules: Dict[str, Any], - schema: Dict[str, Any], - rules: Dict[str, Any], - public_root: str, - private_root: str, - mjspec: Dict[str, Any] | None = None, -) -> List[FileWrite]: - """Emit one .h and one .cpp per subtype for layout=single_uclass_per_file. - - For files that do not exist yet (new subclasses): emit from template. - For files that already exist: migrate (strip owned + insert tags + - strip dead methods) then inject between tags. Custom non-schema - methods and members in the existing files are preserved. - """ - writes: List[FileWrite] = [] - base_class = cat_rules["base_class_name"] - base_header_relpath = cat_rules["base_class_header"] - mjs_struct = cat_rules["mjs_struct"] - mjs_fields = _mjs_fields_for(cat_rules, mjspec) - mjs_array_caps = _mjs_array_caps_for(cat_rules, mjspec) - type_enum = cat_rules.get("type_enum_name") - pub_dir = os.path.join(public_root, cat_rules.get("subclass_directory_pub", "")) - priv_dir = os.path.join(private_root, cat_rules.get("subclass_directory_priv", "")) - - common_attrs = schema_attrs(schema, cat_rules["schema_common_block"]) - common_extras = cat_rules.get("schema_common_extra_attrs", []) - common_set = set(common_attrs) | set(common_extras) - - for subtype in cat_rules.get("subtypes", []): - sub_key = subtype["key"] - per_type_attrs = schema_subtype_attrs(schema, category, sub_key) - attrs = [a for a in per_type_attrs if a not in common_set] - - # Setto-consumed attrs feed the silent-skip path in emit_schema_for_attrs. - # We compute them up front because the setTo block is prepended AFTER - # the per-attr export emission but conceptually fires first. - setto_rules = (cat_rules.get("subtype_setto") or {}).get(sub_key) - setto_consumed = _setto_consumed_attrs(setto_rules, mjspec) - - emitted = emit_schema_for_attrs( - attrs, rules, element_name=category, - category_label=base_class.removeprefix("UMj"), - apply_canonicalizations=False, # base owns the canon block - mjs_fields=mjs_fields, - consumed_by_setto=setto_consumed, - mjs_array_caps=mjs_array_caps, - ) - - # Per-subtype ``mjs_setTo*`` preset call. Codegen owns this snippet — it - # lands at the TOP of the CODEGEN_EXPORT block so every regeneration - # keeps the call in sync with the C API. The call body is derived from - # the function signature in the mjspec snapshot, NOT a hand-written - # literal in codegen_rules.json. See ``_emit_setto_call``. - if setto_rules: - setto_block = _emit_setto_call( - sub_key, - setto_rules, - rules, - mjspec, - subtype_schema_attrs=per_type_attrs, - base_schema_attrs=common_attrs, - ) - if setto_block: - emitted = EmittedSchema( - properties_h=emitted.properties_h, - imports_cpp=emitted.imports_cpp, - exports_cpp=setto_block + emitted.exports_cpp, - ) - - class_name = subtype["class_name"] - header_filename = subtype["header"] - class_file_stem = os.path.splitext(header_filename)[0] - header_path = os.path.join(pub_dir, header_filename) - source_path = os.path.join(priv_dir, class_file_stem + ".cpp") - - # ``fully_emitted: true`` on a subtype rule means codegen owns the - # entire file (header + source). Existing files get overwritten - # from the template every regen — equivalent to "delete and let - # the codegen recreate", but stable across runs. Use this for - # pure-stub subclasses (no extra includes, no extra methods, ctor - # only sets Type). The ``Scripts/codegen/_audit_banner_candidates.py`` - # helper identifies the safe subset. - fully_emitted = bool(subtype.get("fully_emitted")) - - if os.path.exists(header_path) and not fully_emitted: - # Existing subclass file — inject between pre-prepped tags. - # Diagnose missing markers loudly so a typo in the marker pair - # (or a forgotten pre-prep step on a hand-rolled subclass) - # surfaces here rather than as wrong runtime behaviour. - with open(header_path, "r", encoding="utf-8") as f: - h_text = f.read() - h_text, _ = _inject_or_diag( - h_text, "PROPERTIES", emitted.properties_h, - host_path=header_path, host_kind="subclass header", - block_label="per-subtype UPROPERTY block", - diag_source="subclass_inject", - ) - writes.append(FileWrite(path=header_path, content=h_text)) - - if os.path.exists(source_path): - with open(source_path, "r", encoding="utf-8") as f: - c_text = f.read() - c_text, _ = _inject_or_diag( - c_text, "IMPORT", emitted.imports_cpp, - host_path=source_path, host_kind="subclass source", - block_label="per-subtype import block", - diag_source="subclass_inject", - ) - c_text, _ = _inject_or_diag( - c_text, "EXPORT", emitted.exports_cpp, - host_path=source_path, host_kind="subclass source", - block_label="per-subtype export block", - diag_source="subclass_inject", - ) - writes.append(FileWrite(path=source_path, content=c_text)) - else: - _diag_add( - f"[diagnostic] subclass source '{source_path}' does not " - f"exist alongside its header; import + export blocks " - f"could not be injected.", - source="subclass_inject", - ) - else: - # Brand-new file OR ``fully_emitted: true`` → write the whole - # file from the template. - # - # Safety check on the ``fully_emitted`` overwrite path: if the - # existing file carries non-template content (extra includes - # beyond the template, hand-written methods, ctor extras not - # covered by ``extra_constructor``), the rewrite would silently - # destroy it. The audit-helper script - # (Scripts/codegen/_audit_banner_candidates.py) is the - # expected pre-flip gate; this is a defence-in-depth diagnostic - # for cases where someone flips ``fully_emitted: true`` without - # running the helper first. - if fully_emitted and os.path.exists(source_path): - _audit_banner_safety(source_path, subtype) - - include_path = _resolve_include_path(public_root, header_path) - ctor_body = _subclass_constructor_body(category, subtype, type_enum) - - header_text = SUBCLASS_HEADER_TEMPLATE.format( - Copyright=COPYRIGHT_BLOCK, - BaseInclude=base_header_relpath, - ClassName=class_name, - ClassFileStem=class_file_stem, - ElementName=sub_key, - BaseClassName=base_class, - TypeEnumValue=subtype.get("enum_value", "Unknown"), - Properties=emitted.properties_h or " // (no type-specific schema attrs)\n", - MjsStruct=mjs_struct, - ) - - source_text = SUBCLASS_SOURCE_TEMPLATE.format( - Copyright=COPYRIGHT_BLOCK, - IncludePath=include_path, - ClassName=class_name, - ConstructorBody=ctor_body, - Imports=emitted.imports_cpp or " // (no type-specific schema imports)\n", - Exports=emitted.exports_cpp or " // (no type-specific schema exports)\n", - MjsStruct=mjs_struct, - ) - - writes.append(FileWrite(path=header_path, content=header_text)) - writes.append(FileWrite(path=source_path, content=source_text)) - - return writes - - -# --------------------------------------------------------------------------- -# Pipeline: base-class injection (no_subclasses + multi_uclass base) -# --------------------------------------------------------------------------- - -def emit_base_class_injection( - category: str, - cat_rules: Dict[str, Any], - schema: Dict[str, Any], - rules: Dict[str, Any], - public_root: str, - private_root: str, - mjspec: Dict[str, Any] | None = None, -) -> List[FileWrite]: - """Migrate (if needed) + inject codegen output between boundary - tags in existing base class .h / .cpp files.""" - writes: List[FileWrite] = [] - base_class = cat_rules["base_class_name"] - base_header_relpath = cat_rules["base_class_header"] - mjs_fields = _mjs_fields_for(cat_rules, mjspec) - mjs_array_caps = _mjs_array_caps_for(cat_rules, mjspec) - common_attrs = schema_attrs(schema, cat_rules["schema_common_block"]) - # Extras: attrs that URLab puts on the base class even though MJCF - # schema lists them per-subtype (e.g. gainprm/biasprm/dynprm). - common_attrs = list(common_attrs) + cat_rules.get("schema_common_extra_attrs", []) - - emitted = emit_schema_for_attrs( - common_attrs, rules, element_name=category, category_label=base_class.removeprefix("UMj"), - mjs_fields=mjs_fields, - mjs_array_caps=mjs_array_caps, - ) - - header_path = os.path.join(public_root, base_header_relpath) - rel = os.path.relpath(header_path, public_root) - source_path = os.path.join(private_root, rel.replace(".h", ".cpp")) - - if os.path.exists(header_path): - with open(header_path, "r", encoding="utf-8") as f: - h_text = f.read() - new_h, ok = _inject_or_diag( - h_text, "PROPERTIES", emitted.properties_h, - host_path=header_path, host_kind="base header", - block_label=f"category '{category}' UPROPERTY block", - diag_source="base_inject", - ) - if ok: - writes.append(FileWrite(path=header_path, content=new_h)) - else: - _diag_add( - f"[diagnostic] category '{category}' has a rule but base header " - f"'{header_path}' does not exist; codegen emitted no writes for " - f"this category.", - source="base_inject", - ) - - if os.path.exists(source_path): - with open(source_path, "r", encoding="utf-8") as f: - c_text = f.read() - c_text, ok_i = _inject_or_diag( - c_text, "IMPORT", emitted.imports_cpp, - host_path=source_path, host_kind="base source", - block_label=f"category '{category}' import block", - diag_source="base_inject", - ) - # ``xml_passthrough_emission`` routes per-attr export through the - # XML_PASSTHROUGH block, NOT the EXPORT block — the mjs-side - # ``Element->field = ...`` writes don't apply (flexcomp). - # ``disable_mjs_export_emission`` covers categories that have no - # corresponding mjs struct so the export block can't compile even - # if injected (inertial — writes through ParentBody->ipos/iquat - # in a hand-rolled RegisterToSpec). Both flags suppress EXPORT - # injection AND its missing-marker diagnostic. - skip_export = ( - cat_rules.get("xml_passthrough_emission") - or cat_rules.get("disable_mjs_export_emission") - ) - ok_e = False - if not skip_export: - c_text, ok_e = _inject_or_diag( - c_text, "EXPORT", emitted.exports_cpp, - host_path=source_path, host_kind="base source", - block_label=f"category '{category}' export block", - diag_source="base_inject", - ) - ok_x = False - if cat_rules.get("xml_passthrough_emission"): - xml_body = emit_xml_passthrough_body(common_attrs, rules, element_name=category) - c_text, ok_x = _inject_or_diag( - c_text, "XML_PASSTHROUGH", xml_body, - host_path=source_path, host_kind="base source", - block_label=f"category '{category}' xml_passthrough block", - diag_source="base_inject", - ) - if ok_i or ok_e or ok_x: - writes.append(FileWrite(path=source_path, content=c_text)) - else: - _diag_add( - f"[diagnostic] category '{category}' base source '{source_path}' " - f"does not exist; import + export blocks could not be injected.", - source="base_inject", - ) - - return writes - - -# --------------------------------------------------------------------------- -# Pipeline: multi-UCLASS base (site / tendon / equality) -# --------------------------------------------------------------------------- - -def emit_multi_uclass( - category: str, - cat_rules: Dict[str, Any], - schema: Dict[str, Any], - rules: Dict[str, Any], - public_root: str, - private_root: str, - mjspec: Dict[str, Any] | None = None, -) -> List[FileWrite]: - """For categories whose layout is multi_uclass, emit: - - the base class block (between PROPERTIES / IMPORT / EXPORT tags) - - per-subtype UCLASS skeletons appended to the base file (first-run only) - - per-subtype blocks (between SUBCLASS__PROPERTIES / - IMPORT / EXPORT tags)""" - # Run the base injection first (handles migration + properties). - writes = emit_base_class_injection(category, cat_rules, schema, rules, public_root, private_root, mjspec=mjspec) - - base_header_relpath = cat_rules["base_class_header"] - header_path = os.path.join(public_root, base_header_relpath) - rel = os.path.relpath(header_path, public_root) - source_path = os.path.join(private_root, rel.replace(".h", ".cpp")) - - if not os.path.exists(header_path): - return writes - - # Re-load to capture the post-base-injection state. - by_path = {w.path: w.content for w in writes} - h_text = by_path.get(header_path) - if h_text is None: - with open(header_path, "r", encoding="utf-8") as f: - h_text = f.read() - c_text = by_path.get(source_path) - if c_text is None and os.path.exists(source_path): - with open(source_path, "r", encoding="utf-8") as f: - c_text = f.read() - elif c_text is None: - c_text = "" - - base_class = cat_rules["base_class_name"] - mjs_fields = _mjs_fields_for(cat_rules, mjspec) - mjs_array_caps = _mjs_array_caps_for(cat_rules, mjspec) - common_attrs = set(schema_attrs(schema, cat_rules["schema_common_block"])) - common_attrs |= set(cat_rules.get("schema_common_extra_attrs", [])) - - for subtype in cat_rules.get("subtypes", []): - sub_key = subtype["key"] - per_type_attrs = schema_subtype_attrs(schema, category, sub_key) - attrs = [a for a in per_type_attrs if a not in common_attrs] - emitted = emit_schema_for_attrs( - attrs, rules, element_name=category, - category_label=base_class.removeprefix("UMj"), - apply_canonicalizations=False, # base owns the canon block - mjs_fields=mjs_fields, - mjs_array_caps=mjs_array_caps, - ) - type_tag = subtype["enum_value"] - # Diagnose missing per-subtype marker pairs ONLY when there's a - # non-empty body to inject. Subtypes whose schema attrs are entirely - # in the common set (or whose mjs export is handled elsewhere) emit - # nothing per-subtype; not pre-prepping a marker pair for those is - # legitimate, so suppress the diagnostic. - h_text, _ = _inject_or_diag( - h_text, f"SUBCLASS_{type_tag}_PROPERTIES", emitted.properties_h, - host_path=header_path, host_kind="multi_uclass base header", - block_label=f"subtype '{sub_key}' UPROPERTY block", - diag_source="multi_uclass_inject", - ) - if c_text: - c_text, _ = _inject_or_diag( - c_text, f"SUBCLASS_{type_tag}_IMPORT", emitted.imports_cpp, - host_path=source_path, host_kind="multi_uclass base source", - block_label=f"subtype '{sub_key}' import block", - diag_source="multi_uclass_inject", - ) - c_text, _ = _inject_or_diag( - c_text, f"SUBCLASS_{type_tag}_EXPORT", emitted.exports_cpp, - host_path=source_path, host_kind="multi_uclass base source", - block_label=f"subtype '{sub_key}' export block", - diag_source="multi_uclass_inject", - ) - - # Rewrite the merged writes (drop stale base writes; replace with merged). - writes = [w for w in writes if w.path != header_path and w.path != source_path] - writes.append(FileWrite(path=header_path, content=h_text)) - if os.path.exists(source_path) or c_text: - writes.append(FileWrite(path=source_path, content=c_text)) - return writes - - -# --------------------------------------------------------------------------- -# Pipeline: view structs -# --------------------------------------------------------------------------- - -_VIEW_FIELD_TYPE_MAP = { - "int": "int*", "mjtByte": "mjtByte*", "mjtBool": "mjtBool*", - "mjtNum": "mjtNum*", "float": "float*", "char": "char*", -} - - -def _view_field_type_cpp(c_type: str, field_name: str = "") -> str: - mapped = _VIEW_FIELD_TYPE_MAP.get(c_type) - if mapped is None: - # Don't silently default a new MuJoCo scalar type to mjtNum* — that - # ships a wrong (often 8-byte-over-1-byte) pointer with no warning. - # A bump that introduces a new type should surface here loudly. - _diag_add( - f"[diagnostic] view field '{field_name or '?'}' has unmapped C " - f"type '{c_type}'; defaulting to 'mjtNum*'. A MuJoCo bump likely " - f"introduced a new scalar type; add it to _VIEW_FIELD_TYPE_MAP.", - source="view_field_type", - ) - return "mjtNum*" - return mapped - - -_MJ_M_MACRO_RE = re.compile(r"MJ_M\(\s*(\w+)\s*\)") -# Any MJ_*( ... ) macro that survives _expand_stride is an unhandled -# stride shape — emit invalid C++ in MjBind.h. The guard fires a -# diagnostic so a MuJoCo bump that introduces new stride macros (e.g. -# MJ_D(...) for mjData, MJ_V(...) for variable-size views) surfaces -# loudly rather than landing as a compile error far downstream. -_RESIDUAL_MJ_MACRO_RE = re.compile(r"\bMJ_[A-Z]\(\s*\w+\s*\)") - - -def _expand_stride(stride: str) -> str: - """Convert mjxmacro stride expressions to plain C++ expressions usable - in MjBind.h. ``MJ_M(nuser_jnt)`` -> ``m->nuser_jnt``. - - Fires a diagnostic if any ``MJ_*(...)`` macro survives the - substitution — that's an unhandled stride shape we'd otherwise - silently emit into MjBind.h verbatim.""" - expanded = _MJ_M_MACRO_RE.sub(r"m->\1", stride) - residual = _RESIDUAL_MJ_MACRO_RE.findall(expanded) - if residual: - _diag_add( - f"[diagnostic] _expand_stride saw stride '{stride}' with " - f"unhandled MuJoCo macro(s) {residual}. Result '{expanded}' " - f"will land in MjBind.h verbatim and likely fail to compile. " - f"Teach _expand_stride about the new macro shape.", - source="expand_stride", - ) - return expanded - - -def emit_view_structs(rules: Dict[str, Any], mjxmacro: Dict[str, Any]) -> Dict[str, Tuple[str, str]]: - """For each view in `rules['views']`, return (fields_block, bind_block).""" - out: Dict[str, Tuple[str, str]] = {} - views: Dict[str, Any] = rules.get("views", {}) - - for view_name, view_def in views.items(): - renames: Dict[str, str] = view_def.get("field_renames", {}) - block_index_var: Dict[str, str] = view_def.get("block_index_var", {}) - derived_index_vars: Dict[str, str] = view_def.get("derived_index_vars", {}) - include_blocks: List[str] = view_def.get("include_blocks", []) - data_fields: Dict[str, Dict[str, str]] = view_def.get("data_fields", {}) - - # Collect the set of pointer-field names this view defines, so we - # don't accidentally emit a derived local variable that shadows - # one of them. - pointer_field_names: set = set() - for block in include_blocks: - for entry in mjxmacro["mjmodel_pointers"].get(block, []): - pointer_field_names.add(renames.get(entry["name"], entry["name"])) - for data_name in data_fields: - pointer_field_names.add(renames.get(data_name, data_name)) - - fields_lines: List[str] = [] - bind_lines: List[str] = [] - - # Derived index vars: emit at top of the bind block, but suppress - # ones whose name collides with a pointer field. (Caller should - # pick non-colliding names in the rules.) - for var, expr in derived_index_vars.items(): - if var in pointer_field_names: - continue - bind_lines.append(f" const int {var} = {expr};") - - # mjModel pointer fields. - # - # Stride-1 ``int`` entries (e.g. ``geom_type``, ``body_mocapid``, - # ``actuator_trntype``) are emitted as DEREFERENCED scalars rather - # than pointers — there's exactly one int per element, and the - # consumer always wants the value, not the pointer. Matches the - # ergonomic shape the hand-written MjBind.h had before activation. - # Mat-vec entries (``geom_size`` stride 3, ``jnt_solref`` stride - # mjNREF, etc.) stay as pointers since consumers index into them. - for block in include_blocks: - entries = mjxmacro["mjmodel_pointers"].get(block, []) - index_var = block_index_var.get(block, "id") - for entry in entries: - name = entry["name"] - view_field = renames.get(name, name) - stride = _expand_stride(entry["stride"]) - is_scalar_int = (entry["type"] == "int" and entry["stride"] == "1") - if is_scalar_int: - fields_lines.append(f" int {view_field};") - bind_lines.append( - f" {view_field} = m->{name}[{index_var}];" - ) - else: - cpp_type = _view_field_type_cpp(entry["type"], name) - fields_lines.append(f" {cpp_type} {view_field};") - bind_lines.append( - f" {view_field} = m->{name} + {index_var} * {stride};" - ) - - # mjData fields (explicitly listed per view). - # - # Each spec dict has ``index_var`` + ``stride``. Some fields don't - # fit the simple ``d->src + index_var * stride`` formula — e.g. - # SensorView::sensordata, where the right slice depends on a - # bounds-checked sensor_adr lookup. For those, set ``bind_override`` - # in the rule to a C++ statement that assigns the field; the field - # decl is still emitted at the standard cpp_type. This kills the - # fragile "codegen emits wrong code, hand-written override after - # the marker" pattern. - for data_name, spec in data_fields.items(): - # ``_note_*`` keys document the data_fields entries; skip them. - if data_name.startswith("_note_"): - continue - cpp_type = "mjtNum*" - view_field = renames.get(data_name, data_name) - fields_lines.append(f" {cpp_type} {view_field};") - bind_override = spec.get("bind_override") - if bind_override: - bind_lines.append(f" {bind_override}") - else: - index_var = spec.get("index_var", "id") - stride = _expand_stride(spec.get("stride", "1")) - bind_lines.append(f" {view_field} = d->{data_name} + {index_var} * {stride};") - - fields_block = "\n".join(fields_lines) + ("\n" if fields_lines else "") - bind_block = "\n".join(bind_lines) + ("\n" if bind_lines else "") - out[view_name] = (fields_block, bind_block) - return out - - -def emit_bind_h_injection( - rules: Dict[str, Any], mjxmacro: Dict[str, Any], bind_h_path: str -) -> Optional[FileWrite]: - """Read MjBind.h, fill each view struct's - ``// --- CODEGEN_VIEW__FIELDS_START/END ---`` and - ``// --- CODEGEN_VIEW__BIND_START/END ---`` tag pairs with - the codegen-emitted content. MjBind.h must already have the tag - pairs pre-prepped (hand-edited).""" - if not os.path.exists(bind_h_path): - return None - with open(bind_h_path, "r", encoding="utf-8") as f: - text = f.read() - - views = emit_view_structs(rules, mjxmacro) - new_text = text - for view_name, (fields_block, bind_block) in views.items(): - new_text, _ = _inject_or_diag( - new_text, f"VIEW_{view_name}_FIELDS", fields_block, - host_path=bind_h_path, host_kind="MjBind.h", - block_label=f"view struct '{view_name}' fields block (struct will ship empty)", - diag_source="bind_inject", - ) - new_text, _ = _inject_or_diag( - new_text, f"VIEW_{view_name}_BIND", bind_block, - host_path=bind_h_path, host_kind="MjBind.h", - block_label=f"view '{view_name}' Bind() body", - diag_source="bind_inject", - ) - - with open(bind_h_path, "r", encoding="utf-8") as f: - original = f.read() - if new_text == original: - return None - return FileWrite(path=bind_h_path, content=new_text) - - -# --------------------------------------------------------------------------- -# Pipeline: schema-for-tests header -# --------------------------------------------------------------------------- - -def kept_props_for_category( - cat: str, cat_rules: Dict[str, Any], - schema: Dict[str, Any], rules: Dict[str, Any], -) -> List[str]: - """Return the ordered prop names the codegen will emit for one - category. Order matches the canonical emission order: canon- - owned properties first, then per-attr in schema order. xml_enum - attrs lower to their ``ue_property`` because that's the hand- - declared UPROPERTY the completeness test resolves against.""" - elem_rules = rules.get("element_rules", {}).get(cat, {}) - elem_excl = set(elem_rules.get("exclude_attrs", [])) - elem_canons: List[str] = elem_rules.get("applies_canonicalizations", []) - canonicalizations = rules.get("canonicalizations", {}) - canon_absorbed: set = compute_canon_absorbed(elem_canons, canonicalizations) - global_excl = set(rules.get("global_exclusions", [])) - renames: Dict[str, str] = rules.get("property_renames", {}) - xml_enum_attrs = elem_rules.get("xml_enum_attrs", {}) - - attrs = list(schema_attrs(schema, cat_rules["schema_common_block"])) - # ``schema_common_extra_attrs`` covers UPROPERTYs schema attrs miss - # (frame's pos/quat/childclass come via this path because frame.attrs - # is empty in the snapshot). - for a in cat_rules.get("schema_common_extra_attrs", []): - if a not in attrs: - attrs.append(a) - - kept: List[str] = [] - for c in elem_canons: - cd = canonicalizations.get(c) - if cd and cd.get("emits_property"): - kept.append(cd["emits_property"]["name"]) - for a in iter_category_attrs(attrs, global_excl=global_excl, - elem_excl=elem_excl, - canon_absorbed=canon_absorbed): - if a in xml_enum_attrs: - kept.append(xml_enum_attrs[a]["ue_property"]) - continue - kept.append(renames.get(a, a)) - return kept - - -def emit_schema_for_tests(schema: Dict[str, Any], rules: Dict[str, Any]) -> FileWrite: - """Generate a C++ header that exposes schema attribute lists per - category to URLab automation tests. Every category emits a - ``CatProps()`` accessor returning a static ``TArray`` of - the codegen-owned UPROPERTY names.""" - lines: List[str] = [ - COPYRIGHT_BLOCK, - "#pragma once", - "", - "#include \"CoreMinimal.h\"", - "", - "// Autogenerated schema mirror for URLab.Codegen.* tests.", - "// Lists the canonical UPROPERTY name produced by codegen for every", - "// schema attribute on every category.", - "", - "namespace URLabCodegenSchemaForTests", - "{", - ] - - for cat, cat_rules in rules.get("categories", {}).items(): - kept_props = kept_props_for_category(cat, cat_rules, schema, rules) - ident = cat.replace(".", "_") - joined = ", ".join(f'TEXT("{p}")' for p in kept_props) - lines.append(f" inline const TArray& {ident.title()}Props()") - lines.append(" {") - lines.append(f" static const TArray P{{{joined}}};") - lines.append(" return P;") - lines.append(" }") - lines.append("") - - lines.append("}") - lines.append("") - text = "\n".join(lines) - - out_path = os.path.join(DEFAULT_TESTS_DIR, "MjSchemaForTests.h") - return FileWrite(path=out_path, content=text) - - -# --------------------------------------------------------------------------- -# Top-level orchestration -# --------------------------------------------------------------------------- - -@dataclass -class PhaseContext: - """All state a codegen emission phase needs. Phases mutate - ``writes`` and surface diagnostics through ``_diag_add``.""" - schema: Dict[str, Any] - rules: Dict[str, Any] - mjxmacro: Dict[str, Any] - mjspec: Optional[Dict[str, Any]] - public_root: str - private_root: str - bind_h_path: str - writes: List["FileWrite"] - - -@dataclass -class EmissionPhase: - """One step in the codegen pipeline. Phases are run in order; each - either appends to ``ctx.writes`` (emit phases) or has side effects - only (the diagnostics phase). The registry below makes adding a - phase a one-line insert rather than another `writes.extend(...)` - block in ``collect_all_writes``. - """ - name: str - fn: Callable[["PhaseContext"], None] - - -# --------------------------------------------------------------------------- -# Synthetic categories: whole-file banner-mode emission -# --------------------------------------------------------------------------- -# -# "Synthetic" because the USTRUCT doesn't correspond to one entry in the -# MJCF schema (which describes XML element shapes). Instead it mirrors a -# C struct laid out in mjxmacro.h (MJOPTION_FIELDS, MJSTATISTIC_FIELDS, -# MJVISUAL_*_FIELDS) — the runtime-only state URLab exposes as USTRUCT -# blueprint properties. -# -# Files are emitted in BANNER mode (no // --- CODEGEN_*_START/END --- -# tags). The whole .h / .cpp pair is codegen-owned; hand-edits get -# clobbered on next regen. -# -# Rule shape (under top-level ``synthetic_categories`` in codegen_rules.json): -# "MjStatistic": { -# "ue_struct_name": "FMjStatistic", -# "mjxmacro_block": "MJSTATISTIC_FIELDS", -# "public_header": "MuJoCo/Generated/MjStatistic.h", -# "private_source": "MuJoCo/Generated/MjStatistic.cpp", -# "category_label": "MuJoCo|Statistic", -# "exclude_fields": [], -# "per_field_meta": { "extent": "ClampMin=0.0" }, -# "urlab_extra_fields": [], // empty for pure mirrors -# } - - -_CPP_TO_UE_TYPE = { - "int": "int32", - "float": "float", - "mjtNum": "double", - "mjtByte": "uint8", - "mjtBool": "bool", -} - - -def _ue_field_name(c_name: str) -> str: - """snake_case mjxmacro name -> PascalCase UE field. Preserves URLab's - naming convention from the hand-written MjSimOptions.h (ls_iterations - -> LsIterations, ccd_iterations -> CCD_Iterations is a deliberate - one-off — synthetic_categories rules can override via field_renames).""" - parts = c_name.split("_") - return "".join(p[:1].upper() + p[1:] for p in parts if p) - - -# `_VEC3_CONVERT_FMT` lives in `_codegen_core`. - - -def _emit_synth_mirror_properties( - fields: List[Dict[str, Any]], - def_: Dict[str, Any], - category_label: str, -) -> Tuple[List[str], List[Dict[str, Any]]]: - """Emit the UPROPERTY block for the mjxmacro mirror fields and - return the parallel ``fields_meta`` list. ``fields_meta`` carries - the per-field shape (c_name, ue_name, ue_type, is_vec, count) the - out-of-line ApplyTo emitters need.""" - exclude = set(def_.get("exclude_fields", [])) - field_renames: Dict[str, str] = def_.get("field_renames", {}) - field_types: Dict[str, str] = def_.get("field_types", {}) - all_fields_type: Optional[str] = def_.get("all_fields_type") - field_defaults: Dict[str, str] = def_.get("field_defaults", {}) - - prop_lines: List[str] = [] - fields_meta: List[Dict[str, Any]] = [] - for f in fields: - c_name = f["name"] - if c_name in exclude: - continue - ue_name = field_renames.get(c_name, _ue_field_name(c_name)) - c_type = f["type"] - is_vec = f.get("is_vec", False) - # Accept both str ("3") and int (3) — the mjxmacro snapshot - # currently emits strings, but a future shape bump that yields - # ints must NOT silently collapse vec3 fields to ``TArray``. - raw_count = f.get("count", 1) - if isinstance(raw_count, int): - count = raw_count - elif isinstance(raw_count, str) and raw_count.isdigit(): - count = int(raw_count) - else: - count = 1 - - if c_name in field_types: - ue_type = field_types[c_name] - elif all_fields_type: - ue_type = all_fields_type - elif is_vec and count == 3: - ue_type = "FVector" - elif is_vec and count == 4: - ue_type = "FLinearColor" - elif is_vec: - ue_type = "TArray" - else: - ue_type = _CPP_TO_UE_TYPE.get(c_type, "float") - - default_override = field_defaults.get(ue_name) or field_defaults.get(c_name) - - cat = f"MuJoCo|{category_label}" - toggle = override_toggle_name(ue_name) - default = default_override if default_override else _attr_default_value(ue_type) - prop_lines.append( - f' UPROPERTY(EditAnywhere, Category = "{cat}", meta=(InlineEditConditionToggle))\n' - f' bool {toggle} = false;\n\n' - f' UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "{cat}", meta=(EditCondition="{toggle}"))\n' - f' {ue_type} {ue_name} = {default};\n\n' - ) - fields_meta.append({ - "c_name": c_name, - "ue_name": ue_name, - "ue_type": ue_type, - "is_vec": is_vec, - "count": count, - }) - return prop_lines, fields_meta - - -def _emit_synth_extras_properties( - urlab_extras: List[Dict[str, Any]], - category_label: str, -) -> Tuple[List[str], List[Dict[str, Any]]]: - """Emit UPROPERTY blocks for the URLab-extra fields (non-mjxmacro - additions like ``MemoryMB`` on MjOption). Returns prop_lines + - extras_meta. Each extra carries optional ``has_override``, - ``edit_condition``, ``meta`` knobs that the mirror loop above - doesn't surface.""" - prop_lines: List[str] = [] - extras_meta: List[Dict[str, Any]] = [] - for extra in urlab_extras: - ue_name = extra["name"] - ue_type = extra["type"] - default = extra.get("default", _attr_default_value(ue_type)) - sub_category = extra.get("sub_category") - has_override = extra.get("has_override", True) - edit_condition = extra.get("edit_condition") - meta_extra = extra.get("meta") - cat = f"MuJoCo|{category_label}" - if sub_category: - cat = f"{cat}|{sub_category}" - if has_override: - toggle = override_toggle_name(ue_name) - meta_inner = f'EditCondition="{toggle}"' - if meta_extra: - meta_inner = f'{meta_inner}, {meta_extra}' - prop_lines.append( - f' UPROPERTY(EditAnywhere, Category = "{cat}", meta=(InlineEditConditionToggle))\n' - f' bool {toggle} = false;\n\n' - f' UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "{cat}", meta=({meta_inner}))\n' - f' {ue_type} {ue_name} = {default};\n\n' - ) - else: - meta_parts = [] - if edit_condition: - meta_parts.append(f'EditCondition="{edit_condition}"') - if meta_extra: - meta_parts.append(meta_extra) - meta_str = ", ".join(meta_parts) - meta_clause = f', meta=({meta_str})' if meta_str else "" - prop_lines.append( - f' UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "{cat}"{meta_clause})\n' - f' {ue_type} {ue_name} = {default};\n\n' - ) - extras_meta.append({"ue_name": ue_name, "type": ue_type, - "has_override": has_override}) - return prop_lines, extras_meta - - -def _emit_synth_inline_apply_template( - fields_meta: List[Dict[str, Any]], - field_skip_apply: Set[str], -) -> str: - """Emit the templated inline ``ApplyTo`` body for pure-mirror - synthetic structs (mjStatistic etc.). The shape mirrors the - out-of-line ApplyToSpec but lives in the header so it's reusable - against any compatible C struct (mjStatistic, mjVisualRgba).""" - apply_lines: List[str] = [] - for fm in fields_meta: - c_name = fm["c_name"] - ue_name = fm["ue_name"] - if c_name in field_skip_apply: - continue - if fm["ue_type"] == "FLinearColor": - apply_lines.append( - f" Dst.{c_name}[0] = static_cast({ue_name}.R);\n" - f" Dst.{c_name}[1] = static_cast({ue_name}.G);\n" - f" Dst.{c_name}[2] = static_cast({ue_name}.B);\n" - f" Dst.{c_name}[3] = static_cast({ue_name}.A);" - ) - elif fm["ue_type"] == "FVector": - apply_lines.append( - f" Dst.{c_name}[0] = static_cast({ue_name}.X);\n" - f" Dst.{c_name}[1] = static_cast({ue_name}.Y);\n" - f" Dst.{c_name}[2] = static_cast({ue_name}.Z);" - ) - elif fm["is_vec"]: - apply_lines.append( - f" for (int32 I = 0; I < {ue_name}.Num() && I < {fm['count']}; ++I)\n" - f" Dst.{c_name}[I] = static_cast({ue_name}[I]);" - ) - else: - apply_lines.append(f" Dst.{c_name} = static_cast({ue_name});") - return ( - f" /** @brief Mirror every field into a runtime C struct. */\n" - f" template \n" - f" void ApplyTo(TDst& Dst) const\n" - f" {{\n" - f"{chr(10).join(apply_lines)}\n" - f" }}\n" - ) - - -def _emit_synthetic_struct_files( - cat_name: str, def_: Dict[str, Any], mjxmacro: Dict[str, Any], - public_root: str, private_root: str, -) -> List["FileWrite"]: - """Emit the .h + .cpp pair for one synthetic_categories entry. - - Currently supports the mjxmacro_block source (X-macro field list). - """ - block_name = def_.get("mjxmacro_block") - if not block_name: - return [] - fields = mjxmacro.get("struct_fields", {}).get(block_name, []) - if not fields: - _diag_add( - f"[diagnostic] synthetic_categories['{cat_name}'] references " - f"mjxmacro_block='{block_name}' which is empty or missing in " - f"the snapshot. Skipping emission.", - source="synthetic_categories", - ) - return [] - - ue_struct = def_["ue_struct_name"] - category_label = def_.get("category_label", "MuJoCo") - field_apply_mode: Dict[str, str] = def_.get("field_apply_to_spec_mode", {}) - field_skip_apply: set = set(def_.get("field_skip_apply", [])) - public_relpath = def_["public_header"] - private_relpath = def_.get("private_source") - extra_includes: List[str] = def_.get("include_directives", []) - urlab_extras: List[Dict[str, Any]] = def_.get("urlab_extra_fields", []) - apply_extras_function: Optional[str] = def_.get("apply_extras_function") - emit_apply_methods: bool = bool(def_.get("emit_apply_methods", False)) - - mirror_lines, fields_meta = _emit_synth_mirror_properties( - fields, def_, category_label, - ) - extras_lines, _extras_meta = _emit_synth_extras_properties( - urlab_extras, category_label, - ) - properties_block = "".join(mirror_lines + extras_lines) - c_struct = def_.get("c_struct_type", block_name.lower().replace("_fields", "")) - - # ---------- header content ---------- - extra_includes_block = "".join(f'#include "{inc}"\n' for inc in extra_includes) - header_basename = os.path.basename(public_relpath).replace('.h', '') - public_path = os.path.join(public_root, public_relpath.replace("/", os.sep)) - - # Method decls in the header. If emit_apply_methods=True, declare - # ApplyToSpec / ApplyOverridesToModel as forward decls (definitions - # land in the .cpp). Otherwise emit an inline-templated ApplyTo for - # pure-mirror structs (mjStatistic etc.). - if emit_apply_methods: - method_decls = ( - f" /** @brief Writes every mirror field plus URLab extras into the " - f"runtime spec. Used by articulations to set child spec options. */\n" - f" void ApplyToSpec(mjSpec* Spec) const;\n\n" - f" /** @brief Applies only override-toggled mirror fields to a " - f"compiled model (post-compile override path). */\n" - f" void ApplyOverridesToModel(mjModel* Model) const;\n" - ) - else: - method_decls = _emit_synth_inline_apply_template( - fields_meta, field_skip_apply, - ) - - # Forward-decl the extras function so the codegen-emitted .cpp can - # call it without #including a hand-written header. Operator writes - # the body in *Extras.cpp. - extras_forward_decl = "" - if apply_extras_function and urlab_extras: - extras_forward_decl = ( - f"// Forward-decl: hand-written body lives in " - f"{os.path.basename(public_relpath).replace('.h', '')}Extras.cpp.\n" - f"struct {ue_struct};\n" - f"void {apply_extras_function}(" - f"mjOption* Opt, const {ue_struct}& Self, " - f"mjSpec* Spec, mjModel* Model = nullptr);\n\n" - ) - - header_content = ( - f"{COPYRIGHT_BLOCK}\n" - f"#pragma once\n\n" - f"#include \"CoreMinimal.h\"\n" - f"{extra_includes_block}" - f"#include \"{header_basename}.generated.h\"\n\n" - f"{extras_forward_decl}" - f"// Mirror of MuJoCo's ``{c_struct}`` ({block_name}). Codegen-owned —\n" - f"// hand-edits get clobbered on next regen. Add fields by editing\n" - f"// codegen_rules.json[synthetic_categories.{cat_name}].\n" - f"USTRUCT(BlueprintType)\n" - f"struct URLAB_API {ue_struct}\n" - f"{{\n" - f" GENERATED_BODY()\n\n" - f"{properties_block}" - f"{method_decls}" - f"}};\n" - ) - - writes: List[FileWrite] = [FileWrite(path=public_path, content=header_content)] - - if emit_apply_methods and private_relpath: - cpp_content = _emit_synth_apply_methods_cpp( - ue_struct=ue_struct, - fields_meta=fields_meta, - field_apply_mode=field_apply_mode, - field_skip_apply=field_skip_apply, - apply_extras_function=apply_extras_function, - urlab_extras=urlab_extras, - public_relpath=public_relpath, - header_basename=header_basename, - ) - private_path = os.path.join(private_root, private_relpath.replace("/", os.sep)) - writes.append(FileWrite(path=private_path, content=cpp_content)) - - return writes - - -def _emit_synth_apply_methods_cpp( - *, - ue_struct: str, - fields_meta: List[Dict[str, Any]], - field_apply_mode: Dict[str, str], - field_skip_apply: Set[str], - apply_extras_function: Optional[str], - urlab_extras: List[Dict[str, Any]], - public_relpath: str, - header_basename: str, -) -> str: - """Emit the out-of-line ``ApplyToSpec`` and ``ApplyOverridesToModel`` - bodies for a synthetic struct whose mirror writes go through mjSpec - + mjModel (mjOption is the canonical case). - - Both methods gate every field write with ``bOverride_X``. The earlier - ``unconditional`` SPEC default silently clobbered MJCF-XML defaults - with whatever the UE struct happened to carry, even for fields the - user never authored. ``field_apply_mode`` now selects only the - per-FVector conversion flavour (``vec3_cm`` / ``vec3_y_flip`` / - ``pos``); the toggle guard is applied uniformly. - """ - apply_to_spec_lines: List[str] = [" if (!Spec) return;"] - apply_to_model_lines: List[str] = [" if (!Model) return;"] - for fm in fields_meta: - c_name = fm["c_name"] - ue_name = fm["ue_name"] - ue_type = fm["ue_type"] - if c_name in field_skip_apply: - continue - toggle = override_toggle_name(ue_name) - spec_mode = field_apply_mode.get(c_name, "") - if ue_type == "FVector": - # Strip a legacy "guarded_" prefix; the guard is now uniform. - conv = spec_mode.replace("guarded_", "") if spec_mode.startswith("guarded_") else spec_mode - if conv not in _VEC3_CONVERT_FMT: - conv = "vec3_y_flip" - stmt_spec_inner = _VEC3_CONVERT_FMT[conv].format( - c=f"Spec->option.{c_name}", ue=ue_name, - ) - stmt_model_inner = _VEC3_CONVERT_FMT[conv].format( - c=f"Model->opt.{c_name}", ue=ue_name, - ) - elif ue_type.startswith("EMj"): - stmt_spec_inner = f"Spec->option.{c_name} = static_cast({ue_name});" - stmt_model_inner = f"Model->opt.{c_name} = static_cast({ue_name});" - else: - stmt_spec_inner = f"Spec->option.{c_name} = {ue_name};" - stmt_model_inner = f"Model->opt.{c_name} = {ue_name};" - - apply_to_spec_lines.append(f" if ({toggle}) {{ {stmt_spec_inner} }}") - apply_to_model_lines.append(f" if ({toggle}) {{ {stmt_model_inner} }}") - - if apply_extras_function and urlab_extras: - # Spec / Model already null-checked by the early return at the - # top of each body, so the ternary is dead — pass the address - # directly. - apply_to_spec_lines.append( - f" {apply_extras_function}(&Spec->option, *this, Spec);" - ) - apply_to_model_lines.append( - f" {apply_extras_function}(&Model->opt, *this, nullptr, Model);" - ) - - return ( - f"{COPYRIGHT_BLOCK}\n" - f"#include \"{os.path.dirname(public_relpath).replace(os.sep, '/')}/{header_basename}.h\"\n" - f"#include \n\n" - f"void {ue_struct}::ApplyToSpec(mjSpec* Spec) const\n" - f"{{\n" - + "\n".join(apply_to_spec_lines) + "\n" - f"}}\n\n" - f"void {ue_struct}::ApplyOverridesToModel(mjModel* Model) const\n" - f"{{\n" - + "\n".join(apply_to_model_lines) + "\n" - f"}}\n" - ) - - -def _phase_synthetic_categories(ctx: PhaseContext) -> None: - """Run after the main category walk; emits whole-file synthetic structs.""" - synth = ctx.rules.get("synthetic_categories", {}) - for cat_name, def_ in synth.items(): - # Skip _note_* sibling keys used for inline documentation. - if cat_name.startswith("_") or not isinstance(def_, dict): - continue - ctx.writes.extend(_emit_synthetic_struct_files( - cat_name, def_, ctx.mjxmacro, ctx.public_root, ctx.private_root, - )) - - -# --------------------------------------------------------------------------- -# Sensor switch + TagToType codegen -# --------------------------------------------------------------------------- -# -# Replaces the hand-written switch + map in MjSensor.cpp with codegen -# output driven by: -# - codegen_rules.json[categories.sensor.subtypes] for XML key + enum -# value + (optional) case_body_override -# - sensor_per_type (from build_mjcf_schema_snapshot.py's Sensor() -# scrape) for mj_type + static objtype/reftype literals -# -# Variable-objtype/reftype branches (frame*, geomdist, contact, plugin, -# user) stay hand-written in the post-switch block — that block reads -# UE-side ObjType / RefType properties and applies them after the -# codegen case fires. - -def _emit_sensor_switch_block(cat_rules: Dict[str, Any], - sensor_per_type: Dict[str, Any]) -> str: - """One ``case EMjSensorType::X: ...`` per subtype + the default fallback. - Lives between ``CODEGEN_SENSOR_TYPE_SWITCH_*`` markers in MjSensor.cpp. - """ - lines: List[str] = [] - type_enum = cat_rules.get("type_enum_name", "EMjSensorType") - for subtype in cat_rules.get("subtypes", []): - key = subtype["key"] - enum_value = subtype["enum_value"] - override = subtype.get("case_body_override") - if override: - lines.append(f" case {type_enum}::{enum_value}:") - lines.append(f" {override} break;") - continue - per = sensor_per_type.get(key, {}) - mj_type = per.get("mj_type") - if not mj_type: - # The sensor switch will fall through to ``default:`` for this - # subtype at runtime — i.e. all framejerk/whatever sensors get - # the default mjSENS_ACCELEROMETER + mjOBJ_SITE substitution. - # Loud diagnostic so a regression in build_mjcf_schema_snapshot's - # _extract_sensor_per_type regex doesn't silently miscompile. - _diag_add( - f"[diagnostic] sensor subtype '{key}' has no entry in " - f"sensor_per_type snapshot — runtime will fall through to " - f"the default mjSENS_ACCELEROMETER + mjOBJ_SITE branch. " - f"Check the _extract_sensor_per_type regex against " - f"mujoco/src/user/user_objects.cc.", - source="sensor_scrape_miss", - ) - lines.append(f" // (skipped: no mj_type for '{key}' in sensor_per_type)") - continue - stmts = [f"Element->type = {mj_type};"] - # Only emit static objtype/reftype when sensor_per_type carries a - # literal mjOBJ_X. "from_xml" and "computed" entries are handled - # by the post-switch block reading UE-side ObjType/RefType. - objtype = per.get("objtype") - if isinstance(objtype, str) and objtype.startswith("mjOBJ_"): - stmts.append(f"Element->objtype = {objtype};") - reftype = per.get("reftype") - if isinstance(reftype, str) and reftype.startswith("mjOBJ_"): - stmts.append(f"Element->reftype = {reftype};") - lines.append(f" case {type_enum}::{enum_value}: " - f"{' '.join(stmts)} break;") - default_per = sensor_per_type.get("accelerometer", {}) - default_type = default_per.get("mj_type", "mjSENS_ACCELEROMETER") - default_objtype = default_per.get("objtype") or "mjOBJ_SITE" - if not (isinstance(default_objtype, str) and default_objtype.startswith("mjOBJ_")): - default_objtype = "mjOBJ_SITE" - lines.append(f" default: Element->type = {default_type}; " - f"Element->objtype = {default_objtype}; break;") - return "\n".join(lines) + "\n" - - -def _emit_sensor_tag_to_type_block(cat_rules: Dict[str, Any]) -> str: - """One ``{TEXT(""), EMjSensorType::X}`` per subtype. Lives between - ``CODEGEN_SENSOR_TAG_TO_TYPE_*`` markers in MjSensor.cpp. - """ - lines: List[str] = [] - type_enum = cat_rules.get("type_enum_name", "EMjSensorType") - for subtype in cat_rules.get("subtypes", []): - # Some subtype XML keys collide with C++ enum members that diverge - # (e.g. `key=tendonactuatorfrc`, `enum_value=TendonActFrc`, - # mjSENS_TENDONACTFRC). The tag-to-type map uses the XML key - # verbatim so MJCF lookups match. - key = subtype["key"] - enum_value = subtype["enum_value"] - lines.append(f' {{TEXT("{key}"), {type_enum}::{enum_value}}},') - return "\n".join(lines) + "\n" - - -# --------------------------------------------------------------------------- -# Generated enum headers -# --------------------------------------------------------------------------- -# -# Some MuJoCo C enums (mjtIntegrator/mjtCone/mjtSolver/...) map to URLab -# UENUMs that are blueprint-exposed. Hand-written copies diverge from the -# C source over time, so this emitter generates them from the projected -# mjspec dict's ``enums`` table (sourced from the introspect snapshot). -# -# Rule shape (under top-level ``generated_enums`` in codegen_rules.json): -# "MjOptionEnums": { -# "public_header": "MuJoCo/Generated/MjOptionEnums.h", -# "enums": [ -# { -# "ue_name": "EMjIntegrator", -# "from_mj_enum": "mjtIntegrator", -# "underlying_type": "uint8", // optional, defaults uint8 -# "ue_member_from_mj": { -# "mjINT_EULER": "Euler", -# "mjINT_RK4": "RK4", -# ... -# }, -# // Optional: members from C enum to skip in UE (e.g. internal -# // sentinels that aren't blueprint-meaningful). -# "exclude_mj_members": [] -# } -# ] -# } - - -def _emit_generated_enum_file(cat_name: str, def_: Dict[str, Any], - mjspec: Optional[Dict[str, Any]], - public_root: str) -> List["FileWrite"]: - """Emit a single banner-mode .h carrying one or more UENUMs. - - Rule shape (per enum entry): - ``ue_name`` — UE enum class name (e.g. ``EMjGainType``) - ``from_mj_enum`` — MuJoCo source enum (e.g. ``mjtGain``) - ``ue_member_from_mj`` — opt-in mapping mj_const → UE member name - ``exclude_mj_members``— optional skip list within the source enum - ``extra_members`` — list of {"name": ...} URLab-extra - members appended AFTER the mj-derived - ones. Used by Camp B enums that carry - URLab-specific values without a mjt - counterpart (e.g. ``DcMotor`` for - ``EMjGainType``). - - Rule entries with ``"disabled": true`` are skipped entirely — used - while a hand-rolled enum is being staged for migration so the rule - can land in JSON without breaking the build. - """ - if def_.get("disabled"): - return [] - if not mjspec or "enums" not in mjspec: - _diag_add( - f"[diagnostic] generated_enums['{cat_name}'] needs an mjspec " - f"snapshot with an 'enums' key — run build_introspect_snapshot.py.", - source="generated_enums", - ) - return [] - enums = def_.get("enums", []) - if not enums: - return [] - - public_relpath = def_["public_header"] - header_basename = os.path.basename(public_relpath).replace(".h", "") - - enum_blocks: List[str] = [] - for entry in enums: - if entry.get("disabled"): - continue - ue_name = entry["ue_name"] - # ``from_mj_enum`` is optional when the URLab enum has no mjt - # counterpart at all (EMjFluidShape, EMjGeomInertia, etc) — the - # rule populates extra_members and skips the mjspec lookup. - mj_enum_name: Optional[str] = entry.get("from_mj_enum") - underlying = entry.get("underlying_type", "uint8") - ue_member_from_mj: Dict[str, str] = entry.get("ue_member_from_mj", {}) - exclude: set = set(entry.get("exclude_mj_members", [])) - extra_members: List[Dict[str, Any]] = entry.get("extra_members", []) - c_enum: Dict[str, int] = {} - if mj_enum_name: - c_enum = mjspec["enums"].get(mj_enum_name, {}) - if not c_enum: - _diag_add( - f"[diagnostic] generated_enums['{cat_name}']: C enum " - f"'{mj_enum_name}' missing from the introspect snapshot. " - f"Skipping '{ue_name}'.", - source="generated_enums", - ) - continue - elif not extra_members: - _diag_add( - f"[diagnostic] generated_enums['{cat_name}']: '{ue_name}' " - f"has neither from_mj_enum nor extra_members; nothing to " - f"emit.", - source="generated_enums", - ) - continue - member_lines: List[str] = [] - used_values: set = set() - # Iterate by (value, mj_const) so the emitted UENUM order is stable - # across libclang scrapes — the snapshot's dict insertion order is - # whatever the AST walk produced, which can drift on header edits. - for mj_const, value in sorted(c_enum.items(), key=lambda kv: (kv[1], kv[0])): - if mj_const in exclude: - continue - ue_member = ue_member_from_mj.get(mj_const) - if not ue_member: - continue # silently skip; rule author opts in per-const - member_lines.append(f" {ue_member} = {value},") - used_values.add(value) - # URLab-extra members run AFTER the mj-derived block. Values - # auto-increment past the highest used mj value so the resulting - # uint8 sequence stays contiguous + reproducible. - next_value = (max(used_values) + 1) if used_values else 0 - for extra in extra_members: - name = extra.get("name") - if not name: - _diag_add( - f"[diagnostic] generated_enums['{cat_name}'] '{ue_name}'" - f" extra_members entry {extra!r} missing 'name'.", - source="generated_enums", - ) - continue - value = extra.get("value", next_value) - member_lines.append(f" {name} = {value},") - next_value = value + 1 - if not member_lines: - continue - # Strip the trailing comma on the last member for cleaner C++. - member_lines[-1] = member_lines[-1].rstrip(",") - # Doc line: only mention the mj source when the rule pinned - # one. Pure-extras enums (no mjt counterpart) get a one-liner - # naming them as URLab-side. - doc = ( - f"/** {ue_name} — mirror of MuJoCo's {mj_enum_name}. */\n" - if mj_enum_name - else f"/** {ue_name} — URLab editorial enum (no mjt counterpart). */\n" - ) - enum_blocks.append( - doc - + f"UENUM(BlueprintType)\n" - f"enum class {ue_name} : {underlying}\n" - f"{{\n" - + "\n".join(member_lines) + "\n" - f"}};\n" - ) - - if not enum_blocks: - return [] - - content = ( - f"{COPYRIGHT_BLOCK}\n" - f"#pragma once\n\n" - f"#include \"CoreMinimal.h\"\n" - f"#include \"{header_basename}.generated.h\"\n\n" - + "\n".join(enum_blocks) - ) - public_path = os.path.join(public_root, public_relpath.replace("/", os.sep)) - return [FileWrite(path=public_path, content=content)] - - -def _phase_generated_enums(ctx: PhaseContext) -> None: - gen = ctx.rules.get("generated_enums", {}) - for cat_name, def_ in gen.items(): - # _note_* sibling keys document the surrounding entries; they - # appear as strings, not dicts, and must skip the emit path. - if cat_name.startswith("_") or not isinstance(def_, dict): - continue - ctx.writes.extend(_emit_generated_enum_file( - cat_name, def_, ctx.mjspec, ctx.public_root, - )) - - -# --------------------------------------------------------------------------- -# Articulation category registry -# --------------------------------------------------------------------------- -# -# Emits a header with a compile-time table of every URLab-managed MuJoCo -# category, listing the schema element name, UE base class, mjs struct, -# and the layout shape. Consumers (diagnostic dumps, BP iteration, -# test-time category enumeration) can iterate constexpr instead of -# duplicating the category list in C++ source. - -# --------------------------------------------------------------------------- -# Editor option helpers -# --------------------------------------------------------------------------- -# -# Codegens the trivial ``TArray UMjX::GetYOptions() const`` editor -# helpers that just forward to ``UMjComponent::GetSiblingComponentOptions``. -# Wrappers with real per-class logic (UMjActuator::GetTargetNameOptions, -# UMjSensor::Get(Target|Reference)NameOptions, UMjEquality::GetObjOptions) -# stay hand-written in their owning .cpp files. -# -# Emission shape: each host .cpp file has a CODEGEN_EDITOR_OPTIONS_START/END -# marker pair inside its ``#if WITH_EDITOR`` block. The phase injects the -# class's wrappers between those markers — output stays INSIDE the host -# .cpp, not a separate file. (Avoids the ``.gen.cpp`` UHT-collision name.) -# -# Rule shape (top-level ``editor_option_helpers.wrappers``): -# [ -# { -# "class": "UMjActuator", -# "host_cpp": "MuJoCo/Components/Actuators/MjActuator.cpp", -# "method": "GetSliderSiteOptions", -# "filter_class": "UMjSite", -# "include_defaults": false -# }, -# ... -# ] - -def _phase_editor_option_helpers(ctx: PhaseContext) -> None: - def_ = ctx.rules.get("editor_option_helpers") - if not def_: - return - wrappers = def_.get("wrappers", []) - if not wrappers: - return - # Group by host_cpp. - by_host: Dict[str, List[Dict[str, Any]]] = {} - for w in wrappers: - host = w.get("host_cpp") - if not host: - _diag_add( - f"[diagnostic] editor_option_helpers: missing host_cpp on " - f"wrapper {w!r}. Skipping.", - source="editor_option_helpers", - ) - continue - by_host.setdefault(host, []).append(w) - # Inject per-file. - for host_relpath, group in by_host.items(): - host_path = os.path.join(ctx.private_root, host_relpath.replace("/", os.sep)) - if not os.path.exists(host_path): - _diag_add( - f"[diagnostic] editor_option_helpers: host_cpp '{host_relpath}' " - f"not found on disk. Skipping injection.", - source="editor_option_helpers", - ) - continue - with open(host_path, "r", encoding="utf-8") as f: - text = f.read() - lines: List[str] = [] - for w in group: - cls = w["class"] - method = w["method"] - filt = w["filter_class"] - inc_def = bool(w.get("include_defaults", False)) - defaults_arg = ", true" if inc_def else "" - lines.append( - f"TArray {cls}::{method}() const {{ return " - f"UMjComponent::GetSiblingComponentOptions(this, " - f"{filt}::StaticClass(){defaults_arg}); }}" - ) - body = "\n".join(lines) + "\n" - new_text, ok = inject_between_tags(text, "EDITOR_OPTIONS", body) - if not ok: - _diag_add( - f"[diagnostic] editor_option_helpers: host_cpp '{host_relpath}' " - f"missing the CODEGEN_EDITOR_OPTIONS_START/END marker pair. " - f"Add the markers inside the host's #if WITH_EDITOR block " - f"or remove the wrapper entries.", - source="editor_option_helpers", - ) - continue - if new_text != text: - ctx.writes.append(FileWrite(path=host_path, content=new_text)) - - -def _phase_articulation_registry(ctx: PhaseContext) -> None: - cats = ctx.rules.get("categories", {}) - if not cats: - return - out_relpath = "MuJoCo/Generated/MjArticulationRegistry.h" - entries: List[str] = [] - for name in sorted(cats): - cat = cats[name] - base = cat.get("base_class_name", "") - mjs = cat.get("mjs_struct", "") - layout = cat.get("layout", "no_subclasses") - n_subtypes = len(cat.get("subtypes", [])) - entries.append( - f' {{ TEXT("{name}"), TEXT("{base}"), TEXT("{mjs}"), ' - f'TEXT("{layout}"), {n_subtypes} }},' - ) - content = ( - f"{COPYRIGHT_BLOCK}\n" - f"#pragma once\n\n" - f"#include \"CoreMinimal.h\"\n\n" - f"// Compile-time registry of every URLab-managed MuJoCo category.\n" - f"// Codegen-owned — driven by codegen_rules.json[categories].\n" - f"namespace MjArticulationRegistry\n" - f"{{\n" - f" struct CategoryEntry\n" - f" {{\n" - f" const TCHAR* Name; // schema element name, e.g. \"body\"\n" - f" const TCHAR* BaseClassName; // UE base UClass, e.g. \"UMjBody\"\n" - f" const TCHAR* MjsStruct; // MuJoCo spec struct, e.g. \"mjsBody\"\n" - f" const TCHAR* Layout; // \"single_uclass_per_file\" / \"multi_uclass\" / \"no_subclasses\"\n" - f" int NumSubtypes;\n" - f" }};\n\n" - f" inline constexpr CategoryEntry kCategories[] = {{\n" - + "\n".join(entries) + "\n" - f" }};\n" - f" inline constexpr int kNumCategories = sizeof(kCategories) / sizeof(kCategories[0]);\n" - f"}}\n" - ) - out_path = os.path.join(ctx.public_root, out_relpath.replace("/", os.sep)) - ctx.writes.append(FileWrite(path=out_path, content=content)) - - -def _emit_objtype_dispatch_block(cat_rules: Dict[str, Any]) -> Optional[str]: - """Emit a switch on ``cat_rules['objtype_dispatch']`` that assigns - each case's ``expr`` to ``target``. Used by categories where mjs's - objtype field varies by enum value (today: MjEquality).""" - dispatch = cat_rules.get("objtype_dispatch") - if not dispatch: - return None - enum_name = cat_rules.get("type_enum_name") - if not enum_name: - _diag_add( - "[diagnostic] objtype_dispatch on category missing " - "'type_enum_name'; cannot emit case labels.", - source="objtype_dispatch", - ) - return None - discriminator = dispatch["discriminator"] - target = dispatch["target"] - default_expr = dispatch.get("default", "mjOBJ_UNKNOWN") - cases = dispatch.get("cases", []) - if not cases: - _diag_add( - f"[diagnostic] objtype_dispatch '{discriminator}' has no cases; " - f"only the default branch would emit.", - source="objtype_dispatch", - ) - return None - - switch_cases: List[Tuple[Any, str]] = [] - for i, case in enumerate(cases): - keys = case.get("keys", []) - if not keys: - _diag_add( - f"[diagnostic] objtype_dispatch case #{i} has empty " - f"'keys' list; would emit a body with no case label.", - source="objtype_dispatch", - ) - return None - if "expr" not in case: - _diag_add( - f"[diagnostic] objtype_dispatch case #{i} (keys={keys}) " - f"missing 'expr'.", - source="objtype_dispatch", - ) - return None - switch_cases.append((list(keys), f"{target} = {case['expr']}")) - return emit_enum_switch( - discriminator, enum_name, switch_cases, - indent=" ", - default_body=f"{target} = {default_expr}", - ) - - -def _inject_into_category_cpp( - rules: Dict[str, Any], - private_root: str, - writes: List["FileWrite"], - tag: str, - emit_fn, - *, - diag_source: str, -) -> None: - """Shared scaffolding: walk every category, call ``emit_fn(cat_rules)``, - inject the result between ``CODEGEN__START/END`` markers in the - category's base-class .cpp. ``emit_fn`` returns ``None`` to skip - (already diagnosed inside the emit_fn). Missing-file / missing-marker - diagnostics + the brace-balance check come from - ``_inject_tags_into_cpp`` so every injection path shares the same - safety net.""" - for cat_name, cat_rules in rules.get("categories", {}).items(): - body = emit_fn(cat_rules) - if body is None: - continue - base_header = cat_rules.get("base_class_header") - if not base_header: - _diag_add( - f"[diagnostic] category '{cat_name}' has codegen rule for " - f"{diag_source} but no 'base_class_header'; block not emitted.", - source=diag_source, - ) - continue - cpp_path = os.path.join( - private_root, base_header.replace(".h", ".cpp"), - ) - _inject_tags_into_cpp( - cpp_path, [(tag, body)], writes, diag_source=diag_source, - ) - - -def _phase_objtype_dispatch(ctx: PhaseContext) -> None: - """Inject objtype dispatch block; host .cpp must carry - ``CODEGEN_OBJTYPE_DISPATCH_START/END`` markers.""" - _inject_into_category_cpp( - ctx.rules, ctx.private_root, ctx.writes, - tag="OBJTYPE_DISPATCH", - emit_fn=_emit_objtype_dispatch_block, - diag_source="objtype_dispatch", - ) - - -def _emit_geom_final_type_block(cat_rules: Dict[str, Any], - element_rules: Dict[str, Any], - mjspec: Optional[Dict[str, Any]] = None) -> Optional[str]: - """Emit MjGeom's FinalType resolution + mesh-name export. Specific - to geom because the mesh-name write depends on the COMPILE-TIME geom - type (UPROPERTY Type if overridden, else Default's type). - - The value_map is pulled from ``element_rules[xml_enum_ref]`` via - ``_resolve_value_map``. Returns None (with a diagnostic) when the - xml_enum_ref entry is missing — silent failure here would drop the - block from MjGeom.cpp and only surface as a runtime mesh-bind error. - """ - final = cat_rules.get("geom_final_type") - if not final: - return None - xml_enum_attrs = element_rules.get("xml_enum_attrs", {}) - attr_key = final["xml_enum_ref"] - enum_def = xml_enum_attrs.get(attr_key) - if not enum_def: - _diag_add( - f"[diagnostic] geom_final_type references xml_enum '{attr_key}' " - f"that doesn't exist in element_rules; FinalType block will be " - f"empty and mesh-name export will not fire.", - source="geom_final_type", - ) - return None - value_map = _resolve_value_map(attr_key, enum_def) - if not value_map: - _diag_add( - f"[diagnostic] geom_final_type xml_enum '{attr_key}' resolved " - f"to an empty value_map; FinalType switch would have no cases.", - source="geom_final_type", - ) - return None - ue_prop = enum_def["ue_property"] - ue_enum = enum_def["ue_enum_type"] - - override = final["override_field"] - default_lookup = final["default_lookup"] - fallback = final["default_fallback"] - name_field = final["name_field"] - name_for = final["name_export_for"] - name_target = final["name_target"] - name_setter = final["name_setter"] - - # Placeholder for the FinalType init when override=true: any mj_const - # works because the switch below remaps. Pick the value_map's first - # entry for stability. - first_mj = next(iter(value_map.values()))[1] - - switch_cases: List[Tuple[Any, str]] = [ - (mapping[0], f"FinalType = {mapping[1]}") - for mapping in value_map.values() - ] - switch_body = emit_enum_switch( - ue_prop, ue_enum, switch_cases, indent=" ", - ).rstrip("\n") - lines: List[str] = [] - lines.append(f" int FinalType = {override}") - lines.append(f" ? {first_mj} /* placeholder; remapped below */") - lines.append(f" : (Default ? {default_lookup} : {fallback});") - lines.append(f" if ({override})") - lines.append(" {") - lines.append(switch_body) - lines.append(" }") - lines.append(f" if (FinalType == {name_for} && !{name_field}.IsEmpty())") - lines.append(" {") - lines.append(f" {name_setter}({name_target}, {name_field});") - lines.append(" }") - return "\n".join(lines) + "\n" - - -def _phase_geom_final_type(ctx: PhaseContext) -> None: - """Inject geom_final_type block into MjGeom.cpp between the - CODEGEN_GEOM_FINAL_TYPE markers.""" - element_rules_all = ctx.rules.get("element_rules", {}) - - def emit_fn(cat_rules): - # Look up element_rules entry via the category key already used by - # rules.categories. The shared helper passes cat_rules through; - # we recover the category name from the dict identity. - cat_name = next( - (k for k, v in ctx.rules.get("categories", {}).items() - if v is cat_rules), - None, - ) - if cat_name is None: - return None - return _emit_geom_final_type_block( - cat_rules, element_rules_all.get(cat_name, {}), ctx.mjspec, - ) - - _inject_into_category_cpp( - ctx.rules, ctx.private_root, ctx.writes, - tag="GEOM_FINAL_TYPE", - emit_fn=emit_fn, - diag_source="geom_final_type", - ) - - -def _phase_sensor_codegen(ctx: PhaseContext) -> None: - """Inject codegen-emitted sensor switch + TagToType into MjSensor.cpp.""" - sensor_rules = ctx.rules.get("categories", {}).get("sensor") - if not sensor_rules: - return - sensor_per_type = ctx.schema.get("sensor_per_type", {}) - if not sensor_per_type: - _diag_add( - "[diagnostic] sensor_per_type missing from schema snapshot; " - "skipping sensor switch codegen (run " - "build_mjcf_schema_snapshot.py).", - source="sensor_codegen", - ) - return - cpp_path = os.path.join( - ctx.private_root, "MuJoCo", "Components", "Sensors", "MjSensor.cpp", - ) - _inject_tags_into_cpp( - cpp_path, - [ - ("SENSOR_TYPE_SWITCH", _emit_sensor_switch_block(sensor_rules, sensor_per_type)), - ("SENSOR_TAG_TO_TYPE", _emit_sensor_tag_to_type_block(sensor_rules)), - ], - ctx.writes, - diag_source="sensor_codegen", - ) - - -_KNOWN_LAYOUTS = {"single_uclass_per_file", "multi_uclass", "no_subclasses"} - - -def _phase_categories(ctx: PhaseContext) -> None: - rules, schema = ctx.rules, ctx.schema - public_root, private_root = ctx.public_root, ctx.private_root - mjspec = ctx.mjspec - categories: Dict[str, Any] = rules.get("categories", {}) - for cat, cat_rules in categories.items(): - layout = cat_rules.get("layout", "no_subclasses") - if layout == "single_uclass_per_file": - ctx.writes.extend(emit_base_class_injection(cat, cat_rules, schema, rules, public_root, private_root, mjspec=mjspec)) - ctx.writes.extend(emit_subclass_files(cat, cat_rules, schema, rules, public_root, private_root, mjspec=mjspec)) - elif layout == "multi_uclass": - ctx.writes.extend(emit_multi_uclass(cat, cat_rules, schema, rules, public_root, private_root, mjspec=mjspec)) - elif layout == "no_subclasses": - ctx.writes.extend(emit_base_class_injection(cat, cat_rules, schema, rules, public_root, private_root, mjspec=mjspec)) - else: - _diag_add( - f"[diagnostic] category '{cat}' has unknown layout " - f"'{layout}'; expected one of {sorted(_KNOWN_LAYOUTS)}. " - f"Category emitted no writes.", - source="category_layout", - ) - - -def _phase_bind_h(ctx: PhaseContext) -> None: - bind_write = emit_bind_h_injection(ctx.rules, ctx.mjxmacro, ctx.bind_h_path) - if bind_write: - ctx.writes.append(bind_write) - - -def _phase_schema_tests(ctx: PhaseContext) -> None: - ctx.writes.append(emit_schema_for_tests(ctx.schema, ctx.rules)) - - -# The 17 drift checks, _emit_drift_diagnostics, the hand-enum regexes, -# _scan_hand_enums, _hand_enums_from_snapshot, _all_schema_attrs, -# _collect_every_schema_attr, and _VALID_APPLY_MODES live in -# _codegen_checks. The orchestrator re-exports them through the import -# block at the top of this file. - - -def _phase_diagnostics(ctx: PhaseContext) -> None: - rules, schema, mjspec, mjxmacro = ctx.rules, ctx.schema, ctx.mjspec, ctx.mjxmacro - _emit_drift_diagnostics(schema, rules, mjspec) - _check_hand_enum_drift(schema, rules, mjspec, ctx.public_root) - _check_type_shape_drift(schema, rules, mjspec) - _check_new_attr_typing(schema, rules) - _check_value_map_stale_mj_consts(rules, mjspec) - _check_orphan_rule_entries(schema, rules) - _check_compiler_attrs_coverage(schema, rules) - _check_mjxmacro_block_coverage(rules, mjxmacro) - _check_property_units_validity(rules) - _check_apply_mode_validity(rules) - _check_embedded_cpp_references(rules, mjspec) - _check_allowlist_staleness(schema, rules, mjspec) - - -# Order matters: emit phases collect writes; diagnostics runs last and -# scans the final rule set. -EMISSION_PHASES: List[EmissionPhase] = [ - EmissionPhase(name="categories", fn=_phase_categories), - EmissionPhase(name="synthetic_categories", fn=_phase_synthetic_categories), - EmissionPhase(name="generated_enums", fn=_phase_generated_enums), - EmissionPhase(name="editor_option_helpers", fn=_phase_editor_option_helpers), - EmissionPhase(name="articulation_registry", fn=_phase_articulation_registry), - EmissionPhase(name="sensor_codegen", fn=_phase_sensor_codegen), - EmissionPhase(name="objtype_dispatch", fn=_phase_objtype_dispatch), - EmissionPhase(name="geom_final_type", fn=_phase_geom_final_type), - EmissionPhase(name="bind_h", fn=_phase_bind_h), - EmissionPhase(name="schema_tests", fn=_phase_schema_tests), - EmissionPhase(name="diagnostics", fn=_phase_diagnostics), -] - - -def collect_all_writes( - schema: Dict[str, Any], - rules: Dict[str, Any], - mjxmacro: Dict[str, Any], - public_root: str, - private_root: str, - bind_h_path: str, - mjspec: Dict[str, Any] | None = None, -) -> List[FileWrite]: - """Drive every phase in ``EMISSION_PHASES`` and return the full set - of file writes the codegen would perform. No I/O side effects on - disk — diagnostics print to stderr at the end.""" - _reset_diags() - writes: List[FileWrite] = [] - ctx = PhaseContext( - schema=schema, rules=rules, mjxmacro=mjxmacro, mjspec=mjspec, - public_root=public_root, private_root=private_root, - bind_h_path=bind_h_path, writes=writes, - ) - for phase in EMISSION_PHASES: - phase.fn(ctx) - _diag_flush() - return writes - - -def _mjspec_from_introspect( - introspect: Dict[str, Any], -) -> Dict[str, Any]: - """Project the libclang-scraped introspect snapshot into the - shape downstream code expects: - - ``structs``: ``{struct_name: [field_name, ...]}`` - ``enums``: ``{enum_name: {member_name: int_value}}`` - ``setto_functions``: ``{fn_name: {"params": [...], "return_type": ...}}`` - ``introspect``: passthrough of the richer per-field tables for - the drift checks that need them. - - Every ``mjs_setTo*`` C signature takes the spec element as its - first arg; the codegen emit path always passes ``Element`` there, - so the projection drops that arg from the param list and the - downstream emitter enumerates only the schema-attr-bound trailing - args.""" - structs_in = introspect.get("structs", {}) or {} - structs_out: Dict[str, List[str]] = {} - for name, entry in structs_in.items(): - if isinstance(entry, dict): - fields = entry.get("fields", []) - structs_out[name] = [ - f["name"] for f in fields - if isinstance(f, dict) and isinstance(f.get("name"), str) - ] - - enums_in = introspect.get("enums", {}) or {} - enums_out: Dict[str, Dict[str, int]] = {} - for name, entry in enums_in.items(): - if isinstance(entry, dict): - members = entry.get("members", {}) - if isinstance(members, dict): - enums_out[name] = dict(members) - - fns_in = introspect.get("functions", {}) or {} - setto_out: Dict[str, Dict[str, Any]] = {} - for name, sig in fns_in.items(): - if not name.startswith("mjs_setTo"): - continue - if isinstance(sig, dict): - params = sig.get("params", []) - setto_out[name] = { - "params": params[1:] if params else [], - "return_type": sig.get("return_type", ""), - } - - return { - "structs": structs_out, - "enums": enums_out, - "setto_functions": setto_out, - "introspect": { - "functions": fns_in, - "structs": structs_in, - "defines": introspect.get("defines", {}), - "hand_enums": introspect.get("hand_enums", {}), - }, - } - - -_CLANG_FORMAT_EXE: Optional[str] = None -_CLANG_FORMAT_RESOLVED = False - - -def _resolve_clang_format() -> Optional[str]: - """Locate clang-format: $CLANG_FORMAT, then PATH, then VS2022's LLVM.""" - global _CLANG_FORMAT_EXE, _CLANG_FORMAT_RESOLVED - if _CLANG_FORMAT_RESOLVED: - return _CLANG_FORMAT_EXE - _CLANG_FORMAT_RESOLVED = True - cand = (os.environ.get("CLANG_FORMAT") - or shutil.which("clang-format-19") # common on Linux - or shutil.which("clang-format")) - if not cand: - # Windows: VS2022's bundled LLVM. - vs = ("C:/Program Files/Microsoft Visual Studio/2022/Community/" - "VC/Tools/Llvm/bin/clang-format.exe") - if os.path.isfile(vs): - cand = vs - _CLANG_FORMAT_EXE = cand - return cand - - -def _clang_format_content(content: str, path: str) -> str: - """Format emitted C++ with the project .clang-format so generated files - match a clang-format pass over the whole tree. clang-format is a hard - regen dependency (like libclang): the committed generated tree is - formatted, so emitting unformatted would trip the --check drift gate. - Use clang-format 19.x (VS2022 LLVM) to match what the tree was committed - with. Non-C++ artifacts (e.g. the test-only schema header) pass through.""" - if not (path.endswith(".h") or path.endswith(".cpp")): - return content - exe = _resolve_clang_format() - if not exe: - raise RuntimeError( - "clang-format not found — set $CLANG_FORMAT or put it on PATH " - "(clang-format 19.x, e.g. VS2022's bundled LLVM). Required so the " - "codegen emits formatted files; without it the --check drift gate " - "will report spurious changes.") - # --assume-filename lets clang-format pick up the repo-root .clang-format - # by walking up from the (real) generated-file path. - proc = subprocess.run( - [exe, f"--assume-filename={path}", "--style=file"], - input=content.encode("utf-8"), - stdout=subprocess.PIPE, stderr=subprocess.PIPE, - ) - if proc.returncode != 0: - raise RuntimeError( - f"clang-format failed on {path}: " - f"{proc.stderr.decode('utf-8', 'replace')}") - return proc.stdout.decode("utf-8") - - -def apply_writes(writes: List[FileWrite], dry_run: bool, check_only: bool) -> int: - """Either apply, print diff, or check. Returns process exit code. - - Writes use ``newline="\\n"`` and a binary compare so a codegen run on - Windows produces the same bytes as one on Linux — otherwise Python's - default newline translation would silently bake ``\\r\\n`` into the - on-disk artifacts and ``--check`` would still pass because the - matching read would normalize it back.""" - changed = 0 - for w in writes: - content = _clang_format_content(w.content, w.path) - new_bytes = content.encode("utf-8") - old_bytes: Optional[bytes] = None - if os.path.exists(w.path): - with open(w.path, "rb") as f: - old_bytes = f.read() - if old_bytes == new_bytes: - continue - changed += 1 - rel = os.path.relpath(w.path, PLUGIN_ROOT) - if dry_run or check_only: - print(f"==> WOULD WRITE: {rel} ({len(new_bytes)} bytes)") - if old_bytes is not None and dry_run: - old_text = old_bytes.decode("utf-8", errors="replace") - diff = difflib.unified_diff( - old_text.splitlines(keepends=True), - content.splitlines(keepends=True), - fromfile=rel + " (current)", - tofile=rel + " (codegen)", - n=2, - ) - sys.stdout.writelines(diff) - else: - # Atomic write: stage to a sibling tmp file, then os.replace. - # Without this, a Ctrl-C between truncate and write leaves a - # zero-/partial-byte file that UBT picks up, producing a UHT - # error far from "I interrupted codegen". - os.makedirs(os.path.dirname(w.path), exist_ok=True) - tmp_path = w.path + ".codegen.tmp" - try: - with open(tmp_path, "wb") as f: - f.write(new_bytes) - os.replace(tmp_path, w.path) - except BaseException: - if os.path.exists(tmp_path): - try: - os.remove(tmp_path) - except OSError: - pass - raise - print(f"WROTE {rel}") - if check_only and changed: - return 1 - print(f"{'(dry-run) ' if dry_run else ''}files changed: {changed}") - return 0 - - -# Hand-written companion files that legitimately live in MuJoCo/Generated/ -# (their banner explicitly says "NOT codegen-managed"). Listed here so the -# orphan walk doesn't flag them — anything else in Generated/ that the -# codegen doesn't plan to write IS a real orphan. -_HAND_WRITTEN_IN_GENERATED = { - "MjOptionGeneratedExtras.cpp", -} - - -def find_orphan_generated_files( - writes: List[FileWrite], public_root: str, private_root: str, -) -> List[str]: - """Walk ``MuJoCo/Generated/`` under public + private roots and return - any file the codegen no longer plans to emit. A category removed from - rules leaves a stale ``Generated/MjFoo.h`` that UBT will pick up — the - user sees a confusing UHT error far from the JSON edit.""" - expected = {os.path.normcase(os.path.abspath(w.path)) for w in writes} - orphans: List[str] = [] - for root in (public_root, private_root): - gen_dir = os.path.join(root, "MuJoCo", "Generated") - if not os.path.isdir(gen_dir): - continue - for dirpath, _dirs, files in os.walk(gen_dir): - for fname in files: - if not (fname.endswith(".h") or fname.endswith(".cpp")): - continue - if fname in _HAND_WRITTEN_IN_GENERATED: - continue - p = os.path.normcase(os.path.abspath(os.path.join(dirpath, fname))) - if p not in expected: - orphans.append(os.path.join(dirpath, fname)) - return sorted(orphans) - - -def main() -> int: - parser = argparse.ArgumentParser(description="URLab UE component codegen.") - parser.add_argument("--schema", default=DEFAULT_SCHEMA) - parser.add_argument("--mjxmacro", default=DEFAULT_MJXMACRO) - parser.add_argument("--introspect", default=DEFAULT_INTROSPECT, - help="Clang-AST scrape carrying mjsX struct fields, " - "mjt* enums, mjs_setTo* signatures, and EMj* " - "hand-enum members. Built by " - "build_introspect_snapshot.py (requires libclang).") - parser.add_argument("--rules", default=DEFAULT_RULES) - parser.add_argument("--public-root", default=DEFAULT_PUBLIC_ROOT) - parser.add_argument("--private-root", default=DEFAULT_PRIVATE_ROOT) - parser.add_argument("--bind-h", default=DEFAULT_BIND_H) - parser.add_argument("--dry-run", action="store_true", - help="print diffs, write nothing") - parser.add_argument("--check", action="store_true", - help="non-zero exit if writes would change anything") - parser.add_argument("--strict", dest="strict", - action=argparse.BooleanOptionalAction, default=True, - help="treat drift diagnostics as fatal (exit 2). " - "Default on; --no-strict makes diagnostics " - "print but not fail the run.") - parser.add_argument("--require-introspect", dest="require_introspect", - action=argparse.BooleanOptionalAction, default=True, - help="hard-fail when the clang-AST introspect " - "snapshot is missing. Default on; " - "--no-require-introspect lets the codegen run " - "against an existing (possibly stale) snapshot.") - args = parser.parse_args() - - with open(args.schema, "r", encoding="utf-8") as f: - schema = json.load(f) - with open(args.mjxmacro, "r", encoding="utf-8") as f: - mjxmacro = json.load(f) - with open(args.rules, "r", encoding="utf-8") as f: - rules = json.load(f) - - mjspec: Dict[str, Any] | None = None - if not os.path.exists(args.introspect): - msg = ( - f"[codegen] introspect snapshot missing at {args.introspect}. " - f"Run `python Scripts/codegen/build_introspect_snapshot.py` " - f"(requires libclang). Pass --no-require-introspect to bypass." - ) - if args.require_introspect: - print(msg, file=sys.stderr) - return 3 - print(f"[codegen] warning: {msg}", file=sys.stderr) - else: - with open(args.introspect, "r", encoding="utf-8") as f: - introspect = json.load(f) - mjspec = _mjspec_from_introspect(introspect) - # Post-projection invariants: a libclang scrape shape change can - # silently empty the projection, which would neuter the type-shape - # drift check and drop TArray array-cap clamps from emitted code. - # Fail loud rather than ship a degraded but green-on---check run. - if args.require_introspect: - for key in ("enums", "structs", "setto_functions"): - if not mjspec.get(key): - print( - f"[codegen] introspect projection produced empty " - f"'{key}'. The libclang scrape shape may have " - f"drifted — rebuild the snapshot or fix " - f"_mjspec_from_introspect.", - file=sys.stderr, - ) - return 3 - - writes = collect_all_writes( - schema, rules, mjxmacro, - args.public_root, args.private_root, args.bind_h, mjspec=mjspec, - ) - - orphans = find_orphan_generated_files( - writes, args.public_root, args.private_root, - ) - for orphan in orphans: - rel = os.path.relpath(orphan, PLUGIN_ROOT) - _diag_add( - f"[diagnostic] orphan generated file: {rel}. The codegen no " - f"longer plans to emit this path — a category or subtype was " - f"likely removed from codegen_rules.json. Delete the file " - f"(UBT will otherwise pick it up).", - source="orphan_generated_file", - ) - # Snapshot the diag SOURCE tags before flushing, so we can detect - # destructive-overwrite warnings (banner_overwrite) and refuse to - # write under --strict — otherwise apply_writes would destroy the - # hand edits before the strict check fires. - banner_overwrite_pending = any( - d.source == "banner_overwrite" for d in _DIAGS_BUFFER.pending - ) - _diag_flush() - - fired = _DIAGS_BUFFER.fired_count - is_destructive_run = not args.dry_run and not args.check - if is_destructive_run and args.strict and banner_overwrite_pending: - print( - f"[codegen] --strict: banner_overwrite diagnostic fired — " - f"refusing to write (would destroy hand-edited content). " - f"Re-run with --no-strict to proceed, or run the audit " - f"helper at Scripts/codegen/_audit_banner_candidates.py.", - file=sys.stderr, - ) - return 2 - - rc = apply_writes(writes, args.dry_run, args.check) - if args.check and orphans: - print( - f"[codegen] --check: {len(orphans)} orphan generated file(s); " - f"exiting non-zero.", file=sys.stderr, - ) - return 1 - if args.strict and fired: - print( - f"[codegen] --strict: {fired} diagnostic(s) flushed; " - f"exiting non-zero.", file=sys.stderr, - ) - return 2 - return rc - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/Scripts/codegen/regen_all.py b/Scripts/codegen/regen_all.py deleted file mode 100644 index ab3c6227..00000000 --- a/Scripts/codegen/regen_all.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""One-shot regen pipeline for a MuJoCo version bump. - -Runs every codegen step in order: - - 1. Rebuild snapshots from the installed MuJoCo headers + submodule source: - - Scripts/codegen/snapshots/mjxmacro_snapshot.json (model/data array layouts) - - Scripts/codegen/snapshots/mjcf_schema_snapshot.json (MJCF schema + per-sensor objtype scrape) - - Scripts/codegen/snapshots/introspect_snapshot.json (clang-AST scrape — mjsX struct fields, mjt* enums, mjs_setTo* sigs, EMj* hand enums) - 2. Run the C++ generator (generate_ue_components.py) against the fresh - snapshots. Emits / updates the per-component .h/.cpp files in Source/. - -The clang-AST introspect snapshot now supersedes the old -``mjspec_snapshot.json`` — the runtime projects introspect into the -legacy mjspec shape, so build_mjspec_snapshot.py is retired. - -Day-to-day this is the script developers run after either: - - bumping the third_party/MuJoCo submodule pointer + rebuilding the - install (build_all.ps1 / build_all.sh), OR - - editing Scripts/codegen/codegen_rules.json. - -Usage: - python Scripts/codegen/regen_all.py - python Scripts/codegen/regen_all.py --skip-codegen # snapshots only - -Exit code: - 0 on success, non-zero if any sub-step fails. -""" -from __future__ import annotations - -import argparse -import os -import subprocess -import sys -import time - - -HERE = os.path.dirname(os.path.abspath(__file__)) -PLUGIN_ROOT = os.path.normpath(os.path.join(HERE, "..", "..")) - - -def _run(label: str, cmd: list[str]) -> int: - print(f"\n>>> {label}", flush=True) - t0 = time.monotonic() - proc = subprocess.run(cmd, cwd=PLUGIN_ROOT) - dt = time.monotonic() - t0 - if proc.returncode != 0: - print(f"!!! {label} FAILED (exit {proc.returncode}, {dt:.1f}s)", flush=True) - else: - print(f"... {label} ok ({dt:.1f}s)", flush=True) - return proc.returncode - - -def main(argv: list[str] | None = None) -> int: - ap = argparse.ArgumentParser() - ap.add_argument( - "--skip-codegen", action="store_true", - help="Only rebuild the three snapshot JSONs; skip generate_ue_components.py.", - ) - args = ap.parse_args(argv) - - py = sys.executable - - steps: list[tuple[str, list[str]]] = [ - ("mjxmacro snapshot", - [py, os.path.join(HERE, "build_mjxmacro_snapshot.py")]), - ("MJCF schema snapshot", - [py, os.path.join(HERE, "build_mjcf_schema_snapshot.py")]), - ] - # Introspect snapshot needs libclang and now also supplies the data - # build_mjspec_snapshot.py used to scrape via regex (mjsX struct - # fields, mjt* enums, mjs_setTo* sigs). Optional only in the sense - # that a stale on-disk snapshot still drives the codegen — the - # --require-introspect default in generate_ue_components.py makes - # the gate fatal at next run if the snapshot really is missing. - introspect_step = ( - "introspect snapshot (clang AST — supersedes mjspec snapshot)", - [py, os.path.join(HERE, "build_introspect_snapshot.py")], - ) - steps.append(introspect_step) - if not args.skip_codegen: - steps.append(("UE component codegen", - [py, os.path.join(HERE, "generate_ue_components.py")])) - - for label, cmd in steps: - rc = _run(label, cmd) - if rc != 0: - # Introspect is optional — warn but continue when it's the - # one that failed (most commonly: libclang not on PATH). - if label.startswith("introspect snapshot"): - print( - " (introspect is optional; codegen will use the " - "existing snapshot — type-shape drift may be stale " - "until you install libclang.)", - flush=True, - ) - continue - return rc - - print("\nAll regen steps completed.", flush=True) - print("Next: close the editor, run the URLab build + test cycle to verify.", flush=True) - return 0 - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/Scripts/codegen/snapshots/introspect_snapshot.json b/Scripts/codegen/snapshots/introspect_snapshot.json deleted file mode 100644 index cd8538a8..00000000 --- a/Scripts/codegen/snapshots/introspect_snapshot.json +++ /dev/null @@ -1,27250 +0,0 @@ -{ - "_meta": { - "snapshot_version": 1, - "header_hash": "fdebcce03a83af579b82cd49d8c908c70242a54f852ce9e13a98f4bf69e15d6d" - }, - "functions": { - "mj_defaultVFS": { - "name": "mj_defaultVFS", - "return_type": "void", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 82 - }, - "mj_mountVFS": { - "name": "mj_mountVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "filepath", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "provider", - "c_type": "const mjpResourceProvider *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 86 - }, - "mj_unmountVFS": { - "name": "mj_unmountVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 89 - }, - "mj_addFileVFS": { - "name": "mj_addFileVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "directory", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 92 - }, - "mj_addBufferVFS": { - "name": "mj_addBufferVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "buffer", - "c_type": "const void *", - "array_dim": null - }, - { - "name": "nbuffer", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 95 - }, - "mj_deleteFileVFS": { - "name": "mj_deleteFileVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 98 - }, - "mj_containsBufferVFS": { - "name": "mj_containsBufferVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 101 - }, - "mj_containsFileVFS": { - "name": "mj_containsFileVFS", - "return_type": "int", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - }, - { - "name": "directory", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 104 - }, - "mj_deleteVFS": { - "name": "mj_deleteVFS", - "return_type": "void", - "params": [ - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 107 - }, - "mj_getCacheSize": { - "name": "mj_getCacheSize", - "return_type": "size_t", - "params": [ - { - "name": "cache", - "c_type": "const mjCache *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 112 - }, - "mj_getCacheCapacity": { - "name": "mj_getCacheCapacity", - "return_type": "size_t", - "params": [ - { - "name": "cache", - "c_type": "const mjCache *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 115 - }, - "mj_setCacheCapacity": { - "name": "mj_setCacheCapacity", - "return_type": "size_t", - "params": [ - { - "name": "cache", - "c_type": "mjCache *", - "array_dim": null - }, - { - "name": "size", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 118 - }, - "mj_getCache": { - "name": "mj_getCache", - "return_type": "mjCache *", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 121 - }, - "mj_clearCache": { - "name": "mj_clearCache", - "return_type": "void", - "params": [ - { - "name": "cache", - "c_type": "mjCache *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 124 - }, - "mj_loadXML": { - "name": "mj_loadXML", - "return_type": "mjModel *", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 132 - }, - "mj_parseXML": { - "name": "mj_parseXML", - "return_type": "mjSpec *", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 136 - }, - "mj_parseXMLString": { - "name": "mj_parseXMLString", - "return_type": "mjSpec *", - "params": [ - { - "name": "xml", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 140 - }, - "mj_parse": { - "name": "mj_parse", - "return_type": "mjSpec *", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 144 - }, - "mj_encode": { - "name": "mj_encode", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 150 - }, - "mj_compile": { - "name": "mj_compile", - "return_type": "mjModel *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 156 - }, - "mj_copyBack": { - "name": "mj_copyBack", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 159 - }, - "mj_recompile": { - "name": "mj_recompile", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "m", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 163 - }, - "mj_saveLastXML": { - "name": "mj_saveLastXML", - "return_type": "int", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 168 - }, - "mj_freeLastXML": { - "name": "mj_freeLastXML", - "return_type": "void", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 171 - }, - "mj_saveXMLString": { - "name": "mj_saveXMLString", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "xml", - "c_type": "char *", - "array_dim": null - }, - { - "name": "xml_sz", - "c_type": "int", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 176 - }, - "mj_saveXML": { - "name": "mj_saveXML", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 180 - }, - "mju_getXMLDependencies": { - "name": "mju_getXMLDependencies", - "return_type": "void", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "dependencies", - "c_type": "mjStringVec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 184 - }, - "mj_step": { - "name": "mj_step", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 189 - }, - "mj_step1": { - "name": "mj_step1", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 192 - }, - "mj_step2": { - "name": "mj_step2", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 195 - }, - "mj_forward": { - "name": "mj_forward", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 198 - }, - "mj_inverse": { - "name": "mj_inverse", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 201 - }, - "mj_forwardSkip": { - "name": "mj_forwardSkip", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "skipstage", - "c_type": "int", - "array_dim": null - }, - { - "name": "skipsensor", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 204 - }, - "mj_inverseSkip": { - "name": "mj_inverseSkip", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "skipstage", - "c_type": "int", - "array_dim": null - }, - { - "name": "skipsensor", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 207 - }, - "mj_defaultLROpt": { - "name": "mj_defaultLROpt", - "return_type": "void", - "params": [ - { - "name": "opt", - "c_type": "mjLROpt *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 213 - }, - "mj_defaultSolRefImp": { - "name": "mj_defaultSolRefImp", - "return_type": "void", - "params": [ - { - "name": "solref", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "solimp", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 217 - }, - "mj_defaultOption": { - "name": "mj_defaultOption", - "return_type": "void", - "params": [ - { - "name": "opt", - "c_type": "mjOption *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 220 - }, - "mj_defaultVisual": { - "name": "mj_defaultVisual", - "return_type": "void", - "params": [ - { - "name": "vis", - "c_type": "mjVisual *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 223 - }, - "mj_copyModel": { - "name": "mj_copyModel", - "return_type": "mjModel *", - "params": [ - { - "name": "dest", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 227 - }, - "mj_saveModel": { - "name": "mj_saveModel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "buffer", - "c_type": "void *", - "array_dim": null - }, - { - "name": "buffer_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 231 - }, - "mj_loadModel": { - "name": "mj_loadModel", - "return_type": "mjModel *", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 236 - }, - "mj_loadModelBuffer": { - "name": "mj_loadModelBuffer", - "return_type": "mjModel *", - "params": [ - { - "name": "buffer", - "c_type": "const void *", - "array_dim": null - }, - { - "name": "buffer_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 239 - }, - "mj_deleteModel": { - "name": "mj_deleteModel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 242 - }, - "mj_sizeModel": { - "name": "mj_sizeModel", - "return_type": "mjtSize", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 245 - }, - "mj_makeData": { - "name": "mj_makeData", - "return_type": "mjData *", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 249 - }, - "mj_copyData": { - "name": "mj_copyData", - "return_type": "mjData *", - "params": [ - { - "name": "dest", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 253 - }, - "mjv_copyData": { - "name": "mjv_copyData", - "return_type": "mjData *", - "params": [ - { - "name": "dest", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 256 - }, - "mj_resetData": { - "name": "mj_resetData", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 259 - }, - "mj_resetDataDebug": { - "name": "mj_resetDataDebug", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "debug_value", - "c_type": "unsigned char", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 262 - }, - "mj_resetDataKeyframe": { - "name": "mj_resetDataKeyframe", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "key", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 265 - }, - "mj_markStack": { - "name": "mj_markStack", - "return_type": "void", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 270 - }, - "mj_freeStack": { - "name": "mj_freeStack", - "return_type": "void", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 274 - }, - "mj_stackAllocByte": { - "name": "mj_stackAllocByte", - "return_type": "void *", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "bytes", - "c_type": "size_t", - "array_dim": null - }, - { - "name": "alignment", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 280 - }, - "mj_stackAllocNum": { - "name": "mj_stackAllocNum", - "return_type": "mjtNum *", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "size", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 283 - }, - "mj_stackAllocInt": { - "name": "mj_stackAllocInt", - "return_type": "int *", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "size", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 286 - }, - "mj_deleteData": { - "name": "mj_deleteData", - "return_type": "void", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 289 - }, - "mj_resetCallbacks": { - "name": "mj_resetCallbacks", - "return_type": "void", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 292 - }, - "mj_setConst": { - "name": "mj_setConst", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 295 - }, - "mj_setLengthRange": { - "name": "mj_setLengthRange", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "index", - "c_type": "int", - "array_dim": null - }, - { - "name": "opt", - "c_type": "const mjLROpt *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "error_sz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 299 - }, - "mj_makeSpec": { - "name": "mj_makeSpec", - "return_type": "mjSpec *", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 303 - }, - "mj_copySpec": { - "name": "mj_copySpec", - "return_type": "mjSpec *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 306 - }, - "mj_deleteSpec": { - "name": "mj_deleteSpec", - "return_type": "void", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 309 - }, - "mjs_activatePlugin": { - "name": "mjs_activatePlugin", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 312 - }, - "mjs_setDeepCopy": { - "name": "mjs_setDeepCopy", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "deepcopy", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 315 - }, - "mj_printFormattedModel": { - "name": "mj_printFormattedModel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "float_format", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 322 - }, - "mj_printModel": { - "name": "mj_printModel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 325 - }, - "mj_printFormattedData": { - "name": "mj_printFormattedData", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "float_format", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 329 - }, - "mj_printData": { - "name": "mj_printData", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 333 - }, - "mju_printMat": { - "name": "mju_printMat", - "return_type": "void", - "params": [ - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 336 - }, - "mju_printMatSparse": { - "name": "mju_printMatSparse", - "return_type": "void", - "params": [ - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "rownnz", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "rowadr", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "colind", - "c_type": "const int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 339 - }, - "mj_printSchema": { - "name": "mj_printSchema", - "return_type": "int", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "buffer", - "c_type": "char *", - "array_dim": null - }, - { - "name": "buffer_sz", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_html", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_pad", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 343 - }, - "mj_printScene": { - "name": "mj_printScene", - "return_type": "void", - "params": [ - { - "name": "s", - "c_type": "const mjvScene *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 347 - }, - "mj_printFormattedScene": { - "name": "mj_printFormattedScene", - "return_type": "void", - "params": [ - { - "name": "s", - "c_type": "const mjvScene *", - "array_dim": null - }, - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "float_format", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 351 - }, - "mj_fwdKinematics": { - "name": "mj_fwdKinematics", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 358 - }, - "mj_fwdPosition": { - "name": "mj_fwdPosition", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 361 - }, - "mj_fwdVelocity": { - "name": "mj_fwdVelocity", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 364 - }, - "mj_fwdActuation": { - "name": "mj_fwdActuation", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 367 - }, - "mj_fwdAcceleration": { - "name": "mj_fwdAcceleration", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 370 - }, - "mj_fwdConstraint": { - "name": "mj_fwdConstraint", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 373 - }, - "mj_Euler": { - "name": "mj_Euler", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 376 - }, - "mj_RungeKutta": { - "name": "mj_RungeKutta", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "N", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 379 - }, - "mj_implicit": { - "name": "mj_implicit", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 382 - }, - "mj_invPosition": { - "name": "mj_invPosition", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 385 - }, - "mj_invVelocity": { - "name": "mj_invVelocity", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 388 - }, - "mj_invConstraint": { - "name": "mj_invConstraint", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 391 - }, - "mj_compareFwdInv": { - "name": "mj_compareFwdInv", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 394 - }, - "mj_sensorPos": { - "name": "mj_sensorPos", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 400 - }, - "mj_sensorVel": { - "name": "mj_sensorVel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 403 - }, - "mj_sensorAcc": { - "name": "mj_sensorAcc", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 406 - }, - "mj_energyPos": { - "name": "mj_energyPos", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 409 - }, - "mj_energyVel": { - "name": "mj_energyVel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 412 - }, - "mj_checkPos": { - "name": "mj_checkPos", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 415 - }, - "mj_checkVel": { - "name": "mj_checkVel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 418 - }, - "mj_checkAcc": { - "name": "mj_checkAcc", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 421 - }, - "mj_kinematics": { - "name": "mj_kinematics", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 424 - }, - "mj_comPos": { - "name": "mj_comPos", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 427 - }, - "mj_camlight": { - "name": "mj_camlight", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 430 - }, - "mj_flex": { - "name": "mj_flex", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 433 - }, - "mj_tendon": { - "name": "mj_tendon", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 436 - }, - "mj_transmission": { - "name": "mj_transmission", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 439 - }, - "mj_crb": { - "name": "mj_crb", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 442 - }, - "mj_makeM": { - "name": "mj_makeM", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 445 - }, - "mj_factorM": { - "name": "mj_factorM", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 448 - }, - "mj_solveM": { - "name": "mj_solveM", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "x", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "y", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 451 - }, - "mj_solveM2": { - "name": "mj_solveM2", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "x", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "y", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "sqrtInvD", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 454 - }, - "mj_comVel": { - "name": "mj_comVel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 458 - }, - "mj_passive": { - "name": "mj_passive", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 461 - }, - "mj_subtreeVel": { - "name": "mj_subtreeVel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 464 - }, - "mj_rne": { - "name": "mj_rne", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "flg_acc", - "c_type": "int", - "array_dim": null - }, - { - "name": "result", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 467 - }, - "mj_rnePostConstraint": { - "name": "mj_rnePostConstraint", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 470 - }, - "mj_maxContact": { - "name": "mj_maxContact", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "g1", - "c_type": "int", - "array_dim": null - }, - { - "name": "g2", - "c_type": "int", - "array_dim": null - }, - { - "name": "has_margin", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 475 - }, - "mj_collision": { - "name": "mj_collision", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 478 - }, - "mj_makeConstraint": { - "name": "mj_makeConstraint", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 481 - }, - "mj_island": { - "name": "mj_island", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 484 - }, - "mj_projectConstraint": { - "name": "mj_projectConstraint", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 487 - }, - "mj_referenceConstraint": { - "name": "mj_referenceConstraint", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 490 - }, - "mj_constraintUpdate": { - "name": "mj_constraintUpdate", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "jar", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "cost", - "c_type": "mjtNum [1]", - "array_dim": 1 - }, - { - "name": "flg_coneHessian", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 495 - }, - "mj_stateSize": { - "name": "mj_stateSize", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "sig", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 502 - }, - "mj_getState": { - "name": "mj_getState", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "state", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "sig", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 505 - }, - "mj_extractState": { - "name": "mj_extractState", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "srcsig", - "c_type": "int", - "array_dim": null - }, - { - "name": "dst", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "dstsig", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 508 - }, - "mj_setState": { - "name": "mj_setState", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "state", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "sig", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 512 - }, - "mj_copyState": { - "name": "mj_copyState", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "dst", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "sig", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 515 - }, - "mj_readCtrl": { - "name": "mj_readCtrl", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - }, - { - "name": "time", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "interp", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 520 - }, - "mj_readSensor": { - "name": "mj_readSensor", - "return_type": "const mjtNum *", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - }, - { - "name": "time", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "result", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "interp", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 526 - }, - "mj_initCtrlHistory": { - "name": "mj_initCtrlHistory", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - }, - { - "name": "times", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "values", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 531 - }, - "mj_initSensorHistory": { - "name": "mj_initSensorHistory", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - }, - { - "name": "times", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "values", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "phase", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 537 - }, - "mj_setKeyframe": { - "name": "mj_setKeyframe", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "k", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 541 - }, - "mj_addContact": { - "name": "mj_addContact", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjContact *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 544 - }, - "mj_isPyramidal": { - "name": "mj_isPyramidal", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 547 - }, - "mj_isSparse": { - "name": "mj_isSparse", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 550 - }, - "mj_isDual": { - "name": "mj_isDual", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 553 - }, - "mj_mulJacVec": { - "name": "mj_mulJacVec", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 556 - }, - "mj_mulJacTVec": { - "name": "mj_mulJacTVec", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 559 - }, - "mj_jac": { - "name": "mj_jac", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacr", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "point", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 563 - }, - "mj_jacBody": { - "name": "mj_jacBody", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacr", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 568 - }, - "mj_jacBodyCom": { - "name": "mj_jacBodyCom", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacr", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 572 - }, - "mj_jacSubtreeCom": { - "name": "mj_jacSubtreeCom", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 575 - }, - "mj_jacGeom": { - "name": "mj_jacGeom", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacr", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "geom", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 579 - }, - "mj_jacSite": { - "name": "mj_jacSite", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacr", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "site", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 583 - }, - "mj_jacPointAxis": { - "name": "mj_jacPointAxis", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "jacPoint", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacAxis", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "point", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "axis", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 587 - }, - "mj_jacDot": { - "name": "mj_jacDot", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "jacp", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "jacr", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "point", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 592 - }, - "mj_angmomMat": { - "name": "mj_angmomMat", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 596 - }, - "mj_name2id": { - "name": "mj_name2id", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "type", - "c_type": "int", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 599 - }, - "mj_id2name": { - "name": "mj_id2name", - "return_type": "const char *", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "type", - "c_type": "int", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 602 - }, - "mj_fullM": { - "name": "mj_fullM", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "dst", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 605 - }, - "mj_mulM": { - "name": "mj_mulM", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 608 - }, - "mj_mulM2": { - "name": "mj_mulM2", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 611 - }, - "mj_addM": { - "name": "mj_addM", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "dst", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "rownnz", - "c_type": "int *", - "array_dim": null - }, - { - "name": "rowadr", - "c_type": "int *", - "array_dim": null - }, - { - "name": "colind", - "c_type": "int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 616 - }, - "mj_applyFT": { - "name": "mj_applyFT", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "force", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "torque", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "point", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - }, - { - "name": "qfrc_target", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 620 - }, - "mj_objectVelocity": { - "name": "mj_objectVelocity", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "objtype", - "c_type": "int", - "array_dim": null - }, - { - "name": "objid", - "c_type": "int", - "array_dim": null - }, - { - "name": "res", - "c_type": "mjtNum [6]", - "array_dim": 6 - }, - { - "name": "flg_local", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 624 - }, - "mj_objectAcceleration": { - "name": "mj_objectAcceleration", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "objtype", - "c_type": "int", - "array_dim": null - }, - { - "name": "objid", - "c_type": "int", - "array_dim": null - }, - { - "name": "res", - "c_type": "mjtNum [6]", - "array_dim": 6 - }, - { - "name": "flg_local", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 628 - }, - "mj_geomDistance": { - "name": "mj_geomDistance", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "geom1", - "c_type": "int", - "array_dim": null - }, - { - "name": "geom2", - "c_type": "int", - "array_dim": null - }, - { - "name": "distmax", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "fromto", - "c_type": "mjtNum [6]", - "array_dim": 6 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 633 - }, - "mj_contactForce": { - "name": "mj_contactForce", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - }, - { - "name": "result", - "c_type": "mjtNum [6]", - "array_dim": 6 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 637 - }, - "mj_differentiatePos": { - "name": "mj_differentiatePos", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "qvel", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "dt", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "qpos1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "qpos2", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 640 - }, - "mj_integratePos": { - "name": "mj_integratePos", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "qpos", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "qvel", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "dt", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 644 - }, - "mj_normalizeQuat": { - "name": "mj_normalizeQuat", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "qpos", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 647 - }, - "mj_local2Global": { - "name": "mj_local2Global", - "return_type": "void", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "xpos", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "xmat", - "c_type": "mjtNum [9]", - "array_dim": 9 - }, - { - "name": "pos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "body", - "c_type": "int", - "array_dim": null - }, - { - "name": "sameframe", - "c_type": "mjtByte", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 650 - }, - "mj_getTotalmass": { - "name": "mj_getTotalmass", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 654 - }, - "mj_setTotalmass": { - "name": "mj_setTotalmass", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "newmass", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 657 - }, - "mj_getPluginConfig": { - "name": "mj_getPluginConfig", - "return_type": "const char *", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "plugin_id", - "c_type": "int", - "array_dim": null - }, - { - "name": "attrib", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 661 - }, - "mj_loadPluginLibrary": { - "name": "mj_loadPluginLibrary", - "return_type": "void", - "params": [ - { - "name": "path", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 664 - }, - "mj_loadAllPluginLibraries": { - "name": "mj_loadAllPluginLibraries", - "return_type": "void", - "params": [ - { - "name": "directory", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "callback", - "c_type": "mjfPluginLibraryLoadCallback", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 669 - }, - "mj_version": { - "name": "mj_version", - "return_type": "int", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 672 - }, - "mj_versionString": { - "name": "mj_versionString", - "return_type": "const char *", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 675 - }, - "mj_ray": { - "name": "mj_ray", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "geomgroup", - "c_type": "const mjtByte *", - "array_dim": null - }, - { - "name": "flg_static", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "bodyexclude", - "c_type": "int", - "array_dim": null - }, - { - "name": "geomid", - "c_type": "int [1]", - "array_dim": 1 - }, - { - "name": "normal", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 684 - }, - "mj_multiRay": { - "name": "mj_multiRay", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "geomgroup", - "c_type": "const mjtByte *", - "array_dim": null - }, - { - "name": "flg_static", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "bodyexclude", - "c_type": "int", - "array_dim": null - }, - { - "name": "geomid", - "c_type": "int *", - "array_dim": null - }, - { - "name": "dist", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "normal", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "nray", - "c_type": "int", - "array_dim": null - }, - { - "name": "cutoff", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 692 - }, - "mj_rayHfield": { - "name": "mj_rayHfield", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "geomid", - "c_type": "int", - "array_dim": null - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "normal", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 698 - }, - "mj_rayMesh": { - "name": "mj_rayMesh", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "geomid", - "c_type": "int", - "array_dim": null - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "normal", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 703 - }, - "mju_rayGeom": { - "name": "mju_rayGeom", - "return_type": "mjtNum", - "params": [ - { - "name": "pos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - }, - { - "name": "size", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "geomtype", - "c_type": "int", - "array_dim": null - }, - { - "name": "normal", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 708 - }, - "mj_rayFlex": { - "name": "mj_rayFlex", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "flex_layer", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_vert", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "flg_edge", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "flg_face", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "flg_skin", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "flexid", - "c_type": "int", - "array_dim": null - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vertid", - "c_type": "int [1]", - "array_dim": 1 - }, - { - "name": "normal", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 715 - }, - "mju_raySkin": { - "name": "mju_raySkin", - "return_type": "mjtNum", - "params": [ - { - "name": "nface", - "c_type": "int", - "array_dim": null - }, - { - "name": "nvert", - "c_type": "int", - "array_dim": null - }, - { - "name": "face", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "vert", - "c_type": "const float *", - "array_dim": null - }, - { - "name": "pnt", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vertid", - "c_type": "int [1]", - "array_dim": 1 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 723 - }, - "mjv_defaultCamera": { - "name": "mjv_defaultCamera", - "return_type": "void", - "params": [ - { - "name": "cam", - "c_type": "mjvCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 729 - }, - "mjv_defaultFreeCamera": { - "name": "mjv_defaultFreeCamera", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "cam", - "c_type": "mjvCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 732 - }, - "mjv_defaultPerturb": { - "name": "mjv_defaultPerturb", - "return_type": "void", - "params": [ - { - "name": "pert", - "c_type": "mjvPerturb *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 735 - }, - "mjv_room2model": { - "name": "mjv_room2model", - "return_type": "void", - "params": [ - { - "name": "modelpos", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "modelquat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "roompos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "roomquat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 738 - }, - "mjv_model2room": { - "name": "mjv_model2room", - "return_type": "void", - "params": [ - { - "name": "roompos", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "roomquat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "modelpos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "modelquat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 742 - }, - "mjv_cameraInModel": { - "name": "mjv_cameraInModel", - "return_type": "void", - "params": [ - { - "name": "headpos", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "forward", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "up", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 746 - }, - "mjv_cameraInRoom": { - "name": "mjv_cameraInRoom", - "return_type": "void", - "params": [ - { - "name": "headpos", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "forward", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "up", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 750 - }, - "mjv_frustumHeight": { - "name": "mjv_frustumHeight", - "return_type": "mjtNum", - "params": [ - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 754 - }, - "mjv_alignToCamera": { - "name": "mjv_alignToCamera", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "forward", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 757 - }, - "mjv_moveCamera": { - "name": "mjv_moveCamera", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "action", - "c_type": "int", - "array_dim": null - }, - { - "name": "reldx", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "reldy", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - }, - { - "name": "cam", - "c_type": "mjvCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 760 - }, - "mjv_movePerturb": { - "name": "mjv_movePerturb", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "action", - "c_type": "int", - "array_dim": null - }, - { - "name": "reldx", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "reldy", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - }, - { - "name": "pert", - "c_type": "mjvPerturb *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 764 - }, - "mjv_moveModel": { - "name": "mjv_moveModel", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "action", - "c_type": "int", - "array_dim": null - }, - { - "name": "reldx", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "reldy", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "roomup", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 768 - }, - "mjv_initPerturb": { - "name": "mjv_initPerturb", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - }, - { - "name": "pert", - "c_type": "mjvPerturb *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 772 - }, - "mjv_applyPerturbPose": { - "name": "mjv_applyPerturbPose", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "pert", - "c_type": "const mjvPerturb *", - "array_dim": null - }, - { - "name": "flg_paused", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 776 - }, - "mjv_applyPerturbForce": { - "name": "mjv_applyPerturbForce", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "pert", - "c_type": "const mjvPerturb *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 780 - }, - "mjv_averageCamera": { - "name": "mjv_averageCamera", - "return_type": "mjvGLCamera", - "params": [ - { - "name": "cam1", - "c_type": "const mjvGLCamera *", - "array_dim": null - }, - { - "name": "cam2", - "c_type": "const mjvGLCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 783 - }, - "mjv_select": { - "name": "mjv_select", - "return_type": "int", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "vopt", - "c_type": "const mjvOption *", - "array_dim": null - }, - { - "name": "aspectratio", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "relx", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "rely", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "scn", - "c_type": "const mjvScene *", - "array_dim": null - }, - { - "name": "selpnt", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "geomid", - "c_type": "int [1]", - "array_dim": 1 - }, - { - "name": "flexid", - "c_type": "int [1]", - "array_dim": 1 - }, - { - "name": "skinid", - "c_type": "int [1]", - "array_dim": 1 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 787 - }, - "mjv_defaultOption": { - "name": "mjv_defaultOption", - "return_type": "void", - "params": [ - { - "name": "opt", - "c_type": "mjvOption *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 796 - }, - "mjv_defaultFigure": { - "name": "mjv_defaultFigure", - "return_type": "void", - "params": [ - { - "name": "fig", - "c_type": "mjvFigure *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 799 - }, - "mjv_initGeom": { - "name": "mjv_initGeom", - "return_type": "void", - "params": [ - { - "name": "geom", - "c_type": "mjvGeom *", - "array_dim": null - }, - { - "name": "type", - "c_type": "int", - "array_dim": null - }, - { - "name": "size", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "pos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - }, - { - "name": "rgba", - "c_type": "const float [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 803 - }, - "mjv_connector": { - "name": "mjv_connector", - "return_type": "void", - "params": [ - { - "name": "geom", - "c_type": "mjvGeom *", - "array_dim": null - }, - { - "name": "type", - "c_type": "int", - "array_dim": null - }, - { - "name": "width", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "from", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "to", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 809 - }, - "mjv_defaultScene": { - "name": "mjv_defaultScene", - "return_type": "void", - "params": [ - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 813 - }, - "mjv_makeScene": { - "name": "mjv_makeScene", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - }, - { - "name": "maxgeom", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 816 - }, - "mjv_freeScene": { - "name": "mjv_freeScene", - "return_type": "void", - "params": [ - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 819 - }, - "mjv_updateScene": { - "name": "mjv_updateScene", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "opt", - "c_type": "const mjvOption *", - "array_dim": null - }, - { - "name": "pert", - "c_type": "const mjvPerturb *", - "array_dim": null - }, - { - "name": "cam", - "c_type": "mjvCamera *", - "array_dim": null - }, - { - "name": "catmask", - "c_type": "int", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 822 - }, - "mjv_copyModel": { - "name": "mjv_copyModel", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjModel *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const mjModel *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 827 - }, - "mjv_addGeoms": { - "name": "mjv_addGeoms", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "opt", - "c_type": "const mjvOption *", - "array_dim": null - }, - { - "name": "pert", - "c_type": "const mjvPerturb *", - "array_dim": null - }, - { - "name": "catmask", - "c_type": "int", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 830 - }, - "mjv_makeLights": { - "name": "mjv_makeLights", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 834 - }, - "mjv_updateCamera": { - "name": "mjv_updateCamera", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "cam", - "c_type": "mjvCamera *", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 837 - }, - "mjv_updateSkin": { - "name": "mjv_updateSkin", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 840 - }, - "mjv_cameraFrame": { - "name": "mjv_cameraFrame", - "return_type": "void", - "params": [ - { - "name": "headpos", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "forward", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "up", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "right", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "cam", - "c_type": "const mjvCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 844 - }, - "mjv_cameraFrustum": { - "name": "mjv_cameraFrustum", - "return_type": "void", - "params": [ - { - "name": "zver", - "c_type": "float [2]", - "array_dim": 2 - }, - { - "name": "zhor", - "c_type": "float [2]", - "array_dim": 2 - }, - { - "name": "zclip", - "c_type": "float [2]", - "array_dim": 2 - }, - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "cam", - "c_type": "const mjvCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 849 - }, - "mjr_defaultContext": { - "name": "mjr_defaultContext", - "return_type": "void", - "params": [ - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 856 - }, - "mjr_makeContext": { - "name": "mjr_makeContext", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - }, - { - "name": "fontscale", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 859 - }, - "mjr_changeFont": { - "name": "mjr_changeFont", - "return_type": "void", - "params": [ - { - "name": "fontscale", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 862 - }, - "mjr_addAux": { - "name": "mjr_addAux", - "return_type": "void", - "params": [ - { - "name": "index", - "c_type": "int", - "array_dim": null - }, - { - "name": "width", - "c_type": "int", - "array_dim": null - }, - { - "name": "height", - "c_type": "int", - "array_dim": null - }, - { - "name": "samples", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 865 - }, - "mjr_freeContext": { - "name": "mjr_freeContext", - "return_type": "void", - "params": [ - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 868 - }, - "mjr_resizeOffscreen": { - "name": "mjr_resizeOffscreen", - "return_type": "void", - "params": [ - { - "name": "width", - "c_type": "int", - "array_dim": null - }, - { - "name": "height", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 871 - }, - "mjr_uploadTexture": { - "name": "mjr_uploadTexture", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - }, - { - "name": "texid", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 874 - }, - "mjr_uploadMesh": { - "name": "mjr_uploadMesh", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - }, - { - "name": "meshid", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 877 - }, - "mjr_uploadHField": { - "name": "mjr_uploadHField", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - }, - { - "name": "hfieldid", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 880 - }, - "mjr_restoreBuffer": { - "name": "mjr_restoreBuffer", - "return_type": "void", - "params": [ - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 883 - }, - "mjr_setBuffer": { - "name": "mjr_setBuffer", - "return_type": "void", - "params": [ - { - "name": "framebuffer", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 887 - }, - "mjr_readPixels": { - "name": "mjr_readPixels", - "return_type": "void", - "params": [ - { - "name": "rgb", - "c_type": "unsigned char *", - "array_dim": null - }, - { - "name": "depth", - "c_type": "float *", - "array_dim": null - }, - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 891 - }, - "mjr_drawPixels": { - "name": "mjr_drawPixels", - "return_type": "void", - "params": [ - { - "name": "rgb", - "c_type": "const unsigned char *", - "array_dim": null - }, - { - "name": "depth", - "c_type": "const float *", - "array_dim": null - }, - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 896 - }, - "mjr_blitBuffer": { - "name": "mjr_blitBuffer", - "return_type": "void", - "params": [ - { - "name": "src", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "dst", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "flg_color", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_depth", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 901 - }, - "mjr_setAux": { - "name": "mjr_setAux", - "return_type": "void", - "params": [ - { - "name": "index", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 905 - }, - "mjr_blitAux": { - "name": "mjr_blitAux", - "return_type": "void", - "params": [ - { - "name": "index", - "c_type": "int", - "array_dim": null - }, - { - "name": "src", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "left", - "c_type": "int", - "array_dim": null - }, - { - "name": "bottom", - "c_type": "int", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 908 - }, - "mjr_text": { - "name": "mjr_text", - "return_type": "void", - "params": [ - { - "name": "font", - "c_type": "int", - "array_dim": null - }, - { - "name": "txt", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - }, - { - "name": "x", - "c_type": "float", - "array_dim": null - }, - { - "name": "y", - "c_type": "float", - "array_dim": null - }, - { - "name": "r", - "c_type": "float", - "array_dim": null - }, - { - "name": "g", - "c_type": "float", - "array_dim": null - }, - { - "name": "b", - "c_type": "float", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 911 - }, - "mjr_overlay": { - "name": "mjr_overlay", - "return_type": "void", - "params": [ - { - "name": "font", - "c_type": "int", - "array_dim": null - }, - { - "name": "gridpos", - "c_type": "int", - "array_dim": null - }, - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "overlay", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "overlay2", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 915 - }, - "mjr_maxViewport": { - "name": "mjr_maxViewport", - "return_type": "mjrRect", - "params": [ - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 919 - }, - "mjr_rectangle": { - "name": "mjr_rectangle", - "return_type": "void", - "params": [ - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "r", - "c_type": "float", - "array_dim": null - }, - { - "name": "g", - "c_type": "float", - "array_dim": null - }, - { - "name": "b", - "c_type": "float", - "array_dim": null - }, - { - "name": "a", - "c_type": "float", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 922 - }, - "mjr_label": { - "name": "mjr_label", - "return_type": "void", - "params": [ - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "font", - "c_type": "int", - "array_dim": null - }, - { - "name": "txt", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "r", - "c_type": "float", - "array_dim": null - }, - { - "name": "g", - "c_type": "float", - "array_dim": null - }, - { - "name": "b", - "c_type": "float", - "array_dim": null - }, - { - "name": "a", - "c_type": "float", - "array_dim": null - }, - { - "name": "rt", - "c_type": "float", - "array_dim": null - }, - { - "name": "gt", - "c_type": "float", - "array_dim": null - }, - { - "name": "bt", - "c_type": "float", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 925 - }, - "mjr_figure": { - "name": "mjr_figure", - "return_type": "void", - "params": [ - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "fig", - "c_type": "mjvFigure *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 930 - }, - "mjr_render": { - "name": "mjr_render", - "return_type": "void", - "params": [ - { - "name": "viewport", - "c_type": "mjrRect", - "array_dim": null - }, - { - "name": "scn", - "c_type": "mjvScene *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 933 - }, - "mjr_finish": { - "name": "mjr_finish", - "return_type": "void", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 936 - }, - "mjr_getError": { - "name": "mjr_getError", - "return_type": "int", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 939 - }, - "mjr_findRect": { - "name": "mjr_findRect", - "return_type": "int", - "params": [ - { - "name": "x", - "c_type": "int", - "array_dim": null - }, - { - "name": "y", - "c_type": "int", - "array_dim": null - }, - { - "name": "nrect", - "c_type": "int", - "array_dim": null - }, - { - "name": "rect", - "c_type": "const mjrRect *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 942 - }, - "mjui_themeSpacing": { - "name": "mjui_themeSpacing", - "return_type": "mjuiThemeSpacing", - "params": [ - { - "name": "ind", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 948 - }, - "mjui_themeColor": { - "name": "mjui_themeColor", - "return_type": "mjuiThemeColor", - "params": [ - { - "name": "ind", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 951 - }, - "mjui_add": { - "name": "mjui_add", - "return_type": "void", - "params": [ - { - "name": "ui", - "c_type": "mjUI *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjuiDef *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 954 - }, - "mjui_addToSection": { - "name": "mjui_addToSection", - "return_type": "void", - "params": [ - { - "name": "ui", - "c_type": "mjUI *", - "array_dim": null - }, - { - "name": "sect", - "c_type": "int", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjuiDef *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 957 - }, - "mjui_resize": { - "name": "mjui_resize", - "return_type": "void", - "params": [ - { - "name": "ui", - "c_type": "mjUI *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 960 - }, - "mjui_update": { - "name": "mjui_update", - "return_type": "void", - "params": [ - { - "name": "section", - "c_type": "int", - "array_dim": null - }, - { - "name": "item", - "c_type": "int", - "array_dim": null - }, - { - "name": "ui", - "c_type": "const mjUI *", - "array_dim": null - }, - { - "name": "state", - "c_type": "const mjuiState *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 963 - }, - "mjui_event": { - "name": "mjui_event", - "return_type": "mjuiItem *", - "params": [ - { - "name": "ui", - "c_type": "mjUI *", - "array_dim": null - }, - { - "name": "state", - "c_type": "mjuiState *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 967 - }, - "mjui_render": { - "name": "mjui_render", - "return_type": "void", - "params": [ - { - "name": "ui", - "c_type": "mjUI *", - "array_dim": null - }, - { - "name": "state", - "c_type": "const mjuiState *", - "array_dim": null - }, - { - "name": "con", - "c_type": "const mjrContext *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 970 - }, - "mju_error": { - "name": "mju_error", - "return_type": "void", - "params": [ - { - "name": "msg", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 976 - }, - "mju_warning": { - "name": "mju_warning", - "return_type": "void", - "params": [ - { - "name": "msg", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 979 - }, - "mju_clearHandlers": { - "name": "mju_clearHandlers", - "return_type": "void", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 982 - }, - "mju_setLogHandler": { - "name": "mju_setLogHandler", - "return_type": "mjfLogHandler", - "params": [ - { - "name": "handler", - "c_type": "mjfLogHandler", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 986 - }, - "mju_getLogConfig": { - "name": "mju_getLogConfig", - "return_type": "mjLogConfig", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 989 - }, - "mju_setLogConfig": { - "name": "mju_setLogConfig", - "return_type": "void", - "params": [ - { - "name": "config", - "c_type": "mjLogConfig", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 992 - }, - "mju_info": { - "name": "mju_info", - "return_type": "void", - "params": [ - { - "name": "topic", - "c_type": "int", - "array_dim": null - }, - { - "name": "msg", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 995 - }, - "mju_message": { - "name": "mju_message", - "return_type": "void", - "params": [ - { - "name": "msg", - "c_type": "const mjLogMessage *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 998 - }, - "mju_malloc": { - "name": "mju_malloc", - "return_type": "void *", - "params": [ - { - "name": "size", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1001 - }, - "mju_free": { - "name": "mju_free", - "return_type": "void", - "params": [ - { - "name": "ptr", - "c_type": "void *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1004 - }, - "mj_warning": { - "name": "mj_warning", - "return_type": "void", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "warning", - "c_type": "int", - "array_dim": null - }, - { - "name": "info", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1007 - }, - "mju_writeLog": { - "name": "mju_writeLog", - "return_type": "void", - "params": [ - { - "name": "type", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "msg", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1010 - }, - "mjs_getError": { - "name": "mjs_getError", - "return_type": "const char *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1013 - }, - "mjs_getTimer": { - "name": "mjs_getTimer", - "return_type": "const double *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1016 - }, - "mjs_isWarning": { - "name": "mjs_isWarning", - "return_type": "int", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1019 - }, - "mju_zero3": { - "name": "mju_zero3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1063 - }, - "mju_copy3": { - "name": "mju_copy3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "data", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1066 - }, - "mju_scl3": { - "name": "mju_scl3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scl", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1069 - }, - "mju_add3": { - "name": "mju_add3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec1", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec2", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1072 - }, - "mju_sub3": { - "name": "mju_sub3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec1", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec2", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1075 - }, - "mju_addTo3": { - "name": "mju_addTo3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1078 - }, - "mju_subFrom3": { - "name": "mju_subFrom3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1081 - }, - "mju_addToScl3": { - "name": "mju_addToScl3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scl", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1084 - }, - "mju_addScl3": { - "name": "mju_addScl3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec1", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec2", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scl", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1087 - }, - "mju_normalize3": { - "name": "mju_normalize3", - "return_type": "mjtNum", - "params": [ - { - "name": "vec", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1090 - }, - "mju_norm3": { - "name": "mju_norm3", - "return_type": "mjtNum", - "params": [ - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1093 - }, - "mju_dot3": { - "name": "mju_dot3", - "return_type": "mjtNum", - "params": [ - { - "name": "vec1", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec2", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1096 - }, - "mju_dist3": { - "name": "mju_dist3", - "return_type": "mjtNum", - "params": [ - { - "name": "pos1", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "pos2", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1099 - }, - "mju_mulMatVec3": { - "name": "mju_mulMatVec3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1102 - }, - "mju_mulMatTVec3": { - "name": "mju_mulMatTVec3", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1105 - }, - "mju_cross": { - "name": "mju_cross", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "a", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "b", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1108 - }, - "mju_zero4": { - "name": "mju_zero4", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1111 - }, - "mju_unit4": { - "name": "mju_unit4", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1114 - }, - "mju_copy4": { - "name": "mju_copy4", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "data", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1117 - }, - "mju_normalize4": { - "name": "mju_normalize4", - "return_type": "mjtNum", - "params": [ - { - "name": "vec", - "c_type": "mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1120 - }, - "mju_zero": { - "name": "mju_zero", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1123 - }, - "mju_fill": { - "name": "mju_fill", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "val", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1126 - }, - "mju_copy": { - "name": "mju_copy", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1129 - }, - "mju_sum": { - "name": "mju_sum", - "return_type": "mjtNum", - "params": [ - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1132 - }, - "mju_L1": { - "name": "mju_L1", - "return_type": "mjtNum", - "params": [ - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1135 - }, - "mju_scl": { - "name": "mju_scl", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "scl", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1138 - }, - "mju_add": { - "name": "mju_add", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1141 - }, - "mju_sub": { - "name": "mju_sub", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1144 - }, - "mju_addTo": { - "name": "mju_addTo", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1147 - }, - "mju_subFrom": { - "name": "mju_subFrom", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1150 - }, - "mju_addToScl": { - "name": "mju_addToScl", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "scl", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1153 - }, - "mju_addScl": { - "name": "mju_addScl", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "scl", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1156 - }, - "mju_normalize": { - "name": "mju_normalize", - "return_type": "mjtNum", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1159 - }, - "mju_norm": { - "name": "mju_norm", - "return_type": "mjtNum", - "params": [ - { - "name": "res", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1162 - }, - "mju_dot": { - "name": "mju_dot", - "return_type": "mjtNum", - "params": [ - { - "name": "vec1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1165 - }, - "mju_mulMatVec": { - "name": "mju_mulMatVec", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1168 - }, - "mju_mulMatTVec": { - "name": "mju_mulMatTVec", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1171 - }, - "mju_mulVecMatVec": { - "name": "mju_mulVecMatVec", - "return_type": "mjtNum", - "params": [ - { - "name": "vec1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1174 - }, - "mju_transpose": { - "name": "mju_transpose", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1177 - }, - "mju_symmetrize": { - "name": "mju_symmetrize", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1180 - }, - "mju_eye": { - "name": "mju_eye", - "return_type": "void", - "params": [ - { - "name": "mat", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1183 - }, - "mju_mulMatMat": { - "name": "mju_mulMatMat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "mat2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "r1", - "c_type": "int", - "array_dim": null - }, - { - "name": "c1", - "c_type": "int", - "array_dim": null - }, - { - "name": "c2", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1186 - }, - "mju_mulMatMatT": { - "name": "mju_mulMatMatT", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "mat2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "r1", - "c_type": "int", - "array_dim": null - }, - { - "name": "c1", - "c_type": "int", - "array_dim": null - }, - { - "name": "r2", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1190 - }, - "mju_mulMatTMat": { - "name": "mju_mulMatTMat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat1", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "mat2", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "r1", - "c_type": "int", - "array_dim": null - }, - { - "name": "c1", - "c_type": "int", - "array_dim": null - }, - { - "name": "c2", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1194 - }, - "mju_sqrMatTD": { - "name": "mju_sqrMatTD", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "diag", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1198 - }, - "mju_transformSpatial": { - "name": "mju_transformSpatial", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [6]", - "array_dim": 6 - }, - { - "name": "vec", - "c_type": "const mjtNum [6]", - "array_dim": 6 - }, - { - "name": "flg_force", - "c_type": "int", - "array_dim": null - }, - { - "name": "newpos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "oldpos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "rotnew2old", - "c_type": "const mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1203 - }, - "mju_dense2sparse": { - "name": "mju_dense2sparse", - "return_type": "int", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - }, - { - "name": "rownnz", - "c_type": "int *", - "array_dim": null - }, - { - "name": "rowadr", - "c_type": "int *", - "array_dim": null - }, - { - "name": "colind", - "c_type": "int *", - "array_dim": null - }, - { - "name": "nnz", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1212 - }, - "mju_sparse2dense": { - "name": "mju_sparse2dense", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "nr", - "c_type": "int", - "array_dim": null - }, - { - "name": "nc", - "c_type": "int", - "array_dim": null - }, - { - "name": "rownnz", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "rowadr", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "colind", - "c_type": "const int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1216 - }, - "mju_sym2dense": { - "name": "mju_sym2dense", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - }, - { - "name": "rownnz", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "rowadr", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "colind", - "c_type": "const int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1220 - }, - "mju_rotVecQuat": { - "name": "mju_rotVecQuat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1227 - }, - "mju_negQuat": { - "name": "mju_negQuat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1230 - }, - "mju_mulQuat": { - "name": "mju_mulQuat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "quat1", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "quat2", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1233 - }, - "mju_mulQuatAxis": { - "name": "mju_mulQuatAxis", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "axis", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1236 - }, - "mju_axisAngle2Quat": { - "name": "mju_axisAngle2Quat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "axis", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "angle", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1239 - }, - "mju_quat2Vel": { - "name": "mju_quat2Vel", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "dt", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1242 - }, - "mju_subQuat": { - "name": "mju_subQuat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "qa", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "qb", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1245 - }, - "mju_quat2Mat": { - "name": "mju_quat2Mat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [9]", - "array_dim": 9 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1248 - }, - "mju_mat2Quat": { - "name": "mju_mat2Quat", - "return_type": "void", - "params": [ - { - "name": "quat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1251 - }, - "mju_derivQuat": { - "name": "mju_derivQuat", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "vel", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1254 - }, - "mju_quatIntegrate": { - "name": "mju_quatIntegrate", - "return_type": "void", - "params": [ - { - "name": "quat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "vel", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scale", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1257 - }, - "mju_quatZ2Vec": { - "name": "mju_quatZ2Vec", - "return_type": "void", - "params": [ - { - "name": "quat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1260 - }, - "mju_mat2Rot": { - "name": "mju_mat2Rot", - "return_type": "int", - "params": [ - { - "name": "quat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1264 - }, - "mju_euler2Quat": { - "name": "mju_euler2Quat", - "return_type": "void", - "params": [ - { - "name": "quat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "euler", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "seq", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1268 - }, - "mju_mulPose": { - "name": "mju_mulPose", - "return_type": "void", - "params": [ - { - "name": "posres", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quatres", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "pos1", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat1", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "pos2", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat2", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1274 - }, - "mju_negPose": { - "name": "mju_negPose", - "return_type": "void", - "params": [ - { - "name": "posres", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quatres", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "pos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1279 - }, - "mju_trnVecPose": { - "name": "mju_trnVecPose", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "pos", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "quat", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "vec", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1283 - }, - "mju_cholFactor": { - "name": "mju_cholFactor", - "return_type": "int", - "params": [ - { - "name": "mat", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - }, - { - "name": "mindiag", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1290 - }, - "mju_cholSolve": { - "name": "mju_cholSolve", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1293 - }, - "mju_cholUpdate": { - "name": "mju_cholUpdate", - "return_type": "int", - "params": [ - { - "name": "mat", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "x", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_plus", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1296 - }, - "mju_cholFactorBand": { - "name": "mju_cholFactorBand", - "return_type": "mjtNum", - "params": [ - { - "name": "mat", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "ntotal", - "c_type": "int", - "array_dim": null - }, - { - "name": "nband", - "c_type": "int", - "array_dim": null - }, - { - "name": "ndense", - "c_type": "int", - "array_dim": null - }, - { - "name": "diagadd", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "diagmul", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1304 - }, - "mju_cholSolveBand": { - "name": "mju_cholSolveBand", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "ntotal", - "c_type": "int", - "array_dim": null - }, - { - "name": "nband", - "c_type": "int", - "array_dim": null - }, - { - "name": "ndense", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1308 - }, - "mju_band2Dense": { - "name": "mju_band2Dense", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "ntotal", - "c_type": "int", - "array_dim": null - }, - { - "name": "nband", - "c_type": "int", - "array_dim": null - }, - { - "name": "ndense", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_sym", - "c_type": "mjtBool", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1312 - }, - "mju_dense2Band": { - "name": "mju_dense2Band", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "ntotal", - "c_type": "int", - "array_dim": null - }, - { - "name": "nband", - "c_type": "int", - "array_dim": null - }, - { - "name": "ndense", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1316 - }, - "mju_bandMulMatVec": { - "name": "mju_bandMulMatVec", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "mat", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "ntotal", - "c_type": "int", - "array_dim": null - }, - { - "name": "nband", - "c_type": "int", - "array_dim": null - }, - { - "name": "ndense", - "c_type": "int", - "array_dim": null - }, - { - "name": "nvec", - "c_type": "int", - "array_dim": null - }, - { - "name": "flg_sym", - "c_type": "mjtBool", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1319 - }, - "mju_bandDiag": { - "name": "mju_bandDiag", - "return_type": "int", - "params": [ - { - "name": "i", - "c_type": "int", - "array_dim": null - }, - { - "name": "ntotal", - "c_type": "int", - "array_dim": null - }, - { - "name": "nband", - "c_type": "int", - "array_dim": null - }, - { - "name": "ndense", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1323 - }, - "mju_eig3": { - "name": "mju_eig3", - "return_type": "int", - "params": [ - { - "name": "eigval", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "eigvec", - "c_type": "mjtNum [9]", - "array_dim": 9 - }, - { - "name": "quat", - "c_type": "mjtNum [4]", - "array_dim": 4 - }, - { - "name": "mat", - "c_type": "const mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1326 - }, - "mju_boxQP": { - "name": "mju_boxQP", - "return_type": "int", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "R", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "index", - "c_type": "int *", - "array_dim": null - }, - { - "name": "H", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "g", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - }, - { - "name": "lower", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "upper", - "c_type": "const mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1350 - }, - "mju_boxQPmalloc": { - "name": "mju_boxQPmalloc", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum **", - "array_dim": null - }, - { - "name": "R", - "c_type": "mjtNum **", - "array_dim": null - }, - { - "name": "index", - "c_type": "int **", - "array_dim": null - }, - { - "name": "H", - "c_type": "mjtNum **", - "array_dim": null - }, - { - "name": "g", - "c_type": "mjtNum **", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - }, - { - "name": "lower", - "c_type": "mjtNum **", - "array_dim": null - }, - { - "name": "upper", - "c_type": "mjtNum **", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1356 - }, - "mju_muscleGain": { - "name": "mju_muscleGain", - "return_type": "mjtNum", - "params": [ - { - "name": "len", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "vel", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "lengthrange", - "c_type": "const mjtNum [2]", - "array_dim": 2 - }, - { - "name": "acc0", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "prm", - "c_type": "const mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1363 - }, - "mju_muscleBias": { - "name": "mju_muscleBias", - "return_type": "mjtNum", - "params": [ - { - "name": "len", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "lengthrange", - "c_type": "const mjtNum [2]", - "array_dim": 2 - }, - { - "name": "acc0", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "prm", - "c_type": "const mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1367 - }, - "mju_muscleDynamics": { - "name": "mju_muscleDynamics", - "return_type": "mjtNum", - "params": [ - { - "name": "ctrl", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "act", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "prm", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1371 - }, - "mju_encodePyramid": { - "name": "mju_encodePyramid", - "return_type": "void", - "params": [ - { - "name": "pyramid", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "force", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "mu", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "dim", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1374 - }, - "mju_decodePyramid": { - "name": "mju_decodePyramid", - "return_type": "void", - "params": [ - { - "name": "force", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "pyramid", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "mu", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "dim", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1377 - }, - "mju_springDamper": { - "name": "mju_springDamper", - "return_type": "mjtNum", - "params": [ - { - "name": "pos0", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "vel0", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "Kp", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "Kv", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "dt", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1380 - }, - "mju_min": { - "name": "mju_min", - "return_type": "mjtNum", - "params": [ - { - "name": "a", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "b", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1383 - }, - "mju_max": { - "name": "mju_max", - "return_type": "mjtNum", - "params": [ - { - "name": "a", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "b", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1386 - }, - "mju_clip": { - "name": "mju_clip", - "return_type": "mjtNum", - "params": [ - { - "name": "x", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "min", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "max", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1389 - }, - "mju_sign": { - "name": "mju_sign", - "return_type": "mjtNum", - "params": [ - { - "name": "x", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1392 - }, - "mju_round": { - "name": "mju_round", - "return_type": "int", - "params": [ - { - "name": "x", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1395 - }, - "mju_type2Str": { - "name": "mju_type2Str", - "return_type": "const char *", - "params": [ - { - "name": "type", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1398 - }, - "mju_str2Type": { - "name": "mju_str2Type", - "return_type": "int", - "params": [ - { - "name": "str", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1401 - }, - "mju_writeNumBytes": { - "name": "mju_writeNumBytes", - "return_type": "const char *", - "params": [ - { - "name": "nbytes", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1404 - }, - "mju_warningText": { - "name": "mju_warningText", - "return_type": "const char *", - "params": [ - { - "name": "warning", - "c_type": "int", - "array_dim": null - }, - { - "name": "info", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1407 - }, - "mju_isBad": { - "name": "mju_isBad", - "return_type": "int", - "params": [ - { - "name": "x", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1410 - }, - "mju_isZero": { - "name": "mju_isZero", - "return_type": "int", - "params": [ - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1413 - }, - "mju_standardNormal": { - "name": "mju_standardNormal", - "return_type": "mjtNum", - "params": [ - { - "name": "num2", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1416 - }, - "mju_f2n": { - "name": "mju_f2n", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const float *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1419 - }, - "mju_n2f": { - "name": "mju_n2f", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "float *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1422 - }, - "mju_d2n": { - "name": "mju_d2n", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const double *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1425 - }, - "mju_n2d": { - "name": "mju_n2d", - "return_type": "void", - "params": [ - { - "name": "res", - "c_type": "double *", - "array_dim": null - }, - { - "name": "vec", - "c_type": "const mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1428 - }, - "mju_insertionSort": { - "name": "mju_insertionSort", - "return_type": "void", - "params": [ - { - "name": "list", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1431 - }, - "mju_insertionSortInt": { - "name": "mju_insertionSortInt", - "return_type": "void", - "params": [ - { - "name": "list", - "c_type": "int *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1434 - }, - "mju_Halton": { - "name": "mju_Halton", - "return_type": "mjtNum", - "params": [ - { - "name": "index", - "c_type": "int", - "array_dim": null - }, - { - "name": "base", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1437 - }, - "mju_strncpy": { - "name": "mju_strncpy", - "return_type": "char *", - "params": [ - { - "name": "dst", - "c_type": "char *", - "array_dim": null - }, - { - "name": "src", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "n", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1440 - }, - "mju_sigmoid": { - "name": "mju_sigmoid", - "return_type": "mjtNum", - "params": [ - { - "name": "x", - "c_type": "mjtNum", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1443 - }, - "mjc_getSDF": { - "name": "mjc_getSDF", - "return_type": "const mjpPlugin *", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "id", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1449 - }, - "mjc_distance": { - "name": "mjc_distance", - "return_type": "mjtNum", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "s", - "c_type": "const mjSDF *", - "array_dim": null - }, - { - "name": "x", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1452 - }, - "mjc_gradient": { - "name": "mjc_gradient", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "const mjData *", - "array_dim": null - }, - { - "name": "s", - "c_type": "const mjSDF *", - "array_dim": null - }, - { - "name": "gradient", - "c_type": "mjtNum [3]", - "array_dim": 3 - }, - { - "name": "x", - "c_type": "const mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1455 - }, - "mjd_transitionFD": { - "name": "mjd_transitionFD", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "eps", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "flg_centered", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "A", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "B", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "C", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "D", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1470 - }, - "mjd_inverseFD": { - "name": "mjd_inverseFD", - "return_type": "void", - "params": [ - { - "name": "m", - "c_type": "const mjModel *", - "array_dim": null - }, - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "eps", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "flg_actuation", - "c_type": "mjtBool", - "array_dim": null - }, - { - "name": "DfDq", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "DfDv", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "DfDa", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "DsDq", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "DsDv", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "DsDa", - "c_type": "mjtNum *", - "array_dim": null - }, - { - "name": "DmDq", - "c_type": "mjtNum *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1489 - }, - "mjd_subQuat": { - "name": "mjd_subQuat", - "return_type": "void", - "params": [ - { - "name": "qa", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "qb", - "c_type": "const mjtNum [4]", - "array_dim": 4 - }, - { - "name": "Da", - "c_type": "mjtNum [9]", - "array_dim": 9 - }, - { - "name": "Db", - "c_type": "mjtNum [9]", - "array_dim": 9 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1496 - }, - "mjd_quatIntegrate": { - "name": "mjd_quatIntegrate", - "return_type": "void", - "params": [ - { - "name": "vel", - "c_type": "const mjtNum [3]", - "array_dim": 3 - }, - { - "name": "scale", - "c_type": "mjtNum", - "array_dim": null - }, - { - "name": "Dquat", - "c_type": "mjtNum [9]", - "array_dim": 9 - }, - { - "name": "Dvel", - "c_type": "mjtNum [9]", - "array_dim": 9 - }, - { - "name": "Dscale", - "c_type": "mjtNum [3]", - "array_dim": 3 - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1500 - }, - "mjp_defaultPlugin": { - "name": "mjp_defaultPlugin", - "return_type": "void", - "params": [ - { - "name": "plugin", - "c_type": "mjpPlugin *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1507 - }, - "mjp_registerPlugin": { - "name": "mjp_registerPlugin", - "return_type": "int", - "params": [ - { - "name": "plugin", - "c_type": "const mjpPlugin *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1515 - }, - "mjp_pluginCount": { - "name": "mjp_pluginCount", - "return_type": "int", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1518 - }, - "mjp_getPlugin": { - "name": "mjp_getPlugin", - "return_type": "const mjpPlugin *", - "params": [ - { - "name": "name", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "slot", - "c_type": "int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1521 - }, - "mjp_getPluginAtSlot": { - "name": "mjp_getPluginAtSlot", - "return_type": "const mjpPlugin *", - "params": [ - { - "name": "slot", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1524 - }, - "mjp_defaultResourceProvider": { - "name": "mjp_defaultResourceProvider", - "return_type": "void", - "params": [ - { - "name": "provider", - "c_type": "mjpResourceProvider *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1527 - }, - "mjp_registerResourceProvider": { - "name": "mjp_registerResourceProvider", - "return_type": "int", - "params": [ - { - "name": "provider", - "c_type": "const mjpResourceProvider *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1532 - }, - "mjp_resourceProviderCount": { - "name": "mjp_resourceProviderCount", - "return_type": "int", - "params": [], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1535 - }, - "mjp_getResourceProvider": { - "name": "mjp_getResourceProvider", - "return_type": "const mjpResourceProvider *", - "params": [ - { - "name": "resource_name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1539 - }, - "mjp_getResourceProviderAtSlot": { - "name": "mjp_getResourceProviderAtSlot", - "return_type": "const mjpResourceProvider *", - "params": [ - { - "name": "slot", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1543 - }, - "mjp_registerDecoder": { - "name": "mjp_registerDecoder", - "return_type": "void", - "params": [ - { - "name": "decoder", - "c_type": "const mjpDecoder *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1548 - }, - "mjp_defaultDecoder": { - "name": "mjp_defaultDecoder", - "return_type": "void", - "params": [ - { - "name": "decoder", - "c_type": "mjpDecoder *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1551 - }, - "mjp_findDecoder": { - "name": "mjp_findDecoder", - "return_type": "const mjpDecoder *", - "params": [ - { - "name": "resource", - "c_type": "const mjResource *", - "array_dim": null - }, - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1555 - }, - "mjp_registerEncoder": { - "name": "mjp_registerEncoder", - "return_type": "void", - "params": [ - { - "name": "encoder", - "c_type": "const mjpEncoder *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1560 - }, - "mjp_defaultEncoder": { - "name": "mjp_defaultEncoder", - "return_type": "void", - "params": [ - { - "name": "encoder", - "c_type": "mjpEncoder *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1563 - }, - "mjp_findEncoder": { - "name": "mjp_findEncoder", - "return_type": "const mjpEncoder *", - "params": [ - { - "name": "filename", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1567 - }, - "mju_openResource": { - "name": "mju_openResource", - "return_type": "mjResource *", - "params": [ - { - "name": "dir", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - }, - { - "name": "error", - "c_type": "char *", - "array_dim": null - }, - { - "name": "nerror", - "c_type": "size_t", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1576 - }, - "mju_closeResource": { - "name": "mju_closeResource", - "return_type": "void", - "params": [ - { - "name": "resource", - "c_type": "mjResource *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1580 - }, - "mju_readResource": { - "name": "mju_readResource", - "return_type": "int", - "params": [ - { - "name": "resource", - "c_type": "mjResource *", - "array_dim": null - }, - { - "name": "buffer", - "c_type": "const void **", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1584 - }, - "mju_getResourceDir": { - "name": "mju_getResourceDir", - "return_type": "void", - "params": [ - { - "name": "resource", - "c_type": "mjResource *", - "array_dim": null - }, - { - "name": "dir", - "c_type": "const char **", - "array_dim": null - }, - { - "name": "ndir", - "c_type": "int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1587 - }, - "mju_isModifiedResource": { - "name": "mju_isModifiedResource", - "return_type": "int", - "params": [ - { - "name": "resource", - "c_type": "const mjResource *", - "array_dim": null - }, - { - "name": "timestamp", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1591 - }, - "mju_decodeResource": { - "name": "mju_decodeResource", - "return_type": "mjSpec *", - "params": [ - { - "name": "resource", - "c_type": "mjResource *", - "array_dim": null - }, - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1596 - }, - "mju_threadpool": { - "name": "mju_threadpool", - "return_type": "void", - "params": [ - { - "name": "d", - "c_type": "mjData *", - "array_dim": null - }, - { - "name": "nthread", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1603 - }, - "mjs_attach": { - "name": "mjs_attach", - "return_type": "mjsElement *", - "params": [ - { - "name": "parent", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "child", - "c_type": "const mjsElement *", - "array_dim": null - }, - { - "name": "prefix", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "suffix", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1609 - }, - "mjs_addBody": { - "name": "mjs_addBody", - "return_type": "mjsBody *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1617 - }, - "mjs_addSite": { - "name": "mjs_addSite", - "return_type": "mjsSite *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1621 - }, - "mjs_addJoint": { - "name": "mjs_addJoint", - "return_type": "mjsJoint *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1625 - }, - "mjs_addFreeJoint": { - "name": "mjs_addFreeJoint", - "return_type": "mjsJoint *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1628 - }, - "mjs_addGeom": { - "name": "mjs_addGeom", - "return_type": "mjsGeom *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1632 - }, - "mjs_addCamera": { - "name": "mjs_addCamera", - "return_type": "mjsCamera *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1636 - }, - "mjs_addLight": { - "name": "mjs_addLight", - "return_type": "mjsLight *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1640 - }, - "mjs_addFrame": { - "name": "mjs_addFrame", - "return_type": "mjsFrame *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "parentframe", - "c_type": "mjsFrame *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1643 - }, - "mjs_delete": { - "name": "mjs_delete", - "return_type": "int", - "params": [ - { - "name": "spec", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1646 - }, - "mjs_addActuator": { - "name": "mjs_addActuator", - "return_type": "mjsActuator *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1653 - }, - "mjs_addSensor": { - "name": "mjs_addSensor", - "return_type": "mjsSensor *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1656 - }, - "mjs_addFlex": { - "name": "mjs_addFlex", - "return_type": "mjsFlex *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1659 - }, - "mjs_makeFlex": { - "name": "mjs_makeFlex", - "return_type": "mjsFlex *", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "type", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "dim", - "c_type": "int", - "array_dim": null - }, - { - "name": "dof", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "count", - "c_type": "const int [3]", - "array_dim": 3 - }, - { - "name": "cellcount", - "c_type": "const int [3]", - "array_dim": 3 - }, - { - "name": "spacing", - "c_type": "const double [3]", - "array_dim": 3 - }, - { - "name": "scale", - "c_type": "const double [3]", - "array_dim": 3 - }, - { - "name": "radius", - "c_type": "double", - "array_dim": null - }, - { - "name": "mass", - "c_type": "double", - "array_dim": null - }, - { - "name": "inertiabox", - "c_type": "double", - "array_dim": null - }, - { - "name": "equality", - "c_type": "int", - "array_dim": null - }, - { - "name": "rigid", - "c_type": "int", - "array_dim": null - }, - { - "name": "flatskin", - "c_type": "int", - "array_dim": null - }, - { - "name": "elastic2d", - "c_type": "int", - "array_dim": null - }, - { - "name": "pos", - "c_type": "const double [3]", - "array_dim": 3 - }, - { - "name": "quat", - "c_type": "const double [4]", - "array_dim": 4 - }, - { - "name": "origin", - "c_type": "const double [3]", - "array_dim": 3 - }, - { - "name": "file", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "vfs", - "c_type": "const mjVFS *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1663 - }, - "mjs_addPair": { - "name": "mjs_addPair", - "return_type": "mjsPair *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1672 - }, - "mjs_addExclude": { - "name": "mjs_addExclude", - "return_type": "mjsExclude *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1675 - }, - "mjs_addEquality": { - "name": "mjs_addEquality", - "return_type": "mjsEquality *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1679 - }, - "mjs_addTendon": { - "name": "mjs_addTendon", - "return_type": "mjsTendon *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1683 - }, - "mjs_wrapSite": { - "name": "mjs_wrapSite", - "return_type": "mjsWrap *", - "params": [ - { - "name": "tendon", - "c_type": "mjsTendon *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1686 - }, - "mjs_wrapGeom": { - "name": "mjs_wrapGeom", - "return_type": "mjsWrap *", - "params": [ - { - "name": "tendon", - "c_type": "mjsTendon *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "sidesite", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1689 - }, - "mjs_wrapJoint": { - "name": "mjs_wrapJoint", - "return_type": "mjsWrap *", - "params": [ - { - "name": "tendon", - "c_type": "mjsTendon *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "coef", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1692 - }, - "mjs_wrapPulley": { - "name": "mjs_wrapPulley", - "return_type": "mjsWrap *", - "params": [ - { - "name": "tendon", - "c_type": "mjsTendon *", - "array_dim": null - }, - { - "name": "divisor", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1695 - }, - "mjs_addNumeric": { - "name": "mjs_addNumeric", - "return_type": "mjsNumeric *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1698 - }, - "mjs_addText": { - "name": "mjs_addText", - "return_type": "mjsText *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1701 - }, - "mjs_addTuple": { - "name": "mjs_addTuple", - "return_type": "mjsTuple *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1704 - }, - "mjs_addKey": { - "name": "mjs_addKey", - "return_type": "mjsKey *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1707 - }, - "mjs_addPlugin": { - "name": "mjs_addPlugin", - "return_type": "mjsPlugin *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1710 - }, - "mjs_addDefault": { - "name": "mjs_addDefault", - "return_type": "mjsDefault *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "classname", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "parent", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1714 - }, - "mjs_setToMotor": { - "name": "mjs_setToMotor", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1720 - }, - "mjs_setToPosition": { - "name": "mjs_setToPosition", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "kp", - "c_type": "double", - "array_dim": null - }, - { - "name": "kv", - "c_type": "double [1]", - "array_dim": 1 - }, - { - "name": "dampratio", - "c_type": "double [1]", - "array_dim": 1 - }, - { - "name": "timeconst", - "c_type": "double [1]", - "array_dim": 1 - }, - { - "name": "inheritrange", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1723 - }, - "mjs_setToIntVelocity": { - "name": "mjs_setToIntVelocity", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "kp", - "c_type": "double", - "array_dim": null - }, - { - "name": "kv", - "c_type": "double [1]", - "array_dim": 1 - }, - { - "name": "dampratio", - "c_type": "double [1]", - "array_dim": 1 - }, - { - "name": "timeconst", - "c_type": "double [1]", - "array_dim": 1 - }, - { - "name": "inheritrange", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1727 - }, - "mjs_setToVelocity": { - "name": "mjs_setToVelocity", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "kv", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1731 - }, - "mjs_setToDamper": { - "name": "mjs_setToDamper", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "kv", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1734 - }, - "mjs_setToCylinder": { - "name": "mjs_setToCylinder", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "timeconst", - "c_type": "double", - "array_dim": null - }, - { - "name": "bias", - "c_type": "double", - "array_dim": null - }, - { - "name": "area", - "c_type": "double", - "array_dim": null - }, - { - "name": "diameter", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1737 - }, - "mjs_setToMuscle": { - "name": "mjs_setToMuscle", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "timeconst", - "c_type": "double [2]", - "array_dim": 2 - }, - { - "name": "tausmooth", - "c_type": "double", - "array_dim": null - }, - { - "name": "range", - "c_type": "double [2]", - "array_dim": 2 - }, - { - "name": "force", - "c_type": "double", - "array_dim": null - }, - { - "name": "scale", - "c_type": "double", - "array_dim": null - }, - { - "name": "lmin", - "c_type": "double", - "array_dim": null - }, - { - "name": "lmax", - "c_type": "double", - "array_dim": null - }, - { - "name": "vmax", - "c_type": "double", - "array_dim": null - }, - { - "name": "fpmax", - "c_type": "double", - "array_dim": null - }, - { - "name": "fvmax", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1741 - }, - "mjs_setToAdhesion": { - "name": "mjs_setToAdhesion", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "gain", - "c_type": "double", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1746 - }, - "mjs_setToDCMotor": { - "name": "mjs_setToDCMotor", - "return_type": "const char *", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - }, - { - "name": "motorconst", - "c_type": "double [2]", - "array_dim": 2 - }, - { - "name": "resistance", - "c_type": "double", - "array_dim": null - }, - { - "name": "nominal", - "c_type": "double [3]", - "array_dim": 3 - }, - { - "name": "saturation", - "c_type": "double [3]", - "array_dim": 3 - }, - { - "name": "inductance", - "c_type": "double [2]", - "array_dim": 2 - }, - { - "name": "cogging", - "c_type": "double [3]", - "array_dim": 3 - }, - { - "name": "controller", - "c_type": "double [6]", - "array_dim": 6 - }, - { - "name": "thermal", - "c_type": "double [6]", - "array_dim": 6 - }, - { - "name": "lugre", - "c_type": "double [5]", - "array_dim": 5 - }, - { - "name": "input_mode", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1750 - }, - "mjs_addMesh": { - "name": "mjs_addMesh", - "return_type": "mjsMesh *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1760 - }, - "mjs_addHField": { - "name": "mjs_addHField", - "return_type": "mjsHField *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1763 - }, - "mjs_addSkin": { - "name": "mjs_addSkin", - "return_type": "mjsSkin *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1766 - }, - "mjs_addTexture": { - "name": "mjs_addTexture", - "return_type": "mjsTexture *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1769 - }, - "mjs_addMaterial": { - "name": "mjs_addMaterial", - "return_type": "mjsMaterial *", - "params": [ - { - "name": "s", - "c_type": "mjSpec *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1773 - }, - "mjs_makeMesh": { - "name": "mjs_makeMesh", - "return_type": "int", - "params": [ - { - "name": "mesh", - "c_type": "mjsMesh *", - "array_dim": null - }, - { - "name": "builtin", - "c_type": "mjtMeshBuiltin", - "array_dim": null - }, - { - "name": "params", - "c_type": "double *", - "array_dim": null - }, - { - "name": "nparams", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1776 - }, - "mjs_getSpec": { - "name": "mjs_getSpec", - "return_type": "mjSpec *", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1781 - }, - "mjs_getOriginSpec": { - "name": "mjs_getOriginSpec", - "return_type": "mjSpec *", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1785 - }, - "mjs_getCompiler": { - "name": "mjs_getCompiler", - "return_type": "mjsCompiler *", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1788 - }, - "mjs_findSpec": { - "name": "mjs_findSpec", - "return_type": "mjSpec *", - "params": [ - { - "name": "spec", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1791 - }, - "mjs_findBody": { - "name": "mjs_findBody", - "return_type": "mjsBody *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1794 - }, - "mjs_findElement": { - "name": "mjs_findElement", - "return_type": "mjsElement *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "type", - "c_type": "mjtObj", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1797 - }, - "mjs_findChild": { - "name": "mjs_findChild", - "return_type": "mjsBody *", - "params": [ - { - "name": "body", - "c_type": "const mjsBody *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1800 - }, - "mjs_getParent": { - "name": "mjs_getParent", - "return_type": "mjsBody *", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1803 - }, - "mjs_getFrame": { - "name": "mjs_getFrame", - "return_type": "mjsFrame *", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1806 - }, - "mjs_findFrame": { - "name": "mjs_findFrame", - "return_type": "mjsFrame *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1809 - }, - "mjs_getDefault": { - "name": "mjs_getDefault", - "return_type": "mjsDefault *", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1812 - }, - "mjs_findDefault": { - "name": "mjs_findDefault", - "return_type": "mjsDefault *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "classname", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1815 - }, - "mjs_getSpecDefault": { - "name": "mjs_getSpecDefault", - "return_type": "mjsDefault *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1818 - }, - "mjs_getId": { - "name": "mjs_getId", - "return_type": "int", - "params": [ - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1821 - }, - "mjs_firstChild": { - "name": "mjs_firstChild", - "return_type": "mjsElement *", - "params": [ - { - "name": "body", - "c_type": "const mjsBody *", - "array_dim": null - }, - { - "name": "type", - "c_type": "mjtObj", - "array_dim": null - }, - { - "name": "recurse", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1824 - }, - "mjs_nextChild": { - "name": "mjs_nextChild", - "return_type": "mjsElement *", - "params": [ - { - "name": "body", - "c_type": "const mjsBody *", - "array_dim": null - }, - { - "name": "child", - "c_type": "const mjsElement *", - "array_dim": null - }, - { - "name": "recurse", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1828 - }, - "mjs_firstElement": { - "name": "mjs_firstElement", - "return_type": "mjsElement *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "type", - "c_type": "mjtObj", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1831 - }, - "mjs_nextElement": { - "name": "mjs_nextElement", - "return_type": "mjsElement *", - "params": [ - { - "name": "s", - "c_type": "const mjSpec *", - "array_dim": null - }, - { - "name": "element", - "c_type": "const mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1834 - }, - "mjs_getWrapTarget": { - "name": "mjs_getWrapTarget", - "return_type": "mjsElement *", - "params": [ - { - "name": "wrap", - "c_type": "const mjsWrap *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1837 - }, - "mjs_getWrapSideSite": { - "name": "mjs_getWrapSideSite", - "return_type": "mjsSite *", - "params": [ - { - "name": "wrap", - "c_type": "const mjsWrap *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1840 - }, - "mjs_getWrapDivisor": { - "name": "mjs_getWrapDivisor", - "return_type": "double", - "params": [ - { - "name": "wrap", - "c_type": "const mjsWrap *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1843 - }, - "mjs_getWrapCoef": { - "name": "mjs_getWrapCoef", - "return_type": "double", - "params": [ - { - "name": "wrap", - "c_type": "const mjsWrap *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1846 - }, - "mjs_setName": { - "name": "mjs_setName", - "return_type": "int", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "name", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1851 - }, - "mjs_setBuffer": { - "name": "mjs_setBuffer", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjByteVec *", - "array_dim": null - }, - { - "name": "array", - "c_type": "const void *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1854 - }, - "mjs_setString": { - "name": "mjs_setString", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjString *", - "array_dim": null - }, - { - "name": "text", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1857 - }, - "mjs_setStringVec": { - "name": "mjs_setStringVec", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjStringVec *", - "array_dim": null - }, - { - "name": "text", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1860 - }, - "mjs_setInStringVec": { - "name": "mjs_setInStringVec", - "return_type": "mjtBool", - "params": [ - { - "name": "dest", - "c_type": "mjStringVec *", - "array_dim": null - }, - { - "name": "i", - "c_type": "int", - "array_dim": null - }, - { - "name": "text", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1863 - }, - "mjs_appendString": { - "name": "mjs_appendString", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjStringVec *", - "array_dim": null - }, - { - "name": "text", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1866 - }, - "mjs_setInt": { - "name": "mjs_setInt", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjIntVec *", - "array_dim": null - }, - { - "name": "array", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1869 - }, - "mjs_appendIntVec": { - "name": "mjs_appendIntVec", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjIntVecVec *", - "array_dim": null - }, - { - "name": "array", - "c_type": "const int *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1872 - }, - "mjs_setFloat": { - "name": "mjs_setFloat", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjFloatVec *", - "array_dim": null - }, - { - "name": "array", - "c_type": "const float *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1875 - }, - "mjs_appendFloatVec": { - "name": "mjs_appendFloatVec", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjFloatVecVec *", - "array_dim": null - }, - { - "name": "array", - "c_type": "const float *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1878 - }, - "mjs_setDouble": { - "name": "mjs_setDouble", - "return_type": "void", - "params": [ - { - "name": "dest", - "c_type": "mjDoubleVec *", - "array_dim": null - }, - { - "name": "array", - "c_type": "const double *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1881 - }, - "mjs_setPluginAttributes": { - "name": "mjs_setPluginAttributes", - "return_type": "void", - "params": [ - { - "name": "plugin", - "c_type": "mjsPlugin *", - "array_dim": null - }, - { - "name": "attributes", - "c_type": "void *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1884 - }, - "mjs_getName": { - "name": "mjs_getName", - "return_type": "mjString *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1890 - }, - "mjs_getString": { - "name": "mjs_getString", - "return_type": "const char *", - "params": [ - { - "name": "source", - "c_type": "const mjString *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1893 - }, - "mjs_getDouble": { - "name": "mjs_getDouble", - "return_type": "const double *", - "params": [ - { - "name": "source", - "c_type": "const mjDoubleVec *", - "array_dim": null - }, - { - "name": "size", - "c_type": "int *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1897 - }, - "mjs_getWrapNum": { - "name": "mjs_getWrapNum", - "return_type": "int", - "params": [ - { - "name": "tendonspec", - "c_type": "const mjsTendon *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1900 - }, - "mjs_getWrap": { - "name": "mjs_getWrap", - "return_type": "mjsWrap *", - "params": [ - { - "name": "tendonspec", - "c_type": "const mjsTendon *", - "array_dim": null - }, - { - "name": "i", - "c_type": "int", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1903 - }, - "mjs_getPluginAttributes": { - "name": "mjs_getPluginAttributes", - "return_type": "const void *", - "params": [ - { - "name": "plugin", - "c_type": "const mjsPlugin *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1906 - }, - "mjs_setDefault": { - "name": "mjs_setDefault", - "return_type": "void", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "def", - "c_type": "const mjsDefault *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1912 - }, - "mjs_setFrame": { - "name": "mjs_setFrame", - "return_type": "int", - "params": [ - { - "name": "dest", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "frame", - "c_type": "mjsFrame *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1915 - }, - "mjs_resolveOrientation": { - "name": "mjs_resolveOrientation", - "return_type": "const char *", - "params": [ - { - "name": "quat", - "c_type": "double [4]", - "array_dim": 4 - }, - { - "name": "degree", - "c_type": "mjtByte", - "array_dim": null - }, - { - "name": "sequence", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "orientation", - "c_type": "const mjsOrientation *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1918 - }, - "mjs_bodyToFrame": { - "name": "mjs_bodyToFrame", - "return_type": "mjsFrame *", - "params": [ - { - "name": "body", - "c_type": "mjsBody **", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1922 - }, - "mjs_setUserValue": { - "name": "mjs_setUserValue", - "return_type": "void", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "key", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "data", - "c_type": "const void *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1925 - }, - "mjs_setUserValueWithCleanup": { - "name": "mjs_setUserValueWithCleanup", - "return_type": "void", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "key", - "c_type": "const char *", - "array_dim": null - }, - { - "name": "data", - "c_type": "const void *", - "array_dim": null - }, - { - "name": "cleanup", - "c_type": "void (*)(const void *)", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1930 - }, - "mjs_getUserValue": { - "name": "mjs_getUserValue", - "return_type": "const void *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "key", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1935 - }, - "mjs_deleteUserValue": { - "name": "mjs_deleteUserValue", - "return_type": "void", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - }, - { - "name": "key", - "c_type": "const char *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1938 - }, - "mjs_sensorDim": { - "name": "mjs_sensorDim", - "return_type": "int", - "params": [ - { - "name": "sensor", - "c_type": "const mjsSensor *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1941 - }, - "mjs_defaultSpec": { - "name": "mjs_defaultSpec", - "return_type": "void", - "params": [ - { - "name": "spec", - "c_type": "mjSpec *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1946 - }, - "mjs_defaultOrientation": { - "name": "mjs_defaultOrientation", - "return_type": "void", - "params": [ - { - "name": "orient", - "c_type": "mjsOrientation *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1949 - }, - "mjs_defaultBody": { - "name": "mjs_defaultBody", - "return_type": "void", - "params": [ - { - "name": "body", - "c_type": "mjsBody *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1952 - }, - "mjs_defaultFrame": { - "name": "mjs_defaultFrame", - "return_type": "void", - "params": [ - { - "name": "frame", - "c_type": "mjsFrame *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1955 - }, - "mjs_defaultJoint": { - "name": "mjs_defaultJoint", - "return_type": "void", - "params": [ - { - "name": "joint", - "c_type": "mjsJoint *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1958 - }, - "mjs_defaultGeom": { - "name": "mjs_defaultGeom", - "return_type": "void", - "params": [ - { - "name": "geom", - "c_type": "mjsGeom *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1961 - }, - "mjs_defaultSite": { - "name": "mjs_defaultSite", - "return_type": "void", - "params": [ - { - "name": "site", - "c_type": "mjsSite *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1964 - }, - "mjs_defaultCamera": { - "name": "mjs_defaultCamera", - "return_type": "void", - "params": [ - { - "name": "camera", - "c_type": "mjsCamera *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1967 - }, - "mjs_defaultLight": { - "name": "mjs_defaultLight", - "return_type": "void", - "params": [ - { - "name": "light", - "c_type": "mjsLight *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1970 - }, - "mjs_defaultFlex": { - "name": "mjs_defaultFlex", - "return_type": "void", - "params": [ - { - "name": "flex", - "c_type": "mjsFlex *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1973 - }, - "mjs_defaultMesh": { - "name": "mjs_defaultMesh", - "return_type": "void", - "params": [ - { - "name": "mesh", - "c_type": "mjsMesh *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1976 - }, - "mjs_defaultHField": { - "name": "mjs_defaultHField", - "return_type": "void", - "params": [ - { - "name": "hfield", - "c_type": "mjsHField *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1979 - }, - "mjs_defaultSkin": { - "name": "mjs_defaultSkin", - "return_type": "void", - "params": [ - { - "name": "skin", - "c_type": "mjsSkin *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1982 - }, - "mjs_defaultTexture": { - "name": "mjs_defaultTexture", - "return_type": "void", - "params": [ - { - "name": "texture", - "c_type": "mjsTexture *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1985 - }, - "mjs_defaultMaterial": { - "name": "mjs_defaultMaterial", - "return_type": "void", - "params": [ - { - "name": "material", - "c_type": "mjsMaterial *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1988 - }, - "mjs_defaultPair": { - "name": "mjs_defaultPair", - "return_type": "void", - "params": [ - { - "name": "pair", - "c_type": "mjsPair *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1991 - }, - "mjs_defaultEquality": { - "name": "mjs_defaultEquality", - "return_type": "void", - "params": [ - { - "name": "equality", - "c_type": "mjsEquality *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1994 - }, - "mjs_defaultTendon": { - "name": "mjs_defaultTendon", - "return_type": "void", - "params": [ - { - "name": "tendon", - "c_type": "mjsTendon *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1997 - }, - "mjs_defaultActuator": { - "name": "mjs_defaultActuator", - "return_type": "void", - "params": [ - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2000 - }, - "mjs_defaultSensor": { - "name": "mjs_defaultSensor", - "return_type": "void", - "params": [ - { - "name": "sensor", - "c_type": "mjsSensor *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2003 - }, - "mjs_defaultNumeric": { - "name": "mjs_defaultNumeric", - "return_type": "void", - "params": [ - { - "name": "numeric", - "c_type": "mjsNumeric *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2006 - }, - "mjs_defaultText": { - "name": "mjs_defaultText", - "return_type": "void", - "params": [ - { - "name": "text", - "c_type": "mjsText *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2009 - }, - "mjs_defaultTuple": { - "name": "mjs_defaultTuple", - "return_type": "void", - "params": [ - { - "name": "tuple", - "c_type": "mjsTuple *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2012 - }, - "mjs_defaultKey": { - "name": "mjs_defaultKey", - "return_type": "void", - "params": [ - { - "name": "key", - "c_type": "mjsKey *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2015 - }, - "mjs_defaultPlugin": { - "name": "mjs_defaultPlugin", - "return_type": "void", - "params": [ - { - "name": "plugin", - "c_type": "mjsPlugin *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2018 - }, - "mjs_asBody": { - "name": "mjs_asBody", - "return_type": "mjsBody *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2024 - }, - "mjs_asGeom": { - "name": "mjs_asGeom", - "return_type": "mjsGeom *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2027 - }, - "mjs_asJoint": { - "name": "mjs_asJoint", - "return_type": "mjsJoint *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2030 - }, - "mjs_asSite": { - "name": "mjs_asSite", - "return_type": "mjsSite *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2033 - }, - "mjs_asCamera": { - "name": "mjs_asCamera", - "return_type": "mjsCamera *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2036 - }, - "mjs_asLight": { - "name": "mjs_asLight", - "return_type": "mjsLight *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2039 - }, - "mjs_asFrame": { - "name": "mjs_asFrame", - "return_type": "mjsFrame *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2042 - }, - "mjs_asActuator": { - "name": "mjs_asActuator", - "return_type": "mjsActuator *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2045 - }, - "mjs_asSensor": { - "name": "mjs_asSensor", - "return_type": "mjsSensor *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2048 - }, - "mjs_asFlex": { - "name": "mjs_asFlex", - "return_type": "mjsFlex *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2051 - }, - "mjs_asPair": { - "name": "mjs_asPair", - "return_type": "mjsPair *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2054 - }, - "mjs_asEquality": { - "name": "mjs_asEquality", - "return_type": "mjsEquality *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2057 - }, - "mjs_asExclude": { - "name": "mjs_asExclude", - "return_type": "mjsExclude *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2060 - }, - "mjs_asTendon": { - "name": "mjs_asTendon", - "return_type": "mjsTendon *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2063 - }, - "mjs_asNumeric": { - "name": "mjs_asNumeric", - "return_type": "mjsNumeric *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2066 - }, - "mjs_asText": { - "name": "mjs_asText", - "return_type": "mjsText *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2069 - }, - "mjs_asTuple": { - "name": "mjs_asTuple", - "return_type": "mjsTuple *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2072 - }, - "mjs_asKey": { - "name": "mjs_asKey", - "return_type": "mjsKey *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2075 - }, - "mjs_asMesh": { - "name": "mjs_asMesh", - "return_type": "mjsMesh *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2078 - }, - "mjs_asHField": { - "name": "mjs_asHField", - "return_type": "mjsHField *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2081 - }, - "mjs_asSkin": { - "name": "mjs_asSkin", - "return_type": "mjsSkin *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2084 - }, - "mjs_asTexture": { - "name": "mjs_asTexture", - "return_type": "mjsTexture *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2087 - }, - "mjs_asMaterial": { - "name": "mjs_asMaterial", - "return_type": "mjsMaterial *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2090 - }, - "mjs_asPlugin": { - "name": "mjs_asPlugin", - "return_type": "mjsPlugin *", - "params": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 2093 - } - }, - "enums": { - "mjtDisableBit": { - "name": "mjtDisableBit", - "members": { - "mjDSBL_CONSTRAINT": 1, - "mjDSBL_EQUALITY": 2, - "mjDSBL_FRICTIONLOSS": 4, - "mjDSBL_LIMIT": 8, - "mjDSBL_CONTACT": 16, - "mjDSBL_SPRING": 32, - "mjDSBL_DAMPER": 64, - "mjDSBL_GRAVITY": 128, - "mjDSBL_CLAMPCTRL": 256, - "mjDSBL_WARMSTART": 512, - "mjDSBL_FILTERPARENT": 1024, - "mjDSBL_ACTUATION": 2048, - "mjDSBL_REFSAFE": 4096, - "mjDSBL_SENSOR": 8192, - "mjDSBL_MIDPHASE": 16384, - "mjDSBL_EULERDAMP": 32768, - "mjDSBL_AUTORESET": 65536, - "mjDSBL_NATIVECCD": 131072, - "mjDSBL_ISLAND": 262144, - "mjDSBL_MULTICCD": 524288, - "mjNDISABLE": 20 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 53 - }, - "mjtEnableBit": { - "name": "mjtEnableBit", - "members": { - "mjENBL_OVERRIDE": 1, - "mjENBL_ENERGY": 2, - "mjENBL_FWDINV": 4, - "mjENBL_INVDISCRETE": 8, - "mjENBL_SLEEP": 16, - "mjENBL_DIAGEXACT": 32, - "mjNENABLE": 6 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 79 - }, - "mjtJoint": { - "name": "mjtJoint", - "members": { - "mjJNT_FREE": 0, - "mjJNT_BALL": 1, - "mjJNT_SLIDE": 2, - "mjJNT_HINGE": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 91 - }, - "mjtGeom": { - "name": "mjtGeom", - "members": { - "mjGEOM_PLANE": 0, - "mjGEOM_HFIELD": 1, - "mjGEOM_SPHERE": 2, - "mjGEOM_CAPSULE": 3, - "mjGEOM_ELLIPSOID": 4, - "mjGEOM_CYLINDER": 5, - "mjGEOM_BOX": 6, - "mjGEOM_MESH": 7, - "mjGEOM_SDF": 8, - "mjNGEOMTYPES": 9, - "mjGEOM_ARROW": 100, - "mjGEOM_ARROW1": 101, - "mjGEOM_ARROW2": 102, - "mjGEOM_LINE": 103, - "mjGEOM_LINEBOX": 104, - "mjGEOM_FLEX": 105, - "mjGEOM_SKIN": 106, - "mjGEOM_LABEL": 107, - "mjGEOM_TRIANGLE": 108, - "mjGEOM_NONE": 1001 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 99 - }, - "mjtProjection": { - "name": "mjtProjection", - "members": { - "mjPROJ_PERSPECTIVE": 0, - "mjPROJ_ORTHOGRAPHIC": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 128 - }, - "mjtCamLight": { - "name": "mjtCamLight", - "members": { - "mjCAMLIGHT_FIXED": 0, - "mjCAMLIGHT_TRACK": 1, - "mjCAMLIGHT_TRACKCOM": 2, - "mjCAMLIGHT_TARGETBODY": 3, - "mjCAMLIGHT_TARGETBODYCOM": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 134 - }, - "mjtLightType": { - "name": "mjtLightType", - "members": { - "mjLIGHT_SPOT": 0, - "mjLIGHT_DIRECTIONAL": 1, - "mjLIGHT_POINT": 2, - "mjLIGHT_IMAGE": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 143 - }, - "mjtTexture": { - "name": "mjtTexture", - "members": { - "mjTEXTURE_2D": 0, - "mjTEXTURE_CUBE": 1, - "mjTEXTURE_SKYBOX": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 151 - }, - "mjtTextureRole": { - "name": "mjtTextureRole", - "members": { - "mjTEXROLE_USER": 0, - "mjTEXROLE_RGB": 1, - "mjTEXROLE_OCCLUSION": 2, - "mjTEXROLE_ROUGHNESS": 3, - "mjTEXROLE_METALLIC": 4, - "mjTEXROLE_NORMAL": 5, - "mjTEXROLE_OPACITY": 6, - "mjTEXROLE_EMISSIVE": 7, - "mjTEXROLE_RGBA": 8, - "mjTEXROLE_ORM": 9, - "mjNTEXROLE": 10 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 158 - }, - "mjtColorSpace": { - "name": "mjtColorSpace", - "members": { - "mjCOLORSPACE_AUTO": 0, - "mjCOLORSPACE_LINEAR": 1, - "mjCOLORSPACE_SRGB": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 173 - }, - "mjtIntegrator": { - "name": "mjtIntegrator", - "members": { - "mjINT_EULER": 0, - "mjINT_RK4": 1, - "mjINT_IMPLICIT": 2, - "mjINT_IMPLICITFAST": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 180 - }, - "mjtCone": { - "name": "mjtCone", - "members": { - "mjCONE_PYRAMIDAL": 0, - "mjCONE_ELLIPTIC": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 188 - }, - "mjtJacobian": { - "name": "mjtJacobian", - "members": { - "mjJAC_DENSE": 0, - "mjJAC_SPARSE": 1, - "mjJAC_AUTO": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 194 - }, - "mjtSolver": { - "name": "mjtSolver", - "members": { - "mjSOL_PGS": 0, - "mjSOL_CG": 1, - "mjSOL_NEWTON": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 201 - }, - "mjtEq": { - "name": "mjtEq", - "members": { - "mjEQ_CONNECT": 0, - "mjEQ_WELD": 1, - "mjEQ_JOINT": 2, - "mjEQ_TENDON": 3, - "mjEQ_FLEX": 4, - "mjEQ_FLEXVERT": 5, - "mjEQ_FLEXSTRAIN": 6, - "mjEQ_DISTANCE": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 208 - }, - "mjtWrap": { - "name": "mjtWrap", - "members": { - "mjWRAP_NONE": 0, - "mjWRAP_JOINT": 1, - "mjWRAP_PULLEY": 2, - "mjWRAP_SITE": 3, - "mjWRAP_SPHERE": 4, - "mjWRAP_CYLINDER": 5 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 220 - }, - "mjtTrn": { - "name": "mjtTrn", - "members": { - "mjTRN_JOINT": 0, - "mjTRN_JOINTINPARENT": 1, - "mjTRN_SLIDERCRANK": 2, - "mjTRN_TENDON": 3, - "mjTRN_SITE": 4, - "mjTRN_BODY": 5, - "mjTRN_UNDEFINED": 1000 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 230 - }, - "mjtDyn": { - "name": "mjtDyn", - "members": { - "mjDYN_NONE": 0, - "mjDYN_INTEGRATOR": 1, - "mjDYN_FILTER": 2, - "mjDYN_FILTEREXACT": 3, - "mjDYN_MUSCLE": 4, - "mjDYN_DCMOTOR": 5, - "mjDYN_USER": 6 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 242 - }, - "mjtGain": { - "name": "mjtGain", - "members": { - "mjGAIN_FIXED": 0, - "mjGAIN_AFFINE": 1, - "mjGAIN_MUSCLE": 2, - "mjGAIN_DCMOTOR": 3, - "mjGAIN_USER": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 253 - }, - "mjtBias": { - "name": "mjtBias", - "members": { - "mjBIAS_NONE": 0, - "mjBIAS_AFFINE": 1, - "mjBIAS_MUSCLE": 2, - "mjBIAS_DCMOTOR": 3, - "mjBIAS_USER": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 262 - }, - "mjtObj": { - "name": "mjtObj", - "members": { - "mjOBJ_UNKNOWN": 0, - "mjOBJ_BODY": 1, - "mjOBJ_XBODY": 2, - "mjOBJ_JOINT": 3, - "mjOBJ_DOF": 4, - "mjOBJ_GEOM": 5, - "mjOBJ_SITE": 6, - "mjOBJ_CAMERA": 7, - "mjOBJ_LIGHT": 8, - "mjOBJ_FLEX": 9, - "mjOBJ_MESH": 10, - "mjOBJ_SKIN": 11, - "mjOBJ_HFIELD": 12, - "mjOBJ_TEXTURE": 13, - "mjOBJ_MATERIAL": 14, - "mjOBJ_PAIR": 15, - "mjOBJ_EXCLUDE": 16, - "mjOBJ_EQUALITY": 17, - "mjOBJ_TENDON": 18, - "mjOBJ_ACTUATOR": 19, - "mjOBJ_SENSOR": 20, - "mjOBJ_NUMERIC": 21, - "mjOBJ_TEXT": 22, - "mjOBJ_TUPLE": 23, - "mjOBJ_KEY": 24, - "mjOBJ_PLUGIN": 25, - "mjNOBJECT": 26, - "mjOBJ_FRAME": 100, - "mjOBJ_DEFAULT": 101, - "mjOBJ_MODEL": 102 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 271 - }, - "mjtSensor": { - "name": "mjtSensor", - "members": { - "mjSENS_TOUCH": 0, - "mjSENS_ACCELEROMETER": 1, - "mjSENS_VELOCIMETER": 2, - "mjSENS_GYRO": 3, - "mjSENS_FORCE": 4, - "mjSENS_TORQUE": 5, - "mjSENS_MAGNETOMETER": 6, - "mjSENS_RANGEFINDER": 7, - "mjSENS_CAMPROJECTION": 8, - "mjSENS_JOINTPOS": 9, - "mjSENS_JOINTVEL": 10, - "mjSENS_TENDONPOS": 11, - "mjSENS_TENDONVEL": 12, - "mjSENS_ACTUATORPOS": 13, - "mjSENS_ACTUATORVEL": 14, - "mjSENS_ACTUATORFRC": 15, - "mjSENS_JOINTACTFRC": 16, - "mjSENS_TENDONACTFRC": 17, - "mjSENS_BALLQUAT": 18, - "mjSENS_BALLANGVEL": 19, - "mjSENS_JOINTLIMITPOS": 20, - "mjSENS_JOINTLIMITVEL": 21, - "mjSENS_JOINTLIMITFRC": 22, - "mjSENS_TENDONLIMITPOS": 23, - "mjSENS_TENDONLIMITVEL": 24, - "mjSENS_TENDONLIMITFRC": 25, - "mjSENS_FRAMEPOS": 26, - "mjSENS_FRAMEQUAT": 27, - "mjSENS_FRAMEXAXIS": 28, - "mjSENS_FRAMEYAXIS": 29, - "mjSENS_FRAMEZAXIS": 30, - "mjSENS_FRAMELINVEL": 31, - "mjSENS_FRAMEANGVEL": 32, - "mjSENS_FRAMELINACC": 33, - "mjSENS_FRAMEANGACC": 34, - "mjSENS_SUBTREECOM": 35, - "mjSENS_SUBTREELINVEL": 36, - "mjSENS_SUBTREEANGMOM": 37, - "mjSENS_INSIDESITE": 38, - "mjSENS_GEOMDIST": 39, - "mjSENS_GEOMNORMAL": 40, - "mjSENS_GEOMFROMTO": 41, - "mjSENS_CONTACT": 42, - "mjSENS_E_POTENTIAL": 43, - "mjSENS_E_KINETIC": 44, - "mjSENS_CLOCK": 45, - "mjSENS_TACTILE": 46, - "mjSENS_PLUGIN": 47, - "mjSENS_USER": 48 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 308 - }, - "mjtStage": { - "name": "mjtStage", - "members": { - "mjSTAGE_NONE": 0, - "mjSTAGE_POS": 1, - "mjSTAGE_VEL": 2, - "mjSTAGE_ACC": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 384 - }, - "mjtDataType": { - "name": "mjtDataType", - "members": { - "mjDATATYPE_REAL": 0, - "mjDATATYPE_POSITIVE": 1, - "mjDATATYPE_AXIS": 2, - "mjDATATYPE_QUATERNION": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 392 - }, - "mjtConDataField": { - "name": "mjtConDataField", - "members": { - "mjCONDATA_FOUND": 0, - "mjCONDATA_FORCE": 1, - "mjCONDATA_TORQUE": 2, - "mjCONDATA_DIST": 3, - "mjCONDATA_POS": 4, - "mjCONDATA_NORMAL": 5, - "mjCONDATA_TANGENT": 6, - "mjNCONDATA": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 400 - }, - "mjtRayDataField": { - "name": "mjtRayDataField", - "members": { - "mjRAYDATA_DIST": 0, - "mjRAYDATA_DIR": 1, - "mjRAYDATA_ORIGIN": 2, - "mjRAYDATA_POINT": 3, - "mjRAYDATA_NORMAL": 4, - "mjRAYDATA_DEPTH": 5, - "mjNRAYDATA": 6 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 413 - }, - "mjtCamOutBit": { - "name": "mjtCamOutBit", - "members": { - "mjCAMOUT_RGB": 1, - "mjCAMOUT_DEPTH": 2, - "mjCAMOUT_DIST": 4, - "mjCAMOUT_NORMAL": 8, - "mjCAMOUT_SEG": 16, - "mjNCAMOUT": 5 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 425 - }, - "mjtSameFrame": { - "name": "mjtSameFrame", - "members": { - "mjSAMEFRAME_NONE": 0, - "mjSAMEFRAME_BODY": 1, - "mjSAMEFRAME_INERTIA": 2, - "mjSAMEFRAME_BODYROT": 3, - "mjSAMEFRAME_INERTIAROT": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 436 - }, - "mjtSleepPolicy": { - "name": "mjtSleepPolicy", - "members": { - "mjSLEEP_AUTO": 0, - "mjSLEEP_AUTO_NEVER": 1, - "mjSLEEP_AUTO_ALLOWED": 2, - "mjSLEEP_NEVER": 3, - "mjSLEEP_ALLOWED": 4, - "mjSLEEP_INIT": 5 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 445 - }, - "mjtLRMode": { - "name": "mjtLRMode", - "members": { - "mjLRMODE_NONE": 0, - "mjLRMODE_MUSCLE": 1, - "mjLRMODE_MUSCLEUSER": 2, - "mjLRMODE_ALL": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 455 - }, - "mjtFlexSelf": { - "name": "mjtFlexSelf", - "members": { - "mjFLEXSELF_NONE": 0, - "mjFLEXSELF_NARROW": 1, - "mjFLEXSELF_BVH": 2, - "mjFLEXSELF_SAP": 3, - "mjFLEXSELF_AUTO": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 463 - }, - "mjtSDFType": { - "name": "mjtSDFType", - "members": { - "mjSDFTYPE_SINGLE": 0, - "mjSDFTYPE_INTERSECTION": 1, - "mjSDFTYPE_MIDSURFACE": 2, - "mjSDFTYPE_COLLISION": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 472 - }, - "mjtState": { - "name": "mjtState", - "members": { - "mjSTATE_TIME": 1, - "mjSTATE_QPOS": 2, - "mjSTATE_QVEL": 4, - "mjSTATE_ACT": 8, - "mjSTATE_HISTORY": 16, - "mjSTATE_WARMSTART": 32, - "mjSTATE_CTRL": 64, - "mjSTATE_QFRC_APPLIED": 128, - "mjSTATE_XFRC_APPLIED": 256, - "mjSTATE_EQ_ACTIVE": 512, - "mjSTATE_MOCAP_POS": 1024, - "mjSTATE_MOCAP_QUAT": 2048, - "mjSTATE_USERDATA": 4096, - "mjSTATE_PLUGIN": 8192, - "mjNSTATE": 14, - "mjSTATE_PHYSICS": 30, - "mjSTATE_FULLPHYSICS": 8223, - "mjSTATE_USER": 8128, - "mjSTATE_INTEGRATION": 16383 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 483 - }, - "mjtConstraint": { - "name": "mjtConstraint", - "members": { - "mjCNSTR_EQUALITY": 0, - "mjCNSTR_FRICTION_DOF": 1, - "mjCNSTR_FRICTION_TENDON": 2, - "mjCNSTR_LIMIT_JOINT": 3, - "mjCNSTR_LIMIT_TENDON": 4, - "mjCNSTR_CONTACT_FRICTIONLESS": 5, - "mjCNSTR_CONTACT_PYRAMIDAL": 6, - "mjCNSTR_CONTACT_ELLIPTIC": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 511 - }, - "mjtConstraintState": { - "name": "mjtConstraintState", - "members": { - "mjCNSTRSTATE_SATISFIED": 0, - "mjCNSTRSTATE_QUADRATIC": 1, - "mjCNSTRSTATE_LINEARNEG": 2, - "mjCNSTRSTATE_LINEARPOS": 3, - "mjCNSTRSTATE_CONE": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 523 - }, - "mjtWarning": { - "name": "mjtWarning", - "members": { - "mjWARN_INERTIA": 0, - "mjWARN_CONTACTFULL": 1, - "mjWARN_CNSTRFULL": 2, - "mjWARN_BADQPOS": 3, - "mjWARN_BADQVEL": 4, - "mjWARN_BADQACC": 5, - "mjWARN_BADCTRL": 6, - "mjNWARNING": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 532 - }, - "mjtTimer": { - "name": "mjtTimer", - "members": { - "mjTIMER_STEP": 0, - "mjTIMER_FORWARD": 1, - "mjTIMER_INVERSE": 2, - "mjTIMER_POSITION": 3, - "mjTIMER_VELOCITY": 4, - "mjTIMER_ACTUATION": 5, - "mjTIMER_CONSTRAINT": 6, - "mjTIMER_ADVANCE": 7, - "mjTIMER_POS_KINEMATICS": 8, - "mjTIMER_POS_INERTIA": 9, - "mjTIMER_POS_COLLISION": 10, - "mjTIMER_POS_MAKE": 11, - "mjTIMER_POS_PROJECT": 12, - "mjTIMER_COL_BROAD": 13, - "mjTIMER_COL_NARROW": 14, - "mjNTIMER": 15 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 545 - }, - "mjtSleepState": { - "name": "mjtSleepState", - "members": { - "mjS_STATIC": -1, - "mjS_ASLEEP": 0, - "mjS_AWAKE": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 573 - }, - "mjtLogLevel": { - "name": "mjtLogLevel", - "members": { - "mjLOG_DEBUG": 0, - "mjLOG_INFO": 1, - "mjLOG_WARNING": 2, - "mjLOG_ERROR": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 583 - }, - "mjtLogTopic": { - "name": "mjtLogTopic", - "members": { - "mjTOPIC_NONE": 0, - "mjTOPIC_TIME_STP": 1, - "mjTOPIC_TIME_CMP": 2, - "mjTOPIC_SLEEP": 3, - "mjNTOPIC": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 590 - }, - "mjtGeomInertia": { - "name": "mjtGeomInertia", - "members": { - "mjINERTIA_VOLUME": 0, - "mjINERTIA_SHELL": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 60 - }, - "mjtMeshInertia": { - "name": "mjtMeshInertia", - "members": { - "mjMESH_INERTIA_CONVEX": 0, - "mjMESH_INERTIA_EXACT": 1, - "mjMESH_INERTIA_LEGACY": 2, - "mjMESH_INERTIA_SHELL": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 66 - }, - "mjtMeshBuiltin": { - "name": "mjtMeshBuiltin", - "members": { - "mjMESH_BUILTIN_NONE": 0, - "mjMESH_BUILTIN_SPHERE": 1, - "mjMESH_BUILTIN_HEMISPHERE": 2, - "mjMESH_BUILTIN_CONE": 3, - "mjMESH_BUILTIN_SUPERSPHERE": 4, - "mjMESH_BUILTIN_SUPERTORUS": 5, - "mjMESH_BUILTIN_WEDGE": 6, - "mjMESH_BUILTIN_PLATE": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 74 - }, - "mjtBuiltin": { - "name": "mjtBuiltin", - "members": { - "mjBUILTIN_NONE": 0, - "mjBUILTIN_GRADIENT": 1, - "mjBUILTIN_CHECKER": 2, - "mjBUILTIN_FLAT": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 86 - }, - "mjtMark": { - "name": "mjtMark", - "members": { - "mjMARK_NONE": 0, - "mjMARK_EDGE": 1, - "mjMARK_CROSS": 2, - "mjMARK_RANDOM": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 94 - }, - "mjtLimited": { - "name": "mjtLimited", - "members": { - "mjLIMITED_FALSE": 0, - "mjLIMITED_TRUE": 1, - "mjLIMITED_AUTO": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 102 - }, - "mjtAlignFree": { - "name": "mjtAlignFree", - "members": { - "mjALIGNFREE_FALSE": 0, - "mjALIGNFREE_TRUE": 1, - "mjALIGNFREE_AUTO": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 109 - }, - "mjtInertiaFromGeom": { - "name": "mjtInertiaFromGeom", - "members": { - "mjINERTIAFROMGEOM_FALSE": 0, - "mjINERTIAFROMGEOM_TRUE": 1, - "mjINERTIAFROMGEOM_AUTO": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 116 - }, - "mjtOrientation": { - "name": "mjtOrientation", - "members": { - "mjORIENTATION_QUAT": 0, - "mjORIENTATION_AXISANGLE": 1, - "mjORIENTATION_XYAXES": 2, - "mjORIENTATION_ZAXIS": 3, - "mjORIENTATION_EULER": 4 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 123 - }, - "mjtCTimer": { - "name": "mjtCTimer", - "members": { - "mjCTIMER_TOTAL": 0, - "mjCTIMER_ASSETS": 1, - "mjCTIMER_TEXTURE": 2, - "mjCTIMER_MESH_LOAD": 3, - "mjCTIMER_MESH_HULL": 4, - "mjCTIMER_MESH_POLYGON": 5, - "mjCTIMER_MESH_INERTIA": 6, - "mjCTIMER_MESH_BVH": 7, - "mjCTIMER_MESH_OCTREE": 8, - "mjNCTIMER": 9 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 132 - }, - "mjtCatBit": { - "name": "mjtCatBit", - "members": { - "mjCAT_STATIC": 1, - "mjCAT_DYNAMIC": 2, - "mjCAT_DECOR": 4, - "mjCAT_ALL": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 33 - }, - "mjtMouse": { - "name": "mjtMouse", - "members": { - "mjMOUSE_NONE": 0, - "mjMOUSE_ROTATE_V": 1, - "mjMOUSE_ROTATE_H": 2, - "mjMOUSE_MOVE_V": 3, - "mjMOUSE_MOVE_H": 4, - "mjMOUSE_ZOOM": 5, - "mjMOUSE_MOVE_V_REL": 6, - "mjMOUSE_MOVE_H_REL": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 41 - }, - "mjtPertBit": { - "name": "mjtPertBit", - "members": { - "mjPERT_TRANSLATE": 1, - "mjPERT_ROTATE": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 53 - }, - "mjtCamera": { - "name": "mjtCamera", - "members": { - "mjCAMERA_FREE": 0, - "mjCAMERA_TRACKING": 1, - "mjCAMERA_FIXED": 2, - "mjCAMERA_USER": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 59 - }, - "mjtLabel": { - "name": "mjtLabel", - "members": { - "mjLABEL_NONE": 0, - "mjLABEL_BODY": 1, - "mjLABEL_JOINT": 2, - "mjLABEL_GEOM": 3, - "mjLABEL_SITE": 4, - "mjLABEL_CAMERA": 5, - "mjLABEL_LIGHT": 6, - "mjLABEL_TENDON": 7, - "mjLABEL_ACTUATOR": 8, - "mjLABEL_CONSTRAINT": 9, - "mjLABEL_FLEX": 10, - "mjLABEL_SKIN": 11, - "mjLABEL_SELECTION": 12, - "mjLABEL_SELPNT": 13, - "mjLABEL_CONTACTPOINT": 14, - "mjLABEL_CONTACTFORCE": 15, - "mjLABEL_ISLAND": 16, - "mjNLABEL": 17 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 67 - }, - "mjtFrame": { - "name": "mjtFrame", - "members": { - "mjFRAME_NONE": 0, - "mjFRAME_BODY": 1, - "mjFRAME_GEOM": 2, - "mjFRAME_SITE": 3, - "mjFRAME_CAMERA": 4, - "mjFRAME_LIGHT": 5, - "mjFRAME_CONTACT": 6, - "mjFRAME_WORLD": 7, - "mjNFRAME": 8 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 90 - }, - "mjtVisFlag": { - "name": "mjtVisFlag", - "members": { - "mjVIS_CONVEXHULL": 0, - "mjVIS_TEXTURE": 1, - "mjVIS_JOINT": 2, - "mjVIS_CAMERA": 3, - "mjVIS_ACTUATOR": 4, - "mjVIS_ACTIVATION": 5, - "mjVIS_LIGHT": 6, - "mjVIS_TENDON": 7, - "mjVIS_RANGEFINDER": 8, - "mjVIS_CONSTRAINT": 9, - "mjVIS_INERTIA": 10, - "mjVIS_SCLINERTIA": 11, - "mjVIS_PERTFORCE": 12, - "mjVIS_PERTOBJ": 13, - "mjVIS_CONTACTPOINT": 14, - "mjVIS_ISLAND": 15, - "mjVIS_CONTACTFORCE": 16, - "mjVIS_CONTACTSPLIT": 17, - "mjVIS_TRANSPARENT": 18, - "mjVIS_AUTOCONNECT": 19, - "mjVIS_COM": 20, - "mjVIS_SELECT": 21, - "mjVIS_STATIC": 22, - "mjVIS_SKIN": 23, - "mjVIS_FLEXVERT": 24, - "mjVIS_FLEXEDGE": 25, - "mjVIS_FLEXFACE": 26, - "mjVIS_FLEXSKIN": 27, - "mjVIS_BODYBVH": 28, - "mjVIS_MESHBVH": 29, - "mjVIS_SDFITER": 30, - "mjNVISFLAG": 31 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 104 - }, - "mjtRndFlag": { - "name": "mjtRndFlag", - "members": { - "mjRND_SHADOW": 0, - "mjRND_WIREFRAME": 1, - "mjRND_REFLECTION": 2, - "mjRND_ADDITIVE": 3, - "mjRND_SKYBOX": 4, - "mjRND_FOG": 5, - "mjRND_HAZE": 6, - "mjRND_DEPTH": 7, - "mjRND_SEGMENT": 8, - "mjRND_IDCOLOR": 9, - "mjRND_CULL_FACE": 10, - "mjNRNDFLAG": 11 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 141 - }, - "mjtStereo": { - "name": "mjtStereo", - "members": { - "mjSTEREO_NONE": 0, - "mjSTEREO_QUADBUFFERED": 1, - "mjSTEREO_SIDEBYSIDE": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 158 - }, - "mjtPluginCapabilityBit": { - "name": "mjtPluginCapabilityBit", - "members": { - "mjPLUGIN_ACTUATOR": 1, - "mjPLUGIN_SENSOR": 2, - "mjPLUGIN_PASSIVE": 4, - "mjPLUGIN_SDF": 8 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 110 - }, - "mjtGridPos": { - "name": "mjtGridPos", - "members": { - "mjGRID_TOPLEFT": 0, - "mjGRID_TOPRIGHT": 1, - "mjGRID_BOTTOMLEFT": 2, - "mjGRID_BOTTOMRIGHT": 3, - "mjGRID_TOP": 4, - "mjGRID_BOTTOM": 5, - "mjGRID_LEFT": 6, - "mjGRID_RIGHT": 7 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 31 - }, - "mjtFramebuffer": { - "name": "mjtFramebuffer", - "members": { - "mjFB_WINDOW": 0, - "mjFB_OFFSCREEN": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 43 - }, - "mjtDepthMap": { - "name": "mjtDepthMap", - "members": { - "mjDEPTH_ZERONEAR": 0, - "mjDEPTH_ZEROFAR": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 49 - }, - "mjtFontScale": { - "name": "mjtFontScale", - "members": { - "mjFONTSCALE_50": 50, - "mjFONTSCALE_100": 100, - "mjFONTSCALE_150": 150, - "mjFONTSCALE_200": 200, - "mjFONTSCALE_250": 250, - "mjFONTSCALE_300": 300 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 55 - }, - "mjtFont": { - "name": "mjtFont", - "members": { - "mjFONT_NORMAL": 0, - "mjFONT_SHADOW": 1, - "mjFONT_BIG": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 65 - }, - "mjrPixelFormat": { - "name": "mjrPixelFormat", - "members": { - "mjPIXEL_FORMAT_UNKNOWN": 0, - "mjPIXEL_FORMAT_R8": 1, - "mjPIXEL_FORMAT_RGB8": 2, - "mjPIXEL_FORMAT_RGBA8": 3, - "mjPIXEL_FORMAT_R32F": 4, - "mjPIXEL_FORMAT_DEPTH32F": 5, - "mjPIXEL_FORMAT_KTX": 6 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 72 - }, - "mjrVertexAttributeUsage": { - "name": "mjrVertexAttributeUsage", - "members": { - "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 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 83 - }, - "mjrVertexAttributeType": { - "name": "mjrVertexAttributeType", - "members": { - "mjVERTEX_ATTRIBUTE_TYPE_FLOAT2": 0, - "mjVERTEX_ATTRIBUTE_TYPE_FLOAT3": 1, - "mjVERTEX_ATTRIBUTE_TYPE_FLOAT4": 2, - "mjVERTEX_ATTRIBUTE_TYPE_UBYTE4": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 92 - }, - "mjrIndexType": { - "name": "mjrIndexType", - "members": { - "mjINDEX_TYPE_U16": 0, - "mjINDEX_TYPE_U32": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 100 - }, - "mjrMeshPrimitiveType": { - "name": "mjrMeshPrimitiveType", - "members": { - "mjMESH_PRIMITIVE_TYPE_TRIANGLES": 0, - "mjMESH_PRIMITIVE_TYPE_LINES": 1 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 106 - }, - "mjtButton": { - "name": "mjtButton", - "members": { - "mjBUTTON_NONE": 0, - "mjBUTTON_LEFT": 1, - "mjBUTTON_RIGHT": 2, - "mjBUTTON_MIDDLE": 3 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 65 - }, - "mjtEvent": { - "name": "mjtEvent", - "members": { - "mjEVENT_NONE": 0, - "mjEVENT_MOVE": 1, - "mjEVENT_PRESS": 2, - "mjEVENT_RELEASE": 3, - "mjEVENT_SCROLL": 4, - "mjEVENT_KEY": 5, - "mjEVENT_RESIZE": 6, - "mjEVENT_REDRAW": 7, - "mjEVENT_FILESDROP": 8 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 73 - }, - "mjtItem": { - "name": "mjtItem", - "members": { - "mjITEM_END": -2, - "mjITEM_SECTION": -1, - "mjITEM_SEPARATOR": 0, - "mjITEM_STATIC": 1, - "mjITEM_BUTTON": 2, - "mjITEM_CHECKINT": 3, - "mjITEM_CHECKBYTE": 4, - "mjITEM_RADIO": 5, - "mjITEM_RADIOLINE": 6, - "mjITEM_SELECT": 7, - "mjITEM_SLIDERINT": 8, - "mjITEM_SLIDERNUM": 9, - "mjITEM_EDITINT": 10, - "mjITEM_EDITNUM": 11, - "mjITEM_EDITFLOAT": 12, - "mjITEM_EDITTXT": 13, - "mjNITEM": 14 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 86 - }, - "mjtSection": { - "name": "mjtSection", - "members": { - "mjSECT_CLOSED": 0, - "mjSECT_OPEN": 1, - "mjSECT_FIXED": 2 - }, - "member_docs": {}, - "underlying_type": "int", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 110 - } - }, - "structs": { - "mjLogMessage": { - "name": "mjLogMessage", - "fields": [ - { - "name": "level", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "topic", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "subject", - "c_type": "char [1024]", - "array_dim": 1024, - "is_pointer": false, - "doc": "" - }, - { - "name": "body", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "func", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "file", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "line", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "timestamp", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 601 - }, - "mjLogConfig": { - "name": "mjLogConfig", - "fields": [ - { - "name": "logto_console", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "logto_file", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "logfile", - "c_type": "char [1024]", - "array_dim": 1024, - "is_pointer": false, - "doc": "" - }, - { - "name": "topics", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 612 - }, - "mjLROpt": { - "name": "mjLROpt", - "fields": [ - { - "name": "mode", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "useexisting", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "uselimit", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "accel", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxforce", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "timeconst", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "timestep", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "inttotal", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "interval", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "tolrange", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 53 - }, - "mjCache": { - "name": "mjCache", - "fields": [ - { - "name": "impl_", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 72 - }, - "mjVFS": { - "name": "mjVFS", - "fields": [ - { - "name": "impl_", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 79 - }, - "mjOption": { - "name": "mjOption", - "fields": [ - { - "name": "timestep", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "impratio", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "tolerance", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ls_tolerance", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "noslip_tolerance", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ccd_tolerance", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sleep_tolerance", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gravity", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "wind", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "magnetic", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "density", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "viscosity", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "o_margin", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "o_solref", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "o_solimp", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "o_friction", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "integrator", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "cone", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "jacobian", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solver", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "iterations", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ls_iterations", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "noslip_iterations", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ccd_iterations", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "disableflags", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "enableflags", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "disableactuator", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sdf_initpoints", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sdf_iterations", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 86 - }, - "mjVisual": { - "name": "mjVisual", - "fields": [ - { - "name": "global", - "c_type": "struct (anonymous struct at C:\\Users\\jonat\\Documents\\Unreal Projects\\url_proj\\Plugins\\UnrealRoboticsLab\\third_party\\install\\MuJoCo\\include\\mujoco/mjmodel.h:136:3)", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "quality", - "c_type": "struct (anonymous struct at C:\\Users\\jonat\\Documents\\Unreal Projects\\url_proj\\Plugins\\UnrealRoboticsLab\\third_party\\install\\MuJoCo\\include\\mujoco/mjmodel.h:152:3)", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "headlight", - "c_type": "struct (anonymous struct at C:\\Users\\jonat\\Documents\\Unreal Projects\\url_proj\\Plugins\\UnrealRoboticsLab\\third_party\\install\\MuJoCo\\include\\mujoco/mjmodel.h:160:3)", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "map", - "c_type": "struct (anonymous struct at C:\\Users\\jonat\\Documents\\Unreal Projects\\url_proj\\Plugins\\UnrealRoboticsLab\\third_party\\install\\MuJoCo\\include\\mujoco/mjmodel.h:167:3)", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "scale", - "c_type": "struct (anonymous struct at C:\\Users\\jonat\\Documents\\Unreal Projects\\url_proj\\Plugins\\UnrealRoboticsLab\\third_party\\install\\MuJoCo\\include\\mujoco/mjmodel.h:183:3)", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgba", - "c_type": "struct (anonymous struct at C:\\Users\\jonat\\Documents\\Unreal Projects\\url_proj\\Plugins\\UnrealRoboticsLab\\third_party\\install\\MuJoCo\\include\\mujoco/mjmodel.h:203:3)", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 135 - }, - "mjStatistic": { - "name": "mjStatistic", - "fields": [ - { - "name": "meaninertia", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "meanmass", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "meansize", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "extent", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "center", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 236 - }, - "mjModel": { - "name": "mjModel", - "fields": [ - { - "name": "nq", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nv", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nu", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "na", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbody", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbvh", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbvhstatic", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbvhdynamic", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "noct", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "njnt", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntree", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nM", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nB", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nC", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nD", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ngeom", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nsite", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ncam", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nlight", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflex", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexnode", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexvert", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexedge", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexelem", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexelemdata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexstiffness", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexbending", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexelemedge", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexshelldata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflexevpair", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nflextexcoord", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nJfe", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nJfv", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmesh", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshvert", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshnormal", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshtexcoord", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshface", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshgraph", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshpoly", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshpolyvert", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmeshpolymap", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskin", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskinvert", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskintexvert", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskinface", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskinbone", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskinbonevert", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nhfield", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nhfielddata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntex", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntexdata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmat", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "npair", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nexclude", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "neq", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntendon", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nJten", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nwrap", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nsensor", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nnumeric", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nnumericdata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntext", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntextdata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntuple", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntupledata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nkey", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nmocap", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nplugin", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "npluginattr", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_body", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_jnt", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_geom", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_site", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_cam", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_tendon", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_actuator", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_sensor", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nnames", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "npaths", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nnames_map", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nJmom", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ngravcomp", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nemax", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "njmax", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nconmax", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuserdata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nsensordata", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "npluginstate", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nhistory", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "narena", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbuffer", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "opt", - "c_type": "mjOption", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "vis", - "c_type": "mjVisual", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "stat", - "c_type": "mjStatistic", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "buffer", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qpos0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qpos_spring", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_parentid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_rootid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_weldid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_mocapid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_jntnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_jntadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_dofnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_dofadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_treeid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_geomnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_geomadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_simple", - "c_type": "mjtByte *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_sameframe", - "c_type": "mjtByte *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_quat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_ipos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_iquat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_mass", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_subtreemass", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_inertia", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_invweight0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_gravcomp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_plugin", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_contype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_conaffinity", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_bvhadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_bvhnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bvh_depth", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bvh_child", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bvh_nodeid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bvh_aabb", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "oct_depth", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "oct_child", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "oct_aabb", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "oct_coeff", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_qposadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_dofadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_bodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_actuatorid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_limited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_actfrclimited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_actgravcomp", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_solref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_solimp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_axis", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_stiffness", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_stiffnesspoly", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_range", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_actfrcrange", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "jnt_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_bodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_jntid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_parentid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_treeid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_Madr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_simplenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_solref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_solimp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_frictionloss", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_armature", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_damping", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_dampingpoly", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_invweight0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_M0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_length", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_bodyadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_bodynum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_dofadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_dofnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_sleep_policy", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_contype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_conaffinity", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_condim", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_bodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_dataid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_matid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_priority", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_plugin", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_sameframe", - "c_type": "mjtByte *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_solmix", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_solref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_solimp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_size", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_aabb", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_rbound", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_quat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_friction", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_gap", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_fluid", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_rgba", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_bodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_matid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_sameframe", - "c_type": "mjtByte *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_size", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_quat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_rgba", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_mode", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_bodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_targetbodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_quat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_poscom0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_pos0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_mat0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_projection", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_fovy", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_ipd", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_resolution", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_output", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_sensorsize", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_intrinsic", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_mode", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_bodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_targetbodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_texid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_castshadow", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_bulbradius", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_intensity", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_range", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_active", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_dir", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_poscom0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_pos0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_dir0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_attenuation", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_cutoff", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_exponent", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_ambient", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_diffuse", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_specular", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_contype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_conaffinity", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_condim", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_priority", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_solmix", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_solref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_solimp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_friction", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_gap", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_internal", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_selfcollide", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_activelayers", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_passive", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_dim", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_matid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_interp", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_cellnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_nodeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_nodenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edgeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edgenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemdataadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_stiffnessadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemedgeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_bendingadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_shellnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_shelldataadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_evpairadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_evpairnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_texcoordadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_nodebodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertbodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertedgeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertedgenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertedge", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edge", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edgeflap", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elem", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemtexcoord", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemedge", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_elemlayer", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_shell", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_evpair", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vert", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vert0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_vertmetric", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_node", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_node0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_length0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_invweight0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_radius", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_size", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_stiffness", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_bending", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_damping", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edgestiffness", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edgedamping", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_edgeequality", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_rigid", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_rigid", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_centered", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_flatskin", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_bvhadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_bvhnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_J_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_J_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_J_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert_J_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert_J_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert_J_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_rgba", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex_texcoord", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_vertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_vertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_faceadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_facenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_bvhadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_bvhnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_octadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_octnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_normaladr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_normalnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_texcoordadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_texcoordnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_graphadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_vert", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_normal", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_texcoord", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_face", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_facenormal", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_facetexcoord", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_graph", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_scale", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_quat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_pathadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polynum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polyadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polynormal", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polyvertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polyvertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polyvert", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polymapadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polymapnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh_polymap", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_matid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_rgba", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_inflate", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_vertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_vertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_texcoordadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_faceadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_facenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_boneadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_vert", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_texcoord", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_face", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonevertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonevertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonebindpos", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonebindquat", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonebodyid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonevertid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_bonevertweight", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skin_pathadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hfield_size", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hfield_nrow", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hfield_ncol", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hfield_adr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hfield_data", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hfield_pathadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_colorspace", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_height", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_width", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_nchannel", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_adr", - "c_type": "mjtSize *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_data", - "c_type": "mjtByte *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tex_pathadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_texid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_texuniform", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_texrepeat", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_emission", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_specular", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_shininess", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_reflectance", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_metallic", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_roughness", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mat_rgba", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_dim", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_geom1", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_geom2", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_signature", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_solref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_solreffriction", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_solimp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_gap", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair_friction", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "exclude_signature", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_obj1id", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_obj2id", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_objtype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_active0", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_solref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_solimp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_data", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_adr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_num", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_matid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_actuatorid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_treenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_treeid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_J_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_J_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_J_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_limited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_actfrclimited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_width", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_solref_lim", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_solimp_lim", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_solref_fri", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_solimp_fri", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_range", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_actfrcrange", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_stiffness", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_stiffnesspoly", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_damping", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_dampingpoly", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_armature", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_frictionloss", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_lengthspring", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_length0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_invweight0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_rgba", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "wrap_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "wrap_objid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "wrap_prm", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_trntype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_dyntype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_gaintype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_biastype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_trnid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_damping", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_dampingpoly", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_armature", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_actadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_actnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_group", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_history", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_historyadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_delay", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_ctrllimited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_forcelimited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_actlimited", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_dynprm", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_gainprm", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_biasprm", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_actearly", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_ctrlrange", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_forcerange", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_actrange", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_gear", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_cranklength", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_acc0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_length0", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_lengthrange", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_plugin", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_datatype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_needstage", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_objtype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_objid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_reftype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_refid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_intprm", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_dim", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_adr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_cutoff", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_noise", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_history", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_historyadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_delay", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_interval", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_user", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensor_plugin", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_stateadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_statenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_attr", - "c_type": "char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_attradr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "numeric_adr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "numeric_size", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "numeric_data", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "text_adr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "text_size", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "text_data", - "c_type": "char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tuple_adr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tuple_size", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tuple_objtype", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tuple_objid", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tuple_objprm", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_time", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_qpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_qvel", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_act", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_mpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_mquat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "key_ctrl", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_bodyadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_jntadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_geomadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_siteadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_camadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_lightadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_flexadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_meshadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_skinadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_hfieldadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_texadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_matadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_pairadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_excludeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_eqadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_tendonadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_actuatoradr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_sensoradr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_numericadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_textadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_tupleadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_keyadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name_pluginadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "names", - "c_type": "char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "names_map", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "paths", - "c_type": "char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "B_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "B_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "B_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "M_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "M_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "M_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mapM2M", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "D_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "D_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "D_diag", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "D_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mapM2D", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mapD2M", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "signature", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 248 - }, - "mjsElement": { - "name": "mjsElement", - "fields": [ - { - "name": "elemtype", - "c_type": "mjtObj", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "signature", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 152 - }, - "mjsCompiler": { - "name": "mjsCompiler", - "fields": [ - { - "name": "autolimits", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "boundmass", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "boundinertia", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "settotalmass", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "balanceinertia", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fitaabb", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "degree", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "eulerseq", - "c_type": "char [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "discardvisual", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "usethread", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fusestatic", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "inertiafromgeom", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "inertiagrouprange", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "saveinertial", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "alignfree", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "LRopt", - "c_type": "mjLROpt", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "meshdir", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "texturedir", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "authored", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 158 - }, - "mjsAuthored": { - "name": "mjsAuthored", - "fields": [ - { - "name": "option", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "disableflags", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "enableflags", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "disableactuator", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual_global", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual_quality", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual_headlight", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual_map", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual_scale", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual_rgba", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 181 - }, - "mjSpec": { - "name": "mjSpec", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "modelname", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "compiler", - "c_type": "mjsCompiler", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "strippath", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "option", - "c_type": "mjOption", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "visual", - "c_type": "mjVisual", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "stat", - "c_type": "mjStatistic", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "memory", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nemax", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuserdata", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_body", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_jnt", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_geom", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_site", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_cam", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_tendon", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_actuator", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nuser_sensor", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nkey", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "njmax", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nconmax", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nstack", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "comment", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "modelfiledir", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hasImplicitPluginElem", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "authored", - "c_type": "mjsAuthored", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 195 - }, - "mjsOrientation": { - "name": "mjsOrientation", - "fields": [ - { - "name": "type", - "c_type": "mjtOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "axisangle", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "xyaxes", - "c_type": "double [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "zaxis", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "euler", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 237 - }, - "mjsPlugin": { - "name": "mjsPlugin", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_name", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "active", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 246 - }, - "mjsBody": { - "name": "mjsBody", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "childclass", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "quat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "alt", - "c_type": "mjsOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mass", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ipos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "iquat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "inertia", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "ialt", - "c_type": "mjsOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fullinertia", - "c_type": "double [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "mocap", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gravcomp", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sleep", - "c_type": "mjtSleepPolicy", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "explicitinertial", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "plugin", - "c_type": "mjsPlugin", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 255 - }, - "mjsFrame": { - "name": "mjsFrame", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "childclass", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "quat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "alt", - "c_type": "mjsOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 283 - }, - "mjsJoint": { - "name": "mjsJoint", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtJoint", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "axis", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "ref", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "align", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "stiffness", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "springref", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "springdamper", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "limited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "range", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "margin", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref_limit", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp_limit", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "actfrclimited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "actfrcrange", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "armature", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "damping", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "frictionloss", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref_friction", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp_friction", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "actgravcomp", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 293 - }, - "mjsGeom": { - "name": "mjsGeom", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtGeom", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "quat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "alt", - "c_type": "mjsOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fromto", - "c_type": "double [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "size", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "contype", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "conaffinity", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "condim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "priority", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "friction", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "solmix", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "margin", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gap", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mass", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "density", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "typeinertia", - "c_type": "mjtGeomInertia", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fluid_ellipsoid", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fluid_coefs", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "material", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "hfieldname", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "meshname", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "fitscale", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin", - "c_type": "mjsPlugin", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 332 - }, - "mjsSite": { - "name": "mjsSite", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "quat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "alt", - "c_type": "mjsOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fromto", - "c_type": "double [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "size", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtGeom", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "material", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 379 - }, - "mjsCamera": { - "name": "mjsCamera", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "quat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "alt", - "c_type": "mjsOrientation", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mode", - "c_type": "mjtCamLight", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "targetbody", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "proj", - "c_type": "mjtProjection", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "resolution", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "output", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fovy", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ipd", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "intrinsic", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "sensor_size", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "focal_length", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "focal_pixel", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "principal_length", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "principal_pixel", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 401 - }, - "mjsLight": { - "name": "mjsLight", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "dir", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "mode", - "c_type": "mjtCamLight", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "targetbody", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "active", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtLightType", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "texture", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "castshadow", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "bulbradius", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "intensity", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "range", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "attenuation", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "cutoff", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "exponent", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ambient", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "diffuse", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "specular", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 430 - }, - "mjsFlex": { - "name": "mjsFlex", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "contype", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "conaffinity", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "condim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "priority", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "friction", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "solmix", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "margin", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gap", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "radius", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "size", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "internal", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flatskin", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "selfcollide", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "passive", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "activelayers", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "edgestiffness", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "edgedamping", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "material", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "young", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "poisson", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "damping", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "thickness", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "elastic2d", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "cellcount", - "c_type": "int [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "order", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nodebody", - "c_type": "mjStringVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "vertbody", - "c_type": "mjStringVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "node", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "vert", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "elem", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "texcoord", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "elemtexcoord", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 459 - }, - "mjsMesh": { - "name": "mjsMesh", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "content_type", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "file", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "refpos", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "refquat", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "scale", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "inertia", - "c_type": "mjtMeshInertia", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "smoothnormal", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "needsdf", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxhullvert", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "uservert", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "usernormal", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "usertexcoord", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "userface", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "userfacenormal", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "userfacetexcoord", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin", - "c_type": "mjsPlugin", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "material", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "octree_maxdepth", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 510 - }, - "mjsHField": { - "name": "mjsHField", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "content_type", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "file", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "size", - "c_type": "double [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "nrow", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ncol", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 534 - }, - "mjsSkin": { - "name": "mjsSkin", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "file", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "material", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "inflate", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "vert", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "texcoord", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "face", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bodyname", - "c_type": "mjStringVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bindpos", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bindquat", - "c_type": "mjFloatVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "vertid", - "c_type": "mjIntVecVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "vertweight", - "c_type": "mjFloatVecVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 547 - }, - "mjsTexture": { - "name": "mjsTexture", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtTexture", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "colorspace", - "c_type": "mjtColorSpace", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "builtin", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mark", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgb1", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgb2", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "markrgb", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "random", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "height", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "width", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nchannel", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "content_type", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "file", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "gridsize", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "gridlayout", - "c_type": "char [12]", - "array_dim": 12, - "is_pointer": false, - "doc": "" - }, - { - "name": "cubefiles", - "c_type": "mjStringVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "data", - "c_type": "mjByteVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "hflip", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "vflip", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 572 - }, - "mjsMaterial": { - "name": "mjsMaterial", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "textures", - "c_type": "mjStringVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "texuniform", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "texrepeat", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "emission", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "specular", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shininess", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "reflectance", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "metallic", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "roughness", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 609 - }, - "mjsPair": { - "name": "mjsPair", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geomname1", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geomname2", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "condim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solreffriction", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "margin", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gap", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "friction", - "c_type": "double [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 625 - }, - "mjsExclude": { - "name": "mjsExclude", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bodyname1", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bodyname2", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 642 - }, - "mjsEquality": { - "name": "mjsEquality", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtEq", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "data", - "c_type": "double [11]", - "array_dim": 11, - "is_pointer": false, - "doc": "" - }, - { - "name": "active", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "name1", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "name2", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "objtype", - "c_type": "mjtObj", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 650 - }, - "mjsTendon": { - "name": "mjsTendon", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "stiffness", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "springlength", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "damping", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "frictionloss", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref_friction", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp_friction", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "armature", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "limited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "actfrclimited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "range", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "actfrcrange", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "margin", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref_limit", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp_limit", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "material", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "width", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 664 - }, - "mjsWrap": { - "name": "mjsWrap", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtWrap", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 697 - }, - "mjsActuator": { - "name": "mjsActuator", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "gaintype", - "c_type": "mjtGain", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gainprm", - "c_type": "double [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "biastype", - "c_type": "mjtBias", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "biasprm", - "c_type": "double [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "dyntype", - "c_type": "mjtDyn", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dynprm", - "c_type": "double [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "actdim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "actearly", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "trntype", - "c_type": "mjtTrn", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gear", - "c_type": "double [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "target", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "refsite", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "slidersite", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cranklength", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "lengthrange", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "inheritrange", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "damping", - "c_type": "double [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "armature", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ctrllimited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ctrlrange", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "forcelimited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "forcerange", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "actlimited", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "actrange", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "group", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nsample", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "interp", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "delay", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin", - "c_type": "mjsPlugin", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 704 - }, - "mjsSensor": { - "name": "mjsSensor", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtSensor", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "objtype", - "c_type": "mjtObj", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "objname", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "reftype", - "c_type": "mjtObj", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "refname", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "intprm", - "c_type": "int [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "datatype", - "c_type": "mjtDataType", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "needstage", - "c_type": "mjtStage", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "cutoff", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "noise", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nsample", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "interp", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "delay", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "interval", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin", - "c_type": "mjsPlugin", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 750 - }, - "mjsNumeric": { - "name": "mjsNumeric", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "data", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "size", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 783 - }, - "mjsText": { - "name": "mjsText", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "data", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 791 - }, - "mjsTuple": { - "name": "mjsTuple", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "objtype", - "c_type": "mjIntVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "objname", - "c_type": "mjStringVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "objprm", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 798 - }, - "mjsKey": { - "name": "mjsKey", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "time", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "qpos", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qvel", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "act", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mpos", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mquat", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ctrl", - "c_type": "mjDoubleVec *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "info", - "c_type": "mjString *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 807 - }, - "mjsDefault": { - "name": "mjsDefault", - "fields": [ - { - "name": "element", - "c_type": "mjsElement *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "joint", - "c_type": "mjsJoint *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom", - "c_type": "mjsGeom *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site", - "c_type": "mjsSite *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "camera", - "c_type": "mjsCamera *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light", - "c_type": "mjsLight *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flex", - "c_type": "mjsFlex *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mesh", - "c_type": "mjsMesh *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "material", - "c_type": "mjsMaterial *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "pair", - "c_type": "mjsPair *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "equality", - "c_type": "mjsEquality *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon", - "c_type": "mjsTendon *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator", - "c_type": "mjsActuator *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspec.h", - "line": 820 - }, - "mjPreContact": { - "name": "mjPreContact", - "fields": [ - { - "name": "dist", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pos", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "normal", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "tangent", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjdata.h", - "line": 29 - }, - "mjContact": { - "name": "mjContact", - "fields": [ - { - "name": "dist", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pos", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "frame", - "c_type": "mjtNum [9]", - "array_dim": 9, - "is_pointer": false, - "doc": "" - }, - { - "name": "includemargin", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "friction", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "solref", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solreffriction", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "solimp", - "c_type": "mjtNum [5]", - "array_dim": 5, - "is_pointer": false, - "doc": "" - }, - { - "name": "mu", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "H", - "c_type": "mjtNum [36]", - "array_dim": 36, - "is_pointer": false, - "doc": "" - }, - { - "name": "dim", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "geom1", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "geom2", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "geom", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "flex", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "elem", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "vert", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "exclude", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "efc_address", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjdata.h", - "line": 37 - }, - "mjWarningStat": { - "name": "mjWarningStat", - "fields": [ - { - "name": "lastinfo", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "number", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjdata.h", - "line": 74 - }, - "mjTimerStat": { - "name": "mjTimerStat", - "fields": [ - { - "name": "duration", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "number", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjdata.h", - "line": 81 - }, - "mjSolverStat": { - "name": "mjSolverStat", - "fields": [ - { - "name": "improvement", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gradient", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "lineslope", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nactive", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nchange", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "neval", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nupdate", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjdata.h", - "line": 88 - }, - "mjData": { - "name": "mjData", - "fields": [ - { - "name": "narena", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbuffer", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nplugin", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pstack", - "c_type": "size_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pbase", - "c_type": "size_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "parena", - "c_type": "size_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "threadpool", - "c_type": "uintptr_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "threadlock", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxuse_stack", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxuse_arena", - "c_type": "mjtSize", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxuse_con", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxuse_efc", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "solver", - "c_type": "mjSolverStat [4000]", - "array_dim": 4000, - "is_pointer": false, - "doc": "" - }, - { - "name": "solver_niter", - "c_type": "int [20]", - "array_dim": 20, - "is_pointer": false, - "doc": "" - }, - { - "name": "solver_nnz", - "c_type": "int [20]", - "array_dim": 20, - "is_pointer": false, - "doc": "" - }, - { - "name": "solver_fwdinv", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "warning", - "c_type": "mjWarningStat [7]", - "array_dim": 7, - "is_pointer": false, - "doc": "" - }, - { - "name": "timer", - "c_type": "mjTimerStat [15]", - "array_dim": 15, - "is_pointer": false, - "doc": "" - }, - { - "name": "ncon", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ne", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nf", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nl", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nefc", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nJ", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nY", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nA", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nisland", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nidof", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntree_awake", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nbody_awake", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nparent_awake", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nv_awake", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_energypos", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_energyvel", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_subtreevel", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_rnepost", - "c_type": "mjtBool", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "time", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "energy", - "c_type": "mjtNum [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "buffer", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "arena", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qvel", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "act", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "history", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qacc_warmstart", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_state", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ctrl", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_applied", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xfrc_applied", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "eq_active", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mocap_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "mocap_quat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qacc", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "act_dot", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "userdata", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sensordata", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_asleep", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "plugin_data", - "c_type": "uintptr_t *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xquat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xmat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xipos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ximat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xanchor", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "xaxis", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geom_xmat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "site_xmat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cam_xmat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "light_xdir", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "subtree_com", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cdof", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cinert", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert_xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexelem_aabb", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_J", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_length", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert_J", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert_length", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bvh_aabb_dyn", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_wrapadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_wrapnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_J", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_length", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "wrap_obj", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "wrap_xpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_length", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "moment_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "moment_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "moment_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_moment", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "crb", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qM", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "M", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qLD", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qLDiagInv", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "bvh_active", - "c_type": "mjtBool *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_awake", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_awake", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "body_awake_ind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "parent_awake_ind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_awake_ind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge_velocity", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ten_velocity", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_velocity", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cvel", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cdof_dot", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_bias", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_spring", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_damper", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_gravcomp", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_fluid", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_passive", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "subtree_linvel", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "subtree_angmom", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qH", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qHDiagInv", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qDeriv", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qLU", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_force", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_actuator", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_smooth", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qacc_smooth", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_constraint", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "qfrc_inverse", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cacc", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cfrc_int", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "cfrc_ext", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "contact", - "c_type": "mjContact *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_id", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_J_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_J_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_J_rowsuper", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_J_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_J", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_pos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_margin", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_frictionloss", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_diagA", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_KBIP", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_D", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_R", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tendon_efcadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "tree_island", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_ntree", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_itreeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "map_itree2tree", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "dof_island", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_nv", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_idofadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_dofadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "map_dof2idof", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "map_idof2dof", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ifrc_smooth", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iacc_smooth", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iacc", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_island", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_ne", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_nf", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_nefc", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "island_iefcadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "map_efc2iefc", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "map_iefc2efc", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_type", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_id", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_frictionloss", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_D", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_R", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_Y_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_Y_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_Y_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_Y", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_AR_rownnz", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_AR_rowadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_AR_colind", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_AR", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_vel", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_aref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_b", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_aref", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_state", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "iefc_force", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_state", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "efc_force", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "ifrc_constraint", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "signature", - "c_type": "uint64_t", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjdata.h", - "line": 102 - }, - "mjvPerturb": { - "name": "mjvPerturb", - "fields": [ - { - "name": "select", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flexselect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "skinselect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "active", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "active2", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "refpos", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "refquat", - "c_type": "mjtNum [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "refselpos", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "localpos", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "localmass", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "scale", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 167 - }, - "mjvCamera": { - "name": "mjvCamera", - "fields": [ - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fixedcamid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "trackbodyid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "lookat", - "c_type": "mjtNum [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "distance", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "azimuth", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "elevation", - "c_type": "mjtNum", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "orthographic", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 185 - }, - "mjvGLCamera": { - "name": "mjvGLCamera", - "fields": [ - { - "name": "pos", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "forward", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "up", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "frustum_center", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "frustum_width", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "frustum_bottom", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "frustum_top", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "frustum_near", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "frustum_far", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "orthographic", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 205 - }, - "mjvGeom": { - "name": "mjvGeom", - "fields": [ - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dataid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "objtype", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "objid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "category", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "matid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "texcoord", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "segid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "size", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "pos", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "mat", - "c_type": "float [9]", - "array_dim": 9, - "is_pointer": false, - "doc": "" - }, - { - "name": "rgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "emission", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "specular", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shininess", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "reflectance", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "label", - "c_type": "char [100]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "camdist", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "modelrbound", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "transparent", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 227 - }, - "mjvLight": { - "name": "mjvLight", - "fields": [ - { - "name": "id", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pos", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "dir", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "texid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "attenuation", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "cutoff", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "exponent", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ambient", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "diffuse", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "specular", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "headlight", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "castshadow", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "bulbradius", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "intensity", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "range", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 262 - }, - "mjvOption": { - "name": "mjvOption", - "fields": [ - { - "name": "label", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "frame", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "geomgroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "sitegroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "jointgroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "tendongroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "actuatorgroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "flexgroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "skingroup", - "c_type": "mjtByte [6]", - "array_dim": 6, - "is_pointer": false, - "doc": "" - }, - { - "name": "flags", - "c_type": "mjtByte [31]", - "array_dim": 31, - "is_pointer": false, - "doc": "" - }, - { - "name": "bvh_depth", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flex_layer", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 285 - }, - "mjvScene": { - "name": "mjvScene", - "fields": [ - { - "name": "maxgeom", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "ngeom", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "geoms", - "c_type": "mjvGeom *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geomorder", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "nflex", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flexedgeadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedgenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexfaceadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexfacenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexfaceused", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexedge", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvert", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexface", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexnormal", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flextexcoord", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "flexvertopt", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flexedgeopt", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flexfaceopt", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flexskinopt", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskin", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "skinfacenum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skinvertadr", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skinvertnum", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skinvert", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skinnormal", - "c_type": "float *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "nlight", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "lights", - "c_type": "mjvLight [100]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "camera", - "c_type": "mjvGLCamera [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "enabletransform", - "c_type": "mjtByte", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "translate", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "rotate", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "scale", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "stereo", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flags", - "c_type": "mjtByte [11]", - "array_dim": 11, - "is_pointer": false, - "doc": "" - }, - { - "name": "framewidth", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "framergb", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "status", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 304 - }, - "mjvFigure": { - "name": "mjvFigure", - "fields": [ - { - "name": "flg_legend", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_ticklabel", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_extend", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_barplot", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_selection", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "flg_symmetric", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "linewidth", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gridwidth", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "gridsize", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "gridrgb", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "figurergba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "panergba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "legendrgba", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "textrgb", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "linergb", - "c_type": "float [100][3]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "range", - "c_type": "float [2][2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "xformat", - "c_type": "char [20]", - "array_dim": 20, - "is_pointer": false, - "doc": "" - }, - { - "name": "yformat", - "c_type": "char [20]", - "array_dim": 20, - "is_pointer": false, - "doc": "" - }, - { - "name": "minwidth", - "c_type": "char [20]", - "array_dim": 20, - "is_pointer": false, - "doc": "" - }, - { - "name": "title", - "c_type": "char [1000]", - "array_dim": 1000, - "is_pointer": false, - "doc": "" - }, - { - "name": "xlabel", - "c_type": "char [100]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "linename", - "c_type": "char [100][100]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "legendoffset", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "subplot", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "highlight", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "highlightid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "selection", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "linepnt", - "c_type": "int [100]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "linedata", - "c_type": "float [100][2002]", - "array_dim": 100, - "is_pointer": false, - "doc": "" - }, - { - "name": "xaxispixel", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "yaxispixel", - "c_type": "int [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "xaxisdata", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "yaxisdata", - "c_type": "float [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 367 - }, - "mjResource": { - "name": "mjResource", - "fields": [ - { - "name": "name", - "c_type": "char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "data", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "vfs", - "c_type": "mjVFS *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "timestamp", - "c_type": "char [512]", - "array_dim": 512, - "is_pointer": false, - "doc": "" - }, - { - "name": "provider", - "c_type": "const struct mjpResourceProvider *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 27 - }, - "mjpResourceProvider": { - "name": "mjpResourceProvider", - "fields": [ - { - "name": "prefix", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "open", - "c_type": "mjfOpenResource", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "read", - "c_type": "mjfReadResource", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "close", - "c_type": "mjfCloseResource", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mount", - "c_type": "mjfMountResource", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "unmount", - "c_type": "mjfUnmountResource", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "modified", - "c_type": "mjfResourceModified", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "data", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 63 - }, - "mjpDecoder": { - "name": "mjpDecoder", - "fields": [ - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "extension", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "can_decode", - "c_type": "mjfCanDecode", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "decode", - "c_type": "mjfDecode", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 84 - }, - "mjpEncoder": { - "name": "mjpEncoder", - "fields": [ - { - "name": "content_type", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "extension", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "encode", - "c_type": "mjfEncode", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "close_resource", - "c_type": "mjfCloseResource", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 100 - }, - "mjpPlugin": { - "name": "mjpPlugin", - "fields": [ - { - "name": "name", - "c_type": "const char *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "nattribute", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "attributes", - "c_type": "const char *const *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "capabilityflags", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "needstage", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nstate", - "c_type": "int (*)(const mjModel *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "nsensordata", - "c_type": "int (*)(const mjModel *, int, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "init", - "c_type": "int (*)(const mjModel *, mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "destroy", - "c_type": "void (*)(mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "copy", - "c_type": "void (*)(mjData *, const mjModel *, const mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "reset", - "c_type": "void (*)(const mjModel *, mjtNum *, void *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "compute", - "c_type": "void (*)(const mjModel *, mjData *, int, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "advance", - "c_type": "void (*)(const mjModel *, mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "visualize", - "c_type": "void (*)(const mjModel *, mjData *, const mjvOption *, mjvScene *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "actuator_act_dot", - "c_type": "void (*)(const mjModel *, mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sdf_distance", - "c_type": "mjtNum (*)(const mjtNum *, const mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sdf_gradient", - "c_type": "void (*)(mjtNum *, const mjtNum *, const mjData *, int)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sdf_staticdistance", - "c_type": "mjtNum (*)(const mjtNum *, const mjtNum *)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sdf_attribute", - "c_type": "void (*)(mjtNum *, const char **, const char **)", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sdf_aabb", - "c_type": "void (*)(mjtNum *, const mjtNum *)", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 117 - }, - "mjSDF": { - "name": "mjSDF", - "fields": [ - { - "name": "plugin", - "c_type": "const mjpPlugin **", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "id", - "c_type": "int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "mjtSDFType", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "relpos", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "relmat", - "c_type": "mjtNum *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "geomtype", - "c_type": "mjtGeom *", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 178 - }, - "mjrRect": { - "name": "mjrRect", - "fields": [ - { - "name": "left", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "bottom", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "width", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "height", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 112 - }, - "mjrVertexAttribute": { - "name": "mjrVertexAttribute", - "fields": [ - { - "name": "bytes", - "c_type": "const void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "usage", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 121 - }, - "mjrContext": { - "name": "mjrContext", - "fields": [ - { - "name": "lineWidth", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shadowClip", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shadowScale", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fogStart", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fogEnd", - "c_type": "float", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fogRGBA", - "c_type": "float [4]", - "array_dim": 4, - "is_pointer": false, - "doc": "" - }, - { - "name": "shadowSize", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offWidth", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offHeight", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offSamples", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "fontScale", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxWidth", - "c_type": "int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxHeight", - "c_type": "int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxSamples", - "c_type": "int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "offFBO", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offFBO_r", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offColor", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offColor_r", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offDepthStencil", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "offDepthStencil_r", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shadowFBO", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shadowTex", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxFBO", - "c_type": "unsigned int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxFBO_r", - "c_type": "unsigned int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxColor", - "c_type": "unsigned int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxColor_r", - "c_type": "unsigned int [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - }, - { - "name": "mat_texid", - "c_type": "int [10000]", - "array_dim": 10000, - "is_pointer": false, - "doc": "" - }, - { - "name": "mat_texuniform", - "c_type": "int [1000]", - "array_dim": 1000, - "is_pointer": false, - "doc": "" - }, - { - "name": "mat_texrepeat", - "c_type": "float [2000]", - "array_dim": 2000, - "is_pointer": false, - "doc": "" - }, - { - "name": "ntexture", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "textureType", - "c_type": "int [1000]", - "array_dim": 1000, - "is_pointer": false, - "doc": "" - }, - { - "name": "texture", - "c_type": "unsigned int [1000]", - "array_dim": 1000, - "is_pointer": false, - "doc": "" - }, - { - "name": "basePlane", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "baseMesh", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "baseHField", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "baseBuiltin", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "baseFontNormal", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "baseFontShadow", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "baseFontBig", - "c_type": "unsigned int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rangePlane", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rangeMesh", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rangeHField", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rangeBuiltin", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rangeFont", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nskin", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "skinvertVBO", - "c_type": "unsigned int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skinnormalVBO", - "c_type": "unsigned int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skintexcoordVBO", - "c_type": "unsigned int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "skinfaceVBO", - "c_type": "unsigned int *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "charWidth", - "c_type": "int [127]", - "array_dim": 127, - "is_pointer": false, - "doc": "" - }, - { - "name": "charWidthBig", - "c_type": "int [127]", - "array_dim": 127, - "is_pointer": false, - "doc": "" - }, - { - "name": "charHeight", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "charHeightBig", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "glInitialized", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "windowAvailable", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "windowSamples", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "windowStereo", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "windowDoublebuffer", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "currentBuffer", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "readPixelFormat", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "readDepthMap", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 138 - }, - "mjuiState": { - "name": "mjuiState", - "fields": [ - { - "name": "nrect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rect", - "c_type": "mjrRect [25]", - "array_dim": 25, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "left", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "right", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "middle", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "doubleclick", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "button", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "buttontime", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "x", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "y", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dx", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dy", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sx", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sy", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "control", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shift", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "alt", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "key", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "keytime", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mouserect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dragrect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dragbutton", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "dropcount", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "droppaths", - "c_type": "const char **", - "array_dim": null, - "is_pointer": true, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 123 - }, - "mjuiThemeSpacing": { - "name": "mjuiThemeSpacing", - "fields": [ - { - "name": "total", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "scroll", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "label", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "section", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "cornersect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "cornersep", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "itemside", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "itemmid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "itemver", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "texthor", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "textver", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "linescroll", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "samples", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 169 - }, - "mjuiThemeColor": { - "name": "mjuiThemeColor", - "fields": [ - { - "name": "master", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "thumb", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "secttitle", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "secttitle2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "secttitleuncheck", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "secttitleuncheck2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "secttitlecheck", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "secttitlecheck2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "sectfont", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "sectsymbol", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "sectpane", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "separator", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "separator2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "shortcut", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "fontactive", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "fontinactive", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "decorinactive", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "decorinactive2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "button", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "check", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "radio", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "select", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "select2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "slider", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "slider2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "edit", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "edit2", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - }, - { - "name": "cursor", - "c_type": "float [3]", - "array_dim": 3, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 189 - }, - "mjuiItemSingle": { - "name": "mjuiItemSingle", - "fields": [ - { - "name": "modifier", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shortcut", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 224 - }, - "mjuiItemMulti": { - "name": "mjuiItemMulti", - "fields": [ - { - "name": "nelem", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "name", - "c_type": "char [35][40]", - "array_dim": 35, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 230 - }, - "mjuiItemSlider": { - "name": "mjuiItemSlider", - "fields": [ - { - "name": "range", - "c_type": "double [2]", - "array_dim": 2, - "is_pointer": false, - "doc": "" - }, - { - "name": "divisions", - "c_type": "double", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 236 - }, - "mjuiItemEdit": { - "name": "mjuiItemEdit", - "fields": [ - { - "name": "nelem", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "range", - "c_type": "double [7][2]", - "array_dim": 7, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 242 - }, - "mjuiItem": { - "name": "mjuiItem", - "fields": [ - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "name", - "c_type": "char [40]", - "array_dim": 40, - "is_pointer": false, - "doc": "" - }, - { - "name": "state", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pdata", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "sectionid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "itemid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rect", - "c_type": "mjrRect", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "skip", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 248 - }, - "mjuiSection": { - "name": "mjuiSection", - "fields": [ - { - "name": "name", - "c_type": "char [40]", - "array_dim": 40, - "is_pointer": false, - "doc": "" - }, - { - "name": "state", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "modifier", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "shortcut", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "checkbox", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "nitem", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "item", - "c_type": "mjuiItem [200]", - "array_dim": 200, - "is_pointer": false, - "doc": "" - }, - { - "name": "rtitle", - "c_type": "mjrRect", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "rcontent", - "c_type": "mjrRect", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "lastclick", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 275 - }, - "mjUI": { - "name": "mjUI", - "fields": [ - { - "name": "spacing", - "c_type": "mjuiThemeSpacing", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "color", - "c_type": "mjuiThemeColor", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "predicate", - "c_type": "mjfItemEnable", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "userdata", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "rectid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "auxid", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "radiocol", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "width", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "height", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "maxheight", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "scroll", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mousesect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mouseitem", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mousehelp", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mouseclicks", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "mousesectcheck", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "editsect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "edititem", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "editcursor", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "editscroll", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "edittext", - "c_type": "char [300]", - "array_dim": 300, - "is_pointer": false, - "doc": "" - }, - { - "name": "editchanged", - "c_type": "mjuiItem *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "nsect", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "sect", - "c_type": "mjuiSection [10]", - "array_dim": 10, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 295 - }, - "mjuiDef": { - "name": "mjuiDef", - "fields": [ - { - "name": "type", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "name", - "c_type": "char [40]", - "array_dim": 40, - "is_pointer": false, - "doc": "" - }, - { - "name": "state", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - }, - { - "name": "pdata", - "c_type": "void *", - "array_dim": null, - "is_pointer": true, - "doc": "" - }, - { - "name": "other", - "c_type": "char [300]", - "array_dim": 300, - "is_pointer": false, - "doc": "" - }, - { - "name": "otherint", - "c_type": "int", - "array_dim": null, - "is_pointer": false, - "doc": "" - } - ], - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 335 - } - }, - "defines": { - "mjMINVAL": { - "value": "1E-15", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjtype.h", - "line": 27 - }, - "mjPI": { - "value": "3.14159265358979323846", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 24 - }, - "mjMAXVAL": { - "value": "1E+10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 25 - }, - "mjMINMU": { - "value": "1E-5", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 26 - }, - "mjMINIMP": { - "value": "0.0001", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 27 - }, - "mjMAXIMP": { - "value": "0.9999", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 28 - }, - "mjMAXCONPAIR": { - "value": "50", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 29 - }, - "mjMAXTREEDEPTH": { - "value": "50", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 30 - }, - "mjMAXFLEXNODES": { - "value": "27", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 31 - }, - "mjMINAWAKE": { - "value": "10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 32 - }, - "mjNEQDATA": { - "value": "11", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 37 - }, - "mjNDYN": { - "value": "10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 38 - }, - "mjNGAIN": { - "value": "10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 39 - }, - "mjNBIAS": { - "value": "10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 40 - }, - "mjNFLUID": { - "value": "12", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 41 - }, - "mjNREF": { - "value": "2", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 42 - }, - "mjNIMP": { - "value": "5", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 43 - }, - "mjNPOLY": { - "value": "2", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 44 - }, - "mjNSENS": { - "value": "3", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 45 - }, - "mjNSOLVER": { - "value": "200", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 46 - }, - "mjNISLAND": { - "value": "20", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmodel.h", - "line": 47 - }, - "MJOPTION_FIELDS": { - "value": "X ( mjtNum , timestep , 1 ) X ( mjtNum , impratio , 1 ) X ( mjtNum , tolerance , 1 ) X ( mjtNum , ls_tolerance , 1 ) X ( mjtNum , noslip_tolerance , 1 ) X ( mjtNum , ccd_tolerance , 1 ) X ( mjtNum , sleep_tolerance , 1 ) XVEC ( mjtNum , gravity , 3 ) XVEC ( mjtNum , wind , 3 ) XVEC ( mjtNum , magnetic , 3 ) X ( mjtNum , density , 1 ) X ( mjtNum , viscosity , 1 ) X ( mjtNum , o_margin , 1 ) XVEC ( mjtNum , o_solref , mjNREF ) XVEC ( mjtNum , o_solimp , mjNIMP ) XVEC ( mjtNum , o_friction , 5 ) X ( int , integrator , 1 ) X ( int , cone , 1 ) X ( int , jacobian , 1 ) X ( int , solver , 1 ) X ( int , iterations , 1 ) X ( int , ls_iterations , 1 ) X ( int , noslip_iterations , 1 ) X ( int , ccd_iterations , 1 ) X ( int , disableflags , 1 ) X ( int , enableflags , 1 ) X ( int , disableactuator , 1 ) X ( int , sdf_initpoints , 1 ) X ( int , sdf_iterations , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 23 - }, - "MJSTATISTIC_FIELDS": { - "value": "X ( meaninertia , 1 ) X ( meanmass , 1 ) X ( meansize , 1 ) X ( extent , 1 ) XVEC ( center , 3 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 58 - }, - "MJVISUAL_GLOBAL_FIELDS": { - "value": "X ( int , cameraid , 1 ) X ( int , orthographic , 1 ) X ( float , fovy , 1 ) X ( float , ipd , 1 ) X ( float , azimuth , 1 ) X ( float , elevation , 1 ) X ( float , linewidth , 1 ) X ( float , glow , 1 ) X ( float , realtime , 1 ) X ( int , offwidth , 1 ) X ( int , offheight , 1 ) X ( int , ellipsoidinertia , 1 ) X ( int , bvactive , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 69 - }, - "MJVISUAL_QUALITY_FIELDS": { - "value": "X ( int , shadowsize , 1 ) X ( int , offsamples , 1 ) X ( int , numslices , 1 ) X ( int , numstacks , 1 ) X ( int , numquads , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 84 - }, - "MJVISUAL_HEADLIGHT_FIELDS": { - "value": "XVEC ( float , ambient , 3 ) XVEC ( float , diffuse , 3 ) XVEC ( float , specular , 3 ) X ( int , active , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 91 - }, - "MJVISUAL_MAP_FIELDS": { - "value": "X ( float , stiffness , 1 ) X ( float , stiffnessrot , 1 ) X ( float , force , 1 ) X ( float , torque , 1 ) X ( float , alpha , 1 ) X ( float , fogstart , 1 ) X ( float , fogend , 1 ) X ( float , znear , 1 ) X ( float , zfar , 1 ) X ( float , haze , 1 ) X ( float , shadowclip , 1 ) X ( float , shadowscale , 1 ) X ( float , actuatortendon , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 97 - }, - "MJVISUAL_SCALE_FIELDS": { - "value": "X ( float , forcewidth , 1 ) X ( float , contactwidth , 1 ) X ( float , contactheight , 1 ) X ( float , connect , 1 ) X ( float , com , 1 ) X ( float , camera , 1 ) X ( float , light , 1 ) X ( float , selectpoint , 1 ) X ( float , jointlength , 1 ) X ( float , jointwidth , 1 ) X ( float , actuatorlength , 1 ) X ( float , actuatorwidth , 1 ) X ( float , framelength , 1 ) X ( float , framewidth , 1 ) X ( float , constraint , 1 ) X ( float , slidercrank , 1 ) X ( float , frustum , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 112 - }, - "MJVISUAL_RGBA_FIELDS": { - "value": "XVEC ( float , fog , 4 ) XVEC ( float , haze , 4 ) XVEC ( float , force , 4 ) XVEC ( float , inertia , 4 ) XVEC ( float , joint , 4 ) XVEC ( float , actuator , 4 ) XVEC ( float , actuatornegative , 4 ) XVEC ( float , actuatorpositive , 4 ) XVEC ( float , com , 4 ) XVEC ( float , camera , 4 ) XVEC ( float , light , 4 ) XVEC ( float , selectpoint , 4 ) XVEC ( float , connect , 4 ) XVEC ( float , contactpoint , 4 ) XVEC ( float , contactforce , 4 ) XVEC ( float , contactfriction , 4 ) XVEC ( float , contacttorque , 4 ) XVEC ( float , contactgap , 4 ) XVEC ( float , rangefinder , 4 ) XVEC ( float , constraint , 4 ) XVEC ( float , slidercrank , 4 ) XVEC ( float , crankbroken , 4 ) XVEC ( float , frustum , 4 ) XVEC ( float , bv , 4 ) XVEC ( float , bvactive , 4 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 131 - }, - "MJMODEL_SIZES": { - "value": "X ( nq ) X ( nv ) X ( nu ) X ( na ) X ( nbody ) X ( nbvh ) X ( nbvhstatic ) X ( nbvhdynamic ) X ( noct ) X ( njnt ) X ( ntree ) X ( nM ) X ( nB ) X ( nC ) X ( nD ) X ( ngeom ) X ( nsite ) X ( ncam ) X ( nlight ) X ( nflex ) X ( nflexnode ) X ( nflexvert ) X ( nflexedge ) X ( nflexelem ) X ( nflexelemdata ) X ( nflexstiffness ) X ( nflexbending ) X ( nflexelemedge ) X ( nflexshelldata ) X ( nflexevpair ) X ( nflextexcoord ) X ( nJfe ) X ( nJfv ) X ( nmesh ) X ( nmeshvert ) X ( nmeshnormal ) X ( nmeshtexcoord ) X ( nmeshface ) X ( nmeshgraph ) X ( nmeshpoly ) X ( nmeshpolyvert ) X ( nmeshpolymap ) X ( nskin ) X ( nskinvert ) X ( nskintexvert ) X ( nskinface ) X ( nskinbone ) X ( nskinbonevert ) X ( nhfield ) X ( nhfielddata ) X ( ntex ) X ( ntexdata ) X ( nmat ) X ( npair ) X ( nexclude ) X ( neq ) X ( ntendon ) X ( nJten ) X ( nwrap ) X ( nsensor ) X ( nnumeric ) X ( nnumericdata ) X ( ntext ) X ( ntextdata ) X ( ntuple ) X ( ntupledata ) X ( nkey ) X ( nmocap ) X ( nplugin ) X ( npluginattr ) X ( nuser_body ) X ( nuser_jnt ) X ( nuser_geom ) X ( nuser_site ) X ( nuser_cam ) X ( nuser_tendon ) X ( nuser_actuator ) X ( nuser_sensor ) X ( nnames ) X ( npaths ) X ( nnames_map ) X ( nJmom ) X ( ngravcomp ) X ( nemax ) X ( njmax ) X ( nconmax ) X ( nuserdata ) X ( nsensordata ) X ( npluginstate ) X ( nhistory ) X ( narena ) X ( nbuffer )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 162 - }, - "MJMODEL_POINTERS_PREAMBLE": { - "value": "( m ) int nuser_body = m -> nuser_body ; int nuser_jnt = m -> nuser_jnt ; int nuser_geom = m -> nuser_geom ; int nuser_site = m -> nuser_site ; int nuser_cam = m -> nuser_cam ; int nuser_tendon = m -> nuser_tendon ; int nuser_actuator = m -> nuser_actuator ; int nuser_sensor = m -> nuser_sensor ; int nq = m -> nq ; int nv = m -> nv ; int na = m -> na ; int nu = m -> nu ; int nmocap = m -> nmocap ;", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 260 - }, - "MJ_M": { - "value": "( n ) n", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 277 - }, - "MJMODEL_POINTERS_BODY": { - "value": "X ( int , body_parentid , nbody , 1 ) X ( int , body_rootid , nbody , 1 ) X ( int , body_weldid , nbody , 1 ) X ( int , body_mocapid , nbody , 1 ) X ( int , body_jntnum , nbody , 1 ) X ( int , body_jntadr , nbody , 1 ) X ( int , body_dofnum , nbody , 1 ) X ( int , body_dofadr , nbody , 1 ) X ( int , body_treeid , nbody , 1 ) X ( int , body_geomnum , nbody , 1 ) X ( int , body_geomadr , nbody , 1 ) X ( mjtByte , body_simple , nbody , 1 ) X ( mjtByte , body_sameframe , nbody , 1 ) X ( mjtNum , body_pos , nbody , 3 ) X ( mjtNum , body_quat , nbody , 4 ) X ( mjtNum , body_ipos , nbody , 3 ) X ( mjtNum , body_iquat , nbody , 4 ) X ( mjtNum , body_mass , nbody , 1 ) X ( mjtNum , body_subtreemass , nbody , 1 ) X ( mjtNum , body_inertia , nbody , 3 ) X ( mjtNum , body_invweight0 , nbody , 2 ) X ( mjtNum , body_gravcomp , nbody , 1 ) X ( mjtNum , body_margin , nbody , 1 ) X ( mjtNum , body_user , nbody , MJ_M ( nuser_body ) ) X ( int , body_plugin , nbody , 1 ) X ( int , body_contype , nbody , 1 ) X ( int , body_conaffinity , nbody , 1 ) X ( int , body_bvhadr , nbody , 1 ) X ( int , body_bvhnum , nbody , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 283 - }, - "MJMODEL_POINTERS_JOINT": { - "value": "X ( int , jnt_type , njnt , 1 ) X ( int , jnt_qposadr , njnt , 1 ) X ( int , jnt_dofadr , njnt , 1 ) X ( int , jnt_bodyid , njnt , 1 ) X ( int , jnt_actuatorid , njnt , 1 ) X ( int , jnt_group , njnt , 1 ) X ( mjtBool , jnt_limited , njnt , 1 ) X ( mjtBool , jnt_actfrclimited , njnt , 1 ) X ( mjtBool , jnt_actgravcomp , njnt , 1 ) X ( mjtNum , jnt_solref , njnt , mjNREF ) X ( mjtNum , jnt_solimp , njnt , mjNIMP ) X ( mjtNum , jnt_pos , njnt , 3 ) X ( mjtNum , jnt_axis , njnt , 3 ) X ( mjtNum , jnt_stiffness , njnt , 1 ) X ( mjtNum , jnt_stiffnesspoly , njnt , mjNPOLY ) X ( mjtNum , jnt_range , njnt , 2 ) X ( mjtNum , jnt_actfrcrange , njnt , 2 ) X ( mjtNum , jnt_margin , njnt , 1 ) X ( mjtNum , jnt_user , njnt , MJ_M ( nuser_jnt ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 314 - }, - "MJMODEL_POINTERS_DOF": { - "value": "X ( int , dof_bodyid , nv , 1 ) X ( int , dof_jntid , nv , 1 ) X ( int , dof_parentid , nv , 1 ) X ( int , dof_treeid , nv , 1 ) X ( int , dof_Madr , nv , 1 ) X ( int , dof_simplenum , nv , 1 ) X ( mjtNum , dof_solref , nv , mjNREF ) X ( mjtNum , dof_solimp , nv , mjNIMP ) X ( mjtNum , dof_frictionloss , nv , 1 ) X ( mjtNum , dof_armature , nv , 1 ) X ( mjtNum , dof_damping , nv , 1 ) X ( mjtNum , dof_dampingpoly , nv , mjNPOLY ) X ( mjtNum , dof_invweight0 , nv , 1 ) X ( mjtNum , dof_M0 , nv , 1 ) X ( mjtNum , dof_length , nv , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 335 - }, - "MJMODEL_POINTERS_TREE": { - "value": "X ( int , tree_bodyadr , ntree , 1 ) X ( int , tree_bodynum , ntree , 1 ) X ( int , tree_dofadr , ntree , 1 ) X ( int , tree_dofnum , ntree , 1 ) X ( int , tree_sleep_policy , ntree , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 352 - }, - "MJMODEL_POINTERS_GEOM": { - "value": "X ( int , geom_type , ngeom , 1 ) X ( int , geom_contype , ngeom , 1 ) X ( int , geom_conaffinity , ngeom , 1 ) X ( int , geom_condim , ngeom , 1 ) X ( int , geom_bodyid , ngeom , 1 ) X ( int , geom_dataid , ngeom , 1 ) X ( int , geom_matid , ngeom , 1 ) X ( int , geom_group , ngeom , 1 ) X ( int , geom_priority , ngeom , 1 ) X ( int , geom_plugin , ngeom , 1 ) X ( mjtByte , geom_sameframe , ngeom , 1 ) X ( mjtNum , geom_solmix , ngeom , 1 ) X ( mjtNum , geom_solref , ngeom , mjNREF ) X ( mjtNum , geom_solimp , ngeom , mjNIMP ) X ( mjtNum , geom_size , ngeom , 3 ) X ( mjtNum , geom_aabb , ngeom , 6 ) X ( mjtNum , geom_rbound , ngeom , 1 ) X ( mjtNum , geom_pos , ngeom , 3 ) X ( mjtNum , geom_quat , ngeom , 4 ) X ( mjtNum , geom_friction , ngeom , 3 ) X ( mjtNum , geom_margin , ngeom , 1 ) X ( mjtNum , geom_gap , ngeom , 1 ) XNV ( mjtNum , geom_fluid , ngeom , mjNFLUID ) X ( mjtNum , geom_user , ngeom , MJ_M ( nuser_geom ) ) X ( float , geom_rgba , ngeom , 4 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 359 - }, - "MJMODEL_POINTERS_SITE": { - "value": "X ( int , site_type , nsite , 1 ) X ( int , site_bodyid , nsite , 1 ) X ( int , site_matid , nsite , 1 ) X ( int , site_group , nsite , 1 ) X ( mjtByte , site_sameframe , nsite , 1 ) X ( mjtNum , site_size , nsite , 3 ) X ( mjtNum , site_pos , nsite , 3 ) X ( mjtNum , site_quat , nsite , 4 ) X ( mjtNum , site_user , nsite , MJ_M ( nuser_site ) ) X ( float , site_rgba , nsite , 4 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 386 - }, - "MJMODEL_POINTERS_CAMERA": { - "value": "X ( int , cam_mode , ncam , 1 ) X ( int , cam_bodyid , ncam , 1 ) X ( int , cam_targetbodyid , ncam , 1 ) X ( mjtNum , cam_pos , ncam , 3 ) X ( mjtNum , cam_quat , ncam , 4 ) X ( mjtNum , cam_poscom0 , ncam , 3 ) X ( mjtNum , cam_pos0 , ncam , 3 ) X ( mjtNum , cam_mat0 , ncam , 9 ) X ( int , cam_projection , ncam , 1 ) X ( mjtNum , cam_fovy , ncam , 1 ) X ( mjtNum , cam_ipd , ncam , 1 ) X ( int , cam_resolution , ncam , 2 ) X ( int , cam_output , ncam , 1 ) X ( float , cam_sensorsize , ncam , 2 ) X ( float , cam_intrinsic , ncam , 4 ) X ( mjtNum , cam_user , ncam , MJ_M ( nuser_cam ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 398 - }, - "MJMODEL_POINTERS_LIGHT": { - "value": "X ( int , light_mode , nlight , 1 ) X ( int , light_bodyid , nlight , 1 ) X ( int , light_targetbodyid , nlight , 1 ) X ( int , light_type , nlight , 1 ) X ( int , light_texid , nlight , 1 ) X ( mjtBool , light_castshadow , nlight , 1 ) X ( float , light_bulbradius , nlight , 1 ) X ( float , light_intensity , nlight , 1 ) X ( float , light_range , nlight , 1 ) X ( mjtBool , light_active , nlight , 1 ) X ( mjtNum , light_pos , nlight , 3 ) X ( mjtNum , light_dir , nlight , 3 ) X ( mjtNum , light_poscom0 , nlight , 3 ) X ( mjtNum , light_pos0 , nlight , 3 ) X ( mjtNum , light_dir0 , nlight , 3 ) X ( float , light_attenuation , nlight , 3 ) X ( float , light_cutoff , nlight , 1 ) X ( float , light_exponent , nlight , 1 ) X ( float , light_ambient , nlight , 3 ) X ( float , light_diffuse , nlight , 3 ) X ( float , light_specular , nlight , 3 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 416 - }, - "MJMODEL_POINTERS_FLEX": { - "value": "X ( int , flex_contype , nflex , 1 ) X ( int , flex_conaffinity , nflex , 1 ) X ( int , flex_condim , nflex , 1 ) X ( int , flex_priority , nflex , 1 ) X ( mjtNum , flex_solmix , nflex , 1 ) X ( mjtNum , flex_solref , nflex , mjNREF ) X ( mjtNum , flex_solimp , nflex , mjNIMP ) X ( mjtNum , flex_friction , nflex , 3 ) X ( mjtNum , flex_margin , nflex , 1 ) X ( mjtNum , flex_gap , nflex , 1 ) X ( mjtBool , flex_internal , nflex , 1 ) X ( int , flex_selfcollide , nflex , 1 ) X ( int , flex_activelayers , nflex , 1 ) X ( int , flex_passive , nflex , 1 ) X ( int , flex_dim , nflex , 1 ) X ( int , flex_matid , nflex , 1 ) X ( int , flex_group , nflex , 1 ) X ( int , flex_interp , nflex , 1 ) X ( int , flex_cellnum , nflex , 3 ) X ( int , flex_nodeadr , nflex , 1 ) X ( int , flex_nodenum , nflex , 1 ) X ( int , flex_vertadr , nflex , 1 ) X ( int , flex_vertnum , nflex , 1 ) X ( int , flex_edgeadr , nflex , 1 ) X ( int , flex_edgenum , nflex , 1 ) X ( int , flex_elemadr , nflex , 1 ) X ( int , flex_elemnum , nflex , 1 ) X ( int , flex_elemdataadr , nflex , 1 ) X ( int , flex_stiffnessadr , nflex , 1 ) X ( int , flex_elemedgeadr , nflex , 1 ) X ( int , flex_bendingadr , nflex , 1 ) X ( int , flex_shellnum , nflex , 1 ) X ( int , flex_shelldataadr , nflex , 1 ) X ( int , flex_evpairadr , nflex , 1 ) X ( int , flex_evpairnum , nflex , 1 ) X ( int , flex_texcoordadr , nflex , 1 ) X ( int , flex_nodebodyid , nflexnode , 1 ) X ( int , flex_vertbodyid , nflexvert , 1 ) X ( int , flex_vertedgeadr , nflexvert , 1 ) X ( int , flex_vertedgenum , nflexvert , 1 ) X ( int , flex_vertedge , nflexedge , 2 ) X ( int , flex_edge , nflexedge , 2 ) X ( int , flex_edgeflap , nflexedge , 2 ) X ( int , flex_elem , nflexelemdata , 1 ) X ( int , flex_elemtexcoord , nflexelemdata , 1 ) X ( int , flex_elemedge , nflexelemedge , 1 ) X ( int , flex_elemlayer , nflexelem , 1 ) X ( int , flex_shell , nflexshelldata , 1 ) X ( int , flex_evpair , nflexevpair , 2 ) X ( mjtNum , flex_vert , nflexvert , 3 ) X ( mjtNum , flex_vert0 , nflexvert , 3 ) X ( mjtNum , flex_vertmetric , nflexvert , 4 ) X ( mjtNum , flex_node , nflexnode , 3 ) X ( mjtNum , flex_node0 , nflexnode , 3 ) X ( mjtNum , flexedge_length0 , nflexedge , 1 ) X ( mjtNum , flexedge_invweight0 , nflexedge , 1 ) X ( mjtNum , flex_radius , nflex , 1 ) X ( mjtNum , flex_size , nflex , 3 ) X ( mjtNum , flex_stiffness , nflexstiffness , 1 ) X ( mjtNum , flex_bending , nflexbending , 1 ) X ( mjtNum , flex_damping , nflex , 1 ) X ( mjtNum , flex_edgestiffness , nflex , 1 ) X ( mjtNum , flex_edgedamping , nflex , 1 ) X ( int , flex_edgeequality , nflex , 1 ) X ( mjtBool , flex_rigid , nflex , 1 ) X ( mjtBool , flexedge_rigid , nflexedge , 1 ) X ( mjtBool , flex_centered , nflex , 1 ) X ( mjtBool , flex_flatskin , nflex , 1 ) X ( int , flex_bvhadr , nflex , 1 ) X ( int , flex_bvhnum , nflex , 1 ) X ( int , flexedge_J_rownnz , nflexedge , 1 ) X ( int , flexedge_J_rowadr , nflexedge , 1 ) X ( int , flexedge_J_colind , nJfe , 1 ) X ( int , flexvert_J_rownnz , nflexvert , 2 ) X ( int , flexvert_J_rowadr , nflexvert , 2 ) X ( int , flexvert_J_colind , nJfv , 2 ) X ( float , flex_rgba , nflex , 4 ) X ( float , flex_texcoord , nflextexcoord , 2 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 439 - }, - "MJMODEL_POINTERS_MESH": { - "value": "X ( int , mesh_vertadr , nmesh , 1 ) X ( int , mesh_vertnum , nmesh , 1 ) X ( int , mesh_faceadr , nmesh , 1 ) X ( int , mesh_facenum , nmesh , 1 ) X ( int , mesh_bvhadr , nmesh , 1 ) X ( int , mesh_bvhnum , nmesh , 1 ) X ( int , mesh_octadr , nmesh , 1 ) X ( int , mesh_octnum , nmesh , 1 ) X ( int , mesh_normaladr , nmesh , 1 ) X ( int , mesh_normalnum , nmesh , 1 ) X ( int , mesh_texcoordadr , nmesh , 1 ) X ( int , mesh_texcoordnum , nmesh , 1 ) X ( int , mesh_graphadr , nmesh , 1 ) XNV ( float , mesh_vert , nmeshvert , 3 ) XNV ( float , mesh_normal , nmeshnormal , 3 ) XNV ( float , mesh_texcoord , nmeshtexcoord , 2 ) XNV ( int , mesh_face , nmeshface , 3 ) XNV ( int , mesh_facenormal , nmeshface , 3 ) XNV ( int , mesh_facetexcoord , nmeshface , 3 ) XNV ( int , mesh_graph , nmeshgraph , 1 ) X ( mjtNum , mesh_scale , nmesh , 3 ) X ( mjtNum , mesh_pos , nmesh , 3 ) X ( mjtNum , mesh_quat , nmesh , 4 ) X ( int , mesh_pathadr , nmesh , 1 ) XNV ( int , mesh_polynum , nmesh , 1 ) XNV ( int , mesh_polyadr , nmesh , 1 ) XNV ( mjtNum , mesh_polynormal , nmeshpoly , 3 ) XNV ( int , mesh_polyvertadr , nmeshpoly , 1 ) XNV ( int , mesh_polyvertnum , nmeshpoly , 1 ) XNV ( int , mesh_polyvert , nmeshpolyvert , 1 ) XNV ( int , mesh_polymapadr , nmeshvert , 1 ) XNV ( int , mesh_polymapnum , nmeshvert , 1 ) XNV ( int , mesh_polymap , nmeshpolymap , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 519 - }, - "MJMODEL_POINTERS_SKIN": { - "value": "X ( int , skin_matid , nskin , 1 ) X ( int , skin_group , nskin , 1 ) X ( float , skin_rgba , nskin , 4 ) X ( float , skin_inflate , nskin , 1 ) X ( int , skin_vertadr , nskin , 1 ) X ( int , skin_vertnum , nskin , 1 ) X ( int , skin_texcoordadr , nskin , 1 ) X ( int , skin_faceadr , nskin , 1 ) X ( int , skin_facenum , nskin , 1 ) X ( int , skin_boneadr , nskin , 1 ) X ( int , skin_bonenum , nskin , 1 ) X ( float , skin_vert , nskinvert , 3 ) X ( float , skin_texcoord , nskintexvert , 2 ) X ( int , skin_face , nskinface , 3 ) X ( int , skin_bonevertadr , nskinbone , 1 ) X ( int , skin_bonevertnum , nskinbone , 1 ) X ( float , skin_bonebindpos , nskinbone , 3 ) X ( float , skin_bonebindquat , nskinbone , 4 ) X ( int , skin_bonebodyid , nskinbone , 1 ) X ( int , skin_bonevertid , nskinbonevert , 1 ) X ( float , skin_bonevertweight , nskinbonevert , 1 ) X ( int , skin_pathadr , nskin , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 554 - }, - "MJMODEL_POINTERS_HFIELD": { - "value": "X ( mjtNum , hfield_size , nhfield , 4 ) X ( int , hfield_nrow , nhfield , 1 ) X ( int , hfield_ncol , nhfield , 1 ) X ( int , hfield_adr , nhfield , 1 ) XNV ( float , hfield_data , nhfielddata , 1 ) X ( int , hfield_pathadr , nhfield , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 578 - }, - "MJMODEL_POINTERS_TEXTURE": { - "value": "X ( int , tex_type , ntex , 1 ) X ( int , tex_colorspace , ntex , 1 ) X ( int , tex_height , ntex , 1 ) X ( int , tex_width , ntex , 1 ) X ( int , tex_nchannel , ntex , 1 ) X ( mjtSize , tex_adr , ntex , 1 ) XNV ( mjtByte , tex_data , ntexdata , 1 ) X ( int , tex_pathadr , ntex , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 586 - }, - "MJMODEL_POINTERS_MATERIAL": { - "value": "X ( int , mat_texid , nmat , mjNTEXROLE ) X ( mjtBool , mat_texuniform , nmat , 1 ) X ( float , mat_texrepeat , nmat , 2 ) X ( float , mat_emission , nmat , 1 ) X ( float , mat_specular , nmat , 1 ) X ( float , mat_shininess , nmat , 1 ) X ( float , mat_reflectance , nmat , 1 ) X ( float , mat_metallic , nmat , 1 ) X ( float , mat_roughness , nmat , 1 ) X ( float , mat_rgba , nmat , 4 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 596 - }, - "MJMODEL_POINTERS_PAIR": { - "value": "X ( int , pair_dim , npair , 1 ) X ( int , pair_geom1 , npair , 1 ) X ( int , pair_geom2 , npair , 1 ) X ( int , pair_signature , npair , 1 ) X ( mjtNum , pair_solref , npair , mjNREF ) X ( mjtNum , pair_solreffriction , npair , mjNREF ) X ( mjtNum , pair_solimp , npair , mjNIMP ) X ( mjtNum , pair_margin , npair , 1 ) X ( mjtNum , pair_gap , npair , 1 ) X ( mjtNum , pair_friction , npair , 5 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 608 - }, - "MJMODEL_POINTERS_EXCLUDE": { - "value": "X ( int , exclude_signature , nexclude , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 620 - }, - "MJMODEL_POINTERS_EQUALITY": { - "value": "X ( int , eq_type , neq , 1 ) X ( int , eq_obj1id , neq , 1 ) X ( int , eq_obj2id , neq , 1 ) X ( int , eq_objtype , neq , 1 ) X ( mjtBool , eq_active0 , neq , 1 ) X ( mjtNum , eq_solref , neq , mjNREF ) X ( mjtNum , eq_solimp , neq , mjNIMP ) X ( mjtNum , eq_data , neq , mjNEQDATA )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 623 - }, - "MJMODEL_POINTERS_TENDON": { - "value": "X ( int , tendon_adr , ntendon , 1 ) X ( int , tendon_num , ntendon , 1 ) X ( int , tendon_matid , ntendon , 1 ) X ( int , tendon_actuatorid , ntendon , 1 ) X ( int , tendon_group , ntendon , 1 ) X ( int , tendon_treenum , ntendon , 1 ) X ( int , tendon_treeid , ntendon , 2 ) X ( int , ten_J_rownnz , ntendon , 1 ) X ( int , ten_J_rowadr , ntendon , 1 ) X ( int , ten_J_colind , nJten , 1 ) X ( mjtBool , tendon_limited , ntendon , 1 ) X ( mjtBool , tendon_actfrclimited , ntendon , 1 ) X ( mjtNum , tendon_width , ntendon , 1 ) X ( mjtNum , tendon_solref_lim , ntendon , mjNREF ) X ( mjtNum , tendon_solimp_lim , ntendon , mjNIMP ) X ( mjtNum , tendon_solref_fri , ntendon , mjNREF ) X ( mjtNum , tendon_solimp_fri , ntendon , mjNIMP ) X ( mjtNum , tendon_range , ntendon , 2 ) X ( mjtNum , tendon_actfrcrange , ntendon , 2 ) X ( mjtNum , tendon_margin , ntendon , 1 ) X ( mjtNum , tendon_stiffness , ntendon , 1 ) X ( mjtNum , tendon_stiffnesspoly , ntendon , mjNPOLY ) X ( mjtNum , tendon_damping , ntendon , 1 ) X ( mjtNum , tendon_dampingpoly , ntendon , mjNPOLY ) X ( mjtNum , tendon_armature , ntendon , 1 ) X ( mjtNum , tendon_frictionloss , ntendon , 1 ) X ( mjtNum , tendon_lengthspring , ntendon , 2 ) X ( mjtNum , tendon_length0 , ntendon , 1 ) X ( mjtNum , tendon_invweight0 , ntendon , 1 ) X ( mjtNum , tendon_user , ntendon , MJ_M ( nuser_tendon ) ) X ( float , tendon_rgba , ntendon , 4 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 633 - }, - "MJMODEL_POINTERS_ACTUATOR": { - "value": "X ( int , actuator_trntype , nu , 1 ) X ( int , actuator_dyntype , nu , 1 ) X ( int , actuator_gaintype , nu , 1 ) X ( int , actuator_biastype , nu , 1 ) X ( int , actuator_trnid , nu , 2 ) X ( mjtNum , actuator_damping , nu , 1 ) X ( mjtNum , actuator_dampingpoly , nu , mjNPOLY ) X ( mjtNum , actuator_armature , nu , 1 ) X ( int , actuator_actadr , nu , 1 ) X ( int , actuator_actnum , nu , 1 ) X ( int , actuator_group , nu , 1 ) X ( int , actuator_history , nu , 2 ) X ( int , actuator_historyadr , nu , 1 ) X ( mjtNum , actuator_delay , nu , 1 ) X ( mjtBool , actuator_ctrllimited , nu , 1 ) X ( mjtBool , actuator_forcelimited , nu , 1 ) X ( mjtBool , actuator_actlimited , nu , 1 ) X ( mjtNum , actuator_dynprm , nu , mjNDYN ) X ( mjtNum , actuator_gainprm , nu , mjNGAIN ) X ( mjtNum , actuator_biasprm , nu , mjNBIAS ) X ( mjtBool , actuator_actearly , nu , 1 ) X ( mjtNum , actuator_ctrlrange , nu , 2 ) X ( mjtNum , actuator_forcerange , nu , 2 ) X ( mjtNum , actuator_actrange , nu , 2 ) X ( mjtNum , actuator_gear , nu , 6 ) X ( mjtNum , actuator_cranklength , nu , 1 ) X ( mjtNum , actuator_acc0 , nu , 1 ) X ( mjtNum , actuator_length0 , nu , 1 ) X ( mjtNum , actuator_lengthrange , nu , 2 ) X ( mjtNum , actuator_user , nu , MJ_M ( nuser_actuator ) ) X ( int , actuator_plugin , nu , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 666 - }, - "MJMODEL_POINTERS_SENSOR": { - "value": "X ( int , sensor_type , nsensor , 1 ) X ( int , sensor_datatype , nsensor , 1 ) X ( int , sensor_needstage , nsensor , 1 ) X ( int , sensor_objtype , nsensor , 1 ) X ( int , sensor_objid , nsensor , 1 ) X ( int , sensor_reftype , nsensor , 1 ) X ( int , sensor_refid , nsensor , 1 ) X ( int , sensor_intprm , nsensor , mjNSENS ) X ( int , sensor_dim , nsensor , 1 ) X ( int , sensor_adr , nsensor , 1 ) X ( mjtNum , sensor_cutoff , nsensor , 1 ) X ( mjtNum , sensor_noise , nsensor , 1 ) X ( int , sensor_history , nsensor , 2 ) X ( int , sensor_historyadr , nsensor , 1 ) X ( mjtNum , sensor_delay , nsensor , 1 ) X ( mjtNum , sensor_interval , nsensor , 2 ) X ( mjtNum , sensor_user , nsensor , MJ_M ( nuser_sensor ) ) X ( int , sensor_plugin , nsensor , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 699 - }, - "MJMODEL_POINTERS": { - "value": "X ( mjtNum , qpos0 , nq , 1 ) X ( mjtNum , qpos_spring , nq , 1 ) MJMODEL_POINTERS_BODY X ( int , bvh_depth , nbvh , 1 ) X ( int , bvh_child , nbvh , 2 ) X ( int , bvh_nodeid , nbvh , 1 ) X ( mjtNum , bvh_aabb , nbvhstatic , 6 ) X ( int , oct_depth , noct , 1 ) X ( int , oct_child , noct , 8 ) X ( mjtNum , oct_aabb , noct , 6 ) X ( mjtNum , oct_coeff , noct , 8 ) MJMODEL_POINTERS_JOINT MJMODEL_POINTERS_DOF MJMODEL_POINTERS_TREE MJMODEL_POINTERS_GEOM MJMODEL_POINTERS_SITE MJMODEL_POINTERS_CAMERA MJMODEL_POINTERS_LIGHT MJMODEL_POINTERS_FLEX MJMODEL_POINTERS_MESH MJMODEL_POINTERS_SKIN MJMODEL_POINTERS_HFIELD MJMODEL_POINTERS_TEXTURE MJMODEL_POINTERS_MATERIAL MJMODEL_POINTERS_PAIR MJMODEL_POINTERS_EXCLUDE MJMODEL_POINTERS_EQUALITY MJMODEL_POINTERS_TENDON X ( int , wrap_type , nwrap , 1 ) X ( int , wrap_objid , nwrap , 1 ) X ( mjtNum , wrap_prm , nwrap , 1 ) MJMODEL_POINTERS_ACTUATOR MJMODEL_POINTERS_SENSOR X ( int , plugin , nplugin , 1 ) X ( int , plugin_stateadr , nplugin , 1 ) X ( int , plugin_statenum , nplugin , 1 ) X ( char , plugin_attr , npluginattr , 1 ) X ( int , plugin_attradr , nplugin , 1 ) X ( int , numeric_adr , nnumeric , 1 ) X ( int , numeric_size , nnumeric , 1 ) X ( mjtNum , numeric_data , nnumericdata , 1 ) X ( int , text_adr , ntext , 1 ) X ( int , text_size , ntext , 1 ) X ( char , text_data , ntextdata , 1 ) X ( int , tuple_adr , ntuple , 1 ) X ( int , tuple_size , ntuple , 1 ) X ( int , tuple_objtype , ntupledata , 1 ) X ( int , tuple_objid , ntupledata , 1 ) X ( mjtNum , tuple_objprm , ntupledata , 1 ) X ( mjtNum , key_time , nkey , 1 ) X ( mjtNum , key_qpos , nkey , MJ_M ( nq ) ) X ( mjtNum , key_qvel , nkey , MJ_M ( nv ) ) X ( mjtNum , key_act , nkey , MJ_M ( na ) ) X ( mjtNum , key_mpos , nkey , MJ_M ( nmocap ) * 3 ) X ( mjtNum , key_mquat , nkey , MJ_M ( nmocap ) * 4 ) X ( mjtNum , key_ctrl , nkey , MJ_M ( nu ) ) X ( int , name_bodyadr , nbody , 1 ) X ( int , name_jntadr , njnt , 1 ) X ( int , name_geomadr , ngeom , 1 ) X ( int , name_siteadr , nsite , 1 ) X ( int , name_camadr , ncam , 1 ) X ( int , name_lightadr , nlight , 1 ) X ( int , name_flexadr , nflex , 1 ) X ( int , name_meshadr , nmesh , 1 ) X ( int , name_skinadr , nskin , 1 ) X ( int , name_hfieldadr , nhfield , 1 ) X ( int , name_texadr , ntex , 1 ) X ( int , name_matadr , nmat , 1 ) X ( int , name_pairadr , npair , 1 ) X ( int , name_excludeadr , nexclude , 1 ) X ( int , name_eqadr , neq , 1 ) X ( int , name_tendonadr , ntendon , 1 ) X ( int , name_actuatoradr , nu , 1 ) X ( int , name_sensoradr , nsensor , 1 ) X ( int , name_numericadr , nnumeric , 1 ) X ( int , name_textadr , ntext , 1 ) X ( int , name_tupleadr , ntuple , 1 ) X ( int , name_keyadr , nkey , 1 ) X ( int , name_pluginadr , nplugin , 1 ) X ( char , names , nnames , 1 ) X ( int , names_map , nnames_map , 1 ) X ( char , paths , npaths , 1 ) X ( int , B_rownnz , nbody , 1 ) X ( int , B_rowadr , nbody , 1 ) X ( int , B_colind , nB , 1 ) X ( int , M_rownnz , nv , 1 ) X ( int , M_rowadr , nv , 1 ) X ( int , M_colind , nC , 1 ) X ( int , mapM2M , nC , 1 ) X ( int , D_rownnz , nv , 1 ) X ( int , D_rowadr , nv , 1 ) X ( int , D_diag , nv , 1 ) X ( int , D_colind , nD , 1 ) X ( int , mapM2D , nD , 1 ) X ( int , mapD2M , nC , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 719 - }, - "MJDATA_POINTERS": { - "value": "X ( mjtNum , qpos , nq , 1 ) X ( mjtNum , qvel , nv , 1 ) X ( mjtNum , act , na , 1 ) X ( mjtNum , history , nhistory , 1 ) X ( mjtNum , qacc_warmstart , nv , 1 ) X ( mjtNum , plugin_state , npluginstate , 1 ) X ( mjtNum , ctrl , nu , 1 ) X ( mjtNum , qfrc_applied , nv , 1 ) X ( mjtNum , xfrc_applied , nbody , 6 ) X ( mjtBool , eq_active , neq , 1 ) X ( mjtNum , mocap_pos , nmocap , 3 ) X ( mjtNum , mocap_quat , nmocap , 4 ) X ( mjtNum , qacc , nv , 1 ) X ( mjtNum , act_dot , na , 1 ) X ( mjtNum , userdata , nuserdata , 1 ) X ( mjtNum , sensordata , nsensordata , 1 ) X ( int , tree_asleep , ntree , 1 ) X ( int , plugin , nplugin , 1 ) X ( uintptr_t , plugin_data , nplugin , 1 ) X ( mjtNum , xpos , nbody , 3 ) X ( mjtNum , xquat , nbody , 4 ) X ( mjtNum , xmat , nbody , 9 ) X ( mjtNum , xipos , nbody , 3 ) X ( mjtNum , ximat , nbody , 9 ) X ( mjtNum , xanchor , njnt , 3 ) X ( mjtNum , xaxis , njnt , 3 ) X ( mjtNum , geom_xpos , ngeom , 3 ) X ( mjtNum , geom_xmat , ngeom , 9 ) X ( mjtNum , site_xpos , nsite , 3 ) X ( mjtNum , site_xmat , nsite , 9 ) X ( mjtNum , cam_xpos , ncam , 3 ) X ( mjtNum , cam_xmat , ncam , 9 ) X ( mjtNum , light_xpos , nlight , 3 ) X ( mjtNum , light_xdir , nlight , 3 ) X ( mjtNum , subtree_com , nbody , 3 ) X ( mjtNum , cdof , nv , 6 ) X ( mjtNum , cinert , nbody , 10 ) X ( mjtNum , flexvert_xpos , nflexvert , 3 ) X ( mjtNum , flexelem_aabb , nflexelem , 6 ) X ( mjtNum , flexedge_J , nJfe , 1 ) X ( mjtNum , flexedge_length , nflexedge , 1 ) X ( mjtNum , flexvert_J , nJfv , 2 ) X ( mjtNum , flexvert_length , nflexvert , 2 ) X ( mjtNum , bvh_aabb_dyn , nbvhdynamic , 6 ) X ( int , ten_wrapadr , ntendon , 1 ) X ( int , ten_wrapnum , ntendon , 1 ) X ( mjtNum , ten_J , nJten , 1 ) X ( mjtNum , ten_length , ntendon , 1 ) X ( int , wrap_obj , nwrap , 2 ) X ( mjtNum , wrap_xpos , nwrap , 6 ) X ( mjtNum , actuator_length , nu , 1 ) X ( int , moment_rownnz , nu , 1 ) X ( int , moment_rowadr , nu , 1 ) X ( int , moment_colind , nJmom , 1 ) X ( mjtNum , actuator_moment , nJmom , 1 ) XNV ( mjtNum , crb , nbody , 10 ) XNV ( mjtNum , qM , nM , 1 ) XNV ( mjtNum , M , nC , 1 ) XNV ( mjtNum , qLD , nC , 1 ) X ( mjtNum , qLDiagInv , nv , 1 ) X ( mjtBool , bvh_active , nbvh , 1 ) X ( int , tree_awake , ntree , 1 ) X ( int , body_awake , nbody , 1 ) X ( int , body_awake_ind , nbody , 1 ) X ( int , parent_awake_ind , nbody , 1 ) X ( int , dof_awake_ind , nv , 1 ) X ( mjtNum , flexedge_velocity , nflexedge , 1 ) X ( mjtNum , ten_velocity , ntendon , 1 ) X ( mjtNum , actuator_velocity , nu , 1 ) X ( mjtNum , cvel , nbody , 6 ) X ( mjtNum , cdof_dot , nv , 6 ) X ( mjtNum , qfrc_bias , nv , 1 ) X ( mjtNum , qfrc_spring , nv , 1 ) X ( mjtNum , qfrc_damper , nv , 1 ) X ( mjtNum , qfrc_gravcomp , nv , 1 ) X ( mjtNum , qfrc_fluid , nv , 1 ) X ( mjtNum , qfrc_passive , nv , 1 ) X ( mjtNum , subtree_linvel , nbody , 3 ) X ( mjtNum , subtree_angmom , nbody , 3 ) XNV ( mjtNum , qH , nC , 1 ) X ( mjtNum , qHDiagInv , nv , 1 ) XNV ( mjtNum , qDeriv , nD , 1 ) XNV ( mjtNum , qLU , nD , 1 ) X ( mjtNum , actuator_force , nu , 1 ) X ( mjtNum , qfrc_actuator , nv , 1 ) X ( mjtNum , qfrc_smooth , nv , 1 ) X ( mjtNum , qacc_smooth , nv , 1 ) X ( mjtNum , qfrc_constraint , nv , 1 ) X ( mjtNum , qfrc_inverse , nv , 1 ) X ( mjtNum , cacc , nbody , 6 ) X ( mjtNum , cfrc_int , nbody , 6 ) X ( mjtNum , cfrc_ext , nbody , 6 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 821 - }, - "MJ_D": { - "value": "( n ) n", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 918 - }, - "MJDATA_ARENA_POINTERS_CONTACT": { - "value": "X ( mjContact , contact , MJ_D ( ncon ) , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 921 - }, - "MJDATA_ARENA_POINTERS_SOLVER": { - "value": "X ( int , efc_type , MJ_D ( nefc ) , 1 ) X ( int , efc_id , MJ_D ( nefc ) , 1 ) XNV ( int , efc_J_rownnz , MJ_D ( nefc ) , 1 ) XNV ( int , efc_J_rowadr , MJ_D ( nefc ) , 1 ) XNV ( int , efc_J_rowsuper , MJ_D ( nefc ) , 1 ) XNV ( int , efc_J_colind , MJ_D ( nJ ) , 1 ) XNV ( mjtNum , efc_J , MJ_D ( nJ ) , 1 ) X ( mjtNum , efc_pos , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_margin , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_frictionloss , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_diagA , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_KBIP , MJ_D ( nefc ) , 4 ) X ( mjtNum , efc_D , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_R , MJ_D ( nefc ) , 1 ) X ( int , tendon_efcadr , MJ_M ( ntendon ) , 1 ) X ( mjtNum , efc_vel , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_aref , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_b , MJ_D ( nefc ) , 1 ) X ( int , efc_state , MJ_D ( nefc ) , 1 ) X ( mjtNum , efc_force , MJ_D ( nefc ) , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 925 - }, - "MJDATA_ARENA_POINTERS_DUAL": { - "value": "XNV ( int , efc_Y_rownnz , MJ_D ( nefc ) , 1 ) XNV ( int , efc_Y_rowadr , MJ_D ( nefc ) , 1 ) XNV ( int , efc_Y_colind , MJ_D ( nY ) , 1 ) XNV ( mjtNum , efc_Y , MJ_D ( nY ) , 1 ) XNV ( int , efc_AR_rownnz , MJ_D ( nefc ) , 1 ) XNV ( int , efc_AR_rowadr , MJ_D ( nefc ) , 1 ) XNV ( int , efc_AR_colind , MJ_D ( nA ) , 1 ) XNV ( mjtNum , efc_AR , MJ_D ( nA ) , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 948 - }, - "MJDATA_ARENA_POINTERS_ISLAND": { - "value": "X ( int , tree_island , MJ_M ( ntree ) , 1 ) X ( int , island_ntree , MJ_D ( nisland ) , 1 ) X ( int , island_itreeadr , MJ_D ( nisland ) , 1 ) X ( int , map_itree2tree , MJ_M ( ntree ) , 1 ) X ( int , dof_island , MJ_M ( nv ) , 1 ) X ( int , island_nv , MJ_D ( nisland ) , 1 ) X ( int , island_idofadr , MJ_D ( nisland ) , 1 ) X ( int , island_dofadr , MJ_D ( nisland ) , 1 ) X ( int , map_dof2idof , MJ_M ( nv ) , 1 ) X ( int , map_idof2dof , MJ_M ( nv ) , 1 ) X ( mjtNum , ifrc_smooth , MJ_D ( nidof ) , 1 ) X ( mjtNum , iacc_smooth , MJ_D ( nidof ) , 1 ) X ( mjtNum , iacc , MJ_D ( nidof ) , 1 ) X ( int , efc_island , MJ_D ( nefc ) , 1 ) X ( int , island_ne , MJ_D ( nisland ) , 1 ) X ( int , island_nf , MJ_D ( nisland ) , 1 ) X ( int , island_nefc , MJ_D ( nisland ) , 1 ) X ( int , island_iefcadr , MJ_D ( nisland ) , 1 ) X ( int , map_efc2iefc , MJ_D ( nefc ) , 1 ) X ( int , map_iefc2efc , MJ_D ( nefc ) , 1 ) X ( int , iefc_type , MJ_D ( nefc ) , 1 ) X ( int , iefc_id , MJ_D ( nefc ) , 1 ) X ( mjtNum , iefc_frictionloss , MJ_D ( nefc ) , 1 ) X ( mjtNum , iefc_D , MJ_D ( nefc ) , 1 ) X ( mjtNum , iefc_R , MJ_D ( nefc ) , 1 ) X ( mjtNum , iefc_aref , MJ_D ( nefc ) , 1 ) X ( int , iefc_state , MJ_D ( nefc ) , 1 ) X ( mjtNum , iefc_force , MJ_D ( nefc ) , 1 ) X ( mjtNum , ifrc_constraint , MJ_D ( nidof ) , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 959 - }, - "MJDATA_ARENA_POINTERS": { - "value": "MJDATA_ARENA_POINTERS_CONTACT MJDATA_ARENA_POINTERS_SOLVER MJDATA_ARENA_POINTERS_DUAL MJDATA_ARENA_POINTERS_ISLAND", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 991 - }, - "MJDATA_SCALAR": { - "value": "X ( size_t , narena ) X ( size_t , nbuffer ) X ( int , nplugin ) X ( size_t , pstack ) X ( size_t , pbase ) X ( size_t , parena ) X ( uintptr_t , threadpool ) X ( size_t , maxuse_stack ) X ( size_t , maxuse_arena ) X ( int , maxuse_con ) X ( int , maxuse_efc ) X ( int , ncon ) X ( int , ne ) X ( int , nf ) X ( int , nl ) X ( int , nefc ) X ( int , nJ ) X ( int , nY ) X ( int , nA ) X ( int , nisland ) X ( int , nidof ) X ( int , ntree_awake ) X ( int , nbody_awake ) X ( int , nparent_awake ) X ( int , nv_awake ) X ( mjtBool , flg_energypos ) X ( mjtBool , flg_energyvel ) X ( mjtBool , flg_subtreevel ) X ( mjtBool , flg_rnepost ) X ( mjtNum , time )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 999 - }, - "MJDATA_VECTOR": { - "value": "X ( mjSolverStat , solver , mjNISLAND , mjNSOLVER ) X ( int , solver_niter , mjNISLAND , 1 ) X ( int , solver_nnz , mjNISLAND , 1 ) X ( mjtNum , solver_fwdinv , 2 , 1 ) X ( mjWarningStat , warning , mjNWARNING , 1 ) X ( mjTimerStat , timer , mjNTIMER , 1 ) X ( mjtNum , energy , 2 , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 1033 - }, - "XNV": { - "value": "X", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjxmacro.h", - "line": 1045 - }, - "mjVERSION_HEADER": { - "value": "3010000", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 19 - }, - "MUJOCO_HELPER_DLL_IMPORT": { - "value": "__declspec ( dllimport )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjexport.h", - "line": 19 - }, - "MUJOCO_HELPER_DLL_EXPORT": { - "value": "__declspec ( dllexport )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjexport.h", - "line": 20 - }, - "mjMAX": { - "value": "( a , b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmacro.h", - "line": 19 - }, - "mjMIN": { - "value": "( a , b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmacro.h", - "line": 20 - }, - "mjDISABLED": { - "value": "( x ) ( m -> opt . disableflags & ( x ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmacro.h", - "line": 23 - }, - "mjENABLED": { - "value": "( x ) ( m -> opt . enableflags & ( x ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmacro.h", - "line": 24 - }, - "mjACTUATORDISABLED": { - "value": "( i ) ( m -> opt . disableactuator & ( 1 << m -> actuator_group [ i ] ) )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmacro.h", - "line": 27 - }, - "mjPRINTFLIKE": { - "value": "( n , m )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjmacro.h", - "line": 34 - }, - "mjNGROUP": { - "value": "6", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 23 - }, - "mjMAXLIGHT": { - "value": "100", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 24 - }, - "mjMAXOVERLAY": { - "value": "500", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 25 - }, - "mjMAXLINE": { - "value": "100", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 26 - }, - "mjMAXLINEPNT": { - "value": "1001", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 27 - }, - "mjMAXPLANEGRID": { - "value": "200", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjvisualize.h", - "line": 28 - }, - "mjPLUGIN_LIB_INIT": { - "value": "( n ) static void _mj_init_ ## n ( void ) __attribute__ ( ( constructor ) ) ; static void _mj_init_ ## n ( void )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjplugin.h", - "line": 192 - }, - "mjNAUX": { - "value": "10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 25 - }, - "mjMAXTEXTURE": { - "value": "1000", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 26 - }, - "mjMAXMATERIAL": { - "value": "1000", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjrender.h", - "line": 27 - }, - "ASAN_POISON_MEMORY_REGION": { - "value": "( addr , size )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjsan.h", - "line": 33 - }, - "ASAN_UNPOISON_MEMORY_REGION": { - "value": "( addr , size )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjsan.h", - "line": 34 - }, - "mjMAXUISECT": { - "value": "10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 20 - }, - "mjMAXUIITEM": { - "value": "200", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 21 - }, - "mjMAXUITEXT": { - "value": "300", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 22 - }, - "mjMAXUINAME": { - "value": "40", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 23 - }, - "mjMAXUIMULTI": { - "value": "35", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 24 - }, - "mjMAXUIEDIT": { - "value": "7", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 25 - }, - "mjMAXUIRECT": { - "value": "25", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 26 - }, - "mjSEPCLOSED": { - "value": "1000", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 28 - }, - "mjPRESERVE": { - "value": "2000", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 29 - }, - "mjKEY_ESCAPE": { - "value": "256", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 33 - }, - "mjKEY_ENTER": { - "value": "257", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 34 - }, - "mjKEY_TAB": { - "value": "258", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 35 - }, - "mjKEY_BACKSPACE": { - "value": "259", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 36 - }, - "mjKEY_INSERT": { - "value": "260", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 37 - }, - "mjKEY_DELETE": { - "value": "261", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 38 - }, - "mjKEY_RIGHT": { - "value": "262", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 39 - }, - "mjKEY_LEFT": { - "value": "263", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 40 - }, - "mjKEY_DOWN": { - "value": "264", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 41 - }, - "mjKEY_UP": { - "value": "265", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 42 - }, - "mjKEY_PAGE_UP": { - "value": "266", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 43 - }, - "mjKEY_PAGE_DOWN": { - "value": "267", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 44 - }, - "mjKEY_HOME": { - "value": "268", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 45 - }, - "mjKEY_END": { - "value": "269", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 46 - }, - "mjKEY_F1": { - "value": "290", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 47 - }, - "mjKEY_F2": { - "value": "291", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 48 - }, - "mjKEY_F3": { - "value": "292", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 49 - }, - "mjKEY_F4": { - "value": "293", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 50 - }, - "mjKEY_F5": { - "value": "294", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 51 - }, - "mjKEY_F6": { - "value": "295", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 52 - }, - "mjKEY_F7": { - "value": "296", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 53 - }, - "mjKEY_F8": { - "value": "297", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 54 - }, - "mjKEY_F9": { - "value": "298", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 55 - }, - "mjKEY_F10": { - "value": "299", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 56 - }, - "mjKEY_F11": { - "value": "300", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 57 - }, - "mjKEY_F12": { - "value": "301", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 58 - }, - "mjKEY_NUMPAD_0": { - "value": "320", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 59 - }, - "mjKEY_NUMPAD_9": { - "value": "329", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjui.h", - "line": 60 - }, - "MJSELEMENT_FIELDS": { - "value": "X ( mjtObj , elemtype , 1 ) X ( uint64_t , signature , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 21 - }, - "MJSCOMPILER_FIELDS": { - "value": "X ( mjtByte , autolimits , 1 ) X ( double , boundmass , 1 ) X ( double , boundinertia , 1 ) X ( double , settotalmass , 1 ) X ( mjtByte , balanceinertia , 1 ) X ( mjtByte , fitaabb , 1 ) X ( mjtByte , degree , 1 ) XVEC ( char , eulerseq , 3 ) X ( mjtByte , discardvisual , 1 ) X ( mjtByte , usethread , 1 ) X ( mjtByte , fusestatic , 1 ) X ( int , inertiafromgeom , 1 ) XVEC ( int , inertiagrouprange , 2 ) X ( mjtByte , saveinertial , 1 ) X ( int , alignfree , 1 ) X ( mjLROpt , LRopt , 1 ) X ( mjString * , meshdir , 1 ) X ( mjString * , texturedir , 1 ) X ( uint64_t , authored , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 28 - }, - "MJSPEC_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , modelname , 1 ) X ( mjsCompiler , compiler , 1 ) X ( mjtByte , strippath , 1 ) X ( mjOption , option , 1 ) X ( mjVisual , visual , 1 ) X ( mjStatistic , stat , 1 ) X ( mjtSize , memory , 1 ) X ( int , nemax , 1 ) X ( int , nuserdata , 1 ) X ( int , nuser_body , 1 ) X ( int , nuser_jnt , 1 ) X ( int , nuser_geom , 1 ) X ( int , nuser_site , 1 ) X ( int , nuser_cam , 1 ) X ( int , nuser_tendon , 1 ) X ( int , nuser_actuator , 1 ) X ( int , nuser_sensor , 1 ) X ( int , nkey , 1 ) X ( int , njmax , 1 ) X ( int , nconmax , 1 ) X ( mjtSize , nstack , 1 ) X ( mjString * , comment , 1 ) X ( mjString * , modelfiledir , 1 ) X ( mjtByte , hasImplicitPluginElem , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 52 - }, - "MJSORIENTATION_FIELDS": { - "value": "X ( mjtOrientation , type , 1 ) XVEC ( double , axisangle , 4 ) XVEC ( double , xyaxes , 6 ) XVEC ( double , zaxis , 3 ) XVEC ( double , euler , 3 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 82 - }, - "MJSPLUGIN_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , name , 1 ) X ( mjString * , plugin_name , 1 ) X ( mjtByte , active , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 92 - }, - "MJSBODY_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , childclass , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , quat , 4 ) X ( mjsOrientation , alt , 1 ) X ( double , mass , 1 ) XVEC ( double , ipos , 3 ) XVEC ( double , iquat , 4 ) XVEC ( double , inertia , 3 ) X ( mjsOrientation , ialt , 1 ) XVEC ( double , fullinertia , 6 ) X ( mjtByte , mocap , 1 ) X ( double , gravcomp , 1 ) X ( mjtSleepPolicy , sleep , 1 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjtByte , explicitinertial , 1 ) X ( mjsPlugin , plugin , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 102 - }, - "MJSFRAME_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , childclass , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , quat , 4 ) X ( mjsOrientation , alt , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 125 - }, - "MJSJOINT_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtJoint , type , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , axis , 3 ) X ( double , ref , 1 ) X ( int , align , 1 ) XVEC ( double , stiffness , mjNPOLY + 1 ) X ( double , springref , 1 ) XVEC ( double , springdamper , 2 ) X ( int , limited , 1 ) XVEC ( double , range , 2 ) X ( double , margin , 1 ) XVEC ( mjtNum , solref_limit , mjNREF ) XVEC ( mjtNum , solimp_limit , mjNIMP ) X ( int , actfrclimited , 1 ) XVEC ( double , actfrcrange , 2 ) X ( double , armature , 1 ) XVEC ( double , damping , mjNPOLY + 1 ) X ( double , frictionloss , 1 ) XVEC ( mjtNum , solref_friction , mjNREF ) XVEC ( mjtNum , solimp_friction , mjNIMP ) X ( int , group , 1 ) X ( mjtByte , actgravcomp , 1 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 136 - }, - "MJSGEOM_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtGeom , type , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , quat , 4 ) X ( mjsOrientation , alt , 1 ) XVEC ( double , fromto , 6 ) XVEC ( double , size , 3 ) X ( int , contype , 1 ) X ( int , conaffinity , 1 ) X ( int , condim , 1 ) X ( int , priority , 1 ) XVEC ( double , friction , 3 ) X ( double , solmix , 1 ) XVEC ( mjtNum , solref , mjNREF ) XVEC ( mjtNum , solimp , mjNIMP ) X ( double , margin , 1 ) X ( double , gap , 1 ) X ( double , mass , 1 ) X ( double , density , 1 ) X ( mjtGeomInertia , typeinertia , 1 ) X ( mjtNum , fluid_ellipsoid , 1 ) XVEC ( mjtNum , fluid_coefs , 5 ) X ( mjString * , material , 1 ) XVEC ( float , rgba , 4 ) X ( int , group , 1 ) X ( mjString * , hfieldname , 1 ) X ( mjString * , meshname , 1 ) X ( double , fitscale , 1 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjsPlugin , plugin , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 166 - }, - "MJSSITE_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , quat , 4 ) X ( mjsOrientation , alt , 1 ) XVEC ( double , fromto , 6 ) XVEC ( double , size , 3 ) X ( mjtGeom , type , 1 ) X ( mjString * , material , 1 ) X ( int , group , 1 ) XVEC ( float , rgba , 4 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 202 - }, - "MJSCAMERA_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , quat , 4 ) X ( mjsOrientation , alt , 1 ) X ( mjtCamLight , mode , 1 ) X ( mjString * , targetbody , 1 ) X ( mjtProjection , proj , 1 ) XVEC ( int , resolution , 2 ) X ( int , output , 1 ) X ( double , fovy , 1 ) X ( double , ipd , 1 ) XVEC ( float , intrinsic , 4 ) XVEC ( float , sensor_size , 2 ) XVEC ( float , focal_length , 2 ) XVEC ( float , focal_pixel , 2 ) XVEC ( float , principal_length , 2 ) XVEC ( float , principal_pixel , 2 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 219 - }, - "MJSLIGHT_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) XVEC ( double , pos , 3 ) XVEC ( double , dir , 3 ) X ( mjtCamLight , mode , 1 ) X ( mjString * , targetbody , 1 ) X ( mjtByte , active , 1 ) X ( mjtLightType , type , 1 ) X ( mjString * , texture , 1 ) X ( mjtByte , castshadow , 1 ) X ( float , bulbradius , 1 ) X ( float , intensity , 1 ) X ( float , range , 1 ) XVEC ( float , attenuation , 3 ) X ( float , cutoff , 1 ) X ( float , exponent , 1 ) XVEC ( float , ambient , 3 ) XVEC ( float , diffuse , 3 ) XVEC ( float , specular , 3 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 243 - }, - "MJSFLEX_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( int , contype , 1 ) X ( int , conaffinity , 1 ) X ( int , condim , 1 ) X ( int , priority , 1 ) XVEC ( double , friction , 3 ) X ( double , solmix , 1 ) XVEC ( mjtNum , solref , mjNREF ) XVEC ( mjtNum , solimp , mjNIMP ) X ( double , margin , 1 ) X ( double , gap , 1 ) X ( int , dim , 1 ) X ( double , radius , 1 ) XVEC ( double , size , 3 ) X ( mjtByte , internal , 1 ) X ( mjtByte , flatskin , 1 ) X ( int , selfcollide , 1 ) X ( int , passive , 1 ) X ( int , activelayers , 1 ) X ( int , group , 1 ) X ( double , edgestiffness , 1 ) X ( double , edgedamping , 1 ) XVEC ( float , rgba , 4 ) X ( mjString * , material , 1 ) X ( double , young , 1 ) X ( double , poisson , 1 ) X ( double , damping , 1 ) X ( double , thickness , 1 ) X ( int , elastic2d , 1 ) XVEC ( int , cellcount , 3 ) X ( int , order , 1 ) X ( mjStringVec * , nodebody , 1 ) X ( mjStringVec * , vertbody , 1 ) X ( mjDoubleVec * , node , 1 ) X ( mjDoubleVec * , vert , 1 ) X ( mjIntVec * , elem , 1 ) X ( mjFloatVec * , texcoord , 1 ) X ( mjIntVec * , elemtexcoord , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 267 - }, - "MJSMESH_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , content_type , 1 ) X ( mjString * , file , 1 ) XVEC ( double , refpos , 3 ) XVEC ( double , refquat , 4 ) XVEC ( double , scale , 3 ) X ( mjtMeshInertia , inertia , 1 ) X ( mjtByte , smoothnormal , 1 ) X ( mjtByte , needsdf , 1 ) X ( int , maxhullvert , 1 ) X ( mjFloatVec * , uservert , 1 ) X ( mjFloatVec * , usernormal , 1 ) X ( mjFloatVec * , usertexcoord , 1 ) X ( mjIntVec * , userface , 1 ) X ( mjIntVec * , userfacenormal , 1 ) X ( mjIntVec * , userfacetexcoord , 1 ) X ( mjsPlugin , plugin , 1 ) X ( mjString * , material , 1 ) X ( int , octree_maxdepth , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 311 - }, - "MJSHFIELD_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , content_type , 1 ) X ( mjString * , file , 1 ) XVEC ( double , size , 4 ) X ( int , nrow , 1 ) X ( int , ncol , 1 ) X ( mjFloatVec * , userdata , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 336 - }, - "MJSSKIN_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , file , 1 ) X ( mjString * , material , 1 ) XVEC ( float , rgba , 4 ) X ( float , inflate , 1 ) X ( int , group , 1 ) X ( mjFloatVec * , vert , 1 ) X ( mjFloatVec * , texcoord , 1 ) X ( mjIntVec * , face , 1 ) X ( mjStringVec * , bodyname , 1 ) X ( mjFloatVec * , bindpos , 1 ) X ( mjFloatVec * , bindquat , 1 ) X ( mjIntVecVec * , vertid , 1 ) X ( mjFloatVecVec * , vertweight , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 349 - }, - "MJSTEXTURE_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtTexture , type , 1 ) X ( mjtColorSpace , colorspace , 1 ) X ( int , builtin , 1 ) X ( int , mark , 1 ) XVEC ( double , rgb1 , 3 ) XVEC ( double , rgb2 , 3 ) XVEC ( double , markrgb , 3 ) X ( double , random , 1 ) X ( int , height , 1 ) X ( int , width , 1 ) X ( int , nchannel , 1 ) X ( mjString * , content_type , 1 ) X ( mjString * , file , 1 ) XVEC ( int , gridsize , 2 ) XVEC ( char , gridlayout , 12 ) X ( mjStringVec * , cubefiles , 1 ) X ( mjByteVec * , data , 1 ) X ( mjtByte , hflip , 1 ) X ( mjtByte , vflip , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 369 - }, - "MJSMATERIAL_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjStringVec * , textures , 1 ) X ( mjtByte , texuniform , 1 ) XVEC ( float , texrepeat , 2 ) X ( float , emission , 1 ) X ( float , specular , 1 ) X ( float , shininess , 1 ) X ( float , reflectance , 1 ) X ( float , metallic , 1 ) X ( float , roughness , 1 ) XVEC ( float , rgba , 4 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 395 - }, - "MJSPAIR_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , geomname1 , 1 ) X ( mjString * , geomname2 , 1 ) X ( int , condim , 1 ) XVEC ( mjtNum , solref , mjNREF ) XVEC ( mjtNum , solreffriction , mjNREF ) XVEC ( mjtNum , solimp , mjNIMP ) X ( double , margin , 1 ) X ( double , gap , 1 ) XVEC ( double , friction , 5 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 412 - }, - "MJSEXCLUDE_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , bodyname1 , 1 ) X ( mjString * , bodyname2 , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 428 - }, - "MJSEQUALITY_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtEq , type , 1 ) XVEC ( double , data , mjNEQDATA ) X ( mjtByte , active , 1 ) X ( mjString * , name1 , 1 ) X ( mjString * , name2 , 1 ) X ( mjtObj , objtype , 1 ) XVEC ( mjtNum , solref , mjNREF ) XVEC ( mjtNum , solimp , mjNIMP ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 437 - }, - "MJSTENDON_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) XVEC ( double , stiffness , mjNPOLY + 1 ) XVEC ( double , springlength , 2 ) XVEC ( double , damping , mjNPOLY + 1 ) X ( double , frictionloss , 1 ) XVEC ( mjtNum , solref_friction , mjNREF ) XVEC ( mjtNum , solimp_friction , mjNIMP ) X ( double , armature , 1 ) X ( int , limited , 1 ) X ( int , actfrclimited , 1 ) XVEC ( double , range , 2 ) XVEC ( double , actfrcrange , 2 ) X ( double , margin , 1 ) XVEC ( mjtNum , solref_limit , mjNREF ) XVEC ( mjtNum , solimp_limit , mjNIMP ) X ( mjString * , material , 1 ) X ( double , width , 1 ) XVEC ( float , rgba , 4 ) X ( int , group , 1 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 452 - }, - "MJSWRAP_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtWrap , type , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 478 - }, - "MJSACTUATOR_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtGain , gaintype , 1 ) XVEC ( double , gainprm , mjNGAIN ) X ( mjtBias , biastype , 1 ) XVEC ( double , biasprm , mjNGAIN ) X ( mjtDyn , dyntype , 1 ) XVEC ( double , dynprm , mjNDYN ) X ( int , actdim , 1 ) X ( mjtByte , actearly , 1 ) X ( mjtTrn , trntype , 1 ) XVEC ( double , gear , 6 ) X ( mjString * , target , 1 ) X ( mjString * , refsite , 1 ) X ( mjString * , slidersite , 1 ) X ( double , cranklength , 1 ) XVEC ( double , lengthrange , 2 ) X ( double , inheritrange , 1 ) XVEC ( double , damping , mjNPOLY + 1 ) X ( double , armature , 1 ) X ( int , ctrllimited , 1 ) XVEC ( double , ctrlrange , 2 ) X ( int , forcelimited , 1 ) XVEC ( double , forcerange , 2 ) X ( int , actlimited , 1 ) XVEC ( double , actrange , 2 ) X ( int , group , 1 ) X ( int , nsample , 1 ) X ( int , interp , 1 ) X ( double , delay , 1 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjsPlugin , plugin , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 486 - }, - "MJSSENSOR_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjtSensor , type , 1 ) X ( mjtObj , objtype , 1 ) X ( mjString * , objname , 1 ) X ( mjtObj , reftype , 1 ) X ( mjString * , refname , 1 ) XVEC ( int , intprm , mjNSENS ) X ( mjtDataType , datatype , 1 ) X ( mjtStage , needstage , 1 ) X ( int , dim , 1 ) X ( double , cutoff , 1 ) X ( double , noise , 1 ) X ( int , nsample , 1 ) X ( int , interp , 1 ) X ( double , delay , 1 ) XVEC ( double , interval , 2 ) X ( mjDoubleVec * , userdata , 1 ) X ( mjsPlugin , plugin , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 523 - }, - "MJSNUMERIC_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjDoubleVec * , data , 1 ) X ( int , size , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 547 - }, - "MJSTEXT_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjString * , data , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 556 - }, - "MJSTUPLE_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjIntVec * , objtype , 1 ) X ( mjStringVec * , objname , 1 ) X ( mjDoubleVec * , objprm , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 564 - }, - "MJSKEY_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( double , time , 1 ) X ( mjDoubleVec * , qpos , 1 ) X ( mjDoubleVec * , qvel , 1 ) X ( mjDoubleVec * , act , 1 ) X ( mjDoubleVec * , mpos , 1 ) X ( mjDoubleVec * , mquat , 1 ) X ( mjDoubleVec * , ctrl , 1 ) X ( mjString * , info , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 574 - }, - "MJSDEFAULT_FIELDS": { - "value": "X ( mjsElement * , element , 1 ) X ( mjsJoint * , joint , 1 ) X ( mjsGeom * , geom , 1 ) X ( mjsSite * , site , 1 ) X ( mjsCamera * , camera , 1 ) X ( mjsLight * , light , 1 ) X ( mjsFlex * , flex , 1 ) X ( mjsMesh * , mesh , 1 ) X ( mjsMaterial * , material , 1 ) X ( mjsPair * , pair , 1 ) X ( mjsEquality * , equality , 1 ) X ( mjsTendon * , tendon , 1 ) X ( mjsActuator * , actuator , 1 )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjspecmacro.h", - "line": 588 - }, - "MJ_ASSERT_SIZE": { - "value": "( type , size ) _Static_assert ( sizeof ( type ) == ( size ) , # type \" must be \" # size \" bytes for MuJoCo ABI stability\" )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjassert.h", - "line": 34 - }, - "MJ_STATIC_ASSERT": { - "value": "( expr ) _Static_assert ( expr , # expr )", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjassert.h", - "line": 36 - }, - "X": { - "value": "( type , name , dim ) + 1", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjassert.h", - "line": 135 - }, - "XVEC": { - "value": "( type , name , dim ) + 1", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mjassert.h", - "line": 136 - }, - "mju_sqrt": { - "value": "sqrt", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1025 - }, - "mju_exp": { - "value": "exp", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1026 - }, - "mju_sin": { - "value": "sin", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1027 - }, - "mju_cos": { - "value": "cos", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1028 - }, - "mju_tan": { - "value": "tan", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1029 - }, - "mju_asin": { - "value": "asin", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1030 - }, - "mju_acos": { - "value": "acos", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1031 - }, - "mju_atan2": { - "value": "atan2", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1032 - }, - "mju_tanh": { - "value": "tanh", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1033 - }, - "mju_pow": { - "value": "pow", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1034 - }, - "mju_abs": { - "value": "fabs", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1035 - }, - "mju_log": { - "value": "log", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1036 - }, - "mju_log10": { - "value": "log10", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1037 - }, - "mju_floor": { - "value": "floor", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1038 - }, - "mju_ceil": { - "value": "ceil", - "doc": "", - "file": "third_party/install/mujoco/include/mujoco/mujoco.h", - "line": 1039 - } - }, - "hand_enums": { - "EMjActuatorType": { - "name": "EMjActuatorType", - "members": { - "Motor": 0, - "Position": 1, - "Velocity": 2, - "IntVelocity": 3, - "Damper": 4, - "Cylinder": 5, - "Muscle": 6, - "Adhesion": 7, - "DcMotor": 8 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/actuators/mjactuator.h", - "line": 42 - }, - "EMjDcMotorInput": { - "name": "EMjDcMotorInput", - "members": { - "Voltage": 0, - "Position": 1, - "Velocity": 2 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/actuators/mjdcmotoractuator.h", - "line": 35 - }, - "EMjBodySleepPolicy": { - "name": "EMjBodySleepPolicy", - "members": { - "Auto": 0, - "Never": 3, - "Allowed": 4, - "InitAsleep": 5 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/bodies/mjbody.h", - "line": 47 - }, - "EMjEqualityType": { - "name": "EMjEqualityType", - "members": { - "Connect": 0, - "Weld": 1, - "Joint": 2, - "Tendon": 3, - "Flex": 4, - "FlexVert": 5, - "FlexStrain": 6 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/constraints/mjequality.h", - "line": 36 - }, - "EMjFlexcompType": { - "name": "EMjFlexcompType", - "members": { - "Grid": 0, - "Box": 1, - "Cylinder": 2, - "Ellipsoid": 3, - "Square": 4, - "Disc": 5, - "Circle": 6, - "Mesh": 7, - "Gmsh": 8, - "Direct": 9 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/deformable/mjflexcomp.h", - "line": 36 - }, - "EMjGeomType": { - "name": "EMjGeomType", - "members": { - "Plane": 0, - "Hfield": 1, - "Sphere": 2, - "Capsule": 3, - "Ellipsoid": 4, - "Cylinder": 5, - "Box": 6, - "Mesh": 7, - "SDF": 8 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/geometry/mjgeom.h", - "line": 39 - }, - "EMjSiteType": { - "name": "EMjSiteType", - "members": { - "Sphere": 0, - "Capsule": 1, - "Ellipsoid": 2, - "Cylinder": 3, - "Box": 4, - "Mesh": 5, - "Hfield": 6 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/geometry/mjsite.h", - "line": 41 - }, - "EMjJointType": { - "name": "EMjJointType", - "members": { - "Free": 0, - "Ball": 1, - "Slide": 2, - "Hinge": 3 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/joints/mjjoint.h", - "line": 39 - }, - "EMjCameraMode": { - "name": "EMjCameraMode", - "members": { - "Real": 0, - "Depth": 1, - "SemanticSegmentation": 2, - "InstanceSegmentation": 3 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/sensors/mjcameratypes.h", - "line": 35 - }, - "EMjSensorType": { - "name": "EMjSensorType", - "members": { - "Touch": 0, - "Accelerometer": 1, - "Velocimeter": 2, - "Gyro": 3, - "Force": 4, - "Torque": 5, - "Magnetometer": 6, - "RangeFinder": 7, - "CamProjection": 8, - "JointPos": 9, - "JointVel": 10, - "TendonPos": 11, - "TendonVel": 12, - "ActuatorPos": 13, - "ActuatorVel": 14, - "ActuatorFrc": 15, - "JointActFrc": 16, - "TendonActFrc": 17, - "BallQuat": 18, - "BallAngVel": 19, - "JointLimitPos": 20, - "JointLimitVel": 21, - "JointLimitFrc": 22, - "TendonLimitPos": 23, - "TendonLimitVel": 24, - "TendonLimitFrc": 25, - "FramePos": 26, - "FrameQuat": 27, - "FrameXAxis": 28, - "FrameYAxis": 29, - "FrameZAxis": 30, - "FrameLinVel": 31, - "FrameAngVel": 32, - "FrameLinAcc": 33, - "FrameAngAcc": 34, - "SubtreeCom": 35, - "SubtreeLinVel": 36, - "SubtreeAngMom": 37, - "InsideSite": 38, - "GeomDist": 39, - "GeomNormal": 40, - "GeomFromTo": 41, - "Contact": 42, - "EPotential": 43, - "EKinetic": 44, - "Clock": 45, - "Tactile": 46, - "Plugin": 47, - "User": 48 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/sensors/mjsensor.h", - "line": 42 - }, - "EMjTendonWrapType": { - "name": "EMjTendonWrapType", - "members": { - "Joint": 0, - "Site": 1, - "Geom": 2, - "Pulley": 3 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/components/tendons/mjtendon.h", - "line": 40 - }, - "EMjDebugShaderMode": { - "name": "EMjDebugShaderMode", - "members": { - "Off": 0, - "Island": 1, - "InstanceSegmentation": 2, - "SemanticSegmentation": 3 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/core/mjdebugvisualizer.h", - "line": 39 - }, - "EMjGainType": { - "name": "EMjGainType", - "members": { - "Fixed": 0, - "Affine": 1, - "Muscle": 2, - "DcMotor": 3, - "User": 4 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 33 - }, - "EMjBiasType": { - "name": "EMjBiasType", - "members": { - "None": 0, - "Affine": 1, - "Muscle": 2, - "DcMotor": 3, - "User": 4 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 44 - }, - "EMjDynType": { - "name": "EMjDynType", - "members": { - "None": 0, - "Integrator": 1, - "Filter": 2, - "FilterExact": 3, - "Muscle": 4, - "DcMotor": 5, - "User": 6 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 55 - }, - "EMjGeomInertia": { - "name": "EMjGeomInertia", - "members": { - "Volume": 0, - "Shell": 1 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 68 - }, - "EMjFluidShape": { - "name": "EMjFluidShape", - "members": { - "None": 0, - "Ellipsoid": 1 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 76 - }, - "EMjCameraTrackingMode": { - "name": "EMjCameraTrackingMode", - "members": { - "Fixed": 0, - "Track": 1, - "TrackCom": 2, - "TargetBody": 3, - "TargetBodyCom": 4 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 84 - }, - "EMjCameraProjection": { - "name": "EMjCameraProjection", - "members": { - "Perspective": 0, - "Orthographic": 1 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 95 - }, - "EMjObjType": { - "name": "EMjObjType", - "members": { - "Unknown": 0, - "Body": 1, - "XBody": 2, - "Joint": 3, - "DoF": 4, - "Geom": 5, - "Site": 6, - "Camera": 7, - "Light": 8, - "Flex": 9, - "Mesh": 10, - "Skin": 11, - "HField": 12, - "Texture": 13, - "Material": 14, - "Pair": 15, - "Exclude": 16, - "Equality": 17, - "Tendon": 18, - "Actuator": 19, - "Sensor": 20, - "Numeric": 21, - "Text": 22, - "Tuple": 23, - "Key": 24, - "Plugin": 25, - "Frame": 100 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 103 - }, - "EMjActuatorTrnType": { - "name": "EMjActuatorTrnType", - "members": { - "Joint": 0, - "JointInParent": 1, - "SliderCrank": 2, - "Tendon": 3, - "Site": 4, - "Body": 5, - "Undefined": 6 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 136 - }, - "EMjFlexcompDof": { - "name": "EMjFlexcompDof", - "members": { - "Full": 0, - "Radial": 1, - "Trilinear": 2, - "Quadratic": 3, - "TwoD": 4 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjarticulationenums.h", - "line": 149 - }, - "EMjIntegrator": { - "name": "EMjIntegrator", - "members": { - "Euler": 0, - "RK4": 1, - "Implicit": 2, - "ImplicitFast": 3 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjoptionenums.h", - "line": 33 - }, - "EMjCone": { - "name": "EMjCone", - "members": { - "Pyramidal": 0, - "Elliptic": 1 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjoptionenums.h", - "line": 43 - }, - "EMjSolver": { - "name": "EMjSolver", - "members": { - "PGS": 0, - "CG": 1, - "Newton": 2 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/mujoco/generated/mjoptionenums.h", - "line": 51 - }, - "EMjPropertyType": { - "name": "EMjPropertyType", - "members": { - "Slider": 0, - "Toggle": 1, - "LabelOnly": 2, - "Header": 3 - }, - "member_docs": {}, - "underlying_type": "uint8", - "doc": "", - "file": "source/urlab/public/ui/mjpropertyrow.h", - "line": 38 - } - } -} \ No newline at end of file diff --git a/Scripts/codegen/snapshots/mjcf_schema_snapshot.json b/Scripts/codegen/snapshots/mjcf_schema_snapshot.json deleted file mode 100644 index 863c7b68..00000000 --- a/Scripts/codegen/snapshots/mjcf_schema_snapshot.json +++ /dev/null @@ -1,1181 +0,0 @@ -{ - "_meta": { - "mujoco_version": "3.10.0", - "snapshot_date": "2026-06-14", - "source": "https://github.com/google-deepmind/mujoco/blob/main/src/xml/xml_native_reader.cc", - "description": "MJCF schema snapshot, auto-extracted by Scripts/codegen/build_mjcf_schema_snapshot.py from the MuJoCo submodule's xml_native_reader.cc." - }, - "body": { - "attrs": [ - "name", - "childclass", - "pos", - "quat", - "mocap", - "axisangle", - "xyaxes", - "zaxis", - "euler", - "gravcomp", - "sleep", - "user" - ], - "children": [ - "inertial", - "joint", - "freejoint", - "geom", - "attach", - "site", - "camera", - "light", - "plugin", - "composite", - "flexcomp" - ] - }, - "inertial": { - "attrs": [ - "pos", - "quat", - "mass", - "diaginertia", - "axisangle", - "xyaxes", - "zaxis", - "euler", - "fullinertia" - ] - }, - "joint": { - "attrs": [ - "name", - "class", - "type", - "group", - "pos", - "axis", - "springdamper", - "limited", - "actuatorfrclimited", - "solreflimit", - "solimplimit", - "solreffriction", - "solimpfriction", - "stiffness", - "range", - "actuatorfrcrange", - "actuatorgravcomp", - "margin", - "ref", - "springref", - "armature", - "damping", - "frictionloss", - "user" - ] - }, - "freejoint": { - "attrs": [ - "name", - "group", - "align" - ] - }, - "geom": { - "attrs": [ - "name", - "class", - "type", - "contype", - "conaffinity", - "condim", - "group", - "priority", - "size", - "material", - "friction", - "mass", - "density", - "shellinertia", - "solmix", - "solref", - "solimp", - "margin", - "gap", - "fromto", - "pos", - "quat", - "axisangle", - "xyaxes", - "zaxis", - "euler", - "hfield", - "mesh", - "fitscale", - "rgba", - "fluidshape", - "fluidcoef", - "user" - ], - "children": [ - "plugin" - ] - }, - "site": { - "attrs": [ - "name", - "class", - "type", - "group", - "pos", - "quat", - "material", - "size", - "fromto", - "axisangle", - "xyaxes", - "zaxis", - "euler", - "rgba", - "user" - ] - }, - "camera": { - "attrs": [ - "name", - "class", - "projection", - "fovy", - "ipd", - "resolution", - "output", - "pos", - "quat", - "axisangle", - "xyaxes", - "zaxis", - "euler", - "mode", - "target", - "focal", - "focalpixel", - "principal", - "principalpixel", - "sensorsize", - "user" - ] - }, - "light": { - "attrs": [ - "name", - "class", - "directional", - "type", - "castshadow", - "active", - "pos", - "dir", - "bulbradius", - "intensity", - "range", - "attenuation", - "cutoff", - "exponent", - "ambient", - "diffuse", - "specular", - "mode", - "target", - "texture" - ] - }, - "actuator_common": { - "attrs": [ - "name", - "class", - "group", - "nsample", - "interp", - "delay", - "ctrllimited", - "forcelimited", - "actlimited", - "ctrlrange", - "forcerange", - "actrange", - "lengthrange", - "gear", - "damping", - "armature", - "cranklength", - "user" - ], - "targets": [ - "joint", - "jointinparent", - "tendon", - "slidersite", - "cranksite", - "site", - "refsite" - ] - }, - "actuator_types": { - "general": [ - "body", - "actdim", - "dyntype", - "gaintype", - "biastype", - "dynprm", - "gainprm", - "biasprm", - "actearly" - ], - "motor": [], - "position": [ - "inheritrange", - "kp", - "kv", - "dampratio", - "timeconst" - ], - "velocity": [ - "kv" - ], - "intvelocity": [ - "inheritrange", - "kp", - "kv", - "dampratio" - ], - "damper": [ - "kv" - ], - "cylinder": [ - "timeconst", - "area", - "diameter", - "bias" - ], - "muscle": [ - "timeconst", - "tausmooth", - "range", - "force", - "scale", - "lmin", - "lmax", - "vmax", - "fpmax", - "fvmax" - ], - "adhesion": [ - "body", - "gain" - ], - "dcmotor": [ - "motorconst", - "resistance", - "nominal", - "saturation", - "inductance", - "cogging", - "controller", - "thermal", - "lugre", - "input" - ] - }, - "sensor_types": [ - "touch", - "accelerometer", - "velocimeter", - "gyro", - "force", - "torque", - "magnetometer", - "camprojection", - "rangefinder", - "jointpos", - "jointvel", - "tendonpos", - "tendonvel", - "actuatorpos", - "actuatorvel", - "actuatorfrc", - "jointactuatorfrc", - "tendonactuatorfrc", - "ballquat", - "ballangvel", - "jointlimitpos", - "jointlimitvel", - "jointlimitfrc", - "tendonlimitpos", - "tendonlimitvel", - "tendonlimitfrc", - "framepos", - "framequat", - "framexaxis", - "frameyaxis", - "framezaxis", - "framelinvel", - "frameangvel", - "framelinacc", - "frameangacc", - "subtreecom", - "subtreelinvel", - "subtreeangmom", - "insidesite", - "distance", - "normal", - "fromto", - "contact", - "e_potential", - "e_kinetic", - "clock", - "tactile", - "user", - "plugin" - ], - "sensor_common": { - "attrs": [ - "name", - "nsample", - "interp", - "delay", - "interval", - "cutoff", - "noise", - "user" - ] - }, - "sensor_per_type": { - "touch": { - "mj_type": "mjSENS_TOUCH", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "accelerometer": { - "mj_type": "mjSENS_ACCELEROMETER", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "velocimeter": { - "mj_type": "mjSENS_VELOCIMETER", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "gyro": { - "mj_type": "mjSENS_GYRO", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "force": { - "mj_type": "mjSENS_FORCE", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "torque": { - "mj_type": "mjSENS_TORQUE", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "magnetometer": { - "mj_type": "mjSENS_MAGNETOMETER", - "objtype": "mjOBJ_SITE", - "reftype": null, - "obj_attr": "site", - "ref_attr": null, - "computed": false - }, - "camprojection": { - "mj_type": "mjSENS_CAMPROJECTION", - "objtype": "mjOBJ_SITE", - "reftype": "mjOBJ_CAMERA", - "obj_attr": "site", - "ref_attr": "camera", - "computed": false - }, - "rangefinder": { - "mj_type": "mjSENS_RANGEFINDER", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "site", - "ref_attr": null, - "computed": true - }, - "jointpos": { - "mj_type": "mjSENS_JOINTPOS", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "jointvel": { - "mj_type": "mjSENS_JOINTVEL", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "tendonpos": { - "mj_type": "mjSENS_TENDONPOS", - "objtype": "mjOBJ_TENDON", - "reftype": null, - "obj_attr": "tendon", - "ref_attr": null, - "computed": false - }, - "tendonvel": { - "mj_type": "mjSENS_TENDONVEL", - "objtype": "mjOBJ_TENDON", - "reftype": null, - "obj_attr": "tendon", - "ref_attr": null, - "computed": false - }, - "actuatorpos": { - "mj_type": "mjSENS_ACTUATORPOS", - "objtype": "mjOBJ_ACTUATOR", - "reftype": null, - "obj_attr": "actuator", - "ref_attr": null, - "computed": false - }, - "actuatorvel": { - "mj_type": "mjSENS_ACTUATORVEL", - "objtype": "mjOBJ_ACTUATOR", - "reftype": null, - "obj_attr": "actuator", - "ref_attr": null, - "computed": false - }, - "actuatorfrc": { - "mj_type": "mjSENS_ACTUATORFRC", - "objtype": "mjOBJ_ACTUATOR", - "reftype": null, - "obj_attr": "actuator", - "ref_attr": null, - "computed": false - }, - "jointactuatorfrc": { - "mj_type": "mjSENS_JOINTACTFRC", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "tendonactuatorfrc": { - "mj_type": "mjSENS_TENDONACTFRC", - "objtype": "mjOBJ_TENDON", - "reftype": null, - "obj_attr": "tendon", - "ref_attr": null, - "computed": false - }, - "ballquat": { - "mj_type": "mjSENS_BALLQUAT", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "ballangvel": { - "mj_type": "mjSENS_BALLANGVEL", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "jointlimitpos": { - "mj_type": "mjSENS_JOINTLIMITPOS", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "jointlimitvel": { - "mj_type": "mjSENS_JOINTLIMITVEL", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "jointlimitfrc": { - "mj_type": "mjSENS_JOINTLIMITFRC", - "objtype": "mjOBJ_JOINT", - "reftype": null, - "obj_attr": "joint", - "ref_attr": null, - "computed": false - }, - "tendonlimitpos": { - "mj_type": "mjSENS_TENDONLIMITPOS", - "objtype": "mjOBJ_TENDON", - "reftype": null, - "obj_attr": "tendon", - "ref_attr": null, - "computed": false - }, - "tendonlimitvel": { - "mj_type": "mjSENS_TENDONLIMITVEL", - "objtype": "mjOBJ_TENDON", - "reftype": null, - "obj_attr": "tendon", - "ref_attr": null, - "computed": false - }, - "tendonlimitfrc": { - "mj_type": "mjSENS_TENDONLIMITFRC", - "objtype": "mjOBJ_TENDON", - "reftype": null, - "obj_attr": "tendon", - "ref_attr": null, - "computed": false - }, - "framepos": { - "mj_type": "mjSENS_FRAMEPOS", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "framequat": { - "mj_type": "mjSENS_FRAMEQUAT", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "framexaxis": { - "mj_type": "mjSENS_FRAMEXAXIS", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "frameyaxis": { - "mj_type": "mjSENS_FRAMEYAXIS", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "framezaxis": { - "mj_type": "mjSENS_FRAMEZAXIS", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "framelinvel": { - "mj_type": "mjSENS_FRAMELINVEL", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "frameangvel": { - "mj_type": "mjSENS_FRAMEANGVEL", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": "refname", - "computed": false - }, - "framelinacc": { - "mj_type": "mjSENS_FRAMELINACC", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": null, - "computed": false - }, - "frameangacc": { - "mj_type": "mjSENS_FRAMEANGACC", - "objtype": "from_xml", - "reftype": null, - "obj_attr": "objname", - "ref_attr": null, - "computed": false - }, - "subtreecom": { - "mj_type": "mjSENS_SUBTREECOM", - "objtype": "mjOBJ_BODY", - "reftype": null, - "obj_attr": "body", - "ref_attr": null, - "computed": false - }, - "subtreelinvel": { - "mj_type": "mjSENS_SUBTREELINVEL", - "objtype": "mjOBJ_BODY", - "reftype": null, - "obj_attr": "body", - "ref_attr": null, - "computed": false - }, - "subtreeangmom": { - "mj_type": "mjSENS_SUBTREEANGMOM", - "objtype": "mjOBJ_BODY", - "reftype": null, - "obj_attr": "body", - "ref_attr": null, - "computed": false - }, - "insidesite": { - "mj_type": "mjSENS_INSIDESITE", - "objtype": "from_xml", - "reftype": "mjOBJ_SITE", - "obj_attr": "objname", - "ref_attr": "site", - "computed": false - }, - "distance": { - "mj_type": "mjSENS_GEOMDIST", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "body1", - "ref_attr": "body2", - "computed": true - }, - "normal": { - "mj_type": "mjSENS_GEOMNORMAL", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "body1", - "ref_attr": "body2", - "computed": true - }, - "fromto": { - "mj_type": "mjSENS_GEOMFROMTO", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "body1", - "ref_attr": "body2", - "computed": true - }, - "contact": { - "mj_type": "mjSENS_CONTACT", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "site", - "ref_attr": "body2", - "computed": true - }, - "e_potential": { - "mj_type": "mjSENS_E_POTENTIAL", - "objtype": "mjOBJ_UNKNOWN", - "reftype": null, - "obj_attr": null, - "ref_attr": null, - "computed": false - }, - "e_kinetic": { - "mj_type": "mjSENS_E_KINETIC", - "objtype": "mjOBJ_UNKNOWN", - "reftype": null, - "obj_attr": null, - "ref_attr": null, - "computed": false - }, - "clock": { - "mj_type": "mjSENS_CLOCK", - "objtype": "mjOBJ_UNKNOWN", - "reftype": null, - "obj_attr": null, - "ref_attr": null, - "computed": false - }, - "tactile": { - "mj_type": "mjSENS_TACTILE", - "objtype": "mjOBJ_MESH", - "reftype": "mjOBJ_GEOM", - "obj_attr": "mesh", - "ref_attr": "geom", - "computed": false - }, - "user": { - "mj_type": "mjSENS_USER", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "objname", - "ref_attr": null, - "computed": true - }, - "plugin": { - "mj_type": "mjSENS_PLUGIN", - "objtype": "computed", - "reftype": "computed", - "obj_attr": "objname", - "ref_attr": "refname", - "computed": true - } - }, - "tendon": { - "spatial": { - "attrs": [ - "name", - "class", - "group", - "limited", - "actuatorfrclimited", - "range", - "actuatorfrcrange", - "solreflimit", - "solimplimit", - "solreffriction", - "solimpfriction", - "frictionloss", - "springlength", - "width", - "material", - "margin", - "stiffness", - "damping", - "armature", - "rgba", - "user" - ], - "wrap_types": [ - "site", - "geom", - "pulley" - ] - }, - "fixed": { - "attrs": [ - "name", - "class", - "group", - "limited", - "actuatorfrclimited", - "range", - "actuatorfrcrange", - "solreflimit", - "solimplimit", - "solreffriction", - "solimpfriction", - "frictionloss", - "springlength", - "margin", - "stiffness", - "damping", - "armature", - "user" - ], - "wrap_types": [ - "joint" - ] - } - }, - "equality": { - "connect": [ - "name", - "class", - "body1", - "body2", - "anchor", - "site1", - "site2", - "active", - "solref", - "solimp" - ], - "weld": [ - "name", - "class", - "body1", - "body2", - "relpose", - "anchor", - "site1", - "site2", - "active", - "solref", - "solimp", - "torquescale" - ], - "joint": [ - "name", - "class", - "joint1", - "joint2", - "polycoef", - "active", - "solref", - "solimp" - ], - "tendon": [ - "name", - "class", - "tendon1", - "tendon2", - "polycoef", - "active", - "solref", - "solimp" - ], - "flex": [ - "name", - "class", - "flex", - "active", - "solref", - "solimp" - ], - "flexvert": [ - "name", - "class", - "flex", - "active", - "solref", - "solimp" - ], - "flexstrain": [ - "name", - "class", - "flex", - "cell", - "active", - "solref", - "solimp" - ] - }, - "contact": { - "pair": [ - "name", - "class", - "geom1", - "geom2", - "condim", - "friction", - "solref", - "solreffriction", - "solimp", - "gap", - "margin" - ], - "exclude": [ - "name", - "body1", - "body2" - ] - }, - "asset": { - "mesh": [ - "name", - "class", - "content_type", - "file", - "vertex", - "normal", - "texcoord", - "face", - "refpos", - "refquat", - "scale", - "smoothnormal", - "maxhullvert", - "inertia", - "builtin", - "params", - "material" - ], - "hfield": [ - "name", - "content_type", - "file", - "nrow", - "ncol", - "size", - "elevation" - ], - "skin": [ - "name", - "file", - "material", - "rgba", - "inflate", - "vertex", - "texcoord", - "face", - "group" - ], - "texture": [ - "name", - "type", - "colorspace", - "content_type", - "file", - "gridsize", - "gridlayout", - "fileright", - "fileleft", - "fileup", - "filedown", - "filefront", - "fileback", - "builtin", - "rgb1", - "rgb2", - "mark", - "markrgb", - "random", - "width", - "height", - "hflip", - "vflip", - "nchannel" - ], - "material": [ - "name", - "class", - "texture", - "texrepeat", - "texuniform", - "emission", - "specular", - "shininess", - "reflectance", - "metallic", - "roughness", - "rgba" - ], - "model": [ - "name", - "file", - "content_type" - ] - }, - "compiler": { - "attrs": [ - "autolimits", - "boundmass", - "boundinertia", - "settotalmass", - "balanceinertia", - "strippath", - "coordinate", - "angle", - "fitaabb", - "eulerseq", - "meshdir", - "texturedir", - "discardvisual", - "usethread", - "fusestatic", - "inertiafromgeom", - "inertiagrouprange", - "saveinertial", - "assetdir", - "alignfree" - ] - }, - "option": { - "attrs": [ - "timestep", - "impratio", - "tolerance", - "ls_tolerance", - "noslip_tolerance", - "ccd_tolerance", - "sleep_tolerance", - "gravity", - "wind", - "magnetic", - "density", - "viscosity", - "o_margin", - "o_solref", - "o_solimp", - "o_friction", - "integrator", - "cone", - "jacobian", - "solver", - "iterations", - "ls_iterations", - "noslip_iterations", - "ccd_iterations", - "sdf_iterations", - "sdf_initpoints", - "actuatorgroupdisable" - ], - "flag": [ - "constraint", - "equality", - "frictionloss", - "limit", - "contact", - "spring", - "damper", - "gravity", - "clampctrl", - "warmstart", - "filterparent", - "actuation", - "refsafe", - "sensor", - "midphase", - "eulerdamp", - "autoreset", - "nativeccd", - "island", - "override", - "energy", - "fwdinv", - "invdiscrete", - "multiccd", - "sleep", - "diagexact" - ] - }, - "keyframe": { - "key": [ - "name", - "time", - "qpos", - "qvel", - "act", - "mpos", - "mquat", - "ctrl" - ] - }, - "default": { - "child_elements": [ - "mesh", - "material", - "joint", - "geom", - "site", - "camera", - "light", - "pair", - "equality", - "tendon", - "general", - "motor", - "position", - "velocity", - "intvelocity", - "damper", - "cylinder", - "muscle", - "adhesion", - "dcmotor" - ] - }, - "flex": { - "attrs": [ - "name", - "group", - "dim", - "radius", - "material", - "rgba", - "flatskin", - "body", - "vertex", - "element", - "texcoord", - "elemtexcoord", - "node", - "cellcount", - "dof" - ], - "children": [ - "contact", - "edge", - "elasticity" - ] - }, - "skin": { - "attrs": [ - "name", - "file", - "material", - "rgba", - "inflate", - "vertex", - "texcoord", - "face", - "group" - ], - "children": [ - "bone" - ] - }, - "flexcomp": { - "attrs": [ - "name", - "type", - "group", - "dim", - "dof", - "count", - "cellcount", - "spacing", - "radius", - "rigid", - "mass", - "inertiabox", - "scale", - "file", - "point", - "element", - "texcoord", - "material", - "rgba", - "flatskin", - "pos", - "quat", - "axisangle", - "xyaxes", - "zaxis", - "euler", - "origin" - ], - "children": [ - "edge", - "elasticity", - "contact", - "pin", - "plugin" - ] - } -} diff --git a/Scripts/codegen/snapshots/mjxmacro_snapshot.json b/Scripts/codegen/snapshots/mjxmacro_snapshot.json deleted file mode 100644 index 65c7660a..00000000 --- a/Scripts/codegen/snapshots/mjxmacro_snapshot.json +++ /dev/null @@ -1,4301 +0,0 @@ -{ - "_meta": { - "source": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "schema_kind": "mjxmacro", - "struct_field_sources": { - "MJOPTION_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJSTATISTIC_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJVISUAL_GLOBAL_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJVISUAL_QUALITY_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJVISUAL_HEADLIGHT_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJVISUAL_MAP_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJVISUAL_SCALE_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJVISUAL_RGBA_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjxmacro.h", - "MJSCOMPILER_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjspecmacro.h", - "MJSPEC_FIELDS": "third_party\\install\\MuJoCo\\include\\mujoco\\mjspecmacro.h" - } - }, - "mjmodel_pointers": { - "MJMODEL_POINTERS_BODY": [ - { - "type": "int", - "name": "body_parentid", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_rootid", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_weldid", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_mocapid", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_jntnum", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_jntadr", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_dofnum", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_dofadr", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_treeid", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_geomnum", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_geomadr", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtByte", - "name": "body_simple", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtByte", - "name": "body_sameframe", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "body_pos", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "body_quat", - "outer_dim": "nbody", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "body_ipos", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "body_iquat", - "outer_dim": "nbody", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "body_mass", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "body_subtreemass", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "body_inertia", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "body_invweight0", - "outer_dim": "nbody", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "body_gravcomp", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "body_margin", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "body_user", - "outer_dim": "nbody", - "stride": "MJ_M(nuser_body)" - }, - { - "type": "int", - "name": "body_plugin", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_contype", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_conaffinity", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_bvhadr", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_bvhnum", - "outer_dim": "nbody", - "stride": "1" - } - ], - "MJMODEL_POINTERS_JOINT": [ - { - "type": "int", - "name": "jnt_type", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "int", - "name": "jnt_qposadr", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "int", - "name": "jnt_dofadr", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "int", - "name": "jnt_bodyid", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "int", - "name": "jnt_actuatorid", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "int", - "name": "jnt_group", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "jnt_limited", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "jnt_actfrclimited", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "jnt_actgravcomp", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "jnt_solref", - "outer_dim": "njnt", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "jnt_solimp", - "outer_dim": "njnt", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "jnt_pos", - "outer_dim": "njnt", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "jnt_axis", - "outer_dim": "njnt", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "jnt_stiffness", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "jnt_stiffnesspoly", - "outer_dim": "njnt", - "stride": "mjNPOLY" - }, - { - "type": "mjtNum", - "name": "jnt_range", - "outer_dim": "njnt", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "jnt_actfrcrange", - "outer_dim": "njnt", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "jnt_margin", - "outer_dim": "njnt", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "jnt_user", - "outer_dim": "njnt", - "stride": "MJ_M(nuser_jnt)" - } - ], - "MJMODEL_POINTERS_DOF": [ - { - "type": "int", - "name": "dof_bodyid", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "int", - "name": "dof_jntid", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "int", - "name": "dof_parentid", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "int", - "name": "dof_treeid", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "int", - "name": "dof_Madr", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "int", - "name": "dof_simplenum", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "dof_solref", - "outer_dim": "nv", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "dof_solimp", - "outer_dim": "nv", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "dof_frictionloss", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "dof_armature", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "dof_damping", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "dof_dampingpoly", - "outer_dim": "nv", - "stride": "mjNPOLY" - }, - { - "type": "mjtNum", - "name": "dof_invweight0", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "dof_M0", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "dof_length", - "outer_dim": "nv", - "stride": "1" - } - ], - "MJMODEL_POINTERS_TREE": [ - { - "type": "int", - "name": "tree_bodyadr", - "outer_dim": "ntree", - "stride": "1" - }, - { - "type": "int", - "name": "tree_bodynum", - "outer_dim": "ntree", - "stride": "1" - }, - { - "type": "int", - "name": "tree_dofadr", - "outer_dim": "ntree", - "stride": "1" - }, - { - "type": "int", - "name": "tree_dofnum", - "outer_dim": "ntree", - "stride": "1" - }, - { - "type": "int", - "name": "tree_sleep_policy", - "outer_dim": "ntree", - "stride": "1" - } - ], - "MJMODEL_POINTERS_GEOM": [ - { - "type": "int", - "name": "geom_type", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_contype", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_conaffinity", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_condim", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_bodyid", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_dataid", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_matid", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_group", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_priority", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "int", - "name": "geom_plugin", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "mjtByte", - "name": "geom_sameframe", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "geom_solmix", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "geom_solref", - "outer_dim": "ngeom", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "geom_solimp", - "outer_dim": "ngeom", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "geom_size", - "outer_dim": "ngeom", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "geom_aabb", - "outer_dim": "ngeom", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "geom_rbound", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "geom_pos", - "outer_dim": "ngeom", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "geom_quat", - "outer_dim": "ngeom", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "geom_friction", - "outer_dim": "ngeom", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "geom_margin", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "geom_gap", - "outer_dim": "ngeom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "geom_fluid", - "outer_dim": "ngeom", - "stride": "mjNFLUID" - }, - { - "type": "mjtNum", - "name": "geom_user", - "outer_dim": "ngeom", - "stride": "MJ_M(nuser_geom)" - }, - { - "type": "float", - "name": "geom_rgba", - "outer_dim": "ngeom", - "stride": "4" - } - ], - "MJMODEL_POINTERS_SITE": [ - { - "type": "int", - "name": "site_type", - "outer_dim": "nsite", - "stride": "1" - }, - { - "type": "int", - "name": "site_bodyid", - "outer_dim": "nsite", - "stride": "1" - }, - { - "type": "int", - "name": "site_matid", - "outer_dim": "nsite", - "stride": "1" - }, - { - "type": "int", - "name": "site_group", - "outer_dim": "nsite", - "stride": "1" - }, - { - "type": "mjtByte", - "name": "site_sameframe", - "outer_dim": "nsite", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "site_size", - "outer_dim": "nsite", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "site_pos", - "outer_dim": "nsite", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "site_quat", - "outer_dim": "nsite", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "site_user", - "outer_dim": "nsite", - "stride": "MJ_M(nuser_site)" - }, - { - "type": "float", - "name": "site_rgba", - "outer_dim": "nsite", - "stride": "4" - } - ], - "MJMODEL_POINTERS_CAMERA": [ - { - "type": "int", - "name": "cam_mode", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "int", - "name": "cam_bodyid", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "int", - "name": "cam_targetbodyid", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "cam_pos", - "outer_dim": "ncam", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "cam_quat", - "outer_dim": "ncam", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "cam_poscom0", - "outer_dim": "ncam", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "cam_pos0", - "outer_dim": "ncam", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "cam_mat0", - "outer_dim": "ncam", - "stride": "9" - }, - { - "type": "int", - "name": "cam_projection", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "cam_fovy", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "cam_ipd", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "int", - "name": "cam_resolution", - "outer_dim": "ncam", - "stride": "2" - }, - { - "type": "int", - "name": "cam_output", - "outer_dim": "ncam", - "stride": "1" - }, - { - "type": "float", - "name": "cam_sensorsize", - "outer_dim": "ncam", - "stride": "2" - }, - { - "type": "float", - "name": "cam_intrinsic", - "outer_dim": "ncam", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "cam_user", - "outer_dim": "ncam", - "stride": "MJ_M(nuser_cam)" - } - ], - "MJMODEL_POINTERS_LIGHT": [ - { - "type": "int", - "name": "light_mode", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "int", - "name": "light_bodyid", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "int", - "name": "light_targetbodyid", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "int", - "name": "light_type", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "int", - "name": "light_texid", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "light_castshadow", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "float", - "name": "light_bulbradius", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "float", - "name": "light_intensity", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "float", - "name": "light_range", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "light_active", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "light_pos", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "light_dir", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "light_poscom0", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "light_pos0", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "light_dir0", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "float", - "name": "light_attenuation", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "float", - "name": "light_cutoff", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "float", - "name": "light_exponent", - "outer_dim": "nlight", - "stride": "1" - }, - { - "type": "float", - "name": "light_ambient", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "float", - "name": "light_diffuse", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "float", - "name": "light_specular", - "outer_dim": "nlight", - "stride": "3" - } - ], - "MJMODEL_POINTERS_FLEX": [ - { - "type": "int", - "name": "flex_contype", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_conaffinity", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_condim", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_priority", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_solmix", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_solref", - "outer_dim": "nflex", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "flex_solimp", - "outer_dim": "nflex", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "flex_friction", - "outer_dim": "nflex", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flex_margin", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_gap", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "flex_internal", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_selfcollide", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_activelayers", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_passive", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_dim", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_matid", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_group", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_interp", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_cellnum", - "outer_dim": "nflex", - "stride": "3" - }, - { - "type": "int", - "name": "flex_nodeadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_nodenum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_vertadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_vertnum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_edgeadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_edgenum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemnum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemdataadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_stiffnessadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemedgeadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_bendingadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_shellnum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_shelldataadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_evpairadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_evpairnum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_texcoordadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_nodebodyid", - "outer_dim": "nflexnode", - "stride": "1" - }, - { - "type": "int", - "name": "flex_vertbodyid", - "outer_dim": "nflexvert", - "stride": "1" - }, - { - "type": "int", - "name": "flex_vertedgeadr", - "outer_dim": "nflexvert", - "stride": "1" - }, - { - "type": "int", - "name": "flex_vertedgenum", - "outer_dim": "nflexvert", - "stride": "1" - }, - { - "type": "int", - "name": "flex_vertedge", - "outer_dim": "nflexedge", - "stride": "2" - }, - { - "type": "int", - "name": "flex_edge", - "outer_dim": "nflexedge", - "stride": "2" - }, - { - "type": "int", - "name": "flex_edgeflap", - "outer_dim": "nflexedge", - "stride": "2" - }, - { - "type": "int", - "name": "flex_elem", - "outer_dim": "nflexelemdata", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemtexcoord", - "outer_dim": "nflexelemdata", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemedge", - "outer_dim": "nflexelemedge", - "stride": "1" - }, - { - "type": "int", - "name": "flex_elemlayer", - "outer_dim": "nflexelem", - "stride": "1" - }, - { - "type": "int", - "name": "flex_shell", - "outer_dim": "nflexshelldata", - "stride": "1" - }, - { - "type": "int", - "name": "flex_evpair", - "outer_dim": "nflexevpair", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "flex_vert", - "outer_dim": "nflexvert", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flex_vert0", - "outer_dim": "nflexvert", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flex_vertmetric", - "outer_dim": "nflexvert", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "flex_node", - "outer_dim": "nflexnode", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flex_node0", - "outer_dim": "nflexnode", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flexedge_length0", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flexedge_invweight0", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_radius", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_size", - "outer_dim": "nflex", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flex_stiffness", - "outer_dim": "nflexstiffness", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_bending", - "outer_dim": "nflexbending", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_damping", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_edgestiffness", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flex_edgedamping", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_edgeequality", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "flex_rigid", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "flexedge_rigid", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "flex_centered", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "flex_flatskin", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_bvhadr", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flex_bvhnum", - "outer_dim": "nflex", - "stride": "1" - }, - { - "type": "int", - "name": "flexedge_J_rownnz", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "int", - "name": "flexedge_J_rowadr", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "int", - "name": "flexedge_J_colind", - "outer_dim": "nJfe", - "stride": "1" - }, - { - "type": "int", - "name": "flexvert_J_rownnz", - "outer_dim": "nflexvert", - "stride": "2" - }, - { - "type": "int", - "name": "flexvert_J_rowadr", - "outer_dim": "nflexvert", - "stride": "2" - }, - { - "type": "int", - "name": "flexvert_J_colind", - "outer_dim": "nJfv", - "stride": "2" - }, - { - "type": "float", - "name": "flex_rgba", - "outer_dim": "nflex", - "stride": "4" - }, - { - "type": "float", - "name": "flex_texcoord", - "outer_dim": "nflextexcoord", - "stride": "2" - } - ], - "MJMODEL_POINTERS_MESH": [ - { - "type": "int", - "name": "mesh_vertadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_vertnum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_faceadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_facenum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_bvhadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_bvhnum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_octadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_octnum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_normaladr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_normalnum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_texcoordadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_texcoordnum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_graphadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "float", - "name": "mesh_vert", - "outer_dim": "nmeshvert", - "stride": "3" - }, - { - "type": "float", - "name": "mesh_normal", - "outer_dim": "nmeshnormal", - "stride": "3" - }, - { - "type": "float", - "name": "mesh_texcoord", - "outer_dim": "nmeshtexcoord", - "stride": "2" - }, - { - "type": "int", - "name": "mesh_face", - "outer_dim": "nmeshface", - "stride": "3" - }, - { - "type": "int", - "name": "mesh_facenormal", - "outer_dim": "nmeshface", - "stride": "3" - }, - { - "type": "int", - "name": "mesh_facetexcoord", - "outer_dim": "nmeshface", - "stride": "3" - }, - { - "type": "int", - "name": "mesh_graph", - "outer_dim": "nmeshgraph", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "mesh_scale", - "outer_dim": "nmesh", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "mesh_pos", - "outer_dim": "nmesh", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "mesh_quat", - "outer_dim": "nmesh", - "stride": "4" - }, - { - "type": "int", - "name": "mesh_pathadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polynum", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polyadr", - "outer_dim": "nmesh", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "mesh_polynormal", - "outer_dim": "nmeshpoly", - "stride": "3" - }, - { - "type": "int", - "name": "mesh_polyvertadr", - "outer_dim": "nmeshpoly", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polyvertnum", - "outer_dim": "nmeshpoly", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polyvert", - "outer_dim": "nmeshpolyvert", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polymapadr", - "outer_dim": "nmeshvert", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polymapnum", - "outer_dim": "nmeshvert", - "stride": "1" - }, - { - "type": "int", - "name": "mesh_polymap", - "outer_dim": "nmeshpolymap", - "stride": "1" - } - ], - "MJMODEL_POINTERS_SKIN": [ - { - "type": "int", - "name": "skin_matid", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_group", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "float", - "name": "skin_rgba", - "outer_dim": "nskin", - "stride": "4" - }, - { - "type": "float", - "name": "skin_inflate", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_vertadr", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_vertnum", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_texcoordadr", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_faceadr", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_facenum", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_boneadr", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "int", - "name": "skin_bonenum", - "outer_dim": "nskin", - "stride": "1" - }, - { - "type": "float", - "name": "skin_vert", - "outer_dim": "nskinvert", - "stride": "3" - }, - { - "type": "float", - "name": "skin_texcoord", - "outer_dim": "nskintexvert", - "stride": "2" - }, - { - "type": "int", - "name": "skin_face", - "outer_dim": "nskinface", - "stride": "3" - }, - { - "type": "int", - "name": "skin_bonevertadr", - "outer_dim": "nskinbone", - "stride": "1" - }, - { - "type": "int", - "name": "skin_bonevertnum", - "outer_dim": "nskinbone", - "stride": "1" - }, - { - "type": "float", - "name": "skin_bonebindpos", - "outer_dim": "nskinbone", - "stride": "3" - }, - { - "type": "float", - "name": "skin_bonebindquat", - "outer_dim": "nskinbone", - "stride": "4" - }, - { - "type": "int", - "name": "skin_bonebodyid", - "outer_dim": "nskinbone", - "stride": "1" - }, - { - "type": "int", - "name": "skin_bonevertid", - "outer_dim": "nskinbonevert", - "stride": "1" - }, - { - "type": "float", - "name": "skin_bonevertweight", - "outer_dim": "nskinbonevert", - "stride": "1" - }, - { - "type": "int", - "name": "skin_pathadr", - "outer_dim": "nskin", - "stride": "1" - } - ], - "MJMODEL_POINTERS_HFIELD": [ - { - "type": "mjtNum", - "name": "hfield_size", - "outer_dim": "nhfield", - "stride": "4" - }, - { - "type": "int", - "name": "hfield_nrow", - "outer_dim": "nhfield", - "stride": "1" - }, - { - "type": "int", - "name": "hfield_ncol", - "outer_dim": "nhfield", - "stride": "1" - }, - { - "type": "int", - "name": "hfield_adr", - "outer_dim": "nhfield", - "stride": "1" - }, - { - "type": "float", - "name": "hfield_data", - "outer_dim": "nhfielddata", - "stride": "1" - }, - { - "type": "int", - "name": "hfield_pathadr", - "outer_dim": "nhfield", - "stride": "1" - } - ], - "MJMODEL_POINTERS_TEXTURE": [ - { - "type": "int", - "name": "tex_type", - "outer_dim": "ntex", - "stride": "1" - }, - { - "type": "int", - "name": "tex_colorspace", - "outer_dim": "ntex", - "stride": "1" - }, - { - "type": "int", - "name": "tex_height", - "outer_dim": "ntex", - "stride": "1" - }, - { - "type": "int", - "name": "tex_width", - "outer_dim": "ntex", - "stride": "1" - }, - { - "type": "int", - "name": "tex_nchannel", - "outer_dim": "ntex", - "stride": "1" - }, - { - "type": "mjtSize", - "name": "tex_adr", - "outer_dim": "ntex", - "stride": "1" - }, - { - "type": "mjtByte", - "name": "tex_data", - "outer_dim": "ntexdata", - "stride": "1" - }, - { - "type": "int", - "name": "tex_pathadr", - "outer_dim": "ntex", - "stride": "1" - } - ], - "MJMODEL_POINTERS_MATERIAL": [ - { - "type": "int", - "name": "mat_texid", - "outer_dim": "nmat", - "stride": "mjNTEXROLE" - }, - { - "type": "mjtBool", - "name": "mat_texuniform", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_texrepeat", - "outer_dim": "nmat", - "stride": "2" - }, - { - "type": "float", - "name": "mat_emission", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_specular", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_shininess", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_reflectance", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_metallic", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_roughness", - "outer_dim": "nmat", - "stride": "1" - }, - { - "type": "float", - "name": "mat_rgba", - "outer_dim": "nmat", - "stride": "4" - } - ], - "MJMODEL_POINTERS_PAIR": [ - { - "type": "int", - "name": "pair_dim", - "outer_dim": "npair", - "stride": "1" - }, - { - "type": "int", - "name": "pair_geom1", - "outer_dim": "npair", - "stride": "1" - }, - { - "type": "int", - "name": "pair_geom2", - "outer_dim": "npair", - "stride": "1" - }, - { - "type": "int", - "name": "pair_signature", - "outer_dim": "npair", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "pair_solref", - "outer_dim": "npair", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "pair_solreffriction", - "outer_dim": "npair", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "pair_solimp", - "outer_dim": "npair", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "pair_margin", - "outer_dim": "npair", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "pair_gap", - "outer_dim": "npair", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "pair_friction", - "outer_dim": "npair", - "stride": "5" - } - ], - "MJMODEL_POINTERS_EXCLUDE": [ - { - "type": "int", - "name": "exclude_signature", - "outer_dim": "nexclude", - "stride": "1" - } - ], - "MJMODEL_POINTERS_EQUALITY": [ - { - "type": "int", - "name": "eq_type", - "outer_dim": "neq", - "stride": "1" - }, - { - "type": "int", - "name": "eq_obj1id", - "outer_dim": "neq", - "stride": "1" - }, - { - "type": "int", - "name": "eq_obj2id", - "outer_dim": "neq", - "stride": "1" - }, - { - "type": "int", - "name": "eq_objtype", - "outer_dim": "neq", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "eq_active0", - "outer_dim": "neq", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "eq_solref", - "outer_dim": "neq", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "eq_solimp", - "outer_dim": "neq", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "eq_data", - "outer_dim": "neq", - "stride": "mjNEQDATA" - } - ], - "MJMODEL_POINTERS_TENDON": [ - { - "type": "int", - "name": "tendon_adr", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_num", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_matid", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_actuatorid", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_group", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_treenum", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_treeid", - "outer_dim": "ntendon", - "stride": "2" - }, - { - "type": "int", - "name": "ten_J_rownnz", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "ten_J_rowadr", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "ten_J_colind", - "outer_dim": "nJten", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "tendon_limited", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "tendon_actfrclimited", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_width", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_solref_lim", - "outer_dim": "ntendon", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "tendon_solimp_lim", - "outer_dim": "ntendon", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "tendon_solref_fri", - "outer_dim": "ntendon", - "stride": "mjNREF" - }, - { - "type": "mjtNum", - "name": "tendon_solimp_fri", - "outer_dim": "ntendon", - "stride": "mjNIMP" - }, - { - "type": "mjtNum", - "name": "tendon_range", - "outer_dim": "ntendon", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "tendon_actfrcrange", - "outer_dim": "ntendon", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "tendon_margin", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_stiffness", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_stiffnesspoly", - "outer_dim": "ntendon", - "stride": "mjNPOLY" - }, - { - "type": "mjtNum", - "name": "tendon_damping", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_dampingpoly", - "outer_dim": "ntendon", - "stride": "mjNPOLY" - }, - { - "type": "mjtNum", - "name": "tendon_armature", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_frictionloss", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_lengthspring", - "outer_dim": "ntendon", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "tendon_length0", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_invweight0", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "tendon_user", - "outer_dim": "ntendon", - "stride": "MJ_M(nuser_tendon)" - }, - { - "type": "float", - "name": "tendon_rgba", - "outer_dim": "ntendon", - "stride": "4" - } - ], - "MJMODEL_POINTERS_ACTUATOR": [ - { - "type": "int", - "name": "actuator_trntype", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_dyntype", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_gaintype", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_biastype", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_trnid", - "outer_dim": "nu", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "actuator_damping", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_dampingpoly", - "outer_dim": "nu", - "stride": "mjNPOLY" - }, - { - "type": "mjtNum", - "name": "actuator_armature", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_actadr", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_actnum", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_group", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "actuator_history", - "outer_dim": "nu", - "stride": "2" - }, - { - "type": "int", - "name": "actuator_historyadr", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_delay", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "actuator_ctrllimited", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "actuator_forcelimited", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "actuator_actlimited", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_dynprm", - "outer_dim": "nu", - "stride": "mjNDYN" - }, - { - "type": "mjtNum", - "name": "actuator_gainprm", - "outer_dim": "nu", - "stride": "mjNGAIN" - }, - { - "type": "mjtNum", - "name": "actuator_biasprm", - "outer_dim": "nu", - "stride": "mjNBIAS" - }, - { - "type": "mjtBool", - "name": "actuator_actearly", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_ctrlrange", - "outer_dim": "nu", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "actuator_forcerange", - "outer_dim": "nu", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "actuator_actrange", - "outer_dim": "nu", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "actuator_gear", - "outer_dim": "nu", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "actuator_cranklength", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_acc0", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_length0", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_lengthrange", - "outer_dim": "nu", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "actuator_user", - "outer_dim": "nu", - "stride": "MJ_M(nuser_actuator)" - }, - { - "type": "int", - "name": "actuator_plugin", - "outer_dim": "nu", - "stride": "1" - } - ], - "MJMODEL_POINTERS_SENSOR": [ - { - "type": "int", - "name": "sensor_type", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_datatype", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_needstage", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_objtype", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_objid", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_reftype", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_refid", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_intprm", - "outer_dim": "nsensor", - "stride": "mjNSENS" - }, - { - "type": "int", - "name": "sensor_dim", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_adr", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "sensor_cutoff", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "sensor_noise", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "int", - "name": "sensor_history", - "outer_dim": "nsensor", - "stride": "2" - }, - { - "type": "int", - "name": "sensor_historyadr", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "sensor_delay", - "outer_dim": "nsensor", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "sensor_interval", - "outer_dim": "nsensor", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "sensor_user", - "outer_dim": "nsensor", - "stride": "MJ_M(nuser_sensor)" - }, - { - "type": "int", - "name": "sensor_plugin", - "outer_dim": "nsensor", - "stride": "1" - } - ] - }, - "mjdata_pointers": { - "MJDATA_POINTERS": [ - { - "type": "mjtNum", - "name": "qpos", - "outer_dim": "nq", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qvel", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "act", - "outer_dim": "na", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "history", - "outer_dim": "nhistory", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qacc_warmstart", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "plugin_state", - "outer_dim": "npluginstate", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "ctrl", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_applied", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "xfrc_applied", - "outer_dim": "nbody", - "stride": "6" - }, - { - "type": "mjtBool", - "name": "eq_active", - "outer_dim": "neq", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "mocap_pos", - "outer_dim": "nmocap", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "mocap_quat", - "outer_dim": "nmocap", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "qacc", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "act_dot", - "outer_dim": "na", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "userdata", - "outer_dim": "nuserdata", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "sensordata", - "outer_dim": "nsensordata", - "stride": "1" - }, - { - "type": "int", - "name": "tree_asleep", - "outer_dim": "ntree", - "stride": "1" - }, - { - "type": "int", - "name": "plugin", - "outer_dim": "nplugin", - "stride": "1" - }, - { - "type": "uintptr_t", - "name": "plugin_data", - "outer_dim": "nplugin", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "xpos", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "xquat", - "outer_dim": "nbody", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "xmat", - "outer_dim": "nbody", - "stride": "9" - }, - { - "type": "mjtNum", - "name": "xipos", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "ximat", - "outer_dim": "nbody", - "stride": "9" - }, - { - "type": "mjtNum", - "name": "xanchor", - "outer_dim": "njnt", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "xaxis", - "outer_dim": "njnt", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "geom_xpos", - "outer_dim": "ngeom", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "geom_xmat", - "outer_dim": "ngeom", - "stride": "9" - }, - { - "type": "mjtNum", - "name": "site_xpos", - "outer_dim": "nsite", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "site_xmat", - "outer_dim": "nsite", - "stride": "9" - }, - { - "type": "mjtNum", - "name": "cam_xpos", - "outer_dim": "ncam", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "cam_xmat", - "outer_dim": "ncam", - "stride": "9" - }, - { - "type": "mjtNum", - "name": "light_xpos", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "light_xdir", - "outer_dim": "nlight", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "subtree_com", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "cdof", - "outer_dim": "nv", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "cinert", - "outer_dim": "nbody", - "stride": "10" - }, - { - "type": "mjtNum", - "name": "flexvert_xpos", - "outer_dim": "nflexvert", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "flexelem_aabb", - "outer_dim": "nflexelem", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "flexedge_J", - "outer_dim": "nJfe", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flexedge_length", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flexvert_J", - "outer_dim": "nJfv", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "flexvert_length", - "outer_dim": "nflexvert", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "bvh_aabb_dyn", - "outer_dim": "nbvhdynamic", - "stride": "6" - }, - { - "type": "int", - "name": "ten_wrapadr", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "ten_wrapnum", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "ten_J", - "outer_dim": "nJten", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "ten_length", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "int", - "name": "wrap_obj", - "outer_dim": "nwrap", - "stride": "2" - }, - { - "type": "mjtNum", - "name": "wrap_xpos", - "outer_dim": "nwrap", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "actuator_length", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "moment_rownnz", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "moment_rowadr", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "int", - "name": "moment_colind", - "outer_dim": "nJmom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_moment", - "outer_dim": "nJmom", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "crb", - "outer_dim": "nbody", - "stride": "10" - }, - { - "type": "mjtNum", - "name": "qM", - "outer_dim": "nM", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "M", - "outer_dim": "nC", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qLD", - "outer_dim": "nC", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qLDiagInv", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtBool", - "name": "bvh_active", - "outer_dim": "nbvh", - "stride": "1" - }, - { - "type": "int", - "name": "tree_awake", - "outer_dim": "ntree", - "stride": "1" - }, - { - "type": "int", - "name": "body_awake", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "body_awake_ind", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "parent_awake_ind", - "outer_dim": "nbody", - "stride": "1" - }, - { - "type": "int", - "name": "dof_awake_ind", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "flexedge_velocity", - "outer_dim": "nflexedge", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "ten_velocity", - "outer_dim": "ntendon", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_velocity", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "cvel", - "outer_dim": "nbody", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "cdof_dot", - "outer_dim": "nv", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "qfrc_bias", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_spring", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_damper", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_gravcomp", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_fluid", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_passive", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "subtree_linvel", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "subtree_angmom", - "outer_dim": "nbody", - "stride": "3" - }, - { - "type": "mjtNum", - "name": "qH", - "outer_dim": "nC", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qHDiagInv", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qDeriv", - "outer_dim": "nD", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qLU", - "outer_dim": "nD", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "actuator_force", - "outer_dim": "nu", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_actuator", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_smooth", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qacc_smooth", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_constraint", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "qfrc_inverse", - "outer_dim": "nv", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "cacc", - "outer_dim": "nbody", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "cfrc_int", - "outer_dim": "nbody", - "stride": "6" - }, - { - "type": "mjtNum", - "name": "cfrc_ext", - "outer_dim": "nbody", - "stride": "6" - } - ], - "MJDATA_ARENA_POINTERS_CONTACT": [ - { - "type": "mjContact", - "name": "contact", - "outer_dim": "MJ_D(ncon)", - "stride": "1" - } - ], - "MJDATA_ARENA_POINTERS_SOLVER": [ - { - "type": "int", - "name": "efc_type", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_id", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_J_rownnz", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_J_rowadr", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_J_rowsuper", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_J_colind", - "outer_dim": "MJ_D(nJ)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_J", - "outer_dim": "MJ_D(nJ)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_pos", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_margin", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_frictionloss", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_diagA", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_KBIP", - "outer_dim": "MJ_D(nefc)", - "stride": "4" - }, - { - "type": "mjtNum", - "name": "efc_D", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_R", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "tendon_efcadr", - "outer_dim": "MJ_M(ntendon)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_vel", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_aref", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_b", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_state", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_force", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - } - ], - "MJDATA_ARENA_POINTERS_DUAL": [ - { - "type": "int", - "name": "efc_Y_rownnz", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_Y_rowadr", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_Y_colind", - "outer_dim": "MJ_D(nY)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_Y", - "outer_dim": "MJ_D(nY)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_AR_rownnz", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_AR_rowadr", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_AR_colind", - "outer_dim": "MJ_D(nA)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "efc_AR", - "outer_dim": "MJ_D(nA)", - "stride": "1" - } - ], - "MJDATA_ARENA_POINTERS_ISLAND": [ - { - "type": "int", - "name": "tree_island", - "outer_dim": "MJ_M(ntree)", - "stride": "1" - }, - { - "type": "int", - "name": "island_ntree", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "island_itreeadr", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "map_itree2tree", - "outer_dim": "MJ_M(ntree)", - "stride": "1" - }, - { - "type": "int", - "name": "dof_island", - "outer_dim": "MJ_M(nv)", - "stride": "1" - }, - { - "type": "int", - "name": "island_nv", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "island_idofadr", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "island_dofadr", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "map_dof2idof", - "outer_dim": "MJ_M(nv)", - "stride": "1" - }, - { - "type": "int", - "name": "map_idof2dof", - "outer_dim": "MJ_M(nv)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "ifrc_smooth", - "outer_dim": "MJ_D(nidof)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iacc_smooth", - "outer_dim": "MJ_D(nidof)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iacc", - "outer_dim": "MJ_D(nidof)", - "stride": "1" - }, - { - "type": "int", - "name": "efc_island", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "island_ne", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "island_nf", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "island_nefc", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "island_iefcadr", - "outer_dim": "MJ_D(nisland)", - "stride": "1" - }, - { - "type": "int", - "name": "map_efc2iefc", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "map_iefc2efc", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "iefc_type", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "iefc_id", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iefc_frictionloss", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iefc_D", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iefc_R", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iefc_aref", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "int", - "name": "iefc_state", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "iefc_force", - "outer_dim": "MJ_D(nefc)", - "stride": "1" - }, - { - "type": "mjtNum", - "name": "ifrc_constraint", - "outer_dim": "MJ_D(nidof)", - "stride": "1" - } - ], - "MJDATA_ARENA_POINTERS": [] - }, - "struct_fields": { - "MJOPTION_FIELDS": [ - { - "type": "mjtNum", - "name": "timestep", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "impratio", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "tolerance", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "ls_tolerance", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "noslip_tolerance", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "ccd_tolerance", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "sleep_tolerance", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "gravity", - "count": "3", - "is_vec": true - }, - { - "type": "mjtNum", - "name": "wind", - "count": "3", - "is_vec": true - }, - { - "type": "mjtNum", - "name": "magnetic", - "count": "3", - "is_vec": true - }, - { - "type": "mjtNum", - "name": "density", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "viscosity", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "o_margin", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "o_solref", - "count": "mjNREF", - "is_vec": true - }, - { - "type": "mjtNum", - "name": "o_solimp", - "count": "mjNIMP", - "is_vec": true - }, - { - "type": "mjtNum", - "name": "o_friction", - "count": "5", - "is_vec": true - }, - { - "type": "int", - "name": "integrator", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "cone", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "jacobian", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "solver", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "iterations", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "ls_iterations", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "noslip_iterations", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "ccd_iterations", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "disableflags", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "enableflags", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "disableactuator", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "sdf_initpoints", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "sdf_iterations", - "count": "1", - "is_vec": false - } - ], - "MJSTATISTIC_FIELDS": [ - { - "type": "mjtNum", - "name": "meaninertia", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "meanmass", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "meansize", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "extent", - "count": "1", - "is_vec": false - }, - { - "type": "mjtNum", - "name": "center", - "count": "3", - "is_vec": true - } - ], - "MJVISUAL_GLOBAL_FIELDS": [ - { - "type": "int", - "name": "cameraid", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "orthographic", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "fovy", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "ipd", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "azimuth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "elevation", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "linewidth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "glow", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "realtime", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "offwidth", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "offheight", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "ellipsoidinertia", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "bvactive", - "count": "1", - "is_vec": false - } - ], - "MJVISUAL_QUALITY_FIELDS": [ - { - "type": "int", - "name": "shadowsize", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "offsamples", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "numslices", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "numstacks", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "numquads", - "count": "1", - "is_vec": false - } - ], - "MJVISUAL_HEADLIGHT_FIELDS": [ - { - "type": "float", - "name": "ambient", - "count": "3", - "is_vec": true - }, - { - "type": "float", - "name": "diffuse", - "count": "3", - "is_vec": true - }, - { - "type": "float", - "name": "specular", - "count": "3", - "is_vec": true - }, - { - "type": "int", - "name": "active", - "count": "1", - "is_vec": false - } - ], - "MJVISUAL_MAP_FIELDS": [ - { - "type": "float", - "name": "stiffness", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "stiffnessrot", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "force", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "torque", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "alpha", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "fogstart", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "fogend", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "znear", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "zfar", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "haze", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "shadowclip", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "shadowscale", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "actuatortendon", - "count": "1", - "is_vec": false - } - ], - "MJVISUAL_SCALE_FIELDS": [ - { - "type": "float", - "name": "forcewidth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "contactwidth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "contactheight", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "connect", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "com", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "camera", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "light", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "selectpoint", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "jointlength", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "jointwidth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "actuatorlength", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "actuatorwidth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "framelength", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "framewidth", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "constraint", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "slidercrank", - "count": "1", - "is_vec": false - }, - { - "type": "float", - "name": "frustum", - "count": "1", - "is_vec": false - } - ], - "MJVISUAL_RGBA_FIELDS": [ - { - "type": "float", - "name": "fog", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "haze", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "force", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "inertia", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "joint", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "actuator", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "actuatornegative", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "actuatorpositive", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "com", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "camera", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "light", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "selectpoint", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "connect", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "contactpoint", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "contactforce", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "contactfriction", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "contacttorque", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "contactgap", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "rangefinder", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "constraint", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "slidercrank", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "crankbroken", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "frustum", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "bv", - "count": "4", - "is_vec": true - }, - { - "type": "float", - "name": "bvactive", - "count": "4", - "is_vec": true - } - ], - "MJSCOMPILER_FIELDS": [ - { - "type": "mjtByte", - "name": "autolimits", - "count": "1", - "is_vec": false - }, - { - "type": "double", - "name": "boundmass", - "count": "1", - "is_vec": false - }, - { - "type": "double", - "name": "boundinertia", - "count": "1", - "is_vec": false - }, - { - "type": "double", - "name": "settotalmass", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "balanceinertia", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "fitaabb", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "degree", - "count": "1", - "is_vec": false - }, - { - "type": "char", - "name": "eulerseq", - "count": "3", - "is_vec": true - }, - { - "type": "mjtByte", - "name": "discardvisual", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "usethread", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "fusestatic", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "inertiafromgeom", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "inertiagrouprange", - "count": "2", - "is_vec": true - }, - { - "type": "mjtByte", - "name": "saveinertial", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "alignfree", - "count": "1", - "is_vec": false - }, - { - "type": "mjLROpt", - "name": "LRopt", - "count": "1", - "is_vec": false - }, - { - "type": "mjString*", - "name": "meshdir", - "count": "1", - "is_vec": false - }, - { - "type": "mjString*", - "name": "texturedir", - "count": "1", - "is_vec": false - }, - { - "type": "uint64_t", - "name": "authored", - "count": "1", - "is_vec": false - } - ], - "MJSPEC_FIELDS": [ - { - "type": "mjsElement*", - "name": "element", - "count": "1", - "is_vec": false - }, - { - "type": "mjString*", - "name": "modelname", - "count": "1", - "is_vec": false - }, - { - "type": "mjsCompiler", - "name": "compiler", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "strippath", - "count": "1", - "is_vec": false - }, - { - "type": "mjOption", - "name": "option", - "count": "1", - "is_vec": false - }, - { - "type": "mjVisual", - "name": "visual", - "count": "1", - "is_vec": false - }, - { - "type": "mjStatistic", - "name": "stat", - "count": "1", - "is_vec": false - }, - { - "type": "mjtSize", - "name": "memory", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nemax", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuserdata", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_body", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_jnt", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_geom", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_site", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_cam", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_tendon", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_actuator", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nuser_sensor", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nkey", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "njmax", - "count": "1", - "is_vec": false - }, - { - "type": "int", - "name": "nconmax", - "count": "1", - "is_vec": false - }, - { - "type": "mjtSize", - "name": "nstack", - "count": "1", - "is_vec": false - }, - { - "type": "mjString*", - "name": "comment", - "count": "1", - "is_vec": false - }, - { - "type": "mjString*", - "name": "modelfiledir", - "count": "1", - "is_vec": false - }, - { - "type": "mjtByte", - "name": "hasImplicitPluginElem", - "count": "1", - "is_vec": false - } - ] - } -} \ No newline at end of file diff --git a/Scripts/codegen/tests/__init__.py b/Scripts/codegen/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/Scripts/codegen/tests/conftest.py b/Scripts/codegen/tests/conftest.py deleted file mode 100644 index 97eda15e..00000000 --- a/Scripts/codegen/tests/conftest.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Shared fixtures + helpers for codegen pytest tests. -""" - -from __future__ import annotations - -import json -import os -import sys -from typing import Any, Dict - -# Make the parent codegen package importable when running pytest from the -# plugin root or from this directory. -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -if _CODEGEN_DIR not in sys.path: - sys.path.insert(0, _CODEGEN_DIR) -_PLUGIN_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", "..")) - -import pytest - - -@pytest.fixture(scope="session") -def real_schema() -> Dict[str, Any]: - """The actual MJCF schema snapshot shipped with the plugin.""" - path = os.path.join(_PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "mjcf_schema_snapshot.json") - with open(path, "r", encoding="utf-8") as f: - return json.load(f) - - -@pytest.fixture(scope="session") -def real_rules() -> Dict[str, Any]: - path = os.path.join(_CODEGEN_DIR, "codegen_rules.json") - with open(path, "r", encoding="utf-8") as f: - return json.load(f) - - -@pytest.fixture(scope="session") -def real_mjxmacro() -> Dict[str, Any]: - path = os.path.join(_PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "mjxmacro_snapshot.json") - with open(path, "r", encoding="utf-8") as f: - return json.load(f) - - -@pytest.fixture(scope="session") -def real_mjspec() -> Dict[str, Any]: - """The mjspec snapshot file was retired; the runtime - now projects the introspect snapshot into the legacy mjspec shape. - The fixture mirrors that projection so tests stay isomorphic.""" - import generate_ue_components as gen - introspect_path = os.path.join( - _PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "introspect_snapshot.json", - ) - with open(introspect_path, "r", encoding="utf-8") as f: - introspect = json.load(f) - return gen._mjspec_from_introspect(introspect) - - -@pytest.fixture(autouse=True) -def _clear_diags(): - """Each test starts with a clean diagnostics buffer + strict counter. - Module-level state survives across tests otherwise — autouse keeps - drift-check tests from leaking state into one another.""" - import generate_ue_components as gen # noqa: E402, local import - gen._reset_diags() - yield - gen._reset_diags() - - -@pytest.fixture -def minimal_rules() -> Dict[str, Any]: - """A small synthetic rules block, useful for unit-testing emission - shapes without coupling to the real schema.""" - return { - "global_exclusions": ["user"], - "default_type": "float", - "property_renames": {}, - "type_mappings": { - "range": "TArray", - "limited": "bool", - "rgba": "FLinearColor", - "axis": "FVector", - }, - "canonicalizations": { - "spatial_pose": { - "absorbs_attrs": ["pos"], - "emits_property": {"name": "Pos", "type": "FVector"}, - "import_helper": "MjUtils::ReadVec3InMeters", - "export_helper": "MjUtils::UEToMjPosition", - }, - "orientation": { - "absorbs_attrs": ["quat", "euler", "axisangle", "xyaxes", "zaxis"], - "emits_property": {"name": "Quat", "type": "FQuat"}, - "import_helper": "MjOrientationUtils::OrientationToMjQuat", - "export_helper": "MjUtils::UEToMjRotation", - }, - }, - "element_rules": { - "joint": { - "exclude_attrs": ["pos", "axis"], - }, - "geom": { - "exclude_attrs": ["material", "pos", "quat", "size", "fromto", - "axisangle", "euler", "xyaxes", "zaxis"], - "applies_canonicalizations": ["spatial_pose", "orientation"], - }, - }, - } diff --git a/Scripts/codegen/tests/test_behavioral_emit_guarantees.py b/Scripts/codegen/tests/test_behavioral_emit_guarantees.py deleted file mode 100644 index fc13977a..00000000 --- a/Scripts/codegen/tests/test_behavioral_emit_guarantees.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Behavioural lock-ins for the codegen's emit surface. - -Substring-only assertions catch typos but not semantics — these tests -pin three load-bearing properties of the generated C++: - -1. XML enum roundtrip — every ``value_map`` entry produces matching - import + export arms whose XML strings and UE members agree. -2. ``mjs_setTo*`` argument order — the codegen feeds params to the - call in the exact order declared by the C signature in the - snapshot, not by rule-side iteration order. Misordered args here - would call ``mjs_setToPosition(kp_value, kv_value, ...)`` swapped - and silently miscalibrate every Position actuator. -3. Override-toggle gate — every codegen-emitted UPROPERTY pair is - ``bOverride_X`` (toggle) + ``X`` (value). Both names must agree - so the editor InlineEditConditionToggle wiring binds correctly. -""" - -from __future__ import annotations - -import os -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -if _CODEGEN_DIR not in sys.path: - sys.path.insert(0, _CODEGEN_DIR) - -import re - -import generate_ue_components as gen - - -# ---------- xml_enum roundtrip --------------------------------------------- - -def test_xml_enum_roundtrip_arms_match(real_rules): - """Every value_map entry must be reachable from both directions: - ``XML string -> UE member`` (import) and ``UE member -> XML - string`` (export). A typo in either arm would silently misimport - or misexport the attr without a compile error.""" - for elem, elem_rule in real_rules.get("element_rules", {}).items(): - for attr, enum_def in elem_rule.get("xml_enum_attrs", {}).items(): - value_map = enum_def.get("value_map") or {} - if not value_map: - continue - ue_enum_type = enum_def["ue_enum_type"] - import_cpp = gen._emit_xml_enum_import(attr, enum_def) - # Enums with no ``mjs_field`` (e.g. dcmotor.input, routed - # through mjs_setToDCMotor) emit nothing on the export side - # by design — skip the export arm in that case. - export_cpp = ( - gen._emit_xml_enum_export(attr, enum_def) - if enum_def.get("mjs_field") else "" - ) - for xml_val, mapping in value_map.items(): - ue_member = mapping[0] if isinstance(mapping, list) else mapping - # Import side mentions both XML literal and UE member. - assert f'"{xml_val}"' in import_cpp, ( - f"{elem}.{attr}: import arm missing XML literal " - f"'{xml_val}'\n{import_cpp}" - ) - assert f"{ue_enum_type}::{ue_member}" in import_cpp, ( - f"{elem}.{attr}: import arm missing " - f"{ue_enum_type}::{ue_member}\n{import_cpp}" - ) - if export_cpp: - assert f"{ue_enum_type}::{ue_member}" in export_cpp, ( - f"{elem}.{attr}: export arm missing " - f"{ue_enum_type}::{ue_member}\n{export_cpp}" - ) - - -# ---------- mjs_setTo* arg order ------------------------------------------- - -def test_setto_call_args_follow_c_signature_order(): - """The generated ``mjs_setToX(Element, a, b, c)`` call must list - args in the order declared by the C signature. Reordering by - rule-side iteration would silently swap kp/kv on actuator - subtypes.""" - rules = { - "default_type": "float", - "type_mappings": {"kp": "float", "kv": "float", "dampratio": "float"}, - "setto_param_defaults": {}, - } - mjspec = { - "setto_functions": { - "mjs_setToPosition": { - "params": [ - {"name": "kp", "c_type": "double", "array_dim": None}, - {"name": "kv", "c_type": "double", "array_dim": None}, - {"name": "dampratio", "c_type": "double", "array_dim": None}, - ], - }, - }, - } - setto_rules = {"call": "mjs_setToPosition"} - out = gen._emit_setto_call( - subtype_key="position", - setto_rules=setto_rules, - rules=rules, - mjspec=mjspec, - subtype_schema_attrs=["kp", "kv", "dampratio"], - base_schema_attrs=[], - ) - assert "mjs_setToPosition(Element" in out - # Capture the argument list and confirm kp appears before kv before - # dampratio — exact textual order in the call. - match = re.search(r"mjs_setToPosition\(Element,(.*?)\);", out, re.DOTALL) - assert match is not None, out - args_blob = match.group(1) - kp_pos = args_blob.find("(double)kp") - kv_pos = args_blob.find("(double)kv") - dampratio_pos = args_blob.find("(double)dampratio") - assert 0 < kp_pos < kv_pos < dampratio_pos, ( - f"setto args out of order: kp@{kp_pos} kv@{kv_pos} " - f"dampratio@{dampratio_pos}\n{out}" - ) - - -def test_setto_call_respects_signature_order_when_rule_lists_differently(): - """Rule-side ``param_renames`` lookup order MUST NOT influence the - emitted arg order — the C signature is the source of truth.""" - rules = { - "default_type": "float", - "type_mappings": {"PropB": "float", "PropA": "float"}, - "setto_param_defaults": {}, - } - mjspec = { - "setto_functions": { - "mjs_setToFake": { - "params": [ - {"name": "a", "c_type": "double", "array_dim": None}, - {"name": "b", "c_type": "double", "array_dim": None}, - ], - }, - }, - } - # param_renames iteration order intentionally NOT matching signature order. - setto_rules = {"call": "mjs_setToFake", - "param_renames": {"b": "PropB", "a": "PropA"}} - out = gen._emit_setto_call( - subtype_key="fake", - setto_rules=setto_rules, - rules=rules, - mjspec=mjspec, - subtype_schema_attrs=["PropA", "PropB"], - base_schema_attrs=[], - ) - match = re.search(r"mjs_setToFake\(Element,(.*?)\);", out, re.DOTALL) - assert match is not None, out - args_blob = match.group(1) - a_pos = args_blob.find("PropA") - b_pos = args_blob.find("PropB") - assert 0 < a_pos < b_pos, ( - f"renamed setto args out of signature order: A@{a_pos} B@{b_pos}\n{out}" - ) - - -# ---------- override-toggle gate ------------------------------------------- - -_UPROPERTY_PAIR_RE = re.compile( - r"bool\s+(bOverride_(\w+))\s*=\s*false;\s*\n+" - r"\s*UPROPERTY\(.*?\)\)\s*\n" - r"\s*\S+\s+(\w+)\s*=" -) - - -def test_override_toggle_name_matches_uproperty_for_all_categories(real_rules): - """For every category whose schema produces a UPROPERTY pair, the - ``bOverride_X`` toggle and the value property ``X`` must share the - same final identifier. A drift between them would still compile - but the InlineEditConditionToggle wiring in the editor would - silently dead-end.""" - schema_path = os.path.join(_CODEGEN_DIR, "snapshots", "mjcf_schema_snapshot.json") - if not os.path.exists(schema_path): - import pytest - pytest.skip("mjcf_schema.json snapshot not present") - import json - with open(schema_path, "r", encoding="utf-8") as f: - schema = json.load(f) - saw_pair = False - for cat_name, cat_rules in real_rules.get("categories", {}).items(): - attrs = gen.schema_attrs(schema, cat_rules.get("schema_common_block", "")) - if not attrs: - continue - out = gen.emit_schema_for_attrs( - attrs, real_rules, - element_name=cat_name, - category_label=cat_name, - ) - for match in _UPROPERTY_PAIR_RE.finditer(out.properties_h): - full_toggle, toggle_suffix, value_name = match.groups() - assert toggle_suffix == value_name, ( - f"category '{cat_name}': UPROPERTY pair drift — toggle " - f"'{full_toggle}' but value name is '{value_name}'" - ) - saw_pair = True - assert saw_pair, "no UPROPERTY pairs found in any category — check broken" diff --git a/Scripts/codegen/tests/test_camp_b_enum_migration.py b/Scripts/codegen/tests/test_camp_b_enum_migration.py deleted file mode 100644 index f2d73a16..00000000 --- a/Scripts/codegen/tests/test_camp_b_enum_migration.py +++ /dev/null @@ -1,188 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks the generated_enums extensions: -``extra_members`` (URLab-extra members appended after mj-derived ones) -and the ``disabled`` opt-out (skips emission so a rule can land before -the corresponding hand-rolled enums are swapped out). - -Also asserts that the Camp B rule entries shipped under -``MjArticulationEnums`` in codegen_rules.json resolve cleanly against -the current mjspec snapshot — every ``from_mj_enum`` exists and the -``ue_member_from_mj`` keys all map to real ``mjt*`` constants.""" - -from __future__ import annotations - -import os - -import generate_ue_components as gen - - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -_PLUGIN_ROOT = os.path.dirname(os.path.dirname(_CODEGEN_DIR)) - - -# ---------- extra_members -------------------------------------------------- - -def test_extra_members_append_after_mj_derived_members(): - """URLab-extra members (DcMotor on EMjGainType etc) must follow - the mj-derived block so existing member ordering doesn't change.""" - rules_def = { - "public_header": "Generated/MjT.h", - "enums": [{ - "ue_name": "EMjT", - "from_mj_enum": "mjtT", - "ue_member_from_mj": { - "mjT_A": "A", - "mjT_B": "B", - }, - "extra_members": [{"name": "DcMotor"}], - }], - } - mjspec = {"enums": {"mjtT": {"mjT_A": 0, "mjT_B": 1}}} - writes = gen._emit_generated_enum_file( - "MjArticulationEnums", rules_def, mjspec, "/tmp/public", - ) - assert len(writes) == 1 - content = writes[0].content - a_idx = content.index("A = 0") - b_idx = content.index("B = 1") - dc_idx = content.index("DcMotor = 2") - assert a_idx < b_idx < dc_idx - - -def test_extra_member_explicit_value_overrides_auto_increment(): - """``{"name": ..., "value": N}`` pins a specific underlying value - — needed for enums that mirror mjt* numbering exactly.""" - rules_def = { - "public_header": "Generated/MjT.h", - "enums": [{ - "ue_name": "EMjT", - "from_mj_enum": "mjtT", - "ue_member_from_mj": {}, - "extra_members": [ - {"name": "None", "value": 0}, - {"name": "Plane", "value": 7}, - ], - }], - } - mjspec = {"enums": {"mjtT": {"mjT_DUMMY": 0}}} - writes = gen._emit_generated_enum_file( - "MjArticulationEnums", rules_def, mjspec, "/tmp/public", - ) - assert writes - content = writes[0].content - assert "None = 0" in content - assert "Plane = 7" in content - - -def test_extra_member_without_name_fires_diagnostic(): - rules_def = { - "public_header": "Generated/MjT.h", - "enums": [{ - "ue_name": "EMjT", - "from_mj_enum": "mjtT", - "ue_member_from_mj": {"mjT_A": "A"}, - "extra_members": [{"value": 5}], # missing name - }], - } - mjspec = {"enums": {"mjtT": {"mjT_A": 0}}} - gen._emit_generated_enum_file( - "MjArticulationEnums", rules_def, mjspec, "/tmp/public", - ) - assert any("missing 'name'" in d.message for d in gen._DIAGS_BUFFER.pending) - - -# ---------- disabled opt-out ----------------------------------------------- - -def test_disabled_top_level_skips_emission(): - """``disabled: true`` at the rule entry level emits no FileWrite — - used while staging a migration so a hand-rolled enum isn't - duplicated by the codegen output.""" - rules_def = { - "disabled": True, - "public_header": "Generated/MjT.h", - "enums": [{ - "ue_name": "EMjT", - "from_mj_enum": "mjtT", - "ue_member_from_mj": {"mjT_A": "A"}, - }], - } - mjspec = {"enums": {"mjtT": {"mjT_A": 0}}} - writes = gen._emit_generated_enum_file( - "MjArticulationEnums", rules_def, mjspec, "/tmp/public", - ) - assert writes == [] - - -def test_disabled_per_entry_skips_one_enum_in_a_rule(): - """Per-entry ``disabled: true`` skips just that enum but lets the - rule's other entries still emit. Granular staging.""" - rules_def = { - "public_header": "Generated/MjT.h", - "enums": [ - { - "ue_name": "EMjEnabled", - "from_mj_enum": "mjtT", - "ue_member_from_mj": {"mjT_A": "Alpha"}, - }, - { - "ue_name": "EMjDisabled", - "disabled": True, - "from_mj_enum": "mjtT", - "ue_member_from_mj": {"mjT_A": "Beta"}, - }, - ], - } - mjspec = {"enums": {"mjtT": {"mjT_A": 0}}} - writes = gen._emit_generated_enum_file( - "MjArticulationEnums", rules_def, mjspec, "/tmp/public", - ) - assert len(writes) == 1 - content = writes[0].content - assert "EMjEnabled" in content - assert "EMjDisabled" not in content - - -# ---------- Camp B rule audit --------------------------------------------- - -def test_camp_b_rule_resolves_against_real_mjspec(real_rules, real_mjspec): - """Every Camp B entry's from_mj_enum must exist in the mjspec - snapshot, and every ue_member_from_mj key must be a real mjt* - constant. Catches typos in the migration rule before the actual - flip lands.""" - camp_b = real_rules.get("generated_enums", {}).get("MjArticulationEnums") - if not camp_b or not isinstance(camp_b, dict): - return # rule not present yet - mj_enums = real_mjspec.get("enums", {}) - for entry in camp_b.get("enums", []): - # Pure URLab enums (no mjt counterpart) omit from_mj_enum and - # rely entirely on extra_members. Skip the cross-check for those. - mj_enum_name = entry.get("from_mj_enum") - if not mj_enum_name: - continue - assert mj_enum_name in mj_enums, ( - f"Camp B '{entry['ue_name']}' -> from_mj_enum " - f"'{mj_enum_name}' not in mjspec.enums" - ) - members = mj_enums[mj_enum_name] - for mj_const in entry.get("ue_member_from_mj", {}): - assert mj_const in members, ( - f"Camp B '{entry['ue_name']}' ue_member_from_mj key " - f"'{mj_const}' is not in mjtEnum '{mj_enum_name}'" - ) - - -def test_camp_b_rule_post_flip_state(real_rules): - """After the flip, the rule no longer carries ``disabled: true`` - and the hand-rolled UENUM decls under - ``Source/URLab/Public/MuJoCo/Components/`` have been replaced - with includes of the generated header.""" - camp_b = real_rules.get("generated_enums", {}).get("MjArticulationEnums") - if not camp_b or not isinstance(camp_b, dict): - return - assert not camp_b.get("disabled"), ( - "MjArticulationEnums should be enabled after the Camp B flip." - ) - - -# On-disk presence is covered by test_regen_no_diff.py::test_codegen_check_clean. diff --git a/Scripts/codegen/tests/test_canon_emitter_bodies.py b/Scripts/codegen/tests/test_canon_emitter_bodies.py deleted file mode 100644 index 13b81706..00000000 --- a/Scripts/codegen/tests/test_canon_emitter_bodies.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Behavioural lock-ins for the canon emitter bodies that previously -only had registry-presence coverage. ``actuator_transmission`` and -``fromto_decompose`` are inline (no rule-side helper string); their -emitted C++ is hand-crafted, so any rename or signature change there -should surface in pytest rather than at UE compile.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -# ---------- actuator_transmission --------------------------------------- - -def test_actuator_transmission_import_reads_every_target_attr(): - """The import block reads joint/jointinparent/tendon/site/body and - the slidersite/refsite siblings. A missing branch means a rule - author can't author that transmission type via XML.""" - out = gen._canon_import_actuator_transmission(canon_def={}, element_name="actuator") - for attr in ("joint", "jointinparent", "tendon", "site", "body", - "slidersite", "refsite"): - assert f'TEXT("{attr}")' in out, f"import missing TEXT(\"{attr}\")" - # Each path resolves to an EMjActuatorTrnType. - for trn in ("Joint", "JointInParent", "Tendon", "Site", "Body", - "SliderCrank"): - assert f"EMjActuatorTrnType::{trn}" in out, trn - - -def test_actuator_transmission_export_emits_switch_with_undefined_default(): - out = gen._canon_export_actuator_transmission(canon_def={}) - # Every case writes Element->trntype. - for trn, mj_const in [ - ("Joint", "mjTRN_JOINT"), - ("JointInParent", "mjTRN_JOINTINPARENT"), - ("Tendon", "mjTRN_TENDON"), - ("Site", "mjTRN_SITE"), - ("Body", "mjTRN_BODY"), - ("SliderCrank", "mjTRN_SLIDERCRANK"), - ]: - assert f"EMjActuatorTrnType::{trn}" in out, trn - assert mj_const in out, mj_const - assert "default:" in out and "mjTRN_UNDEFINED" in out - # slidersite / refsite only written when their type is set. - assert "SliderCrank && !SliderSite.IsEmpty()" in out - assert "Site && !RefSite.IsEmpty()" in out - - -# ---------- fromto_decompose -------------------------------------------- - -def test_fromto_decompose_import_uses_helper_and_writes_pos_quat_size(): - canon_def = { - "import_helper": "MjUtils::DecomposeFromTo", - "category_specifics": {"geom": { - "z_slot_check": "(Type == EMjGeomType::Box || Type == EMjGeomType::Ellipsoid)" - }}, - } - out = gen._canon_import_fromto_decompose(canon_def, element_name="geom") - assert "MjUtils::DecomposeFromTo(Node, FTPos, FTQuat, FTHalf)" in out - # Writes Pos / Quat with their override toggles. - assert "Pos = FTPos;" in out and "bOverride_Pos = true;" in out - assert "Quat = FTQuat;" in out and "bOverride_Quat = true;" in out - # Uses the category-specific z_slot_check inline. - assert "EMjGeomType::Box" in out and "EMjGeomType::Ellipsoid" in out - # Grows the size array to accommodate the slot. - assert "while (size.Num() <= FTSlot)" in out - - -def test_fromto_decompose_import_falls_back_to_y_slot_for_capsule(): - canon_def = { - "import_helper": "MjUtils::DecomposeFromTo", - "category_specifics": {"site": { - "z_slot_check": "(Type == EMjSiteType::Box || Type == EMjSiteType::Ellipsoid)" - }}, - } - out = gen._canon_import_fromto_decompose(canon_def, element_name="site") - # The z_slot_check is element-specific; site uses EMjSiteType, not Geom. - assert "EMjSiteType::Box" in out - assert "EMjGeomType" not in out - # Without a matching category_specifics entry, the slot would be `false` (Y slot). - out_other = gen._canon_import_fromto_decompose( - {"import_helper": "MjUtils::DecomposeFromTo"}, - element_name="unknown", - ) - assert "bFTZSlot = false" in out_other - - -def test_fromto_decompose_export_is_empty(): - """The export path is a no-op: ``fromto`` decomposes to Pos/Quat/Size - on import; those properties carry the values through to export via - their own canonicalisations.""" - assert gen._canon_export_fromto_decompose(canon_def={}) == "" diff --git a/Scripts/codegen/tests/test_canon_registry.py b/Scripts/codegen/tests/test_canon_registry.py deleted file mode 100644 index f0081887..00000000 --- a/Scripts/codegen/tests/test_canon_registry.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the canonicalisation registry. - -The registry collapses the previous duplicated if-chains in -``_canon_import_block`` / ``_canon_export_block`` into one dict. -Adding a new canon means adding one ``Canonicalisation`` entry; an -unknown canon name in the JSON rules still fires the same loud -diagnostic instead of silently dropping its emission.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_registry_covers_every_canonicalisation_in_rules_json(real_rules): - """Every canonicalisation declared in codegen_rules.json must have - a registry entry. Mismatch fires the silent-drop diagnostic at - runtime — catch it here at test time instead.""" - declared = set(real_rules.get("canonicalizations", {}).keys()) - registered = set(gen._CANONICALISATIONS.keys()) - missing = declared - registered - assert not missing, ( - f"canonicalizations declared in rules JSON but not in the " - f"registry: {sorted(missing)}" - ) - - -def test_unknown_canon_fires_import_diagnostic(): - out = gen._canon_import_block("nonexistent_canon", {}, element_name="geom") - assert out == "" - assert any("nonexistent_canon" in d.message and "import" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_unknown_canon_fires_export_diagnostic(): - out = gen._canon_export_block("nonexistent_canon", {}) - assert out == "" - assert any("nonexistent_canon" in d.message and "export" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_spatial_pose_import_is_one_liner_helper_call(): - """Locks the trivial-shape canonicalisation so a regression in the - registry dispatch shows up directly.""" - canon_def = { - "import_helper": "MjUtils::ReadVec3InMeters", - "emits_property": {"name": "Pos", "type": "FVector"}, - } - out = gen._canon_import_block("spatial_pose", canon_def, "") - assert out.strip() == ( - 'MjUtils::ReadVec3InMeters(Node, TEXT("pos"), Pos, bOverride_Pos);' - ) - - -def test_canonicalisation_class_is_frozen(): - """Registry entries must be frozen so a phase can't accidentally - swap an emitter at runtime.""" - entry = next(iter(gen._CANONICALISATIONS.values())) - import dataclasses - assert dataclasses.is_dataclass(entry) - # FrozenInstanceError on attempted assignment. - import pytest - with pytest.raises(dataclasses.FrozenInstanceError): - entry.name = "mutated" # type: ignore[misc] diff --git a/Scripts/codegen/tests/test_canonicalization.py b/Scripts/codegen/tests/test_canonicalization.py deleted file mode 100644 index 2fe93e21..00000000 --- a/Scripts/codegen/tests/test_canonicalization.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Canonicalisation (orientation + position) emission tests.""" - -from __future__ import annotations - -from generate_ue_components import emit_schema_for_attrs - - -def test_orientation_absorbs_all_attrs(minimal_rules): - """Every orientation sibling (euler / axisangle / xyaxes / zaxis) - is absorbed into the canonical Quat UPROPERTY — none should leak - out as a standalone property.""" - attrs = ["quat", "euler", "axisangle", "xyaxes", "zaxis", "size"] - out = emit_schema_for_attrs(attrs, minimal_rules, "geom", "Geom") - assert "FQuat Quat" in out.properties_h - assert "bOverride_Quat" in out.properties_h - assert "FQuat Euler" not in out.properties_h - assert "AxisAngle " not in out.properties_h - assert "Xyaxes " not in out.properties_h - - -def test_orientation_roundtrips_through_canon_helpers(minimal_rules): - """A single ``quat`` schema attr drives both arms of the canon — the - import pulls through ``MjOrientationUtils::OrientationToMjQuat`` + - ``MjToUERotation``, and the export pushes through - ``UEToMjRotation``.""" - out = emit_schema_for_attrs(["quat"], minimal_rules, "geom", "Geom") - # Import arm. - assert "MjOrientationUtils::OrientationToMjQuat(Node, CompilerSettings, TmpQuat)" in out.imports_cpp - assert "Quat = MjUtils::MjToUERotation(TmpQuat)" in out.imports_cpp - assert "bOverride_Quat = true" in out.imports_cpp - # Export arm. - assert "MjUtils::UEToMjRotation(Quat, TmpQuat)" in out.exports_cpp - assert "Element->quat[0] = TmpQuat[0]" in out.exports_cpp - assert "Element->quat[3] = TmpQuat[3]" in out.exports_cpp - - -def test_position_absorbs_pos(minimal_rules): - out = emit_schema_for_attrs(["pos", "stiffness"], minimal_rules, "geom", "Geom") - # Canonical Pos is emitted; raw pos attr is not. - assert "FVector Pos" in out.properties_h - # The non-canonical attr survives. - assert "stiffness" in out.properties_h - # Import + export route through the helpers. - assert "MjUtils::ReadVec3InMeters(Node, TEXT(\"pos\"), Pos, bOverride_Pos)" in out.imports_cpp - assert "MjUtils::UEToMjPosition(Pos, TmpPos)" in out.exports_cpp - assert "Element->pos[0] = TmpPos[0]" in out.exports_cpp - - -def test_no_canonicalization_when_element_opts_out(minimal_rules): - # 'joint' has no applies_canonicalizations in minimal_rules — make sure - # nothing canonical leaks in. - out = emit_schema_for_attrs(["pos", "axis", "stiffness"], minimal_rules, "joint", "Joint") - assert "FQuat Quat" not in out.properties_h - assert "FVector Pos" not in out.properties_h diff --git a/Scripts/codegen/tests/test_coverage_checks.py b/Scripts/codegen/tests/test_coverage_checks.py deleted file mode 100644 index 099f7e04..00000000 --- a/Scripts/codegen/tests/test_coverage_checks.py +++ /dev/null @@ -1,218 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Behavioural coverage tests for the six drift checks that previously -had no direct test — every check now has a "should fire" path and a -"should be quiet" path. Each check fires through ``_diag_add`` with a -specific ``source=`` tag so assertions can target the message bucket -without depending on prose.""" - -from __future__ import annotations - -import _codegen_checks as checks -import generate_ue_components as gen - - -def _pending_sources() -> list: - return [d.source for d in gen._DIAGS_BUFFER.pending] - - -def _reset() -> None: - gen._reset_diags() - - -# ---------- _check_top_level_elements_coverage -------------------------- - -def test_top_level_elements_coverage_fires_for_unmodeled_element(): - _reset() - schema = {"new_widget": {}, "joint": {}} - rules = { - "categories": {"joint": {"schema_common_block": "joint"}}, - "intentionally_unmodeled_elements": {}, - } - checks._check_top_level_elements_coverage(schema, rules) - assert any("new_widget" in d.message and d.source == "top_level_coverage" - for d in gen._DIAGS_BUFFER.pending) - - -def test_top_level_elements_coverage_quiet_for_documented_element(): - _reset() - schema = {"asset": {}, "joint": {}} - rules = { - "categories": {"joint": {"schema_common_block": "joint"}}, - "intentionally_unmodeled_elements": {}, - } - # 'asset' is in the container_keys allowlist. - checks._check_top_level_elements_coverage(schema, rules) - assert "top_level_coverage" not in _pending_sources() - - -# ---------- _check_actuator_subtypes_coverage -------------------------- - -def test_actuator_subtypes_coverage_fires_for_unmodeled_subtype(): - _reset() - schema = {"actuator_types": {"newkind": []}} - rules = {"categories": {"actuator": { - "subtypes": [{"key": "motor"}, {"key": "position"}], - }}} - checks._check_actuator_subtypes_coverage(schema, rules) - assert any("newkind" in d.message and d.source == "actuator_subtypes_coverage" - for d in gen._DIAGS_BUFFER.pending) - - -def test_actuator_subtypes_coverage_quiet_when_all_modeled(): - _reset() - schema = {"actuator_types": {"motor": []}} - rules = {"categories": {"actuator": { - "subtypes": [{"key": "motor"}], - }}} - checks._check_actuator_subtypes_coverage(schema, rules) - assert "actuator_subtypes_coverage" not in _pending_sources() - - -# ---------- _check_sensor_subtypes_coverage ---------------------------- - -def test_sensor_subtypes_coverage_fires_for_unmodeled_subtype(): - _reset() - schema = {"sensor_types": ["touch", "newsensor"]} - rules = {"categories": {"sensor": { - "subtypes": [{"key": "touch"}], - }}} - checks._check_sensor_subtypes_coverage(schema, rules) - assert any("newsensor" in d.message and d.source == "sensor_subtypes_coverage" - for d in gen._DIAGS_BUFFER.pending) - - -def test_sensor_subtypes_coverage_quiet_when_all_modeled(): - _reset() - schema = {"sensor_types": ["touch"]} - rules = {"categories": {"sensor": { - "subtypes": [{"key": "touch"}], - }}} - checks._check_sensor_subtypes_coverage(schema, rules) - assert "sensor_subtypes_coverage" not in _pending_sources() - - -# ---------- _check_setto_param_coverage -------------------------------- - -def test_setto_param_coverage_fires_for_unmapped_param(): - _reset() - schema = {"actuator_common": {"attrs": ["kp"]}, "actuator_types": {"position": ["kv"]}} - rules = { - "categories": {"actuator": { - "schema_common_block": "actuator_common", - "subtype_setto": {"position": {"call": "mjs_setToPosition"}}, - }}, - "setto_param_defaults": {}, - } - mjspec = {"setto_functions": {"mjs_setToPosition": { - "params": [ - {"name": "kp", "c_type": "double", "array_dim": None}, - {"name": "kv", "c_type": "double", "array_dim": None}, - {"name": "noveltie", "c_type": "double", "array_dim": None}, - ], - }}} - checks._check_setto_param_coverage(schema, rules, mjspec) - assert any("noveltie" in d.message and d.source == "setto_param_coverage" - for d in gen._DIAGS_BUFFER.pending) - - -def test_setto_param_coverage_quiet_when_all_mapped(): - _reset() - schema = {"actuator_common": {"attrs": ["kp"]}, "actuator_types": {"position": ["kv"]}} - rules = { - "categories": {"actuator": { - "schema_common_block": "actuator_common", - "subtype_setto": {"position": {"call": "mjs_setToPosition"}}, - }}, - "setto_param_defaults": {"mjs_setToPosition": {"inheritrange": "0.0"}}, - } - mjspec = {"setto_functions": {"mjs_setToPosition": { - "params": [ - {"name": "kp", "c_type": "double", "array_dim": None}, - {"name": "kv", "c_type": "double", "array_dim": None}, - {"name": "inheritrange", "c_type": "double", "array_dim": None}, - ], - }}} - checks._check_setto_param_coverage(schema, rules, mjspec) - assert "setto_param_coverage" not in _pending_sources() - - -# ---------- _check_mjs_struct_field_drift ------------------------------ - -def test_mjs_struct_field_drift_fires_for_uncovered_field(): - _reset() - schema = {"joint_common": ["stiffness"]} - rules = { - "categories": {"joint": { - "schema_common_block": "joint_common", - "mjs_struct": "mjsJoint", - }}, - "intentionally_unmodeled_mjs_fields": {}, - } - mjspec = {"structs": {"mjsJoint": ["stiffness", "mystery_field"]}} - checks._check_mjs_struct_field_drift(schema, rules, mjspec) - assert any("mystery_field" in d.message and d.source == "mjs_field_drift" - for d in gen._DIAGS_BUFFER.pending) - - -def test_mjs_struct_field_drift_quiet_when_all_modeled(): - _reset() - schema = {"joint_common": ["stiffness"]} - rules = { - "categories": {"joint": { - "schema_common_block": "joint_common", - "mjs_struct": "mjsJoint", - }}, - "intentionally_unmodeled_mjs_fields": {}, - } - mjspec = {"structs": {"mjsJoint": ["stiffness", "name"]}} # 'name' is structural - checks._check_mjs_struct_field_drift(schema, rules, mjspec) - assert "mjs_field_drift" not in _pending_sources() - - -# ---------- _check_generated_enum_coverage ----------------------------- - -def test_generated_enum_coverage_fires_for_unmapped_member(): - _reset() - rules = {"generated_enums": {"MjOptionEnums": { - "enums": [{ - "ue_name": "EMjIntegrator", - "from_mj_enum": "mjtIntegrator", - "ue_member_from_mj": {"mjINT_EULER": "Euler"}, - # mjINT_RK4 missing from the rule -> should diag. - }], - }}} - mjspec = {"enums": {"mjtIntegrator": {"mjINT_EULER": 0, "mjINT_RK4": 1}}} - checks._check_generated_enum_coverage(rules, mjspec) - assert any("mjINT_RK4" in d.message and d.source == "enum_drift" - for d in gen._DIAGS_BUFFER.pending) - - -def test_generated_enum_coverage_quiet_when_all_mapped(): - _reset() - rules = {"generated_enums": {"MjOptionEnums": { - "enums": [{ - "ue_name": "EMjIntegrator", - "from_mj_enum": "mjtIntegrator", - "ue_member_from_mj": {"mjINT_EULER": "Euler", "mjINT_RK4": "RK4"}, - }], - }}} - mjspec = {"enums": {"mjtIntegrator": {"mjINT_EULER": 0, "mjINT_RK4": 1}}} - checks._check_generated_enum_coverage(rules, mjspec) - assert "enum_drift" not in _pending_sources() - - -def test_generated_enum_coverage_fires_when_from_mj_enum_unknown(): - """Snapshot drift: a from_mj_enum that's not in the projected - mjspec must produce a diagnostic instead of a silent skip.""" - _reset() - rules = {"generated_enums": {"MjOptionEnums": { - "enums": [{ - "ue_name": "EMjGone", - "from_mj_enum": "mjtGoneRenamed", - "ue_member_from_mj": {"mjGONE_X": "X"}, - }], - }}} - mjspec = {"enums": {"mjtIntegrator": {"mjINT_EULER": 0}}} - checks._check_generated_enum_coverage(rules, mjspec) - assert any("mjtGoneRenamed" in d.message and d.source == "enum_drift" - for d in gen._DIAGS_BUFFER.pending) diff --git a/Scripts/codegen/tests/test_data_packed_export.py b/Scripts/codegen/tests/test_data_packed_export.py deleted file mode 100644 index b9a06a01..00000000 --- a/Scripts/codegen/tests/test_data_packed_export.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for mjs_data_packed_attrs emission, including the per-element -``export_op`` unit-conversion path used by mjsEquality.anchor (cm -> m -before packing into data[0..2]).""" - -from __future__ import annotations - -import pytest - -from generate_ue_components import _emit_data_packed_export - - -def test_slot_scalar_no_conversion(): - """Single-slot pack without export_op writes raw scalar.""" - cpp = _emit_data_packed_export( - attr="torquescale", - packed_def={ - "slot": 10, - "condition": "EqualityType == EMjEqualityType::Weld", - }, - ue_type="float", - prop_name="torquescale", - ) - assert "Element->data[10] = (mjtNum)torquescale" in cpp - assert "bOverride_torquescale" in cpp - assert "EqualityType == EMjEqualityType::Weld" in cpp - # No temp var needed when no conversion - assert "float V" not in cpp - - -def test_slot_range_no_conversion(): - """Slot-range pack without export_op writes raw array elements.""" - cpp = _emit_data_packed_export( - attr="polycoef", - packed_def={ - "slot_range": [0, 5], - "condition": "(EqualityType == EMjEqualityType::Joint) || (EqualityType == EMjEqualityType::Tendon)", - }, - ue_type="TArray", - prop_name="polycoef", - ) - assert "Element->data[0 + i] = (mjtNum)polycoef[i]" in cpp - assert "i < polycoef.Num() && i < 5" in cpp - # No temp var needed when no conversion - assert "float V" not in cpp - - -def test_slot_range_with_cm_to_m(): - """anchor pattern: cm -> m per-element conversion before packing.""" - cpp = _emit_data_packed_export( - attr="anchor", - packed_def={ - "slot_range": [0, 3], - "condition": "(EqualityType == EMjEqualityType::Connect) || (EqualityType == EMjEqualityType::Weld)", - "export_op": "cm_to_m", - }, - ue_type="TArray", - prop_name="anchor", - ) - # Temp var used to apply conversion before write - assert "float V = anchor[i]" in cpp - assert "V *= 0.01f" in cpp - assert "Element->data[0 + i] = (mjtNum)V" in cpp - assert "i < anchor.Num() && i < 3" in cpp - - -def test_slot_scalar_with_m_to_cm(): - """Scalar slot with export_op — symmetric to the range case.""" - cpp = _emit_data_packed_export( - attr="z_offset_cm", - packed_def={ - "slot": 4, - "export_op": "m_to_cm", - }, - ue_type="float", - prop_name="z_offset_cm", - ) - assert "float V = (float)z_offset_cm" in cpp - assert "V *= 100.0f" in cpp - assert "Element->data[4] = (mjtNum)V" in cpp - - -def test_unknown_export_op_raises(): - with pytest.raises(RuntimeError, match="unknown export_op"): - _emit_data_packed_export( - attr="x", - packed_def={"slot": 0, "export_op": "lightyears_to_furlongs"}, - ue_type="float", - prop_name="x", - ) - - -def test_missing_slot_and_slot_range_raises(): - with pytest.raises(RuntimeError, match="must specify either"): - _emit_data_packed_export( - attr="x", - packed_def={"condition": "true"}, - ue_type="float", - prop_name="x", - ) - - -def test_slot_range_requires_tarray(): - """slot_range path needs a TArray UPROPERTY — fail loudly on scalar.""" - with pytest.raises(RuntimeError, match="slot_range requires UE TArray"): - _emit_data_packed_export( - attr="x", - packed_def={"slot_range": [0, 3]}, - ue_type="float", - prop_name="x", - ) diff --git a/Scripts/codegen/tests/test_dry_run.py b/Scripts/codegen/tests/test_dry_run.py deleted file mode 100644 index 8a05fcba..00000000 --- a/Scripts/codegen/tests/test_dry_run.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""End-to-end dry-run test: generator runs against the real schema + -mjxmacro snapshots and produces a deterministic set of writes.""" - -from __future__ import annotations - -import json -import os -import sys - -import pytest - - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_PLUGIN_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", "..")) - - -def test_collect_all_writes_includes_expected_categories(real_schema, real_rules, real_mjxmacro, real_mjspec): - from generate_ue_components import collect_all_writes - # The plugin's actual Public/Private roots (read-only — we don't write). - plugin_root = _PLUGIN_ROOT - public_root = os.path.join(plugin_root, "Source", "URLab", "Public") - private_root = os.path.join(plugin_root, "Source", "URLab", "Private") - bind_h = os.path.join(public_root, "MuJoCo", "Utils", "MjBind.h") - writes = collect_all_writes(real_schema, real_rules, real_mjxmacro, - public_root, private_root, bind_h, - mjspec=real_mjspec) - # We always emit the test-schema header. - paths = [w.path for w in writes] - assert any(p.endswith("MjSchemaForTests.h") for p in paths) - # 49 sensor subtypes × (.h + .cpp) = 98 subclass writes; plus the - # base files (MjSensor.h/.cpp + MjCamera.h/.cpp, post-migration). - sensor_subclass_writes = [ - p for p in paths - if "Components" in p and "Sensors" in p - and (p.endswith(".h") or p.endswith(".cpp")) - and "MjSensor" not in os.path.basename(p) - and "MjCamera" not in os.path.basename(p) - ] - assert len(sensor_subclass_writes) == 98, ( - f"expected 98 sensor subclass file writes, got {len(sensor_subclass_writes)}" - ) - - -def test_generator_is_deterministic(real_schema, real_rules, real_mjxmacro, real_mjspec): - """Same inputs -> same outputs.""" - from generate_ue_components import collect_all_writes - plugin_root = _PLUGIN_ROOT - public_root = os.path.join(plugin_root, "Source", "URLab", "Public") - private_root = os.path.join(plugin_root, "Source", "URLab", "Private") - bind_h = os.path.join(public_root, "MuJoCo", "Utils", "MjBind.h") - a = collect_all_writes(real_schema, real_rules, real_mjxmacro, - public_root, private_root, bind_h, mjspec=real_mjspec) - b = collect_all_writes(real_schema, real_rules, real_mjxmacro, - public_root, private_root, bind_h, mjspec=real_mjspec) - a_map = {w.path: w.content for w in a} - b_map = {w.path: w.content for w in b} - assert a_map == b_map diff --git a/Scripts/codegen/tests/test_emit_enum_switch.py b/Scripts/codegen/tests/test_emit_enum_switch.py deleted file mode 100644 index 34da5276..00000000 --- a/Scripts/codegen/tests/test_emit_enum_switch.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the ``emit_enum_switch`` codegen helper. - -The helper backs three sites: xml_enum exports (single-line cases + -``default: break;``), objtype dispatch (multi-key groups + multi-line -default), and geom FinalType resolution (single-line cases, no -default). Each shape is locked here so a future tweak to the helper -can't silently break byte-identity on the emitted .cpp.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_single_line_case_shape_matches_xml_enum_export(): - """Single ``str`` member → one-line ``case Enum::M: ; break;`` - — the shape every xml_enum_export emits.""" - out = gen.emit_enum_switch( - "JointType", "EMjJointType", - [ - ("Hinge", "Element->type = (mjtJoint)mjJNT_HINGE"), - ("Slide", "Element->type = (mjtJoint)mjJNT_SLIDE"), - ], - indent=" ", - default_break=True, - ) - expected = ( - " switch (JointType)\n" - " {\n" - " case EMjJointType::Hinge: Element->type = (mjtJoint)mjJNT_HINGE; break;\n" - " case EMjJointType::Slide: Element->type = (mjtJoint)mjJNT_SLIDE; break;\n" - " default: break;\n" - " }\n" - ) - assert out == expected - - -def test_multi_key_group_stacks_labels_with_multiline_body(): - """A list-of-strings member stacks case labels and emits body + - break on indented separate lines — the shape objtype_dispatch - needs for ``case Body:`` / ``case XBody:`` collapsing to one expr.""" - out = gen.emit_enum_switch( - "ObjType", "EMjObjType", - [ - (["Body", "XBody"], "Target = mjOBJ_BODY"), - ("Geom", "Target = mjOBJ_GEOM"), - ], - indent=" ", - default_body="Target = mjOBJ_UNKNOWN", - ) - expected = ( - " switch (ObjType)\n" - " {\n" - " case EMjObjType::Body:\n" - " case EMjObjType::XBody:\n" - " Target = mjOBJ_BODY;\n" - " break;\n" - " case EMjObjType::Geom: Target = mjOBJ_GEOM; break;\n" - " default:\n" - " Target = mjOBJ_UNKNOWN;\n" - " break;\n" - " }\n" - ) - assert out == expected - - -def test_no_default_when_neither_flag_set(): - """geom_final_type emits no default branch — the helper must - omit it cleanly.""" - out = gen.emit_enum_switch( - "Type", "EMjGeomType", - [("Sphere", "FinalType = mjGEOM_SPHERE")], - indent=" ", - ) - expected = ( - " switch (Type)\n" - " {\n" - " case EMjGeomType::Sphere: FinalType = mjGEOM_SPHERE; break;\n" - " }\n" - ) - assert out == expected - - -def test_default_body_takes_priority_over_default_break(): - """When both are set, the multi-line default wins. Keeps caller - intent obvious — ``default_break`` is the simple short-form.""" - out = gen.emit_enum_switch( - "X", "EMjX", - [("A", "y = 1")], - default_break=True, - default_body="y = 0", - ) - assert "default:\n" in out - assert "default: break;" not in out - - -def test_indent_propagates_to_case_labels_and_body(): - """Case labels sit at ``indent + 4 spaces``; multi-line case bodies - at ``indent + 8 spaces``. The lock prevents indentation drift.""" - out = gen.emit_enum_switch( - "X", "EMjX", - [(["A", "B"], "z = 1")], - indent=" ", # 12 spaces - ) - # switch line at 12 spaces. - assert out.startswith(" switch (X)\n") - # case labels at 12 + 4 = 16 spaces. - assert " case EMjX::A:\n" in out - # body at 12 + 8 = 20 spaces. - assert " z = 1;\n" in out diff --git a/Scripts/codegen/tests/test_emit_property.py b/Scripts/codegen/tests/test_emit_property.py deleted file mode 100644 index 3b66f3dd..00000000 --- a/Scripts/codegen/tests/test_emit_property.py +++ /dev/null @@ -1,72 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Per-property emission shape tests.""" - -from __future__ import annotations - -import pytest - -from generate_ue_components import ( - EmittedSchema, - emit_schema_for_attrs, -) - - -# override_toggle_name is exercised everywhere emit_schema_for_attrs runs; -# the cross-category guarantee is pinned in -# test_behavioral_emit_guarantees::test_override_toggle_name_matches_uproperty_for_all_categories. - - -def test_emit_basic_property(minimal_rules): - out = emit_schema_for_attrs(["stiffness"], minimal_rules, "joint", "Joint") - assert "float stiffness" in out.properties_h - assert "bOverride_stiffness" in out.properties_h - assert "= 0.0f;" in out.properties_h - assert "MjXmlUtils::ReadAttrFloat" in out.imports_cpp - assert 'TEXT("stiffness")' in out.imports_cpp - assert "Element->stiffness" in out.exports_cpp - - -def test_emit_typed_property_range(minimal_rules): - out = emit_schema_for_attrs(["range"], minimal_rules, "joint", "Joint") - assert "TArray range" in out.properties_h - assert "MjXmlUtils::ReadAttrFloatArray" in out.imports_cpp - assert "Element->range[i]" in out.exports_cpp - - -def test_emit_bool_property(minimal_rules): - out = emit_schema_for_attrs(["limited"], minimal_rules, "joint", "Joint") - assert "bool limited" in out.properties_h - assert "MjXmlUtils::ReadAttrBool" in out.imports_cpp - assert "Element->limited = limited ? 1 : 0;" in out.exports_cpp - - -def test_emit_vector_property(minimal_rules): - out = emit_schema_for_attrs(["axis"], minimal_rules, "joint", "Joint") - # 'axis' is in joint excludes; should not appear. - assert "FVector axis" not in out.properties_h - rules2 = {**minimal_rules, - "element_rules": {"joint": {"exclude_attrs": []}}} - out2 = emit_schema_for_attrs(["axis"], rules2, "joint", "Joint") - assert "FVector axis" in out2.properties_h - assert "MjXmlUtils::ReadAttrVec3" in out2.imports_cpp - - -def test_global_exclude_skipped(minimal_rules): - out = emit_schema_for_attrs(["user", "stiffness"], minimal_rules, "joint", "Joint") - assert "stiffness" in out.properties_h - assert " user " not in out.properties_h - - -def test_element_exclude_skipped(minimal_rules): - out = emit_schema_for_attrs(["pos", "axis", "stiffness"], minimal_rules, "joint", "Joint") - assert "stiffness" in out.properties_h - assert "FVector pos" not in out.properties_h - assert "FVector axis" not in out.properties_h - - -def test_no_b_prefix_on_bool(minimal_rules): - """`limited` emits as `bool limited`, not `bool bLimited`.""" - out = emit_schema_for_attrs(["limited"], minimal_rules, "joint", "Joint") - assert "bool limited" in out.properties_h - assert "bOverride_limited" in out.properties_h - assert "bLimited" not in out.properties_h diff --git a/Scripts/codegen/tests/test_emitter_capabilities.py b/Scripts/codegen/tests/test_emitter_capabilities.py deleted file mode 100644 index 24d85153..00000000 --- a/Scripts/codegen/tests/test_emitter_capabilities.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Tests for opt-in emitter capabilities in generate_ue_components.py: - -1. ``_resolve_value_map``: returns the rule's literal ``value_map``; - missing ``value_map`` raises. -2. ``xml_enum_attrs.emit_property_decl: true``: codegen emits the UE - enum UPROPERTY decl into the schema PROPERTIES block. - -Each test uses synthetic rules to exercise its capability in isolation. -""" - -from __future__ import annotations - -import os -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -if _CODEGEN_DIR not in sys.path: - sys.path.insert(0, _CODEGEN_DIR) - -import pytest # noqa: E402 - -from generate_ue_components import ( # noqa: E402 - _resolve_value_map, - _emit_xml_enum_import, - emit_schema_for_attrs, -) - - -# ----- value_map resolver ---------------------------------------------- - -def test_value_map_returned_verbatim(): - """The resolver returns the rule's literal ``value_map`` unchanged.""" - enum_def = { - "ue_property": "Type", - "ue_enum_type": "EMjJointType", - "value_map": {"hinge": ["Hinge", "mjJNT_HINGE"]}, - } - resolved = _resolve_value_map("type", enum_def) - assert resolved == {"hinge": ["Hinge", "mjJNT_HINGE"]} - - -def test_value_map_resolver_errors_when_missing(): - enum_def = {"ue_property": "X", "ue_enum_type": "E"} - with pytest.raises(RuntimeError, match="missing value_map"): - _resolve_value_map("attr", enum_def) - - -def test_xml_enum_import_emits_expected_blocks(): - enum_def = { - "ue_property": "Type", - "ue_enum_type": "EMjJointType", - "value_map": {"hinge": ["Hinge", "mjJNT_HINGE"], "slide": ["Slide", "mjJNT_SLIDE"]}, - } - out = _emit_xml_enum_import("type", enum_def) - assert "Type = EMjJointType::Hinge" in out - assert "Type = EMjJointType::Slide" in out - assert "bOverride_Type = true" in out - - -# ----- emit_property_decl opt-in --------------------------------------- - -def test_emit_property_decl_off_skips_uproperty(): - """Default behaviour: xml_enum_attrs entries do NOT emit the UE - UPROPERTY decl (the .h hand-declares it).""" - rules = { - "element_rules": { - "joint": { - "xml_enum_attrs": { - "type": { - "ue_property": "Type", - "ue_enum_type": "EMjJointType", - "value_map": {"hinge": ["Hinge", "mjJNT_HINGE"]}, - } - } - } - } - } - out = emit_schema_for_attrs([], rules, element_name="joint", category_label="Joint") - assert "EMjJointType" not in out.properties_h - - -def test_emit_property_decl_on_emits_uproperty_pair(): - """With opt-in, codegen emits both the override toggle and the - EMjX UPROPERTY using the standard _emit_uproperty shape.""" - rules = { - "element_rules": { - "joint": { - "xml_enum_attrs": { - "type": { - "ue_property": "Type", - "ue_enum_type": "EMjJointType", - "emit_property_decl": True, - "value_map": {"hinge": ["Hinge", "mjJNT_HINGE"]}, - } - } - } - } - } - out = emit_schema_for_attrs([], rules, element_name="joint", category_label="Joint") - assert "EMjJointType Type" in out.properties_h - assert "bOverride_Type" in out.properties_h - - diff --git a/Scripts/codegen/tests/test_enum_scrape.py b/Scripts/codegen/tests/test_enum_scrape.py deleted file mode 100644 index c4055014..00000000 --- a/Scripts/codegen/tests/test_enum_scrape.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the mjt* enum surface the codegen consumes. - -mjt* enums come from the clang-AST introspect snapshot — libclang -gives us the real member values and handles every ``typedef enum -mjtX_ { ... } mjtX;`` shape uniformly. The shipped snapshot has to -carry every enum the codegen rules reference; the cross-check below -is the guard.""" - -from __future__ import annotations - -import json -import os -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -if _CODEGEN_DIR not in sys.path: - sys.path.insert(0, _CODEGEN_DIR) - - -def _load_projected_mjspec(): - """Load introspect + project into the legacy mjspec shape via the - same helper the runtime uses. Mirrors the conftest fixture.""" - import generate_ue_components as gen - plugin_root = os.path.normpath(os.path.join(_HERE, "..", "..", "..")) - snap_path = os.path.join( - plugin_root, "Scripts", "codegen", "snapshots", - "introspect_snapshot.json", - ) - with open(snap_path, "r", encoding="utf-8") as f: - introspect = json.load(f) - return gen._mjspec_from_introspect(introspect) - - -def test_real_snapshot_has_expected_enums(): - """Introspect must carry every mjt* enum the codegen rules drift - checks rely on (the cross-walk between value_map entries and the - enum members the snapshot reports for that mjt* type).""" - mjspec = _load_projected_mjspec() - enums = mjspec.get("enums", {}) - for expected in ( - "mjtJoint", # joint.type - "mjtGeom", # geom.type - "mjtSensor", # sensor types - "mjtObj", # objtype / reftype - "mjtTrn", # actuator transmission - "mjtIntegrator", # mjOption.integrator - "mjtCone", # mjOption.cone - "mjtSolver", # mjOption.solver - ): - assert expected in enums, f"missing enum {expected} from snapshot" - assert len(enums[expected]) > 0, f"enum {expected} has no members" diff --git a/Scripts/codegen/tests/test_expand_stride_guard.py b/Scripts/codegen/tests/test_expand_stride_guard.py deleted file mode 100644 index 58d156dc..00000000 --- a/Scripts/codegen/tests/test_expand_stride_guard.py +++ /dev/null @@ -1,54 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks the ``_expand_stride`` guard. ``MJ_M(...)`` lowers to -``m->...``; numeric and ``mjN*`` strides pass through unchanged. Any -``MJ_*( ... )`` macro that survives is an unhandled shape and must -surface a diagnostic before the generated MjBind.h fails to compile.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_mj_m_macro_lowers_to_pointer_field(): - assert gen._expand_stride("MJ_M(nuser_jnt)") == "m->nuser_jnt" - - -def test_numeric_stride_passes_through_unchanged(): - for s in ("1", "3", "9"): - assert gen._expand_stride(s) == s - - -def test_mj_n_constant_passes_through_unchanged(): - assert gen._expand_stride("mjNREF") == "mjNREF" - assert gen._expand_stride("mjNBIAS") == "mjNBIAS" - - -def test_compound_expression_expands_and_keeps_other_tokens(): - """Real MuJoCo strides occasionally combine ``MJ_M(...)*N`` for - derived per-element block sizes. The guard must NOT fire because - the substitution removes the only MJ_ macro.""" - assert gen._expand_stride("MJ_M(nuser_geom)*3") == "m->nuser_geom*3" - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_unknown_macro_fires_guard_diagnostic(): - """A future MuJoCo bump that introduces e.g. ``MJ_D(foo)`` must - not silently land verbatim in MjBind.h.""" - out = gen._expand_stride("MJ_D(foo)") - assert "MJ_D(foo)" in out - assert any("unhandled MuJoCo macro" in d.message and "MJ_D(foo)" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_multiple_unknown_macros_listed_together(): - gen._expand_stride("MJ_D(a) + MJ_V(b)") - diags = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("MJ_D(a)" in m and "MJ_V(b)" in m for m in diags), diags - - -def test_known_and_unknown_mixed_still_fires_for_unknown(): - """Known macro expanded, unknown surfaces a diag.""" - out = gen._expand_stride("MJ_M(nuser_jnt) * MJ_D(foo)") - assert "m->nuser_jnt" in out - assert "MJ_D(foo)" in out - assert any("MJ_D(foo)" in d.message for d in gen._DIAGS_BUFFER.pending) diff --git a/Scripts/codegen/tests/test_find_matching_brace.py b/Scripts/codegen/tests/test_find_matching_brace.py deleted file mode 100644 index 61c2e702..00000000 --- a/Scripts/codegen/tests/test_find_matching_brace.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks ``_find_matching_brace`` — the brace-aware extractor that -backs the banner-overwrite audit. The previous ``\\{([^}]*)\\}`` regex -truncated at the first inner ``}``, silently underreporting extra ctor -content when a real ctor body contained a range-for loop or nested -if. The extractor must track string literals and comments so a ``{`` -inside ``TEXT("...")`` does not shift the depth count.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_finds_simple_matching_brace(): - text = "x { hello } y" - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - assert close_idx is not None - assert text[close_idx] == "}" - assert text[open_idx + 1:close_idx] == " hello " - - -def test_handles_nested_braces(): - text = "ctor() { if (x) { do(); } else { other(); } }" - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - # close_idx must be the FINAL '}', not the first inner one. - assert close_idx == len(text) - 1 - - -def test_ignores_braces_in_string_literal(): - text = 'fn() { Print(TEXT("{nested}")); }' - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - # The string contains '{' and '}' but they must NOT change depth; - # the matcher should land on the final outer '}'. - assert close_idx == len(text) - 1 - - -def test_ignores_braces_in_char_literal(): - text = "fn() { char c = '{'; print(c); }" - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - assert close_idx == len(text) - 1 - - -def test_ignores_braces_in_line_comment(): - text = "fn() { // tail {\n real(); }" - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - assert close_idx == len(text) - 1 - - -def test_ignores_braces_in_block_comment(): - text = "fn() { /* tail { in comment */ real(); }" - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - assert close_idx == len(text) - 1 - - -def test_returns_none_for_unmatched_open(): - """If the input is malformed (open without a matching close), the - helper returns None — caller decides whether that's a diag or a - silent skip.""" - assert gen._find_matching_brace("fn() { unclosed", 5) is None - - -def test_returns_none_when_index_is_not_open_brace(): - assert gen._find_matching_brace("x }", 1) is None - assert gen._find_matching_brace("x", 5) is None # off-end - - -def test_escape_sequences_in_string_dont_terminate_early(): - """A ``\\"`` inside a string literal must not close the string - early — the matcher would otherwise re-enter brace-counting mode - inside the literal and mis-count.""" - text = r'fn() { print("a\"b{c"); real(); }' - open_idx = text.index("{") - close_idx = gen._find_matching_brace(text, open_idx) - assert close_idx == len(text) - 1 diff --git a/Scripts/codegen/tests/test_geom_final_type.py b/Scripts/codegen/tests/test_geom_final_type.py deleted file mode 100644 index b0f40af9..00000000 --- a/Scripts/codegen/tests/test_geom_final_type.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the ``geom_final_type`` rule shape — resolves an mjsGeom -type at compile time + emits the mesh-name export under the right -EMjGeomType condition.""" - -from __future__ import annotations - -import generate_ue_components as gen -from generate_ue_components import _emit_geom_final_type_block - - -def _geom_value_map_rules(): - """Minimal cat/element rules that exercise the FinalType switch.""" - cat_rules = { - "geom_final_type": { - "xml_enum_ref": "type", - "override_field": "bOverride_Type", - "default_lookup": "Default->Type", - "default_fallback": "mjGEOM_SPHERE", - "name_export_for": "mjGEOM_MESH", - "name_field": "MeshName", - "name_setter": "Element->meshname.assign", - "name_target": "*Element", - }, - } - element_rules = { - "xml_enum_attrs": { - "type": { - "ue_property": "Type", - "ue_enum": "EMjGeomType", - "ue_enum_type": "EMjGeomType", - "override_toggle": "bOverride_Type", - "default_lookup": "Default->Type", - "value_map": { - "sphere": ["Sphere", "mjGEOM_SPHERE"], - "box": ["Box", "mjGEOM_BOX"], - "mesh": ["Mesh", "mjGEOM_MESH"], - }, - }, - }, - } - return cat_rules, element_rules - - -def test_geom_final_type_emits_full_block(): - cat_rules, element_rules = _geom_value_map_rules() - out = _emit_geom_final_type_block(cat_rules, element_rules) - assert out is not None - assert "FinalType" in out - assert "case EMjGeomType::Sphere: FinalType = mjGEOM_SPHERE;" in out - assert "case EMjGeomType::Box: FinalType = mjGEOM_BOX;" in out - assert "case EMjGeomType::Mesh: FinalType = mjGEOM_MESH;" in out - assert "MeshName" in out - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_geom_final_type_returns_none_when_no_rule(): - assert _emit_geom_final_type_block({}, {}) is None - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_geom_final_type_diagnoses_missing_enum_def(): - cat_rules = { - "geom_final_type": { - "xml_enum_ref": "nonexistent", - "default_fallback": "mjGEOM_SPHERE", - "first_mj_const_token": "mjGEOM_PLANE", - "name_for": "mjGEOM_MESH", - "name_field": "MeshName", - "name_setter": "Element->meshname.assign", - "name_target": "*Element", - }, - } - out = _emit_geom_final_type_block(cat_rules, {"xml_enum_attrs": {}}) - assert out is None - assert any("nonexistent" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_geom_final_type_diagnoses_empty_value_map(): - cat_rules, element_rules = _geom_value_map_rules() - # Wipe out the value_map and provide no mjspec to fall back to. - element_rules["xml_enum_attrs"]["type"]["value_map"] = {} - out = _emit_geom_final_type_block(cat_rules, element_rules) - assert out is None - assert any("empty value_map" in d.message for d in gen._DIAGS_BUFFER.pending) diff --git a/Scripts/codegen/tests/test_hand_enum_drift.py b/Scripts/codegen/tests/test_hand_enum_drift.py deleted file mode 100644 index 9c8d1688..00000000 --- a/Scripts/codegen/tests/test_hand_enum_drift.py +++ /dev/null @@ -1,234 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the hand-enum drift checks: ``_scan_hand_enums`` scrapes -URLab ``EMj*`` enum members from Source/, and ``_check_hand_enum_drift`` -cross-references them against MuJoCo's ``mjt*`` enums and the rules' -``value_map`` UE-member references.""" - -from __future__ import annotations - -import generate_ue_components as gen -import _codegen_checks as _checks -gen._scan_hand_enums = _checks._scan_hand_enums - - -def _write_enum_header(tmp_path, filename: str, enum_text: str): - p = tmp_path / filename - p.write_text(enum_text, encoding="utf-8") - return p - - -# ---------- _scan_hand_enums ----------------------------------------------- - -def test_scan_hand_enums_parses_umeta_members(tmp_path): - """UMETA(DisplayName=...) on each member must NOT hide the member - from the scanner (was the original parser bug — EMjCameraTrackingMode - lost TrackCom and TargetBodyCom because the per-line regex required - the line to end at the member).""" - _write_enum_header(tmp_path, "Sample.h", """ - UENUM(BlueprintType) - enum class EMjSample : uint8 - { - Fixed UMETA(DisplayName = "Fixed"), - Track UMETA(DisplayName = "Track"), - TrackCom UMETA(DisplayName = "Track (Centre of Mass)"), - TargetBody UMETA(DisplayName = "Target Body"), - TargetBodyCom UMETA(DisplayName = "Target Body (Centre of Mass)"), - }; - """) - enums = gen._scan_hand_enums(str(tmp_path)) - assert "EMjSample" in enums - assert enums["EMjSample"] == [ - "Fixed", "Track", "TrackCom", "TargetBody", "TargetBodyCom", - ] - - -def test_scan_hand_enums_handles_explicit_values(tmp_path): - """Explicit ``= N`` initialisers must not break member capture.""" - _write_enum_header(tmp_path, "WithVals.h", """ - enum class EMjWithVals : uint8 - { - Connect = 0, - Weld = 1, - Joint = 2, - }; - """) - enums = gen._scan_hand_enums(str(tmp_path)) - assert enums["EMjWithVals"] == ["Connect", "Weld", "Joint"] - - -# ---------- _check_hand_enum_drift ----------------------------------------- - -def test_hand_enum_drift_diagnoses_subtype_enum_value_typo(tmp_path): - _write_enum_header(tmp_path, "MjJoint.h", """ - enum class EMjJointType : uint8 { Hinge, Slide, Ball }; - """) - rules = { - "categories": { - "joint": { - "type_enum_name": "EMjJointType", - "subtypes": [ - {"key": "hinge", "enum_value": "Hinge"}, - {"key": "screw", "enum_value": "Screwd"}, # typo - ], - }, - }, - } - gen._check_hand_enum_drift({}, rules, None, str(tmp_path)) - assert any("Screwd" in d.message and "EMjJointType" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_hand_enum_drift_diagnoses_value_map_ue_member_typo(tmp_path): - _write_enum_header(tmp_path, "MjGeom.h", """ - enum class EMjGeomType : uint8 { Sphere, Box, Mesh }; - """) - rules = { - "categories": {}, - "element_rules": { - "geom": { - "xml_enum_attrs": { - "type": { - "ue_enum_type": "EMjGeomType", - "value_map": { - "sphere": ["Sphere", "mjGEOM_SPHERE"], - "box": ["Bxox", "mjGEOM_BOX"], # typo - }, - }, - }, - }, - }, - } - gen._check_hand_enum_drift({}, rules, None, str(tmp_path)) - assert any("Bxox" in d.message and "EMjGeomType" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_hand_enum_drift_surfaces_new_mj_enum_value(tmp_path): - """A new mjJNT_SCREW upstream with no value_map entry fires a - diagnostic — closes the silent-runtime-fallthrough class.""" - _write_enum_header(tmp_path, "MjJoint.h", """ - enum class EMjJointType : uint8 { Hinge, Slide, Ball }; - """) - rules = { - "categories": {}, - "element_rules": { - "joint": { - "xml_enum_attrs": { - "type": { - "ue_enum_type": "EMjJointType", - "mjs_cast": "mjtJoint", - "value_map": { - "hinge": ["Hinge", "mjJNT_HINGE"], - "slide": ["Slide", "mjJNT_SLIDE"], - "ball": ["Ball", "mjJNT_BALL"], - }, - }, - }, - }, - }, - } - mjspec = { - "enums": { - "mjtJoint": ["mjJNT_FREE", "mjJNT_BALL", "mjJNT_SLIDE", - "mjJNT_HINGE", "mjJNT_SCREW"], - }, - } - gen._check_hand_enum_drift({}, rules, mjspec, str(tmp_path)) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("mjJNT_SCREW" in m for m in msgs), msgs - assert any("mjJNT_FREE" in m for m in msgs), msgs - - -def test_hand_enum_drift_respects_intentional_skips(tmp_path): - _write_enum_header(tmp_path, "MjActuator.h", """ - enum class EMjGainType : uint8 { Fixed, Affine, Muscle }; - """) - rules = { - "categories": {}, - "intentionally_unmapped_mj_enum_values": { - "mjtGain": ["mjGAIN_DCMOTOR"], - }, - "element_rules": { - "actuator": { - "xml_enum_attrs": { - "gaintype": { - "ue_enum_type": "EMjGainType", - "mjs_cast": "mjtGain", - "value_map": { - "fixed": ["Fixed", "mjGAIN_FIXED"], - "affine": ["Affine", "mjGAIN_AFFINE"], - "muscle": ["Muscle", "mjGAIN_MUSCLE"], - }, - }, - }, - }, - }, - } - mjspec = { - "enums": { - "mjtGain": ["mjGAIN_FIXED", "mjGAIN_AFFINE", "mjGAIN_MUSCLE", - "mjGAIN_USER", "mjGAIN_DCMOTOR"], - }, - } - gen._check_hand_enum_drift({}, rules, mjspec, str(tmp_path)) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - # DCMOTOR is in the skip list -> no diag. - assert not any("mjGAIN_DCMOTOR" in m for m in msgs) - # USER is not -> diag fires. - assert any("mjGAIN_USER" in m for m in msgs), msgs - - -def test_hand_enum_drift_per_element_overrides(tmp_path): - """Same mjt* enum (mjtGeom) admits different subsets for geom vs - site. The __per_element__ override must scope correctly.""" - _write_enum_header(tmp_path, "MjGeom.h", """ - enum class EMjGeomType : uint8 { Sphere, Box, Plane, Mesh }; - """) - _write_enum_header(tmp_path, "MjSite.h", """ - enum class EMjSiteType : uint8 { Sphere, Box }; - """) - rules = { - "categories": {}, - "intentionally_unmapped_mj_enum_values": { - "__per_element__": { - "site.type": ["mjGEOM_PLANE", "mjGEOM_MESH"], - }, - }, - "element_rules": { - "geom": { - "xml_enum_attrs": { - "type": { - "ue_enum_type": "EMjGeomType", - "mjs_cast": "mjtGeom", - "value_map": { - "sphere": ["Sphere", "mjGEOM_SPHERE"], - "box": ["Box", "mjGEOM_BOX"], - "plane": ["Plane", "mjGEOM_PLANE"], - "mesh": ["Mesh", "mjGEOM_MESH"], - }, - }, - }, - }, - "site": { - "xml_enum_attrs": { - "type": { - "ue_enum_type": "EMjSiteType", - "mjs_cast": "mjtGeom", - "value_map": { - "sphere": ["Sphere", "mjGEOM_SPHERE"], - "box": ["Box", "mjGEOM_BOX"], - }, - }, - }, - }, - }, - } - mjspec = {"enums": {"mjtGeom": ["mjGEOM_SPHERE", "mjGEOM_BOX", - "mjGEOM_PLANE", "mjGEOM_MESH"]}} - gen._check_hand_enum_drift({}, rules, mjspec, str(tmp_path)) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - # Geom has all 4 mapped -> no diag. - assert not any("element 'geom'" in m for m in msgs) - # Site skips Plane + Mesh -> no diag for those either. - assert not any("mjGEOM_PLANE" in m and "site" in m for m in msgs) - assert not any("mjGEOM_MESH" in m and "site" in m for m in msgs) diff --git a/Scripts/codegen/tests/test_inject_helpers.py b/Scripts/codegen/tests/test_inject_helpers.py deleted file mode 100644 index adfc4513..00000000 --- a/Scripts/codegen/tests/test_inject_helpers.py +++ /dev/null @@ -1,97 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the codegen's inject-guard layer: ``_audit_banner_safety`` -on ``fully_emitted`` overwrites, layout dispatcher fall-through, -and canonicalisation dispatch on unknown canon names.""" - -from __future__ import annotations - -import generate_ue_components as gen -from generate_ue_components import _audit_banner_safety - - -# ---------- banner-safety audit --------------------------------------------- - -def test_audit_banner_safety_silent_on_template_only(tmp_path): - """A subclass file containing only its own header include and a - bare ``Type = ...`` constructor should not trigger a diagnostic.""" - p = tmp_path / "MjBoxGeom.cpp" - p.write_text( - '#include "MuJoCo/Components/Geometry/MjBoxGeom.h"\n' - "UMjBoxGeom::UMjBoxGeom()\n" - "{\n" - " Type = EMjGeomType::Box;\n" - "}\n", - encoding="utf-8", - ) - subtype = {"class_name": "UMjBoxGeom"} - _audit_banner_safety(str(p), subtype) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_audit_banner_safety_flags_extra_include(tmp_path): - """An extra non-template #include in a soon-to-be-overwritten file - triggers a diagnostic so the rule author knows hand-rolled content - will be discarded.""" - p = tmp_path / "MjBoxGeom.cpp" - p.write_text( - '#include "MuJoCo/Components/Geometry/MjBoxGeom.h"\n' - '#include "SomeOther/Header.h"\n' - "UMjBoxGeom::UMjBoxGeom()\n" - "{\n" - " Type = EMjGeomType::Box;\n" - "}\n", - encoding="utf-8", - ) - subtype = {"class_name": "UMjBoxGeom"} - _audit_banner_safety(str(p), subtype) - assert any("SomeOther/Header.h" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_audit_banner_safety_flags_extra_ctor_body(tmp_path): - """Constructor body content beyond ``Type = EMj...::X`` and the - rule-provided ``extra_constructor`` is flagged — that's where the - joint-subtype bOverride_Type bug came from.""" - p = tmp_path / "MjHingeJoint.cpp" - p.write_text( - '#include "MuJoCo/Components/Joints/MjHingeJoint.h"\n' - "UMjHingeJoint::UMjHingeJoint()\n" - "{\n" - " Type = EMjJointType::Hinge;\n" - " HandRolledHelperCall();\n" - "}\n", - encoding="utf-8", - ) - subtype = { - "class_name": "UMjHingeJoint", - "extra_constructor": "", - } - _audit_banner_safety(str(p), subtype) - assert any("non-template ctor content" in d.message - and "HandRolledHelperCall" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -# ---------- inject-guard diagnostics ---------------------------------------- - -def test_layout_dispatch_diagnoses_unknown_layout(): - """`_phase_categories` else-branch fires a diag for typo'd layouts. - Catches the silent fall-through where mistyped 'single_uclass' - instead of 'single_uclass_per_file' produces zero output.""" - writes = [] - rules = { - "categories": { - "bogus": {"layout": "single_uclass"}, - }, - } - ctx = gen.PhaseContext( - schema={}, rules=rules, mjxmacro={}, mjspec={}, - public_root="", private_root="", bind_h_path="", writes=writes, - ) - gen._phase_categories(ctx) - assert any("unknown layout" in d.message and "bogus" in d.message - and "single_uclass" in d.message - for d in gen._DIAGS_BUFFER.pending) - assert writes == [] - - -# Unknown-canon diagnostic is pinned in test_canon_registry.py. diff --git a/Scripts/codegen/tests/test_inject_helpers_brace.py b/Scripts/codegen/tests/test_inject_helpers_brace.py deleted file mode 100644 index e82fdf45..00000000 --- a/Scripts/codegen/tests/test_inject_helpers_brace.py +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the codegen injection helpers: brace-balance gate + -multi-tag inject + string/comment-aware brace stripping. - -The brace-balance check exists because a stray ``}`` lost inside a -long composed block silently relocates the compile error to a place -unrelated to the codegen site. We want the diagnostic to point at the -exact tag instead.""" - -from __future__ import annotations - -import os - -import generate_ue_components as gen -import _codegen_inject as _inject -gen._check_brace_balance = _inject._check_brace_balance - - -def _write(tmp_path, name: str, body: str) -> str: - p = tmp_path / name - p.write_text(body, encoding="utf-8") - return str(p) - - -# ---------- brace-balance -------------------------------------------------- - -def test_brace_balance_flags_unbalanced_block(): - body = "if (x) { do_thing(); " # missing close - gen._check_brace_balance(body, "FOO", "unit_test") - assert any("unbalanced braces" in d.message and "CODEGEN_FOO" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_brace_balance_quiet_on_balanced_block(): - body = "if (x) { do_thing(); }" - gen._check_brace_balance(body, "FOO", "unit_test") - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_brace_balance_ignores_braces_in_strings_and_comments(): - """``"{"`` inside a TEXT() literal must not count toward the open - tally; same for braces in // and /* */ comments.""" - body = ( - 'if (x)\n' - '{\n' - ' Log(TEXT("brace { in string")); // and { in line comment\n' - ' /* and { in block comment */\n' - '}\n' - ) - gen._check_brace_balance(body, "FOO", "unit_test") - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_inject_between_tags_with_balance_flag_fires_diag(): - """``balance_source`` kwarg routes through the brace-balance check.""" - file_text = "before\n// --- CODEGEN_FOO_START ---\nold\n// --- CODEGEN_FOO_END ---\nafter\n" - new_inner = "if (x) {" # unbalanced - _, ok = gen.inject_between_tags(file_text, "FOO", new_inner, - balance_source="unit_test") - assert ok is True - assert any("unbalanced braces" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_inject_between_tags_default_skips_balance_check(): - """Without ``balance_source``, the helper does not run the gate — - callers writing non-C++ blocks (markdown, JSON) aren't forced to - opt out.""" - file_text = "// --- CODEGEN_FOO_START ---\nold\n// --- CODEGEN_FOO_END ---\n" - gen.inject_between_tags(file_text, "FOO", "{ stray") - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -# ---------- _inject_tags_into_cpp ----------------------------------------- - -def test_inject_tags_into_cpp_handles_multiple_tags(tmp_path): - body = ( - "// --- CODEGEN_FOO_START ---\n" - "old foo\n" - "// --- CODEGEN_FOO_END ---\n" - "between\n" - "// --- CODEGEN_BAR_START ---\n" - "old bar\n" - "// --- CODEGEN_BAR_END ---\n" - ) - cpp_path = _write(tmp_path, "host.cpp", body) - writes: list = [] - gen._inject_tags_into_cpp( - cpp_path, - [("FOO", "new foo body"), ("BAR", "new bar body")], - writes, - diag_source="unit_test", - ) - assert len(writes) == 1 - out = writes[0].content - assert "new foo body" in out - assert "new bar body" in out - assert "old foo" not in out - assert "old bar" not in out - - -def test_inject_tags_into_cpp_diagnoses_missing_file(tmp_path): - missing = str(tmp_path / "nope.cpp") - writes: list = [] - gen._inject_tags_into_cpp( - missing, [("FOO", "x")], writes, diag_source="unit_test", - ) - assert writes == [] - assert any("does not exist" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_inject_tags_into_cpp_diagnoses_missing_marker(tmp_path): - cpp_path = _write(tmp_path, "host.cpp", - "// --- CODEGEN_FOO_START ---\nx\n// --- CODEGEN_FOO_END ---\n") - writes: list = [] - gen._inject_tags_into_cpp( - cpp_path, - [("FOO", "new foo"), ("MISSING_TAG", "new missing")], - writes, - diag_source="unit_test", - ) - # The FOO write still landed; MISSING_TAG fires a diagnostic. - assert len(writes) == 1 - assert any("CODEGEN_MISSING_TAG" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_inject_tags_into_cpp_skips_write_when_unchanged(tmp_path): - """Idempotency: re-injecting the same body must not append a - FileWrite. The ``--check`` gate relies on this. The END marker's - indent mirrors the START marker's, so a START at column 0 lands - END at column 0 too.""" - body = ( - "// --- CODEGEN_FOO_START ---\n" - "same body\n" - "// --- CODEGEN_FOO_END ---\n" - ) - cpp_path = _write(tmp_path, "host.cpp", body) - writes: list = [] - gen._inject_tags_into_cpp( - cpp_path, [("FOO", "same body")], writes, diag_source="unit_test", - ) - assert writes == [] - - -def test_inject_between_tags_normalises_end_indent_to_match_start(tmp_path): - """A hand-prepped source where START is at 8 spaces but END is - at 4 spaces (or any mismatch) gets its END normalised to match - START.""" - text = ( - " // --- CODEGEN_FOO_START ---\n" - " body content\n" - " // --- CODEGEN_FOO_END ---\n" - ) - new_text, ok = gen.inject_between_tags(text, "FOO", "new body") - assert ok is True - # Both marker lines now sit at the START's 8-space indent — assert - # the END's leading whitespace exactly so we don't accidentally - # match a substring of a deeper indent. - import re - match = re.search(r"^(\s*)// --- CODEGEN_FOO_END ---$", new_text, - re.MULTILINE) - assert match is not None - assert match.group(1) == " ", repr(match.group(1)) diff --git a/Scripts/codegen/tests/test_injection.py b/Scripts/codegen/tests/test_injection.py deleted file mode 100644 index 00bd278f..00000000 --- a/Scripts/codegen/tests/test_injection.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Boundary-tag injection tests.""" - -from __future__ import annotations - -from generate_ue_components import inject_between_tags - - -def test_replace_between_existing_tags(): - src = """class UFoo { -public: - // --- CODEGEN_PROPERTIES_START --- - int OldStuff; - // --- CODEGEN_PROPERTIES_END --- -}; -""" - out, found = inject_between_tags(src, "PROPERTIES", " int NewStuff;\n") - assert found is True - assert "OldStuff" not in out - assert "NewStuff" in out - # Tag lines preserved. - assert "// --- CODEGEN_PROPERTIES_START ---" in out - assert "// --- CODEGEN_PROPERTIES_END ---" in out - - -def test_missing_tags_returns_unchanged(): - src = "class UFoo { int X; };" - out, found = inject_between_tags(src, "PROPERTIES", "int Y;") - assert found is False - assert out == src - - -def test_idempotent_replace(): - src = """// --- CODEGEN_FOO_START --- -old body -// --- CODEGEN_FOO_END ---""" - out1, _ = inject_between_tags(src, "FOO", "new body\n") - out2, _ = inject_between_tags(out1, "FOO", "new body\n") - # second injection of the same body produces byte-identical output. - assert out1 == out2 - - -def test_preserves_text_outside_tags(): - prefix = "// custom comment that must survive\n" - suffix = "\n// trailing custom code\nvoid Helper() {}\n" - src = prefix + "// --- CODEGEN_X_START ---\nold\n// --- CODEGEN_X_END ---" + suffix - out, _ = inject_between_tags(src, "X", "new\n") - assert out.startswith(prefix) - assert out.endswith(suffix) - - -def test_dotall_does_not_swallow_subsequent_tags(): - # Two separate codegen blocks must be replaced independently. - src = """// --- CODEGEN_A_START --- -A old -// --- CODEGEN_A_END --- -some middle code -// --- CODEGEN_B_START --- -B old -// --- CODEGEN_B_END ---""" - out, _ = inject_between_tags(src, "A", "A new\n") - out, _ = inject_between_tags(out, "B", "B new\n") - assert "A new" in out and "B new" in out - assert "A old" not in out and "B old" not in out - # Middle code is still there. - assert "some middle code" in out diff --git a/Scripts/codegen/tests/test_introspect_snapshot.py b/Scripts/codegen/tests/test_introspect_snapshot.py deleted file mode 100644 index 54d7f0e3..00000000 --- a/Scripts/codegen/tests/test_introspect_snapshot.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Smoke tests for the clang-AST introspect snapshot. - -Skipped automatically when libclang isn't available (e.g. CI runners -without the package). The real validation happens on the committed -``Scripts/introspect_snapshot.json`` itself — these tests just spot-check -that schema-evolution surprises don't slip through. -""" - -from __future__ import annotations - -import json -import os - -import pytest - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_PLUGIN_ROOT = os.path.normpath(os.path.join(_HERE, "..", "..", "..")) -_SNAPSHOT = os.path.join(_PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "introspect_snapshot.json") - - -def _load() -> dict: - if not os.path.exists(_SNAPSHOT): - pytest.skip("introspect_snapshot.json not built yet; run " - "build_introspect_snapshot.py") - with open(_SNAPSHOT, "r", encoding="utf-8") as f: - return json.load(f) - - -def test_snapshot_meta_and_top_level_keys_present(): - """One combined smoke test: top-level sections are present, each is - non-empty, and the ``_meta`` block carries a stable header hash. - Substring presence of specific MuJoCo names (mjs_addBody, mjtJoint, - etc.) is covered by ``test_enum_scrape::test_real_snapshot_has_ - expected_enums`` plus the regen --check gate.""" - snap = _load() - for section in ("functions", "enums", "structs", "defines"): - assert section in snap, section - assert len(snap[section]) > 0, section - assert "_meta" in snap - assert snap["_meta"]["snapshot_version"] >= 1 - assert len(snap["_meta"]["header_hash"]) == 64 diff --git a/Scripts/codegen/tests/test_kept_props_for_category.py b/Scripts/codegen/tests/test_kept_props_for_category.py deleted file mode 100644 index 87232c05..00000000 --- a/Scripts/codegen/tests/test_kept_props_for_category.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks ``kept_props_for_category`` — the ordered prop-list helper -extracted from emit_schema_for_tests. Used by the -schema-completeness header that drives URLab.Codegen.* automation -tests, so the order + composition must stay deterministic.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_emits_canon_property_first_then_per_attr_in_schema_order(): - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "canonicalizations": { - "spatial_pose": { - "absorbs_attrs": ["pos"], - "emits_property": {"name": "Pos", "type": "FVector"}, - "import_helper": "X", - "export_helper": "Y", - }, - }, - "element_rules": { - "joint": { - "applies_canonicalizations": ["spatial_pose"], - }, - }, - } - schema = {"joint": {"attrs": ["pos", "type", "axis", "limited"]}} - cat_rules = {"schema_common_block": "joint.attrs"} - out = gen.kept_props_for_category("joint", cat_rules, schema, rules) - # Pos comes first (canon-owned), then per-attr in schema order - # minus the canon-absorbed ``pos``. - assert out == ["Pos", "type", "axis", "limited"] - - -def test_disable_schema_emission_returns_empty(): - rules = {"global_exclusions": [], "canonicalizations": {}, - "element_rules": {}} - cat_rules = {"disable_schema_emission": True, - "schema_common_block": "joint.attrs"} - out = gen.kept_props_for_category("joint", cat_rules, {}, rules) - assert out == [] - - -def test_xml_enum_attr_lowers_to_ue_property(): - rules = { - "global_exclusions": [], - "property_renames": {}, - "canonicalizations": {}, - "element_rules": { - "camera": { - "xml_enum_attrs": { - "mode": { - "ue_property": "TrackingMode", - "ue_enum_type": "EMjCameraTrackingMode", - "value_map": {"fixed": ["Fixed", "mjCAMLIGHT_FIXED"]}, - }, - }, - }, - }, - } - schema = {"camera": {"attrs": ["mode", "fovy"]}} - cat_rules = {"schema_common_block": "camera.attrs"} - out = gen.kept_props_for_category("camera", cat_rules, schema, rules) - # ``mode`` -> ``TrackingMode``; ``fovy`` passes through. - assert out == ["TrackingMode", "fovy"] - - -def test_schema_common_extra_attrs_append_after_block(): - rules = {"global_exclusions": [], "property_renames": {}, - "canonicalizations": {}, "element_rules": {}} - schema = {"frame": {"attrs": []}} - cat_rules = { - "schema_common_block": "frame.attrs", - "schema_common_extra_attrs": ["pos", "quat", "childclass"], - } - out = gen.kept_props_for_category("frame", cat_rules, schema, rules) - assert out == ["pos", "quat", "childclass"] - - -def test_global_and_element_exclusions_drop_attrs(): - rules = { - "global_exclusions": ["type"], - "property_renames": {}, - "canonicalizations": {}, - "element_rules": { - "joint": {"exclude_attrs": ["limited"]}, - }, - } - schema = {"joint": {"attrs": ["type", "axis", "limited", "stiffness"]}} - cat_rules = {"schema_common_block": "joint.attrs"} - out = gen.kept_props_for_category("joint", cat_rules, schema, rules) - assert "type" not in out - assert "limited" not in out - assert out == ["axis", "stiffness"] - - -def test_property_rename_lowers_to_renamed_prop(): - rules = { - "global_exclusions": [], - "property_renames": {"qpos": "Qpos"}, - "canonicalizations": {}, - "element_rules": {}, - } - schema = {"keyframe": {"attrs": ["qpos", "time"]}} - cat_rules = {"schema_common_block": "keyframe.attrs"} - out = gen.kept_props_for_category("keyframe", cat_rules, schema, rules) - assert out == ["Qpos", "time"] diff --git a/Scripts/codegen/tests/test_libclang_hand_enum_scrape.py b/Scripts/codegen/tests/test_libclang_hand_enum_scrape.py deleted file mode 100644 index 15f929ae..00000000 --- a/Scripts/codegen/tests/test_libclang_hand_enum_scrape.py +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the libclang-backed EMj* hand-enum scrape in -``build_introspect_snapshot.py`` and the snapshot consumer -``_hand_enums_from_snapshot``. - -The scrape: - - extracts every ``enum class EMj* : uint8 { ... }`` block under - ``Source/URLab/Public``; - - strips UE-only macros (UMETA / UENUM) so a minimal libclang TU - can parse the block — including nested parens like - ``UMETA(DisplayName="Track (Centre of Mass)")``; - - lands the result under ``snapshot.hand_enums`` in - ``introspect_snapshot.json``. - -The hand-enum drift check prefers this over the fallback regex -scrape — the snapshot is byte-stable across machines because the -macro stripping is deterministic.""" - -from __future__ import annotations - -import json -import os - -import pytest -import generate_ue_components as gen -import _codegen_checks as _checks -gen._hand_enums_from_snapshot = _checks._hand_enums_from_snapshot - -# build_introspect_snapshot lives alongside generate_ue_components but is -# not always import-clean (it would try to load clang.cindex at import -# time). Test the strip helper via a tolerant import path. -import importlib.util -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -_BUILD_SNAP = os.path.join(_CODEGEN_DIR, "build_introspect_snapshot.py") - - -def _load_build_snapshot_module(): - spec = importlib.util.spec_from_file_location( - "_build_introspect_snapshot_for_test", _BUILD_SNAP, - ) - mod = importlib.util.module_from_spec(spec) - sys.modules[spec.name] = mod - spec.loader.exec_module(mod) - return mod - - -@pytest.fixture(scope="module") -def build_snap(): - return _load_build_snapshot_module() - - -# ---------- macro stripper ------------------------------------------------- - -def test_strip_handles_nested_parens_in_umeta(build_snap): - """``UMETA(DisplayName="Track (Centre of Mass)")`` — the inner - ``(Centre`` opens a paren the regex must not bail on. The previous - naive ``UMETA\\([^)]*\\)`` regex stopped at the first ``)``, leaving - a stray ``)`` that broke libclang's enum-member walk for any member - after the first nested-paren UMETA.""" - src = ( - "enum class EMjSample : uint8 {\n" - " Fixed UMETA(DisplayName = \"Fixed\"),\n" - " TrackCom UMETA(DisplayName = \"Track (Centre of Mass)\"),\n" - " TargetBody UMETA(DisplayName = \"Target Body\"),\n" - " TargetBodyCom UMETA(DisplayName = \"Target Body (Centre of Mass)\"),\n" - "};\n" - ) - out = build_snap._strip_ue_macros_from_enum_block(src) - assert "UMETA" not in out - # No stray close-parens left behind after the strip. - assert ")" not in out - # All four members survive. - for m in ("Fixed", "TrackCom", "TargetBody", "TargetBodyCom"): - assert m in out, f"member {m} lost during strip; out={out!r}" - - -def test_strip_handles_uenum_header(build_snap): - src = ( - "UENUM(BlueprintType)\n" - "enum class EMjFoo : uint8 { A, B };\n" - ) - out = build_snap._strip_ue_macros_from_enum_block(src) - assert "UENUM" not in out - assert "enum class EMjFoo : uint8 { A, B };" in out - - -def test_strip_handles_line_and_block_comments(build_snap): - src = ( - "enum class EMjFoo : uint8 {\n" - " A, // first\n" - " B, /* second */\n" - " /* a multi\n" - " line */ C,\n" - "};\n" - ) - out = build_snap._strip_ue_macros_from_enum_block(src) - assert "first" not in out - assert "second" not in out - assert "multi" not in out - for m in ("A", "B", "C"): - assert m in out - - -def test_strip_balanced_call_terminates_on_unbalanced_input(build_snap): - """An open-paren without a matching close should consume to EOF - rather than infinite-loop the macro stripper.""" - src = "UMETA(unbalanced" - out = build_snap._strip_ue_macros_from_enum_block(src) - # Strip drove the cursor to EOF -> empty residue. - assert out == "" - - -# ---------- snapshot output ------------------------------------------------ - -def test_snapshot_contains_at_least_one_hand_enum(): - """If the shipped snapshot has hand_enums, we trust the build step - ran libclang against URLab's Public/ tree.""" - snap_path = os.path.join( - os.path.dirname(_CODEGEN_DIR), - "codegen", "snapshots", "introspect_snapshot.json", - ) - if not os.path.exists(snap_path): - pytest.skip("introspect_snapshot.json not present") - with open(snap_path, "r", encoding="utf-8") as f: - snap = json.load(f) - hand_enums = snap.get("hand_enums", {}) - if not hand_enums: - pytest.skip("hand_enums empty (libclang scrape disabled?)") - # At least one EMj* enum and every entry has members. - emj = [k for k in hand_enums if k.startswith("EMj")] - assert emj, f"hand_enums missing EMj* entries; keys={list(hand_enums)[:5]}" - for name, entry in hand_enums.items(): - assert "members" in entry, name - assert entry["members"], f"{name} has no members" - - -# ---------- runtime consumer ---------------------------------------------- - -def test_hand_enums_from_snapshot_returns_flat_member_list(): - """The runtime drift check expects ``{enum_name: [member,...]}`` — - the same shape the regex scrape produced. Confirm the converter - flattens the introspect dict shape correctly.""" - mjspec = { - "introspect": { - "hand_enums": { - "EMjSample": { - "members": {"A": 0, "B": 1, "C": 2}, - }, - }, - }, - } - out = gen._hand_enums_from_snapshot(mjspec) - assert out == {"EMjSample": ["A", "B", "C"]} - - -def test_hand_enums_from_snapshot_empty_when_missing(): - """No introspect / no hand_enums section -> empty dict (the drift - check then falls back to the regex scrape).""" - assert gen._hand_enums_from_snapshot({}) == {} - assert gen._hand_enums_from_snapshot({"introspect": {}}) == {} - assert gen._hand_enums_from_snapshot(None) == {} diff --git a/Scripts/codegen/tests/test_mjxmacro_snapshot.py b/Scripts/codegen/tests/test_mjxmacro_snapshot.py deleted file mode 100644 index 1ef35291..00000000 --- a/Scripts/codegen/tests/test_mjxmacro_snapshot.py +++ /dev/null @@ -1,166 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the mjxmacro snapshot parser. - -The XNV-accepting regex was added after a silent-drop bug — every entry -listed AFTER an XNV line in the same block (e.g. ``geom_user``, ``geom_rgba`` -after ``XNV(mjtNum, geom_fluid, ...)`` in MJMODEL_POINTERS_GEOM) was -discarded because the body-collector treated XNV as a non-entry sentinel -and stopped the loop. These tests pin the regex behaviour so adding a -new tag is a deliberate change with a test failure to acknowledge. -""" - -from __future__ import annotations - -import textwrap - -from build_mjxmacro_snapshot import ( - _BODY_ENTRY_RE, - _X_ENTRY_RE, - _X_TAG_RE, - _parse_block_body, - _parse_pointer_entry, -) - - -# --------------------------------------------------------------------------- -# Regex behaviour -# --------------------------------------------------------------------------- - -def test_x_tag_re_accepts_known_tags(): - """X, XVEC, XNV are the three macro tags MuJoCo uses today.""" - import re - rx = re.compile(rf"^{_X_TAG_RE}$") - assert rx.match("X") - assert rx.match("XVEC") - assert rx.match("XNV") - # Future tags should fail until added (the failure is the canary). - assert not rx.match("XSEG") - assert not rx.match("XMODEL") - - -def test_body_entry_re_matches_all_three_tags(): - assert _BODY_ENTRY_RE.match(" X ( int, foo, nfoo, 1 )") - assert _BODY_ENTRY_RE.match(" XVEC( mjtNum, bar, nbar, 3 )") - assert _BODY_ENTRY_RE.match(" XNV ( mjtNum, baz, nbaz, mjNFLUID )") - # Tags that look similar but aren't in the set. - assert not _BODY_ENTRY_RE.match(" X3 ( ... )") - - -def test_x_entry_re_captures_payload_for_all_tags(): - m = _X_ENTRY_RE.match(" XNV ( mjtNum, geom_fluid, ngeom, mjNFLUID ) \\") - assert m - assert "geom_fluid" in m.group(1) - m2 = _X_ENTRY_RE.match(" X ( int, geom_type, ngeom, 1 ) \\") - assert m2 - assert "geom_type" in m2.group(1) - - -# --------------------------------------------------------------------------- -# End-to-end block parsing — the bug that motivated this file -# --------------------------------------------------------------------------- - -def _block_lines(block_body: str) -> list: - """Strip the leading newline + dedent so test bodies read naturally.""" - return textwrap.dedent(block_body).strip("\n").split("\n") - - -def test_block_body_does_not_stop_at_xnv(): - """The original regex matched (X|XVEC) only; XNV broke the body collector - so every subsequent entry in the block was silently dropped. This pins - the behaviour so MJMODEL_POINTERS_GEOM can never lose geom_user / - geom_rgba again.""" - lines = _block_lines(""" - X ( int, geom_type, ngeom, 1 ) - XNV ( mjtNum, geom_fluid, ngeom, mjNFLUID ) - X ( mjtNum, geom_user, ngeom, MJ_M(nuser_geom) ) - X ( float, geom_rgba, ngeom, 4 ) - """) - entries = _parse_block_body(lines, 0) - assert len(entries) == 4, "XNV must not abort the body collector" - parsed = [_parse_pointer_entry(e) for e in entries] - names = [p["name"] for p in parsed if p] - assert names == ["geom_type", "geom_fluid", "geom_user", "geom_rgba"] - - -def test_parse_pointer_entry_for_xnv(): - """XNV entries parse as 4-tuples identical to X entries.""" - entry = _parse_pointer_entry("XNV ( mjtNum, geom_fluid, ngeom, mjNFLUID )") - assert entry == { - "type": "mjtNum", - "name": "geom_fluid", - "outer_dim": "ngeom", - "stride": "mjNFLUID", - } - - -# --------------------------------------------------------------------------- -# Two-source fold-in: mjxmacro.h + mjspecmacro.h => one snapshot -# --------------------------------------------------------------------------- - -def test_shipped_snapshot_has_struct_field_sources(real_mjxmacro): - """The snapshot writer should record which header each struct-field - block came from so consumers can tell mjmodel-side from mjspec-side.""" - meta = real_mjxmacro.get("_meta", {}) - sources = meta.get("struct_field_sources") - assert isinstance(sources, dict) and sources, ( - "expected _meta.struct_field_sources to be populated" - ) - - -def test_shipped_snapshot_attributes_mjspec_blocks_to_mjspecmacro(real_mjxmacro): - """MJSCOMPILER_FIELDS + MJSPEC_FIELDS must come from mjspecmacro.h, - not mjxmacro.h — they only exist in the spec header.""" - sources = real_mjxmacro["_meta"]["struct_field_sources"] - for block in ("MJSCOMPILER_FIELDS", "MJSPEC_FIELDS"): - assert block in sources, f"snapshot missing {block} source attribution" - assert "mjspecmacro.h" in sources[block], ( - f"{block} should be sourced from mjspecmacro.h, got {sources[block]!r}" - ) - - -def test_shipped_snapshot_attributes_mjoption_blocks_to_mjxmacro(real_mjxmacro): - """MJOPTION_FIELDS / MJSTATISTIC_FIELDS live in mjxmacro.h.""" - sources = real_mjxmacro["_meta"]["struct_field_sources"] - for block in ("MJOPTION_FIELDS", "MJSTATISTIC_FIELDS"): - assert block in sources, f"snapshot missing {block} source attribution" - assert "mjxmacro.h" in sources[block], ( - f"{block} should be sourced from mjxmacro.h, got {sources[block]!r}" - ) - - -# --------------------------------------------------------------------------- -# Defensive block-coverage tripwire -# --------------------------------------------------------------------------- - -def test_shipped_snapshot_includes_arena_pointers_contact(real_mjxmacro): - """MJDATA_ARENA_POINTERS_CONTACT must round-trip. It was previously - dropped silently because POINTER_BLOCKS_MJDATA omitted it; the - unrecognised-block warning in build_mjxmacro_snapshot.py catches - similar gaps at parse time, and this test pins the result.""" - blocks = real_mjxmacro["mjdata_pointers"] - assert "MJDATA_ARENA_POINTERS_CONTACT" in blocks, ( - "MJDATA_ARENA_POINTERS_CONTACT was previously silently dropped — " - "if this fails again, check POINTER_BLOCKS_MJDATA in " - "build_mjxmacro_snapshot.py." - ) - assert blocks["MJDATA_ARENA_POINTERS_CONTACT"], ( - "block recognised but body collection produced 0 entries" - ) - - -def test_parse_mjxmacro_warns_on_unrecognised_block(tmp_path, capsys): - """An MJ-prefixed block not in any allow-list AND not in - KNOWN_UNCONSUMED_BLOCKS must print a stderr warning, NOT silently - appear in the snapshot output.""" - from build_mjxmacro_snapshot import parse_mjxmacro - fake = tmp_path / "fake_mjxmacro.h" - fake.write_text( - "#define MJMODEL_POINTERS_FROBNOZ \\\n" - " X ( int, frob_a, nfrob, 1 ) \\\n" - " X ( int, frob_b, nfrob, 1 )\n", - encoding="utf-8", - ) - parse_mjxmacro(str(fake)) - captured = capsys.readouterr() - assert "MJMODEL_POINTERS_FROBNOZ" in captured.err - assert "unrecognised" in captured.err.lower() diff --git a/Scripts/codegen/tests/test_new_rule_shapes.py b/Scripts/codegen/tests/test_new_rule_shapes.py deleted file mode 100644 index f46cb228..00000000 --- a/Scripts/codegen/tests/test_new_rule_shapes.py +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Residual unit tests for rule shapes that don't have a dedicated -file: ``property_default`` (with value_map cross-check), -``_guarded_export(multiline=...)`` emission shapes, and the full-coverage -drift checks (value_map stale, orphan type_mappings, compiler attrs, -mjxmacro block coverage, mjs_array_caps_for). - -Larger concerns moved to dedicated files: see ``test_objtype_dispatch``, -``test_geom_final_type``, ``test_inject_helpers``, ``test_hand_enum_drift``, -``test_type_shape_drift``, ``test_property_units``. -""" - -from __future__ import annotations - -import generate_ue_components as gen -from generate_ue_components import _guarded_export - - -# ---------- property_default + cross-check --------------------------------- - -def test_property_default_emitted_on_base(): - """`property_default` ends up as the UPROPERTY initializer when - ``emit_property_decl: true`` is set on an xml_enum_attr.""" - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {}, - "canonicalizations": {}, - "element_rules": { - "actuator": { - "xml_enum_attrs": { - "gaintype": { - "ue_property": "GainType", - "ue_enum": "EMjGainType", - "ue_enum_type": "EMjGainType", - "override_toggle": "bOverride_GainType", - "default_lookup": "Default->GainType", - "emit_property_decl": True, - "property_default": "Fixed", - "value_map": { - "fixed": ["Fixed", "mjGAIN_FIXED"], - "affine": ["Affine", "mjGAIN_AFFINE"], - }, - }, - }, - }, - }, - } - out = gen.emit_schema_for_attrs( - ["gaintype"], rules, "actuator", "Actuator", - apply_canonicalizations=True, - ) - assert "EMjGainType GainType = EMjGainType::Fixed;" in out.properties_h - - -def test_property_default_cross_check_diagnoses_bad_member(): - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {}, - "canonicalizations": {}, - "element_rules": { - "actuator": { - "xml_enum_attrs": { - "gaintype": { - "ue_property": "GainType", - "ue_enum": "EMjGainType", - "ue_enum_type": "EMjGainType", - "override_toggle": "bOverride_GainType", - "default_lookup": "Default->GainType", - "emit_property_decl": True, - # NotAMember does not appear in value_map below. - "property_default": "NotAMember", - "value_map": { - "fixed": ["Fixed", "mjGAIN_FIXED"], - "affine": ["Affine", "mjGAIN_AFFINE"], - }, - }, - }, - }, - }, - } - gen.emit_schema_for_attrs( - ["gaintype"], rules, "actuator", "Actuator", - apply_canonicalizations=True, - ) - assert any("NotAMember" in d.message for d in gen._DIAGS_BUFFER.pending) - - -# ---------- _guarded_export(multiline=True) -------------------------------- - -def test_guarded_export_singleline_form(): - """Single-line form: ``if (toggle) `` on one line.""" - out = _guarded_export("bOverride_x", "Element->x = X;") - assert out == " if (bOverride_x) Element->x = X;\n" - - -def test_guarded_export_multiline_brace_layout(): - """Multiline form: brace on its own line; body is emitted verbatim - between braces (caller owns inner indentation).""" - body = " line_a;\n line_b;\n" - out = _guarded_export("bOverride_x", body, multiline=True) - expected = ( - " if (bOverride_x)\n" - " {\n" - " line_a;\n" - " line_b;\n" - " }\n" - ) - assert out == expected - - -def test_guarded_export_multiline_with_extra_cond(): - """``extra_cond`` (default order) appears after the toggle.""" - body = " do_thing();\n" - out = _guarded_export( - "bOverride_x", body, - extra_cond="ready", - multiline=True, - ) - assert out.startswith(" if (bOverride_x && ready)\n {\n") - assert " do_thing();\n" in out - assert out.endswith(" }\n") - - -def test_guarded_export_extra_cond_first_order(): - """``extra_cond_first=True`` flips the order and parenthesises - the extra cond.""" - out = _guarded_export( - "bOverride_x", "Element->x = X;", - extra_cond="ready", - extra_cond_first=True, - ) - assert out == " if ((ready) && bOverride_x) Element->x = X;\n" - - -# ---------- full-coverage drift checks ------------------------------------- - -def test_value_map_stale_mj_const_flagged(): - """A value_map entry referencing a constant NOT in any mjspec enum - is stale — surface it before the C++ compile fails.""" - rules = { - "element_rules": { - "joint": { - "xml_enum_attrs": { - "type": { - "value_map": { - "hinge": ["Hinge", "mjJNT_HINGE"], - "screwd": ["Screwd", "mjJNT_NONEXISTENT"], - }, - }, - }, - }, - }, - } - mjspec = {"enums": {"mjtJoint": {"mjJNT_HINGE": 0, "mjJNT_SLIDE": 1}}} - gen._check_value_map_stale_mj_consts(rules, mjspec) - assert any("mjJNT_NONEXISTENT" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_orphan_type_mappings_flagged(): - schema = {"joint": {"attrs": ["stiffness"]}} - rules = { - "type_mappings": { - "stiffness": "TArray", - "vanishedattr": "TArray", - }, - "categories": {"joint": {"schema_common_block": "joint.attrs"}}, - "element_rules": {}, - } - gen._check_orphan_rule_entries(schema, rules) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("'vanishedattr'" in m for m in msgs) - assert not any("'stiffness'" in m for m in msgs) - - -def test_compiler_attrs_coverage_flags_unhandled(): - schema = {"compiler": {"attrs": ["angle", "newflag2030", "eulerseq"]}} - rules = { - "compiler_attr_field_map": {"angle": "bAngleInDegrees", "eulerseq": "EulerSeq"}, - "intentionally_unmodeled_compiler_attrs": [], - } - gen._check_compiler_attrs_coverage(schema, rules) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("newflag2030" in m for m in msgs), msgs - assert not any("'angle'" in m for m in msgs) - - -def test_compiler_attrs_coverage_respects_intentional_skip(): - schema = {"compiler": {"attrs": ["angle", "balanceinertia"]}} - rules = { - "compiler_attr_field_map": {"angle": "bAngleInDegrees"}, - "intentionally_unmodeled_compiler_attrs": ["balanceinertia"], - } - gen._check_compiler_attrs_coverage(schema, rules) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_mjxmacro_block_coverage_forward(): - rules = { - "synthetic_categories": { - "MjOption": {"mjxmacro_block": "MJOPTION_FIELDS"}, - }, - } - mjxmacro = { - "struct_fields": { - "MJOPTION_FIELDS": [{"name": "timestep"}], - "MJNEW_FIELDS": [{"name": "wat"}], # no rule entry - }, - } - gen._check_mjxmacro_block_coverage(rules, mjxmacro) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("MJNEW_FIELDS" in m for m in msgs) - assert not any("MJOPTION_FIELDS" in m for m in msgs) - - -def test_mjxmacro_block_coverage_reverse(): - rules = { - "synthetic_categories": { - "MjGhost": {"mjxmacro_block": "MJGHOST_FIELDS"}, - }, - } - mjxmacro = {"struct_fields": {}} - gen._check_mjxmacro_block_coverage(rules, mjxmacro) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("MJGHOST_FIELDS" in m and "MjGhost" in m for m in msgs) - - -def test_mjs_array_caps_for_extracts_fixed_size_arrays(): - cat_rules = {"mjs_struct": "mjsActuator"} - mjspec = { - "introspect": { - "structs": { - "mjsActuator": { - "fields": [ - {"name": "gainprm", "c_type": "double [10]", "array_dim": 10}, - {"name": "scalar", "c_type": "double", "array_dim": None}, - {"name": "noslot", "c_type": "double [1]", "array_dim": 1}, - ], - }, - }, - }, - } - caps = gen._mjs_array_caps_for(cat_rules, mjspec) - assert caps == {"gainprm": 10} diff --git a/Scripts/codegen/tests/test_objtype_dispatch.py b/Scripts/codegen/tests/test_objtype_dispatch.py deleted file mode 100644 index b633c8f2..00000000 --- a/Scripts/codegen/tests/test_objtype_dispatch.py +++ /dev/null @@ -1,90 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the ``objtype_dispatch`` rule shape — emits a switch on a -UE enum that assigns each case's ``expr`` to ``target``.""" - -from __future__ import annotations - -import generate_ue_components as gen -from generate_ue_components import _emit_objtype_dispatch_block - - -def test_objtype_dispatch_emits_switch_with_default(): - cat_rules = { - "type_enum_name": "EMjEqualityType", - "objtype_dispatch": { - "discriminator": "Type", - "target": "Element->objtype", - "default": "mjOBJ_UNKNOWN", - "cases": [ - {"keys": ["Connect", "Weld"], "expr": "mjOBJ_BODY"}, - {"keys": ["Joint"], "expr": "mjOBJ_JOINT"}, - ], - }, - } - out = _emit_objtype_dispatch_block(cat_rules) - assert "switch (Type)" in out - assert "case EMjEqualityType::Connect:" in out - assert "case EMjEqualityType::Weld:" in out - assert "Element->objtype = mjOBJ_BODY;" in out - assert "case EMjEqualityType::Joint:" in out - assert "Element->objtype = mjOBJ_JOINT;" in out - assert "default:" in out - assert "Element->objtype = mjOBJ_UNKNOWN;" in out - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_objtype_dispatch_returns_none_when_no_rule(): - assert _emit_objtype_dispatch_block({}) is None - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_objtype_dispatch_diagnoses_missing_type_enum_name(): - out = _emit_objtype_dispatch_block({ - "objtype_dispatch": { - "discriminator": "Type", - "target": "Element->objtype", - "cases": [{"keys": ["A"], "expr": "X"}], - }, - }) - assert out is None - assert any("type_enum_name" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_objtype_dispatch_diagnoses_empty_cases(): - out = _emit_objtype_dispatch_block({ - "type_enum_name": "EMjEqualityType", - "objtype_dispatch": { - "discriminator": "Type", - "target": "Element->objtype", - "cases": [], - }, - }) - assert out is None - assert any("no cases" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_objtype_dispatch_diagnoses_empty_keys(): - out = _emit_objtype_dispatch_block({ - "type_enum_name": "EMjEqualityType", - "objtype_dispatch": { - "discriminator": "Type", - "target": "Element->objtype", - "cases": [{"keys": [], "expr": "mjOBJ_BODY"}], - }, - }) - assert out is None - assert any("empty" in d.message and "keys" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_objtype_dispatch_diagnoses_missing_expr(): - out = _emit_objtype_dispatch_block({ - "type_enum_name": "EMjEqualityType", - "objtype_dispatch": { - "discriminator": "Type", - "target": "Element->objtype", - "cases": [{"keys": ["Connect"]}], - }, - }) - assert out is None - assert any("missing 'expr'" in d.message for d in gen._DIAGS_BUFFER.pending) diff --git a/Scripts/codegen/tests/test_phase_2_6_checks.py b/Scripts/codegen/tests/test_phase_2_6_checks.py deleted file mode 100644 index a9df2f8d..00000000 --- a/Scripts/codegen/tests/test_phase_2_6_checks.py +++ /dev/null @@ -1,221 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the three new drift checks: -``_check_apply_mode_validity``, ``_check_embedded_cpp_references``, -and ``_check_allowlist_staleness`` (narrowed to 4 of 6 allowlists per -the rule design).""" - -from __future__ import annotations - -import generate_ue_components as gen -import _codegen_checks as _checks -gen._collect_every_schema_attr = _checks._collect_every_schema_attr - - -# ---------- _check_apply_mode_validity ------------------------------------- - -def test_apply_mode_unknown_value_fires_diag(): - """A typo'd mode (``"guarderd"`` instead of ``"guarded"``) would - silently fall through to the unconditional branch — the check - surfaces it before the codegen emits a bad write path.""" - rules = { - "synthetic_categories": { - "MjOption": { - "field_apply_to_spec_mode": {"timestep": "guarderd"}, - }, - }, - } - gen._check_apply_mode_validity(rules) - assert any("guarderd" in d.message and "timestep" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_apply_mode_known_values_silent(): - rules = { - "synthetic_categories": { - "MjOption": { - "field_apply_to_spec_mode": { - "timestep": "unconditional", - "gravity": "guarded_pos", - "wind": "vec3_y_flip", - }, - }, - }, - } - gen._check_apply_mode_validity(rules) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_apply_mode_skips_note_keys(): - """The phase iterator must ignore ``_note_*`` sibling keys; they - appear as strings, not dicts.""" - rules = { - "synthetic_categories": { - "_note_intent": "doc string for the section", - "MjOption": { - "field_apply_to_spec_mode": {"timestep": "unconditional"}, - }, - }, - } - gen._check_apply_mode_validity(rules) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -# ---------- _check_embedded_cpp_references --------------------------------- - -def test_embedded_cpp_refs_flags_stale_mj_const(): - """``value_map[...][1]`` referencing a constant not in any mjspec - enum is a UHT-bound failure — the check moves it forward into - codegen-time.""" - rules = { - "element_rules": { - "joint": { - "xml_enum_attrs": { - "type": { - "value_map": { - "hinge": ["Hinge", "mjJNT_HINGE"], - "screw": ["Screw", "mjJNT_FXED"], # typo - }, - }, - }, - }, - }, - } - mjspec = {"enums": {"mjtJoint": {"mjJNT_HINGE": 0, "mjJNT_SLIDE": 1}}} - gen._check_embedded_cpp_references(rules, mjspec) - assert any("mjJNT_FXED" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_embedded_cpp_refs_silent_on_real_const(): - rules = { - "element_rules": { - "joint": { - "xml_enum_attrs": { - "type": { - "value_map": { - "hinge": ["Hinge", "mjJNT_HINGE"], - }, - }, - }, - }, - }, - } - mjspec = {"enums": {"mjtJoint": {"mjJNT_HINGE": 0}}} - gen._check_embedded_cpp_references(rules, mjspec) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_embedded_cpp_refs_flags_bad_geom_final_type_fallback(): - rules = { - "categories": { - "geom": { - "geom_final_type": { - "xml_enum_ref": "type", - "override_field": "bOverride_Type", - "default_lookup": "Default->Type", - "default_fallback": "mjGEOM_FXED", # typo - "name_field": "MeshName", - "name_export_for": "mjGEOM_MESH", - "name_target": "Element->mesh", - "name_setter": "MjSetString", - }, - }, - }, - } - mjspec = {"enums": {"mjtGeom": {"mjGEOM_SPHERE": 0, "mjGEOM_MESH": 7}}} - gen._check_embedded_cpp_references(rules, mjspec) - assert any("mjGEOM_FXED" in d.message for d in gen._DIAGS_BUFFER.pending) - - -def test_embedded_cpp_refs_quiet_without_snapshot(): - """No mjspec / no enums -> can't check; the helper no-ops without - crashing.""" - gen._check_embedded_cpp_references({}, None) - assert len(gen._DIAGS_BUFFER.pending) == 0 - gen._check_embedded_cpp_references({}, {"enums": {}}) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -# ---------- _check_allowlist_staleness ------------------------------------- - -def test_unmodeled_element_must_exist_in_schema(): - rules = { - "intentionally_unmodeled_elements": { - "skin": "URLab uses UE skeletal meshes instead", - "phantom_element": "no longer exists", # stale - }, - } - schema = {"body": {"attrs": ["pos"]}, "skin": {"attrs": []}} - gen._check_allowlist_staleness(schema, rules, None) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("phantom_element" in m and "no longer carries" in m for m in msgs) - assert not any("'skin'" in m for m in msgs) - - -def test_unmodeled_mjs_field_must_exist_on_struct(): - rules = { - "intentionally_unmodeled_mjs_fields": { - "mjsBody": ["explicitinertial", "phantom_field"], - "mjsGhost": ["any"], # stale struct - }, - } - mjspec = { - "structs": {"mjsBody": ["explicitinertial", "name", "parent"]}, - } - gen._check_allowlist_staleness({}, rules, mjspec) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("phantom_field" in m and "mjsBody" in m for m in msgs) - assert any("mjsGhost" in m for m in msgs) - assert not any("'explicitinertial'" in m and "mjsBody" in m for m in msgs) - - -def test_default_typed_attr_must_exist_somewhere_in_schema(): - rules = { - "intentionally_default_typed_attrs": [ - "kp", # in actuator_types.position list - "torquescale", # nested under equality.weld - "phantom_attr", # stale - ], - } - schema = { - "actuator_types": {"position": ["kp", "kv"]}, - "equality": {"weld": ["torquescale", "anchor"]}, - } - gen._check_allowlist_staleness(schema, rules, None) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("phantom_attr" in m for m in msgs) - assert not any("'kp'" in m for m in msgs) - assert not any("'torquescale'" in m for m in msgs) - - -def test_unmapped_mj_enum_must_exist_in_mjspec(): - rules = { - "intentionally_unmapped_mj_enum_values": { - "mjtJoint": ["mjJNT_SLIDE"], - "mjtGhost": ["mjGHOST_X"], - "__per_element__": {"site.type": ["mjGEOM_PLANE"]}, # skipped - }, - } - mjspec = {"enums": {"mjtJoint": {"mjJNT_HINGE": 0, "mjJNT_SLIDE": 1}}} - gen._check_allowlist_staleness({}, rules, mjspec) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("mjtGhost" in m for m in msgs) - assert not any("mjtJoint" in m for m in msgs) - # __per_element__ never fires its own staleness diag. - assert not any("__per_element__" in m for m in msgs) - - -# ---------- _collect_every_schema_attr ------------------------------------ - -def test_collect_every_schema_attr_picks_up_three_shapes(): - """Cover the three list shapes the walker handles: - nested-attrs key, flat list at any depth (actuator_types, - equality.weld), nested-attrs inside sensor_types entries.""" - schema = { - "body": {"attrs": ["pos", "quat"]}, - "actuator_types": {"position": ["kp", "kv"]}, - "equality": {"weld": ["torquescale"]}, - "sensor_types": [{"name": "accel", "attrs": ["cutoff"]}], - } - out = gen._collect_every_schema_attr(schema) - for needed in ("pos", "quat", "kp", "kv", "torquescale", "cutoff"): - assert needed in out, f"missing {needed} from {out}" diff --git a/Scripts/codegen/tests/test_phase_context.py b/Scripts/codegen/tests/test_phase_context.py deleted file mode 100644 index ea87bce8..00000000 --- a/Scripts/codegen/tests/test_phase_context.py +++ /dev/null @@ -1,39 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the ``PhaseContext``. Every -emission phase used to take 7 (or 8) positional args; they now take -one ``PhaseContext`` so adding a new field to the pipeline doesn't -touch a dozen signatures.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_every_emission_phase_accepts_phase_context(): - """Every entry in EMISSION_PHASES.fn must be callable with one - PhaseContext arg. Catches refactor drift where one phase keeps the - old 7-positional-arg signature.""" - ctx = gen.PhaseContext( - schema={}, rules={}, mjxmacro={}, mjspec={}, - public_root="", private_root="", bind_h_path="", - writes=[], - ) - for phase in gen.EMISSION_PHASES: - # No assertion on side effects — just that the call doesn't - # TypeError on signature mismatch. - phase.fn(ctx) - - -def test_phase_context_is_dataclass_with_writes_list(): - """Quick sanity: ``writes`` must be a mutable list, not a frozen - sequence — phases append to it.""" - ctx = gen.PhaseContext( - schema={}, rules={}, mjxmacro={}, mjspec={}, - public_root="", private_root="", bind_h_path="", - writes=[], - ) - ctx.writes.append("sentinel") - assert ctx.writes == ["sentinel"] - - -# Unknown-layout diagnostic is pinned in test_inject_helpers::test_layout_dispatch_diagnoses_unknown_layout. diff --git a/Scripts/codegen/tests/test_property_units.py b/Scripts/codegen/tests/test_property_units.py deleted file mode 100644 index a7a904be..00000000 --- a/Scripts/codegen/tests/test_property_units.py +++ /dev/null @@ -1,130 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the editor display-unit rule shapes: ``property_units`` -(UE-native ``Units="m"`` meta on scalars, custom ``MjUnit="m"`` on -TArrays), ``hidden_attrs`` / ``hidden_canon_properties`` -(EditConditionHides), and the validity drift check.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_property_units_emits_units_meta_on_scalar(): - """Scalar/struct UE types get UE-native meta=(Units="...").""" - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {"margin": "float"}, - "canonicalizations": {}, - "element_rules": { - "geom": {"property_units": {"margin": "m"}}, - }, - } - out = gen.emit_schema_for_attrs(["margin"], rules, "geom", "Geom") - assert 'Units="m"' in out.properties_h, out.properties_h - assert out.properties_h.count('Units="m"') == 1 - - -def test_property_units_emits_mjunit_meta_on_tarray(): - """TArray UE types get MjUnit="..." — UHT rejects Units on arrays; - URLab's detail customization reads MjUnit at runtime.""" - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {"size": "TArray"}, - "canonicalizations": {}, - "element_rules": { - "geom": {"property_units": {"size": "m"}}, - }, - } - out = gen.emit_schema_for_attrs(["size"], rules, "geom", "Geom") - assert 'MjUnit="m"' in out.properties_h, out.properties_h - assert 'Units="m"' not in out.properties_h - - -def test_property_units_coexists_with_property_meta(): - """Units composes with existing property_meta (e.g. DisplayName) - without one clobbering the other.""" - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {}, - "canonicalizations": {}, - "element_rules": { - "geom": { - "property_meta": {"foo": 'DisplayName="Foo Bar"'}, - "property_units": {"foo": "m"}, - }, - }, - } - out = gen.emit_schema_for_attrs(["foo"], rules, "geom", "Geom") - assert 'DisplayName="Foo Bar"' in out.properties_h - assert 'Units="m"' in out.properties_h - - -def test_property_units_validity_diagnoses_bad_unit(): - rules = { - "element_rules": { - "geom": {"property_units": {"size": "meters"}}, - }, - } - gen._check_property_units_validity(rules) - assert any("'meters'" in d.message and "geom" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_property_units_validity_silent_on_known_units(): - rules = { - "element_rules": { - "geom": {"property_units": {"size": "m", "fovy": "deg"}}, - }, - } - gen._check_property_units_validity(rules) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -def test_hidden_attrs_emits_edit_condition_hides(): - """hidden_attrs entries emit EditCondition=false + EditConditionHides - so the field stays in C++ but the Details panel widget vanishes.""" - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {"size": "TArray"}, - "canonicalizations": {}, - "element_rules": { - "geom": {"hidden_attrs": ["size"]}, - }, - } - out = gen.emit_schema_for_attrs(["size"], rules, "geom", "Geom") - assert 'EditCondition="false"' in out.properties_h - assert 'EditConditionHides' in out.properties_h - - -def test_hidden_canon_properties_emits_edit_condition_hides(): - rules = { - "global_exclusions": [], - "default_type": "float", - "property_renames": {}, - "type_mappings": {}, - "canonicalizations": { - "spatial_pose": { - "absorbs_attrs": ["pos"], - "emits_property": {"name": "Pos", "type": "FVector"}, - "import_helper": "X", - "export_helper": "Y", - }, - }, - "element_rules": { - "geom": { - "applies_canonicalizations": ["spatial_pose"], - "hidden_canon_properties": ["spatial_pose"], - }, - }, - } - out = gen.emit_schema_for_attrs([], rules, "geom", "Geom") - assert 'FVector Pos' in out.properties_h - assert 'EditConditionHides' in out.properties_h diff --git a/Scripts/codegen/tests/test_regen_no_diff.py b/Scripts/codegen/tests/test_regen_no_diff.py deleted file mode 100644 index fdea920d..00000000 --- a/Scripts/codegen/tests/test_regen_no_diff.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Golden-file gate. - -Runs ``generate_ue_components.py --check`` against the real schema / -mjxmacro / rules / mjspec snapshots and asserts: - 1. Every emitted file is byte-identical to what's on disk (exit 0). - 2. The run produced zero diagnostics in stderr — catches new warn-mode - drift introduced by a rule change that the byte-identity check - can't see (drift fires on stderr without changing the emit output). -""" - -from __future__ import annotations - -import os -import subprocess -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_GENERATOR = os.path.normpath(os.path.join(_HERE, "..", "generate_ue_components.py")) - - -def test_codegen_check_clean() -> None: - """`generate_ue_components.py --check` exits 0 with no diagnostics.""" - proc = subprocess.run( - [sys.executable, _GENERATOR, "--check"], - capture_output=True, - text=True, - ) - stdout = proc.stdout or "" - stderr = proc.stderr or "" - assert proc.returncode == 0, ( - "codegen drift detected: re-running the generator would rewrite one or " - "more on-disk files. Re-run `python Scripts/codegen/generate_ue_components.py` " - "to regenerate, or update the emit functions if hand-edits to " - "CODEGEN_*_START/END regions slipped past the generator.\n" - f"--- stdout ---\n{stdout}\n--- stderr ---\n{stderr}" - ) - # Diagnostics fire on stderr without changing emit output — catches the - # warn-mode drift class that the byte-identity check is blind to. - assert "[diagnostic]" not in stderr, ( - "codegen ran clean (no file rewrites) but emitted one or more " - "diagnostics. A rule entry or snapshot likely drifted; rules JSON or " - "an intentionally_* allowlist needs updating.\n" - f"--- stderr ---\n{stderr}" - ) diff --git a/Scripts/codegen/tests/test_require_introspect.py b/Scripts/codegen/tests/test_require_introspect.py deleted file mode 100644 index 92c975d9..00000000 --- a/Scripts/codegen/tests/test_require_introspect.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Verifies the ``--require-introspect`` flag's exit-code contract. - -The clang-AST introspect snapshot drives the embedded-C++ drift checks -(value_map / hand-enum / mjs_array shape). Letting codegen silently fall -back to a stale or missing snapshot turned subtle rule typos into runtime -fall-through; this flag is the hard-fail gate. - -These tests shell out to ``generate_ue_components.py`` so the real -``main()`` arg-parsing + early-exit branches are exercised. -""" - -from __future__ import annotations - -import os -import subprocess -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -_PLUGIN_ROOT = os.path.dirname(os.path.dirname(_CODEGEN_DIR)) -_GENERATOR = os.path.join(_CODEGEN_DIR, "generate_ue_components.py") - - -def _run(extra_args: list[str], introspect_path: str) -> subprocess.CompletedProcess: - return subprocess.run( - [sys.executable, _GENERATOR, - "--check", "--introspect", introspect_path, - *extra_args], - capture_output=True, text=True, cwd=_PLUGIN_ROOT, - ) - - -def test_require_introspect_default_hard_fails_when_missing(tmp_path): - """Default behaviour (no flag = require_introspect=True) MUST exit - non-zero with the documented exit code (3) when the snapshot is - absent. Closes the silent-fallback path.""" - missing = str(tmp_path / "introspect_snapshot.json") - assert not os.path.exists(missing) - proc = _run([], missing) - assert proc.returncode == 3, ( - f"expected exit 3, got {proc.returncode}\n" - f"stdout={proc.stdout!r}\nstderr={proc.stderr!r}" - ) - assert "introspect snapshot missing" in proc.stderr - - -def test_no_require_introspect_skips_the_exit_3_gate(tmp_path): - """``--no-require-introspect`` skips the up-front exit-3 hard-fail - so the run reaches the codegen loop. The loop itself raises - downstream once it needs the setto signatures the introspect - carries — the flag isn't a way to make the codegen succeed - without introspect, it's a way to defer the failure for triage.""" - missing = str(tmp_path / "introspect_snapshot.json") - proc = _run(["--no-require-introspect"], missing) - # The up-front gate didn't fire (exit 3), and the run reached the - # loop. Exit 1 (the downstream RuntimeError) is the documented - # failure mode. - assert proc.returncode != 3 - assert "warning" in proc.stderr.lower() - assert "introspect snapshot missing" in proc.stderr diff --git a/Scripts/codegen/tests/test_resolve_mjs_field.py b/Scripts/codegen/tests/test_resolve_mjs_field.py deleted file mode 100644 index 8aa80421..00000000 --- a/Scripts/codegen/tests/test_resolve_mjs_field.py +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks ``_resolve_mjs_field``'s 5-step resolution chain and the -per-strategy helpers. Each strategy lives in -its own function and is composed via ``_MJS_FIELD_RESOLVERS``.""" - -from __future__ import annotations - -import _codegen_core as gen # private resolver surface lives in _codegen_core - - -# ---------- per-strategy resolvers ---------------------------------------- - -def test_resolve_direct_match(): - assert gen._resolve_direct("condim", {"condim", "type"}) == "condim" - assert gen._resolve_direct("absent", {"other"}) is None - - -def test_resolve_name_suffix(): - """MuJoCo name-ref fields uniformly append 'name'.""" - assert gen._resolve_name_suffix("mesh", {"meshname"}) == "meshname" - assert gen._resolve_name_suffix("scalar", {"scalarvalue"}) is None - - -def test_resolve_root_name_digits_variants(): - """``body1`` -> ``bodyname1`` and the underscore variant - ``body_name1`` both resolve.""" - assert gen._resolve_root_name_digits("body1", {"bodyname1"}) == "bodyname1" - assert gen._resolve_root_name_digits("body1", {"body_name1"}) == "body_name1" - # No-digit suffix -> no match. - assert gen._resolve_root_name_digits("body", {"bodyname"}) is None - - -def test_resolve_underscore_norm_matches_drop_underscores(): - assert gen._resolve_underscore_norm("solreflimit", {"solref_limit"}) == "solref_limit" - assert gen._resolve_underscore_norm("solreflimit", {"solref_friction"}) is None - - -def test_resolve_actuator_prefix_with_underscore_norm(): - """schema ``actuatorfrclimited`` -> mjs ``actfrclimited`` (direct) - or ``act_frc_limited`` (via underscore norm).""" - assert gen._resolve_actuator_prefix("actuatorfrclimited", {"actfrclimited"}) == "actfrclimited" - assert gen._resolve_actuator_prefix("actuatorfrclimited", {"act_frc_limited"}) == "act_frc_limited" - assert gen._resolve_actuator_prefix("notanactuator", {"anything"}) is None - - -# ---------- composition through _resolve_mjs_field ------------------------ - -def test_resolve_mjs_field_prefers_override_table(): - """Per-element ``attr_to_mjs_field`` rules ALWAYS win — no - convention can override a rule author's explicit mapping.""" - out = gen._resolve_mjs_field( - "target", {"target", "targetbody"}, - attr_to_mjs_field={"target": "targetbody"}, - ) - assert out == "targetbody" - - -def test_resolve_mjs_field_falls_through_strategy_order(): - """Each strategy fires in declared order; only the first match - wins. Strategies that don't apply return None and fall through.""" - # No direct match; falls through to root+name+digits. - out = gen._resolve_mjs_field("body1", {"bodyname1", "geomname1"}) - assert out == "bodyname1" - - -def test_resolve_mjs_field_falls_back_to_attr_when_nothing_matches(): - """If every strategy returns None, return the attr verbatim — the - downstream C++ build will fail loudly on the bad field name.""" - out = gen._resolve_mjs_field( - "novel_attr", {"unrelated_field"}, - ) - assert out == "novel_attr" - - -def test_resolve_mjs_field_empty_fields_returns_attr(): - """When ``mjs_fields`` is empty, no resolution is possible — the - attr passes through unchanged.""" - assert gen._resolve_mjs_field("any", set()) == "any" - - -def test_resolvers_tuple_is_module_constant(): - """The resolver chain order must be a module-level constant so a - test can assert it explicitly. Catches accidental reordering that - would change which strategy wins on ambiguous inputs.""" - assert gen._MJS_FIELD_RESOLVERS == ( - gen._resolve_direct, - gen._resolve_name_suffix, - gen._resolve_root_name_digits, - gen._resolve_underscore_norm, - gen._resolve_actuator_prefix, - ) diff --git a/Scripts/codegen/tests/test_rule_iter_helpers.py b/Scripts/codegen/tests/test_rule_iter_helpers.py deleted file mode 100644 index d2e92c40..00000000 --- a/Scripts/codegen/tests/test_rule_iter_helpers.py +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the rule-iteration helpers ``compute_canon_absorbed`` / -``iter_canon_absorbed`` / ``iter_category_attrs``. They are the single -source of truth for the standard three-skip exclusion gate that every -per-attr emission site used to inline.""" - -from __future__ import annotations - -import generate_ue_components as gen -import _codegen_core as _core -gen.iter_canon_absorbed = _core.iter_canon_absorbed - - -# ---------- iter_canon_absorbed / compute_canon_absorbed ------------------- - -def test_compute_canon_absorbed_unions_applied_canons(): - canons = { - "spatial_pose": {"absorbs_attrs": ["pos", "quat", "euler", "axisangle"]}, - "fromto_decompose": {"absorbs_attrs": ["fromto"]}, - "unused": {"absorbs_attrs": ["other"]}, - } - got = gen.compute_canon_absorbed(["spatial_pose", "fromto_decompose"], canons) - assert got == {"pos", "quat", "euler", "axisangle", "fromto"} - # An unapplied canon stays out — gating is by applies_canonicalizations. - assert "other" not in got - - -def test_compute_canon_absorbed_silent_on_missing_canon(): - canons = {"spatial_pose": {"absorbs_attrs": ["pos"]}} - got = gen.compute_canon_absorbed(["spatial_pose", "ghost_canon"], canons) - assert got == {"pos"} - - -def test_compute_canon_absorbed_handles_empty_inputs(): - assert gen.compute_canon_absorbed([], {}) == set() - assert gen.compute_canon_absorbed(["any"], {}) == set() - assert gen.compute_canon_absorbed([], {"spatial_pose": {"absorbs_attrs": ["pos"]}}) == set() - - -def test_iter_canon_absorbed_is_lazy_view(): - canons = {"c": {"absorbs_attrs": ["a", "b"]}} - it = gen.iter_canon_absorbed(["c"], canons) - # Confirm it's an iterator, not a materialised list — call sites that - # only want a single membership check shouldn't pay for a list build. - assert iter(it) is it - assert list(it) == ["a", "b"] - - -# ---------- iter_category_attrs -------------------------------------------- - -def test_iter_category_attrs_filters_three_gates(): - attrs = ["a", "b", "c", "d", "e"] - got = list(gen.iter_category_attrs( - attrs, - global_excl={"a"}, - elem_excl={"b"}, - canon_absorbed={"c"}, - )) - assert got == ["d", "e"] - - -def test_iter_category_attrs_preserves_order(): - """Codegen output depends on attr order — schema order must survive - the filter so emitted .h/.cpp stay byte-identical to the schema.""" - attrs = ["zeta", "alpha", "mu"] - got = list(gen.iter_category_attrs( - attrs, global_excl=set(), elem_excl=set(), canon_absorbed=set(), - )) - assert got == ["zeta", "alpha", "mu"] - - -def test_iter_category_attrs_handles_empty_attrs(): - got = list(gen.iter_category_attrs( - [], global_excl={"a"}, elem_excl=set(), canon_absorbed=set(), - )) - assert got == [] diff --git a/Scripts/codegen/tests/test_rule_shape_contract.py b/Scripts/codegen/tests/test_rule_shape_contract.py deleted file mode 100644 index fc1cd6f9..00000000 --- a/Scripts/codegen/tests/test_rule_shape_contract.py +++ /dev/null @@ -1,326 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Contract tests for codegen_rules.json. The rules file is the -single source of truth for the codegen — these tests pin the -structural invariants every emitter depends on, so a hand-edit -that breaks them surfaces in pytest instead of as a UE compile -failure far downstream.""" - -from __future__ import annotations - -import generate_ue_components as gen # noqa: F401 (kept for module discovery) -from _codegen_core import _UE_TYPE_INFO - - -_KNOWN_TOP_LEVEL_KEYS = { - # data driving the emit path - "global_exclusions", - "default_type", - "property_renames", - "setto_param_defaults", - "type_mappings", - "categories", - "element_rules", - "canonicalizations", - "synthetic_categories", - "generated_enums", - "editor_option_helpers", - "views", - # drift-suppression allowlists - "intentionally_unmodeled_elements", - "intentionally_unmodeled_mjs_fields", - "intentionally_unmodeled_compiler_attrs", - "intentionally_default_typed_attrs", - "intentionally_unmapped_mj_enum_values", - # shared per-rule namespaces - "compiler_attr_field_map", -} - - -def test_top_level_keys_are_recognised(real_rules): - """Every top-level key in the rules JSON is either a known shape - or a ``_note_*`` sibling string. Catches typo'd renames at edit - time.""" - unknown = [] - for key in real_rules: - if key.startswith("_") or key in _KNOWN_TOP_LEVEL_KEYS: - continue - unknown.append(key) - assert not unknown, ( - f"unknown top-level keys in codegen_rules.json: {unknown}. " - f"Either add them to _KNOWN_TOP_LEVEL_KEYS in this test or " - f"_note_-prefix them to mark as documentation." - ) - - -def test_every_category_carries_required_keys(real_rules): - """Every entry under ``categories`` needs a schema_common_block. - Missing the block silently produces an empty emit; the test - surfaces the missing key at JSON-edit time.""" - for cat, cat_rules in real_rules.get("categories", {}).items(): - if cat.startswith("_"): - continue - assert "schema_common_block" in cat_rules, ( - f"category '{cat}' missing schema_common_block") - - -def test_canonicalisations_referenced_by_element_rules_exist(real_rules): - """element_rules[X].applies_canonicalizations must list canon - names that exist in the top-level ``canonicalizations`` block. - Otherwise per-element XML import silently drops absorbed attrs.""" - canon_keys = set(real_rules.get("canonicalizations", {}).keys()) - for elem, elem_rule in real_rules.get("element_rules", {}).items(): - for canon in elem_rule.get("applies_canonicalizations", []): - assert canon in canon_keys, ( - f"element_rules['{elem}'].applies_canonicalizations " - f"references '{canon}' which is not declared in the " - f"top-level canonicalizations block.") - - -def test_xml_enum_attr_value_maps_have_correct_shape(real_rules): - """Each ``value_map`` entry must be a 2-element ``[ue_member, - mj_const]`` list, non-empty xml key, and the map itself non-empty. - An empty map silently emits a dead block that toggles the override - flag without ever assigning the enum; empty xml keys match every - missing attr at runtime via ``GetAttribute()`` returning ``""``.""" - for elem, elem_rule in real_rules.get("element_rules", {}).items(): - for attr, enum_def in elem_rule.get("xml_enum_attrs", {}).items(): - value_map = enum_def.get("value_map", {}) - assert value_map, ( - f"element_rules['{elem}'].xml_enum_attrs['{attr}']" - f".value_map is empty — emit would set the override " - f"toggle without ever assigning the enum.") - for xml_val, mapping in value_map.items(): - assert xml_val != "", ( - f"element_rules['{elem}'].xml_enum_attrs['{attr}']" - f".value_map has an empty-string xml key — at " - f"runtime this matches every missing attr.") - assert isinstance(mapping, (list, tuple)), ( - f"element_rules['{elem}'].xml_enum_attrs['{attr}']" - f".value_map['{xml_val}'] must be a list, got " - f"{type(mapping).__name__}") - assert len(mapping) == 2, ( - f"element_rules['{elem}'].xml_enum_attrs['{attr}']" - f".value_map['{xml_val}'] must be " - f"[ue_member, mj_const]; got {mapping!r}") - - -def test_synthetic_categories_carry_block_or_skip_flag(real_rules): - """Every synthetic_categories entry must either name an - mjxmacro_block or carry a disabled flag. A bare entry would - silently emit nothing.""" - synth = real_rules.get("synthetic_categories", {}) - for cat_name, def_ in synth.items(): - if cat_name.startswith("_") or not isinstance(def_, dict): - continue - assert def_.get("mjxmacro_block") or def_.get("disabled"), ( - f"synthetic_categories['{cat_name}'] needs either an " - f"mjxmacro_block or a disabled flag") - - -# ---------- inner-key shape contracts ---------------------------------- -# -# The top-level allowlist catches typo'd renames at the rules root, but -# inner-key typos (e.g. ``attr_to_mjs_fields`` instead of -# ``attr_to_mjs_field``) silently fall through every reader's -# ``.get(..., {})``. The contracts below close that loop: every reader -# fetches by bracketed access against the keys listed here, so a typo -# surfaces in pytest instead of as a KeyError mid-emit (or worse — a -# silent no-op that ships wrong-runtime C++). - - -_CATEGORY_REQUIRED = {"schema_common_block", "base_class_name", "base_class_header", "mjs_struct"} -_CATEGORY_OPTIONAL = { - "subtypes", "layout", "subtype_setto", "type_enum_name", - "schema_subtypes_block", "schema_common_extra_attrs", - "objtype_dispatch", "geom_final_type", "applies_canonicalizations", - "subclass_directory_pub", "subclass_directory_priv", - "xml_passthrough_emission", "disable_mjs_export_emission", -} -_SUBTYPE_REQUIRED = {"key", "enum_value", "class_name"} -_SUBTYPE_OPTIONAL = { - "header", "fully_emitted", "case_body_override", "extra_constructor", -} -_ELEMENT_RULE_OPTIONAL = { - "exclude_attrs", "applies_canonicalizations", "xml_enum_attrs", - "attr_to_mjs_field", "property_renames", "property_meta", - "property_units", "property_defaults", "target_collations", - "common_imports", "mjs_data_packed_attrs", "mjs_double_vec_attrs", - "sentinel_skip_export", "export_skip_attrs", "unit_conversion", - "vec3_convert", "hidden_canon_properties", "hidden_attrs", - "canon_export_skip", "xml_passthrough_emission", "common_attrs", - "field_apply_to_spec_mode", "field_defaults", -} -_XML_ENUM_ATTR_REQUIRED = {"ue_property", "ue_enum_type", "value_map"} -_XML_ENUM_ATTR_OPTIONAL = { - "mjs_field", "mjs_cast", "has_override_toggle", - "emit_property_decl", "property_default", "property_meta", - "from_mj_enum", -} -_CANON_REQUIRED = {"absorbs_attrs"} -_CANON_OPTIONAL = { - "emits_property", "category_specifics", - "import_helper", "export_helper", -} - - -def _assert_inner_keys(path: str, obj: dict, required: set, optional: set): - for k in required: - assert k in obj, f"{path}: missing required key '{k}'" - allowed = required | optional - bad = [k for k in obj if not k.startswith("_") and k not in allowed] - assert not bad, ( - f"{path}: unknown key(s) {bad}. Either add to the contract " - f"allowlist in test_rule_shape_contract.py or _note_-prefix " - f"as documentation." - ) - - -def test_category_inner_keys_match_contract(real_rules): - for cat, cat_rules in real_rules.get("categories", {}).items(): - if cat.startswith("_"): - continue - _assert_inner_keys( - f"categories['{cat}']", cat_rules, - _CATEGORY_REQUIRED, _CATEGORY_OPTIONAL, - ) - - -def test_category_subtype_inner_keys_match_contract(real_rules): - for cat, cat_rules in real_rules.get("categories", {}).items(): - if cat.startswith("_"): - continue - for i, sub in enumerate(cat_rules.get("subtypes", [])): - if not isinstance(sub, dict): - continue - _assert_inner_keys( - f"categories['{cat}'].subtypes[{i}] ({sub.get('key', '?')})", - sub, _SUBTYPE_REQUIRED, _SUBTYPE_OPTIONAL, - ) - - -def test_element_rule_inner_keys_match_contract(real_rules): - for elem, elem_rule in real_rules.get("element_rules", {}).items(): - if elem.startswith("_"): - continue - _assert_inner_keys( - f"element_rules['{elem}']", elem_rule, - set(), _ELEMENT_RULE_OPTIONAL, - ) - - -def test_xml_enum_attr_inner_keys_match_contract(real_rules): - for elem, elem_rule in real_rules.get("element_rules", {}).items(): - for attr, enum_def in elem_rule.get("xml_enum_attrs", {}).items(): - if not isinstance(enum_def, dict): - continue - _assert_inner_keys( - f"element_rules['{elem}'].xml_enum_attrs['{attr}']", - enum_def, - _XML_ENUM_ATTR_REQUIRED, _XML_ENUM_ATTR_OPTIONAL, - ) - - -def test_canonicalisation_inner_keys_match_contract(real_rules): - for canon, canon_def in real_rules.get("canonicalizations", {}).items(): - if canon.startswith("_"): - continue - _assert_inner_keys( - f"canonicalizations['{canon}']", canon_def, - _CANON_REQUIRED, _CANON_OPTIONAL, - ) - - -def test_synthetic_category_required_public_header(real_rules): - """Every active synthetic_categories entry needs ``public_header`` - pointing under ``MuJoCo/Generated/``. Missing key would KeyError at - emit; a stray absolute path would write outside the plugin tree.""" - for cat_name, def_ in real_rules.get("synthetic_categories", {}).items(): - if cat_name.startswith("_") or not isinstance(def_, dict): - continue - if def_.get("disabled"): - continue - assert "public_header" in def_, ( - f"synthetic_categories['{cat_name}']: missing public_header" - ) - ph = def_["public_header"] - assert ph.startswith("MuJoCo/Generated/") and ph.endswith(".h"), ( - f"synthetic_categories['{cat_name}'].public_header = " - f"'{ph}' must be 'MuJoCo/Generated/*.h'" - ) - - -def test_generated_enums_carry_ue_name_and_mapping(real_rules): - """generated_enums entries must declare ue_name (starts with EMj) - and at least one mapping source (ue_member_from_mj or - extra_members).""" - for group, group_def in real_rules.get("generated_enums", {}).items(): - if group.startswith("_") or not isinstance(group_def, dict): - continue - for i, entry in enumerate(group_def.get("enums", [])): - if entry.get("disabled"): - continue - ue_name = entry.get("ue_name", "") - assert ue_name.startswith("EMj"), ( - f"generated_enums['{group}'].enums[{i}].ue_name = " - f"'{ue_name}' must start with 'EMj'" - ) - has_mj = entry.get("from_mj_enum") - has_members = entry.get("ue_member_from_mj") or entry.get("extra_members") - assert has_mj or has_members, ( - f"generated_enums['{group}'].enums[{i}] ({ue_name}): " - f"needs from_mj_enum or extra_members; otherwise emits " - f"nothing." - ) - - -def test_setto_param_defaults_are_finite_numeric_literals(real_rules): - """Values land VERBATIM in the generated C++ as a ternary's else - branch (see ``_emit_setto_call``). Allowed shapes: int / signed-or- - -unsigned float with optional decimal + exponent. Reject ``"inf"``, - ``"nan"``, hex, or empty strings — they compile to non-finite or - invalid C++.""" - import re - finite_re = re.compile(r"^-?\d+(\.\d*)?([eE][+-]?\d+)?$") - defaults = real_rules.get("setto_param_defaults", {}) - for fn_name, params in defaults.items(): - if fn_name.startswith("_") or not isinstance(params, dict): - continue - for pname, value in params.items(): - assert isinstance(value, str), ( - f"setto_param_defaults['{fn_name}']['{pname}'] = " - f"{value!r}: must be a string (drops verbatim into C++)" - ) - assert finite_re.match(value), ( - f"setto_param_defaults['{fn_name}']['{pname}'] = " - f"'{value}': must be a finite decimal literal " - f"('inf' / 'nan' / hex / empty rejected)" - ) - - -def test_type_mappings_use_known_ue_types(real_rules): - """Every type in type_mappings must be a UE type the codegen - can emit (i.e. resolves through _UE_TYPE_INFO or is a known - TArray<...>). Surfacing typo'd types here catches "TArray" - long before UHT fails.""" - known = set(_UE_TYPE_INFO.keys()) - for attr, ue_type in real_rules.get("type_mappings", {}).items(): - if attr.startswith("_"): - continue - if ue_type in known: - continue - if ue_type.startswith("TArray<") and ue_type.endswith(">"): - inner = ue_type[len("TArray<"):-1] - assert (inner in known or inner.startswith("U") - or inner.startswith("EMj")), ( - f"type_mappings['{attr}'] = '{ue_type}' has unknown " - f"inner type '{inner}'") - continue - # URLab-side hand-rolled enums (EMj*) are legitimate UPROPERTY - # types but live outside the registry's UE type universe. - if ue_type.startswith("EMj"): - continue - # Unrecognised types fall through to default_type with a diag; - # the test surfaces the JSON-edit-time intent. - assert False, ( - f"type_mappings['{attr}'] = '{ue_type}' is not a " - f"recognised UE type") diff --git a/Scripts/codegen/tests/test_sensor_per_type_extractor.py b/Scripts/codegen/tests/test_sensor_per_type_extractor.py deleted file mode 100644 index 3197e0cf..00000000 --- a/Scripts/codegen/tests/test_sensor_per_type_extractor.py +++ /dev/null @@ -1,198 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Unit tests for the per-sensor objtype/reftype extractor in -build_mjcf_schema_snapshot.py. - -The extractor scrapes xml_native_reader.cc's ``Sensor()`` if/else cascade -so codegen rules don't have to hand-list each sensor's mjsSensor object -defaults. Tests cover the four shape classes the extractor produces: -literal default, from_xml, computed (multi-attr derivation), and the -nested-branch suppression case (distance/normal/fromto share an outer -branch whose inner ``if`` would otherwise overwrite the entry). -""" - -from __future__ import annotations - -import os -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -if _CODEGEN_DIR not in sys.path: - sys.path.insert(0, _CODEGEN_DIR) - -from build_mjcf_schema_snapshot import _extract_sensor_per_type # noqa: E402 - - -_FAKE_SOURCE = r""" -void mjXReader::Sensor(XMLElement* section) { - while (elem) { - mjsSensor* sensor = mjs_addSensor(spec); - string type = elem->Value(); - string text, name, objname, refname; - - if (type == "touch") { - sensor->type = mjSENS_TOUCH; - sensor->objtype = mjOBJ_SITE; - ReadAttrTxt(elem, "site", objname, true); - } else if (type == "framepos") { - sensor->type = mjSENS_FRAMEPOS; - ReadAttrTxt(elem, "objtype", text, true); - sensor->objtype = (mjtObj)mju_str2Type(text.c_str()); - ReadAttrTxt(elem, "objname", objname, true); - if (ReadAttrTxt(elem, "reftype", text)) { - sensor->reftype = (mjtObj)mju_str2Type(text.c_str()); - ReadAttrTxt(elem, "refname", refname, true); - } - } else if (type == "distance" || type == "normal" || type == "fromto") { - bool has_body1 = ReadAttrTxt(elem, "body1", objname); - sensor->objtype = has_body1 ? mjOBJ_BODY : mjOBJ_GEOM; - ReadAttrTxt(elem, "body2", refname); - sensor->reftype = mjOBJ_BODY; - if (type == "distance") { - sensor->type = mjSENS_GEOMDIST; - } else if (type == "normal") { - sensor->type = mjSENS_GEOMNORMAL; - } else { - sensor->type = mjSENS_GEOMFROMTO; - } - } else if (type == "clock") { - sensor->type = mjSENS_CLOCK; - sensor->objtype = mjOBJ_UNKNOWN; - } - elem = NextSiblingElement(elem); - } -} -""" - - -def test_literal_objtype_with_site_name_read(): - result = _extract_sensor_per_type(_FAKE_SOURCE) - assert result["touch"] == { - "mj_type": "mjSENS_TOUCH", - "objtype": "mjOBJ_SITE", - "reftype": None, - "obj_attr": "site", - "ref_attr": None, - "computed": False, - } - - -def test_from_xml_objtype_picks_up_objname_attr(): - result = _extract_sensor_per_type(_FAKE_SOURCE) - framepos = result["framepos"] - assert framepos["objtype"] == "from_xml" - assert framepos["obj_attr"] == "objname" - assert framepos["ref_attr"] == "refname" - assert framepos["computed"] is False - - -def test_multi_name_branch_emits_one_entry_per_name(): - result = _extract_sensor_per_type(_FAKE_SOURCE) - for name in ("distance", "normal", "fromto"): - assert name in result, f"missing {name}" - assert result[name]["computed"] is True - assert result[name]["objtype"] == "computed" - # All three share the same body1/body2 attr-read. - assert result[name]["obj_attr"] == "body1" - assert result[name]["ref_attr"] == "body2" - - -def test_inner_if_inside_multi_name_branch_does_not_overwrite(): - """Regression: the body of the distance/normal/fromto branch contains - ``if (type == "distance")`` and ``else if (type == "normal")`` which set - only ->type. Those inner matches would override the outer entry's - obj_attr/ref_attr with None if the extractor walked them naively.""" - result = _extract_sensor_per_type(_FAKE_SOURCE) - assert result["distance"]["obj_attr"] == "body1" - assert result["normal"]["obj_attr"] == "body1" - - -def test_clock_has_unknown_objtype_no_name_read(): - result = _extract_sensor_per_type(_FAKE_SOURCE) - assert result["clock"] == { - "mj_type": "mjSENS_CLOCK", - "objtype": "mjOBJ_UNKNOWN", - "reftype": None, - "obj_attr": None, - "ref_attr": None, - "computed": False, - } - - -def test_missing_sensor_method_returns_empty(): - assert _extract_sensor_per_type("// no Sensor method here") == {} - - -# ---------- sensor switch + TagToType codegen ---------------------------- - -def test_sensor_switch_body_emits_static_objtype_when_present(): - from generate_ue_components import _emit_sensor_switch_block # noqa - cat_rules = { - "type_enum_name": "EMjSensorType", - "subtypes": [ - {"key": "touch", "enum_value": "Touch"}, - {"key": "framepos", "enum_value": "FramePos"}, - ], - } - sensor_per_type = { - "touch": {"mj_type": "mjSENS_TOUCH", "objtype": "mjOBJ_SITE", "reftype": None}, - "framepos": {"mj_type": "mjSENS_FRAMEPOS", "objtype": "from_xml", "reftype": None}, - "accelerometer": {"mj_type": "mjSENS_ACCELEROMETER", "objtype": "mjOBJ_SITE", "reftype": None}, - } - out = _emit_sensor_switch_block(cat_rules, sensor_per_type) - # Touch gets static objtype literal. - assert "case EMjSensorType::Touch: Element->type = mjSENS_TOUCH; Element->objtype = mjOBJ_SITE; break;" in out - # framepos has objtype=from_xml -> NO static objtype emitted (handled in post-switch block). - assert "case EMjSensorType::FramePos: Element->type = mjSENS_FRAMEPOS; break;" in out - # Default fallback is hard-wired to accelerometer. - assert "default: Element->type = mjSENS_ACCELEROMETER; Element->objtype = mjOBJ_SITE; break;" in out - - -def test_sensor_switch_body_honours_case_override(): - from generate_ue_components import _emit_sensor_switch_block - cat_rules = { - "type_enum_name": "EMjSensorType", - "subtypes": [ - {"key": "rangefinder", "enum_value": "RangeFinder", - "case_body_override": "Element->type = mjSENS_RANGEFINDER; Element->objtype = X;"}, - ], - } - out = _emit_sensor_switch_block(cat_rules, {"rangefinder": {"mj_type": "mjSENS_RANGEFINDER"}}) - # Override appears verbatim; the emitter's automatic mj_type / objtype - # lines are SKIPPED for this case. - assert "Element->objtype = X;" in out - assert out.count("RangeFinder") >= 1 - - -def test_sensor_tag_to_type_map_uses_xml_key_verbatim(): - from generate_ue_components import _emit_sensor_tag_to_type_block - cat_rules = { - "type_enum_name": "EMjSensorType", - "subtypes": [ - {"key": "touch", "enum_value": "Touch"}, - {"key": "e_potential", "enum_value": "EPotential"}, - {"key": "jointactuatorfrc", "enum_value": "JointActFrc"}, - ], - } - out = _emit_sensor_tag_to_type_block(cat_rules) - assert '{TEXT("touch"), EMjSensorType::Touch},' in out - # Underscore-containing XML keys preserved literally. - assert '{TEXT("e_potential"), EMjSensorType::EPotential},' in out - # XML key and UE enum_value can diverge; map uses XML key. - assert '{TEXT("jointactuatorfrc"), EMjSensorType::JointActFrc},' in out - - -def test_real_snapshot_covers_every_schema_sensor(): - """End-to-end: the snapshot shipped in Scripts/ should have one - per_type entry for every sensor_types entry — and vice versa. Catches - drift between the static MJCF[] schema and the parser cascade.""" - import json - plugin_root = os.path.normpath(os.path.join(_HERE, "..", "..", "..")) - snap = json.load(open(os.path.join(plugin_root, "Scripts", "codegen", "snapshots", "mjcf_schema_snapshot.json"))) - schema_names = set(snap["sensor_types"]) - per_type_names = set(snap.get("sensor_per_type", {}).keys()) - assert schema_names == per_type_names, ( - f"schema/per_type drift: only-in-schema={sorted(schema_names - per_type_names)}; " - f"only-in-per_type={sorted(per_type_names - schema_names)}" - ) diff --git a/Scripts/codegen/tests/test_setto_introspection.py b/Scripts/codegen/tests/test_setto_introspection.py deleted file mode 100644 index 9e20e496..00000000 --- a/Scripts/codegen/tests/test_setto_introspection.py +++ /dev/null @@ -1,149 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Regression guard for introspected mjs_setTo* emission. - -The 9 hand-written C++ literal blocks that used to live in -``codegen_rules.json`` were replaced by ``_emit_setto_call``, which derives -the call body from the parsed mjs_setTo* signature. The signature now -comes from the clang-AST introspect snapshot, projected into the -legacy mjspec shape via ``_mjspec_from_introspect``.""" - -from __future__ import annotations - -import json -import os - -from generate_ue_components import _emit_setto_call, _mjspec_from_introspect - - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_PLUGIN_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", "..")) - - -def _load_mjspec(): - with open(os.path.join(_PLUGIN_ROOT, "Scripts", "codegen", "snapshots", "introspect_snapshot.json"), "r") as f: - return _mjspec_from_introspect(json.load(f)) - - -def _setto_for(subtype_key, real_rules, real_schema, mjspec): - """Helper: invoke _emit_setto_call with the rules-real args for one actuator subtype.""" - actuator = real_rules["categories"]["actuator"] - setto_rules = actuator["subtype_setto"].get(subtype_key) - if setto_rules is None: - return "" - per_type_attrs = real_schema["actuator_types"].get(subtype_key, []) - base_attrs = real_schema.get("actuator_common", {}).get("attrs", []) - return _emit_setto_call( - subtype_key, setto_rules, real_rules, mjspec, - subtype_schema_attrs=per_type_attrs, base_schema_attrs=base_attrs, - ) - - -def test_motor_call_minimal(real_rules, real_schema): - """Motor's setTo has no params besides Element — output is a one-liner.""" - mjspec = _load_mjspec() - out = _setto_for("motor", real_rules, real_schema, mjspec) - assert "mjs_setToMotor(Element);" in out - - -def test_position_inherits_kp_and_nulls_unset_pointers(real_rules, real_schema): - """Position's kv/dampratio/timeconst are double[1] in C and nullable: the - buffer is built but passed only when overridden, else nullptr. kp is written - unconditionally by MuJoCo, so an unset kp re-asserts the inherited element - gain. inheritrange keeps its 0.0 sentinel (MuJoCo guards it with `> 0`).""" - mjspec = _load_mjspec() - out = _setto_for("position", real_rules, real_schema, mjspec) - assert "double kvBuf[1] = { bOverride_kv ? (double)kv : -1.0 };" in out - assert "double dampratioBuf[1] = { bOverride_dampratio ? (double)dampratio : -1.0 };" in out - # kp is unconditional-by-value -> re-assert the inherited gain, not -1.0. - assert "bOverride_kp ? (double)kp : Element->gainprm[0]" in out - # Pointer params passed only when authored, else nullptr (MuJoCo's unset - # convention) so the class default survives. - assert "bOverride_kv ? kvBuf : nullptr" in out - assert "bOverride_dampratio ? dampratioBuf : nullptr" in out - # timeconst is TArray-backed: an authored-but-empty array is unset too. - assert "(bOverride_timeconst && timeconst.Num() > 0) ? timeconstBuf : nullptr" in out - # inheritrange override sentinel from setto_param_defaults. - assert "bOverride_inheritrange ? (double)inheritrange : 0.0" in out - assert "mjs_setToPosition(Element," in out - - -def test_unconditional_gain_params_reassert_inherited_field(real_rules, real_schema): - """velocity/adhesion/damper each write a single gain slot unconditionally. - An unset param must re-assert the inherited element field so a class default - (e.g. under a ) is preserved, not clobbered - to a sentinel. damper's kv maps to -gainprm[2] (setToDamper does gainprm[2] - = -kv), so the re-assert carries the sign.""" - mjspec = _load_mjspec() - assert "bOverride_kv ? (double)kv : Element->gainprm[0]" in \ - _setto_for("velocity", real_rules, real_schema, mjspec) - assert "bOverride_gain ? (double)gain : Element->gainprm[0]" in \ - _setto_for("adhesion", real_rules, real_schema, mjspec) - assert "bOverride_kv ? (double)kv : -Element->gainprm[2]" in \ - _setto_for("damper", real_rules, real_schema, mjspec) - - -def test_setto_call_surfaces_error_return(real_rules, real_schema): - """Every mjs_setTo* return string is checked and logged, not discarded — - that discarded return is what hid the gain-clobber bug for so long.""" - mjspec = _load_mjspec() - for sub in ("position", "velocity", "cylinder", "motor"): - out = _setto_for(sub, real_rules, real_schema, mjspec) - assert "const char* SetToErr =" in out - assert "if (SetToErr && *SetToErr)" in out - - -def test_muscle_scale_projection_and_tausmooth_zero(real_rules, real_schema): - """Muscle's scale (TArray) is collapsed to a scalar for the - setTo call — we take element [0]. tausmooth has the 0.0 sentinel - (so MuJoCo treats unset as 'no smoothing', not 'broken default').""" - mjspec = _load_mjspec() - out = _setto_for("muscle", real_rules, real_schema, mjspec) - assert "(bOverride_scale && scale.Num() > 0) ? (double)scale[0] : -1.0" in out - assert "bOverride_tausmooth ? (double)tausmooth : 0.0" in out - assert "double timeconstBuf[2]" in out - assert "double rangeBuf[2]" in out - - -def test_dcmotor_nullable_arrays_use_filldouble_and_input_rename(real_rules, real_schema): - """DCMotor passes nullptr when arrays not overridden; uses FillDouble - helper. input_mode (C param) -> input (UE prop) rename.""" - mjspec = _load_mjspec() - out = _setto_for("dcmotor", real_rules, real_schema, mjspec) - assert "FillDouble(motorconstBuf, motorconst)" in out - assert "bOverride_motorconst ? motorconstBuf : nullptr" in out - # input_mode -> input rename: the C arg name in the signature is input_mode, - # but the UE-side property is `input`. The expression must reference `input`, - # not `input_mode`. - assert "bOverride_input ? (int)input : 0" in out - # resistance has 0.0 sentinel per setto_param_defaults. - assert "bOverride_resistance ? (double)resistance : 0.0" in out - - -def test_intvelocity_inherits_kp_and_nulls_unset_pointers(real_rules, real_schema): - """IntVelocity writes gainprm[0] = kp unconditionally, so an unset kp must - re-assert the inherited element value, not a sentinel that clobbers it. - timeconst (not in the intvelocity schema) is a nullable pointer param, so it - passes nullptr — MuJoCo then leaves the type-derived default alone.""" - mjspec = _load_mjspec() - out = _setto_for("intvelocity", real_rules, real_schema, mjspec) - assert "double kvBuf[1]" in out - # kp is unconditional-by-value -> re-assert the inherited gain, not -1.0. - assert "bOverride_kp ? (double)kp : Element->gainprm[0]" in out - # timeconst nullable + not authored -> bare nullptr, no sentinel buffer. - assert "double timeconstBuf" not in out - assert "nullptr" in out - # kv/dampratio are nullable too: buffer built, but passed only when set. - assert "bOverride_kv ? kvBuf : nullptr" in out - - -def test_cylinder_reasserts_inherited_gain_fields(real_rules, real_schema): - """Cylinder writes dynprm[0]/biasprm[0]/gainprm[0] unconditionally, so unset - timeconst/bias/area re-assert the inherited element field rather than a -1 - sentinel that would clobber the class default. diameter keeps its sentinel - because MuJoCo guards it with `>= 0`.""" - mjspec = _load_mjspec() - out = _setto_for("cylinder", real_rules, real_schema, mjspec) - assert "(bOverride_timeconst && timeconst.Num() > 0) ? (double)timeconst[0] : Element->dynprm[0]" in out - assert "bOverride_bias ? (double)bias : Element->biasprm[0]" in out - assert "bOverride_area ? (double)area : Element->gainprm[0]" in out - assert "bOverride_diameter ? (double)diameter : -1.0" in out diff --git a/Scripts/codegen/tests/test_should_emit_xml_enum_at_layer.py b/Scripts/codegen/tests/test_should_emit_xml_enum_at_layer.py deleted file mode 100644 index 8bff7f11..00000000 --- a/Scripts/codegen/tests/test_should_emit_xml_enum_at_layer.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks the two-case ``_should_emit_xml_enum_at_this_layer`` decision -the previous closure inside ``emit_schema_for_attrs`` encoded -implicitly: - - subtype-specific xml_enum attrs fire on the subtype's own emission - - "type"-like globally-excluded xml_enum attrs fire on the base - (apply_canonicalizations=True) layer.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_attr_in_attrs_list_fires_at_any_layer(): - """Subtype case — dcmotor.input is a subtype attr the base doesn't - carry; the subtype layer (apply_canonicalizations=False) owns it.""" - assert gen._should_emit_xml_enum_at_this_layer( - "input", attrs=["input"], global_excl=set(), - apply_canonicalizations=False, - ) - assert gen._should_emit_xml_enum_at_this_layer( - "input", attrs=["input"], global_excl=set(), - apply_canonicalizations=True, - ) - - -def test_globally_excluded_attr_fires_only_on_base_layer(): - """Always-at-base case — joint.type is globally excluded from - per-attr UPROPERTY emission but xml_enum still owns its import/ - export at the base. Subtype layer must NOT re-emit it (would cause - duplicate switch case + UHT shadowing).""" - assert gen._should_emit_xml_enum_at_this_layer( - "type", attrs=[], global_excl={"type"}, - apply_canonicalizations=True, - ) - assert not gen._should_emit_xml_enum_at_this_layer( - "type", attrs=[], global_excl={"type"}, - apply_canonicalizations=False, - ) - - -def test_unrelated_attr_never_fires(): - assert not gen._should_emit_xml_enum_at_this_layer( - "stranger", attrs=["unrelated"], global_excl={"other"}, - apply_canonicalizations=True, - ) diff --git a/Scripts/codegen/tests/test_strict_exit_code.py b/Scripts/codegen/tests/test_strict_exit_code.py deleted file mode 100644 index 08c9d0b3..00000000 --- a/Scripts/codegen/tests/test_strict_exit_code.py +++ /dev/null @@ -1,42 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Verifies that ``--strict`` (the default) maps fired diagnostics to -exit code 2. ``--no-strict`` lets the same rules surface diagnostics -without failing the run — local-triage workflow. - -These tests drive the public DiagBuffer surface directly rather than -shelling out so they stay deterministic across CI without a libclang -install (the introspect snapshot is read from disk regardless of CI -state).""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def test_strict_exit_2_when_diagnostics_fire(): - """A single diagnostic + ``--strict`` semantics yields exit 2 in - main()'s gate path. We simulate by adding to the buffer, flushing, - and reading the gate's input — ``_DIAGS_BUFFER.fired_count``.""" - gen._reset_diags() - gen._diag_add("[diagnostic] test", source="unit_test") - gen._diag_flush() - fired = gen._DIAGS_BUFFER.fired_count - assert fired == 1 - # The gate in main() is: `if args.strict and fired: return 2`. - # That predicate is true here -> exit code would be 2. - assert fired > 0 - - -def test_strict_exit_0_when_no_diagnostics(): - gen._reset_diags() - gen._diag_flush() - assert gen._DIAGS_BUFFER.fired_count == 0 - - -def test_reset_diags_clears_both_pending_and_fired_count(): - gen._diag_add("[diagnostic] alpha", source="unit_test") - gen._diag_flush() - assert gen._DIAGS_BUFFER.fired_count >= 1 - gen._reset_diags() - assert gen._DIAGS_BUFFER.fired_count == 0 - assert gen._DIAGS_BUFFER.pending == [] diff --git a/Scripts/codegen/tests/test_subclass_emission.py b/Scripts/codegen/tests/test_subclass_emission.py deleted file mode 100644 index 959bbed1..00000000 --- a/Scripts/codegen/tests/test_subclass_emission.py +++ /dev/null @@ -1,109 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Subclass file emission tests (single_uclass_per_file layout).""" - -from __future__ import annotations - -import json -import os -import tempfile - -import pytest - -from generate_ue_components import emit_subclass_files - - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_PLUGIN_ROOT = os.path.abspath(os.path.join(_HERE, "..", "..", "..")) - - -def _find_writes(writes, suffix): - return [w for w in writes if w.path.endswith(suffix)] - - -def test_actuator_subclass_count(real_schema, real_rules, real_mjspec): - cat_rules = real_rules["categories"]["actuator"] - writes = emit_subclass_files( - "actuator", cat_rules, real_schema, real_rules, - "/tmp/pub", "/tmp/priv", mjspec=real_mjspec, - ) - # 10 subtypes * 2 (h + cpp) = 20 writes. - assert len(writes) == 20 - # Every advertised class_name has an .h. - for st in cat_rules["subtypes"]: - assert _find_writes(writes, st["header"]), f"missing {st['header']}" - - -def test_sensor_subclass_count(real_schema, real_rules): - cat_rules = real_rules["categories"]["sensor"] - writes = emit_subclass_files( - "sensor", cat_rules, real_schema, real_rules, - "/tmp/pub", "/tmp/priv", - ) - # 49 subtypes * 2 = 98 writes. - assert len(writes) == 98 - - -def test_subclass_header_has_codegen_tags(real_schema, real_rules): - cat_rules = real_rules["categories"]["joint"] - writes = emit_subclass_files( - "joint", cat_rules, real_schema, real_rules, "/tmp/pub", "/tmp/priv", - ) - hinge_h = _find_writes(writes, "MjHingeJoint.h")[0] - assert "// --- CODEGEN_PROPERTIES_START ---" in hinge_h.content - assert "// --- CODEGEN_PROPERTIES_END ---" in hinge_h.content - assert "GENERATED_BODY()" in hinge_h.content - # Inherits base class. - assert ": public UMjJoint" in hinge_h.content - - -def test_subclass_source_has_super_export(real_schema, real_rules): - cat_rules = real_rules["categories"]["joint"] - writes = emit_subclass_files( - "joint", cat_rules, real_schema, real_rules, "/tmp/pub", "/tmp/priv", - ) - hinge_cpp = _find_writes(writes, "MjHingeJoint.cpp")[0] - # ImportFromXml chains to Super. - assert "Super::ImportFromXml(Node, CompilerSettings);" in hinge_cpp.content - # ExportTo chains to Super. - assert "Super::ExportTo(Element, Default);" in hinge_cpp.content - # Constructor sets the right enum. - assert "Type = EMjJointType::Hinge;" in hinge_cpp.content - - -def test_actuator_subclass_calls_mjs_set_to(real_schema, real_rules, real_mjspec): - cat_rules = real_rules["categories"]["actuator"] - writes = emit_subclass_files( - "actuator", cat_rules, real_schema, real_rules, - "/tmp/pub", "/tmp/priv", mjspec=real_mjspec, - ) - motor_cpp = _find_writes(writes, "MjMotorActuator.cpp")[0] - # The introspected setto emitter places ``mjs_setToMotor(Element);`` at - # the TOP of the CODEGEN_EXPORT block. Super::ExportTo is called BEFORE - # the codegen block (so that base attrs are written before subtype - # presets). Both orderings used to be claimed in different places of the - # generator — assert the current actual behaviour. - src = motor_cpp.content - set_idx = src.index("mjs_setToMotor(Element);") - super_idx = src.index("Super::ExportTo(Element, Default);") - # Super first, then setto preset. - assert super_idx < set_idx - # No ApplyRawOverrides call anywhere. - assert "ApplyRawOverrides" not in src - - -def test_new_geom_subtypes_emitted(real_schema, real_rules): - cat_rules = real_rules["categories"]["geom"] - writes = emit_subclass_files( - "geom", cat_rules, real_schema, real_rules, "/tmp/pub", "/tmp/priv", - ) - for new_subtype in ("MjEllipsoid.h", "MjPlane.h", "MjSdf.h"): - assert _find_writes(writes, new_subtype), f"missing geom subtype {new_subtype}" - - -def test_new_sensor_subtypes_emitted(real_schema, real_rules): - cat_rules = real_rules["categories"]["sensor"] - writes = emit_subclass_files( - "sensor", cat_rules, real_schema, real_rules, "/tmp/pub", "/tmp/priv", - ) - for new_subtype in ("MjUserSensor.h", "MjPluginSensor.h"): - assert _find_writes(writes, new_subtype), f"missing sensor subtype {new_subtype}" diff --git a/Scripts/codegen/tests/test_synthetic_categories.py b/Scripts/codegen/tests/test_synthetic_categories.py deleted file mode 100644 index 6ed0d6ee..00000000 --- a/Scripts/codegen/tests/test_synthetic_categories.py +++ /dev/null @@ -1,219 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -""" -Tests for the synthetic_categories emitter. - -Emits one banner-mode .h file per entry from an mjxmacro X-macro field -list. Covers MjStatistic, MjOption, the 6 MjVisual_* structs, plus -MjsCompilerOptions and MjsSpec. -""" - -from __future__ import annotations - -import os -import sys - -_HERE = os.path.dirname(os.path.abspath(__file__)) -_CODEGEN_DIR = os.path.dirname(_HERE) -if _CODEGEN_DIR not in sys.path: - sys.path.insert(0, _CODEGEN_DIR) - -from generate_ue_components import ( # noqa: E402 - _emit_synthetic_struct_files, - _ue_field_name, -) - - -def test_ue_field_name_pascal_cases_snake_input(): - assert _ue_field_name("timestep") == "Timestep" - assert _ue_field_name("ls_iterations") == "LsIterations" - assert _ue_field_name("sleep_tolerance") == "SleepTolerance" - # Single component stays capitalised. - assert _ue_field_name("extent") == "Extent" - - -def test_synthetic_struct_emits_uproperty_per_field(): - mjxmacro = { - "struct_fields": { - "TEST_FIELDS": [ - {"type": "mjtNum", "name": "timestep", "count": "1", "is_vec": False}, - {"type": "int", "name": "iterations", "count": "1", "is_vec": False}, - {"type": "float", "name": "gravity", "count": "3", "is_vec": True}, - ], - }, - } - def_ = { - "ue_struct_name": "FTestStruct", - "mjxmacro_block": "TEST_FIELDS", - "c_struct_type": "mjTest", - "public_header": "MuJoCo/Generated/MjTest.h", - "private_source": "MuJoCo/Generated/MjTest.cpp", - "category_label": "Test", - "exclude_fields": [], - "per_field_meta": {}, - "urlab_extra_fields": [], - } - writes = _emit_synthetic_struct_files( - "MjTest", def_, mjxmacro, public_root="/tmp/pub", private_root="/tmp/priv", - ) - assert len(writes) == 1 - h = writes[0].content - # USTRUCT outline. - assert "USTRUCT(BlueprintType)" in h - assert "struct URLAB_API FTestStruct" in h - # Each field gets its toggle + value pair. - assert "bOverride_Timestep" in h - assert "double Timestep" in h - assert "bOverride_Iterations" in h - assert "int32 Iterations" in h - # is_vec + count==3 -> FVector. - assert "FVector Gravity" in h - # Category labels use the rule's category_label. - assert 'Category = "MuJoCo|Test"' in h - # ApplyTo body assigns each field with a static_cast. - assert "Dst.timestep = static_cast(Timestep);" in h - assert "Dst.gravity[0] = static_cast(Gravity.X);" in h - - -def test_synthetic_struct_skips_excluded_fields(): - mjxmacro = { - "struct_fields": { - "TEST_FIELDS": [ - {"type": "int", "name": "keep_me", "count": "1", "is_vec": False}, - {"type": "int", "name": "drop_me", "count": "1", "is_vec": False}, - ], - }, - } - def_ = { - "ue_struct_name": "F", - "mjxmacro_block": "TEST_FIELDS", - "public_header": "P/F.h", - "private_source": "P/F.cpp", - "category_label": "C", - "exclude_fields": ["drop_me"], - "per_field_meta": {}, - } - writes = _emit_synthetic_struct_files("X", def_, mjxmacro, "/tmp/pub", "/tmp/priv") - h = writes[0].content - assert "KeepMe" in h - assert "DropMe" not in h - - -def test_synthetic_struct_field_rename_overrides_default(): - """``field_renames`` lets rules pin a non-default UE name (e.g. URLab's - historical CCD_Iterations vs default CcdIterations).""" - mjxmacro = { - "struct_fields": { - "T_FIELDS": [ - {"type": "int", "name": "ccd_iterations", "count": "1", "is_vec": False}, - ], - }, - } - def_ = { - "ue_struct_name": "FT", - "mjxmacro_block": "T_FIELDS", - "public_header": "P/T.h", - "private_source": "P/T.cpp", - "category_label": "T", - "field_renames": {"ccd_iterations": "CCD_Iterations"}, - } - writes = _emit_synthetic_struct_files("MjT", def_, mjxmacro, "/tmp/pub", "/tmp/priv") - h = writes[0].content - assert "CCD_Iterations" in h - assert "CcdIterations" not in h - - -def test_synthetic_struct_skips_when_mjxmacro_block_missing(): - """Defensive: a typo in mjxmacro_block produces a diagnostic, not a - crash, and emits no file.""" - from generate_ue_components import _DIAGS_BUFFER, _reset_diags - _reset_diags() - mjxmacro = {"struct_fields": {}} - def_ = { - "ue_struct_name": "F", "mjxmacro_block": "NONEXISTENT", - "public_header": "P/F.h", "private_source": "P/F.cpp", - "category_label": "C", - } - writes = _emit_synthetic_struct_files("M", def_, mjxmacro, "/tmp/pub", "/tmp/priv") - assert writes == [] - assert any("NONEXISTENT" in d.message for d in _DIAGS_BUFFER.pending) - _reset_diags() - - -def test_generated_enum_emits_uenum_per_entry(): - from generate_ue_components import _emit_generated_enum_file - mjspec = {"enums": { - "mjtIntegrator": {"mjINT_EULER": 0, "mjINT_RK4": 1, "mjINT_IMPLICIT": 2}, - }} - def_ = { - "public_header": "Generated/MjOptionEnums.h", - "enums": [{ - "ue_name": "EMjIntegrator", - "from_mj_enum": "mjtIntegrator", - "ue_member_from_mj": { - "mjINT_EULER": "Euler", - "mjINT_RK4": "RK4", - "mjINT_IMPLICIT": "Implicit", - }, - }], - } - writes = _emit_generated_enum_file("MjOptionEnums", def_, mjspec, "/tmp/pub") - assert len(writes) == 1 - h = writes[0].content - assert "UENUM(BlueprintType)" in h - assert "enum class EMjIntegrator : uint8" in h - assert "Euler = 0" in h - assert "RK4 = 1" in h - assert "Implicit = 2" in h - - -def test_generated_enum_skips_when_mj_enum_missing(): - from generate_ue_components import _emit_generated_enum_file, _DIAGS_BUFFER, _reset_diags - _reset_diags() - mjspec = {"enums": {}} - def_ = { - "public_header": "P/H.h", - "enums": [{"ue_name": "E", "from_mj_enum": "mjtMissing", "ue_member_from_mj": {}}], - } - writes = _emit_generated_enum_file("X", def_, mjspec, "/tmp/pub") - assert writes == [] - assert any("mjtMissing" in d.message for d in _DIAGS_BUFFER.pending) - _reset_diags() - - -def test_generated_enum_member_without_mapping_silently_skipped(): - """C enums often have sentinel constants (mjNGEOMTYPES) that don't - map to UE. Rule author omits them from ue_member_from_mj; emitter - skips them without diagnostics.""" - from generate_ue_components import _emit_generated_enum_file - mjspec = {"enums": {"mjtX": {"mjX_A": 0, "mjX_SENTINEL": 1, "mjX_B": 2}}} - def_ = { - "public_header": "P/H.h", - "enums": [{ - "ue_name": "EMjX", "from_mj_enum": "mjtX", - "ue_member_from_mj": {"mjX_A": "A", "mjX_B": "B"}, - }], - } - h = _emit_generated_enum_file("X", def_, mjspec, "/tmp/pub")[0].content - assert "A = 0" in h - # B retains its original value 2 (skipped sentinel doesn't renumber). - assert "B = 2" in h - assert "Sentinel" not in h - - -def test_real_snapshot_mjstatistic_pilot(): - """End-to-end: the shipped rule produces a syntactically plausible - .h file. UE compile separately verifies it actually builds.""" - import json - plugin_root = os.path.normpath(os.path.join(_HERE, "..", "..", "..")) - h_path = os.path.join(plugin_root, "Source", "URLab", "Public", - "MuJoCo", "Generated", "MjStatistic.h") - assert os.path.exists(h_path), "MjStatistic.h was not generated" - with open(h_path, "r", encoding="utf-8") as f: - h = f.read() - # Pilot fields from MJSTATISTIC_FIELDS. - for ue_name in ("Meaninertia", "Meanmass", "Meansize", "Extent", "Center"): - assert ue_name in h, f"missing field {ue_name}" - # ApplyTo is templated so it works against both mjStatistic and any - # alias type. Compile correctness is verified by UE's build step. - assert "template " in h - assert "void ApplyTo(TDst& Dst) const" in h diff --git a/Scripts/codegen/tests/test_type_shape_drift.py b/Scripts/codegen/tests/test_type_shape_drift.py deleted file mode 100644 index 99c4fd59..00000000 --- a/Scripts/codegen/tests/test_type_shape_drift.py +++ /dev/null @@ -1,168 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for the type-shape reconcile + new-attr fail-loud checks. - -Covers ``_classify_c_type`` (C-type kind classifier), ``_shapes_compatible`` -(UE-vs-C shape gate), ``_check_type_shape_drift`` (rule-vs-introspect -cross-check), and ``_check_new_attr_typing`` (every schema attr must -be typed or explicitly default-acknowledged).""" - -from __future__ import annotations - -import generate_ue_components as gen -import _codegen_core as _core -import _codegen_checks as _checks - -# Stitch test-only re-exports onto `gen` so the assertions below keep -# their compact ``gen._classify_c_type`` form. The orchestrator's import -# block intentionally no longer carries these — they're private to -# _codegen_core / _codegen_checks. -gen._classify_c_type = _core._classify_c_type -gen._shapes_compatible = _core._shapes_compatible -gen._check_type_shape_drift = _checks._check_type_shape_drift -gen._check_new_attr_typing = _checks._check_new_attr_typing - - -# ---------- _classify_c_type ------------------------------------------------ - -def test_classify_c_type_scalar_num(): - assert gen._classify_c_type("double", None) == ("scalar", "num") - assert gen._classify_c_type("mjtNum", None) == ("scalar", "num") - assert gen._classify_c_type("float", 1) == ("scalar", "num") - - -def test_classify_c_type_vec_dim(): - assert gen._classify_c_type("double [3]", 3) == ("vec", 3) - assert gen._classify_c_type("mjtNum [4]", 4) == ("vec", 4) - - -def test_classify_c_type_dynamic_vec_pointer(): - """mjFloatVec* / mjDoubleVec* are dynamic-length vectors, NOT - opaque pointers — must classify as ('array', None) so TArray - rules match cleanly.""" - assert gen._classify_c_type("mjDoubleVec *", None) == ("array", None) - assert gen._classify_c_type("mjFloatVec *", None) == ("array", None) - - -def test_classify_c_type_opaque_element_pointer(): - """mjsElement* is the parent-spec back-pointer URLab never models; - classify as opaque so the shape check skips it.""" - assert gen._classify_c_type("mjsElement *", None) == ("opaque", None) - - -def test_classify_c_type_string_pointer(): - assert gen._classify_c_type("mjString", None) == ("scalar", "string") - assert gen._classify_c_type("char *", None) == ("scalar", "string") - - -# ---------- _shapes_compatible ---------------------------------------------- - -def test_shapes_compatible_array_matches_vec(): - """TArray at the UE layer is the universal carrier for - fixed-size C arrays — codegen writes prop.Num() elements and the - common case is len(prop) <= C cap.""" - assert gen._shapes_compatible(("array", None), ("vec", 3)) - assert gen._shapes_compatible(("array", None), ("array", None)) - - -def test_shapes_compatible_scalar_vs_array_is_mismatch(): - assert not gen._shapes_compatible(("scalar", "num"), ("vec", 3)) - assert not gen._shapes_compatible(("array", None), ("scalar", "num")) - assert not gen._shapes_compatible(("vec", 3), ("vec", 4)) - - -# ---------- _check_type_shape_drift ---------------------------------------- - -def test_type_shape_drift_flags_scalar_vs_array_mismatch(): - """Rule says TArray but the introspect struct field is a - scalar mjtNum — diagnostic fires; emitted code would not compile - correctly.""" - rules = { - "type_mappings": {"stiffness": "TArray"}, - "categories": { - "joint": {"mjs_struct": "mjsJoint"}, - }, - } - mjspec = { - "introspect": { - "structs": { - "mjsJoint": { - "fields": [ - {"name": "stiffness", "c_type": "double", - "array_dim": None, "is_pointer": False}, - ], - }, - }, - }, - } - gen._check_type_shape_drift({}, rules, mjspec) - assert any("stiffness" in d.message and "mismatch" in d.message - for d in gen._DIAGS_BUFFER.pending) - - -def test_type_shape_drift_quiet_on_unmodeled_field(): - """A field listed in intentionally_unmodeled_mjs_fields is not - emitted by codegen — drift check must NOT fire on it.""" - rules = { - "type_mappings": {"damping": "TArray"}, - "categories": { - "flexcomp": {"mjs_struct": "mjsFlex"}, - }, - "intentionally_unmodeled_mjs_fields": { - "mjsFlex": ["damping"], - }, - } - mjspec = { - "introspect": { - "structs": { - "mjsFlex": { - "fields": [ - {"name": "damping", "c_type": "double", - "array_dim": None, "is_pointer": False}, - ], - }, - }, - }, - } - gen._check_type_shape_drift({}, rules, mjspec) - assert len(gen._DIAGS_BUFFER.pending) == 0 - - -# ---------- _check_new_attr_typing ----------------------------------------- - -def test_new_attr_typing_flags_unmapped_attr(): - schema = { - "joint": {"attrs": ["stiffness", "novel_attr"]}, - } - rules = { - "type_mappings": {"stiffness": "TArray"}, - "default_type": "float", - "global_exclusions": [], - "intentionally_default_typed_attrs": [], - "element_rules": {"joint": {}}, - "canonicalizations": {}, - "categories": { - "joint": {"schema_common_block": "joint.attrs"}, - }, - } - gen._check_new_attr_typing(schema, rules) - msgs = [d.message for d in gen._DIAGS_BUFFER.pending] - assert any("novel_attr" in m and "joint" in m for m in msgs), msgs - # stiffness is typed -> no diag. - assert not any("'stiffness'" in m for m in msgs) - - -def test_new_attr_typing_respects_intentional_default(): - schema = {"sensor": {"attrs": ["cutoff"]}} - rules = { - "type_mappings": {}, - "default_type": "float", - "global_exclusions": [], - "intentionally_default_typed_attrs": ["cutoff"], - "element_rules": {"sensor": {}}, - "canonicalizations": {}, - "categories": { - "sensor": {"schema_common_block": "sensor.attrs"}, - }, - } - gen._check_new_attr_typing(schema, rules) - assert len(gen._DIAGS_BUFFER.pending) == 0 diff --git a/Scripts/codegen/tests/test_ue_type_registry.py b/Scripts/codegen/tests/test_ue_type_registry.py deleted file mode 100644 index 2b938e51..00000000 --- a/Scripts/codegen/tests/test_ue_type_registry.py +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Locks the ``_UE_TYPE_INFO`` registry as the single source of truth -for every UE type codegen emits. Prevents the previous failure mode -where ``_attr_default_value`` / ``_UE_TYPE_FAMILY`` / ``Units``-meta -gates each declared their own type list and drifted apart.""" - -from __future__ import annotations - -from _codegen_core import ( - _UE_TYPE_INFO, _UE_TYPE_FAMILY, _UE_TYPES_ACCEPTING_UNITS_META, - _attr_default_value, _ue_type_accepts_units_meta, -) - - -def test_ue_type_family_is_view_over_registry(): - """``_UE_TYPE_FAMILY`` (drift-check input) must be a faithful view - over ``_UE_TYPE_INFO`` — same keys, same shape values.""" - assert set(_UE_TYPE_FAMILY.keys()) == set(_UE_TYPE_INFO.keys()) - for k, info in _UE_TYPE_INFO.items(): - assert _UE_TYPE_FAMILY[k] == info.shape, k - - -def test_units_meta_set_is_view_over_registry(): - expected = {k for k, v in _UE_TYPE_INFO.items() if v.accepts_units_meta} - assert _UE_TYPES_ACCEPTING_UNITS_META == expected - - -def test_attr_default_value_uses_registry(): - for ue_type, info in _UE_TYPE_INFO.items(): - assert _attr_default_value(ue_type) == info.default_init, ue_type - - -def test_attr_default_value_falls_back_for_novel_tarray(): - """Novel TArray<...> shapes (e.g. TArray) the registry - doesn't enumerate still default-construct empty — the catch-all - stays load-bearing for forward compatibility.""" - assert _attr_default_value("TArray") == "{}" - - -def test_ue_type_accepts_units_meta_matches_registry(): - for ue_type, info in _UE_TYPE_INFO.items(): - assert _ue_type_accepts_units_meta(ue_type) == info.accepts_units_meta, ue_type - # Unknown types must default to "no Units meta" — UHT would reject. - assert _ue_type_accepts_units_meta("UMjFoo*") is False diff --git a/Scripts/codegen/tests/test_view_struct.py b/Scripts/codegen/tests/test_view_struct.py deleted file mode 100644 index 4d542bc9..00000000 --- a/Scripts/codegen/tests/test_view_struct.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""View-struct emitter tests.""" - -from __future__ import annotations - -import generate_ue_components as gen -from generate_ue_components import emit_view_structs - - -def test_joint_view_includes_dof_fields(real_rules, real_mjxmacro): - views = emit_view_structs(real_rules, real_mjxmacro) - assert "JointView" in views - fields, bind = views["JointView"] - # Natural mjModel/JOINT field names (no renames — codegen pure). - assert "jnt_pos;" in fields - assert "jnt_axis;" in fields - assert "jnt_solref;" in fields - # mjModel/DOF block fields (mapped onto dof_adr). - assert "dof_damping;" in fields - assert "dof_solref;" in fields - # mjData fields. - assert "qpos;" in fields - assert "qvel;" in fields - assert "xanchor;" in fields - - -def test_joint_view_bind_uses_correct_indices(real_rules, real_mjxmacro): - views = emit_view_structs(real_rules, real_mjxmacro) - fields, bind = views["JointView"] - # Per-joint fields use id stride. - assert "jnt_pos = m->jnt_pos + id * 3;" in bind - assert "jnt_axis = m->jnt_axis + id * 3;" in bind - # solref uses mjNREF stride. - assert "jnt_solref = m->jnt_solref + id * mjNREF;" in bind - # DOF fields use dof_adr. - assert "dof_damping = m->dof_damping + dof_adr * 1;" in bind - # mjData qpos uses qpos_adr. - assert "qpos = d->qpos + qpos_adr * 1;" in bind - # Derived index vars declared at top of bind. - assert "const int qpos_adr = m->jnt_qposadr[id];" in bind - assert "const int dof_adr = m->jnt_dofadr[id];" in bind - - -def test_body_view_basic(real_rules, real_mjxmacro): - views = emit_view_structs(real_rules, real_mjxmacro) - assert "BodyView" in views - fields, bind = views["BodyView"] - # mjModel/BODY block emits pointer fields. - # `body_pos`, `body_quat`, `body_mass` should be present (verbatim names). - assert "body_pos" in fields or "BodyPos" in fields - # mjData fields present. - assert "xpos;" in fields - assert "xquat;" in fields - - -def test_view_has_one_field_per_mjxmacro_entry(real_rules, real_mjxmacro): - """Schema-completeness: every entry in the included mjmodel blocks - should appear as a pointer field on the view.""" - views = emit_view_structs(real_rules, real_mjxmacro) - fields, _ = views["JointView"] - joint_block = real_mjxmacro["mjmodel_pointers"]["MJMODEL_POINTERS_JOINT"] - field_renames = real_rules["views"]["JointView"].get("field_renames", {}) - for entry in joint_block: - name = entry["name"] - view_name = field_renames.get(name, name) - assert f" {view_name};" in fields, f"missing view field for {name} (-> {view_name})" - - -def test_stride_one_int_entries_emit_as_dereferenced_scalars(): - """Single-int entries (stride=="1", type=="int") emit as ``int X`` - with ``X = m->src[id]`` in the bind body — value semantics, not pointer.""" - rules = {"views": {"FooView": { - "obj_type": "mjOBJ_BODY", - "include_blocks": ["FOO_BLOCK"], - }}} - mjxmacro = {"mjmodel_pointers": {"FOO_BLOCK": [ - {"type": "int", "name": "foo_kind", "outer_dim": "nfoo", "stride": "1"}, - {"type": "int", "name": "foo_size_arr", "outer_dim": "nfoo", "stride": "3"}, - {"type": "mjtNum", "name": "foo_pos", "outer_dim": "nfoo", "stride": "3"}, - ]}} - views = emit_view_structs(rules, mjxmacro) - fields, bind = views["FooView"] - # Scalar int — dereferenced, not pointer. - assert " int foo_kind;" in fields - assert " foo_kind = m->foo_kind[id];" in bind - # Int but stride>1 — stays a pointer. - assert " int* foo_size_arr;" in fields - assert " foo_size_arr = m->foo_size_arr + id * 3;" in bind - # mjtNum stride>1 — pointer. - assert " mjtNum* foo_pos;" in fields - assert " foo_pos = m->foo_pos + id * 3;" in bind - - -def test_data_fields_bind_override(): - """``data_fields[name].bind_override`` short-circuits the standard - ``base + index_var * stride`` emit and drops the override verbatim - inside the BIND marker. Used by SensorView::sensordata (bounds-checked - slice) and ActuatorView::act (nullable lookup).""" - rules = {"views": {"BarView": { - "obj_type": "mjOBJ_GEOM", - "include_blocks": [], - "data_fields": { - "_note_skip_me": "comment-only; codegen must skip _note_* keys", - "plain": {"index_var": "id", "stride": "1"}, - "bounds_check": {"bind_override": - "bounds_check = (id < m->nfoo) ? d->bounds_check + id : nullptr;"}, - }, - }}} - mjxmacro = {"mjmodel_pointers": {}} - views = emit_view_structs(rules, mjxmacro) - fields, bind = views["BarView"] - # Standard data_field bind emitted. - assert " mjtNum* plain;" in fields - assert " plain = d->plain + id * 1;" in bind - # bind_override field still declared at the standard cpp_type. - assert " mjtNum* bounds_check;" in fields - # bind_override body dropped verbatim — no base+id*stride. - assert " bounds_check = (id < m->nfoo) ? d->bounds_check + id : nullptr;" in bind - # _note_* keys are documentation, not data fields. - assert "_note_skip_me" not in fields - assert "_note_skip_me" not in bind - - -def test_mjtbool_field_emits_mjtbool_pointer(): - """A field typed ``mjtBool`` (MuJoCo 3.9.0's selective mjtByte->mjtBool - migration of boolean fields like jnt_limited) must emit ``mjtBool*``, - not the silent ``mjtNum*`` fallback (an 8-byte stride over 1-byte data).""" - rules = {"views": {"BoolView": { - "obj_type": "mjOBJ_JOINT", - "include_blocks": ["BOOL_BLOCK"], - }}} - mjxmacro = {"mjmodel_pointers": {"BOOL_BLOCK": [ - {"type": "mjtBool", "name": "jnt_limited", "outer_dim": "njnt", "stride": "1"}, - ]}} - views = emit_view_structs(rules, mjxmacro) - fields, _ = views["BoolView"] - assert " mjtBool* jnt_limited;" in fields - assert " mjtNum* jnt_limited;" not in fields - - -def test_unmapped_view_field_type_fires_diagnostic(): - """An unknown C type must fire a diagnostic (while still falling back to - ``mjtNum*``), never default silently — a silent fallback is exactly how the - 3.9.0 ``mjtBool`` gap shipped a wrong pointer type with no warning.""" - gen._reset_diags() - rules = {"views": {"OddView": { - "obj_type": "mjOBJ_GEOM", - "include_blocks": ["ODD_BLOCK"], - }}} - mjxmacro = {"mjmodel_pointers": {"ODD_BLOCK": [ - {"type": "mjFutureType", "name": "geom_whatever", - "outer_dim": "ngeom", "stride": "3"}, - ]}} - views = gen.emit_view_structs(rules, mjxmacro) - fields, _ = views["OddView"] - assert " mjtNum* geom_whatever;" in fields # fallback still applies - assert any(d.source == "view_field_type" for d in gen._DIAGS_BUFFER.pending), \ - "unmapped view field type should fire a diagnostic, not default silently" - gen._reset_diags() diff --git a/Scripts/codegen/tests/test_xml_passthrough_emit.py b/Scripts/codegen/tests/test_xml_passthrough_emit.py deleted file mode 100644 index d01d5b34..00000000 --- a/Scripts/codegen/tests/test_xml_passthrough_emit.py +++ /dev/null @@ -1,75 +0,0 @@ -# Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. -"""Tests for ``emit_xml_passthrough_body``. Categories with -``xml_passthrough_emission: true`` (flexcomp) build an MJCF XML string -instead of writing through the mjs spec API — the codegen owns the -per-attr formatter so a new schema attr flows end-to-end. The body -emits ``name="value"`` substrings for every override-tracked -UPROPERTY.""" - -from __future__ import annotations - -import generate_ue_components as gen - - -def _rules(attr: str, ue_type: str) -> dict: - return { - "default_type": "float", - "type_mappings": {attr: ue_type}, - "element_rules": {"thing": {}}, - } - - -def test_float_attr_emits_printf_with_toggle_guard(): - body = gen.emit_xml_passthrough_body(["stiffness"], _rules("stiffness", "float"), element_name="thing") - assert "FString Out;" in body - assert "if (bOverride_stiffness)" in body - assert '" stiffness=\\"%f\\""' in body - assert "return Out;" in body - - -def test_int32_attr_emits_printf_with_d_format(): - body = gen.emit_xml_passthrough_body(["group"], _rules("group", "int32"), element_name="thing") - assert "if (bOverride_group)" in body - assert '" group=\\"%d\\""' in body - - -def test_bool_attr_emits_ternary_string(): - body = gen.emit_xml_passthrough_body(["limited"], _rules("limited", "bool"), element_name="thing") - assert 'TEXT("true") : TEXT("false")' in body - assert '" limited=\\"%s\\""' in body - - -def test_tarray_float_emits_space_separated_loop(): - body = gen.emit_xml_passthrough_body(["range"], _rules("range", "TArray"), element_name="thing") - assert "if (bOverride_range && range.Num() > 0)" in body - assert "for (int32 i = 0; i < range.Num(); ++i)" in body - assert 'if (i > 0) Out += TEXT(" ");' in body - - -def test_flinearcolor_emits_four_component_format(): - body = gen.emit_xml_passthrough_body(["rgba"], _rules("rgba", "FLinearColor"), element_name="thing") - assert "rgba.R" in body and "rgba.G" in body and "rgba.B" in body and "rgba.A" in body - assert '" rgba=\\"%f %f %f %f\\""' in body - - -def test_name_attr_is_skipped_by_caller_owned_logic(): - """The caller handles ``name`` (prefix / MjName logic); the body must - not duplicate it.""" - body = gen.emit_xml_passthrough_body(["name", "stiffness"], _rules("stiffness", "float"), element_name="thing") - assert '" name=' not in body - assert "stiffness" in body - - -def test_global_exclusion_skips_attr(): - rules = {**_rules("stiffness", "float"), "global_exclusions": ["stiffness"]} - body = gen.emit_xml_passthrough_body(["stiffness", "group"], rules, element_name="thing") - assert "stiffness" not in body - # 'group' still passes if it's typed (it isn't here -> falls back to default_type 'float'). - - -def test_unsupported_type_emits_todo_marker(): - """Novel UE types produce a TODO comment so a reviewer sees the gap - instead of a silent missing attr.""" - body = gen.emit_xml_passthrough_body(["mystery"], _rules("mystery", "FQuat"), element_name="thing") - assert "TODO(xml_passthrough)" in body - assert '"mystery"' in body and "FQuat" in body diff --git a/Scripts/format.ps1 b/Scripts/format.ps1 index 1cafe713..a25100ed 100644 --- a/Scripts/format.ps1 +++ b/Scripts/format.ps1 @@ -4,9 +4,10 @@ # Scripts/format.ps1 # format Source/ in place # Scripts/format.ps1 -Check # exit 1 if anything is unformatted (CI) # -# Codegen-managed files are also formatted here, but the codegen emits the -# same formatting (see _clang_format_content in generate_ue_components.py), so -# running this after a regen is a no-op on generated files. +# `*.gen.h` / `*.gen.cpp` are skipped. The emitter owns their bytes and the +# codegen drift gate compares them exactly, so formatting them here would be +# reverted by the next regen and the two gates would each undo the other. The +# emitter's own output is the standard those files are held to. # # Requires clang-format 19.x (VS2022's bundled LLVM, or set $env:CLANG_FORMAT). param([switch]$Check) @@ -25,7 +26,8 @@ function Resolve-ClangFormat { $cf = Resolve-ClangFormat $files = Get-ChildItem -Path (Join-Path $root 'Source') -Recurse -Include *.h, *.cpp -File | - Where-Object { $_.FullName -notmatch '[\\/](Intermediate|Binaries)[\\/]' } + Where-Object { $_.FullName -notmatch '[\\/](Intermediate|Binaries)[\\/]' } | + Where-Object { $_.Name -notmatch '\.gen\.(h|cpp)$' } Write-Host "clang-format: $cf" Write-Host "files: $($files.Count)" diff --git a/Scripts/format.sh b/Scripts/format.sh index 65fe5175..737f1740 100755 --- a/Scripts/format.sh +++ b/Scripts/format.sh @@ -5,9 +5,10 @@ # Scripts/format.sh # format Source/ in place # Scripts/format.sh --check # exit 1 if anything is unformatted (CI) # -# Codegen-managed files are also formatted here, but the codegen emits the -# same formatting (see _clang_format_content in generate_ue_components.py), so -# running this after a regen is a no-op on generated files. +# `*.gen.h` / `*.gen.cpp` are skipped. The emitter owns their bytes and the +# codegen drift gate compares them exactly, so formatting them here would be +# reverted by the next regen and the two gates would each undo the other. The +# emitter's own output is the standard those files are held to. # # Requires clang-format 19.x. Set $CLANG_FORMAT, or have clang-format(-19) on PATH. set -euo pipefail @@ -23,7 +24,8 @@ resolve_cf() { CF="$(resolve_cf)" mapfile -t FILES < <(find "${ROOT}/Source" -type f \( -name '*.h' -o -name '*.cpp' \) \ - -not -path '*/Intermediate/*' -not -path '*/Binaries/*' | sort) + -not -path '*/Intermediate/*' -not -path '*/Binaries/*' \ + -not -name '*.gen.h' -not -name '*.gen.cpp' | sort) echo "clang-format: ${CF}" echo "files: ${#FILES[@]}" diff --git a/Scripts/regen_ue_profile.ps1 b/Scripts/regen_ue_profile.ps1 new file mode 100644 index 00000000..cec87b8f --- /dev/null +++ b/Scripts/regen_ue_profile.ps1 @@ -0,0 +1,36 @@ +# Generated by protospec_gen.emit_ue -- do not edit. +# +# Regenerate URLab's MuJoCo spec profile from the MJCF schema. +# +# The generator lives in the plugin (protospec/), and reads the schema from the +# MuJoCo submodule: a MuJoCo bump moves the grammar and this regenerates against +# it, without a ProtoSpec change dragging MuJoCo's own build along with it. +# +# .\Scripts\regen_ue_profile.ps1 # rewrite Source/URLab/*/MuJoCo/Gen +# .\Scripts\regen_ue_profile.ps1 -Check # fail if it is out of date +[CmdletBinding()] +param( + [switch]$Check, + [string]$ProtospecRoot +) +$ErrorActionPreference = "Stop" + +$UrlabRoot = Split-Path -Parent $PSScriptRoot +if (-not $ProtospecRoot) { + $ProtospecRoot = Join-Path $UrlabRoot "protospec" +} +if (-not (Test-Path (Join-Path $ProtospecRoot "protospec_gen"))) { + throw "No ProtoSpec generator at $ProtospecRoot. Pass -ProtospecRoot." +} + +$Args = @("run", "python", "-m", "protospec_gen.emit_ue", "--out", $UrlabRoot) +if ($Check) { $Args += "--check" } + +Push-Location $ProtospecRoot +try { + & uv @Args + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } +} +finally { + Pop-Location +} diff --git a/Scripts/regen_ue_profile.sh b/Scripts/regen_ue_profile.sh new file mode 100644 index 00000000..72d003dc --- /dev/null +++ b/Scripts/regen_ue_profile.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Generated by protospec_gen.emit_ue -- do not edit. +# +# Regenerate URLab's MuJoCo spec profile from the MJCF schema. +# +# The generator lives in the plugin (protospec/), and reads the schema from the +# MuJoCo submodule: a MuJoCo bump moves the grammar and this regenerates against +# it, without a ProtoSpec change dragging MuJoCo's own build along with it. +# +# ./Scripts/regen_ue_profile.sh # rewrite Source/URLab/*/MuJoCo/Gen +# ./Scripts/regen_ue_profile.sh --check # fail if it is out of date +set -euo pipefail + +check="" +protospec_root="" +while [ $# -gt 0 ]; do + case "$1" in + --check) check="--check"; shift ;; + --protospec-root) protospec_root="$2"; shift 2 ;; + *) echo "unknown argument: $1" >&2; exit 2 ;; + esac +done + +script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +urlab_root="$(dirname "$script_dir")" +if [ -z "$protospec_root" ]; then + protospec_root="$urlab_root/protospec" +fi +if [ ! -d "$protospec_root/protospec_gen" ]; then + echo "No ProtoSpec generator at $protospec_root." >&2 + echo "Pass --protospec-root." >&2 + exit 1 +fi + +cd "$protospec_root" +exec uv run python -m protospec_gen.emit_ue --out "$urlab_root" ${check:+$check} diff --git a/Source/URLab/Private/Bridge/AssetCache.cpp b/Source/URLab/Private/Bridge/AssetCache.cpp new file mode 100644 index 00000000..f846f2b3 --- /dev/null +++ b/Source/URLab/Private/Bridge/AssetCache.cpp @@ -0,0 +1,314 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/AssetCache.h" + +#include "HAL/FileManager.h" +#include "HAL/PlatformMisc.h" +#include "HAL/PlatformProcess.h" +#include "Misc/DateTime.h" +#include "Misc/FileHelper.h" +#include "Misc/Guid.h" +#include "Misc/Paths.h" + +namespace +{ +// --- SHA-256 (FIPS 180-4), self-contained ----------------------------------- +// UE's Core exposes SHA-1 (FSHA1) but no public SHA-256; the upload protocol is +// content-addressed on SHA-256, so a small standard implementation lives here. +// It hashes raw bytes only (no secrets), so this is a plain digest, not a MAC. +struct FSha256 +{ + uint32 State[8]; + uint64 BitLen = 0; + uint8 Block[64]; + int32 BlockLen = 0; + + FSha256() { Reset(); } + + void Reset() + { + State[0] = 0x6a09e667u; + State[1] = 0xbb67ae85u; + State[2] = 0x3c6ef372u; + State[3] = 0xa54ff53au; + State[4] = 0x510e527fu; + State[5] = 0x9b05688cu; + State[6] = 0x1f83d9abu; + State[7] = 0x5be0cd19u; + BitLen = 0; + BlockLen = 0; + } + + static uint32 Rotr(uint32 X, uint32 N) { return (X >> N) | (X << (32 - N)); } + + void Transform() + { + static const uint32 K[64] = { + 0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u, 0x3956c25bu, 0x59f111f1u, + 0x923f82a4u, 0xab1c5ed5u, 0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u, + 0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u, 0xe49b69c1u, 0xefbe4786u, + 0x0fc19dc6u, 0x240ca1ccu, 0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau, + 0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u, 0xc6e00bf3u, 0xd5a79147u, + 0x06ca6351u, 0x14292967u, 0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u, + 0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u, 0xa2bfe8a1u, 0xa81a664bu, + 0xc24b8b70u, 0xc76c51a3u, 0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u, + 0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u, 0x391c0cb3u, 0x4ed8aa4au, + 0x5b9cca4fu, 0x682e6ff3u, 0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u, + 0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u}; + + uint32 W[64]; + for (int32 i = 0; i < 16; ++i) + { + W[i] = (uint32(Block[i * 4]) << 24) | (uint32(Block[i * 4 + 1]) << 16) + | (uint32(Block[i * 4 + 2]) << 8) | uint32(Block[i * 4 + 3]); + } + for (int32 i = 16; i < 64; ++i) + { + const uint32 S0 = Rotr(W[i - 15], 7) ^ Rotr(W[i - 15], 18) ^ (W[i - 15] >> 3); + const uint32 S1 = Rotr(W[i - 2], 17) ^ Rotr(W[i - 2], 19) ^ (W[i - 2] >> 10); + W[i] = W[i - 16] + S0 + W[i - 7] + S1; + } + + uint32 A = State[0], B = State[1], C = State[2], D = State[3]; + uint32 E = State[4], F = State[5], G = State[6], H = State[7]; + for (int32 i = 0; i < 64; ++i) + { + const uint32 S1 = Rotr(E, 6) ^ Rotr(E, 11) ^ Rotr(E, 25); + const uint32 Ch = (E & F) ^ (~E & G); + const uint32 T1 = H + S1 + Ch + K[i] + W[i]; + const uint32 S0 = Rotr(A, 2) ^ Rotr(A, 13) ^ Rotr(A, 22); + const uint32 Maj = (A & B) ^ (A & C) ^ (B & C); + const uint32 T2 = S0 + Maj; + H = G; + G = F; + F = E; + E = D + T1; + D = C; + C = B; + B = A; + A = T1 + T2; + } + State[0] += A; + State[1] += B; + State[2] += C; + State[3] += D; + State[4] += E; + State[5] += F; + State[6] += G; + State[7] += H; + } + + void Update(const uint8* Data, int32 Size) + { + for (int32 i = 0; i < Size; ++i) + { + Block[BlockLen++] = Data[i]; + if (BlockLen == 64) + { + Transform(); + BitLen += 512; + BlockLen = 0; + } + } + } + + FString Finalize() + { + const uint64 TotalBits = BitLen + uint64(BlockLen) * 8; + Block[BlockLen++] = 0x80; + if (BlockLen > 56) + { + while (BlockLen < 64) + Block[BlockLen++] = 0; + Transform(); + BlockLen = 0; + } + while (BlockLen < 56) + Block[BlockLen++] = 0; + for (int32 i = 7; i >= 0; --i) + Block[BlockLen++] = uint8((TotalBits >> (i * 8)) & 0xff); + Transform(); + + FString Hex; + Hex.Reserve(64); + const TCHAR* Digits = TEXT("0123456789abcdef"); + for (int32 i = 0; i < 8; ++i) + { + for (int32 Shift = 28; Shift >= 0; Shift -= 4) + Hex.AppendChar(Digits[(State[i] >> Shift) & 0xf]); + } + return Hex; + } +}; +} // namespace + +FURLabAssetCache::FURLabAssetCache(const FString& InRoot) + : Root(InRoot) +{ +} + +FURLabAssetCache& FURLabAssetCache::Get() +{ + static FURLabAssetCache Instance(ResolveCacheRoot()); + return Instance; +} + +FString FURLabAssetCache::ResolveCacheRoot() +{ + const FString Override = FPlatformMisc::GetEnvironmentVariable(TEXT("URLAB_ASSET_CACHE")); + if (!Override.IsEmpty()) + return Override; + +#if PLATFORM_WINDOWS + FString Base = FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")); + if (Base.IsEmpty()) + Base = FPlatformProcess::UserSettingsDir(); +#else + FString Base = FPlatformMisc::GetEnvironmentVariable(TEXT("XDG_CACHE_HOME")); + if (Base.IsEmpty()) + Base = FPaths::Combine(FPlatformMisc::GetEnvironmentVariable(TEXT("HOME")), TEXT(".cache")); +#endif + return FPaths::Combine(Base, TEXT("URLab"), TEXT("cache")); +} + +FString FURLabAssetCache::Sha256Hex(const uint8* Data, int32 Size) +{ + FSha256 Ctx; + if (Data && Size > 0) + Ctx.Update(Data, Size); + return Ctx.Finalize(); +} + +bool FURLabAssetCache::IsValidSha256Hex(const FString& Candidate) +{ + if (Candidate.Len() != 64) + return false; + for (const TCHAR C : Candidate) + { + const bool bHex = (C >= '0' && C <= '9') || (C >= 'a' && C <= 'f'); + if (!bHex) + return false; + } + return true; +} + +FString FURLabAssetCache::BlobPath(const FString& Sha256Hex) const +{ + return FPaths::Combine(Root, TEXT("blobs"), Sha256Hex.Left(2), Sha256Hex); +} + +bool FURLabAssetCache::Has(const FString& Sha256Hex) const +{ + if (!IsValidSha256Hex(Sha256Hex)) + return false; + return IFileManager::Get().FileExists(*BlobPath(Sha256Hex)); +} + +bool FURLabAssetCache::GetPath(const FString& Sha256Hex, FString& OutPath) const +{ + if (!Has(Sha256Hex)) + return false; + OutPath = BlobPath(Sha256Hex); + return true; +} + +bool FURLabAssetCache::Put(const FString& Sha256Hex, const uint8* Data, int32 Size) +{ + if (!IsValidSha256Hex(Sha256Hex)) + return false; + + const FString Final = BlobPath(Sha256Hex); + if (IFileManager::Get().FileExists(*Final)) + return true; // immutable content already present + + const FString Dir = FPaths::GetPath(Final); + IFileManager::Get().MakeDirectory(*Dir, /*Tree=*/true); + + // Temp file in the SAME directory so the rename is an atomic same-volume + // move. A distinct GUID per writer keeps concurrent writers of the same + // hash from clobbering each other's temp file. + const FString Temp = Final + TEXT(".") + FGuid::NewGuid().ToString(EGuidFormats::Digits) + TEXT(".tmp"); + + TArray Payload; + if (Data && Size > 0) + Payload.Append(Data, Size); + if (!FFileHelper::SaveArrayToFile(Payload, *Temp)) + return false; + + // bReplace=true: content is identical across writers, so replacing a blob a + // racing process just created is harmless and keeps this call idempotent. + if (!IFileManager::Get().Move(*Final, *Temp, /*bReplace=*/true)) + { + // A concurrent writer may have won the rename; treat an existing final + // blob as success and drop our temp copy. + IFileManager::Get().Delete(*Temp, /*RequireExists=*/false, /*EvenReadOnly=*/true); + return IFileManager::Get().FileExists(*Final); + } + return true; +} + +int64 FURLabAssetCache::EvictToBudget(int64 MaxBytes) +{ + if (MaxBytes <= 0) + { + const FString Env = FPlatformMisc::GetEnvironmentVariable(TEXT("URLAB_ASSET_CACHE_MAX_BYTES")); + MaxBytes = Env.IsEmpty() ? 0 : FCString::Atoi64(*Env); + } + if (MaxBytes <= 0) + return 0; // eviction disabled + + const FString BlobsDir = FPaths::Combine(Root, TEXT("blobs")); + + struct FBlob + { + FString Path; + int64 Size = 0; + FDateTime Modified; + }; + TArray Blobs; + int64 Total = 0; + IFileManager::Get().IterateDirectoryStatRecursively(*BlobsDir, + [&Blobs, &Total](const TCHAR* Path, const FFileStatData& Stat) { + if (!Stat.bIsDirectory && Stat.FileSize > 0) + { + Blobs.Add({Path, Stat.FileSize, Stat.ModificationTime}); + Total += Stat.FileSize; + } + return true; + }); + + if (Total <= MaxBytes) + return 0; + + Blobs.Sort([](const FBlob& A, const FBlob& B) { return A.Modified < B.Modified; }); + + int64 Freed = 0; + for (const FBlob& B : Blobs) + { + if (Total - Freed <= MaxBytes) + break; + if (IFileManager::Get().Delete(*B.Path, /*RequireExists=*/false, /*EvenReadOnly=*/true)) + Freed += B.Size; + } + return Freed; +} diff --git a/Source/URLab/Private/Bridge/BridgeServer.cpp b/Source/URLab/Private/Bridge/BridgeServer.cpp index 0ce9ab5c..54335eca 100644 --- a/Source/URLab/Private/Bridge/BridgeServer.cpp +++ b/Source/URLab/Private/Bridge/BridgeServer.cpp @@ -7,11 +7,127 @@ #include "Bridge/RpcDispatcher.h" #include "Transport/ZmqRpcTransport.h" #include "Transport/ShmRpcTransport.h" +#include "Transport/RpcTransport.h" +#include "Transport/PublishTransport.h" +#include "Transport/MjExternalTransportProvider.h" #include "MuJoCo/Core/AMjManager.h" #include "Utils/URLabLogging.h" +#include "HAL/IConsoleManager.h" +#include "HAL/PlatformMisc.h" +#include "Engine/Engine.h" +#include "Misc/CommandLine.h" +#include "Misc/Parse.h" +#include "Misc/Guid.h" +#include "HAL/PlatformTime.h" + +namespace +{ +/** Parse the trailing port from a "tcp://host:port" endpoint. Returns 0 when + * no numeric port is present. */ +int32 ParseEndpointPort(const FString& Endpoint) +{ + int32 ColonIdx = INDEX_NONE; + if (Endpoint.FindLastChar(TEXT(':'), ColonIdx)) + { + const FString PortStr = Endpoint.Mid(ColonIdx + 1); + if (!PortStr.IsEmpty() && PortStr.IsNumeric()) + return FCString::Atoi(*PortStr); + } + return 0; +} + +/** Resolve the step-RPC endpoint, letting an operator override the port per + * editor instance without editing the project INI. Precedence: command-line + * `-URLabStepPort=N`, then the `URLAB_STEP_PORT` environment variable, then + * the requested endpoint unchanged. This is what lets many render-server + * editors run side by side on one host, each on its own port. */ +FString ResolveStepEndpoint(const FString& Requested) +{ + int32 OverridePort = 0; + if (!FParse::Value(FCommandLine::Get(), TEXT("URLabStepPort="), OverridePort)) + { + const FString Env = FPlatformMisc::GetEnvironmentVariable(TEXT("URLAB_STEP_PORT")); + if (!Env.IsEmpty() && Env.IsNumeric()) + OverridePort = FCString::Atoi(*Env); + } + if (OverridePort <= 0) + return Requested; + + int32 ColonIdx = INDEX_NONE; + if (Requested.FindLastChar(TEXT(':'), ColonIdx)) + return FString::Printf(TEXT("%s:%d"), *Requested.Left(ColonIdx), OverridePort); + return FString::Printf(TEXT("tcp://0.0.0.0:%d"), OverridePort); +} +} // namespace UURLabBridgeServer::UURLabBridgeServer() = default; +void UURLabBridgeServer::EnsureDispatcher() +{ + if (!Dispatcher.IsValid()) + { + Dispatcher = MakeUnique(); + Dispatcher->SetOwningBridge(this); + } +} + +void UURLabBridgeServer::ApplyPerformanceOverrides() +{ + if (bPacingOverridden || !GEngine) + return; + + auto Override = [](const TCHAR* Name, const TCHAR* Command, float& OutSaved, bool& OutHad) { + if (IConsoleVariable* CVar = IConsoleManager::Get().FindConsoleVariable(Name)) + { + OutHad = true; + OutSaved = CVar->GetFloat(); + } + GEngine->Exec(nullptr, Command); + }; + + Override(TEXT("r.VSync"), TEXT("r.VSync 0"), SavedVSync, bHadVSync); +#if WITH_EDITOR + Override(TEXT("r.VSyncEditor"), TEXT("r.VSyncEditor 0"), SavedVSyncEditor, bHadVSyncEditor); +#endif + Override(TEXT("t.MaxFPS"), TEXT("t.MaxFPS 240"), SavedMaxFPS, bHadMaxFPS); + + // Keep rendering when the editor is not the foreground window. Without this + // Slate throttles the whole app to a few FPS in the background, which starves + // camera capture/readback -- a headless RPC client then sees near-zero frame + // throughput even though the bridge is serving. + Override(TEXT("Slate.bAllowThrottling"), TEXT("Slate.bAllowThrottling 0"), + SavedSlateThrottle, bHadSlateThrottle); + Override(TEXT("t.IdleWhenNotForeground"), TEXT("t.IdleWhenNotForeground 0"), + SavedIdleWhenNotForeground, bHadIdleWhenNotForeground); + + bPacingOverridden = true; + UE_LOG(LogURLabNet, Log, + TEXT("UURLabBridgeServer: disabled editor frame pacing + background throttling " + "while serving (VSync off, MaxFPS 240, Slate throttling off)")); +} + +void UURLabBridgeServer::RestorePerformanceOverrides() +{ + if (!bPacingOverridden || !GEngine) + return; + + auto Restore = [](const TCHAR* Name, float Value, bool bHad) { + if (bHad) + GEngine->Exec(nullptr, *FString::Printf(TEXT("%s %g"), Name, Value)); + }; + + Restore(TEXT("r.VSync"), SavedVSync, bHadVSync); +#if WITH_EDITOR + Restore(TEXT("r.VSyncEditor"), SavedVSyncEditor, bHadVSyncEditor); +#endif + Restore(TEXT("t.MaxFPS"), SavedMaxFPS, bHadMaxFPS); + Restore(TEXT("Slate.bAllowThrottling"), SavedSlateThrottle, bHadSlateThrottle); + Restore(TEXT("t.IdleWhenNotForeground"), SavedIdleWhenNotForeground, bHadIdleWhenNotForeground); + + bPacingOverridden = false; + UE_LOG(LogURLabNet, Log, TEXT("UURLabBridgeServer: restored editor frame pacing")); +} + void UURLabBridgeServer::BeginDestroy() { Stop(); @@ -20,16 +136,13 @@ void UURLabBridgeServer::BeginDestroy() void UURLabBridgeServer::Start(const FString& StepEndpoint) { - if (!Dispatcher.IsValid()) - { - Dispatcher = MakeUnique(); - } + EnsureDispatcher(); // Empty endpoint: dispatcher only, no transports (test path). if (StepEndpoint.IsEmpty()) return; - EnsureZmqBound(StepEndpoint); + EnsureZmqBound(ResolveStepEndpoint(StepEndpoint)); } bool UURLabBridgeServer::EnsureZmqBound(const FString& Endpoint) @@ -37,10 +150,7 @@ bool UURLabBridgeServer::EnsureZmqBound(const FString& Endpoint) if (Endpoint.IsEmpty()) return false; - if (!Dispatcher.IsValid()) - { - Dispatcher = MakeUnique(); - } + EnsureDispatcher(); for (const TObjectPtr& T : RpcTransports) { @@ -49,7 +159,9 @@ bool UURLabBridgeServer::EnsureZmqBound(const FString& Endpoint) return true; } - UURLabZmqRpcTransport* Zmq = NewObject(this, TEXT("BridgeZmqTransport")); + // NAME_None: let UE pick a fresh unique name. A fixed name collides on + // rebind while the previous worker is still tearing down. + UURLabZmqRpcTransport* Zmq = NewObject(this, NAME_None); Zmq->StepEndpoint = Endpoint; Zmq->SetOwningBridge(this); if (!Zmq->TransportInit()) @@ -59,6 +171,7 @@ bool UURLabBridgeServer::EnsureZmqBound(const FString& Endpoint) return false; } RpcTransports.Add(Zmq); + ApplyPerformanceOverrides(); UE_LOG(LogURLabNet, Log, TEXT("UURLabBridgeServer: ZMQ REP bound at %s"), *Endpoint); return true; @@ -66,10 +179,7 @@ bool UURLabBridgeServer::EnsureZmqBound(const FString& Endpoint) bool UURLabBridgeServer::EnsureShmBound(const FString& SessionId) { - if (!Dispatcher.IsValid()) - { - Dispatcher = MakeUnique(); - } + EnsureDispatcher(); const FString Sid = SessionId.IsEmpty() ? FString(TEXT("live")) : SessionId; @@ -86,8 +196,24 @@ bool UURLabBridgeServer::EnsureShmBound(const FString& SessionId) } } - UURLabShmRpcTransport* Shm = NewObject(this, TEXT("BridgeShmTransport")); + // SHM session naming includes the instance's step port for traceability. + // SHM currently depends on ZMQ being bound first: the step port is read from + // the ZMQ transport's bound endpoint. If ZMQ is not running (e.g. a + // same-host-only deployment that skips TCP), InstancePort stays 0 and the + // session name falls back to the bare session id. + int32 StepPort = 0; + for (const TObjectPtr& T : RpcTransports) + { + if (UURLabZmqRpcTransport* Zmq = Cast(T)) + { + StepPort = ParseEndpointPort(Zmq->StepEndpoint); + break; + } + } + + UURLabShmRpcTransport* Shm = NewObject(this, NAME_None); Shm->SessionId = SessionId; // empty -> defaults to "live" inside Init + Shm->InstancePort = StepPort; Shm->SetOwningBridge(this); if (!Shm->TransportInit()) { @@ -96,11 +222,91 @@ bool UURLabBridgeServer::EnsureShmBound(const FString& SessionId) return false; } RpcTransports.Add(Shm); + ApplyPerformanceOverrides(); UE_LOG(LogURLabNet, Log, TEXT("UURLabBridgeServer: SHM RPC bound (session=%s)"), *Sid); return true; } +bool UURLabBridgeServer::EnsureExternalTransportsBound() +{ + // The concrete transports live in a separate, optional module that installs + // factory hooks at startup. Absent that module the hooks are unbound, so there + // is nothing to bind and the core names no external transport type. + if (!FMjExternalTransportProvider::HasControlRpcTransport()) + { + UE_LOG(LogURLabNet, Log, + TEXT("UURLabBridgeServer: external control transport unavailable; " + "EnsureExternalTransportsBound is a no-op.")); + return false; + } + + EnsureDispatcher(); + + // RPC / control leg: one external executor transport, persisting across PIE + // like the other RPC transports. Its TransportInit brings up the process-wide + // context; a false return means the runtime is unavailable, so there is + // nothing to bind. Identified by its transport name so the core needs no cast. + bool bHaveRpc = false; + for (const TObjectPtr& T : RpcTransports) + { + if (T && T->GetTransportName() == TEXT("ros2-rpc")) + { + bHaveRpc = true; + break; + } + } + if (!bHaveRpc) + { + UURLabRpcTransport* External = FMjExternalTransportProvider::MakeControlRpcTransport.Execute(this); + if (!External || !External->TransportInit()) + { + UE_LOG(LogURLabNet, Log, + TEXT("UURLabBridgeServer: external control runtime unavailable; " + "EnsureExternalTransportsBound is a no-op.")); + return false; + } + RpcTransports.Add(External); + ApplyPerformanceOverrides(); + UE_LOG(LogURLabNet, Log, TEXT("UURLabBridgeServer: control RPC transport bound")); + } + + // Publish / fan-out leg: registered with the live manager, which owns per-PIE + // publish transports and tears them down in EndPlay. When no manager is live + // yet the publish leg is deferred to the next call with one present. + if (AAMjManager* Manager = GetActiveManager()) + { + bool bHavePub = false; + for (const TObjectPtr& T : Manager->ManagerOwnedPublishTransports) + { + if (T && T->GetTransportName() == TEXT("ros2-pub")) + { + bHavePub = true; + break; + } + } + if (!bHavePub && FMjExternalTransportProvider::MakeStatePublishTransport.IsBound()) + { + UURLabPublishTransport* Pub = + FMjExternalTransportProvider::MakeStatePublishTransport.Execute(Manager); + if (Pub && Pub->TransportInit()) + { + Manager->ManagerOwnedPublishTransports.Add(Pub); + UE_LOG(LogURLabNet, Log, + TEXT("UURLabBridgeServer: state publish transport registered with manager")); + } + } + } + else + { + UE_LOG(LogURLabNet, Warning, + TEXT("UURLabBridgeServer: EnsureExternalTransportsBound with no active manager; " + "publish leg deferred until a manager is live.")); + } + + return true; +} + void UURLabBridgeServer::Stop() { // Drain before tearing transports down so blocking handlers see the @@ -117,6 +323,7 @@ void UURLabBridgeServer::Stop() T->TransportShutdown(); } RpcTransports.Reset(); + RestorePerformanceOverrides(); if (!Dispatcher.IsValid()) return; @@ -148,3 +355,77 @@ void UURLabBridgeServer::UnregisterManager(AAMjManager* InManager) } ActiveManager.Reset(); } + +double UURLabBridgeServer::LeaseNow() const +{ + return LeaseClockOverrideForTest >= 0.0 ? LeaseClockOverrideForTest : FPlatformTime::Seconds(); +} + +bool UURLabBridgeServer::IsLeaseHeldInternal(double NowSeconds) +{ + if (bLeaseHeld && (NowSeconds - LeaseLastActivitySeconds) > LeaseTtlSeconds) + { + // Idle past its TTL: auto-release so the next acquire succeeds. + bLeaseHeld = false; + LeaseId.Empty(); + LeaseOwner.Empty(); + } + return bLeaseHeld; +} + +bool UURLabBridgeServer::TryAcquireLease(const FString& Owner, double TtlSeconds, + FString& OutLeaseId, FString& OutExistingLeaseId) +{ + FScopeLock Lock(&LeaseMutex); + const double Now = LeaseNow(); + if (IsLeaseHeldInternal(Now)) + { + OutExistingLeaseId = LeaseId; + return false; + } + + bLeaseHeld = true; + LeaseId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); + LeaseOwner = Owner; + LeaseTtlSeconds = TtlSeconds; + LeaseLastActivitySeconds = Now; + OutLeaseId = LeaseId; + return true; +} + +bool UURLabBridgeServer::ReleaseLease(const FString& InLeaseId) +{ + FScopeLock Lock(&LeaseMutex); + if (!bLeaseHeld || !LeaseId.Equals(InLeaseId)) + return false; + + bLeaseHeld = false; + LeaseId.Empty(); + LeaseOwner.Empty(); + return true; +} + +void UURLabBridgeServer::TouchLease() +{ + FScopeLock Lock(&LeaseMutex); + if (bLeaseHeld) + LeaseLastActivitySeconds = LeaseNow(); +} + +bool UURLabBridgeServer::IsLeaseHeld() +{ + FScopeLock Lock(&LeaseMutex); + return IsLeaseHeldInternal(LeaseNow()); +} + +FString UURLabBridgeServer::GetLeaseId() const +{ + FScopeLock Lock(&LeaseMutex); + return LeaseId; +} + +void UURLabBridgeServer::SetLeaseClockForTest(double NowSeconds) +{ + FScopeLock Lock(&LeaseMutex); + LeaseClockOverrideForTest = NowSeconds; +} diff --git a/Source/URLab/Private/Bridge/BridgeServerConfigUtils.cpp b/Source/URLab/Private/Bridge/BridgeServerConfigUtils.cpp index dacea2dd..15f78012 100644 --- a/Source/URLab/Private/Bridge/BridgeServerConfigUtils.cpp +++ b/Source/URLab/Private/Bridge/BridgeServerConfigUtils.cpp @@ -8,7 +8,10 @@ #include "Interfaces/IPluginManager.h" #include "Misc/ConfigCacheIni.h" #include "Misc/Paths.h" +#include "Misc/CommandLine.h" +#include "Misc/Parse.h" #include "HAL/FileManager.h" +#include "HAL/PlatformMisc.h" namespace URLabBridgeServerConfigUtils { @@ -47,6 +50,21 @@ void LoadFromIni(FURLabBridgeServerConfig& Out) Out.StepPort = TmpInt; if (File.GetInt(SectionName, TEXT("StatePort"), TmpInt)) Out.StatePort = TmpInt; + + FString TmpStr; + if (File.GetString(SectionName, TEXT("InstanceId"), TmpStr)) + Out.InstanceId = TmpStr; + if (File.GetInt(SectionName, TEXT("InstanceIndex"), TmpInt)) + Out.InstanceIndex = TmpInt; + if (File.GetInt(SectionName, TEXT("PortBase"), TmpInt)) + Out.PortBase = TmpInt; + if (File.GetInt(SectionName, TEXT("PortStride"), TmpInt)) + Out.PortStride = TmpInt; + if (File.GetInt(SectionName, TEXT("CamBasePort"), TmpInt)) + Out.CamBasePort = TmpInt; + if (File.GetString(SectionName, TEXT("BindAddress"), TmpStr)) + Out.BindAddress = TmpStr; + if (File.GetBool(SectionName, TEXT("StopOnPIEEnd"), TmpBool)) Out.bStopOnPIEEnd = TmpBool; } @@ -62,9 +80,94 @@ void SaveToIni(const FURLabBridgeServerConfig& In) File.SetString(SectionName, TEXT("AutoStart"), In.bAutoStart ? TEXT("True") : TEXT("False")); File.SetInt64(SectionName, TEXT("StepPort"), In.StepPort); File.SetInt64(SectionName, TEXT("StatePort"), In.StatePort); + File.SetString(SectionName, TEXT("InstanceId"), *In.InstanceId); + File.SetInt64(SectionName, TEXT("InstanceIndex"), In.InstanceIndex); + File.SetInt64(SectionName, TEXT("PortBase"), In.PortBase); + File.SetInt64(SectionName, TEXT("PortStride"), In.PortStride); + File.SetInt64(SectionName, TEXT("CamBasePort"), In.CamBasePort); + File.SetString(SectionName, TEXT("BindAddress"), *In.BindAddress); File.SetString(SectionName, TEXT("StopOnPIEEnd"), In.bStopOnPIEEnd ? TEXT("True") : TEXT("False")); File.Dirty = true; File.Write(Path); } + +void DerivePorts(FURLabBridgeServerConfig& Cfg, + bool bStepExplicit, bool bStateExplicit, bool bCamExplicit) +{ + if (Cfg.InstanceIndex >= 0) + { + const int32 Slot = Cfg.PortBase + Cfg.InstanceIndex * Cfg.PortStride; + if (!bStepExplicit) + Cfg.StepPort = Slot + 0; + if (!bStateExplicit) + Cfg.StatePort = Slot + 1; + if (!bCamExplicit) + Cfg.CamBasePort = Slot + 2; + } + + if (Cfg.CamBasePort == 0) + Cfg.CamBasePort = Cfg.StepPort + 2; + + if (Cfg.InstanceId.IsEmpty() && Cfg.InstanceIndex >= 0) + Cfg.InstanceId = FString::Printf(TEXT("instance_%d"), Cfg.InstanceIndex); +} + +FString BuildCameraEndpoint(const FURLabBridgeServerConfig& Cfg, int32 CameraIndex) +{ + const int32 Index = FMath::Max(0, CameraIndex); + return FString::Printf(TEXT("tcp://%s:%d"), *Cfg.BindAddress, Cfg.CamBasePort + Index); +} + +void ApplyEnvAndCommandLineOverrides(FURLabBridgeServerConfig& Cfg) +{ + // Command line beats environment; a value from either replaces the INI + // one. Returns true when a value was found so callers can flag explicit + // port overrides (which then survive derivation). + auto ResolveString = [](const TCHAR* CmdKey, const TCHAR* EnvKey, FString& Out) -> bool { + if (FParse::Value(FCommandLine::Get(), CmdKey, Out)) + return true; + const FString Env = FPlatformMisc::GetEnvironmentVariable(EnvKey); + if (!Env.IsEmpty()) + { + Out = Env; + return true; + } + return false; + }; + auto ResolveInt = [&ResolveString](const TCHAR* CmdKey, const TCHAR* EnvKey, int32& Out) -> bool { + FString Str; + if (!ResolveString(CmdKey, EnvKey, Str) || !Str.IsNumeric()) + return false; + Out = FCString::Atoi(*Str); + return true; + }; + + FString StrVal; + int32 IntVal = 0; + + if (ResolveString(TEXT("URLabInstanceId="), TEXT("URLAB_INSTANCE_ID"), StrVal)) + Cfg.InstanceId = StrVal; + if (ResolveInt(TEXT("URLabInstanceIndex="), TEXT("URLAB_INSTANCE_INDEX"), IntVal)) + Cfg.InstanceIndex = IntVal; + if (ResolveInt(TEXT("URLabPortBase="), TEXT("URLAB_PORT_BASE"), IntVal)) + Cfg.PortBase = IntVal; + if (ResolveInt(TEXT("URLabPortStride="), TEXT("URLAB_PORT_STRIDE"), IntVal)) + Cfg.PortStride = IntVal; + + const bool bStepExplicit = ResolveInt(TEXT("URLabStepPort="), TEXT("URLAB_STEP_PORT"), IntVal); + if (bStepExplicit) + Cfg.StepPort = IntVal; + const bool bStateExplicit = ResolveInt(TEXT("URLabStatePort="), TEXT("URLAB_STATE_PORT"), IntVal); + if (bStateExplicit) + Cfg.StatePort = IntVal; + const bool bCamExplicit = ResolveInt(TEXT("URLabCamBasePort="), TEXT("URLAB_CAM_BASE_PORT"), IntVal); + if (bCamExplicit) + Cfg.CamBasePort = IntVal; + + if (ResolveString(TEXT("URLabBindAddress="), TEXT("URLAB_BIND_ADDRESS"), StrVal)) + Cfg.BindAddress = StrVal; + + DerivePorts(Cfg, bStepExplicit, bStateExplicit, bCamExplicit); +} } // namespace URLabBridgeServerConfigUtils diff --git a/Source/URLab/Private/Bridge/ControlOwnership.cpp b/Source/URLab/Private/Bridge/ControlOwnership.cpp new file mode 100644 index 00000000..42ae50df --- /dev/null +++ b/Source/URLab/Private/Bridge/ControlOwnership.cpp @@ -0,0 +1,138 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/ControlOwnership.h" +#include "HAL/PlatformTime.h" + +double FMjControlOwnership::Now() const +{ + return ClockOverrideForTest >= 0.0 ? ClockOverrideForTest : FPlatformTime::Seconds(); +} + +bool FMjControlOwnership::IsExpired(const FMjControlClaim& Claim, double NowSeconds) +{ + return Claim.TtlSeconds > 0.0 && (NowSeconds - Claim.LastActivitySeconds) > Claim.TtlSeconds; +} + +FMjControlOwnership::EClaimResult FMjControlOwnership::Claim(FName Art, const FString& Source, + double TtlSeconds, bool bForce, FString& OutCurrentOwner) +{ + FScopeLock Lock(&Mutex); + const double N = Now(); + + if (FMjControlClaim* Existing = Claims.Find(Art)) + { + // An expired claim is free for the taking; a live one held by someone + // else blocks unless the caller forces the steal. + if (!IsExpired(*Existing, N) && !bForce && !Existing->Owner.Equals(Source)) + { + OutCurrentOwner = Existing->Owner; + return EClaimResult::AlreadyOwned; + } + } + + FMjControlClaim& Held = Claims.FindOrAdd(Art); + Held.Owner = Source; + Held.TtlSeconds = TtlSeconds; + Held.LastActivitySeconds = N; + OutCurrentOwner = Source; + return EClaimResult::Ok; +} + +bool FMjControlOwnership::Release(FName Art, const FString& Source) +{ + FScopeLock Lock(&Mutex); + const double N = Now(); + + FMjControlClaim* Existing = Claims.Find(Art); + if (!Existing) + return false; + if (IsExpired(*Existing, N)) + { + Claims.Remove(Art); + return false; + } + if (!Existing->Owner.Equals(Source)) + return false; + + Claims.Remove(Art); + return true; +} + +FMjControlOwnership::EWriteCheck FMjControlOwnership::CheckWrite(FName Art, const FString& Source, + FString& OutCurrentOwner) +{ + FScopeLock Lock(&Mutex); + const double N = Now(); + + FMjControlClaim* Existing = Claims.Find(Art); + if (Existing && IsExpired(*Existing, N)) + { + Claims.Remove(Art); + Existing = nullptr; + } + + if (!Existing) + { + OutCurrentOwner = TEXT("(unclaimed)"); + return EWriteCheck::NotOwner; + } + if (!Existing->Owner.Equals(Source)) + { + OutCurrentOwner = Existing->Owner; + return EWriteCheck::NotOwner; + } + + Existing->LastActivitySeconds = N; + OutCurrentOwner = Existing->Owner; + return EWriteCheck::Ok; +} + +TMap FMjControlOwnership::GetActiveOwners() +{ + FScopeLock Lock(&Mutex); + const double N = Now(); + + TMap Owners; + for (auto It = Claims.CreateIterator(); It; ++It) + { + if (IsExpired(It->Value, N)) + { + It.RemoveCurrent(); + continue; + } + Owners.Add(It->Key, It->Value.Owner); + } + return Owners; +} + +void FMjControlOwnership::Reset() +{ + FScopeLock Lock(&Mutex); + Claims.Empty(); +} + +void FMjControlOwnership::SetClockOverrideForTest(double NowSeconds) +{ + FScopeLock Lock(&Mutex); + ClockOverrideForTest = NowSeconds; +} diff --git a/Source/URLab/Private/Bridge/InstanceRegistry.cpp b/Source/URLab/Private/Bridge/InstanceRegistry.cpp new file mode 100644 index 00000000..1fab5c3a --- /dev/null +++ b/Source/URLab/Private/Bridge/InstanceRegistry.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. + +#include "Bridge/InstanceRegistry.h" +#include "Bridge/BridgeServerConfig.h" + +#include "Dom/JsonObject.h" +#include "Dom/JsonValue.h" +#include "Serialization/JsonSerializer.h" +#include "Serialization/JsonWriter.h" +#include "Misc/FileHelper.h" +#include "Misc/Paths.h" +#include "Misc/DateTime.h" +#include "HAL/FileManager.h" +#include "HAL/PlatformProcess.h" +#include "HAL/PlatformMisc.h" + +namespace +{ +/** Instance id used for the file name and the JSON, falling back to the SHM + * default so a single-editor entry stays readable. */ +FString EffectiveInstanceId(const FURLabBridgeServerConfig& Cfg) +{ + return Cfg.InstanceId.IsEmpty() ? FString(TEXT("live")) : Cfg.InstanceId; +} +} // namespace + +const TArray& FURLabInstanceRegistry::Capabilities() +{ + static const TArray Caps = { + TEXT("render_sync"), + TEXT("render_async"), + TEXT("shm_rpc"), + TEXT("model_upload"), + TEXT("content_cache"), + }; + return Caps; +} + +FString FURLabInstanceRegistry::ResolveRegistryDir() +{ + const FString Override = FPlatformMisc::GetEnvironmentVariable(TEXT("URLAB_REGISTRY_DIR")); + if (!Override.IsEmpty()) + return Override; + +#if PLATFORM_WINDOWS + FString Base = FPlatformMisc::GetEnvironmentVariable(TEXT("LOCALAPPDATA")); + if (Base.IsEmpty()) + Base = FPlatformProcess::UserSettingsDir(); +#else + FString Base = FPlatformMisc::GetEnvironmentVariable(TEXT("XDG_CACHE_HOME")); + if (Base.IsEmpty()) + Base = FPaths::Combine(FPlatformMisc::GetEnvironmentVariable(TEXT("HOME")), TEXT(".cache")); +#endif + return FPaths::Combine(Base, TEXT("URLab"), TEXT("registry")); +} + +FString FURLabInstanceRegistry::ResolveEntryPath(const FURLabBridgeServerConfig& Cfg) +{ + const FString FileName = FString::Printf(TEXT("%s_%u.json"), + *EffectiveInstanceId(Cfg), FPlatformProcess::GetCurrentProcessId()); + return FPaths::Combine(ResolveRegistryDir(), FileName); +} + +void FURLabInstanceRegistry::WriteEntry(const FURLabBridgeServerConfig& Cfg, + const FString& UrlabVersion, bool bManagerPresent, bool bBusy) +{ + TSharedPtr Entry = MakeShared(); + Entry->SetStringField(TEXT("instance_id"), EffectiveInstanceId(Cfg)); + Entry->SetNumberField(TEXT("index"), Cfg.InstanceIndex); + Entry->SetNumberField(TEXT("pid"), static_cast(FPlatformProcess::GetCurrentProcessId())); + Entry->SetStringField(TEXT("host"), FPlatformProcess::ComputerName()); + Entry->SetNumberField(TEXT("step_port"), Cfg.StepPort); + Entry->SetNumberField(TEXT("state_port"), Cfg.StatePort); + Entry->SetNumberField(TEXT("cam_base_port"), Cfg.CamBasePort); + Entry->SetBoolField(TEXT("manager_present"), bManagerPresent); + Entry->SetBoolField(TEXT("busy"), bBusy); + Entry->SetStringField(TEXT("urlab_version"), UrlabVersion); + + TArray> Caps; + for (const FString& Cap : Capabilities()) + Caps.Add(MakeShared(Cap)); + Entry->SetArrayField(TEXT("capabilities"), Caps); + + Entry->SetStringField(TEXT("registry_written_at"), FDateTime::UtcNow().ToIso8601()); + + FString Serialized; + TSharedRef> Writer = TJsonWriterFactory<>::Create(&Serialized); + FJsonSerializer::Serialize(Entry.ToSharedRef(), Writer); + + const FString Path = ResolveEntryPath(Cfg); + IFileManager::Get().MakeDirectory(*FPaths::GetPath(Path), /*Tree=*/true); + FFileHelper::SaveStringToFile(Serialized, *Path); +} + +void FURLabInstanceRegistry::RefreshEntry(const FURLabBridgeServerConfig& Cfg, + const FString& UrlabVersion, bool bManagerPresent, bool bBusy) +{ + WriteEntry(Cfg, UrlabVersion, bManagerPresent, bBusy); +} + +void FURLabInstanceRegistry::RemoveEntry(const FURLabBridgeServerConfig& Cfg) +{ + IFileManager::Get().Delete(*ResolveEntryPath(Cfg), /*RequireExists=*/false, /*EvenReadOnly=*/true); +} diff --git a/Source/URLab/Private/Bridge/MsgpackHelpers.cpp b/Source/URLab/Private/Bridge/MsgpackHelpers.cpp index 117dbd55..ac7fda35 100644 --- a/Source/URLab/Private/Bridge/MsgpackHelpers.cpp +++ b/Source/URLab/Private/Bridge/MsgpackHelpers.cpp @@ -25,6 +25,7 @@ #include "Dom/JsonObject.h" #include "Dom/JsonValue.h" #include "Misc/Base64.h" +#include "Utils/URLabLogging.h" // rpclib's msgpack-cxx headers use member functions and templates named // `check`, which collides with UE's `check(cond)` assertion macro. Save @@ -50,6 +51,37 @@ namespace constexpr const TCHAR* kBinSuffix = TEXT("__b64__"); constexpr int32 kBinSuffixLen = 7; +// FJsonValue carrying raw bytes to pack straight through as msgpack `bin`, with +// no base64 encode/decode round trip and no intermediate FString. It holds a +// Keeper that owns the underlying buffer so the bytes stay valid until the reply +// is packed. It is always stored under a kBinSuffix key, and its Type is set to +// EJson::Null so PackJsonObjectInner can tell it apart from the legacy base64 +// string form (a msgpack->JSON round trip yields EJson::String there, and no +// code ever stores a plain null under a __b64__ key). EJson::Null also serialises +// safely if the reply ever goes out as JSON: the __b64__ field is the +// msgpack-canonical form and JSON clients read the parallel *_base64 field. +class FURLabJsonValueBinary : public FJsonValue +{ +public: + FURLabJsonValueBinary(const uint8* InData, int32 InSize, + TSharedPtr InKeeper) + : Data(InData), Size(InSize), Keeper(MoveTemp(InKeeper)) + { + Type = EJson::Null; + } + + const uint8* GetBinaryData() const { return Data; } + int32 GetBinarySize() const { return Size; } + +protected: + virtual FString GetType() const override { return TEXT("URLabBinary"); } + +private: + const uint8* Data = nullptr; + int32 Size = 0; + TSharedPtr Keeper; +}; + template void PackString(clmdep_msgpack::packer& Packer, const FString& S) { @@ -74,24 +106,39 @@ void PackJsonObjectInner(clmdep_msgpack::packer& Packer, for (const auto& Kv : Obj->Values) { const FString& Key = Kv.Key; - // Special-case keys with kBinSuffix: emit real msgpack bin rather - // than the base64-as-string form the JSON tree carries. + // Keys with kBinSuffix emit real msgpack bin (the suffix is stripped from + // the wire field name). The value is either our raw-binary carrier + // (EJson::Null, bytes packed directly) or a legacy base64 string (from a + // msgpack->JSON round trip, decoded back to bytes). if (Key.EndsWith(kBinSuffix, ESearchCase::CaseSensitive)) { - FString StrippedKey = Key.LeftChop(kBinSuffixLen); - PackString(Packer, StrippedKey); - FString Encoded; - if (Kv.Value.IsValid() && Kv.Value->TryGetString(Encoded)) + PackString(Packer, Key.LeftChop(kBinSuffixLen)); + + const FJsonValue* Val = Kv.Value.Get(); + if (Val && Val->Type == EJson::Null) { - TArray Decoded; - FBase64::Decode(Encoded, Decoded); - Packer.pack_bin(Decoded.Num()); - if (Decoded.Num() > 0) - Packer.pack_bin_body(reinterpret_cast(Decoded.GetData()), Decoded.Num()); + // Raw bytes already in hand: pack directly, no base64 round trip. + const FURLabJsonValueBinary* Bin = static_cast(Val); + const int32 N = Bin->GetBinarySize(); + Packer.pack_bin(N); + if (N > 0 && Bin->GetBinaryData()) + Packer.pack_bin_body(reinterpret_cast(Bin->GetBinaryData()), N); } else { - Packer.pack_bin(0); + FString Encoded; + if (Val && Kv.Value->TryGetString(Encoded)) + { + TArray Decoded; + FBase64::Decode(Encoded, Decoded); + Packer.pack_bin(Decoded.Num()); + if (Decoded.Num() > 0) + Packer.pack_bin_body(reinterpret_cast(Decoded.GetData()), Decoded.Num()); + } + else + { + Packer.pack_bin(0); + } } continue; } @@ -164,11 +211,24 @@ void FURLabMsgpackUtil::SetBinaryField(TSharedPtr& Obj, const FStri { if (!Obj.IsValid()) return; - FString Encoded; + // Own a single copy of the bytes so the caller's buffer need not outlive the + // reply, then pack it straight through as msgpack bin (no base64). The __b64__ + // key suffix tells PackJsonObjectInner to strip the suffix and emit bin. + TSharedRef, ESPMode::ThreadSafe> Owned = + MakeShared, ESPMode::ThreadSafe>(); if (Size > 0 && Data) - Encoded = FBase64::Encode(Data, Size); - // The packer recognises the __b64__ suffix and converts back to msgpack bin. - Obj->SetStringField(Field + kBinSuffix, Encoded); + Owned->Append(Data, Size); + Obj->SetField(Field + kBinSuffix, + MakeShared(Owned->GetData(), Owned->Num(), Owned)); +} + +void FURLabMsgpackUtil::SetBinaryFieldShared(TSharedPtr& Obj, const FString& Field, + const uint8* Data, int32 Size, TSharedPtr Keeper) +{ + if (!Obj.IsValid()) + return; + Obj->SetField(Field + kBinSuffix, + MakeShared(Data, Size, MoveTemp(Keeper))); } // ============================================================================= @@ -274,6 +334,7 @@ bool FURLabMsgpackUtil::UnpackToJsonObject(const uint8* Data, int32 Size, } catch (...) { + UE_LOG(LogURLab, Warning, TEXT("MsgpackHelpers: parse failure in UnpackToJsonObject")); return false; } } diff --git a/Source/URLab/Private/Bridge/RpcDispatcher.cpp b/Source/URLab/Private/Bridge/RpcDispatcher.cpp index 9758d6f8..7bebae4e 100644 --- a/Source/URLab/Private/Bridge/RpcDispatcher.cpp +++ b/Source/URLab/Private/Bridge/RpcDispatcher.cpp @@ -21,27 +21,38 @@ // CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. #include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" #include "Bridge/OpRegistry.h" -#include "Transport/ZmqRpcTransport.h" +#include "Bridge/StepCommands.h" #include "Bridge/MsgpackHelpers.h" #include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Spec/MjElementIdentity.h" +#include "MuJoCo/Spec/MjNodeComponent.h" +#include "MuJoCo/Spec/MjSceneMjcf.h" #include "MuJoCo/Core/MjArticulation.h" -#include "MuJoCo/Components/Actuators/MjActuator.h" -#include "MuJoCo/Components/Sensors/MjSensor.h" -#include "MuJoCo/Components/Sensors/MjCamera.h" -#include "MuJoCo/Components/Joints/MjJoint.h" -#include "MuJoCo/Components/Bodies/MjBody.h" -#include "MuJoCo/Components/Controllers/MjArticulationController.h" +#include "State/MjCanonicalName.h" +#include "MuJoCo/Elements/MjActuatorRuntime.h" +#include "MuJoCo/Elements/MjSensorRuntime.h" +#include "MuJoCo/Elements/MjCamera.h" +#include "MuJoCo/Elements/MjJointRuntime.h" +#include "MuJoCo/Elements/MjBody.h" +#include "MuJoCo/Controllers/MjArticulationController.h" #include "MuJoCo/Input/MjPerturbation.h" #include "MuJoCo/Input/MjTwistController.h" #include "Transport/NetworkManager.h" #include "Transport/ShmPublishTransport.h" +#include "Transport/ShmRpcTransport.h" +#include "Transport/RpcTransport.h" +#include "Bridge/BridgeServer.h" +#include "Bridge/BridgeServerConfig.h" +#include "Bridge/InstanceRegistry.h" +#include "HAL/PlatformProcess.h" +#include "Transport/ShmRegion.h" // FMjShmHeader (header_size in shm_rpc block) #include "Replay/MjReplayManager.h" #include "Kismet/GameplayStatics.h" #include "Misc/Base64.h" #include "Misc/Paths.h" #include "Misc/FileHelper.h" -#include "Internationalization/Regex.h" #include "HAL/FileManager.h" #include "EngineUtils.h" #include "Engine/World.h" @@ -50,94 +61,54 @@ namespace { -/** Map enum to wire-format string, matching the Python StepMode enum values. */ -FString StepModeToString(EStepMode Mode) +/** + * The authored kind of an actuator, as the wire spells it. + * + * The kind is the element itself -- a `` is a different element from a + * `` -- so this is the element's own MJCF tag and there is no mapping + * table to keep in step with the schema. The compiled model cannot answer it: + * every shortcut compiles down to ``. + */ +FString ActuatorTypeToString(const UMjNodeComponent* Actuator) { - switch (Mode) +#if URLAB_MJ_GEN + urlab::spec::psm::ElementType Type; + if (Actuator != nullptr && urlab::spec::MjElementTypeOfNode(*Actuator, Type)) { - case EStepMode::Live: - return TEXT("live"); - case EStepMode::Direct: - return TEXT("direct"); - case EStepMode::Puppet: - return TEXT("puppet"); - case EStepMode::Auto: - return TEXT("auto"); + return urlab::spec::MjTagOf(Type); } - return TEXT("live"); -} - -bool StepModeFromString(const FString& Str, EStepMode& OutMode) -{ - if (Str.Equals(TEXT("live"), ESearchCase::IgnoreCase) || Str.Equals(TEXT("streaming"), ESearchCase::IgnoreCase)) - { - OutMode = EStepMode::Live; - return true; - } - if (Str.Equals(TEXT("direct"), ESearchCase::IgnoreCase)) - { - OutMode = EStepMode::Direct; - return true; - } - if (Str.Equals(TEXT("puppet"), ESearchCase::IgnoreCase)) - { - OutMode = EStepMode::Puppet; - return true; - } - if (Str.Equals(TEXT("auto"), ESearchCase::IgnoreCase)) - { - OutMode = EStepMode::Auto; - return true; - } - return false; +#endif + return TEXT("motor"); } -/** Map URLab actuator enum -> wire-format string. */ -FString ActuatorTypeToString(EMjActuatorType T) +/** An element's authored MJCF name with the articulation prefix taken off. */ +FString LocalElementName(const UMjNodeComponent& Element, const FString& ArtPrefix) { - switch (T) + FString Name = Element.MjName.Get(Element.GetName()); + if (Name.StartsWith(ArtPrefix)) { - case EMjActuatorType::Motor: - return TEXT("motor"); - case EMjActuatorType::Position: - return TEXT("position"); - case EMjActuatorType::Velocity: - return TEXT("velocity"); - case EMjActuatorType::IntVelocity: - return TEXT("intvelocity"); - case EMjActuatorType::Damper: - return TEXT("damper"); - case EMjActuatorType::Cylinder: - return TEXT("cylinder"); - case EMjActuatorType::Muscle: - return TEXT("muscle"); - case EMjActuatorType::Adhesion: - return TEXT("adhesion"); - case EMjActuatorType::DcMotor: - return TEXT("dcmotor"); + Name = Name.Mid(ArtPrefix.Len()); } - return TEXT("motor"); + return Name; } -/** Resolve a save / load path. Bare filename -> /Saved/URLab/Replays/. - * Must match AMjReplayManager::SaveRecordingToFile so the - * recording_save_ok absolute_path actually resolves on the bridge. */ -FString ResolveReplayPath(const FString& UserPath, const FString& DefaultBaseName = TEXT("")) +/** Ops that do NOT count as lease-owner activity: discovery, bootstrap, and + * lifecycle/status polls that a non-owner (e.g. a pool client probing for a + * free instance) issues to inspect an instance. Refreshing the lease on these + * would let steady discovery polling keep a held lease alive forever and + * defeat TTL auto-release. Real owner activity (step / reset / forward / set_* + * and every other state-advancing op) still refreshes. */ +bool OpRefreshesLease(const FString& Op) { - FString BaseDir = FPaths::ProjectSavedDir() / TEXT("URLab") / TEXT("Replays"); - IFileManager::Get().MakeDirectory(*BaseDir, true); - - FString Path = UserPath; - if (Path.IsEmpty()) - { - Path = DefaultBaseName.IsEmpty() ? TEXT("recording.json") : DefaultBaseName; - } - - if (FPaths::IsRelative(Path)) - { - Path = FPaths::Combine(BaseDir, Path); - } - return FPaths::ConvertRelativePathToFull(Path); + static const TSet NonActivityOps = { + TEXT("hello"), + TEXT("meta"), + TEXT("pie_status"), + TEXT("op_status"), + TEXT("acquire_lease"), + TEXT("release_lease"), + }; + return !NonActivityOps.Contains(Op); } } // namespace @@ -192,6 +163,14 @@ void FURLabRpcDispatcher::RegisterDispatcherOps() [this](auto& R) { return HandleSetPaused(R); }, /*Reply=*/{TEXT("op:string"), TEXT("paused:bool")}, /*Required=*/{TEXT("paused")}); + Reg(TEXT("set_camera_streaming"), EOpCategory::ManagerRequired, TEXT("runtime"), + [this](auto& R) { return HandleSetCameraStreaming(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("cameras:object")}, + /*Required=*/{TEXT("cameras")}); + Reg(TEXT("set_camera_delay"), EOpCategory::ManagerRequired, TEXT("runtime"), + [this](auto& R) { return HandleSetCameraDelay(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("cameras:object")}, + /*Required=*/{TEXT("cameras")}); Reg(TEXT("configure_controller"), EOpCategory::ManagerRequired, TEXT("runtime"), [this](auto& R) { return HandleConfigureController(R); }, /*Reply=*/{TEXT("op:string"), TEXT("articulation:string"), TEXT("params:object")}, @@ -206,9 +185,20 @@ void FURLabRpcDispatcher::RegisterDispatcherOps() Reg(TEXT("set_control_source"), EOpCategory::ManagerRequired, TEXT("runtime"), [this](auto& R) { return HandleSetControlSource(R); }, {TEXT("op:string")}); + Reg(TEXT("claim_control"), EOpCategory::ManagerRequired, TEXT("runtime"), + [this](auto& R) { return HandleClaimControl(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("articulation:string"), TEXT("owner:string"), TEXT("ttl_s:float")}, + /*Required=*/{TEXT("articulation")}); + Reg(TEXT("release_control"), EOpCategory::ManagerRequired, TEXT("runtime"), + [this](auto& R) { return HandleReleaseControl(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("articulation:string")}, + /*Required=*/{TEXT("articulation")}); Reg(TEXT("set_twist"), EOpCategory::ManagerRequired, TEXT("runtime"), [this](auto& R) { return HandleSetTwist(R); }, {TEXT("op:string")}); + Reg(TEXT("set_user_channels"), EOpCategory::ManagerRequired, TEXT("runtime"), + [this](auto& R) { return HandleSetUserChannels(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("applied:int"), TEXT("rejected:object")}); Reg(TEXT("set_qpos"), EOpCategory::ManagerRequired, TEXT("runtime"), [this](auto& R) { return HandleSetQpos(R); }, /*Reply=*/{TEXT("op:string"), TEXT("target:string"), TEXT("actor_id:string?"), TEXT("actor_name:string?"), TEXT("qpos:array"), TEXT("free_base_shortcut:bool")}, @@ -228,6 +218,36 @@ void FURLabRpcDispatcher::RegisterDispatcherOps() [this](auto& R) { return HandleListKeyframes(R); }, {TEXT("op:string"), TEXT("keyframes:array")}); + // Cooperative render-farm lease. No-manager: a pool client claims the + // process itself, whether or not a scene is loaded. + Reg(TEXT("acquire_lease"), EOpCategory::NoManager, TEXT("farm"), + [this](auto& R) { return HandleAcquireLease(R); }, + {TEXT("op:string"), TEXT("lease_id:string"), TEXT("ttl_s:float")}); + // lease_id is validated in the handler (not declaratively) so a missing + // value returns `bad_request` per the schema, not the generic + // `missing_field`. + Reg(TEXT("release_lease"), EOpCategory::NoManager, TEXT("farm"), + [this](auto& R) { return HandleReleaseLease(R); }, + /*Reply=*/{TEXT("op:string")}); + + // Network model upload (RpcHandlers_ModelUpload.cpp). Manifest + chunk are + // pure data staging (no manager, no editor). Commit drives the existing + // import_xml editor job on a materialised temp dir; it self-checks for the + // editor import handler, so it stays NoManager and returns `import_failed` + // when the editor module isn't loaded. + Reg(TEXT("upload_model_manifest"), EOpCategory::NoManager, TEXT("scene"), + [this](auto& R) { return HandleUploadModelManifest(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("upload_id:string"), TEXT("need_xml:bool"), TEXT("need_assets:array"), TEXT("max_asset_bytes:int"), TEXT("max_total_bytes:int")}, + /*Required=*/{TEXT("xml_sha256")}); + Reg(TEXT("upload_model_chunk"), EOpCategory::NoManager, TEXT("scene"), + [this](auto& R) { return HandleUploadModelChunk(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("name:string"), TEXT("received:int"), TEXT("complete:bool")}, + /*Required=*/{TEXT("upload_id"), TEXT("kind")}); + Reg(TEXT("upload_model_commit"), EOpCategory::NoManager, TEXT("scene"), + [this](auto& R) { return HandleUploadModelCommit(R); }, + /*Reply=*/{TEXT("op:string"), TEXT("imported:bool"), TEXT("nq:int?"), TEXT("nv:int?"), TEXT("nu:int?"), TEXT("nbody:int?"), TEXT("ngeom:int?"), TEXT("mjb:object?"), TEXT("warnings:array")}, + /*Required=*/{TEXT("upload_id")}); + auto RecBody = [this](auto& R) { FString OpName; R->TryGetStringField(TEXT("op"), OpName); @@ -272,18 +292,17 @@ void FURLabRpcDispatcher::Init(AAMjManager* InManager) const EStepMode InitMode = (OwnerMgr->StepMode == EStepMode::Auto) ? EStepMode::Live : OwnerMgr->StepMode; - ActiveStepMode.store(InitMode, std::memory_order_release); - // Mirror onto the manager so the physics loop paces off the resolved mode - // (Auto -> Live), not the configured StepMode which would stay Auto. - OwnerMgr->EffectiveStepMode.store(InitMode, std::memory_order_release); - const bool bPaused = (InitMode != EStepMode::Live); - OwnerMgr->bPublishersPaused.store(bPaused, std::memory_order_release); - FCameraZmqWorker::bPublishersPaused.store(bPaused, std::memory_order_release); - - if (InitMode == EStepMode::Puppet) - InstallPuppetHandler(); - else if (InitMode == EStepMode::Direct) - InstallDirectHandler(); + { + // Serialise strategy construction + OnEnter against a concurrent + // set_mode / OnManagerGone (PIE-end) touching the same members. + FScopeLock Lock(&DispatchMutex); + ActiveStepMode.store(InitMode, std::memory_order_release); + // Camera publishers stream in every mode; the strategy's OnEnter handles + // the state/ctrl publishers, the engine step mode, and the handler install. + FCameraZmqWorker::bPublishersPaused.store(false, std::memory_order_release); + CurrentStepStrategy = MakeStepStrategy(InitMode); + CurrentStepStrategy->OnEnter(*this, *OwnerMgr); + } // Cached on the game thread; worker threads later use Get() (TActorIterator // asserts IsInGameThread). @@ -297,13 +316,23 @@ void FURLabRpcDispatcher::Init(AAMjManager* InManager) void FURLabRpcDispatcher::OnManagerGone() { - UninstallPuppetHandler(); + // Serialise handler teardown + strategy reset against a concurrent set_mode + // racing PIE-end (would otherwise be a UAF on CurrentStepStrategy / the + // handler flags). + FScopeLock Lock(&DispatchMutex); + UninstallDirectHandler(); - DrainQueuesForTest(); + CurrentStepStrategy.Reset(); + DrainQueues(); + + // Claims are per-PIE: the articulations die with the world. + ControlOwnership.Reset(); if (OwnerMgr.IsValid()) { OwnerMgr->bPublishersPaused.store(false, std::memory_order_release); + if (OwnerMgr->PhysicsEngine) + OwnerMgr->PhysicsEngine->SetStepMode(EStepMode::Live); } FCameraZmqWorker::bPublishersPaused.store(false, std::memory_order_release); OwnerMgr.Reset(); @@ -340,6 +369,13 @@ void FURLabRpcDispatcher::SetCachedReplayManager(AMjReplayManager* RM) CachedReplayManager = RM; } +// Out-of-line so the TWeakObjectPtr assignment sees the full UURLabBridgeServer +// type (the header only forward-declares it to avoid an include cycle). +void FURLabRpcDispatcher::SetOwningBridge(UURLabBridgeServer* InBridge) +{ + OwningBridge = InBridge; +} + void FURLabRpcDispatcher::EnqueueStepRequestForTest(FMjStepRequest&& Req) { TSharedPtr Cmd = MakeShared(); @@ -354,20 +390,18 @@ void FURLabRpcDispatcher::EnqueueStepRequestForTest(FMjStepRequest&& Req) } } -void FURLabRpcDispatcher::EnqueuePushStateRequestForTest(FMjPushStateRequest&& Req) -{ - PushStateQueue.Enqueue(MoveTemp(Req)); -} - -void FURLabRpcDispatcher::DrainQueuesForTest() +void FURLabRpcDispatcher::DrainQueues() { TSharedPtr Cmd; while (StepQueue.Dequeue(Cmd)) { - } // shared_ptr deallocates on scope exit - FMjPushStateRequest P; - while (PushStateQueue.Dequeue(P)) - { + if (!Cmd.IsValid()) + continue; + // Abandon + wake any RPC thread blocked on this command so a mode switch + // / PIE-end returns immediately instead of eating the 5s step deadline. + Cmd->bAbandoned.store(true, std::memory_order_release); + if (Cmd->Completion) + Cmd->Completion->Trigger(); } } @@ -410,16 +444,28 @@ TSharedPtr FURLabRpcDispatcher::DispatchInternal(const TSharedPtrTryGetStringField(TEXT("op"), Op)) { - return MakeError(TEXT("missing_op"), TEXT("Request missing 'op' field")); + return MakeError(URLabError::MissingOp, TEXT("Request missing 'op' field")); + } + + // Only real owner activity refreshes the lease. Discovery / bootstrap / + // status-poll ops (see OpRefreshesLease) must NOT, or a pool client's + // steady `hello`/status polling would keep a held lease alive forever and + // defeat TTL auto-release. No-op when no lease is held. + if (OpRefreshesLease(Op)) + { + if (UURLabBridgeServer* Bridge = OwningBridge.Get()) + Bridge->TouchLease(); } - // hello / meta are pre-session bootstrap endpoints. + // hello / meta are pre-session ops that run before a client has a + // session_id, so they bypass the registry. Both validate fields inline + // (all hello fields are optional with sensible defaults; meta takes none). if (Op.Equals(TEXT("hello"))) return HandleHello(Req); if (Op.Equals(TEXT("meta"))) @@ -431,7 +477,7 @@ TSharedPtr FURLabRpcDispatcher::DispatchInternal(const TSharedPtrTryGetStringField(TEXT("session_id"), SessionId); if (!ValidateSession(SessionId)) { - return MakeError(TEXT("session_expired"), + return MakeError(URLabError::SessionExpired, FString::Printf(TEXT("Session id '%s' does not match active session"), *SessionId)); } } @@ -441,10 +487,10 @@ TSharedPtr FURLabRpcDispatcher::DispatchInternal(const TSharedPtr FURLabRpcDispatcher::DispatchInternal(const TSharedPtrHasField(Field)) { - return MakeError(TEXT("missing_field"), + return MakeError(URLabError::MissingField, FString::Printf(TEXT("op '%s' missing required field '%s'"), *Op, *Field)); } @@ -462,7 +508,7 @@ TSharedPtr FURLabRpcDispatcher::DispatchInternal(const TSharedPtrCategory == URLabOpRegistry::EOpCategory::ManagerRequired && !OwnerMgr.IsValid()) { - return MakeError(TEXT("no_active_manager"), + return MakeError(URLabError::NoActiveManager, FString::Printf( TEXT("op '%s' requires an active AAMjManager (PIE not running?)"), *Op)); @@ -520,26 +566,6 @@ TSharedPtr FURLabRpcDispatcher::HandleMeta(const TSharedPtr FURLabRpcDispatcher::HandleSetPaused(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine) - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - - bool bPause = false; - if (!Req->TryGetBoolField(TEXT("paused"), bPause)) - return MakeError(TEXT("missing_field"), TEXT("set_paused requires 'paused' bool")); - - Mgr->PhysicsEngine->SetPaused(bPause); - UE_LOG(LogURLabNet, Log, TEXT("FURLabRpcDispatcher: set_paused -> %s"), - bPause ? TEXT("true") : TEXT("false")); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_paused_ok")); - Reply->SetBoolField(TEXT("paused"), Mgr->PhysicsEngine->bIsPaused); - return Reply; -} - TSharedPtr FURLabRpcDispatcher::MakeError(const FString& Code, const FString& Message) { TSharedPtr Err = MakeShared(); @@ -596,6 +622,12 @@ TSharedPtr FURLabRpcDispatcher::HandleHello(const TSharedPtrTryGetStringField(TEXT("client_version"), ClientVersion); + UE_LOG(LogURLabNet, Log, TEXT("URLab client connected: %s (server %s)"), + *ClientVersion, *URLabVersion); + // Reset to msgpack default on each handshake; per-session opt-in via encoding=json. bUseJsonEncoding.store(false, std::memory_order_release); FString Encoding; @@ -641,6 +673,34 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* const bool bManagerPresent = (Manager && Manager->PhysicsEngine); Reply->SetBoolField(TEXT("manager_present"), bManagerPresent); + // Per-instance identity for render-farm discovery. Values come from the + // bridge server's resolved config (env / command line / INI), so a client + // learns exactly which instance answered and on which ports. + if (Manager && Manager->BridgeServer) + { + const FURLabBridgeServerConfig& Cfg = Manager->BridgeServer->GetInstanceConfig(); + TSharedPtr Instance = MakeShared(); + Instance->SetStringField(TEXT("instance_id"), + Cfg.InstanceId.IsEmpty() ? FString(TEXT("live")) : Cfg.InstanceId); + Instance->SetNumberField(TEXT("index"), Cfg.InstanceIndex); + Instance->SetNumberField(TEXT("pid"), + static_cast(FPlatformProcess::GetCurrentProcessId())); + Instance->SetStringField(TEXT("host"), FPlatformProcess::ComputerName()); + Instance->SetNumberField(TEXT("step_port"), Cfg.StepPort); + Instance->SetNumberField(TEXT("state_port"), Cfg.StatePort); + Instance->SetNumberField(TEXT("cam_base_port"), Cfg.CamBasePort); + Instance->SetBoolField(TEXT("manager_present"), bManagerPresent); + Instance->SetBoolField(TEXT("busy"), Manager->BridgeServer->IsLeaseHeld()); + Instance->SetStringField(TEXT("urlab_version"), URLabVer); + + TArray> Capabilities; + for (const FString& Cap : FURLabInstanceRegistry::Capabilities()) + Capabilities.Add(MakeShared(Cap)); + Instance->SetArrayField(TEXT("capabilities"), Capabilities); + + Reply->SetObjectField(TEXT("instance"), Instance); + } + if (!bManagerPresent) { // Editor-time / pre-PIE handshake. Only editor-only ops can run @@ -654,35 +714,32 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* mjModel* m = Manager->PhysicsEngine->GetModel(); - // SHM session directory. Ship an absolute path so the bridge can - // open the SHM regions regardless of its own working directory -- - // UE's path APIs return strings relative to the engine binary, which - // would mmap from the bridge's CWD otherwise. + // Let each bound transport append its own block to the handshake. + // SHM publish transport sets shm_session_dir; SHM RPC transport sets + // shm_rpc (paths/events/strides). Adding a third transport type + // requires no changes here — just override AppendHandshakeBlock. + for (const TObjectPtr& T : Manager->ManagerOwnedPublishTransports) { - FString ShmDir; - // Snapshot publisher is a UObject in - // ManagerOwnedPublishTransports. Walk the manager's transport - // array to find it. - for (const TObjectPtr& T : Manager->ManagerOwnedPublishTransports) - { - if (UURLabShmPublishTransport* ShmPub = Cast(T.Get())) - { - const FString StatePath = ShmPub->GetStatePath(); - if (!StatePath.IsEmpty()) - { - ShmDir = FPaths::GetPath(StatePath); - } - break; - } - } - if (ShmDir.IsEmpty()) - { - ShmDir = UURLabShmPublishTransport::ResolveSessionDir(SessionId); - } + T->AppendHandshakeBlock(Reply); + } + + // Fall back to the static helper when no publish transport set + // shm_session_dir (e.g. no SHM transport bound). + if (!Reply->HasField(TEXT("shm_session_dir"))) + { + const FString ShmDir = UURLabShmPublishTransport::ResolveSessionDir(SessionId); Reply->SetStringField(TEXT("shm_session_dir"), FPaths::ConvertRelativePathToFull(ShmDir)); } + if (Manager->BridgeServer) + { + for (const TObjectPtr& T : Manager->BridgeServer->GetRpcTransports()) + { + T->AppendHandshakeBlock(Reply); + } + } + // MJB bytes: real msgpack bin under "mjb" for msgpack clients; // legacy "mjb_base64" / "mjb_size" stays available for JSON clients. if (m) @@ -701,82 +758,44 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* // the client can reload the model offline (MJX, custom integrators, // headless renderers). Opt-in because the payload can be tens of MB // for typical robots. - if (bIncludeAssets && m && Manager->PhysicsEngine->m_spec) + if (bIncludeAssets && m) { - // Two-pass XML serialise. mj_saveXMLString returns -1 on buffer - // overflow; start at 256 KB (covers most robots, including G1) - // and double up to 32 MB before giving up. - TArray XmlBuf; - FString XmlContent; - for (int32 Cap = 256 * 1024; Cap <= 32 * 1024 * 1024; Cap *= 2) + // The text goes out exactly as the scene writer produced it. Every + // `file=` in it already names the mount the asset pass chose, and those + // mount names are what the VFS below is keyed by; rewriting either end + // here would be a second naming convention for the same bytes, and the + // one it used to apply -- reducing every reference to its basename -- + // collapsed two participants' `base.obj` onto one mount. + FMjCompiledScene Scene; + FString SceneError; + if (Manager->PhysicsEngine->BuildCompiledScene(Scene, SceneError)) { - XmlBuf.SetNumUninitialized(Cap); - FMemory::Memzero(XmlBuf.GetData(), Cap); - char SaveError[1024] = ""; - const int XmlResult = mj_saveXMLString( - Manager->PhysicsEngine->m_spec, - reinterpret_cast(XmlBuf.GetData()), Cap, - SaveError, sizeof(SaveError)); - if (XmlResult == 0) - { - XmlContent = UTF8_TO_TCHAR(reinterpret_cast(XmlBuf.GetData())); - break; - } - // Overflow or another fault. Grow + retry. The error string - // distinguishes "buffer too small" from real failures; for - // any non-overflow we still break to avoid silent corruption. - const FString Err = UTF8_TO_TCHAR(SaveError); - if (!Err.Contains(TEXT("buffer"), ESearchCase::IgnoreCase)) - { - UE_LOG(LogURLabNet, Warning, - TEXT("BuildHandshake: mj_saveXMLString failed (%s); skipping mjcf_compiled"), - *Err); - XmlContent.Reset(); - break; - } + Reply->SetStringField(TEXT("mjcf_compiled"), Scene.Xml); } - if (!XmlContent.IsEmpty()) + else { - // Flatten file="dir/sub/foo.STL" -> file="foo.STL" so a VFS - // keyed by bare filename (which is what mj_addFileVFS does) - // can resolve the references on the client side. - FRegexPattern Pattern(TEXT("file=\"([^\"]*?)([^/\\\\\"]+)\"")); - FRegexMatcher Matcher(Pattern, XmlContent); - FString Rewritten; - int32 Cursor = 0; - while (Matcher.FindNext()) - { - const int32 MatchStart = Matcher.GetMatchBeginning(); - const int32 MatchEnd = Matcher.GetMatchEnding(); - const FString Filename = Matcher.GetCaptureGroup(2); - Rewritten += XmlContent.Mid(Cursor, MatchStart - Cursor); - Rewritten += FString::Printf(TEXT("file=\"%s\""), *Filename); - Cursor = MatchEnd; - } - Rewritten += XmlContent.Mid(Cursor); - Reply->SetStringField(TEXT("mjcf_compiled"), Rewritten); + UE_LOG(LogURLabNet, Warning, + TEXT("BuildHandshake: no scene MJCF (%s); skipping mjcf_compiled"), *SceneError); } - // Asset bytes: one msgpack-bin field per file, keyed by bare - // filename (matches the flattened file= refs above). No base64 - // duplicate — clients should use the msgpack decoder. + // VFS bytes: one msgpack-bin field per entry, keyed by the name the + // text asks for. Asset files come off disk; a scene that references + // participant specs carries their text the same way, because to the + // client's VFS the two are the same kind of thing. No base64 duplicate — + // clients should use the msgpack decoder. Shipped whether or not the + // MJCF made it, because a client that already has the model still needs + // the meshes. TSharedPtr VfsAssets = MakeShared(); int64 TotalBytes = 0; - for (const FString& FilePath : Manager->PhysicsEngine->ActiveAssetPaths) - { - TArray FileData; - if (FFileHelper::LoadFileToArray(FileData, *FilePath)) - { - const FString Filename = FPaths::GetCleanFilename(FilePath); - FURLabMsgpackUtil::SetBinaryField(VfsAssets, *Filename, - FileData.GetData(), FileData.Num()); - TotalBytes += FileData.Num(); - } - } + MjForEachSceneVfsEntry(Scene.AssetFiles, Scene.ParticipantXml, + [&VfsAssets, &TotalBytes](const FString& Name, TArrayView Bytes) { + FURLabMsgpackUtil::SetBinaryField(VfsAssets, Name, Bytes.GetData(), Bytes.Num()); + TotalBytes += Bytes.Num(); + }); Reply->SetObjectField(TEXT("vfs_assets"), VfsAssets); UE_LOG(LogURLabNet, Log, - TEXT("BuildHandshake: shipped %d assets (%lld bytes) + mjcf_compiled (%d chars)"), - VfsAssets->Values.Num(), TotalBytes, XmlContent.Len()); + TEXT("BuildHandshake: shipped %d VFS entries (%lld bytes) + mjcf_compiled (%d chars)"), + VfsAssets->Values.Num(), TotalBytes, Scene.Xml.Len()); } // Articulations block. @@ -787,8 +806,11 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* continue; TSharedPtr ArtObj = MakeShared(); - ArtObj->SetStringField(TEXT("prefix"), Art->GetName()); + // The public segment (ActorId-based) is the art's topic / observation + // namespace, matching the IR Art.Name the state stream keys arts under. + ArtObj->SetStringField(TEXT("prefix"), FMjCanonicalName::ArtSegment(Art).ToString()); ArtObj->SetStringField(TEXT("actor_id"), Art->ActorId); + ArtObj->SetStringField(TEXT("actor_name"), Art->GetName()); // Default control mode follows whether a controller is attached. UMjArticulationController* Ctrl = Art->FindComponentByClass(); @@ -818,15 +840,12 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* // Per-actuator authored kind. The MJB doesn't carry the original // / shortcut — they all compile to . TSharedPtr ActTypes = MakeShared(); - for (UMjActuator* Act : Art->GetActuators()) + for (const UMjNodeComponent* Act : Art->GetActuators()) { - if (!Act) + if (Act == nullptr) continue; - FString Local = Act->GetMjName(); - FString Prefix = Art->GetName() + TEXT("_"); - if (Local.StartsWith(Prefix)) - Local = Local.Mid(Prefix.Len()); - ActTypes->SetStringField(Local, ActuatorTypeToString(Act->Type)); + ActTypes->SetStringField(LocalElementName(*Act, Art->GetCompiledPrefix()), + ActuatorTypeToString(Act)); } ArtObj->SetObjectField(TEXT("actuator_types"), ActTypes); @@ -834,42 +853,18 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* // components whose live name was renamed by SCS / spec-time // dedup. The bridge resolves mjlab patterns against original // names. Identity entries + default-class templates are skipped. + // Live-to-authored name maps, one per category. They are empty and the + // wire contract keeps them: the reader writes the MJCF `name` attribute + // into the element and nothing renames it afterwards, so an element's + // live name IS its authored name. The maps existed because the component + // tree went through Blueprint variable naming, which deduplicated names + // the MJCF had kept distinct. { - const FString ArtPrefix = Art->GetName() + TEXT("_"); - auto MakeMap = [&ArtPrefix](auto& Components) { - TSharedPtr Obj = MakeShared(); - for (auto* C : Components) - { - if (!C || C->bIsDefault) - continue; - if (C->OriginalMjName.IsEmpty()) - continue; - FString Live = C->GetMjName(); - if (Live.StartsWith(ArtPrefix)) - Live = Live.Mid(ArtPrefix.Len()); - if (Live.IsEmpty() || Live == C->OriginalMjName) - continue; - Obj->SetStringField(Live, C->OriginalMjName); - } - return Obj; - }; - TSharedPtr OriginalNames = MakeShared(); - - TArray ActComps = Art->GetActuators(); - OriginalNames->SetObjectField(TEXT("actuators"), MakeMap(ActComps)); - - TArray JointComps = Art->GetJoints(); - OriginalNames->SetObjectField(TEXT("joints"), MakeMap(JointComps)); - - TArray SensorComps; - Art->GetComponents(SensorComps); - OriginalNames->SetObjectField(TEXT("sensors"), MakeMap(SensorComps)); - - TArray BodyComps; - Art->GetComponents(BodyComps); - OriginalNames->SetObjectField(TEXT("bodies"), MakeMap(BodyComps)); - + OriginalNames->SetObjectField(TEXT("actuators"), MakeShared()); + OriginalNames->SetObjectField(TEXT("joints"), MakeShared()); + OriginalNames->SetObjectField(TEXT("sensors"), MakeShared()); + OriginalNames->SetObjectField(TEXT("bodies"), MakeShared()); ArtObj->SetObjectField(TEXT("original_names"), OriginalNames); } @@ -879,7 +874,7 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* Art->GetComponents(Cameras); for (UMjCamera* Cam : Cameras) { - if (!Cam || Cam->bIsDefault) + if (!Cam) continue; TSharedPtr CamObj = MakeShared(); @@ -901,18 +896,19 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* } CamObj->SetStringField(TEXT("mode"), ModeStr); + const FIntPoint CamRes = Cam->CaptureResolution(); TArray> Res; - Res.Add(MakeShared(Cam->resolution.Num() > 0 ? Cam->resolution[0] : 0)); - Res.Add(MakeShared(Cam->resolution.Num() > 1 ? Cam->resolution[1] : 0)); + Res.Add(MakeShared(CamRes.X)); + Res.Add(MakeShared(CamRes.Y)); CamObj->SetArrayField(TEXT("resolution"), Res); - CamObj->SetNumberField(TEXT("fovy"), Cam->fovy); + CamObj->SetNumberField(TEXT("fovy"), Cam->DerivedFovy); FString Endpoint = Cam->GetActualZmqEndpoint(); Endpoint.ReplaceInline(TEXT("*"), TEXT("127.0.0.1")); CamObj->SetStringField(TEXT("zmq_endpoint"), Endpoint); - CamObj->SetStringField(TEXT("zmq_topic"), - FString::Printf(TEXT("%s/camera/%s"), *Art->GetName(), *Cam->GetName())); - CamMap->SetObjectField(Cam->GetName(), CamObj); + const FString CamCanon = Cam->GetCanonicalName(); + CamObj->SetStringField(TEXT("zmq_topic"), CamCanon); + CamMap->SetObjectField(CamCanon, CamObj); } ArtObj->SetObjectField(TEXT("camera_topics"), CamMap); @@ -961,2192 +957,3 @@ TSharedPtr FURLabRpcDispatcher::BuildHandshakePayload(AAMjManager* return Reply; } - -// ============================================================================= -// step -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleStep(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->m_model) - { - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - } - - if (ActiveStepMode == EStepMode::Live) - { - // Live mode: UE drives its own physics. step() applies ctrl and - // returns current state. n_steps is ignored (UE steps at its own - // rate). Same per_articulation shape as direct mode. - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - - FMjStepRequest TmpReq; - const TSharedPtr* PerArt = nullptr; - if (Req->TryGetObjectField(TEXT("per_articulation"), PerArt) && PerArt && PerArt->IsValid()) - { - for (auto& Pair : (*PerArt)->Values) - { - const TSharedPtr* ArtObj = nullptr; - if (!Pair.Value->TryGetObject(ArtObj) || !ArtObj || !ArtObj->IsValid()) - continue; - - FString CtlMode; - if ((*ArtObj)->TryGetStringField(TEXT("control_mode"), CtlMode)) - TmpReq.PerArticulationControlMode.Add(Pair.Key, CtlMode); - - const TArray>* CtrlList = nullptr; - if ((*ArtObj)->TryGetArrayField(TEXT("ctrl"), CtrlList) && CtrlList) - { - if (AMjArticulation* Art = Cast(Mgr->GetArticulation(Pair.Key))) - { - TArray Acts = Art->GetActuators(); - for (int32 i = 0; i < CtrlList->Num() && i < Acts.Num(); ++i) - { - UMjActuator* A = Acts[i]; - if (!A) - continue; - FString Local = A->GetMjName(); - FString Prefix = Art->GetName() + TEXT("_"); - if (Local.StartsWith(Prefix)) - Local = Local.Mid(Prefix.Len()); - TmpReq.PerArticulationCtrl.FindOrAdd(Pair.Key).Add( - {Local, (float)(*CtrlList)[i]->AsNumber()}); - } - } - } - } - } - - TSharedPtr Reply = MakeShared(); - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - ApplyStepCtrl(Mgr, TmpReq, m, d); - Reply->SetStringField(TEXT("op"), TEXT("step_ok")); - Reply->SetNumberField(TEXT("time"), d->time); - Reply->SetNumberField(TEXT("step"), StepCounter.load(std::memory_order_relaxed)); - AppendClockFields(Reply, d->time); - TSharedPtr Obs = BuildStepObservations(Mgr, m, d, ActiveObservationLevel); - if (Obs.IsValid()) - Reply->SetObjectField(TEXT("per_articulation"), Obs); - TSharedPtr Scene = BuildEntitiesBlock(Mgr, m, d); - if (Scene.IsValid()) - Reply->SetObjectField(TEXT("entities"), Scene); - } - return Reply; - } - - // Per-step observations override (does not change the session default). - FString StepObs; - if (Req->TryGetStringField(TEXT("observations"), StepObs)) - { - if (StepObs.Equals(TEXT("minimal"), ESearchCase::IgnoreCase)) - ActiveObservationLevel = EObservationLevel::Minimal; - else if (StepObs.Equals(TEXT("full"), ESearchCase::IgnoreCase)) - ActiveObservationLevel = EObservationLevel::Full; - else if (StepObs.Equals(TEXT("standard"), ESearchCase::IgnoreCase)) - ActiveObservationLevel = EObservationLevel::Standard; - } - - // Parse include_cameras: accepts either - // - object: { "": "sync"|"latest" } -- per-camera mode - // - bool true: all registered cameras at "latest" mode (legacy form) - // - false/absent: no cameras - TMap CameraSpec; - { - const TSharedPtr* CamObj = nullptr; - if (Req->TryGetObjectField(TEXT("include_cameras"), CamObj) && CamObj && CamObj->IsValid()) - { - for (const auto& Kv : (*CamObj)->Values) - { - FString Mode; - if (Kv.Value.IsValid() && Kv.Value->TryGetString(Mode)) - { - ECameraInclude E = ECameraInclude::Latest; - if (Mode.Equals(TEXT("sync"), ESearchCase::IgnoreCase)) - E = ECameraInclude::Sync; - CameraSpec.Add(Kv.Key, E); - } - } - } - else - { - bool bAll = false; - if (Req->TryGetBoolField(TEXT("include_cameras"), bAll) && bAll) - { - for (AMjArticulation* Art : Mgr->GetAllArticulations()) - { - if (!Art) - continue; - TArray Cams; - Art->GetComponents(Cams); - for (UMjCamera* C : Cams) - { - if (!C || C->bIsDefault) - continue; - CameraSpec.Add(C->GetName(), ECameraInclude::Latest); - } - } - } - } - } - - if (ActiveStepMode == EStepMode::Puppet) - { - FMjPushStateRequest Push; - const TArray>* QPosArr = nullptr; - const TArray>* QVelArr = nullptr; - const TArray>* CtrlArr = nullptr; - if (Req->TryGetArrayField(TEXT("qpos"), QPosArr)) - { - Push.QPos.Reserve(QPosArr->Num()); - for (auto& V : *QPosArr) - Push.QPos.Add(V->AsNumber()); - } - if (Req->TryGetArrayField(TEXT("qvel"), QVelArr)) - { - Push.QVel.Reserve(QVelArr->Num()); - for (auto& V : *QVelArr) - Push.QVel.Add(V->AsNumber()); - } - if (Req->TryGetArrayField(TEXT("ctrl"), CtrlArr)) - { - Push.bIncludeCtrl = true; - Push.Ctrl.Reserve(CtrlArr->Num()); - for (auto& V : *CtrlArr) - Push.Ctrl.Add(V->AsNumber()); - } - double TimeVal = 0.0; - Req->TryGetNumberField(TEXT("time"), TimeVal); - Push.Time = TimeVal; - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - if (Push.QPos.Num() == m->nq) - FMemory::Memcpy(d->qpos, Push.QPos.GetData(), m->nq * sizeof(mjtNum)); - if (Push.QVel.Num() == m->nv) - FMemory::Memcpy(d->qvel, Push.QVel.GetData(), m->nv * sizeof(mjtNum)); - if (Push.bIncludeCtrl && Push.Ctrl.Num() == m->nu) - FMemory::Memcpy(d->ctrl, Push.Ctrl.GetData(), m->nu * sizeof(mjtNum)); - d->time = Push.Time; - mj_forward(m, d); - - if (Mgr->PhysicsEngine->OnPostStep) - Mgr->PhysicsEngine->OnPostStep(m, d); - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("step_ok")); - Reply->SetNumberField(TEXT("time"), d->time); - Reply->SetNumberField(TEXT("step"), StepCounter.fetch_add(1, std::memory_order_relaxed) + 1); - AppendClockFields(Reply, d->time); - TSharedPtr Obs = BuildStepObservations(Mgr, m, d, ActiveObservationLevel); - if (Obs.IsValid()) - Reply->SetObjectField(TEXT("per_articulation"), Obs); - TSharedPtr Scene = BuildEntitiesBlock(Mgr, m, d); - if (Scene.IsValid()) - Reply->SetObjectField(TEXT("entities"), Scene); - if (CameraSpec.Num() > 0) - { - TSharedPtr Cams = BuildCamerasBlock(Mgr, CameraSpec); - if (Cams.IsValid() && Cams->Values.Num() > 0) - Reply->SetObjectField(TEXT("cameras"), Cams); - } - // Puppet-mode perturbation: include the latest sample so the client - // can apply the editor click-drag widget's force to its own MjData. - if (Mgr->Perturbation) - { - FMjPerturbationSample Sample = Mgr->Perturbation->GetLatestPerturbationSample(); - if (Sample.BodyId > 0) - { - TSharedPtr Pert = MakeShared(); - Pert->SetNumberField(TEXT("body_id"), Sample.BodyId); - Pert->SetNumberField(TEXT("version"), Sample.Version); - TArray> Six; - for (int i = 0; i < 6; ++i) - Six.Add(MakeShared(Sample.Xfrc[i])); - Pert->SetArrayField(TEXT("xfrc"), Six); - Reply->SetObjectField(TEXT("perturbation"), Pert); - } - } - return Reply; - } - - // Direct mode. - TSharedPtr Cmd = MakeShared(); - int32 NSteps = 1; - Req->TryGetNumberField(TEXT("n_steps"), NSteps); - Cmd->Request.NSteps = NSteps > 0 ? NSteps : 1; - - const TSharedPtr* PerArt = nullptr; - if (Req->TryGetObjectField(TEXT("per_articulation"), PerArt) && PerArt && PerArt->IsValid()) - { - for (auto& Pair : (*PerArt)->Values) - { - const TSharedPtr* ArtObj = nullptr; - if (!Pair.Value->TryGetObject(ArtObj) || !ArtObj || !ArtObj->IsValid()) - continue; - - // control_mode override - FString CtlMode; - if ((*ArtObj)->TryGetStringField(TEXT("control_mode"), CtlMode)) - { - Cmd->Request.PerArticulationControlMode.Add(Pair.Key, CtlMode); - } - - const TArray>* CtrlList = nullptr; - if ((*ArtObj)->TryGetArrayField(TEXT("ctrl"), CtrlList) && CtrlList) - { - // Positional ctrl array: indexed in articulation actuator order. - if (AMjArticulation* Art = Cast(Mgr->GetArticulation(Pair.Key))) - { - TArray Acts = Art->GetActuators(); - for (int32 i = 0; i < CtrlList->Num() && i < Acts.Num(); ++i) - { - UMjActuator* A = Acts[i]; - if (!A) - continue; - FString LocalName = A->GetMjName(); - FString Prefix = Art->GetName() + TEXT("_"); - if (LocalName.StartsWith(Prefix)) - LocalName = LocalName.Mid(Prefix.Len()); - Cmd->Request.PerArticulationCtrl.FindOrAdd(Pair.Key).Add( - {LocalName, (float)(*CtrlList)[i]->AsNumber()}); - } - } - } - - // Named ctrl map alternative. - const TSharedPtr* CtrlMap = nullptr; - if ((*ArtObj)->TryGetObjectField(TEXT("ctrl_map"), CtrlMap) && CtrlMap && CtrlMap->IsValid()) - { - for (auto& KV : (*CtrlMap)->Values) - { - Cmd->Request.PerArticulationCtrl.FindOrAdd(Pair.Key).Add( - {KV.Key, (float)KV.Value->AsNumber()}); - } - } - - // xfrc_applied: { body_name: [fx,fy,fz,tx,ty,tz] }. Cleared after step. - const TSharedPtr* XfrcMap = nullptr; - if ((*ArtObj)->TryGetObjectField(TEXT("xfrc_applied"), XfrcMap) && XfrcMap && XfrcMap->IsValid()) - { - TMap>& BodyMap = Cmd->Request.PerArticulationXfrc.FindOrAdd(Pair.Key); - for (auto& KV : (*XfrcMap)->Values) - { - const TArray>* Arr = nullptr; - if (KV.Value->TryGetArray(Arr) && Arr && Arr->Num() == 6) - { - TArray& Six = BodyMap.FindOrAdd(KV.Key); - Six.SetNum(6); - for (int i = 0; i < 6; ++i) - Six[i] = (*Arr)[i]->AsNumber(); - } - } - } - } - } - - // Submit to physics-thread custom handler (no race) then wait. If the - // engine isn't running its async loop (test path), fall back to inline. - Cmd->Completion = FPlatformProcess::GetSynchEventFromPool(true); - - const bool bAsyncRunning = Mgr->PhysicsEngine->AsyncPhysicsFuture.IsValid(); - StepQueue.Enqueue(Cmd); - if (Mgr->PhysicsEngine->StepRequestEvent) - { - Mgr->PhysicsEngine->StepRequestEvent->Trigger(); - } - - if (!bAsyncRunning) - { - // Test / editor path: pump the handler synchronously so we don't - // block forever waiting for an engine that isn't ticking. - if (Mgr->PhysicsEngine->CustomStepHandler) - { - Mgr->PhysicsEngine->CustomStepHandler( - Mgr->PhysicsEngine->GetModel(), - Mgr->PhysicsEngine->GetData()); - } - } - - // 5-second hard cap so a wedged engine returns an error rather than - // wedging the RPC thread. Polled in 50ms slices so the bDraining - // flag (set when the bridge is being stopped) can short-circuit the - // wait without forcing the user to sit through the full 5s while - // the editor closes. - bool bSignaled = false; - { - const double Deadline = FPlatformTime::Seconds() + 5.0; - while (FPlatformTime::Seconds() < Deadline) - { - if (bDraining.load(std::memory_order_acquire)) - { - break; - } - if (Cmd->Completion->Wait(FTimespan::FromMilliseconds(50))) - { - bSignaled = true; - break; - } - } - } - - TSharedPtr Reply = MakeShared(); - if (bSignaled && Cmd->bDone) - { - Reply->SetStringField(TEXT("op"), TEXT("step_ok")); - Reply->SetNumberField(TEXT("time"), Cmd->ResultTime); - Reply->SetNumberField(TEXT("step"), Cmd->ResultStep); - AppendClockFields(Reply, Cmd->ResultTime); - if (Cmd->Observations.IsValid()) - Reply->SetObjectField(TEXT("per_articulation"), Cmd->Observations); - if (Cmd->Entities.IsValid()) - Reply->SetObjectField(TEXT("entities"), Cmd->Entities); - - if (CameraSpec.Num() > 0) - { - TSharedPtr Cams = BuildCamerasBlock(Mgr, CameraSpec); - if (Cams.IsValid() && Cams->Values.Num() > 0) - Reply->SetObjectField(TEXT("cameras"), Cams); - } - } - else - { - if (bDraining.load(std::memory_order_acquire)) - { - Reply = MakeError(TEXT("shutting_down"), - TEXT("Bridge stopping; Direct-mode step abandoned")); - } - else - { - Reply = MakeError(TEXT("step_timeout"), - TEXT("Direct-mode step did not complete within 5s")); - } - } - - return Reply; -} - -void FURLabRpcDispatcher::ApplyStepCtrl(AAMjManager* Manager, const FMjStepRequest& Req, - mjModel* m, mjData* d) -{ - if (!Manager) - return; - for (auto& Pair : Req.PerArticulationCtrl) - { - AMjArticulation* Art = Manager->GetArticulation(Pair.Key); - if (!Art) - continue; - - const FString Prefix = Art->GetName() + TEXT("_"); - TMap ByName; - ByName.Reserve(Art->GetActuators().Num() * 2); - for (UMjActuator* A : Art->GetActuators()) - { - if (!A) - continue; - FString FullName = A->GetMjName(); - FString Local = FullName.StartsWith(Prefix) ? FullName.Mid(Prefix.Len()) : FullName; - ByName.Add(Local, A); - ByName.Add(FullName, A); - } - - // Stage to actuator NetworkValue; AMjArticulation::ApplyControls - // copies it into d->ctrl every sub-step. Writing d->ctrl directly - // would be overwritten on the next sub-step. - for (const TPair& KV : Pair.Value) - { - UMjActuator** Found = ByName.Find(KV.Key); - if (!Found || !*Found) - continue; - (*Found)->SetNetworkControl(KV.Value); - } - } - - // xfrc_applied writes: per_articulation -> body_name -> 6-vec. - // MuJoCo clears d->xfrc_applied on every mj_step, so this is a one-shot - // impulse for the next mj_step n_steps loop. Body name lookup tries both - // the local (no-prefix) form and the prefixed full name. - if (m && d) - { - for (auto& APair : Req.PerArticulationXfrc) - { - FString ArtPrefix = APair.Key + TEXT("_"); - for (auto& BPair : APair.Value) - { - if (BPair.Value.Num() != 6) - continue; - FString FullName = ArtPrefix + BPair.Key; - int Bid = mj_name2id(m, mjOBJ_BODY, TCHAR_TO_UTF8(*FullName)); - if (Bid < 0) - Bid = mj_name2id(m, mjOBJ_BODY, TCHAR_TO_UTF8(*BPair.Key)); - if (Bid < 0 || Bid >= m->nbody) - continue; - for (int i = 0; i < 6; ++i) - d->xfrc_applied[6 * Bid + i] = (mjtNum)BPair.Value[i]; - } - } - } -} - -TSharedPtr FURLabRpcDispatcher::BuildStepObservations(AAMjManager* Manager, mjModel* m, mjData* d, - EObservationLevel Level) -{ - TSharedPtr PerArt = MakeShared(); - if (!Manager || !m || !d) - return PerArt; - - const bool bWantStandard = (Level == EObservationLevel::Standard) || (Level == EObservationLevel::Full); - const bool bWantFull = (Level == EObservationLevel::Full); - - for (AMjArticulation* Art : Manager->GetAllArticulations()) - { - if (!Art) - continue; - TSharedPtr ArtObj = MakeShared(); - - // qpos / qvel — present at every level. Joints are emitted in the - // discovery order that GetJoints() returns, which matches the MjModel - // jnt_id order at compile time. Per-joint qpos/qvel slot widths follow - // jnt_type. Wire-side consumers should always rebuild full m.nq / m.nv - // arrays from per-articulation slices in this same order. - TArray Joints = Art->GetJoints(); - TArray> QPos; - TArray> QVel; - for (UMjJoint* J : Joints) - { - if (!J) - continue; - int32 Id = J->GetMjID(); - if (Id < 0 || Id >= m->njnt) - continue; - int QAddr = m->jnt_qposadr[Id]; - int VAddr = m->jnt_dofadr[Id]; - int QSize = 1, VSize = 1; - switch (m->jnt_type[Id]) - { - case mjJNT_FREE: - QSize = 7; - VSize = 6; - break; - case mjJNT_BALL: - QSize = 4; - VSize = 3; - break; - case mjJNT_SLIDE: - case mjJNT_HINGE: - QSize = 1; - VSize = 1; - break; - } - for (int i = 0; i < QSize; ++i) - QPos.Add(MakeShared(d->qpos[QAddr + i])); - for (int i = 0; i < VSize; ++i) - QVel.Add(MakeShared(d->qvel[VAddr + i])); - } - ArtObj->SetArrayField(TEXT("qpos"), QPos); - ArtObj->SetArrayField(TEXT("qvel"), QVel); - - if (bWantStandard) - { - // ctrl positional array, same order as GetActuators(). - TArray> Ctrl; - TArray> Act; - for (UMjActuator* A : Art->GetActuators()) - { - if (!A) - continue; - int32 Id = A->GetMjID(); - if (Id < 0 || Id >= m->nu) - continue; - Ctrl.Add(MakeShared(d->ctrl[Id])); - // Each actuator's "act" slot, if it has one (intvelocity, muscle, ...) - int ActAddr = m->actuator_actadr ? m->actuator_actadr[Id] : -1; - if (ActAddr >= 0 && ActAddr < m->na) - Act.Add(MakeShared(d->act[ActAddr])); - else - Act.Add(MakeShared(0.0)); - } - ArtObj->SetArrayField(TEXT("ctrl"), Ctrl); - ArtObj->SetArrayField(TEXT("act"), Act); - - // sensors by name — use sensor MjID + dim. - TSharedPtr Sensors = MakeShared(); - TArray SensorComponents; - Art->GetComponents(SensorComponents); - FString Prefix = Art->GetName() + TEXT("_"); - for (UMjSensor* S : SensorComponents) - { - if (!S) - continue; - int32 Sid = S->GetMjID(); - if (Sid < 0 || Sid >= m->nsensor) - continue; - int Adr = m->sensor_adr[Sid]; - int Dim = m->sensor_dim[Sid]; - if (Adr < 0 || Dim <= 0 || (Adr + Dim) > m->nsensordata) - continue; - TArray> Vals; - for (int i = 0; i < Dim; ++i) - Vals.Add(MakeShared(d->sensordata[Adr + i])); - FString LocalName = S->GetMjName(); - if (LocalName.StartsWith(Prefix)) - LocalName = LocalName.Mid(Prefix.Len()); - Sensors->SetArrayField(LocalName, Vals); - } - ArtObj->SetObjectField(TEXT("sensors"), Sensors); - } - - if (bWantFull) - { - // body xpos/xquat — discovered through articulation's MjBody components. - TSharedPtr Bodies = MakeShared(); - TArray BodyComponents; - Art->GetComponents(BodyComponents); - FString Prefix = Art->GetName() + TEXT("_"); - for (UMjBody* B : BodyComponents) - { - if (!B || B->bIsDefault) - continue; - int32 Bid = B->GetMjID(); - if (Bid < 0 || Bid >= m->nbody) - continue; - TSharedPtr Bo = MakeShared(); - TArray> XPos, XQuat; - for (int i = 0; i < 3; ++i) - XPos.Add(MakeShared(d->xpos[Bid * 3 + i])); - for (int i = 0; i < 4; ++i) - XQuat.Add(MakeShared(d->xquat[Bid * 4 + i])); - Bo->SetArrayField(TEXT("xpos"), XPos); - Bo->SetArrayField(TEXT("xquat"), XQuat); - FString LocalName = B->GetMjName(); - if (LocalName.StartsWith(Prefix)) - LocalName = LocalName.Mid(Prefix.Len()); - Bodies->SetObjectField(LocalName, Bo); - } - ArtObj->SetObjectField(TEXT("bodies"), Bodies); - - // actuator_force per actuator (positional, same order as ctrl) - TArray> AForce; - for (UMjActuator* A : Art->GetActuators()) - { - if (!A) - continue; - int32 Id = A->GetMjID(); - if (Id < 0 || Id >= m->nu) - continue; - AForce.Add(MakeShared(d->actuator_force[Id])); - } - ArtObj->SetArrayField(TEXT("actuator_force"), AForce); - } - - // geometry_msgs/Twist-aligned: (linear.x, linear.y, angular.z) - // filled; rest stays zero. Only when a TwistController is attached. - if (UMjTwistController* TwistCtrl = Art->FindComponentByClass()) - { - const FVector Twist = TwistCtrl->GetTwist(); // (Vx, Vy, YawRate) - - TArray> Linear; - Linear.Add(MakeShared(Twist.X)); - Linear.Add(MakeShared(Twist.Y)); - Linear.Add(MakeShared(0.0)); - - TArray> Angular; - Angular.Add(MakeShared(0.0)); - Angular.Add(MakeShared(0.0)); - Angular.Add(MakeShared(Twist.Z)); - - TSharedPtr TwistObj = MakeShared(); - TwistObj->SetArrayField(TEXT("linear"), Linear); - TwistObj->SetArrayField(TEXT("angular"), Angular); - ArtObj->SetObjectField(TEXT("twist"), TwistObj); - - ArtObj->SetNumberField(TEXT("actions"), - static_cast(TwistCtrl->GetActiveActions())); - } - - PerArt->SetObjectField(Art->GetName(), ArtObj); - } - return PerArt; -} - -TSharedPtr FURLabRpcDispatcher::BuildCamerasBlock(AAMjManager* Manager, - const TMap& CameraSpec, int32 TimeoutMs) -{ - TSharedPtr Cams = MakeShared(); - if (!Manager || CameraSpec.Num() == 0) - return Cams; - - UWorld* World = Manager->GetWorld(); - if (!World) - return Cams; - - // Build a name -> camera lookup using the canonical "/" - // form the handshake exposes plus the bare camera name as a fallback. - TMap ByName; - for (AMjArticulation* Art : Manager->GetAllArticulations()) - { - if (!Art) - continue; - TArray Cameras; - Art->GetComponents(Cameras); - for (UMjCamera* C : Cameras) - { - if (!C || C->bIsDefault) - continue; - FString Qualified = Art->GetName() + TEXT("/") + C->GetName(); - ByName.Add(Qualified, C); - ByName.Add(C->GetName(), C); - } - } - - for (const TPair& Spec : CameraSpec) - { - UMjCamera** Found = ByName.Find(Spec.Key); - if (!Found || !*Found) - { - UE_LOG(LogURLabNet, Verbose, - TEXT("[BuildCamerasBlock] camera '%s' not found"), *Spec.Key); - continue; - } - UMjCamera* Cam = *Found; - - // Streaming auto-enables in UMjCamera::BeginPlay via - // UMjNetworkManager::RegisterCamera (bEnableAllCameras=true by - // default), so by the time we hit this path the camera is - // already streaming. The earlier worker-thread - // SetStreamingEnabled call here was dead code; left only the - // game-thread RequestReadback marshalling below, which is - // the actual fix — RenderTarget->GameThread_GetRenderTargetResource - // returns null on non-game threads and silently bails the - // readback, which is why include_cameras saw empty frames. - - // For "sync" we kick a readback and poll. RequestReadback - // must run on the game thread (it touches - // RenderTarget->GameThread_GetRenderTargetResource and enqueues - // a render command); calling it from the bridge worker thread - // makes Resource null and the readback never lands. We marshal - // here and wait briefly; the per-tick auto-readback in - // UMjCamera::TickComponent keeps PendingPixels fresh for the - // "latest" path so consumers without an explicit sync call - // still get frames. - if (Spec.Value == ECameraInclude::Sync) - { - FEvent* ReqDone = FPlatformProcess::GetSynchEventFromPool(/*bIsManualReset=*/false); - TWeakObjectPtr WeakCam(Cam); - AsyncTask(ENamedThreads::GameThread, [WeakCam, ReqDone]() { - if (UMjCamera* C = WeakCam.Get()) - { - C->RequestReadback(); - } - ReqDone->Trigger(); - }); - ReqDone->Wait(2000); - FPlatformProcess::ReturnSynchEventToPool(ReqDone); - - const double DeadlineSec = FPlatformTime::Seconds() + (TimeoutMs / 1000.0); - while (!Cam->IsReadbackReady() && FPlatformTime::Seconds() < DeadlineSec) - { - FPlatformProcess::Sleep(0.001f); - } - } - - TSharedPtr CamObj = MakeShared(); - CamObj->SetNumberField(TEXT("width"), Cam->resolution.Num() > 0 ? Cam->resolution[0] : 0); - CamObj->SetNumberField(TEXT("height"), Cam->resolution.Num() > 1 ? Cam->resolution[1] : 0); - - if (Cam->CaptureMode == EMjCameraMode::Depth) - { - TArray Pixels = Cam->ConsumeFloatPixels(); - if (Pixels.Num() == 0) - continue; - CamObj->SetStringField(TEXT("dtype"), TEXT("float32")); - FURLabMsgpackUtil::SetBinaryField(CamObj, TEXT("data"), - reinterpret_cast(Pixels.GetData()), - Pixels.Num() * sizeof(float)); - } - else - { - TArray Pixels = Cam->ConsumePixels(); - if (Pixels.Num() == 0) - continue; - // Real / SemSeg / InstanceSeg all ship 4-byte BGRA. Bridge - // discriminates the seg modes by the camera_topics handshake. - CamObj->SetStringField(TEXT("dtype"), TEXT("bgra8")); - FURLabMsgpackUtil::SetBinaryField(CamObj, TEXT("data"), - reinterpret_cast(Pixels.GetData()), - Pixels.Num() * sizeof(FColor)); - } - Cams->SetObjectField(Spec.Key, CamObj); - } - return Cams; -} - -TSharedPtr FURLabRpcDispatcher::BuildEntitiesBlock(AAMjManager* Manager, mjModel* m, mjData* d) -{ - TSharedPtr Scene = MakeShared(); - if (!Manager || !m || !d) - return Scene; - - // Prefer the cached scene-body record table when populated. Avoids a - // per-call TActorIterator walk on the physics thread. - auto BuildFromBody = [&](int32 Id, const FString& Name) { - if (Id < 0 || Id >= m->nbody) - return; - TSharedPtr Obj = MakeShared(); - TArray> XPos, XQuat; - for (int i = 0; i < 3; ++i) - XPos.Add(MakeShared(d->xpos[Id * 3 + i])); - for (int i = 0; i < 4; ++i) - XQuat.Add(MakeShared(d->xquat[Id * 4 + i])); - Obj->SetArrayField(TEXT("xpos"), XPos); - Obj->SetArrayField(TEXT("xquat"), XQuat); - - // Free-joint detection: a body with a single jntnum=1 of mjJNT_FREE - // owns a 7-vec qpos and 6-vec qvel. Stream both. Other joint types - // get xpos/xquat only — a kinematic-driven heightfield base, etc. - if (Id < m->nbody && m->body_jntnum && m->body_jntadr) - { - int FirstJnt = m->body_jntadr[Id]; - int NumJnt = m->body_jntnum[Id]; - if (FirstJnt >= 0 && NumJnt > 0 && FirstJnt < m->njnt && m->jnt_type[FirstJnt] == mjJNT_FREE) - { - int QAddr = m->jnt_qposadr[FirstJnt]; - int VAddr = m->jnt_dofadr[FirstJnt]; - TArray> QPos, QVel; - for (int i = 0; i < 7; ++i) - QPos.Add(MakeShared(d->qpos[QAddr + i])); - for (int i = 0; i < 6; ++i) - QVel.Add(MakeShared(d->qvel[VAddr + i])); - Obj->SetArrayField(TEXT("qpos"), QPos); - Obj->SetArrayField(TEXT("qvel"), QVel); - } - } - Scene->SetObjectField(Name, Obj); - }; - - // Cache fast path. - const TArray& Cache = Manager->GetEntities(); - if (Cache.Num() > 0) - { - for (const FMjEntityRecord& R : Cache) - BuildFromBody(R.MjId, R.Name); - return Scene; - } - - // Fallback: walk the world via TActorIterator. ONLY safe from the game - // thread -- the iterator asserts IsInGameThread(). DirectStepHandler / - // PuppetStepHandler run on the physics async thread, so when called from - // there with an empty cache (no scene bodies were registered), return an - // empty block rather than crashing. Tests / pre-cache callers on the - // game thread still use the fallback path. - if (!IsInGameThread()) - return Scene; - - UWorld* World = Manager->GetWorld(); - if (!World) - return Scene; - - TSet ArticSet; - for (AMjArticulation* A : Manager->GetAllArticulations()) - ArticSet.Add(A); - - for (TActorIterator It(World); It; ++It) - { - AActor* Actor = *It; - if (!Actor) - continue; - if (AMjArticulation* AsArt = Cast(Actor)) - { - if (ArticSet.Contains(AsArt)) - continue; - } - TArray Bodies; - Actor->GetComponents(Bodies); - for (UMjBody* B : Bodies) - { - if (!B || B->bIsDefault) - continue; - BuildFromBody(B->GetMjID(), B->GetMjName()); - } - } - return Scene; -} - -// ============================================================================= -// reset / set_mode -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleReset(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->m_model) - { - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - } - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - - int32 SeedVal = 0; - if (Req->TryGetNumberField(TEXT("seed"), SeedVal)) - { - Mgr->Seed = SeedVal; - // Modern mjOption has no "seed" field; mj_step is deterministic and - // doesn't depend on a stored seed (random elements come from - // user-set noise inputs, not an integrator-internal RNG). The seed - // is recorded on the manager so any RNG used by client code or by - // the recording layer can mirror it for reproducibility. UE itself - // does not reseed the integrator here. - } - - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - - FString KfName; - if (Req->TryGetStringField(TEXT("keyframe_name"), KfName) && !KfName.IsEmpty()) - { - int Kid = mj_name2id(m, mjOBJ_KEY, TCHAR_TO_UTF8(*KfName)); - if (Kid < 0) - return MakeError(TEXT("unknown_keyframe"), KfName); - mj_resetDataKeyframe(m, d, Kid); - } - else - { - mj_resetData(m, d); - } - - // Per-articulation qpos overrides (joint-name -> value). - const TSharedPtr* PerArt = nullptr; - if (Req->TryGetObjectField(TEXT("per_articulation_qpos"), PerArt) && PerArt && PerArt->IsValid()) - { - for (auto& APair : (*PerArt)->Values) - { - AMjArticulation* Art = Mgr->GetArticulation(APair.Key); - if (!Art) - continue; - const TSharedPtr* QObj = nullptr; - if (!APair.Value->TryGetObject(QObj) || !QObj || !QObj->IsValid()) - continue; - - FString Prefix = Art->GetName() + TEXT("_"); - for (auto& JPair : (*QObj)->Values) - { - FString FullName = Prefix + JPair.Key; - int Jid = mj_name2id(m, mjOBJ_JOINT, TCHAR_TO_UTF8(*FullName)); - if (Jid < 0) - Jid = mj_name2id(m, mjOBJ_JOINT, TCHAR_TO_UTF8(*JPair.Key)); - if (Jid < 0) - continue; - int QAddr = m->jnt_qposadr[Jid]; - d->qpos[QAddr] = (mjtNum)JPair.Value->AsNumber(); - } - } - } - mj_forward(m, d); - } - - StepCounter.store(0, std::memory_order_relaxed); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("reset_ok")); - Reply->SetNumberField(TEXT("time"), d->time); - Reply->SetNumberField(TEXT("step"), 0); - AppendClockFields(Reply, d->time); - TSharedPtr Obs = BuildStepObservations(Mgr, m, d, ActiveObservationLevel); - if (Obs.IsValid()) - Reply->SetObjectField(TEXT("per_articulation"), Obs); - return Reply; -} - -// Run mj_forward (kinematics + dynamics, no integration) and return -// observations. Lets a client write qpos / qvel then read consistent -// derived state (xpos, sensors, contacts, ...) without advancing time. -TSharedPtr FURLabRpcDispatcher::HandleForward(const TSharedPtr& /*Req*/) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->m_model) - { - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - } - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - mj_forward(m, d); - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("forward_ok")); - Reply->SetNumberField(TEXT("time"), d->time); - Reply->SetNumberField(TEXT("step"), StepCounter.load(std::memory_order_relaxed)); - AppendClockFields(Reply, d->time); - TSharedPtr Obs = BuildStepObservations(Mgr, m, d, ActiveObservationLevel); - if (Obs.IsValid()) - Reply->SetObjectField(TEXT("per_articulation"), Obs); - return Reply; -} - -TSharedPtr FURLabRpcDispatcher::HandleSetMode(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr) - return MakeError(TEXT("not_ready"), TEXT("Manager missing")); - - if (Mgr->StepMode != EStepMode::Auto) - { - return MakeError(TEXT("mode_locked_by_server"), - FString::Printf(TEXT("Project pinned StepMode to %s"), *StepModeToString(Mgr->StepMode))); - } - - FString ModeStr; - if (!Req->TryGetStringField(TEXT("mode"), ModeStr)) - return MakeError(TEXT("missing_field"), TEXT("set_mode requires 'mode'")); - - EStepMode NewMode; - if (!StepModeFromString(ModeStr, NewMode)) - return MakeError(TEXT("bad_mode"), FString::Printf(TEXT("Unknown mode '%s'"), *ModeStr)); - - EStepMode Prev = ActiveStepMode; - SetActiveStepMode(NewMode); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_mode_ok")); - Reply->SetStringField(TEXT("previous_mode"), StepModeToString(Prev)); - Reply->SetStringField(TEXT("current_mode"), StepModeToString(ActiveStepMode)); - return Reply; -} - -void FURLabRpcDispatcher::SetActiveStepMode(EStepMode NewMode) -{ - // Serialises install/uninstall side effects against concurrent - // set_mode calls; Dispatch releases DispatchMutex before handlers. - FScopeLock Lock(&DispatchMutex); - - const EStepMode CurMode = ActiveStepMode.load(std::memory_order_acquire); - if (NewMode == CurMode) - return; - - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr) - return; - - if (CurMode == EStepMode::Puppet) - UninstallPuppetHandler(); - if (CurMode == EStepMode::Direct) - UninstallDirectHandler(); - DrainQueuesForTest(); - - ActiveStepMode.store(NewMode, std::memory_order_release); - Mgr->EffectiveStepMode.store(NewMode, std::memory_order_release); - const bool bPaused = (NewMode != EStepMode::Live); - Mgr->bPublishersPaused.store(bPaused, std::memory_order_release); - FCameraZmqWorker::bPublishersPaused.store(bPaused, std::memory_order_release); - - if (NewMode == EStepMode::Puppet) - InstallPuppetHandler(); - else if (NewMode == EStepMode::Direct) - InstallDirectHandler(); - - // Engine defaults bIsPaused=true and is normally unpaused via the editor - // UI / hotkey. A remote client has no UI handle, so entering Direct or - // Direct/Puppet imply "client drives physics" — force unpause so the - // async loop calls CustomStepHandler and the request queue drains. - if (Mgr->PhysicsEngine && NewMode != EStepMode::Live) - { - if (Mgr->PhysicsEngine->bIsPaused) - { - Mgr->PhysicsEngine->SetPaused(false); - UE_LOG(LogURLabNet, Log, - TEXT("FURLabRpcDispatcher: unpaused PhysicsEngine for %s mode"), - *StepModeToString(NewMode)); - } - } - - UE_LOG(LogURLabNet, Log, TEXT("FURLabRpcDispatcher: step mode -> %s (publishers_paused=%s)"), - *StepModeToString(NewMode), bPaused ? TEXT("true") : TEXT("false")); -} - -void FURLabRpcDispatcher::InstallPuppetHandler() -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine) - return; - if (bPuppetHandlerInstalled) - return; - - UMjPhysicsEngine* Engine = Mgr->PhysicsEngine; - PuppetStepHandler = [this, Engine](mjModel* m, mjData* d) { - FMjPushStateRequest Req; - if (!PushStateQueue.Dequeue(Req)) - return; - if (Req.QPos.Num() == m->nq) - FMemory::Memcpy(d->qpos, Req.QPos.GetData(), m->nq * sizeof(mjtNum)); - if (Req.QVel.Num() == m->nv) - FMemory::Memcpy(d->qvel, Req.QVel.GetData(), m->nv * sizeof(mjtNum)); - if (Req.bIncludeCtrl && Req.Ctrl.Num() == m->nu) - FMemory::Memcpy(d->ctrl, Req.Ctrl.GetData(), m->nu * sizeof(mjtNum)); - d->time = Req.Time; - mj_forward(m, d); - if (Engine->OnPostStep) - Engine->OnPostStep(m, d); - }; - Engine->SetCustomStepHandler(PuppetStepHandler); - bPuppetHandlerInstalled = true; -} - -void FURLabRpcDispatcher::UninstallPuppetHandler() -{ - if (!bPuppetHandlerInstalled) - return; - if (AAMjManager* Mgr = OwnerMgr.Get()) - { - if (Mgr->PhysicsEngine) - Mgr->PhysicsEngine->ClearCustomStepHandler(); - } - bPuppetHandlerInstalled = false; - PuppetStepHandler = nullptr; -} - -void FURLabRpcDispatcher::InstallDirectHandler() -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine) - return; - if (bDirectHandlerInstalled) - return; - - UMjPhysicsEngine* Engine = Mgr->PhysicsEngine; - DirectStepHandler = [this, Engine, Mgr](mjModel* m, mjData* d) { - // Only the physics-engine async worker thread runs this handler, - // so d is exclusively owned for its duration. - TSharedPtr Cmd; - if (!StepQueue.Dequeue(Cmd) || !Cmd.IsValid()) - return; - - ApplyStepCtrl(Mgr, Cmd->Request, m, d); - - // control_mode="raw" per articulation bypasses the UE controller - // (NetworkValue treated as direct ctrl setpoint). Name-keyed so - // adding/removing articulations doesn't shift the mapping. - TMap SkipController; - for (AMjArticulation* Art : Mgr->GetAllArticulations()) - { - if (!Art) - continue; - const FString* Mode = Cmd->Request.PerArticulationControlMode.Find(Art->GetName()); - const bool bRaw = Mode && Mode->Equals(TEXT("raw"), ESearchCase::IgnoreCase); - SkipController.Add(Art, bRaw); - } - - for (int32 i = 0; i < Cmd->Request.NSteps; ++i) - { - for (AMjArticulation* Art : Mgr->GetAllArticulations()) - { - if (!Art) - continue; - const bool* bSkip = SkipController.Find(Art); - Art->ApplyControls(bSkip != nullptr && *bSkip); - } - mj_step(m, d); - if (Engine->OnPostStep) - Engine->OnPostStep(m, d); - } - Cmd->ResultTime = d->time; - Cmd->ResultStep = StepCounter.fetch_add(Cmd->Request.NSteps, std::memory_order_relaxed) - + Cmd->Request.NSteps; - Cmd->Observations = BuildStepObservations(Mgr, m, d, ActiveObservationLevel); - Cmd->Entities = BuildEntitiesBlock(Mgr, m, d); - Cmd->bDone = true; - if (Cmd->Completion) - Cmd->Completion->Trigger(); - }; - Engine->SetCustomStepHandler(DirectStepHandler); - bDirectHandlerInstalled = true; -} - -void FURLabRpcDispatcher::UninstallDirectHandler() -{ - if (!bDirectHandlerInstalled) - return; - if (AAMjManager* Mgr = OwnerMgr.Get()) - { - if (Mgr->PhysicsEngine) - Mgr->PhysicsEngine->ClearCustomStepHandler(); - } - bDirectHandlerInstalled = false; - DirectStepHandler = nullptr; -} - -// ============================================================================= -// configure_controller -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleConfigureController(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr) - return MakeError(TEXT("not_ready"), TEXT("Manager missing")); - - FString ArtName; - if (!Req->TryGetStringField(TEXT("articulation"), ArtName)) - return MakeError(TEXT("missing_field"), TEXT("configure_controller requires 'articulation'")); - - AMjArticulation* Art = Mgr->GetArticulation(ArtName); - if (!Art) - return MakeError(TEXT("unknown_articulation"), ArtName); - - UMjArticulationController* Ctrl = Art->FindComponentByClass(); - if (!Ctrl) - return MakeError(TEXT("no_controller"), FString::Printf(TEXT("Articulation '%s' has no controller"), *ArtName)); - - const TSharedPtr* Params = nullptr; - if (Req->TryGetObjectField(TEXT("params"), Params) && Params && Params->IsValid()) - { - Ctrl->ApplyConfig(*Params); - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("configure_controller_ok")); - Reply->SetStringField(TEXT("articulation"), ArtName); - - TSharedPtr Out = MakeShared(); - Ctrl->GetCurrentConfig(Out); - Reply->SetObjectField(TEXT("params"), Out); - return Reply; -} - -// ============================================================================= -// set_sim_options -// ============================================================================= - -namespace -{ -bool ParseIntegrator(const FString& S, EMjIntegrator& Out) -{ - if (S.Equals(TEXT("euler"), ESearchCase::IgnoreCase)) - { - Out = EMjIntegrator::Euler; - return true; - } - if (S.Equals(TEXT("rk4"), ESearchCase::IgnoreCase)) - { - Out = EMjIntegrator::RK4; - return true; - } - if (S.Equals(TEXT("implicit"), ESearchCase::IgnoreCase)) - { - Out = EMjIntegrator::Implicit; - return true; - } - if (S.Equals(TEXT("implicitfast"), ESearchCase::IgnoreCase)) - { - Out = EMjIntegrator::ImplicitFast; - return true; - } - return false; -} -FString IntegratorToString(EMjIntegrator I) -{ - switch (I) - { - case EMjIntegrator::Euler: - return TEXT("euler"); - case EMjIntegrator::RK4: - return TEXT("rk4"); - case EMjIntegrator::Implicit: - return TEXT("implicit"); - case EMjIntegrator::ImplicitFast: - return TEXT("implicitfast"); - } - return TEXT("euler"); -} -bool ParseCone(const FString& S, EMjCone& Out) -{ - if (S.Equals(TEXT("pyramidal"), ESearchCase::IgnoreCase)) - { - Out = EMjCone::Pyramidal; - return true; - } - if (S.Equals(TEXT("elliptic"), ESearchCase::IgnoreCase)) - { - Out = EMjCone::Elliptic; - return true; - } - return false; -} -FString ConeToString(EMjCone C) -{ - return C == EMjCone::Elliptic ? TEXT("elliptic") : TEXT("pyramidal"); -} -bool ParseSolver(const FString& S, EMjSolver& Out) -{ - if (S.Equals(TEXT("pgs"), ESearchCase::IgnoreCase)) - { - Out = EMjSolver::PGS; - return true; - } - if (S.Equals(TEXT("cg"), ESearchCase::IgnoreCase)) - { - Out = EMjSolver::CG; - return true; - } - if (S.Equals(TEXT("newton"), ESearchCase::IgnoreCase)) - { - Out = EMjSolver::Newton; - return true; - } - return false; -} -FString SolverToString(EMjSolver S) -{ - switch (S) - { - case EMjSolver::PGS: - return TEXT("pgs"); - case EMjSolver::CG: - return TEXT("cg"); - case EMjSolver::Newton: - return TEXT("newton"); - } - return TEXT("newton"); -} - -bool TryReadVec3(const TSharedPtr& Obj, const TCHAR* Key, double Out[3]) -{ - const TArray>* Arr = nullptr; - if (!Obj->TryGetArrayField(Key, Arr) || !Arr || Arr->Num() != 3) - return false; - Out[0] = (*Arr)[0]->AsNumber(); - Out[1] = (*Arr)[1]->AsNumber(); - Out[2] = (*Arr)[2]->AsNumber(); - return true; -} -} // namespace - -TSharedPtr FURLabRpcDispatcher::HandleSetSimOptions(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine) - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - if (!m) - return MakeError(TEXT("not_ready"), TEXT("mjModel not compiled")); - - const TSharedPtr* OptsPtr = nullptr; - if (!Req->TryGetObjectField(TEXT("options"), OptsPtr) || !OptsPtr || !(*OptsPtr).IsValid()) - return MakeError(TEXT("missing_field"), TEXT("set_sim_options requires 'options' object")); - const TSharedPtr& Opts = *OptsPtr; - - FMjOptionGenerated& O = Mgr->PhysicsEngine->Options; - - double DNum = 0.0; - if (Opts->TryGetNumberField(TEXT("timestep"), DNum)) - { - O.Timestep = (float)DNum; - O.bOverride_Timestep = true; - } - - // Wire is MJ-native SI; FMjOptionGenerated stores UE cm/s² with Y-flip and - // ApplyOverridesToModel reverses that, so pre-bake the inverse here. - double V3[3]; - if (TryReadVec3(Opts, TEXT("gravity"), V3)) - { - O.Gravity = FVector((float)(V3[0] * 100.0), (float)(-V3[1] * 100.0), (float)(V3[2] * 100.0)); - O.bOverride_Gravity = true; - } - if (TryReadVec3(Opts, TEXT("wind"), V3)) - { - O.Wind = FVector((float)(V3[0] * 100.0), (float)(-V3[1] * 100.0), (float)(V3[2] * 100.0)); - O.bOverride_Wind = true; - } - if (TryReadVec3(Opts, TEXT("magnetic"), V3)) - { - O.Magnetic = FVector((float)V3[0], (float)-V3[1], (float)V3[2]); - O.bOverride_Magnetic = true; - } - - if (Opts->TryGetNumberField(TEXT("density"), DNum)) - { - O.Density = (float)DNum; - O.bOverride_Density = true; - } - if (Opts->TryGetNumberField(TEXT("viscosity"), DNum)) - { - O.Viscosity = (float)DNum; - O.bOverride_Viscosity = true; - } - if (Opts->TryGetNumberField(TEXT("impratio"), DNum)) - { - O.Impratio = (float)DNum; - O.bOverride_Impratio = true; - } - if (Opts->TryGetNumberField(TEXT("tolerance"), DNum)) - { - O.Tolerance = (float)DNum; - O.bOverride_Tolerance = true; - } - - int32 INum = 0; - if (Opts->TryGetNumberField(TEXT("iterations"), INum)) - { - O.Iterations = INum; - O.bOverride_Iterations = true; - } - if (Opts->TryGetNumberField(TEXT("ls_iterations"), INum)) - { - O.LsIterations = INum; - O.bOverride_LsIterations = true; - } - - FString SNum; - if (Opts->TryGetStringField(TEXT("integrator"), SNum)) - { - EMjIntegrator E; - if (!ParseIntegrator(SNum, E)) - return MakeError(TEXT("bad_value"), FString::Printf(TEXT("unknown integrator '%s'"), *SNum)); - O.Integrator = E; - O.bOverride_Integrator = true; - } - if (Opts->TryGetStringField(TEXT("cone"), SNum)) - { - EMjCone E; - if (!ParseCone(SNum, E)) - return MakeError(TEXT("bad_value"), FString::Printf(TEXT("unknown cone '%s'"), *SNum)); - O.Cone = E; - O.bOverride_Cone = true; - } - if (Opts->TryGetStringField(TEXT("solver"), SNum)) - { - EMjSolver E; - if (!ParseSolver(SNum, E)) - return MakeError(TEXT("bad_value"), FString::Printf(TEXT("unknown solver '%s'"), *SNum)); - O.Solver = E; - O.bOverride_Solver = true; - } - - if (Opts->TryGetNumberField(TEXT("noslip_iterations"), INum)) - { - O.NoslipIterations = INum; - O.bOverride_NoslipIterations = true; - } - if (Opts->TryGetNumberField(TEXT("noslip_tolerance"), DNum)) - { - O.NoslipTolerance = (float)DNum; - O.bOverride_NoslipTolerance = true; - } - if (Opts->TryGetNumberField(TEXT("ccd_iterations"), INum)) - { - O.CCD_Iterations = INum; - O.bOverride_CCD_Iterations = true; - } - if (Opts->TryGetNumberField(TEXT("ccd_tolerance"), DNum)) - { - O.CCD_Tolerance = (float)DNum; - O.bOverride_CCD_Tolerance = true; - } - - bool BNum = false; - if (Opts->TryGetBoolField(TEXT("enable_multiccd"), BNum)) - { - O.bEnableMultiCCD = BNum; - } - if (Opts->TryGetBoolField(TEXT("enable_sleep"), BNum)) - { - O.bEnableSleep = BNum; - } - if (Opts->TryGetNumberField(TEXT("sleep_tolerance"), DNum)) - { - O.SleepTolerance = (float)DNum; - } - - // Raw disable / enable bit masks. Values are bitwise-ORs of - // mujoco/mjmodel.h mjtDisableBit / mjtEnableBit constants. - // Applied BEFORE FMjOptionGenerated::ApplyOverridesToModel so any named - // bits the caller also set (enable_sleep / enable_multiccd) win on - // top of the raw mask. Treat the raw masks as a coarse baseline. - int32 DisableMask = 0; - if (Opts->TryGetNumberField(TEXT("disableflags"), DisableMask)) - { - m->opt.disableflags = DisableMask; - } - int32 EnableMask = 0; - if (Opts->TryGetNumberField(TEXT("enableflags"), EnableMask)) - { - m->opt.enableflags = EnableMask; - } - - O.ApplyOverridesToModel(m); - - // Worker thread pool (mju_threadpool). Not a MuJoCo option-struct field — - // it's a URLab engine setting applied to the live mjData. Clamped to the - // detected CPU core count; ApplyThreadPool is idempotent. - int32 NumThreads = 0; - if (Opts->TryGetNumberField(TEXT("num_worker_threads"), NumThreads)) - { - Mgr->PhysicsEngine->NumWorkerThreads = - FMath::Clamp(NumThreads, 0, UMjPhysicsEngine::MaxWorkerThreads()); - Mgr->PhysicsEngine->ApplyThreadPool(); - } - - UE_LOG(LogURLabNet, Log, - TEXT("FURLabRpcDispatcher: set_sim_options applied (timestep=%.5fs, gravity=[%.3f %.3f %.3f] m/s²)"), - m->opt.timestep, m->opt.gravity[0], m->opt.gravity[1], m->opt.gravity[2]); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_sim_options_ok")); - - TSharedPtr Out = MakeShared(); - Out->SetNumberField(TEXT("timestep"), m->opt.timestep); - Out->SetNumberField(TEXT("num_worker_threads"), Mgr->PhysicsEngine->NumWorkerThreads); - Out->SetNumberField(TEXT("max_worker_threads"), UMjPhysicsEngine::MaxWorkerThreads()); - { - TArray> G; - G.Add(MakeShared(m->opt.gravity[0])); - G.Add(MakeShared(m->opt.gravity[1])); - G.Add(MakeShared(m->opt.gravity[2])); - Out->SetArrayField(TEXT("gravity"), G); - TArray> W; - W.Add(MakeShared(m->opt.wind[0])); - W.Add(MakeShared(m->opt.wind[1])); - W.Add(MakeShared(m->opt.wind[2])); - Out->SetArrayField(TEXT("wind"), W); - TArray> Mg; - Mg.Add(MakeShared(m->opt.magnetic[0])); - Mg.Add(MakeShared(m->opt.magnetic[1])); - Mg.Add(MakeShared(m->opt.magnetic[2])); - Out->SetArrayField(TEXT("magnetic"), Mg); - } - Out->SetNumberField(TEXT("density"), m->opt.density); - Out->SetNumberField(TEXT("viscosity"), m->opt.viscosity); - Out->SetNumberField(TEXT("impratio"), m->opt.impratio); - Out->SetNumberField(TEXT("tolerance"), m->opt.tolerance); - Out->SetNumberField(TEXT("iterations"), m->opt.iterations); - Out->SetNumberField(TEXT("ls_iterations"), m->opt.ls_iterations); - Out->SetStringField(TEXT("integrator"), IntegratorToString((EMjIntegrator)m->opt.integrator)); - Out->SetStringField(TEXT("cone"), ConeToString((EMjCone)m->opt.cone)); - Out->SetStringField(TEXT("solver"), SolverToString((EMjSolver)m->opt.solver)); - Out->SetNumberField(TEXT("noslip_iterations"), m->opt.noslip_iterations); - Out->SetNumberField(TEXT("noslip_tolerance"), m->opt.noslip_tolerance); - Out->SetNumberField(TEXT("ccd_iterations"), m->opt.ccd_iterations); - Out->SetNumberField(TEXT("ccd_tolerance"), m->opt.ccd_tolerance); - - constexpr int MJ_ENBL_MULTICCD = 1 << 4; - constexpr int MJ_ENBL_SLEEP = 1 << 5; - Out->SetBoolField(TEXT("enable_multiccd"), (m->opt.enableflags & MJ_ENBL_MULTICCD) != 0); - Out->SetBoolField(TEXT("enable_sleep"), (m->opt.enableflags & MJ_ENBL_SLEEP) != 0); - Out->SetNumberField(TEXT("sleep_tolerance"), m->opt.sleep_tolerance); - - // Echo the raw bit masks so callers using disableflags / enableflags - // can verify the final composed state (named overrides + raw mask). - Out->SetNumberField(TEXT("disableflags"), (int32)m->opt.disableflags); - Out->SetNumberField(TEXT("enableflags"), (int32)m->opt.enableflags); - - Reply->SetObjectField(TEXT("options"), Out); - return Reply; -} - -// ============================================================================= -// set_sim_speed -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleSetSimSpeed(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine) - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - - double Pct = 0.0; - if (!Req->TryGetNumberField(TEXT("percent"), Pct)) - return MakeError(TEXT("missing_field"), TEXT("set_sim_speed requires 'percent'")); - - // Engine clamps internally (5..100); echo back so the caller sees what stuck. - Mgr->PhysicsEngine->SimSpeedPercent = (float)Pct; - const float Effective = FMath::Clamp((float)Pct, 5.0f, 100.0f); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_sim_speed_ok")); - Reply->SetNumberField(TEXT("percent"), Effective); - return Reply; -} - -// ============================================================================= -// set_control_source -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleSetControlSource(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine) - return MakeError(TEXT("not_ready"), TEXT("PhysicsEngine not initialised")); - - FString SourceStr; - if (!Req->TryGetStringField(TEXT("source"), SourceStr)) - return MakeError(TEXT("missing_field"), TEXT("set_control_source requires 'source' (\"zmq\" | \"ui\")")); - - EControlSource NewSource; - if (SourceStr.Equals(TEXT("zmq"), ESearchCase::IgnoreCase)) - NewSource = EControlSource::ZMQ; - else if (SourceStr.Equals(TEXT("ui"), ESearchCase::IgnoreCase)) - NewSource = EControlSource::UI; - else - return MakeError(TEXT("bad_value"), FString::Printf(TEXT("unknown source '%s'"), *SourceStr)); - - FString ArtName; - Req->TryGetStringField(TEXT("articulation"), ArtName); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_control_source_ok")); - Reply->SetStringField(TEXT("source"), SourceStr.ToLower()); - - if (ArtName.IsEmpty()) - { - // Global: update engine + every articulation so the per-actor field - // doesn't keep stale state after a global flip. - Mgr->PhysicsEngine->SetControlSource(NewSource); - for (AMjArticulation* Art : Mgr->GetAllArticulations()) - { - if (Art) - Art->ControlSource = (uint8)NewSource; - } - Reply->SetStringField(TEXT("scope"), TEXT("global")); - } - else - { - AMjArticulation* Art = Mgr->GetArticulation(ArtName); - if (!Art) - return MakeError(TEXT("unknown_articulation"), ArtName); - Art->ControlSource = (uint8)NewSource; - Reply->SetStringField(TEXT("scope"), TEXT("articulation")); - Reply->SetStringField(TEXT("articulation"), ArtName); - } - return Reply; -} - -// ============================================================================= -// set_twist -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleSetTwist(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr) - return MakeError(TEXT("not_ready"), TEXT("Manager missing")); - - FString ArtName; - if (!Req->TryGetStringField(TEXT("articulation"), ArtName)) - return MakeError(TEXT("missing_field"), TEXT("set_twist requires 'articulation'")); - - AMjArticulation* Art = Mgr->GetArticulation(ArtName); - if (!Art) - return MakeError(TEXT("unknown_articulation"), ArtName); - - UMjTwistController* TC = Art->FindComponentByClass(); - if (!TC) - return MakeError(TEXT("no_twist_controller"), - FString::Printf(TEXT("Articulation '%s' has no UMjTwistController"), *ArtName)); - - // Wire format mirrors how the bridge already reads twist: linear is - // (vx, vy, _) m/s, angular is (_, _, yaw_rate) rad/s. Tuple slots - // beyond the ones used are accepted but ignored. - auto ReadAxis = [](const TArray>* Arr, int32 Idx, float& Out) { - if (Arr && Arr->IsValidIndex(Idx)) - Out = (float)(*Arr)[Idx]->AsNumber(); - }; - - float Vx = 0.f, Vy = 0.f, YawRate = 0.f; - const TArray>* LinArr = nullptr; - const TArray>* AngArr = nullptr; - Req->TryGetArrayField(TEXT("linear"), LinArr); - Req->TryGetArrayField(TEXT("angular"), AngArr); - ReadAxis(LinArr, 0, Vx); - ReadAxis(LinArr, 1, Vy); - ReadAxis(AngArr, 2, YawRate); - - TC->SetTwist(Vx, Vy, YawRate); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_twist_ok")); - Reply->SetStringField(TEXT("articulation"), ArtName); - { - TArray> L; - L.Add(MakeShared(Vx)); - L.Add(MakeShared(Vy)); - L.Add(MakeShared(0.0)); - Reply->SetArrayField(TEXT("linear"), L); - TArray> A; - A.Add(MakeShared(0.0)); - A.Add(MakeShared(0.0)); - A.Add(MakeShared(YawRate)); - Reply->SetArrayField(TEXT("angular"), A); - } - return Reply; -} - -// ============================================================================= -// set_qpos — manager-required runtime write to a single articulation's qpos. -// -// Two write modes: -// - Free-base 7-vec shortcut: len=7 and the first joint is mjJNT_FREE, -// writes only the 7 free-joint slots (xyz + quat). Skips dof joints. -// - Full per-articulation qpos: len matches the articulation's total qpos -// dim (sum of per-joint slot widths in GetJoints() order). Writes the -// whole slice. -// Always calls mj_forward after the write, mirroring the puppet push-state -// path so derived quantities (xpos, sensors) reflect the new state. -// ============================================================================= -TSharedPtr FURLabRpcDispatcher::HandleSetQpos(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) - return MakeError(TEXT("not_ready"), TEXT("Manager not initialised")); - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - if (!m || !d) - return MakeError(TEXT("not_ready"), TEXT("MjModel/MjData missing")); - - // target/target_by wire shape. target_by="actor_name" looks up via - // the manager's GetArticulation (UE name match); default - // "actor_id" walks ActorId. - FString Target, By; - Req->TryGetStringField(TEXT("target"), Target); - Req->TryGetStringField(TEXT("target_by"), By); - if (Target.IsEmpty()) - { - return MakeError(TEXT("missing_field"), - TEXT("set_qpos: missing 'target' field")); - } - const bool bByName = By.Equals(TEXT("actor_name"), ESearchCase::IgnoreCase); - - AMjArticulation* Art = nullptr; - if (bByName) - { - Art = Mgr->GetArticulation(Target); - } - else - { - for (AMjArticulation* A : Mgr->GetAllArticulations()) - { - if (A && A->ActorId.Equals(Target)) - { - Art = A; - break; - } - } - } - if (!Art) - { - return MakeError(TEXT("unknown_articulation"), Target); - } - - const TArray>* QPosArr = nullptr; - if (!Req->TryGetArrayField(TEXT("qpos"), QPosArr) || !QPosArr) - return MakeError(TEXT("missing_field"), TEXT("set_qpos requires 'qpos' array")); - - struct FJointSlot - { - int32 Adr; - int32 Size; - int32 Type; - }; - TArray Slots; - int32 ArtQDim = 0; - for (UMjJoint* J : Art->GetJoints()) - { - if (!J) - continue; - int32 Id = J->GetMjID(); - if (Id < 0 || Id >= m->njnt) - continue; - int32 Size = 1; - switch (m->jnt_type[Id]) - { - case mjJNT_FREE: - Size = 7; - break; - case mjJNT_BALL: - Size = 4; - break; - case mjJNT_SLIDE: - case mjJNT_HINGE: - Size = 1; - break; - } - Slots.Add({m->jnt_qposadr[Id], Size, m->jnt_type[Id]}); - ArtQDim += Size; - } - - if (Slots.Num() == 0) - return MakeError(TEXT("no_joints"), - TEXT("Articulation has no joints; nothing to write")); - - const int32 InN = QPosArr->Num(); - bool bFreeBaseShortcut = false; - if (InN == 7 && Slots[0].Type == mjJNT_FREE && ArtQDim != 7) - bFreeBaseShortcut = true; - else if (InN != ArtQDim) - return MakeError(TEXT("dim_mismatch"), - FString::Printf( - TEXT("qpos length %d != articulation qpos dim %d (free-base shortcut requires len=7 with FREE root)"), - InN, ArtQDim)); - - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - if (bFreeBaseShortcut) - { - const int32 Adr = Slots[0].Adr; - for (int32 i = 0; i < 7; ++i) - d->qpos[Adr + i] = (mjtNum)(*QPosArr)[i]->AsNumber(); - } - else - { - int32 Cursor = 0; - for (const FJointSlot& S : Slots) - { - for (int32 i = 0; i < S.Size; ++i, ++Cursor) - d->qpos[S.Adr + i] = (mjtNum)(*QPosArr)[Cursor]->AsNumber(); - } - } - mj_forward(m, d); - } - - TArray> Out; - if (bFreeBaseShortcut) - { - const int32 Adr = Slots[0].Adr; - for (int32 i = 0; i < 7; ++i) - Out.Add(MakeShared(d->qpos[Adr + i])); - } - else - { - for (const FJointSlot& S : Slots) - for (int32 i = 0; i < S.Size; ++i) - Out.Add(MakeShared(d->qpos[S.Adr + i])); - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_qpos_ok")); - // Echo back the resolved actor identifiers so the caller can - // confirm which articulation actually got the write. `target` - // matches the request's target field; `actor_name` is the UE - // name (always present, even if actor_id was the lookup key). - Reply->SetStringField(TEXT("target"), Target); - Reply->SetStringField(TEXT("actor_name"), Art->GetName()); - if (!Art->ActorId.IsEmpty()) - Reply->SetStringField(TEXT("actor_id"), Art->ActorId); - Reply->SetArrayField(TEXT("qpos"), Out); - Reply->SetBoolField(TEXT("free_base_shortcut"), bFreeBaseShortcut); - return Reply; -} - -// ============================================================================= -// set_mocap_pose / read_mocap_pose / get_contacts — runtime MJ-side reads/writes. -// -// All three operate directly on the live mjModel/mjData under the engine's -// CallbackMutex (same as set_qpos). Body name lookup uses mj_name2id with -// the full compiled MJ name (URLab prefixes are already included). -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleSetMocapPose(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) - return MakeError(TEXT("not_ready"), TEXT("Manager not initialised")); - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - if (!m || !d) - return MakeError(TEXT("not_ready"), TEXT("MjModel/MjData missing")); - - FString Body; - Req->TryGetStringField(TEXT("body"), Body); - if (Body.IsEmpty()) - return MakeError(TEXT("missing_field"), TEXT("set_mocap_pose: missing 'body'")); - - const int32 BodyId = mj_name2id(m, mjOBJ_BODY, TCHAR_TO_UTF8(*Body)); - if (BodyId < 0) - return MakeError(TEXT("unknown_body"), Body); - - const int32 MocapId = m->body_mocapid[BodyId]; - if (MocapId < 0) - return MakeError(TEXT("not_mocap_body"), - FString::Printf(TEXT("Body '%s' is not a mocap body"), *Body)); - - const TArray>* PosArr = nullptr; - const TArray>* QuatArr = nullptr; - const bool bHasPos = Req->TryGetArrayField(TEXT("pos"), PosArr) && PosArr && PosArr->Num() == 3; - const bool bHasQuat = Req->TryGetArrayField(TEXT("quat"), QuatArr) && QuatArr && QuatArr->Num() == 4; - if (!bHasPos && !bHasQuat) - return MakeError(TEXT("missing_field"), - TEXT("set_mocap_pose requires at least one of pos[3] or quat[4]")); - - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - if (bHasPos) - { - for (int32 i = 0; i < 3; ++i) - d->mocap_pos[3 * MocapId + i] = (mjtNum)(*PosArr)[i]->AsNumber(); - } - if (bHasQuat) - { - for (int32 i = 0; i < 4; ++i) - d->mocap_quat[4 * MocapId + i] = (mjtNum)(*QuatArr)[i]->AsNumber(); - } - } - - TArray> PosOut, QuatOut; - for (int32 i = 0; i < 3; ++i) - PosOut.Add(MakeShared(d->mocap_pos[3 * MocapId + i])); - for (int32 i = 0; i < 4; ++i) - QuatOut.Add(MakeShared(d->mocap_quat[4 * MocapId + i])); - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("set_mocap_pose_ok")); - Reply->SetStringField(TEXT("body"), Body); - Reply->SetArrayField(TEXT("pos"), PosOut); - Reply->SetArrayField(TEXT("quat"), QuatOut); - return Reply; -} - -TSharedPtr FURLabRpcDispatcher::HandleReadMocapPose(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) - return MakeError(TEXT("not_ready"), TEXT("Manager not initialised")); - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - if (!m || !d) - return MakeError(TEXT("not_ready"), TEXT("MjModel/MjData missing")); - - FString Body; - Req->TryGetStringField(TEXT("body"), Body); - if (Body.IsEmpty()) - return MakeError(TEXT("missing_field"), TEXT("read_mocap_pose: missing 'body'")); - - const int32 BodyId = mj_name2id(m, mjOBJ_BODY, TCHAR_TO_UTF8(*Body)); - if (BodyId < 0) - return MakeError(TEXT("unknown_body"), Body); - - const int32 MocapId = m->body_mocapid[BodyId]; - if (MocapId < 0) - return MakeError(TEXT("not_mocap_body"), - FString::Printf(TEXT("Body '%s' is not a mocap body"), *Body)); - - TArray> PosOut, QuatOut; - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - for (int32 i = 0; i < 3; ++i) - PosOut.Add(MakeShared(d->mocap_pos[3 * MocapId + i])); - for (int32 i = 0; i < 4; ++i) - QuatOut.Add(MakeShared(d->mocap_quat[4 * MocapId + i])); - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("read_mocap_pose_ok")); - Reply->SetStringField(TEXT("body"), Body); - Reply->SetArrayField(TEXT("pos"), PosOut); - Reply->SetArrayField(TEXT("quat"), QuatOut); - return Reply; -} - -TSharedPtr FURLabRpcDispatcher::HandleGetContacts(const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) - return MakeError(TEXT("not_ready"), TEXT("Manager not initialised")); - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - mjData* d = Mgr->PhysicsEngine->GetData(); - if (!m || !d) - return MakeError(TEXT("not_ready"), TEXT("MjModel/MjData missing")); - - int32 MaxContacts = 64; - { - int32 Cap = 0; - if (Req->TryGetNumberField(TEXT("max_contacts"), Cap) && Cap > 0) - MaxContacts = Cap; - } - - // Optional filter: {body1?, body2?, geom1?, geom2?}. AND across set fields. - FString FBody1, FBody2, FGeom1, FGeom2; - const TSharedPtr* FilterObj = nullptr; - if (Req->TryGetObjectField(TEXT("filter"), FilterObj) && FilterObj && *FilterObj) - { - (*FilterObj)->TryGetStringField(TEXT("body1"), FBody1); - (*FilterObj)->TryGetStringField(TEXT("body2"), FBody2); - (*FilterObj)->TryGetStringField(TEXT("geom1"), FGeom1); - (*FilterObj)->TryGetStringField(TEXT("geom2"), FGeom2); - } - - auto NameOrEmpty = [&](int Type, int Id) -> FString { - if (Id < 0) - return FString(); - const char* p = mj_id2name(m, Type, Id); - return p ? FString(UTF8_TO_TCHAR(p)) : FString(); - }; - - TArray> Out; - bool bTruncated = false; - int32 Matched = 0; - - { - FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); - const int32 N = d->ncon; - for (int32 i = 0; i < N; ++i) - { - const mjContact& c = d->contact[i]; - const int32 G1 = c.geom[0]; - const int32 G2 = c.geom[1]; - const int32 B1 = (G1 >= 0 && G1 < m->ngeom) ? m->geom_bodyid[G1] : -1; - const int32 B2 = (G2 >= 0 && G2 < m->ngeom) ? m->geom_bodyid[G2] : -1; - const FString G1Name = NameOrEmpty(mjOBJ_GEOM, G1); - const FString G2Name = NameOrEmpty(mjOBJ_GEOM, G2); - const FString B1Name = NameOrEmpty(mjOBJ_BODY, B1); - const FString B2Name = NameOrEmpty(mjOBJ_BODY, B2); - - if (!FGeom1.IsEmpty() && !G1Name.Equals(FGeom1)) - continue; - if (!FGeom2.IsEmpty() && !G2Name.Equals(FGeom2)) - continue; - if (!FBody1.IsEmpty() && !B1Name.Equals(FBody1)) - continue; - if (!FBody2.IsEmpty() && !B2Name.Equals(FBody2)) - continue; - - if (Matched >= MaxContacts) - { - bTruncated = true; - break; - } - - mjtNum Force[6] = {0}; - mj_contactForce(m, d, i, Force); - - TArray> Pos; - for (int32 k = 0; k < 3; ++k) - Pos.Add(MakeShared(c.pos[k])); - // First row of the contact frame is the contact normal. - TArray> Normal; - for (int32 k = 0; k < 3; ++k) - Normal.Add(MakeShared(c.frame[k])); - TArray> ForceArr; - for (int32 k = 0; k < 6; ++k) - ForceArr.Add(MakeShared(Force[k])); - - TSharedPtr CObj = MakeShared(); - CObj->SetStringField(TEXT("geom1"), G1Name); - CObj->SetStringField(TEXT("geom2"), G2Name); - CObj->SetStringField(TEXT("body1"), B1Name); - CObj->SetStringField(TEXT("body2"), B2Name); - CObj->SetArrayField(TEXT("pos"), Pos); - CObj->SetArrayField(TEXT("normal"), Normal); - CObj->SetNumberField(TEXT("dist"), c.dist); - CObj->SetArrayField(TEXT("force"), ForceArr); - Out.Add(MakeShared(CObj)); - ++Matched; - } - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("get_contacts_ok")); - Reply->SetNumberField(TEXT("n_contacts"), Matched); - Reply->SetBoolField(TEXT("truncated"), bTruncated); - Reply->SetArrayField(TEXT("contacts"), Out); - return Reply; -} - -TSharedPtr FURLabRpcDispatcher::HandleListKeyframes(const TSharedPtr& /*Req*/) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) - return MakeError(TEXT("not_ready"), TEXT("Manager not initialised")); - - mjModel* m = Mgr->PhysicsEngine->GetModel(); - if (!m) - return MakeError(TEXT("not_ready"), TEXT("MjModel missing")); - - auto Slice = [](const mjtNum* src, int32 stride, int32 idx, int32 width) { - TArray> Out; - if (!src || width <= 0) - return Out; - for (int32 k = 0; k < width; ++k) - Out.Add(MakeShared(src[idx * stride + k])); - return Out; - }; - - TArray> Keys; - for (int32 i = 0; i < m->nkey; ++i) - { - const char* NameC = mj_id2name(m, mjOBJ_KEY, i); - TSharedPtr K = MakeShared(); - K->SetStringField(TEXT("name"), NameC ? UTF8_TO_TCHAR(NameC) : TEXT("")); - K->SetNumberField(TEXT("time"), m->key_time ? m->key_time[i] : 0.0); - K->SetArrayField(TEXT("qpos"), Slice(m->key_qpos, m->nq, i, m->nq)); - K->SetArrayField(TEXT("qvel"), Slice(m->key_qvel, m->nv, i, m->nv)); - K->SetArrayField(TEXT("ctrl"), Slice(m->key_ctrl, m->nu, i, m->nu)); - K->SetArrayField(TEXT("mocap_pos"), Slice(m->key_mpos, m->nmocap * 3, i, m->nmocap * 3)); - K->SetArrayField(TEXT("mocap_quat"), Slice(m->key_mquat, m->nmocap * 4, i, m->nmocap * 4)); - Keys.Add(MakeShared(K)); - } - - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("list_keyframes_ok")); - Reply->SetArrayField(TEXT("keyframes"), Keys); - return Reply; -} - -// ============================================================================= -// recording_* / replay_* delegate to AMjReplayManager -// ============================================================================= - -TSharedPtr FURLabRpcDispatcher::HandleRecording(const FString& Op, const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr) - return MakeError(TEXT("not_ready"), TEXT("Manager missing")); - // Use the game-thread-cached pointer; TActorIterator from this worker - // thread would assert IsInGameThread() and crash. - AMjReplayManager* RM = CachedReplayManager.Get(); - if (!RM) - return MakeError(TEXT("not_ready"), TEXT("AMjReplayManager not present in scene")); - - if (Op.Equals(TEXT("recording_start"))) - { - if (RM->bIsRecording) - return MakeError(TEXT("recording_already_active"), TEXT("Recording already active")); - double MaxDur = 0.0; - if (Req->TryGetNumberField(TEXT("max_duration_s"), MaxDur)) - RM->MaxRecordDuration = (float)MaxDur; - else - RM->MaxRecordDuration = FLT_MAX; - RM->StartRecording(); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("recording_start_ok")); - Reply->SetStringField(TEXT("name"), AMjReplayManager::LiveSessionName); - Reply->SetNumberField(TEXT("max_duration_s"), RM->MaxRecordDuration); - return Reply; - } - if (Op.Equals(TEXT("recording_stop"))) - { - if (!RM->bIsRecording) - return MakeError(TEXT("recording_not_active"), TEXT("Recording is not active")); - RM->StopRecording(); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("recording_stop_ok")); - Reply->SetStringField(TEXT("name"), AMjReplayManager::LiveSessionName); - // Populate the summary fields the Python client maps to - // RecordingSummary. Recording has been stopped (StopRecording - // above set bIsRecording=false) so the OnPostStep hook isn't - // mutating Frames in parallel. - Reply->SetNumberField(TEXT("frame_count"), static_cast(RM->GetLiveFrameCount())); - Reply->SetNumberField(TEXT("sim_duration_s"), RM->GetLiveSimDurationS()); - return Reply; - } - if (Op.Equals(TEXT("recording_save"))) - { - FString Path; - Req->TryGetStringField(TEXT("path"), Path); - FString FileName = Path.IsEmpty() ? TEXT("recording.json") : FPaths::GetCleanFilename(Path); - if (Path.IsEmpty()) - Path = FileName; - bool bOk = RM->SaveRecordingToFile(FileName); - // ResolveReplayPath now matches the manager's - // ProjectSavedDir/URLab/Replays/ output dir, so the absolute_path - // returned to the client points at the file the manager actually wrote. - FString Abs = ResolveReplayPath(Path); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), bOk ? TEXT("recording_save_ok") : TEXT("error")); - Reply->SetStringField(TEXT("absolute_path"), Abs); - if (!bOk) - Reply->SetStringField(TEXT("code"), TEXT("path_not_writable")); - return Reply; - } - if (Op.Equals(TEXT("recording_clear"))) - { - RM->ClearRecording(); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("recording_clear_ok")); - return Reply; - } - return MakeError(TEXT("unknown_op"), Op); -} - -TSharedPtr FURLabRpcDispatcher::HandleReplay(const FString& Op, const TSharedPtr& Req) -{ - AAMjManager* Mgr = OwnerMgr.Get(); - if (!Mgr) - return MakeError(TEXT("not_ready"), TEXT("Manager missing")); - AMjReplayManager* RM = CachedReplayManager.Get(); - if (!RM) - return MakeError(TEXT("not_ready"), TEXT("AMjReplayManager not present in scene")); - - if (Op.Equals(TEXT("replay_load"))) - { - FString P; - if (!Req->TryGetStringField(TEXT("path"), P)) - return MakeError(TEXT("missing_field"), TEXT("replay_load requires 'path'")); - FString FileName = FPaths::GetCleanFilename(P); - bool bOk = RM->LoadRecordingFromFile(FileName); - if (!bOk) - return MakeError(TEXT("path_not_readable"), P); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("replay_load_ok")); - Reply->SetStringField(TEXT("name"), FPaths::GetBaseFilename(FileName)); - return Reply; - } - if (Op.Equals(TEXT("replay_list_sessions"))) - { - TArray> Names; - for (const FString& N : RM->GetSessionNames()) - Names.Add(MakeShared(N)); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("replay_list_sessions_ok")); - Reply->SetArrayField(TEXT("sessions"), Names); - return Reply; - } - if (Op.Equals(TEXT("replay_set_active"))) - { - FString N; - if (!Req->TryGetStringField(TEXT("name"), N)) - return MakeError(TEXT("missing_field"), TEXT("replay_set_active requires 'name'")); - if (!RM->Sessions.Contains(N)) - return MakeError(TEXT("replay_session_not_found"), N); - RM->SetActiveSession(N); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("replay_set_active_ok")); - return Reply; - } - if (Op.Equals(TEXT("replay_start"))) - { - if (ActiveStepMode == EStepMode::Live) - return MakeError(TEXT("replay_requires_stepped"), - TEXT("Switch to direct or puppet before starting replay")); - RM->StartReplay(); - int32 Total = RM->Sessions.Contains(RM->GetActiveSessionName()) - ? RM->Sessions[RM->GetActiveSessionName()].Frames.Num() - : 0; - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("replay_start_ok")); - Reply->SetStringField(TEXT("active_session"), RM->GetActiveSessionName()); - Reply->SetNumberField(TEXT("total_frames"), Total); - return Reply; - } - if (Op.Equals(TEXT("replay_stop"))) - { - RM->StopReplay(); - TSharedPtr Reply = MakeShared(); - Reply->SetStringField(TEXT("op"), TEXT("replay_stop_ok")); - return Reply; - } - return MakeError(TEXT("unknown_op"), Op); -} diff --git a/Source/URLab/Private/Bridge/RpcHandlerCommon.h b/Source/URLab/Private/Bridge/RpcHandlerCommon.h new file mode 100644 index 00000000..65a1f474 --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlerCommon.h @@ -0,0 +1,23 @@ +// Shared includes for RPC handler translation units. Each RpcHandlers_*.cpp +// previously copy-pasted ~30 includes; this header carries the common set. +// Handler-specific headers (e.g. Async/Async.h for Camera) stay in the +// individual .cpp files. +#pragma once + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/OpRegistry.h" +#include "Bridge/BridgeServer.h" +#include "Bridge/MsgpackHelpers.h" +#include "State/MjStateTypes.h" +#include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Spec/MjNodeComponent.h" +#include "MuJoCo/Core/MjArticulation.h" +#include "MuJoCo/Elements/MjActuatorRuntime.h" +#include "MuJoCo/Elements/MjSensorRuntime.h" +#include "MuJoCo/Elements/MjCamera.h" +#include "MuJoCo/Elements/MjJointRuntime.h" +#include "MuJoCo/Elements/MjBody.h" +#include "MuJoCo/Controllers/MjArticulationController.h" +#include "MuJoCo/Input/MjTwistController.h" +#include "Transport/NetworkManager.h" +#include "Utils/URLabLogging.h" diff --git a/Source/URLab/Private/Bridge/RpcHandlers_Camera.cpp b/Source/URLab/Private/Bridge/RpcHandlers_Camera.cpp new file mode 100644 index 00000000..a093d5bc --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlers_Camera.cpp @@ -0,0 +1,589 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" +#include "Bridge/OpRegistry.h" +#include "Bridge/MsgpackHelpers.h" +#include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Spec/MjNodeComponent.h" +#include "MuJoCo/Core/MjArticulation.h" +#include "MuJoCo/Elements/MjActuatorRuntime.h" +#include "MuJoCo/Elements/MjSensorRuntime.h" +#include "MuJoCo/Elements/MjCamera.h" +#include "MuJoCo/Elements/MjJointRuntime.h" +#include "MuJoCo/Elements/MjBody.h" +#include "MuJoCo/Controllers/MjArticulationController.h" +#include "MuJoCo/Input/MjPerturbation.h" +#include "MuJoCo/Input/MjTwistController.h" +#include "Transport/NetworkManager.h" +#include "Transport/ShmPublishTransport.h" +#include "Transport/ShmRpcTransport.h" +#include "Transport/RpcTransport.h" +#include "Transport/ShmRegion.h" // FMjShmHeader (header_size in shm_rpc block) +#include "Bridge/BridgeServer.h" +#include "Replay/MjReplayManager.h" +#include "Kismet/GameplayStatics.h" +#include "Misc/Base64.h" +#include "Misc/Paths.h" +#include "Misc/FileHelper.h" +#include "Internationalization/Regex.h" +#include "HAL/FileManager.h" +#include "EngineUtils.h" +#include "Engine/World.h" +#include "Misc/Guid.h" +#include "Utils/URLabLogging.h" +#include "Async/Async.h" +#include "RenderingThread.h" + +bool FURLabRpcDispatcher::RenderCamerasSync(AAMjManager* Mgr, + const TMap& CameraSpec, + uint64 MinFrameId, int32 TimeoutMs, + TMap& CameraMinFrameIds, + bool bWait) +{ + if (CameraSpec.Num() == 0 || !Mgr) + return true; + if (bWait && MinFrameId == 0) + return true; + + TArray Keys; + CameraSpec.GetKeys(Keys); + + // Kick the render on the game thread: apply the just-produced physics snapshot, + // ensure each requested camera is streaming, and force an immediate capture. + // For the fresh (bWait) path the task then drives the readback to completion so + // the frame lands in ~render+readback time rather than waiting for the next + // editor tick to harvest it (that tick is background-throttled when the editor + // is unfocused, which added ~a full frame of latency). IssueSyncCapture submits + // the GPU copy to the RHI thread immediately, so the readback fence signals + // without a frame boundary and this pump finishes quickly; it takes no + // render-thread flush and is bounded by the request timeout (the worker wait + // below is the real deadline), so it cannot wedge the game thread the way the + // earlier sole-path, non-submitting poll did. Pipelined mode (!bWait) never + // pumps: it returns at once and serves the most-recently-completed frame. + TWeakObjectPtr WeakMgr(Mgr); + AsyncTask(ENamedThreads::GameThread, [WeakMgr, Keys, MinFrameId, TimeoutMs, bWait]() { + AAMjManager* M = WeakMgr.Get(); + if (!M) + return; + + M->ApplyLatestRenderState(); + + TMap ByName; + BuildCameraNameMap(M, ByName); + TArray Cams; + for (const FString& K : Keys) + { + if (UMjCamera* Cam = ByName.FindRef(K)) + { + if (!Cam->IsStreamingActive()) + Cam->SetStreamingEnabled(true); + Cams.Add(Cam); + } + } + + for (UMjCamera* Cam : Cams) + Cam->IssueSyncCapture(); + + if (!bWait) + return; // pipelined: kick only, no pump + + // Bound the game-thread pump under the request timeout: it needs to cover + // render + readback (and a cold RT's one-time warm-up) for every requested + // camera, and capping it means a genuinely stuck frame frees the game thread + // rather than freezing it for the whole timeout. The budget scales with the + // camera count -- each camera is a separate scene render + readback, so a + // single fixed cap (tuned for one camera) starved multi-camera requests and + // dropped their tail onto the slow off-thread wait, inflating latency under + // load. The worker wait below, off the game thread, remains the real deadline + // for anything the pump does not finish. + const int32 MaxPumpMs = FMath::Max(60, Cams.Num() * 50); + const double Deadline = + FPlatformTime::Seconds() + FMath::Min(FMath::Max(1, TimeoutMs), MaxPumpMs) / 1000.0; + for (;;) + { + bool bAllReady = true; + for (UMjCamera* Cam : Cams) + { + Cam->HarvestCompletedReadbacks(); + if (Cam->GetLatestFrameId() < MinFrameId) + { + bAllReady = false; + // Re-issue only while nothing is outstanding, so a cold RT that + // was not renderable on the first attempt retries without piling + // captures behind an in-flight one. + if (!Cam->HasPendingReadbacks()) + Cam->IssueSyncCapture(); + } + } + if (bAllReady || FPlatformTime::Seconds() >= Deadline) + break; + FPlatformProcess::SleepNoStats(0.0002f); + } + }); + + // Pipelined mode serves the most-recently-completed frame (up to one step + // stale), so it does not wait for the kicked frame. + if (!bWait) + return true; + + // Fresh mode: record the reached cameras so BuildCamerasBlock serves this step's + // frame. WaitForCameraFrames reads the thread-safe history and returns as soon + // as the frame is present, which the game-thread pump above has already made + // true for a warm camera. + return WaitForCameraFrames(Mgr, CameraSpec, MinFrameId, TimeoutMs, CameraMinFrameIds); +} + +bool FURLabRpcDispatcher::WaitForCameraFrames(AAMjManager* Mgr, + const TMap& CameraSpec, + uint64 MinFrameId, int32 TimeoutMs, + TMap& CameraMinFrameIds) +{ + if (MinFrameId == 0 || CameraSpec.Num() == 0) + return true; + + TMap ByName; + BuildCameraNameMap(Mgr, ByName); + + const double Deadline = FPlatformTime::Seconds() + FMath::Max(0, TimeoutMs) / 1000.0; + bool bAllReady = true; + for (const TPair& Spec : CameraSpec) + { + UMjCamera* Cam = ByName.FindRef(Spec.Key); + if (!Cam) + { + bAllReady = false; + continue; + } + // Mark it consumed so per-camera capture gating keeps it live. + Cam->TouchRequested(); + while (Cam->GetLatestFrameId() < MinFrameId + && FPlatformTime::Seconds() < Deadline + && !bDraining.load(std::memory_order_acquire)) + { + FPlatformProcess::SleepNoStats(0.002f); + } + if (Cam->GetLatestFrameId() >= MinFrameId) + CameraMinFrameIds.Add(Spec.Key, MinFrameId); + else + bAllReady = false; + } + return bAllReady; +} + +void FURLabRpcDispatcher::BuildCameraNameMap(AAMjManager* Manager, + TMap& OutByName) +{ + if (!Manager) + return; + // One canonical identity per camera: "/" from FMjCanonicalName, + // matching the zmq_topic the hello handshake advertises and the camera binds. + // First writer wins so a sanitize-collision can't hide an already-registered + // distinct camera. + auto AddCanonical = [&OutByName](UMjCamera* C) { + if (!C) + return; + UMjCamera*& Slot = OutByName.FindOrAdd(C->GetCanonicalName()); + if (Slot == nullptr) + Slot = C; + }; + for (AMjArticulation* Art : Manager->GetAllArticulations()) + { + if (!Art) + continue; + TArray Cameras; + Art->GetComponents(Cameras); + for (UMjCamera* C : Cameras) + AddCanonical(C); + } + // Manager-owned (global) cameras: not attached to any articulation. Their + // canonical name uses the owning actor name as the art segment (see + // UMjCamera::GetCanonicalName). Kept in sync with the include_cameras:true + // walk in ParseStepCommon so a global camera the client asks for resolves + // here instead of being dropped. + TArray GlobalCameras; + Manager->GetComponents(GlobalCameras); + for (UMjCamera* C : GlobalCameras) + AddCanonical(C); +} + +TSharedPtr FURLabRpcDispatcher::ApplyCameraStreamingGameThread( + AAMjManager* Manager, const TMap>& Requests) +{ + // Game thread: render target + ZMQ/SHM workers must be set up here. + check(IsInGameThread()); + TSharedPtr Out = MakeShared(); + if (!Manager) + return Out; + + TMap ByName; + BuildCameraNameMap(Manager, ByName); + + for (const TPair>& Req : Requests) + { + UMjCamera** Found = ByName.Find(Req.Key); + if (!Found || !*Found) + { + UE_LOG(LogURLabNet, Warning, + TEXT("[set_camera_streaming] camera '%s' not found"), *Req.Key); + continue; + } + UMjCamera* Cam = *Found; + const bool bZmq = Req.Value.Key; + const bool bShm = Req.Value.Value; + + const bool bStream = bZmq || bShm; + // Idempotent: only (re)build when the requested flags actually change. + // SetStreamingEnabled(false) destroys the RenderTarget, so toggling on + // every call orphans any consumer bound to it -- the editor Simulate + // camera-feed widget then freezes on its last frame. Clients call this + // on every discover/set_mode, so unchanged requests MUST be no-ops. + const bool bUnchanged = (Cam->bEnableZmqBroadcast == bZmq) + && (Cam->bEnableShmBroadcast == bShm) + && (Cam->IsStreamingActive() == bStream); + if (!bUnchanged) + { + Cam->bEnableZmqBroadcast = bZmq; + Cam->bEnableShmBroadcast = bShm; + Cam->SetStreamingEnabled(false); + if (bStream) + Cam->SetStreamingEnabled(true); + } + + TSharedPtr CamObj = MakeShared(); + CamObj->SetBoolField(TEXT("streaming"), bStream); + CamObj->SetBoolField(TEXT("zmq"), bZmq); + CamObj->SetBoolField(TEXT("shm"), bShm); + if (bZmq) + { + FString Endpoint = Cam->GetActualZmqEndpoint(); + Endpoint.ReplaceInline(TEXT("*"), TEXT("127.0.0.1")); + CamObj->SetStringField(TEXT("zmq_endpoint"), Endpoint); + CamObj->SetStringField(TEXT("zmq_topic"), Cam->GetCanonicalName()); + } + // Key the reply by the canonical name so the bridge always gets a + // stable identity back regardless of which alias it requested. + Out->SetObjectField(Cam->GetCanonicalName(), CamObj); + } + return Out; +} + +TSharedPtr FURLabRpcDispatcher::HandleSetCameraStreaming( + const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + + const TSharedPtr* CamObj = nullptr; + if (!Req->TryGetObjectField(TEXT("cameras"), CamObj) || !CamObj || !CamObj->IsValid()) + return MakeError(URLabError::MissingField, + TEXT("set_camera_streaming requires a 'cameras' object")); + + // Parse per-camera requests. Value forms: + // true / false -> both transports on / off + // { "zmq": b, "shm": b } -> per-transport; an omitted sub-field is off + // UNLESS both are omitted, which means "both on" + TMap> Requests; // key -> {zmq, shm} + for (const TPair>& Kv : (*CamObj)->Values) + { + if (!Kv.Value.IsValid()) + continue; + bool bZmq = true; + bool bShm = true; + bool bBool = false; + const TSharedPtr* Sub = nullptr; + if (Kv.Value->TryGetBool(bBool)) + { + bZmq = bBool; + bShm = bBool; + } + else if (Kv.Value->TryGetObject(Sub) && Sub && Sub->IsValid()) + { + bool z = false, s = false; + const bool bHasZ = (*Sub)->TryGetBoolField(TEXT("zmq"), z); + const bool bHasS = (*Sub)->TryGetBoolField(TEXT("shm"), s); + if (bHasZ || bHasS) + { + bZmq = bHasZ ? z : false; + bShm = bHasS ? s : false; + } + // else leave both true (default = enable both) + } + Requests.Add(Kv.Key, TPair(bZmq, bShm)); + } + + if (Requests.Num() == 0) + return MakeError(URLabError::BadRequest, TEXT("'cameras' had no usable entries")); + + // Camera RT / worker setup is game-thread only; marshal and wait. + struct FResult + { + FEvent* Done = nullptr; + TSharedPtr Cameras; + }; + TSharedPtr Res = MakeShared(); + Res->Done = FPlatformProcess::GetSynchEventFromPool(/*bIsManualReset=*/false); + TWeakObjectPtr WeakMgr(Mgr); + AsyncTask(ENamedThreads::GameThread, [Res, WeakMgr, Requests]() { + if (AAMjManager* M = WeakMgr.Get()) + Res->Cameras = ApplyCameraStreamingGameThread(M, Requests); + Res->Done->Trigger(); + }); + + TSharedPtr Reply = MakeShared(); + if (Res->Done->Wait(5000)) + { + FPlatformProcess::ReturnSynchEventToPool(Res->Done); + Res->Done = nullptr; + Reply->SetStringField(TEXT("op"), TEXT("set_camera_streaming_ok")); + Reply->SetObjectField(TEXT("cameras"), + Res->Cameras.IsValid() ? Res->Cameras : MakeShared()); + return Reply; + } + // Timed out — leave the event un-pooled (the task still references it). + return MakeError(URLabError::Timeout, TEXT("set_camera_streaming game-thread apply timed out")); +} + +namespace +{ +// Parsed per-camera latency + capture-rate request. bSetCapture marks whether +// the on_state_change / max_fps capture knobs were present (so we only override +// them when the caller actually sent them). +struct FCameraDelayReq +{ + float DelaySeconds = 0.0f; + float JitterSeconds = 0.0f; + bool bWallClock = false; + int32 Seed = 0; + bool bOnStateChange = true; + float MaxFps = 0.0f; + bool bSetCapture = false; +}; +} // namespace + +TSharedPtr FURLabRpcDispatcher::HandleSetCameraDelay( + const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + + const TSharedPtr* CamObj = nullptr; + if (!Req->TryGetObjectField(TEXT("cameras"), CamObj) || !CamObj || !CamObj->IsValid()) + return MakeError(URLabError::MissingField, + TEXT("set_camera_delay requires a 'cameras' object")); + + // Parse per-camera requests. A bare number means delay_s with defaults; an + // object carries delay_s / jitter_s / clock / seed / on_state_change / max_fps. + TMap Requests; + for (const TPair>& Kv : (*CamObj)->Values) + { + if (!Kv.Value.IsValid()) + continue; + FCameraDelayReq R; + double Num = 0.0; + const TSharedPtr* Sub = nullptr; + if (Kv.Value->TryGetNumber(Num)) + { + R.DelaySeconds = static_cast(Num); + } + else if (Kv.Value->TryGetObject(Sub) && Sub && Sub->IsValid()) + { + double D = 0.0; + if ((*Sub)->TryGetNumberField(TEXT("delay_s"), D)) + R.DelaySeconds = static_cast(D); + double J = 0.0; + if ((*Sub)->TryGetNumberField(TEXT("jitter_s"), J)) + R.JitterSeconds = static_cast(J); + FString Clock; + if ((*Sub)->TryGetStringField(TEXT("clock"), Clock)) + R.bWallClock = Clock.Equals(TEXT("wall"), ESearchCase::IgnoreCase); + int32 Seed = 0; + if ((*Sub)->TryGetNumberField(TEXT("seed"), Seed)) + R.Seed = Seed; + bool bOnState = true; + if ((*Sub)->TryGetBoolField(TEXT("on_state_change"), bOnState)) + { + R.bOnStateChange = bOnState; + R.bSetCapture = true; + } + double Fps = 0.0; + if ((*Sub)->TryGetNumberField(TEXT("max_fps"), Fps)) + { + R.MaxFps = static_cast(Fps); + R.bSetCapture = true; + } + } + Requests.Add(Kv.Key, R); + } + + if (Requests.Num() == 0) + return MakeError(URLabError::BadRequest, TEXT("'cameras' had no usable entries")); + + // SetCameraDelay / SetCaptureRate touch state the game thread reads each tick; + // marshal and wait, like set_camera_streaming. + struct FResult + { + FEvent* Done = nullptr; + TSharedPtr Cameras; + }; + TSharedPtr Res = MakeShared(); + Res->Done = FPlatformProcess::GetSynchEventFromPool(/*bIsManualReset=*/false); + TWeakObjectPtr WeakMgr(Mgr); + AsyncTask(ENamedThreads::GameThread, [Res, WeakMgr, Requests]() { + TSharedPtr Out = MakeShared(); + if (AAMjManager* M = WeakMgr.Get()) + { + TMap ByName; + FURLabRpcDispatcher::BuildCameraNameMap(M, ByName); + for (const TPair& Req : Requests) + { + UMjCamera** Found = ByName.Find(Req.Key); + if (!Found || !*Found) + { + UE_LOG(LogURLabNet, Warning, + TEXT("[set_camera_delay] camera '%s' not found"), *Req.Key); + continue; + } + UMjCamera* Cam = *Found; + const FCameraDelayReq& V = Req.Value; + Cam->SetCameraDelay(V.DelaySeconds, V.JitterSeconds, V.bWallClock, V.Seed); + if (V.bSetCapture) + Cam->SetCaptureRate(V.bOnStateChange, V.MaxFps); + + TSharedPtr CamOut = MakeShared(); + CamOut->SetNumberField(TEXT("delay_s"), Cam->DelaySeconds); + CamOut->SetNumberField(TEXT("jitter_s"), Cam->DelayJitterSeconds); + CamOut->SetStringField(TEXT("clock"), + Cam->bDelayUseWallClock ? TEXT("wall") : TEXT("sim")); + CamOut->SetBoolField(TEXT("on_state_change"), Cam->bCaptureOnStateChange); + CamOut->SetNumberField(TEXT("max_fps"), Cam->CaptureMaxFps); + Out->SetObjectField(Cam->GetCanonicalName(), CamOut); + } + } + Res->Cameras = Out; + Res->Done->Trigger(); + }); + + TSharedPtr Reply = MakeShared(); + if (Res->Done->Wait(5000)) + { + FPlatformProcess::ReturnSynchEventToPool(Res->Done); + Res->Done = nullptr; + Reply->SetStringField(TEXT("op"), TEXT("set_camera_delay_ok")); + Reply->SetObjectField(TEXT("cameras"), + Res->Cameras.IsValid() ? Res->Cameras : MakeShared()); + return Reply; + } + return MakeError(URLabError::Timeout, TEXT("set_camera_delay game-thread apply timed out")); +} + +TSharedPtr FURLabRpcDispatcher::BuildCamerasBlock(AAMjManager* Manager, + const TMap& CameraSpec, + const TMap& MinFrameIds, int32 TimeoutMs) +{ + (void)TimeoutMs; // retrieval is non-blocking; param kept for ABI compatibility + TSharedPtr Cams = MakeShared(); + if (!Manager || CameraSpec.Num() == 0) + return Cams; + + UWorld* World = Manager->GetWorld(); + if (!World) + return Cams; + + TMap ByName; + BuildCameraNameMap(Manager, ByName); + + // Non-blocking retrieval from each camera's frame-history ring. No capture, + // no FlushRenderingCommands — the per-tick async readback (decoupled from + // the step, like MuJoCo simulate's render thread) keeps the ring fresh. + // A client gets the frame for a specific step by passing its frame_id + // (MinFrameIds); otherwise it gets the latest available frame. + for (const TPair& Spec : CameraSpec) + { + UMjCamera** Found = ByName.Find(Spec.Key); + if (!Found || !*Found) + { + UE_LOG(LogURLabNet, Warning, + TEXT("[BuildCamerasBlock] camera '%s' not found; dropped from reply"), + *Spec.Key); + continue; + } + UMjCamera* Cam = *Found; + + // Mark consumed so per-camera capture gating keeps this camera live. + Cam->TouchRequested(); + + const uint64* MinIdPtr = MinFrameIds.Find(Spec.Key); + const uint64 MinId = MinIdPtr ? *MinIdPtr : 0; + + // A camera requested in "sync" mode wants the freshest rendered (ground + // truth) frame; a plain "latest" request should instead agree with what + // the stream is publishing, which under latency emulation is the delayed + // past. GetFrameForRequest routes both correctly and hands the retained + // frame back by refcount, so no multi-MB pixel copy happens under the + // history lock. + const bool bIgnoreDelay = (Spec.Value == ECameraInclude::Sync); + TSharedPtr Frame = Cam->GetFrameForRequest(MinId, bIgnoreDelay); + if (!Frame.IsValid()) + { + // Not ready yet (camera just activated, or the requested step's + // frame hasn't been rendered/read back). Omit; the client retries + // on a later step. First request of a dormant camera always misses. + continue; + } + + TSharedPtr CamObj = MakeShared(); + CamObj->SetNumberField(TEXT("width"), Frame->Width); + CamObj->SetNumberField(TEXT("height"), Frame->Height); + CamObj->SetNumberField(TEXT("frame_id"), static_cast(Frame->FrameId)); + CamObj->SetNumberField(TEXT("sim_time"), Frame->SimTime); + + if (Cam->CaptureMode == EMjCameraMode::Depth) + { + if (Frame->Depth.Num() == 0) + continue; + CamObj->SetStringField(TEXT("dtype"), TEXT("float32")); + // Zero-copy: pack the pixel bytes straight from the shared frame (no + // base64, no intermediate FString). The frame is retained as the keeper + // until the reply is packed, so the buffer stays valid. + FURLabMsgpackUtil::SetBinaryFieldShared(CamObj, TEXT("data"), + reinterpret_cast(Frame->Depth.GetData()), + Frame->Depth.Num() * sizeof(float), Frame); + } + else + { + // Real / SemSeg / InstanceSeg all ship 4-byte BGRA. Bridge + // discriminates the seg modes by the camera_topics handshake. + if (Frame->Color.Num() == 0) + continue; + CamObj->SetStringField(TEXT("dtype"), TEXT("bgra8")); + FURLabMsgpackUtil::SetBinaryFieldShared(CamObj, TEXT("data"), + reinterpret_cast(Frame->Color.GetData()), + Frame->Color.Num() * sizeof(FColor), Frame); + } + Cams->SetObjectField(Spec.Key, CamObj); + } + return Cams; +} diff --git a/Source/URLab/Private/Bridge/RpcHandlers_Control.cpp b/Source/URLab/Private/Bridge/RpcHandlers_Control.cpp new file mode 100644 index 00000000..6d155ef2 --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlers_Control.cpp @@ -0,0 +1,245 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" +#include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Core/MjArticulation.h" +#include "State/MjStateTypes.h" + +namespace +{ +// Parse one inbound user-channel value into an IR channel. The inferred kind only +// distinguishes the text family from the numeric family; the declaring component +// stores the value under its own declared kind. Returns false with a reason for a +// shape no input kind accepts. A bare object without pos/quat (a generic struct) +// is rejected: only declared typed channels accept input. +bool JsonValueToUserChannel(const TSharedPtr& Value, FMjUserChannel& Out, + FString& OutReason) +{ + if (!Value.IsValid()) + { + OutReason = TEXT("null_value"); + return false; + } + switch (Value->Type) + { + case EJson::Boolean: + Out.Kind = EMjUserChannelKind::Bool; + Out.Values = {Value->AsBool() ? 1.0 : 0.0}; + return true; + case EJson::Number: + Out.Kind = EMjUserChannelKind::Scalar; + Out.Values = {Value->AsNumber()}; + return true; + case EJson::String: + Out.Kind = EMjUserChannelKind::String; + Out.Text = Value->AsString(); + return true; + case EJson::Array: + { + Out.Kind = EMjUserChannelKind::Array; + for (const TSharedPtr& E : Value->AsArray()) + Out.Values.Add(E.IsValid() ? E->AsNumber() : 0.0); + return true; + } + case EJson::Object: + { + // {pos:[3], quat:[4]} is a transform; anything else is unsupported input. + const TSharedPtr Obj = Value->AsObject(); + const TArray>* Pos = nullptr; + const TArray>* Quat = nullptr; + if (Obj.IsValid() && Obj->TryGetArrayField(TEXT("pos"), Pos) + && Obj->TryGetArrayField(TEXT("quat"), Quat)) + { + Out.Kind = EMjUserChannelKind::Transform; + Out.Values.SetNumZeroed(7); + for (int32 i = 0; i < 3 && i < Pos->Num(); ++i) + Out.Values[i] = (*Pos)[i].IsValid() ? (*Pos)[i]->AsNumber() : 0.0; + for (int32 i = 0; i < 4 && i < Quat->Num(); ++i) + Out.Values[3 + i] = (*Quat)[i].IsValid() ? (*Quat)[i]->AsNumber() : 0.0; + return true; + } + OutReason = TEXT("unsupported_object"); + return false; + } + default: + OutReason = TEXT("unsupported_value"); + return false; + } +} +} // namespace + +FString FURLabRpcDispatcher::ResolveControlSource(const TSharedPtr& Req) const +{ + FString Source; + if (Req->TryGetStringField(TEXT("control_owner"), Source) && !Source.IsEmpty()) + return Source; + Req->TryGetStringField(TEXT("session_id"), Source); + return Source; +} + +TSharedPtr FURLabRpcDispatcher::RejectIfNotControlOwner(FName ArtKey, + const TSharedPtr& Req) +{ + const FString Source = ResolveControlSource(Req); + FString CurrentOwner; + if (ControlOwnership.CheckWrite(ArtKey, Source, CurrentOwner) + == FMjControlOwnership::EWriteCheck::Ok) + { + return nullptr; + } + + TSharedPtr Err = MakeError(TEXT("not_control_owner"), + FString::Printf(TEXT("%s owned by %s"), *ArtKey.ToString(), *CurrentOwner)); + Err->SetStringField(TEXT("owner"), CurrentOwner); + return Err; +} + +TSharedPtr FURLabRpcDispatcher::HandleClaimControl(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + + FString ArtName; + if (!Req->TryGetStringField(TEXT("articulation"), ArtName)) + return MakeError(URLabError::MissingField, TEXT("claim_control requires 'articulation'")); + + AMjArticulation* Art = Mgr->GetArticulation(ArtName); + if (!Art) + return MakeError(URLabError::UnknownArticulation, ArtName); + + const FName Key(*Art->GetName()); + const FString Source = ResolveControlSource(Req); + + double Ttl = 0.0; + Req->TryGetNumberField(TEXT("ttl_s"), Ttl); + bool bForce = false; + Req->TryGetBoolField(TEXT("force"), bForce); + + FString CurrentOwner; + if (ControlOwnership.Claim(Key, Source, Ttl, bForce, CurrentOwner) + == FMjControlOwnership::EClaimResult::AlreadyOwned) + { + TSharedPtr Err = MakeError(TEXT("control_claimed"), + FString::Printf(TEXT("%s already owned by %s"), *Key.ToString(), *CurrentOwner)); + Err->SetStringField(TEXT("owner"), CurrentOwner); + return Err; + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("claim_control_ok")); + Reply->SetStringField(TEXT("articulation"), Art->GetName()); + Reply->SetStringField(TEXT("owner"), Source); + Reply->SetNumberField(TEXT("ttl_s"), Ttl); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleReleaseControl(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + + FString ArtName; + if (!Req->TryGetStringField(TEXT("articulation"), ArtName)) + return MakeError(URLabError::MissingField, TEXT("release_control requires 'articulation'")); + + AMjArticulation* Art = Mgr->GetArticulation(ArtName); + if (!Art) + return MakeError(URLabError::UnknownArticulation, ArtName); + + const FName Key(*Art->GetName()); + const FString Source = ResolveControlSource(Req); + + if (!ControlOwnership.Release(Key, Source)) + { + FString CurrentOwner; + ControlOwnership.CheckWrite(Key, Source, CurrentOwner); + TSharedPtr Err = MakeError(TEXT("not_control_owner"), + FString::Printf(TEXT("%s owned by %s"), *Key.ToString(), *CurrentOwner)); + Err->SetStringField(TEXT("owner"), CurrentOwner); + return Err; + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("release_control_ok")); + Reply->SetStringField(TEXT("articulation"), Art->GetName()); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleSetUserChannels(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + + int32 Applied = 0; + TSharedPtr Rejected = MakeShared(); + + // One scope's channel map: {channel: value}. ArtOrNone is the canonical art + // segment (None for scene). Undeclared names and unparseable / kind-mismatched + // values land in `rejected` rather than failing the whole batch. + auto ApplyScope = [&](const TSharedPtr& Channels, FName ArtOrNone, + const FString& RejectPrefix) { + if (!Channels.IsValid()) + return; + for (const TPair>& Pair : Channels->Values) + { + const FName Channel(*Pair.Key); + FMjUserChannel Value; + FString Reason; + if (!JsonValueToUserChannel(Pair.Value, Value, Reason)) + { + Rejected->SetStringField(RejectPrefix + Pair.Key, Reason); + continue; + } + if (Mgr->ApplyUserChannelInput(ArtOrNone, Channel, Value)) + ++Applied; + else + Rejected->SetStringField(RejectPrefix + Pair.Key, + TEXT("undeclared_or_kind_mismatch")); + } + }; + + const TSharedPtr* ArtsObj = nullptr; + if (Req->TryGetObjectField(TEXT("arts"), ArtsObj)) + { + for (const TPair>& ArtPair : (*ArtsObj)->Values) + { + const TSharedPtr* ArtChannels = nullptr; + if (ArtPair.Value.IsValid() && ArtPair.Value->TryGetObject(ArtChannels)) + ApplyScope(*ArtChannels, FName(*ArtPair.Key), ArtPair.Key + TEXT("/")); + } + } + + const TSharedPtr* SceneObj = nullptr; + if (Req->TryGetObjectField(TEXT("scene"), SceneObj)) + ApplyScope(*SceneObj, NAME_None, TEXT("scene/")); + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("set_user_channels_ok")); + Reply->SetNumberField(TEXT("applied"), Applied); + Reply->SetObjectField(TEXT("rejected"), Rejected); + return Reply; +} diff --git a/Source/URLab/Private/Bridge/RpcHandlers_Lease.cpp b/Source/URLab/Private/Bridge/RpcHandlers_Lease.cpp new file mode 100644 index 00000000..6859cf12 --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlers_Lease.cpp @@ -0,0 +1,91 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" +#include "Bridge/BridgeServer.h" + +// Cooperative render-farm lease ops. A lease is a claim, not a security +// boundary: one client leases an instance so a pool won't hand the same +// editor process to two clients. One lease per process. Auto-release is +// TTL-based (an idle lease past its TTL frees on the next check); socket-level +// disconnect detection is a Phase 5 hardening item, deferred. + +TSharedPtr FURLabRpcDispatcher::HandleAcquireLease(const TSharedPtr& Req) +{ + UURLabBridgeServer* Bridge = OwningBridge.Get(); + if (!Bridge) + { + return MakeError(URLabError::NotReady, + TEXT("lease state is unavailable (no bridge server)")); + } + + FString Owner; + if (Req.IsValid()) + Req->TryGetStringField(TEXT("owner"), Owner); + + double TtlSeconds = 60.0; + if (Req.IsValid()) + Req->TryGetNumberField(TEXT("ttl_s"), TtlSeconds); + + FString AcquiredLeaseId; + FString ExistingLeaseId; + if (!Bridge->TryAcquireLease(Owner, TtlSeconds, AcquiredLeaseId, ExistingLeaseId)) + { + TSharedPtr Err = MakeError(TEXT("busy"), + TEXT("instance is already leased")); + Err->SetStringField(TEXT("lease_id"), ExistingLeaseId); + return Err; + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("acquire_lease_ok")); + Reply->SetStringField(TEXT("lease_id"), AcquiredLeaseId); + Reply->SetNumberField(TEXT("ttl_s"), TtlSeconds); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleReleaseLease(const TSharedPtr& Req) +{ + UURLabBridgeServer* Bridge = OwningBridge.Get(); + if (!Bridge) + { + return MakeError(URLabError::NotReady, + TEXT("lease state is unavailable (no bridge server)")); + } + + FString LeaseId; + if (!Req.IsValid() || !Req->TryGetStringField(TEXT("lease_id"), LeaseId) || LeaseId.IsEmpty()) + { + return MakeError(URLabError::BadRequest, TEXT("release_lease requires a non-empty lease_id")); + } + + if (!Bridge->ReleaseLease(LeaseId)) + { + return MakeError(URLabError::BadRequest, + TEXT("lease_id does not match the held lease (or no lease is held)")); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("release_lease_ok")); + return Reply; +} diff --git a/Source/URLab/Private/Bridge/RpcHandlers_ModelUpload.cpp b/Source/URLab/Private/Bridge/RpcHandlers_ModelUpload.cpp new file mode 100644 index 00000000..80eaae1b --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlers_ModelUpload.cpp @@ -0,0 +1,636 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +// Network model upload: a remote client ships raw MJCF XML bytes plus the asset +// bytes the XML references over ZMQ; the server materialises them to a temp dir +// and runs the EXISTING import_xml editor job on the result (no separate compile +// path). Three ops: +// upload_model_manifest content-addressed negotiation (which blobs are missing) +// upload_model_chunk bulk, chunked bytes; each completed blob is verified + cached +// upload_model_commit materialise + invoke import_xml + echo model dims/mjb +// The manifest/chunk ops are pure data staging on the RPC worker thread; only +// commit drives the editor import (via the shared op registry, so no URLabEditor +// link dependency). Assets reuse the vfs_assets msgpack-bin framing and the +// bare-filename flatten convention of the DOWN handshake. + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" +#include "Bridge/AssetCache.h" +#include "Bridge/OpRegistry.h" + +#include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Core/MjPhysicsEngine.h" +#include "Bridge/MsgpackHelpers.h" +#include "Utils/URLabLogging.h" + +#include "HAL/FileManager.h" +#include "HAL/PlatformProcess.h" +#include "Misc/Base64.h" +#include "Misc/DateTime.h" +#include "Misc/FileHelper.h" +#include "Misc/Guid.h" +#include "Misc/Paths.h" +#include "Misc/ScopeLock.h" + +namespace +{ +// Security caps (plan 4.6). Advertised in the manifest reply and enforced on +// every chunk. +constexpr int64 kMaxAssetBytes = 64ll * 1024 * 1024; // 64 MiB per blob +constexpr int64 kMaxTotalBytes = 512ll * 1024 * 1024; // 512 MiB per upload +constexpr int32 kMaxAssets = 4096; +constexpr int32 kMaxOpenUploads = 16; +constexpr double kUploadTtlSeconds = 300.0; + +/** One asset's staging slot. The receive buffer only exists while incomplete; + * once verified it is written to the content-addressed cache and dropped, so + * materialisation reads the blob back from the cache by hash. */ +struct FAssetSlot +{ + FString Sha; + int64 DeclaredSize = 0; + TArray Buffer; + int64 Received = 0; + bool bComplete = false; +}; + +struct FStagingEntry +{ + double LastTouch = 0.0; + FString StepMode; + + FString XmlSha; + TArray XmlBuffer; + int64 XmlReceived = 0; + int64 XmlTotal = 0; + bool bXmlComplete = false; + + // keyed by bare filename + TMap Assets; +}; + +/** Process-wide staging store. One editor process is one instance, so uploads + * are per-process; the map is keyed by upload_id (FGuid) and guarded by a + * mutex. Concurrent-upload cap + TTL expiry live here. */ +class FUploadStaging +{ +public: + static FUploadStaging& Get() + { + static FUploadStaging Instance; + return Instance; + } + + FCriticalSection Mutex; + TMap Entries; + + /** Drop entries idle past the TTL. Caller holds Mutex. */ + void SweepExpired() + { + const double Now = FPlatformTime::Seconds(); + for (auto It = Entries.CreateIterator(); It; ++It) + { + if (Now - It->Value.LastTouch > kUploadTtlSeconds) + It.RemoveCurrent(); + } + } +}; + +/** True if Name is a bare filename safe to materialise next to the XML: no + * path separators, no parent refs, no drive letter, no leading separator, + * no NUL. */ +bool IsBareFilename(const FString& Name) +{ + if (Name.IsEmpty() || Name == TEXT(".")) + return false; + if (Name.Contains(TEXT("/")) || Name.Contains(TEXT("\\"))) + return false; + if (Name.Contains(TEXT(".."))) + return false; + // Drive letter (C:) or any embedded colon (alternate data stream / drive). + if (Name.Contains(TEXT(":"))) + return false; + for (const TCHAR C : Name) + { + if (C == TEXT('\0')) + return false; + } + return true; +} + +/** Read a msgpack-bin request field. On the wire a bin map-value arrives with + * a `__b64__`-suffixed key holding base64 (see MsgpackHelpers); accept a plain + * base64 string under the bare name too (JSON clients / tests). */ +bool ReadBinField(const TSharedPtr& Req, const FString& Name, TArray& Out) +{ + FString B64; + if (Req->TryGetStringField(Name + TEXT("__b64__"), B64) || Req->TryGetStringField(Name, B64)) + { + Out.Reset(); + return FBase64::Decode(B64, Out); + } + return false; +} +} // namespace + +TSharedPtr FURLabRpcDispatcher::HandleUploadModelManifest(const TSharedPtr& Req) +{ + FString XmlSha; + if (!Req->TryGetStringField(TEXT("xml_sha256"), XmlSha) + || !FURLabAssetCache::IsValidSha256Hex(XmlSha)) + { + return MakeError(URLabError::BadRequest, + TEXT("upload_model_manifest requires a valid hex 'xml_sha256'")); + } + + const TArray>* AssetsArr = nullptr; + Req->TryGetArrayField(TEXT("assets"), AssetsArr); + const int32 AssetCount = AssetsArr ? AssetsArr->Num() : 0; + if (AssetCount > kMaxAssets) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("manifest lists %d assets; cap is %d"), AssetCount, kMaxAssets)); + } + + double TotalBytes = 0.0; + Req->TryGetNumberField(TEXT("total_bytes"), TotalBytes); + if (static_cast(TotalBytes) > kMaxTotalBytes) + { + return MakeError(TEXT("payload_too_large"), + FString::Printf(TEXT("total_bytes %.0f exceeds max_total_bytes %lld"), + TotalBytes, kMaxTotalBytes)); + } + + FString StepMode; + Req->TryGetStringField(TEXT("step_mode"), StepMode); + + FURLabAssetCache& Cache = FURLabAssetCache::Get(); + + FStagingEntry Entry; + Entry.StepMode = StepMode; + Entry.XmlSha = XmlSha; + Entry.bXmlComplete = Cache.Has(XmlSha); + + TArray> NeedAssets; + for (int32 i = 0; i < AssetCount; ++i) + { + const TSharedPtr* AObj = nullptr; + if (!(*AssetsArr)[i]->TryGetObject(AObj) || !AObj) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("manifest asset[%d] is not an object"), i)); + } + + FString Name, Sha; + (*AObj)->TryGetStringField(TEXT("name"), Name); + (*AObj)->TryGetStringField(TEXT("sha256"), Sha); + double Size = 0.0; + (*AObj)->TryGetNumberField(TEXT("size"), Size); + + if (!IsBareFilename(Name)) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("asset name '%s' is not a bare filename"), *Name)); + } + if (!FURLabAssetCache::IsValidSha256Hex(Sha)) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("asset '%s' has an invalid sha256"), *Name)); + } + if (static_cast(Size) > kMaxAssetBytes) + { + return MakeError(TEXT("payload_too_large"), + FString::Printf(TEXT("asset '%s' size %.0f exceeds max_asset_bytes %lld"), + *Name, Size, kMaxAssetBytes)); + } + + FAssetSlot Slot; + Slot.Sha = Sha; + Slot.DeclaredSize = static_cast(Size); + Slot.bComplete = Cache.Has(Sha); + if (!Slot.bComplete) + NeedAssets.Add(MakeShared(Name)); + Entry.Assets.Add(Name, MoveTemp(Slot)); + } + + const FString UploadId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); + Entry.LastTouch = FPlatformTime::Seconds(); + + { + FUploadStaging& Staging = FUploadStaging::Get(); + FScopeLock Lock(&Staging.Mutex); + Staging.SweepExpired(); + if (Staging.Entries.Num() >= kMaxOpenUploads) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("too many concurrent uploads (cap %d); retry later"), + kMaxOpenUploads)); + } + Staging.Entries.Add(UploadId, MoveTemp(Entry)); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("upload_model_manifest_ok")); + Reply->SetStringField(TEXT("upload_id"), UploadId); + Reply->SetBoolField(TEXT("need_xml"), !Cache.Has(XmlSha)); + Reply->SetArrayField(TEXT("need_assets"), NeedAssets); + Reply->SetNumberField(TEXT("max_asset_bytes"), static_cast(kMaxAssetBytes)); + Reply->SetNumberField(TEXT("max_total_bytes"), static_cast(kMaxTotalBytes)); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleUploadModelChunk(const TSharedPtr& Req) +{ + // The reassembled blob is verified against the manifest-declared hash, not + // the chunk's own sha256 field, so that field is accepted but not consulted. + FString UploadId, Kind, Name; + Req->TryGetStringField(TEXT("upload_id"), UploadId); + Req->TryGetStringField(TEXT("kind"), Kind); + Req->TryGetStringField(TEXT("name"), Name); + + double OffsetD = 0.0, TotalD = 0.0; + Req->TryGetNumberField(TEXT("offset"), OffsetD); + Req->TryGetNumberField(TEXT("total"), TotalD); + const int64 Offset = static_cast(OffsetD); + const int64 Total = static_cast(TotalD); + + if (UploadId.IsEmpty() || (Kind != TEXT("xml") && Kind != TEXT("asset"))) + { + return MakeError(URLabError::BadRequest, + TEXT("upload_model_chunk requires 'upload_id' and kind 'xml'|'asset'")); + } + if (Offset < 0 || Total < 0) + { + return MakeError(URLabError::BadRequest, TEXT("offset/total must be non-negative")); + } + if (Total > kMaxAssetBytes) + { + return MakeError(TEXT("payload_too_large"), + FString::Printf(TEXT("blob total %lld exceeds max_asset_bytes %lld"), + Total, kMaxAssetBytes)); + } + + TArray Data; + if (!ReadBinField(Req, TEXT("data"), Data)) + { + return MakeError(URLabError::BadRequest, TEXT("upload_model_chunk missing binary 'data'")); + } + if (Offset + Data.Num() > Total) + { + return MakeError(URLabError::BadRequest, + TEXT("chunk offset+len exceeds declared total")); + } + if (Offset + Data.Num() > kMaxAssetBytes) + { + return MakeError(TEXT("payload_too_large"), + FString::Printf(TEXT("chunk end %lld exceeds max_asset_bytes %lld"), + Offset + Data.Num(), kMaxAssetBytes)); + } + + FURLabAssetCache& Cache = FURLabAssetCache::Get(); + FUploadStaging& Staging = FUploadStaging::Get(); + FScopeLock Lock(&Staging.Mutex); + + FStagingEntry* Entry = Staging.Entries.Find(UploadId); + if (!Entry || FPlatformTime::Seconds() - Entry->LastTouch > kUploadTtlSeconds) + { + if (Entry) + Staging.Entries.Remove(UploadId); + return MakeError(TEXT("unknown_upload"), + FString::Printf(TEXT("upload_id '%s' is unknown or expired"), *UploadId)); + } + Entry->LastTouch = FPlatformTime::Seconds(); + + // Route to the XML slot or a named asset slot. + TArray* Buffer = nullptr; + int64* Received = nullptr; + bool* bComplete = nullptr; + FString DeclaredSha; + int64* XmlTotalPtr = nullptr; + + if (Kind == TEXT("xml")) + { + Buffer = &Entry->XmlBuffer; + Received = &Entry->XmlReceived; + bComplete = &Entry->bXmlComplete; + DeclaredSha = Entry->XmlSha; + XmlTotalPtr = &Entry->XmlTotal; + } + else + { + FAssetSlot* Slot = Entry->Assets.Find(Name); + if (!Slot) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("asset '%s' was not declared in the manifest"), *Name)); + } + if (Slot->DeclaredSize != Total) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("asset '%s' chunk total %lld != manifest size %lld"), + *Name, Total, Slot->DeclaredSize)); + } + Buffer = &Slot->Buffer; + Received = &Slot->Received; + bComplete = &Slot->bComplete; + DeclaredSha = Slot->Sha; + } + + if (XmlTotalPtr) + *XmlTotalPtr = Total; + + // Already satisfied (e.g. resolved from the cache at manifest time): ack. + if (*bComplete && Buffer->Num() == 0) + { + TSharedPtr Ack = MakeShared(); + Ack->SetStringField(TEXT("op"), TEXT("upload_model_chunk_ok")); + Ack->SetStringField(TEXT("name"), Kind == TEXT("xml") ? TEXT("") : Name); + Ack->SetNumberField(TEXT("received"), static_cast(Total)); + Ack->SetBoolField(TEXT("complete"), true); + return Ack; + } + + const int32 TotalI = static_cast(Total); // <= kMaxAssetBytes, fits int32 + if (Buffer->Num() != TotalI) + { + Buffer->SetNumZeroed(TotalI); + *Received = 0; + } + if (Data.Num() > 0) + FMemory::Memcpy(Buffer->GetData() + Offset, Data.GetData(), Data.Num()); + *Received += Data.Num(); + + bool bJustCompleted = false; + if (*Received >= Total) + { + const FString ActualSha = FURLabAssetCache::Sha256Hex(*Buffer); + if (ActualSha != DeclaredSha) + { + // Reset the slot so the client can retry cleanly. + Buffer->Reset(); + *Received = 0; + return MakeError(TEXT("hash_mismatch"), + FString::Printf(TEXT("%s sha256 %s does not match declared %s"), + *(Kind == TEXT("xml") ? FString(TEXT("xml")) : Name), *ActualSha, *DeclaredSha)); + } + Cache.Put(DeclaredSha, *Buffer); + Buffer->Empty(); // blob now lives in the cache; free staging memory + *bComplete = true; + bJustCompleted = true; + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("upload_model_chunk_ok")); + Reply->SetStringField(TEXT("name"), Kind == TEXT("xml") ? TEXT("") : Name); + Reply->SetNumberField(TEXT("received"), + static_cast(bJustCompleted ? Total : *Received)); + Reply->SetBoolField(TEXT("complete"), *bComplete); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleUploadModelCommit(const TSharedPtr& Req) +{ + FString UploadId; + Req->TryGetStringField(TEXT("upload_id"), UploadId); + if (UploadId.IsEmpty()) + return MakeError(URLabError::BadRequest, TEXT("upload_model_commit requires 'upload_id'")); + + // Snapshot the staging entry (xml sha, asset name->sha, step mode) under the + // lock, verify completeness, then release before the slow materialise+import. + FString XmlSha, StepMode; + TMap AssetShaByName; + { + FUploadStaging& Staging = FUploadStaging::Get(); + FScopeLock Lock(&Staging.Mutex); + FStagingEntry* Entry = Staging.Entries.Find(UploadId); + if (!Entry || FPlatformTime::Seconds() - Entry->LastTouch > kUploadTtlSeconds) + { + if (Entry) + Staging.Entries.Remove(UploadId); + return MakeError(TEXT("unknown_upload"), + FString::Printf(TEXT("upload_id '%s' is unknown or expired"), *UploadId)); + } + if (!Entry->bXmlComplete) + return MakeError(URLabError::BadRequest, TEXT("commit before the xml blob completed")); + for (const TPair& Kv : Entry->Assets) + { + if (!Kv.Value.bComplete) + { + return MakeError(URLabError::BadRequest, + FString::Printf(TEXT("commit before asset '%s' completed"), *Kv.Key)); + } + AssetShaByName.Add(Kv.Key, Kv.Value.Sha); + } + XmlSha = Entry->XmlSha; + StepMode = Entry->StepMode; + Entry->LastTouch = FPlatformTime::Seconds(); + } + + // Materialise to a per-process temp working dir. Keyed by upload_id under the + // instance's own ProjectIntermediateDir so farm instances never collide. + FURLabAssetCache& Cache = FURLabAssetCache::Get(); + const FString WorkDir = FPaths::Combine(FPaths::ProjectIntermediateDir(), + TEXT("URLabUpload"), UploadId); + IFileManager::Get().MakeDirectory(*WorkDir, /*Tree=*/true); + + auto Cleanup = [&WorkDir, &UploadId]() { + IFileManager::Get().DeleteDirectory(*WorkDir, /*RequireExists=*/false, /*Tree=*/true); + FUploadStaging& Staging = FUploadStaging::Get(); + FScopeLock Lock(&Staging.Mutex); + Staging.Entries.Remove(UploadId); + }; + + auto CopyBlob = [&Cache](const FString& Sha, const FString& Dest, FString& OutErr) -> bool { + FString SrcPath; + if (!Cache.GetPath(Sha, SrcPath)) + { + OutErr = FString::Printf(TEXT("blob %s missing from cache"), *Sha); + return false; + } + if (IFileManager::Get().Copy(*Dest, *SrcPath) != COPY_OK) + { + OutErr = FString::Printf(TEXT("failed to materialise %s"), *Dest); + return false; + } + return true; + }; + + const FString XmlPath = FPaths::Combine(WorkDir, TEXT("model.xml")); + FString CopyErr; + if (!CopyBlob(XmlSha, XmlPath, CopyErr)) + { + Cleanup(); + return MakeError(TEXT("import_failed"), CopyErr); + } + for (const TPair& Kv : AssetShaByName) + { + const FString Dest = FPaths::Combine(WorkDir, Kv.Key); + if (!CopyBlob(Kv.Value, Dest, CopyErr)) + { + Cleanup(); + return MakeError(TEXT("import_failed"), CopyErr); + } + } + + // Run the EXISTING import_xml editor job on the materialised temp XML. It is + // registered (by URLabEditor) as an async game-thread job returning + // op_started + job_id; we drive it to completion by polling op_status, the + // same contract a remote client uses. No separate compile path. + URLabOpRegistry::FHandler ImportFn = URLabOpRegistry::GetHandler(TEXT("import_xml")); + URLabOpRegistry::FHandler StatusFn = URLabOpRegistry::GetHandler(TEXT("op_status")); + if (!ImportFn || !StatusFn) + { + Cleanup(); + return MakeError(TEXT("import_failed"), + TEXT("import_xml op is unavailable (editor module not loaded)")); + } + + TSharedPtr ImportReq = MakeShared(); + ImportReq->SetStringField(TEXT("op"), TEXT("import_xml")); + ImportReq->SetStringField(TEXT("path"), FPaths::ConvertRelativePathToFull(XmlPath)); + ImportReq->SetBoolField(TEXT("force_reimport"), true); + + TSharedPtr Started = ImportFn(ImportReq); + FString JobId, StartedOp; + if (Started.IsValid()) + { + Started->TryGetStringField(TEXT("op"), StartedOp); + Started->TryGetStringField(TEXT("job_id"), JobId); + } + + // A synchronous handler (no async job) returns the terminal reply directly. + TSharedPtr ImportResult; + if (StartedOp == TEXT("op_started") && !JobId.IsEmpty()) + { + // Poll op_status until terminal. No hard deadline (imports vary wildly); + // the client's recv timeout is the operational bound, and a draining + // bridge aborts us early. + for (;;) + { + if (IsDraining()) + { + Cleanup(); + return MakeError(URLabError::ShuttingDown, + TEXT("bridge draining; upload_model_commit abandoned")); + } + TSharedPtr StatusReq = MakeShared(); + StatusReq->SetStringField(TEXT("op"), TEXT("op_status")); + StatusReq->SetStringField(TEXT("job_id"), JobId); + TSharedPtr Status = StatusFn(StatusReq); + FString State; + if (Status.IsValid()) + Status->TryGetStringField(TEXT("state"), State); + if (State == TEXT("done") || State == TEXT("failed")) + { + const TSharedPtr* ResObj = nullptr; + if (Status->TryGetObjectField(TEXT("result"), ResObj) && ResObj) + ImportResult = *ResObj; + break; + } + if (State.IsEmpty()) // job vanished (unknown_job) + { + break; + } + FPlatformProcess::Sleep(0.05f); + } + } + else + { + ImportResult = Started; + } + + FString ResultOp, ResultErr; + if (ImportResult.IsValid()) + { + ImportResult->TryGetStringField(TEXT("op"), ResultOp); + ImportResult->TryGetStringField(TEXT("message"), ResultErr); + } + if (ResultOp != TEXT("import_xml_ok")) + { + Cleanup(); + return MakeError(TEXT("import_failed"), + ResultErr.IsEmpty() ? TEXT("import_xml did not complete") : ResultErr); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("upload_model_commit_ok")); + Reply->SetBoolField(TEXT("imported"), true); + { + FString ClassPath, ShortName; + ImportResult->TryGetStringField(TEXT("blueprint_class_path"), ClassPath); + ImportResult->TryGetStringField(TEXT("blueprint_short_name"), ShortName); + if (!ClassPath.IsEmpty()) + Reply->SetStringField(TEXT("blueprint_class_path"), ClassPath); + if (!ShortName.IsEmpty()) + Reply->SetStringField(TEXT("blueprint_short_name"), ShortName); + } + + // Echo model dims + mjb when a live manager already holds a compiled model + // (i.e. a scene is running). A fresh import only produces the Blueprint + // asset; standing up a live model requires PIE + spawn, which the client + // drives after commit. Absent a live model these fields are omitted. + if (!StepMode.IsEmpty()) + { + EStepMode Mode = EStepMode::Auto; + bool bHaveMode = true; + if (StepMode == TEXT("live")) + Mode = EStepMode::Live; + else if (StepMode == TEXT("direct")) + Mode = EStepMode::Direct; + else if (StepMode == TEXT("puppet")) + Mode = EStepMode::Puppet; + else + bHaveMode = false; + if (bHaveMode && OwnerMgr.IsValid()) + SetActiveStepMode(Mode); + } + + if (AAMjManager* Mgr = OwnerMgr.Get()) + { + if (Mgr->PhysicsEngine) + { + if (mjModel* m = Mgr->PhysicsEngine->GetModel()) + { + Reply->SetNumberField(TEXT("nq"), m->nq); + Reply->SetNumberField(TEXT("nv"), m->nv); + Reply->SetNumberField(TEXT("nu"), m->nu); + Reply->SetNumberField(TEXT("nbody"), m->nbody); + Reply->SetNumberField(TEXT("ngeom"), m->ngeom); + const int Sz = mj_sizeModel(m); + TArray Buf; + Buf.SetNum(Sz); + mj_saveModel(m, nullptr, Buf.GetData(), Sz); + FURLabMsgpackUtil::SetBinaryField(Reply, TEXT("mjb"), Buf.GetData(), Sz); + } + } + } + Reply->SetArrayField(TEXT("warnings"), TArray>()); + + UE_LOG(LogURLabNet, Log, TEXT("upload_model_commit: imported %s (%d assets)"), + *UploadId, AssetShaByName.Num()); + + // Opportunistic LRU eviction (no-op unless URLAB_ASSET_CACHE_MAX_BYTES is set). + Cache.EvictToBudget(); + + Cleanup(); + return Reply; +} diff --git a/Source/URLab/Private/Bridge/RpcHandlers_Scene.cpp b/Source/URLab/Private/Bridge/RpcHandlers_Scene.cpp new file mode 100644 index 00000000..6d2a3bce --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlers_Scene.cpp @@ -0,0 +1,669 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" +#include "Bridge/OpRegistry.h" +#include "Bridge/MsgpackHelpers.h" +#include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Spec/MjNodeComponent.h" +#include "MuJoCo/Core/MjArticulation.h" +#include "MuJoCo/Elements/MjActuatorRuntime.h" +#include "MuJoCo/Elements/MjSensorRuntime.h" +#include "MuJoCo/Elements/MjCamera.h" +#include "MuJoCo/Elements/MjJointRuntime.h" +#include "MuJoCo/Elements/MjBody.h" +#include "MuJoCo/Controllers/MjArticulationController.h" +#include "MuJoCo/Input/MjPerturbation.h" +#include "MuJoCo/Input/MjTwistController.h" +#include "Transport/NetworkManager.h" +#include "Transport/ShmPublishTransport.h" +#include "Transport/ShmRpcTransport.h" +#include "Transport/RpcTransport.h" +#include "Transport/ShmRegion.h" // FMjShmHeader (header_size in shm_rpc block) +#include "Bridge/BridgeServer.h" +#include "Replay/MjReplayManager.h" +#include "Kismet/GameplayStatics.h" +#include "Misc/Base64.h" +#include "Misc/Paths.h" +#include "Misc/FileHelper.h" +#include "Internationalization/Regex.h" +#include "HAL/FileManager.h" +#include "EngineUtils.h" +#include "Engine/World.h" +#include "Misc/Guid.h" +#include "Utils/URLabLogging.h" + +namespace +{ +/** Resolve a save / load path. Bare filename -> /Saved/URLab/Replays/. + * Must match AMjReplayManager::SaveRecordingToFile so the + * recording_save_ok absolute_path actually resolves on the bridge. */ +FString ResolveReplayPath(const FString& UserPath, const FString& DefaultBaseName = TEXT("")) +{ + FString BaseDir = FPaths::ProjectSavedDir() / TEXT("URLab") / TEXT("Replays"); + IFileManager::Get().MakeDirectory(*BaseDir, true); + + FString Path = UserPath; + if (Path.IsEmpty()) + { + Path = DefaultBaseName.IsEmpty() ? TEXT("recording.json") : DefaultBaseName; + } + + if (FPaths::IsRelative(Path)) + { + Path = FPaths::Combine(BaseDir, Path); + } + return FPaths::ConvertRelativePathToFull(Path); +} +} // namespace + +// ============================================================================= +// set_qpos — manager-required runtime write to a single articulation's qpos. +// +// Two write modes: +// - Free-base 7-vec shortcut: len=7 and the first joint is mjJNT_FREE, +// writes only the 7 free-joint slots (xyz + quat). Skips dof joints. +// - Full per-articulation qpos: len matches the articulation's total qpos +// dim (sum of per-joint slot widths in GetJoints() order). Writes the +// whole slice. +// Always calls mj_forward after the write, mirroring the puppet push-state +// path so derived quantities (xpos, sensors) reflect the new state. +// ============================================================================= +TSharedPtr FURLabRpcDispatcher::HandleSetQpos(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) + return MakeError(URLabError::NotReady, TEXT("Manager not initialised")); + + mjModel* m = Mgr->PhysicsEngine->GetModel(); + mjData* d = Mgr->PhysicsEngine->GetData(); + if (!m || !d) + return MakeError(URLabError::NotReady, TEXT("MjModel/MjData missing")); + + // target/target_by wire shape. target_by="actor_name" looks up via + // the manager's GetArticulation (UE name match); default + // "actor_id" walks ActorId. + FString Target, By; + Req->TryGetStringField(TEXT("target"), Target); + Req->TryGetStringField(TEXT("target_by"), By); + if (Target.IsEmpty()) + { + return MakeError(URLabError::MissingField, + TEXT("set_qpos: missing 'target' field")); + } + const bool bByName = By.Equals(TEXT("actor_name"), ESearchCase::IgnoreCase); + + AMjArticulation* Art = nullptr; + if (bByName) + { + Art = Mgr->GetArticulation(Target); + } + else + { + for (AMjArticulation* A : Mgr->GetAllArticulations()) + { + if (A && A->ActorId.Equals(Target)) + { + Art = A; + break; + } + } + } + if (!Art) + { + return MakeError(URLabError::UnknownArticulation, Target); + } + + if (TSharedPtr Denied = RejectIfNotControlOwner(FName(*Art->GetName()), Req)) + return Denied; + + const TArray>* QPosArr = nullptr; + if (!Req->TryGetArrayField(TEXT("qpos"), QPosArr) || !QPosArr) + return MakeError(URLabError::MissingField, TEXT("set_qpos requires 'qpos' array")); + + struct FJointSlot + { + int32 Adr; + int32 Size; + int32 Type; + }; + TArray Slots; + int32 ArtQDim = 0; + for (const UMjNodeComponent* J : Art->GetJoints()) + { + if (!J) + continue; + const int32 Id = J->GetBoundId().Get(-1); + if (Id < 0 || Id >= m->njnt) + continue; + int32 Size = 1; + switch (m->jnt_type[Id]) + { + case mjJNT_FREE: + Size = 7; + break; + case mjJNT_BALL: + Size = 4; + break; + case mjJNT_SLIDE: + case mjJNT_HINGE: + Size = 1; + break; + } + Slots.Add({m->jnt_qposadr[Id], Size, m->jnt_type[Id]}); + ArtQDim += Size; + } + + if (Slots.Num() == 0) + return MakeError(URLabError::NoJoints, + TEXT("Articulation has no joints; nothing to write")); + + const int32 InN = QPosArr->Num(); + bool bFreeBaseShortcut = false; + if (InN == 7 && Slots[0].Type == mjJNT_FREE && ArtQDim != 7) + bFreeBaseShortcut = true; + else if (InN != ArtQDim) + return MakeError(URLabError::DimMismatch, + FString::Printf( + TEXT("qpos length %d != articulation qpos dim %d (free-base shortcut requires len=7 with FREE root)"), + InN, ArtQDim)); + + // Read-back of the written qpos must stay inside the lock: a concurrent + // worker step (or a recompile) would otherwise tear the echoed values. + TArray> Out; + { + FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); + if (bFreeBaseShortcut) + { + const int32 Adr = Slots[0].Adr; + for (int32 i = 0; i < 7; ++i) + d->qpos[Adr + i] = (mjtNum)(*QPosArr)[i]->AsNumber(); + } + else + { + int32 Cursor = 0; + for (const FJointSlot& S : Slots) + { + for (int32 i = 0; i < S.Size; ++i, ++Cursor) + d->qpos[S.Adr + i] = (mjtNum)(*QPosArr)[Cursor]->AsNumber(); + } + } + mj_forward(m, d); + + if (bFreeBaseShortcut) + { + const int32 Adr = Slots[0].Adr; + for (int32 i = 0; i < 7; ++i) + Out.Add(MakeShared(d->qpos[Adr + i])); + } + else + { + for (const FJointSlot& S : Slots) + for (int32 i = 0; i < S.Size; ++i) + Out.Add(MakeShared(d->qpos[S.Adr + i])); + } + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("set_qpos_ok")); + // Echo back the resolved actor identifiers so the caller can + // confirm which articulation actually got the write. `target` + // matches the request's target field; `actor_name` is the UE + // name (always present, even if actor_id was the lookup key). + Reply->SetStringField(TEXT("target"), Target); + Reply->SetStringField(TEXT("actor_name"), Art->GetName()); + if (!Art->ActorId.IsEmpty()) + Reply->SetStringField(TEXT("actor_id"), Art->ActorId); + Reply->SetArrayField(TEXT("qpos"), Out); + Reply->SetBoolField(TEXT("free_base_shortcut"), bFreeBaseShortcut); + return Reply; +} + +// ============================================================================= +// set_mocap_pose / read_mocap_pose / get_contacts — runtime MJ-side reads/writes. +// +// All three operate directly on the live mjModel/mjData under the engine's +// CallbackMutex (same as set_qpos). Body name lookup uses mj_name2id with +// the full compiled MJ name (URLab prefixes are already included). +// ============================================================================= + +TSharedPtr FURLabRpcDispatcher::HandleSetMocapPose(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) + return MakeError(URLabError::NotReady, TEXT("Manager not initialised")); + + mjModel* m = Mgr->PhysicsEngine->GetModel(); + mjData* d = Mgr->PhysicsEngine->GetData(); + if (!m || !d) + return MakeError(URLabError::NotReady, TEXT("MjModel/MjData missing")); + + FString Body; + Req->TryGetStringField(TEXT("body"), Body); + if (Body.IsEmpty()) + return MakeError(URLabError::MissingField, TEXT("set_mocap_pose: missing 'body'")); + + const int32 BodyId = mj_name2id(m, mjOBJ_BODY, TCHAR_TO_UTF8(*Body)); + if (BodyId < 0) + return MakeError(URLabError::UnknownBody, Body); + + { + FName ArtKey; + for (AMjArticulation* Art : Mgr->GetAllArticulations()) + { + if (!Art) + continue; + for (UMjBody* B : Art->GetBodies()) + { + if (B && B->MjName.Get(B->GetName()).Equals(Body)) + { + ArtKey = FName(*Art->GetName()); + break; + } + } + if (!ArtKey.IsNone()) + break; + } + if (!ArtKey.IsNone()) + { + if (TSharedPtr Denied = RejectIfNotControlOwner(ArtKey, Req)) + return Denied; + } + } + + const int32 MocapId = m->body_mocapid[BodyId]; + if (MocapId < 0) + return MakeError(URLabError::NotMocapBody, + FString::Printf(TEXT("Body '%s' is not a mocap body"), *Body)); + + const TArray>* PosArr = nullptr; + const TArray>* QuatArr = nullptr; + const bool bHasPos = Req->TryGetArrayField(TEXT("pos"), PosArr) && PosArr && PosArr->Num() == 3; + const bool bHasQuat = Req->TryGetArrayField(TEXT("quat"), QuatArr) && QuatArr && QuatArr->Num() == 4; + if (!bHasPos && !bHasQuat) + return MakeError(URLabError::MissingField, + TEXT("set_mocap_pose requires at least one of pos[3] or quat[4]")); + + // Read-back of the written mocap pose must stay inside the lock: a concurrent + // worker step (or a recompile) would otherwise tear the echoed values. + TArray> PosOut, QuatOut; + { + FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); + if (bHasPos) + { + for (int32 i = 0; i < 3; ++i) + d->mocap_pos[3 * MocapId + i] = (mjtNum)(*PosArr)[i]->AsNumber(); + } + if (bHasQuat) + { + for (int32 i = 0; i < 4; ++i) + d->mocap_quat[4 * MocapId + i] = (mjtNum)(*QuatArr)[i]->AsNumber(); + } + + for (int32 i = 0; i < 3; ++i) + PosOut.Add(MakeShared(d->mocap_pos[3 * MocapId + i])); + for (int32 i = 0; i < 4; ++i) + QuatOut.Add(MakeShared(d->mocap_quat[4 * MocapId + i])); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("set_mocap_pose_ok")); + Reply->SetStringField(TEXT("body"), Body); + Reply->SetArrayField(TEXT("pos"), PosOut); + Reply->SetArrayField(TEXT("quat"), QuatOut); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleReadMocapPose(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) + return MakeError(URLabError::NotReady, TEXT("Manager not initialised")); + + mjModel* m = Mgr->PhysicsEngine->GetModel(); + mjData* d = Mgr->PhysicsEngine->GetData(); + if (!m || !d) + return MakeError(URLabError::NotReady, TEXT("MjModel/MjData missing")); + + FString Body; + Req->TryGetStringField(TEXT("body"), Body); + if (Body.IsEmpty()) + return MakeError(URLabError::MissingField, TEXT("read_mocap_pose: missing 'body'")); + + const int32 BodyId = mj_name2id(m, mjOBJ_BODY, TCHAR_TO_UTF8(*Body)); + if (BodyId < 0) + return MakeError(URLabError::UnknownBody, Body); + + const int32 MocapId = m->body_mocapid[BodyId]; + if (MocapId < 0) + return MakeError(URLabError::NotMocapBody, + FString::Printf(TEXT("Body '%s' is not a mocap body"), *Body)); + + TArray> PosOut, QuatOut; + { + FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); + for (int32 i = 0; i < 3; ++i) + PosOut.Add(MakeShared(d->mocap_pos[3 * MocapId + i])); + for (int32 i = 0; i < 4; ++i) + QuatOut.Add(MakeShared(d->mocap_quat[4 * MocapId + i])); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("read_mocap_pose_ok")); + Reply->SetStringField(TEXT("body"), Body); + Reply->SetArrayField(TEXT("pos"), PosOut); + Reply->SetArrayField(TEXT("quat"), QuatOut); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleGetContacts(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) + return MakeError(URLabError::NotReady, TEXT("Manager not initialised")); + + mjModel* m = Mgr->PhysicsEngine->GetModel(); + mjData* d = Mgr->PhysicsEngine->GetData(); + if (!m || !d) + return MakeError(URLabError::NotReady, TEXT("MjModel/MjData missing")); + + int32 MaxContacts = 64; + { + int32 Cap = 0; + if (Req->TryGetNumberField(TEXT("max_contacts"), Cap) && Cap > 0) + MaxContacts = Cap; + } + + // Optional filter: {body1?, body2?, geom1?, geom2?}. AND across set fields. + FString FBody1, FBody2, FGeom1, FGeom2; + const TSharedPtr* FilterObj = nullptr; + if (Req->TryGetObjectField(TEXT("filter"), FilterObj) && FilterObj && *FilterObj) + { + (*FilterObj)->TryGetStringField(TEXT("body1"), FBody1); + (*FilterObj)->TryGetStringField(TEXT("body2"), FBody2); + (*FilterObj)->TryGetStringField(TEXT("geom1"), FGeom1); + (*FilterObj)->TryGetStringField(TEXT("geom2"), FGeom2); + } + + auto NameOrEmpty = [&](int Type, int Id) -> FString { + if (Id < 0) + return FString(); + const char* p = mj_id2name(m, Type, Id); + return p ? FString(UTF8_TO_TCHAR(p)) : FString(); + }; + + TArray> Out; + bool bTruncated = false; + int32 Matched = 0; + + { + FScopeLock Lock(&Mgr->PhysicsEngine->CallbackMutex); + const int32 N = d->ncon; + for (int32 i = 0; i < N; ++i) + { + const mjContact& c = d->contact[i]; + const int32 G1 = c.geom[0]; + const int32 G2 = c.geom[1]; + const int32 B1 = (G1 >= 0 && G1 < m->ngeom) ? m->geom_bodyid[G1] : -1; + const int32 B2 = (G2 >= 0 && G2 < m->ngeom) ? m->geom_bodyid[G2] : -1; + const FString G1Name = NameOrEmpty(mjOBJ_GEOM, G1); + const FString G2Name = NameOrEmpty(mjOBJ_GEOM, G2); + const FString B1Name = NameOrEmpty(mjOBJ_BODY, B1); + const FString B2Name = NameOrEmpty(mjOBJ_BODY, B2); + + if (!FGeom1.IsEmpty() && !G1Name.Equals(FGeom1)) + continue; + if (!FGeom2.IsEmpty() && !G2Name.Equals(FGeom2)) + continue; + if (!FBody1.IsEmpty() && !B1Name.Equals(FBody1)) + continue; + if (!FBody2.IsEmpty() && !B2Name.Equals(FBody2)) + continue; + + if (Matched >= MaxContacts) + { + bTruncated = true; + break; + } + + mjtNum Force[6] = {0}; + mj_contactForce(m, d, i, Force); + + TArray> Pos; + for (int32 k = 0; k < 3; ++k) + Pos.Add(MakeShared(c.pos[k])); + // First row of the contact frame is the contact normal. + TArray> Normal; + for (int32 k = 0; k < 3; ++k) + Normal.Add(MakeShared(c.frame[k])); + TArray> ForceArr; + for (int32 k = 0; k < 6; ++k) + ForceArr.Add(MakeShared(Force[k])); + + TSharedPtr CObj = MakeShared(); + CObj->SetStringField(TEXT("geom1"), G1Name); + CObj->SetStringField(TEXT("geom2"), G2Name); + CObj->SetStringField(TEXT("body1"), B1Name); + CObj->SetStringField(TEXT("body2"), B2Name); + CObj->SetArrayField(TEXT("pos"), Pos); + CObj->SetArrayField(TEXT("normal"), Normal); + CObj->SetNumberField(TEXT("dist"), c.dist); + CObj->SetArrayField(TEXT("force"), ForceArr); + Out.Add(MakeShared(CObj)); + ++Matched; + } + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("get_contacts_ok")); + Reply->SetNumberField(TEXT("n_contacts"), Matched); + Reply->SetBoolField(TEXT("truncated"), bTruncated); + Reply->SetArrayField(TEXT("contacts"), Out); + return Reply; +} + +TSharedPtr FURLabRpcDispatcher::HandleListKeyframes(const TSharedPtr& /*Req*/) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr || !Mgr->PhysicsEngine || !Mgr->PhysicsEngine->IsInitialized()) + return MakeError(URLabError::NotReady, TEXT("Manager not initialised")); + + mjModel* m = Mgr->PhysicsEngine->GetModel(); + if (!m) + return MakeError(URLabError::NotReady, TEXT("MjModel missing")); + + auto Slice = [](const mjtNum* src, int32 stride, int32 idx, int32 width) { + TArray> Out; + if (!src || width <= 0) + return Out; + for (int32 k = 0; k < width; ++k) + Out.Add(MakeShared(src[idx * stride + k])); + return Out; + }; + + TArray> Keys; + for (int32 i = 0; i < m->nkey; ++i) + { + const char* NameC = mj_id2name(m, mjOBJ_KEY, i); + TSharedPtr K = MakeShared(); + K->SetStringField(TEXT("name"), NameC ? UTF8_TO_TCHAR(NameC) : TEXT("")); + K->SetNumberField(TEXT("time"), m->key_time ? m->key_time[i] : 0.0); + K->SetArrayField(TEXT("qpos"), Slice(m->key_qpos, m->nq, i, m->nq)); + K->SetArrayField(TEXT("qvel"), Slice(m->key_qvel, m->nv, i, m->nv)); + K->SetArrayField(TEXT("ctrl"), Slice(m->key_ctrl, m->nu, i, m->nu)); + K->SetArrayField(TEXT("mocap_pos"), Slice(m->key_mpos, m->nmocap * 3, i, m->nmocap * 3)); + K->SetArrayField(TEXT("mocap_quat"), Slice(m->key_mquat, m->nmocap * 4, i, m->nmocap * 4)); + Keys.Add(MakeShared(K)); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("list_keyframes_ok")); + Reply->SetArrayField(TEXT("keyframes"), Keys); + return Reply; +} + +// ============================================================================= +// recording_* / replay_* delegate to AMjReplayManager +// ============================================================================= + +TSharedPtr FURLabRpcDispatcher::HandleRecording(const FString& Op, const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + // Use the game-thread-cached pointer; TActorIterator from this worker + // thread would assert IsInGameThread() and crash. + AMjReplayManager* RM = CachedReplayManager.Get(); + if (!RM) + return MakeError(URLabError::NotReady, TEXT("AMjReplayManager not present in scene")); + + if (Op.Equals(TEXT("recording_start"))) + { + if (RM->bIsRecording) + return MakeError(URLabError::RecordingAlreadyActive, TEXT("Recording already active")); + double MaxDur = 0.0; + if (Req->TryGetNumberField(TEXT("max_duration_s"), MaxDur)) + RM->MaxRecordDuration = (float)MaxDur; + else + RM->MaxRecordDuration = FLT_MAX; + RM->StartRecording(); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("recording_start_ok")); + Reply->SetStringField(TEXT("name"), AMjReplayManager::LiveSessionName); + Reply->SetNumberField(TEXT("max_duration_s"), RM->MaxRecordDuration); + return Reply; + } + if (Op.Equals(TEXT("recording_stop"))) + { + if (!RM->bIsRecording) + return MakeError(URLabError::RecordingNotActive, TEXT("Recording is not active")); + RM->StopRecording(); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("recording_stop_ok")); + Reply->SetStringField(TEXT("name"), AMjReplayManager::LiveSessionName); + // Populate the summary fields the Python client maps to + // RecordingSummary. Recording has been stopped (StopRecording + // above set bIsRecording=false) so the OnPostStep hook isn't + // mutating Frames in parallel. + Reply->SetNumberField(TEXT("frame_count"), static_cast(RM->GetLiveFrameCount())); + Reply->SetNumberField(TEXT("sim_duration_s"), RM->GetLiveSimDurationS()); + return Reply; + } + if (Op.Equals(TEXT("recording_save"))) + { + FString Path; + Req->TryGetStringField(TEXT("path"), Path); + FString FileName = Path.IsEmpty() ? TEXT("recording.json") : FPaths::GetCleanFilename(Path); + if (Path.IsEmpty()) + Path = FileName; + bool bOk = RM->SaveRecordingToFile(FileName); + // ResolveReplayPath now matches the manager's + // ProjectSavedDir/URLab/Replays/ output dir, so the absolute_path + // returned to the client points at the file the manager actually wrote. + FString Abs = ResolveReplayPath(Path); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), bOk ? TEXT("recording_save_ok") : TEXT("error")); + Reply->SetStringField(TEXT("absolute_path"), Abs); + if (!bOk) + Reply->SetStringField(TEXT("code"), URLabError::PathNotWritable); + return Reply; + } + if (Op.Equals(TEXT("recording_clear"))) + { + RM->ClearRecording(); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("recording_clear_ok")); + return Reply; + } + return MakeError(URLabError::UnknownOp, Op); +} + +TSharedPtr FURLabRpcDispatcher::HandleReplay(const FString& Op, const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + AMjReplayManager* RM = CachedReplayManager.Get(); + if (!RM) + return MakeError(URLabError::NotReady, TEXT("AMjReplayManager not present in scene")); + + if (Op.Equals(TEXT("replay_load"))) + { + FString P; + if (!Req->TryGetStringField(TEXT("path"), P)) + return MakeError(URLabError::MissingField, TEXT("replay_load requires 'path'")); + FString FileName = FPaths::GetCleanFilename(P); + bool bOk = RM->LoadRecordingFromFile(FileName); + if (!bOk) + return MakeError(URLabError::PathNotReadable, P); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("replay_load_ok")); + Reply->SetStringField(TEXT("name"), FPaths::GetBaseFilename(FileName)); + return Reply; + } + if (Op.Equals(TEXT("replay_list_sessions"))) + { + TArray> Names; + for (const FString& N : RM->GetSessionNames()) + Names.Add(MakeShared(N)); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("replay_list_sessions_ok")); + Reply->SetArrayField(TEXT("sessions"), Names); + return Reply; + } + if (Op.Equals(TEXT("replay_set_active"))) + { + FString N; + if (!Req->TryGetStringField(TEXT("name"), N)) + return MakeError(URLabError::MissingField, TEXT("replay_set_active requires 'name'")); + if (!RM->Sessions.Contains(N)) + return MakeError(URLabError::ReplaySessionNotFound, N); + RM->SetActiveSession(N); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("replay_set_active_ok")); + return Reply; + } + if (Op.Equals(TEXT("replay_start"))) + { + if (ActiveStepMode == EStepMode::Live) + return MakeError(URLabError::ReplayRequiresStepped, + TEXT("Switch to direct or puppet before starting replay")); + RM->StartReplay(); + int32 Total = RM->Sessions.Contains(RM->GetActiveSessionName()) + ? RM->Sessions[RM->GetActiveSessionName()].Frames.Num() + : 0; + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("replay_start_ok")); + Reply->SetStringField(TEXT("active_session"), RM->GetActiveSessionName()); + Reply->SetNumberField(TEXT("total_frames"), Total); + return Reply; + } + if (Op.Equals(TEXT("replay_stop"))) + { + RM->StopReplay(); + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("replay_stop_ok")); + return Reply; + } + return MakeError(URLabError::UnknownOp, Op); +} diff --git a/Source/URLab/Private/Bridge/RpcHandlers_SimOptions.cpp b/Source/URLab/Private/Bridge/RpcHandlers_SimOptions.cpp new file mode 100644 index 00000000..15747772 --- /dev/null +++ b/Source/URLab/Private/Bridge/RpcHandlers_SimOptions.cpp @@ -0,0 +1,536 @@ +// Copyright (c) 2026 Jonathan Embley-Riches. All rights reserved. +// +// 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. +// +// --- LEGAL DISCLAIMER --- +// UnrealRoboticsLab is an independent software plugin. It is NOT affiliated with, +// endorsed by, or sponsored by Epic Games, Inc. "Unreal" and "Unreal Engine" are +// trademarks or registered trademarks of Epic Games, Inc. in the US and elsewhere. +// +// This plugin incorporates third-party software: MuJoCo (Apache 2.0), +// CoACD (MIT), and libzmq (MPL 2.0). See ThirdPartyNotices.txt for details. + +#include "Bridge/RpcDispatcher.h" +#include "Bridge/RpcErrorCodes.h" +#include "Bridge/OpRegistry.h" +#include "Bridge/MsgpackHelpers.h" +#include "MuJoCo/Core/AMjManager.h" +#include "MuJoCo/Spec/MjNodeComponent.h" +#include "MuJoCo/Core/MjSceneOptions.h" +#include "MuJoCo/Gen/Elements/Options/MjFlag.gen.h" +#include "MuJoCo/Gen/Elements/Options/MjOption.gen.h" +#include "MuJoCo/Gen/MjKeywords.gen.h" +#include "MuJoCo/Core/MjArticulation.h" +#include "MuJoCo/Elements/MjActuatorRuntime.h" +#include "MuJoCo/Elements/MjSensorRuntime.h" +#include "MuJoCo/Elements/MjCamera.h" +#include "MuJoCo/Elements/MjJointRuntime.h" +#include "MuJoCo/Elements/MjBody.h" +#include "MuJoCo/Controllers/MjArticulationController.h" +#include "MuJoCo/Input/MjPerturbation.h" +#include "MuJoCo/Input/MjTwistController.h" +#include "Transport/NetworkManager.h" +#include "Transport/ShmPublishTransport.h" +#include "Transport/ShmRpcTransport.h" +#include "Transport/RpcTransport.h" +#include "Transport/ShmRegion.h" // FMjShmHeader (header_size in shm_rpc block) +#include "Bridge/BridgeServer.h" +#include "Replay/MjReplayManager.h" +#include "Kismet/GameplayStatics.h" +#include "Misc/Base64.h" +#include "Misc/Paths.h" +#include "Misc/FileHelper.h" +#include "Internationalization/Regex.h" +#include "HAL/FileManager.h" +#include "EngineUtils.h" +#include "Engine/World.h" +#include "Misc/Guid.h" +#include "Utils/URLabLogging.h" + +// ============================================================================= +// configure_controller +// ============================================================================= + +TSharedPtr FURLabRpcDispatcher::HandleConfigureController(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr) + return MakeError(URLabError::NotReady, TEXT("Manager missing")); + + FString ArtName; + if (!Req->TryGetStringField(TEXT("articulation"), ArtName)) + return MakeError(URLabError::MissingField, TEXT("configure_controller requires 'articulation'")); + + AMjArticulation* Art = Mgr->GetArticulation(ArtName); + if (!Art) + return MakeError(URLabError::UnknownArticulation, ArtName); + + UMjArticulationController* Ctrl = Art->FindComponentByClass(); + if (!Ctrl) + return MakeError(URLabError::NoController, FString::Printf(TEXT("Articulation '%s' has no controller"), *ArtName)); + + const TSharedPtr* Params = nullptr; + if (Req->TryGetObjectField(TEXT("params"), Params) && Params && Params->IsValid()) + { + Ctrl->ApplyConfig(*Params); + } + + TSharedPtr Reply = MakeShared(); + Reply->SetStringField(TEXT("op"), TEXT("configure_controller_ok")); + Reply->SetStringField(TEXT("articulation"), ArtName); + + TSharedPtr Out = MakeShared(); + Ctrl->GetCurrentConfig(Out); + Reply->SetObjectField(TEXT("params"), Out); + return Reply; +} + +// ============================================================================= +// set_sim_options +// ============================================================================= + +namespace +{ +/** An MJCF keyword. The schema's spellings are ASCII by construction. */ +FString KeywordToString(std::string_view Keyword) +{ + FString Out; + Out.Reserve(static_cast(Keyword.size())); + for (const char Character : Keyword) + { + Out.AppendChar(static_cast(Character)); + } + return Out; +} + +/** + * An MJCF keyword, matched without regard to case. + * + * The keyword tables are generated from the schema, so the spellings are + * MuJoCo's own ("Euler", "RK4", "PGS"). The wire has always accepted any + * casing, and the ordinal scan below stops at the first spelling the table + * does not know, which is how it learns the enum's arity without one being + * declared anywhere. + */ +template +bool ParseKeyword(const FString& Text, TEnum& Out) +{ + for (int32 Ordinal = 0; Ordinal < 256; ++Ordinal) + { + const TEnum Candidate = static_cast(Ordinal); + const std::string_view Keyword = ps::ue::ToMjcf(Candidate); + if (Keyword.empty()) + { + return false; + } + if (Text.Equals(KeywordToString(Keyword), ESearchCase::IgnoreCase)) + { + Out = Candidate; + return true; + } + } + return false; +} + +template +FString KeywordOf(TEnum Value) +{ + return KeywordToString(ps::ue::ToMjcf(Value)); +} + +bool TryReadVec3(const TSharedPtr& Obj, const TCHAR* Key, double Out[3]) +{ + const TArray>* Arr = nullptr; + if (!Obj->TryGetArrayField(Key, Arr) || !Arr || Arr->Num() != 3) + return false; + Out[0] = (*Arr)[0]->AsNumber(); + Out[1] = (*Arr)[1]->AsNumber(); + Out[2] = (*Arr)[2]->AsNumber(); + return true; +} +} // namespace + +TSharedPtr FURLabRpcDispatcher::HandleSetSimOptions(const TSharedPtr& Req) +{ + AAMjManager* Mgr = OwnerMgr.Get(); + if (!Mgr || !Mgr->PhysicsEngine) + return MakeError(URLabError::NotReady, TEXT("PhysicsEngine not initialised")); + + const TSharedPtr* OptsPtr = nullptr; + if (!Req->TryGetObjectField(TEXT("options"), OptsPtr) || !OptsPtr || !(*OptsPtr).IsValid()) + return MakeError(URLabError::MissingField, TEXT("set_sim_options requires 'options' object")); + const TSharedPtr& Opts = *OptsPtr; + + UMjOption* const O = Mgr->SceneOption; + UMjFlag* const F = Mgr->SceneFlags; + if (O == nullptr || F == nullptr) + return MakeError(URLabError::NotReady, TEXT("scene