MVSim
Lightweight simulator for 2.5D vehicles and robots
World.h
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2026 Jose Luis Blanco Claraco |
5  | Copyright (C) 2017 Borys Tymchenko (Odessa Polytechnic University) |
6  | Distributed under 3-clause BSD License |
7  | See COPYING |
8  +-------------------------------------------------------------------------+ */
9 
10 #pragma once
11 
12 #include <box2d/b2_body.h>
13 #include <box2d/b2_distance_joint.h>
14 #include <box2d/b2_revolute_joint.h>
15 #include <box2d/b2_world.h>
16 #include <mrpt/core/bits_math.h>
17 #include <mrpt/core/format.h>
18 #include <mrpt/gui/CDisplayWindowGUI.h>
19 #include <mrpt/img/CImage.h>
20 #include <mrpt/img/TColor.h>
21 #include <mrpt/math/TPoint3D.h>
22 #include <mrpt/obs/CObservation.h>
23 #include <mrpt/obs/CObservationImage.h>
24 #include <mrpt/obs/obs_frwds.h>
25 #include <mrpt/system/COutputLogger.h>
26 #include <mrpt/system/CTicTac.h>
27 #include <mrpt/system/CTimeLogger.h>
28 #include <mrpt/topography/data_types.h>
29 #include <mvsim/Block.h>
30 #include <mvsim/HumanActor.h>
31 #include <mvsim/Joystick.h>
32 #include <mvsim/RemoteResourcesManager.h>
33 #include <mvsim/TParameterDefinitions.h>
34 #include <mvsim/VehicleBase.h>
35 #include <mvsim/WorldElements/WorldElementBase.h>
36 
37 //
38 #include <mrpt/version.h>
39 #if MRPT_VERSION >= 0x020f07
40 #include <mrpt/io/CCompressedOutputStream.h>
41 #else
42 #include <mrpt/io/CFileGZOutputStream.h>
43 #endif
44 
45 #if defined(MVSIM_HAS_ZMQ) && defined(MVSIM_HAS_PROTOBUF)
46 #include <mvsim/Comms/Client.h>
47 #endif
48 
49 #include <any>
50 #include <atomic>
51 #include <functional>
52 #include <list>
53 #include <map>
54 #include <set>
55 #include <unordered_map>
56 
57 #if MVSIM_HAS_ZMQ && MVSIM_HAS_PROTOBUF
58 // forward declarations:
59 namespace mvsim_msgs
60 {
61 class SrvGetPose;
62 class SrvGetPoseAnswer;
63 class SrvSetPose;
64 class SrvSetPoseAnswer;
65 class SrvSetControllerTwist;
66 class SrvSetControllerTwistAnswer;
67 class SrvShutdown;
68 class SrvShutdownAnswer;
69 } // namespace mvsim_msgs
70 #endif
71 
72 namespace mvsim
73 {
90 struct WorldJoint
91 {
92  enum class Type : uint8_t
93  {
94  Distance = 0,
95  Revolute
96  };
97 
98  Type type = Type::Distance;
99 
101  std::string bodyA_name;
102  std::string bodyB_name;
103 
105  mrpt::math::TPoint2D anchorA{0, 0};
106  mrpt::math::TPoint2D anchorB{0, 0};
107 
109  float maxLength = 5.0f;
110  float minLength = 0.0f;
111  float stiffness = 0.0f;
112  float damping = 0.0f;
113 
115  bool enableLimit = false;
116  float lowerAngle_deg = 0.0f;
117  float upperAngle_deg = 0.0f;
118 
120  b2Joint* b2joint = nullptr;
121 };
122 
132 class World : public mrpt::system::COutputLogger
133 {
134  public:
137  World();
138  ~World();
139 
140  // Rule of Five: explicitly delete copy operations, allow move operations
141  World(const World&) = delete;
142  World& operator=(const World&) = delete;
143  World(World&&) = delete;
144  World& operator=(World&&) = delete;
145 
148  void clear_all();
149 
156  void load_from_XML_file(const std::string& xmlFileNamePath);
157 
158  void internal_initialize();
159 
168  const std::string& xml_text, const std::string& fileNameForPath = std::string("."));
175  double get_simul_time() const
176  {
177  auto lck = mrpt::lockHelper(simul_time_mtx_);
178  return simulTime_;
179  }
180 
182  void force_set_simul_time(double newSimulatedTime)
183  {
184  auto lck = mrpt::lockHelper(simul_time_mtx_);
185  simulTime_ = newSimulatedTime;
186  }
187 
193  mrpt::Clock::time_point get_simul_timestamp() const
194  {
195  auto lck = mrpt::lockHelper(simul_time_mtx_);
196  ASSERT_(simul_start_wallclock_time_.has_value());
197  return mrpt::Clock::fromDouble(simulTime_ + simul_start_wallclock_time_.value());
198  }
199 
203  bool has_simul_timestamp() const
204  {
205  auto lck = mrpt::lockHelper(simul_time_mtx_);
206  return simul_start_wallclock_time_.has_value();
207  }
208 
212  double get_realtime_factor_achieved() const { return achievedRealtimeFactor_.load(); }
213 
215  double get_simul_timestep() const;
216 
220  void set_simul_timestep(double timestep) { simulTimestep_ = timestep; }
221 
224  double get_gravity() const { return gravity_; }
225 
228  void set_gravity(double accel) { gravity_ = accel; }
229 
235  void run_simulation(double dt);
236 
237  void insert_vehicle(const VehicleBase::Ptr& veh);
238 
240  struct GUIKeyEvent
241  {
242  int keycode = 0;
243  bool modifierShift = false;
244  bool modifierCtrl = false;
245  bool modifierAlt = false;
246  bool modifierSuper = false;
247 
248  GUIKeyEvent() = default;
249  };
250 
252  {
254  std::string msg_lines;
255 
256  TUpdateGUIParams() = default;
257  };
258 
266  void update_GUI(TUpdateGUIParams* params = nullptr);
267 
268  const mrpt::gui::CDisplayWindowGUI::Ptr& gui_window() const { return gui_.gui_win; }
269 
270  const mrpt::math::TPoint3D& gui_mouse_point() const { return gui_.clickedPt; }
271 
277  mrpt::opengl::CSetOfObjects::Ptr guiUserObjectsPhysical_, guiUserObjectsViz_;
278  std::mutex guiUserObjectsMtx_;
279 
284 
285  void internalRunSensorsOn3DScene(mrpt::opengl::COpenGLScene& physicalObjects);
286 
287  void internalUpdate3DSceneObjects(
288  mrpt::opengl::COpenGLScene& viz, mrpt::opengl::COpenGLScene& physical);
289  void internal_GUI_thread();
290  void internal_process_pending_gui_user_tasks();
291 
292  std::mutex pendingRunSensorsOn3DSceneMtx_;
293  bool pendingRunSensorsOn3DScene_ = false;
294 
295  void mark_as_pending_running_sensors_on_3D_scene()
296  {
297  pendingRunSensorsOn3DSceneMtx_.lock();
298  pendingRunSensorsOn3DScene_ = true;
299  pendingRunSensorsOn3DSceneMtx_.unlock();
300  }
301  void clear_pending_running_sensors_on_3D_scene()
302  {
303  pendingRunSensorsOn3DSceneMtx_.lock();
304  pendingRunSensorsOn3DScene_ = false;
305  pendingRunSensorsOn3DSceneMtx_.unlock();
306  }
307  bool pending_running_sensors_on_3D_scene()
308  {
309  pendingRunSensorsOn3DSceneMtx_.lock();
310  bool ret = pendingRunSensorsOn3DScene_;
311  pendingRunSensorsOn3DSceneMtx_.unlock();
312  return ret;
313  }
314 
315  std::string guiMsgLines_;
316  std::mutex guiMsgLinesMtx_;
317 
318  std::thread gui_thread_;
319 
320  std::atomic_bool gui_thread_running_ = false;
321  std::atomic_bool simulator_must_close_ = false;
322  mutable std::mutex gui_thread_start_mtx_;
323 
324  bool simulator_must_close() const
325  {
326  gui_thread_start_mtx_.lock();
327  const bool v = simulator_must_close_;
328  gui_thread_start_mtx_.unlock();
329  return v;
330  }
331  void simulator_must_close(bool value)
332  {
333  gui_thread_start_mtx_.lock();
334  simulator_must_close_ = value;
335  gui_thread_start_mtx_.unlock();
336  }
337 
338  void enqueue_task_to_run_in_gui_thread(const std::function<void(void)>& f)
339  {
340  guiUserPendingTasksMtx_.lock();
341  guiUserPendingTasks_.emplace_back(f);
342  guiUserPendingTasksMtx_.unlock();
343  }
344 
345  std::vector<std::function<void(void)>> guiUserPendingTasks_;
346  std::mutex guiUserPendingTasksMtx_;
347 
348  GUIKeyEvent lastKeyEvent_;
349  std::atomic_bool lastKeyEventValid_ = false;
350  std::mutex lastKeyEventMtx_;
351 
352  bool is_GUI_open() const;
354 
355  void close_GUI();
356 
363  using VehicleList = std::multimap<std::string, VehicleBase::Ptr>;
364 
366  using WorldElementList = std::list<WorldElementBase::Ptr>;
367 
369  using BlockList = std::multimap<std::string, Block::Ptr>;
370 
373  using SimulableList = std::multimap<std::string, Simulable::Ptr>;
374 
376  using ActorList = std::multimap<std::string, HumanActor::Ptr>;
377 
382  std::unique_ptr<b2World>& getBox2DWorld() { return box2d_world_; }
383  const std::unique_ptr<b2World>& getBox2DWorld() const { return box2d_world_; }
384  b2Body* getBox2DGroundBody() { return b2_ground_body_; }
385  const VehicleList& getListOfVehicles() const { return vehicles_; }
386  VehicleList& getListOfVehicles() { return vehicles_; }
387  const BlockList& getListOfBlocks() const { return blocks_; }
388  BlockList& getListOfBlocks() { return blocks_; }
389  const WorldElementList& getListOfWorldElements() const { return worldElements_; }
390 
391  const std::vector<WorldJoint>& getListOfJoints() const { return joints_; }
392 
393  const ActorList& getListOfActors() const { return actors_; }
394  ActorList& getListOfActors() { return actors_; }
395 
397  SimulableList& getListOfSimulableObjects() { return simulableObjects_; }
398  const SimulableList& getListOfSimulableObjects() const { return simulableObjects_; }
399  auto& getListOfSimulableObjectsMtx() { return simulableObjectsMtx_; }
400 
401  mrpt::system::CTimeLogger& getTimeLogger() { return timlogger_; }
402 
406  std::string local_to_abs_path(const std::string& in_path) const;
407 
414  std::string xmlPathToActualPath(const std::string& modelURI) const;
415 
421  using vehicle_visitor_t = std::function<void(VehicleBase&)>;
422  using world_element_visitor_t = std::function<void(WorldElementBase&)>;
423  using block_visitor_t = std::function<void(Block&)>;
424 
426  void runVisitorOnVehicles(const vehicle_visitor_t& v);
427 
429  void runVisitorOnWorldElements(const world_element_visitor_t& v);
430 
432  void runVisitorOnBlocks(const block_visitor_t& v);
433 
439  using on_observation_callback_t =
440  std::function<void(const Simulable& /*veh*/, const mrpt::obs::CObservation::Ptr& /*obs*/)>;
441 
442  void registerCallbackOnObservation(const on_observation_callback_t& f)
443  {
444  callbacksOnObservation_.emplace_back(f);
445  }
446 
448  void dispatchOnObservation(const Simulable& veh, const mrpt::obs::CObservation::Ptr& obs);
449 
455 
456 #if defined(MVSIM_HAS_ZMQ) && defined(MVSIM_HAS_PROTOBUF)
457  mvsim::Client& commsClient() { return client_; }
458  const mvsim::Client& commsClient() const { return client_; }
459 #endif
460 
461  void free_opengl_resources();
462 
463  auto& physical_objects_mtx() { return worldPhysicalMtx_; }
464 
465  bool headless() const { return guiOptions_.headless; }
466  void headless(bool setHeadless) { guiOptions_.headless = setHeadless; }
467 
468  bool sensor_has_to_create_egl_context();
469 
470  const std::map<std::string, std::string>& user_defined_variables() const
471  {
472  return userDefinedVariables_;
473  }
474 
479  std::optional<mvsim::TJoyStickEvent> getJoystickState() const;
480 
481  bool evaluate_tag_if(const rapidxml::xml_node<char>& node) const;
482 
483  float collisionThreshold() const { return collisionThreshold_; }
484 
490  std::set<float> getElevationsAt(const mrpt::math::TPoint2D& worldXY) const;
491 
494  float getHighestElevationUnder(const mrpt::math::TPoint3Df& queryPt) const;
495 
496  void internal_simul_pre_step_terrain_elevation();
497 
502  std::optional<std::any> getPropertyAt(
503  const std::string& propertyName, const mrpt::math::TPoint3D& worldXYZ) const;
504 
505  private:
506  friend class VehicleBase;
507  friend class Block;
508 
509 #if defined(MVSIM_HAS_ZMQ) && defined(MVSIM_HAS_PROTOBUF)
510  mvsim::Client client_{"World"};
511 #endif
512 
513  std::vector<on_observation_callback_t> callbacksOnObservation_;
514 
515  // -------- World Params ----------
518  double gravity_ = 9.81;
519 
524  mutable double simulTimestep_ = 0;
525 
526  mutable bool joystickEnabled_ = false;
527  mutable std::optional<Joystick> joystick_;
528 
530  int b2dVelIters_ = 8, b2dPosIters_ = 3;
531 
533  float collisionThreshold_ = 0.03f;
534 
535  std::string serverAddress_ = "localhost";
536 
538  std::string save_to_rawlog_;
539 
540  double rawlog_odometry_rate_ = 10.0;
541 
544  std::string save_ground_truth_trajectory_;
545 
546  double ground_truth_rate_ = 50.0;
547 
548  double max_slope_to_collide_ = 0.30;
549  double min_slope_to_collide_ = -0.50;
550 
551  const TParameterDefinitions otherWorldParams_ = {
552  {"server_address", {"%s", &serverAddress_}},
553  {"gravity", {"%lf", &gravity_}},
554  {"simul_timestep", {"%lf", &simulTimestep_}},
555  {"b2d_vel_iters", {"%i", &b2dVelIters_}},
556  {"b2d_pos_iters", {"%i", &b2dPosIters_}},
557  {"collision_threshold", {"%f", &collisionThreshold_}},
558  {"joystick_enabled", {"%bool", &joystickEnabled_}},
559  {"save_to_rawlog", {"%s", &save_to_rawlog_}},
560  {"rawlog_odometry_rate", {"%lf", &rawlog_odometry_rate_}},
561  {"save_ground_truth_trajectory", {"%s", &save_ground_truth_trajectory_}},
562  {"ground_truth_rate", {"%lf", &ground_truth_rate_}},
563  {"max_slope_to_collide", {"%lf", &max_slope_to_collide_}},
564  {"min_slope_to_collide", {"%lf", &min_slope_to_collide_}},
565  };
566 
569  std::map<std::string, std::string> userDefinedVariables_;
570 
573  double simulTime_ = 0;
574  std::optional<double> simul_start_wallclock_time_;
575  std::mutex simul_time_mtx_;
576 
580  std::atomic<double> achievedRealtimeFactor_{1.0};
581  std::optional<double> lastRunSimulWallclock_;
582 
584  std::string basePath_{"."};
585 
588  mrpt::opengl::CSetOfObjects::Ptr glUserObjsPhysical_ = mrpt::opengl::CSetOfObjects::Create();
589  mrpt::opengl::CSetOfObjects::Ptr glUserObjsViz_ = mrpt::opengl::CSetOfObjects::Create();
590 
591  // ------- GUI options -----
592  struct TGUI_Options
593  {
594  unsigned int win_w = 800, win_h = 600;
595  bool start_maximized = true;
596  int refresh_fps = 20;
597  bool ortho = false;
598  bool show_forces = false;
599  bool show_sensor_points = true;
600  bool show_trajectories = false;
601  double force_scale = 0.01;
602  double camera_distance = 80.0;
603  double camera_azimuth_deg = 45.0;
604  double camera_elevation_deg = 40.0;
605  double fov_deg = 60.0;
606  float clip_plane_min = 0.05f;
607  float clip_plane_max = 10e3f;
608  mrpt::math::TPoint3D camera_point_to{0, 0, 0};
609  std::string follow_vehicle;
610  bool headless = false;
611 
612  const TParameterDefinitions params = {
613  {"win_w", {"%u", &win_w}},
614  {"win_h", {"%u", &win_h}},
615  {"ortho", {"%bool", &ortho}},
616  {"show_forces", {"%bool", &show_forces}},
617  {"show_sensor_points", {"%bool", &show_sensor_points}},
618  {"show_trajectories", {"%bool", &show_trajectories}},
619  {"force_scale", {"%lf", &force_scale}},
620  {"fov_deg", {"%lf", &fov_deg}},
621  {"follow_vehicle", {"%s", &follow_vehicle}},
622  {"start_maximized", {"%bool", &start_maximized}},
623  {"refresh_fps", {"%i", &refresh_fps}},
624  {"headless", {"%bool", &headless}},
625  {"clip_plane_min", {"%f", &clip_plane_min}},
626  {"clip_plane_max", {"%f", &clip_plane_max}},
627  {"cam_distance", {"%lf", &camera_distance}},
628  {"cam_azimuth", {"%lf", &camera_azimuth_deg}},
629  {"cam_elevation", {"%lf", &camera_elevation_deg}},
630  {"cam_point_to", {"%point3d", &camera_point_to}},
631  };
632 
633  TGUI_Options() = default;
634  void parse_from(const rapidxml::xml_node<char>& node, COutputLogger& logger);
635  };
636 
639  TGUI_Options guiOptions_;
640 
641  struct LightOptions
642  {
643  LightOptions() = default;
644 
645  void parse_from(const rapidxml::xml_node<char>& node, COutputLogger& logger);
646 
647  bool enable_shadows = true;
648  int shadow_map_size = 2048;
649 
650  double light_azimuth = mrpt::DEG2RAD(45.0);
651  double light_elevation = mrpt::DEG2RAD(70.0);
652 
653  float light_clip_plane_min = 0.1f;
654  float light_clip_plane_max = 900.0f;
655 
656  float shadow_bias = 1e-5;
657  float shadow_bias_cam2frag = 1e-5;
658  float shadow_bias_normal = 1e-4;
659 
660  mrpt::img::TColor light_color = {0xff, 0xff, 0xff, 0xff};
661  float light_ambient = 0.5f;
662 
663  float eye_distance_to_shadow_map_extension = 2.0f;
664  float minimum_shadow_map_extension_ratio = 0.005f;
665 
666  const TParameterDefinitions params = {
667  {"enable_shadows", {"%bool", &enable_shadows}},
668  {"shadow_map_size", {"%i", &shadow_map_size}},
669  {"light_azimuth_deg", {"%lf_deg", &light_azimuth}},
670  {"light_elevation_deg", {"%lf_deg", &light_elevation}},
671  {"light_clip_plane_min", {"%f", &light_clip_plane_min}},
672  {"light_clip_plane_max", {"%f", &light_clip_plane_max}},
673  {"light_color", {"%color", &light_color}},
674  {"shadow_bias", {"%f", &shadow_bias}},
675  {"shadow_bias_cam2frag", {"%f", &shadow_bias_cam2frag}},
676  {"shadow_bias_normal", {"%f", &shadow_bias_normal}},
677  {"light_ambient", {"%f", &light_ambient}},
678  {"eye_distance_to_shadow_map_extension", {"%f", &eye_distance_to_shadow_map_extension}},
679  {"minimum_shadow_map_extension_ratio", {"%f", &minimum_shadow_map_extension_ratio}},
680  };
681  };
682 
684  LightOptions lightOptions_;
685 
686  public:
687  // Options for simulating GNSS (GPS) sensors.
689  {
690  GeoreferenceOptions() = default;
691 
692  void parse_from(const rapidxml::xml_node<char>& node, COutputLogger& logger);
693 
695  mrpt::topography::TGeodeticCoords georefCoord;
696 
701 
703  bool world_is_utm = false;
704 
706  mrpt::topography::TUTMCoords utmRef;
707  int utm_zone = 0; // auto calculated
708  char utm_band = 'X'; // auto calculated
709 
710  const TParameterDefinitions params = {
711  {"latitude", {"%lf", &georefCoord.lat.decimal_value}},
712  {"longitude", {"%lf", &georefCoord.lon.decimal_value}},
713  {"height", {"%lf", &georefCoord.height}},
714  {"world_to_enu_rotation_deg", {"%lf_deg", &world_to_enu_rotation}},
715  {"world_is_utm", {"%bool", &world_is_utm}},
716  };
717  };
718 
719  const GeoreferenceOptions& georeferenceOptions() const { return georeferenceOptions_; }
720 
722  mrpt::math::TVector3D worldRenderOffset() const
723  {
724  return worldRenderOffset_ ? *worldRenderOffset_ : mrpt::math::TVector3D(0, 0, 0);
725  }
726  mrpt::math::TPose3D applyWorldRenderOffset(mrpt::math::TPose3D p) const
727  {
728  const auto t = worldRenderOffset();
729  p.x += t.x;
730  p.y += t.y;
731  p.z += t.z;
732  return p;
733  }
734  mrpt::poses::CPose3D applyWorldRenderOffset(mrpt::poses::CPose3D p) const
735  {
736  const auto t = worldRenderOffset();
737  p.x_incr(t.x);
738  p.y_incr(t.y);
739  p.z_incr(t.z);
740  return p;
741  }
743  void worldRenderOffsetPropose(const mrpt::math::TVector3D& v)
744  {
745  if (!worldRenderOffset_)
746  {
747  worldRenderOffset_ = v;
748  }
749  }
750 
751  private:
753  GeoreferenceOptions georeferenceOptions_;
754 
755  // -------- World contents ----------
757  std::recursive_mutex world_cs_;
758 
760  std::unique_ptr<b2World> box2d_world_;
761 
763  b2Body* b2_ground_body_ = nullptr;
764 
765  VehicleList vehicles_;
766  WorldElementList worldElements_;
767  BlockList blocks_;
768  ActorList actors_;
769 
771  std::vector<WorldJoint> joints_;
772 
773  bool initialized_ = false;
774 
775  // List of all objects above (vehicles, world_elements, blocks), but as
776  // shared_ptr to their Simulable interfaces, so we can easily iterate on
777  // this list only for common tasks:
778  SimulableList simulableObjects_;
779  std::mutex simulableObjectsMtx_;
780 
782  void internal_one_timestep(double dt);
783 
784  std::mutex simulationStepRunningMtx_;
785 
786  // A 2D-hash table of objects
787  struct lut_2d_coordinates_t
788  {
789  int32_t x, y;
790 
791  bool operator==(const lut_2d_coordinates_t& o) const noexcept
792  {
793  return (x == o.x && y == o.y);
794  }
795  };
796 
797  static lut_2d_coordinates_t xy_to_lut_coords(const mrpt::math::TPoint2Df& p);
798 
799  struct LutIndexHash
800  {
801  std::size_t operator()(const lut_2d_coordinates_t& p) const noexcept
802  {
803  // These are the implicit assumptions of the reinterpret cast below:
804  static_assert(sizeof(int32_t) == sizeof(uint32_t));
805  static_assert(offsetof(lut_2d_coordinates_t, x) == 0 * sizeof(uint32_t));
806  static_assert(offsetof(lut_2d_coordinates_t, y) == 1 * sizeof(uint32_t));
807 
808  const uint32_t* vec = reinterpret_cast<const uint32_t*>(&p);
809  return ((1 << 20) - 1) & (vec[0] * 73856093 ^ vec[1] * 19349663);
810  }
812  bool operator()(
813  const lut_2d_coordinates_t& k1, const lut_2d_coordinates_t& k2) const noexcept
814  {
815  if (k1.x != k2.x)
816  {
817  return k1.x < k2.x;
818  }
819  return k1.y < k2.y;
820  }
821  };
822 
823  using LUTCache =
824  std::unordered_map<lut_2d_coordinates_t, std::vector<Simulable::Ptr>, LutIndexHash>;
825 
827  const LUTCache& getLUTCacheOfObjects() const;
828 
829  mutable LUTCache lut2d_objects_;
830  mutable bool lut2d_objects_is_up_to_date_ = false;
831 
832  void internal_update_lut_cache() const;
833 
835  struct GUI
836  {
837  GUI(World& parent) : parent_(parent) {}
838 
839  mrpt::gui::CDisplayWindowGUI::Ptr gui_win;
840  nanogui::Label* lbCpuUsage = nullptr;
841  std::vector<nanogui::Label*> lbStatuses;
842  nanogui::Button* btnReplaceObject = nullptr;
843 
845  {
846  nanogui::CheckBox* cb = nullptr;
847  Simulable::Ptr simulable;
848  VisualObject* visual = nullptr;
849  };
850 
851  // Buttons that must be {dis,en}abled when there is a selected object:
852  std::vector<nanogui::Widget*> btns_selectedOps;
853  std::vector<InfoPerObject> gui_cbObjects;
854  InfoPerObject gui_selectedObject;
855 
856  mrpt::math::TPoint3D clickedPt{0, 0, 0};
857 
858  void prepare_control_window();
859  void prepare_status_window();
860  void prepare_editor_window();
861 
862  void handle_mouse_operations();
863 
864  private:
865  World& parent_;
866  };
867  GUI gui_{*this};
868 
872  mrpt::opengl::COpenGLScene::Ptr worldVisual_ = mrpt::opengl::COpenGLScene::Create();
873 
879  mrpt::opengl::COpenGLScene worldPhysical_;
880  std::recursive_mutex worldPhysicalMtx_;
881 
887  std::optional<mrpt::math::TVector3D> worldRenderOffset_;
888 
890  std::map<std::string, mrpt::math::TPose3D> copy_of_objects_dynstate_pose_;
891  std::map<std::string, mrpt::math::TTwist2D> copy_of_objects_dynstate_twist_;
892  std::set<std::string> copy_of_objects_had_collision_;
893  std::recursive_mutex copy_of_objects_dynstate_mtx_;
894 
895  std::set<std::string> reset_collision_flags_;
896  std::mutex reset_collision_flags_mtx_;
897 
898  void internal_gui_on_observation(const Simulable& veh, const mrpt::obs::CObservation::Ptr& obs);
899  void internal_gui_on_observation_3Dscan(
900  const Simulable& veh, const std::shared_ptr<mrpt::obs::CObservation3DRangeScan>& obs);
901  void internal_gui_on_observation_image(
902  const Simulable& veh, const std::shared_ptr<mrpt::obs::CObservationImage>& obs);
903 
904  mrpt::math::TPoint2D internal_gui_on_image(
905  const std::string& label, const mrpt::img::CImage& im, int winPosX, bool startVisible);
906 
910  static bool internal_gui_sensor_preview_visible(
911  const Simulable& veh, const std::string& sensorLabel);
912 
913  std::map<std::string, nanogui::Window*> guiObsViz_;
914 
917  void setLightDirectionFromAzimuthElevation(const float azimuth, const float elevation);
918  // end GUI stuff
920 
921  mrpt::system::CTimeLogger timlogger_{true /*enabled*/, "mvsim::World"};
922  mrpt::system::CTicTac timer_iteration_;
923 
924  void process_load_walls(const rapidxml::xml_node<char>& node);
925  void insertBlock(const Block::Ptr& block);
926 
927  struct XmlParserContext
928  {
929  XmlParserContext(const rapidxml::xml_node<char>* n, const std::string& basePath)
930  : node(n), currentBasePath(basePath)
931  {
932  }
933 
934  const rapidxml::xml_node<char>* node = nullptr;
935  const std::string currentBasePath;
936  };
937 
939  void internal_recursive_parse_XML(const XmlParserContext& ctx);
940 
941  using xml_tag_parser_function_t = std::function<void(const XmlParserContext&)>;
942 
943  std::map<std::string, xml_tag_parser_function_t> xmlParsers_;
944 
945  void register_standard_xml_tag_parsers();
946 
947  void register_tag_parser(const std::string& xmlTagName, const xml_tag_parser_function_t& f)
948  {
949  xmlParsers_.emplace(xmlTagName, f);
950  }
951  void register_tag_parser(
952  const std::string& xmlTagName, void (World::*f)(const XmlParserContext& ctx))
953  {
954  xmlParsers_.emplace(
955  xmlTagName, [this, f](const XmlParserContext& ctx) { (this->*f)(ctx); });
956  }
957 
958  // ======== XML parser tags ========
959  void parse_tag_element(const XmlParserContext& ctx);
960  void parse_tag_vehicle(const XmlParserContext& ctx);
962  void parse_tag_vehicle_class(const XmlParserContext& ctx);
963  void parse_tag_sensor(const XmlParserContext& ctx);
964  void parse_tag_block(const XmlParserContext& ctx);
965  void parse_tag_block_class(const XmlParserContext& ctx);
966  void parse_tag_gui(const XmlParserContext& ctx);
967  void parse_tag_lights(const XmlParserContext& ctx);
968  void parse_tag_georeference(const XmlParserContext& ctx);
969  void parse_tag_walls(const XmlParserContext& ctx);
970  void parse_tag_include(const XmlParserContext& ctx);
971  void parse_tag_variable(const XmlParserContext& ctx);
972  void parse_tag_for(const XmlParserContext& ctx);
973  void parse_tag_if(const XmlParserContext& ctx);
974  void parse_tag_marker(const XmlParserContext& ctx);
975  void parse_tag_joint(const XmlParserContext& ctx);
976  void parse_tag_actor(const XmlParserContext& ctx);
977  void parse_tag_actor_class(const XmlParserContext& ctx);
978  void parse_tag_remote_resources(const XmlParserContext& ctx);
979 
980  // ======== end of XML parser tags ========
981 
982  mutable RemoteResourcesManager remoteResources_;
983 
984  void internalOnObservation(const Simulable& veh, const mrpt::obs::CObservation::Ptr& obs);
985 
986  void internalPostSimulStepForRawlog();
987  void internalPostSimulStepForTrajectory();
988 
989  std::mutex rawlog_io_mtx_;
990 #if MRPT_VERSION >= 0x020f07
991  std::map<std::string, std::shared_ptr<mrpt::io::CCompressedOutputStream>> rawlog_io_per_veh_;
992 #else
993  std::map<std::string, std::shared_ptr<mrpt::io::CFileGZOutputStream>> rawlog_io_per_veh_;
994 #endif
995  std::optional<double> rawlog_last_odom_time_;
996 
997  std::mutex gt_io_mtx_;
998  std::map<std::string, std::fstream> gt_io_per_veh_;
999  std::optional<double> gt_last_time_;
1000 
1001  // ============ Elevation Field Collision artifacts ==============
1002  struct TFixturePtr
1003  {
1004  TFixturePtr() = default;
1005  b2Fixture* fixture = nullptr;
1006  };
1007  struct TInfoPerCollidableobj
1008  {
1009  TInfoPerCollidableobj() = default;
1010 
1011  mrpt::poses::CPose3D pose;
1012  b2Body* collide_body = nullptr;
1013  double representativeHeight = 0.01;
1014  double maxWorkableStepHeight = 0.10;
1015  double speed = .0;
1016  mrpt::math::TPolygon2D contour;
1017  const std::vector<float>* wheel_heights = nullptr;
1018  std::vector<float> contour_heights;
1019  std::vector<TFixturePtr> collide_fixtures;
1020  };
1021  std::vector<std::optional<TInfoPerCollidableobj>> obstacles_for_each_obj_;
1022  // ============ end of elevation field collision =================
1023 
1024  // Services:
1025  void internal_advertiseServices(); // called from connectToServer()
1026 
1027 #if MVSIM_HAS_ZMQ && MVSIM_HAS_PROTOBUF
1028 
1029  mvsim_msgs::SrvSetPoseAnswer srv_set_pose(const mvsim_msgs::SrvSetPose& req);
1030  mvsim_msgs::SrvGetPoseAnswer srv_get_pose(const mvsim_msgs::SrvGetPose& req);
1031  mvsim_msgs::SrvSetControllerTwistAnswer srv_set_controller_twist(
1032  const mvsim_msgs::SrvSetControllerTwist& req);
1033  mvsim_msgs::SrvShutdownAnswer srv_shutdown(const mvsim_msgs::SrvShutdown& req);
1034 #endif
1035 };
1036 } // namespace mvsim
Definition: Block.h:51
Definition: Client.h:49
Definition: Simulable.h:40
Definition: VehicleBase.h:44
Definition: VisualObject.h:36
Definition: WorldElementBase.h:28
Definition: World.h:133
bool is_GUI_open() const
std::multimap< std::string, HumanActor::Ptr > ActorList
Definition: World.h:376
void internalGraphicsLoopTasksForSimulation()
std::multimap< std::string, Simulable::Ptr > SimulableList
Definition: World.h:373
float getHighestElevationUnder(const mrpt::math::TPoint3Df &queryPt) const
void force_set_simul_time(double newSimulatedTime)
Normally should not be called by users, for internal use only.
Definition: World.h:182
SimulableList & getListOfSimulableObjects()
Always lock/unlock getListOfSimulableObjectsMtx() before using this:
Definition: World.h:397
void connectToServer()
mrpt::Clock::time_point get_simul_timestamp() const
Definition: World.h:193
void runVisitorOnVehicles(const vehicle_visitor_t &v)
mrpt::opengl::CSetOfObjects::Ptr guiUserObjectsPhysical_
Definition: World.h:277
mrpt::math::TVector3D worldRenderOffset() const
(See docs for worldRenderOffset_)
Definition: World.h:722
double get_simul_time() const
Definition: World.h:175
void runVisitorOnBlocks(const block_visitor_t &v)
std::optional< std::any > getPropertyAt(const std::string &propertyName, const mrpt::math::TPoint3D &worldXYZ) const
void worldRenderOffsetPropose(const mrpt::math::TVector3D &v)
(See docs for worldRenderOffset_)
Definition: World.h:743
void update_GUI(TUpdateGUIParams *params=nullptr)
void dispatchOnObservation(const Simulable &veh, const mrpt::obs::CObservation::Ptr &obs)
void load_from_XML_file(const std::string &xmlFileNamePath)
double get_gravity() const
Definition: World.h:224
std::multimap< std::string, VehicleBase::Ptr > VehicleList
Definition: World.h:363
void close_GUI()
Forces closing the GUI window, if any.
double get_simul_timestep() const
Simulation fixed-time interval for numerical integration.
std::string xmlPathToActualPath(const std::string &modelURI) const
double get_realtime_factor_achieved() const
Definition: World.h:212
std::set< float > getElevationsAt(const mrpt::math::TPoint2D &worldXY) const
std::string local_to_abs_path(const std::string &in_path) const
void clear_all()
bool has_simul_timestamp() const
Definition: World.h:203
std::multimap< std::string, Block::Ptr > BlockList
Definition: World.h:369
std::optional< mvsim::TJoyStickEvent > getJoystickState() const
World()
Default ctor: inits an empty world.
void set_gravity(double accel)
Definition: World.h:228
void set_simul_timestep(double timestep)
Definition: World.h:220
void load_from_XML(const std::string &xml_text, const std::string &fileNameForPath=std::string("."))
std::list< WorldElementBase::Ptr > WorldElementList
Definition: World.h:366
void run_simulation(double dt)
void runVisitorOnWorldElements(const world_element_visitor_t &v)
Definition: World.h:91
std::string bodyA_name
Names of the two connected Simulable objects (vehicles, blocks, etc.)
Definition: World.h:101
mrpt::math::TPoint2D anchorA
Local anchor points on each body (in body-local coordinates)
Definition: World.h:105
bool enableLimit
Revolute joint parameters:
Definition: World.h:115
float maxLength
Distance joint parameters (only used when type == Distance):
Definition: World.h:109
b2Joint * b2joint
Runtime Box2D joint (owned by the b2World, do NOT delete manually)
Definition: World.h:120
float lowerAngle_deg
Parsed in degrees, converted to radians.
Definition: World.h:116
Type
Definition: World.h:93
@ Distance
b2DistanceJoint (rope-like max length constraint)
@ Revolute
b2RevoluteJoint (pin/hinge)
Definition: World.h:241
int keycode
0=no Key. Otherwise, ASCII code.
Definition: World.h:242
Definition: World.h:845
Definition: World.h:689
mrpt::topography::TGeodeticCoords georefCoord
Latitude/longitude/height of the world (0,0,0) frame.
Definition: World.h:695
double world_to_enu_rotation
Definition: World.h:700
mrpt::topography::TUTMCoords utmRef
Definition: World.h:706
bool world_is_utm
Definition: World.h:703
Definition: World.h:252
std::string msg_lines
Messages to show.
Definition: World.h:254
GUIKeyEvent keyevent
Keystrokes in the window are returned here.
Definition: World.h:253