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