VisualObject.cpp
Go to the documentation of this file.
1 /*+-------------------------------------------------------------------------+
2  | MultiVehicle simulator (libmvsim) |
3  | |
4  | Copyright (C) 2014-2023 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 #include <mrpt/opengl/COpenGLScene.h>
11 #include <mrpt/opengl/CPolyhedron.h>
12 #include <mrpt/opengl/CSetOfObjects.h>
13 #include <mrpt/version.h>
14 #include <mvsim/Block.h>
16 #include <mvsim/Simulable.h>
17 #include <mvsim/VisualObject.h>
18 #include <mvsim/World.h>
19 
20 #include <atomic>
21 #include <rapidxml.hpp>
22 
23 #include "JointXMLnode.h"
24 #include "ModelsCache.h"
25 #include "xml_utils.h"
26 
27 using namespace mvsim;
28 
29 static std::atomic_int32_t g_uniqueCustomVisualId = 0;
30 double VisualObject::GeometryEpsilon = 1e-3;
31 
33  World* parent, bool insertCustomVizIntoViz,
34  bool insertCustomVizIntoPhysical)
35  : world_(parent),
36  insertCustomVizIntoViz_(insertCustomVizIntoViz),
37  insertCustomVizIntoPhysical_(insertCustomVizIntoPhysical)
38 {
39  glCollision_ = mrpt::opengl::CSetOfObjects::Create();
40  glCollision_->setName("bbox");
41 }
42 
43 VisualObject::~VisualObject() = default;
44 
46  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& viz,
47  const mrpt::optional_ref<mrpt::opengl::COpenGLScene>& physical)
48 {
49  using namespace std::string_literals;
50 
51  const auto* meSim = dynamic_cast<Simulable*>(this);
52  ASSERT_(meSim);
53 
54  // If "viz" does not have a value, it's because we are already inside a
55  // setPose() change event, so my caller already holds the mutex and we don't
56  // need/can't acquire it again:
57  const auto objectPose =
58  viz.has_value() ? meSim->getPose() : meSim->getPoseNoLock();
59 
60  if (glCustomVisual_ && viz.has_value() && physical.has_value())
61  {
62  // Assign a unique ID on first call:
63  if (glCustomVisualId_ < 0)
64  {
65  // Assign a unique name, so we can localize the object in the scene
66  // if needed.
68  const auto name = "_autoViz"s + std::to_string(glCustomVisualId_);
69  glCustomVisual_->setName(name);
70 
71  // Add to the 3D scene:
72  if (insertCustomVizIntoViz_) viz->get().insert(glCustomVisual_);
73 
75  physical->get().insert(glCustomVisual_);
76  }
77 
78  // Update pose:
79  glCustomVisual_->setPose(objectPose);
80  }
81 
82  if (glCollision_ && viz.has_value())
83  {
84  if (glCollision_->empty() && collisionShape_)
85  {
86  const auto& cs = collisionShape_.value();
87 
88  const double height = cs.zMax() - cs.zMin();
89  ASSERT_(height > 0);
90 
91  const auto c = cs.getContour();
92 
93  // Adapt mrpt::math geometry epsilon to the scale of the smallest
94  // edge in this polygon, so we don't get false positives about
95  // wrong aligned points in a 3D face just becuase it's too small:
96  const auto savedMrptGeomEps = mrpt::math::getEpsilon();
97 
98  double smallestEdge = std::abs(height);
99  for (size_t i = 0; i < c.size(); i++)
100  {
101  size_t im1 = i == 0 ? c.size() - 1 : i - 1;
102  const auto Ap = c[i] - c[im1];
103  mrpt::keep_min(smallestEdge, Ap.norm());
104  }
105  mrpt::math::setEpsilon(1e-5 * smallestEdge);
106 
107  mrpt::opengl::CPolyhedron::Ptr glCS;
108 
109  try
110  {
111  glCS = mrpt::opengl::CPolyhedron::CreateCustomPrism(c, height);
112  }
113  catch (const std::exception& e)
114  {
115 #if 0
116  std::cerr << "[mvsim::VisualObject] **WARNING**: Ignoring the "
117  "following error while building the visualization "
118  "of the collision shape for object named '"
119  << meSim->getName()
120  << "' placed by pose=" << meSim->getPose()
121  << "). Falling back to rectangular collision shape "
122  "from bounding box:\n"
123  << e.what() << std::endl;
124 #endif
125 
126  mrpt::math::TPoint2D bbMax, bbMin;
127  cs.getContour().getBoundingBox(bbMin, bbMax);
128  mrpt::math::TPolygon2D p;
129  p.emplace_back(bbMin.x, bbMin.y);
130  p.emplace_back(bbMin.x, bbMax.y);
131  p.emplace_back(bbMax.x, bbMax.y);
132  p.emplace_back(bbMax.x, bbMin.y);
133  glCS = mrpt::opengl::CPolyhedron::CreateCustomPrism(p, height);
134  }
135  glCS->setWireframe(true);
136 
137  mrpt::math::setEpsilon(savedMrptGeomEps);
138  // Default epsilon is restored now
139 
140  glCS->setLocation(0, 0, cs.zMin());
141 
142  glCollision_->insert(glCS);
143  glCollision_->setVisibility(false);
144  viz->get().insert(glCollision_);
145  }
146  glCollision_->setPose(objectPose);
147  }
148 
149  const bool childrenOnly = !!glCustomVisual_;
150 
151  internalGuiUpdate(viz, physical, childrenOnly);
152 }
153 
155 
157 {
158  MRPT_TRY_START
159 
160  bool any = false;
161  for (auto n = rootNode.first_node("visual"); n;
162  n = n->next_sibling("visual"))
163  {
164  bool hasViz = implParseVisual(*n);
165  any = any || hasViz;
166  }
167  return any;
168 
169  MRPT_TRY_END
170 }
171 
173 {
174  MRPT_TRY_START
175 
176  bool any = false;
177  for (const auto& n : rootNode.getListOfNodes())
178  {
179  bool hasViz = parseVisual(*n);
180  any = any || hasViz;
181  }
182 
183  return any;
184  MRPT_TRY_END
185 }
186 
188 {
189  MRPT_TRY_START
190 
191  {
192  bool visualEnabled = true;
193  TParameterDefinitions auxPar;
194  auxPar["enabled"] = TParamEntry("%bool", &visualEnabled);
195  parse_xmlnode_attribs(visNode, auxPar);
196  if (!visualEnabled)
197  {
198  // "enabled=false" -> Ignore the rest of the contents
199  return false;
200  }
201  }
202 
203  std::string modelURI;
204  double modelScale = 1.0;
205  mrpt::math::TPose3D modelPose;
206  bool initialShowBoundingBox = false;
207  std::string objectName = "group";
208 
210 
211  TParameterDefinitions params;
212  params["model_uri"] = TParamEntry("%s", &modelURI);
213  params["model_scale"] = TParamEntry("%lf", &modelScale);
214  params["model_offset_x"] = TParamEntry("%lf", &modelPose.x);
215  params["model_offset_y"] = TParamEntry("%lf", &modelPose.y);
216  params["model_offset_z"] = TParamEntry("%lf", &modelPose.z);
217  params["model_yaw"] = TParamEntry("%lf_deg", &modelPose.yaw);
218  params["model_pitch"] = TParamEntry("%lf_deg", &modelPose.pitch);
219  params["model_roll"] = TParamEntry("%lf_deg", &modelPose.roll);
220  params["show_bounding_box"] = TParamEntry("%bool", &initialShowBoundingBox);
221  params["model_cull_faces"] = TParamEntry("%s", &opts.modelCull);
222  params["model_color"] = TParamEntry("%color", &opts.modelColor);
223  params["name"] = TParamEntry("%s", &objectName);
224 
225  // Parse XML params:
226  parse_xmlnode_children_as_param(visNode, params);
227 
228  if (modelURI.empty()) return false;
229 
230  const std::string localFileName = world_->xmlPathToActualPath(modelURI);
231 
232  auto& gModelsCache = ModelsCache::Instance();
233 
234  auto glModel = gModelsCache.get(localFileName, opts);
235 
236  // Add the 3D model as custom viz:
238  glModel, mrpt::poses::CPose3D(modelPose), modelScale, objectName,
239  modelURI);
240 
241  return true; // yes, we have a custom viz model
242 
243  MRPT_TRY_END
244 }
245 
247 {
248  if (!glCollision_) return;
249  glCollision_->setVisibility(show);
250 }
251 
252 void VisualObject::customVisualVisible(const bool visible)
253 {
254  if (!glCustomVisual_) return;
255 
256  glCustomVisual_->setVisibility(visible);
257 }
258 
260 {
261  return glCustomVisual_ && glCustomVisual_->isVisible();
262 }
263 
265  const mrpt::opengl::CRenderizable::Ptr& glModel,
266  const mrpt::poses::CPose3D& modelPose, const float modelScale,
267  const std::string& modelName, const std::optional<std::string>& modelURI,
268  const bool initialShowBoundingBox)
269 {
270  ASSERT_(glModel);
271 
272  auto& chc = CollisionShapeCache::Instance();
273 
274  float zMin = -std::numeric_limits<float>::max();
275  float zMax = std::numeric_limits<float>::max();
276 
277  if (const Block* block = dynamic_cast<const Block*>(this);
278  block && !block->default_block_z_min_max())
279  {
280  zMin = block->block_z_min() - GeometryEpsilon;
281  zMax = block->block_z_max() + GeometryEpsilon;
282  }
283 #if 0
284  std::cout << "MODEL: " << (modelURI ? *modelURI : "none")
285  << " glModel: " << glModel->GetRuntimeClass()->className
286  << " zmin=" << zMin << " zMax:" << zMax << "\n";
287 #endif
288 
289  // Calculate its convex hull:
290  const auto shape =
291  chc.get(*glModel, zMin, zMax, modelPose, modelScale, modelURI);
292 
293  auto glGroup = mrpt::opengl::CSetOfObjects::Create();
294 
295  // Note: we cannot apply pose/scale to the original glModel since
296  // it may be shared (many instances of the same object):
297  glGroup->insert(glModel);
298  glGroup->setScale(modelScale);
299  glGroup->setPose(modelPose);
300 
301  glGroup->setName(modelName);
302 
303  if (!glCustomVisual_)
304  {
305  glCustomVisual_ = mrpt::opengl::CSetOfObjects::Create();
306  glCustomVisual_->setName("glCustomVisual");
307  }
308  glCustomVisual_->insert(glGroup);
309 
310  if (glCollision_) glCollision_->setVisibility(initialShowBoundingBox);
311 
312  // Auto bounds from visual model bounding-box:
313  if (!collisionShape_)
314  {
315  // Copy:
316  collisionShape_ = shape;
317  }
318  else
319  {
320  // ... or update collision volume:
321  collisionShape_->mergeWith(shape);
322  }
323 }
This file contains rapidxml parser and DOM implementation.
static double GeometryEpsilon
Definition: VisualObject.h:64
virtual void guiUpdate(const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &viz, const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &physical)
static ModelsCache & Instance()
Definition: ModelsCache.cpp:21
std::map< std::string, TParamEntry > TParameterDefinitions
void parse_xmlnode_children_as_param(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="", mrpt::system::COutputLogger *logger=nullptr)
Definition: xml_utils.cpp:224
bool default_block_z_min_max() const
Definition: Block.cpp:541
XmlRpcServer s
void parse_xmlnode_attribs(const rapidxml::xml_node< char > &xml_node, const TParameterDefinitions &params, const std::map< std::string, std::string > &variableNamesValues={}, const char *functionNameContext="")
Definition: xml_utils.cpp:184
const bool insertCustomVizIntoPhysical_
Definition: VisualObject.h:80
xml_node< Ch > * first_node(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:936
mrpt::img::TColor modelColor
Definition: ModelsCache.h:23
virtual void internalGuiUpdate(const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &viz, const mrpt::optional_ref< mrpt::opengl::COpenGLScene > &physical, bool childrenOnly=false)=0
std::shared_ptr< mrpt::opengl::CSetOfObjects > glCustomVisual_
Definition: VisualObject.h:75
virtual ~VisualObject()
static void FreeOpenGLResources()
std::optional< Shape2p5 > collisionShape_
Definition: VisualObject.h:97
static CollisionShapeCache & Instance()
void showCollisionShape(bool show)
static std::atomic_int32_t g_uniqueCustomVisualId
const bool insertCustomVizIntoViz_
Definition: VisualObject.h:79
TListNodes & getListOfNodes()
Definition: JointXMLnode.h:41
VisualObject(World *parent, bool insertCustomVizIntoViz=true, bool insertCustomVizIntoPhysical=true)
bool customVisualVisible() const
int32_t glCustomVisualId_
Definition: VisualObject.h:77
bool parseVisual(const rapidxml::xml_node< char > &rootNode)
Returns true if there is at least one <visual>...</visual> entry.
void addCustomVisualization(const mrpt::opengl::CRenderizable::Ptr &glModel, const mrpt::poses::CPose3D &modelPose={}, const float modelScale=1.0f, const std::string &modelName="group", const std::optional< std::string > &modelURI=std::nullopt, const bool initialShowBoundingBox=false)
bool implParseVisual(const rapidxml::xml_node< char > &visual_node)
Called by parseVisual once per "visual" block.
std::shared_ptr< mrpt::opengl::CSetOfObjects > glCollision_
Definition: VisualObject.h:76
xml_node< Ch > * next_sibling(const Ch *name=0, std::size_t name_size=0, bool case_sensitive=true) const
Definition: rapidxml.hpp:1004
std::string xmlPathToActualPath(const std::string &modelURI) const
Definition: World.cpp:244


mvsim
Author(s):
autogenerated on Tue Jul 4 2023 03:08:21