mujoco_rs/cpp_viewer.rs
1use crate::mujoco_c::*;
2use std::ffi::CString;
3use std::ops::Deref;
4
5use crate::wrappers::mj_visualization::*;
6use crate::wrappers::mj_model::MjModel;
7use crate::wrappers::mj_data::MjData;
8
9
10/// Wrapper around the C++ implementation of MujoCo viewer.
11/// If you don't need the side UI, we recommend you use the Rust-native viewer [`crate::viewer::MjViewer`] instead.
12///
13/// # Safety
14/// Calls to [`MjViewerCpp::render`] must be done only on the **main** thread!
15/// For convenience [`MjViewerCpp`] implements both `Send` and `Sync`, however that is meant only for
16/// syncing the viewer.
17///
18/// Additionally, to allow certain flexibility while still maintaining
19/// compatibility with the C++ code, [`MjViewerCpp`] keeps internal pointers to mjModel and mjData,
20/// which are wrapped inside [`MjModel`] and [`MjData`], respectively.
21/// This technically allows `model` and `data` to be modified
22/// while the viewer keeps a pointer to them (their wrapped pointers).
23/// Undefined behavior should not occur, however caution is advised as this is a violation
24/// of the Rust's borrowing rules.
25pub struct MjViewerCpp<M: Deref<Target = MjModel> + Clone> {
26 sim: *mut mujoco_Simulate,
27 running: bool,
28
29 // Store these here since the C++ bindings save references to them.
30 // We don't actually need them ourselves, at least not here.
31 _cam: Box<MjvCamera>,
32 _opt: Box<MjvOption>,
33 _pert: Box<MjvPerturb>,
34 _user_scn: Box<MjvScene<M>>,
35}
36
37impl<M: Deref<Target = MjModel> + Clone> MjViewerCpp<M> {
38 #[inline]
39 pub fn running(&self) -> bool {
40 self.running
41 }
42
43 #[inline]
44 pub fn user_scn_mut(&mut self) -> &mut MjvScene<M> {
45 &mut self._user_scn
46 }
47
48 /// Launches a wrapper around MuJoCo's C++ viewer. The `max_user_geom` parameter
49 /// defines how much space will be allocated for additional, user-defined visual-only geoms.
50 /// It can thus be set to 0 if no additional geoms will be drawn by the user.
51 /// Unlike the Rust-native viewer ([`crate::viewer::MjViewer`]), this also accepts a `data` parameter.
52 /// Additionally, this just returns a [`MjViewerCpp`] instance directly, without result
53 /// as the initialization may fail internally in C++ anyway, which we have no way of checking.
54 ///
55 /// # Safety
56 /// See [`MjViewerCpp`]'s note.
57 pub fn launch_passive(model: M, data: &MjData<M>, max_user_geom: usize) -> Self {
58 // Allocate on the heap as the data must not be moved due to C++ bindings
59 let mut _cam = Box::new(MjvCamera::default());
60 let mut _opt: Box<MjvOption> = Box::new(MjvOption::default());
61 let mut _pert = Box::new(MjvPerturb::default());
62 let mut _user_scn = Box::new(MjvScene::new(model.clone(), max_user_geom));
63 let sim;
64 let c_filename = CString::new("file.xml").unwrap();
65 unsafe {
66 sim = mujoco_cSimulate_create(&mut *_cam, &mut *_opt, &mut *_pert, _user_scn.ffi_mut(), true);
67 mujoco_cSimulate_RenderInit(sim);
68 mujoco_cSimulate_Load(sim, model.__raw(), data.__raw(), c_filename.as_ptr());
69 mujoco_cSimulate_RenderStep(sim, true);
70 }
71
72 Self {sim, running: true, _cam, _opt, _pert, _user_scn}
73 }
74
75 /// Returns the underlying C++ binding object of the viewer.
76 pub fn __raw(&self) -> *mut mujoco_Simulate {
77 self.sim
78 }
79
80 /// Renders the simulation.
81 /// `update_timer` flag specifies whether the time should be updated
82 /// inside the viewer (for FPS calculation).
83 /// # SAFETY
84 /// This needs to be called periodically from the MAIN thread, otherwise
85 /// GLFW stops working.
86 pub fn render(&mut self, update_timer: bool) {
87 unsafe {
88 assert!(self.running, "render called after viewer has been closed!");
89 self.running = mujoco_cSimulate_RenderStep(self.sim, update_timer) == 1;
90 }
91 }
92
93 /// Syncs the simulation state with the viewer as well as perform
94 /// rendering on the viewer.
95 pub fn sync(&mut self) {
96 unsafe {
97 mujoco_cSimulate_Sync(self.sim, false);
98 }
99 }
100}
101
102impl<M: Deref<Target = MjModel> + Clone> Drop for MjViewerCpp<M> {
103 fn drop(&mut self) {
104 unsafe {
105 mujoco_cSimulate_RenderCleanup(self.sim);
106 mujoco_cSimulate_destroy(self.sim);
107 }
108 }
109}
110
111unsafe impl<M: Deref<Target = MjModel> + Clone> Send for MjViewerCpp<M> {}
112unsafe impl<M: Deref<Target = MjModel> + Clone> Sync for MjViewerCpp<M> {}