CollisionShapeCache.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 <box2d/b2_settings.h> // b2_maxPolygonVertices
11 #include <mrpt/opengl/CAssimpModel.h>
12 #include <mrpt/opengl/CBox.h>
13 #include <mrpt/opengl/CCylinder.h>
14 #include <mrpt/opengl/CSphere.h>
15 #include <mrpt/version.h>
17 
18 using namespace mvsim;
19 
21 {
22  static CollisionShapeCache o;
23  return o;
24 }
25 
27  mrpt::opengl::CRenderizable& obj, float zMin, float zMax,
28  const mrpt::poses::CPose3D& modelPose, const float modelScale,
29  const std::optional<std::string>& modelFile)
30 {
31  // already cached?
32  if (modelFile)
33  {
34  if (auto it = cache.find(modelFile.value()); it != cache.end())
35  return it->second.shape;
36  }
37 
38  // No, it's a new model path, create its placeholder:
39  Shape2p5 retVal;
40  Shape2p5& ret = modelFile ? cache[*modelFile].shape : retVal;
41 
42  // Now, decide whether it's a simple geometry, or an arbitrary model:
43  const auto simpleGeom =
44  processSimpleGeometries(obj, zMin, zMax, modelPose, modelScale);
45 
46  if (simpleGeom)
47  {
48  ret = simpleGeom.value();
49  }
50  else
51  {
52  ret = processGenericGeometry(obj, zMin, zMax, modelPose, modelScale);
53  }
54 
55  const auto vol = ret.volume();
56 
57 #if 0
58  std::cout << "shape2.5 for ["
59  << (modelFile.has_value() ? *modelFile : "none")
60  << "] glClass=" << obj.GetRuntimeClass()->className
61  << " shape=" << ret.getContour().size() << " pts, "
62  << " volume=" << vol << " zMin=" << zMin << " zMax=" << zMax
63  << " was simpleGeom=" << (simpleGeom ? "yes" : "no") << "\n";
64 #endif
65 
66  if (vol < 1e-8)
67  {
68  THROW_EXCEPTION_FMT(
69  "Error: Collision volume for visual model ('%s') has almost null "
70  "volume (=%g m³). A possible cause, if this is a <block>, is not "
71  "enough vertices within the given range [zmin,zmax]",
72  modelFile.has_value() ? modelFile->c_str() : "none", vol);
73  }
74 
75  return ret;
76 }
77 
79  const mrpt::opengl::CRenderizable& obj, float zMin, float zMax,
80  const mrpt::poses::CPose3D& modelPose, const float modelScale)
81 {
82 #if MRPT_VERSION >= 0x260
83  using namespace mrpt::literals; // _deg
84 #else
85  using namespace mrpt; // _deg
86 #endif
87 
88  if (auto oCyl = dynamic_cast<const mrpt::opengl::CCylinder*>(&obj); oCyl)
89  {
90  // ===============================
91  // Cylinder
92  // ===============================
93  // If the cylinder is not upright, skip and go for the generic algorithm
94  if (std::abs(modelPose.pitch()) > 0.02_deg ||
95  std::abs(modelPose.roll()) > 0.02_deg)
96  return {};
97 
98  const size_t actualEdgeCount = oCyl->getSlicesCount();
99  double actualRadius =
100  std::max<double>(oCyl->getTopRadius(), oCyl->getBottomRadius());
101 
102  return processCylinderLike(
103  actualEdgeCount, actualRadius, zMin, zMax, modelPose, modelScale);
104  }
105  else if (auto oSph = dynamic_cast<const mrpt::opengl::CSphere*>(&obj); oSph)
106  {
107  // ===============================
108  // Sphere
109  // ===============================
110 #if MRPT_VERSION >= 0x271
111  const size_t actualEdgeCount = oSph->getNumberOfSegments();
112 #else
113  // workaround mrpt <2.7.1
114  const size_t actualEdgeCount =
115  const_cast<mrpt::opengl::CSphere*>(oSph)->getNumberOfSegments();
116 #endif
117 
118  double actualRadius = oSph->getRadius();
119 
120  return processCylinderLike(
121  actualEdgeCount, actualRadius, zMin, zMax, modelPose, modelScale);
122  }
123  else if (auto oBox = dynamic_cast<const mrpt::opengl::CBox*>(&obj); oBox)
124  {
125  // ===============================
126  // Box
127  // ===============================
128  // If the object is not upright, skip and go for the generic algorithm
129  if (std::abs(modelPose.pitch()) > 0.02_deg ||
130  std::abs(modelPose.roll()) > 0.02_deg)
131  return {};
132 
133  mrpt::math::TPoint3D p1, p2;
134  oBox->getBoxCorners(p1, p2);
135  p1 *= modelScale;
136  p2 *= modelScale;
137 
138  const mrpt::math::TPoint3D corners[4] = {
139  modelPose.composePoint({p1.x, p1.y, 0}),
140  modelPose.composePoint({p1.x, p2.y, 0}),
141  modelPose.composePoint({p2.x, p2.y, 0}),
142  modelPose.composePoint({p2.x, p1.y, 0})};
143 
144  mrpt::math::TPolygon2D contour;
145  for (int i = 0; i < 4; i++)
146  contour.emplace_back(corners[i].x, corners[i].y);
147 
148  Shape2p5 s;
149  s.setShapeManual(contour, zMin, zMax);
150  return {s};
151  }
152  else
153  {
154  // unknown:
155  return {};
156  }
157 }
158 
160  mrpt::opengl::CRenderizable& obj, float zMin, float zMax,
161  const mrpt::poses::CPose3D& modelPose, const float modelScale)
162 {
163  Shape2p5 ret;
164 
165  // Make sure the points and vertices buffers are up to date, so we can
166  // access them:
167  auto* oAssimp = dynamic_cast<mrpt::opengl::CAssimpModel*>(&obj);
168  if (oAssimp)
169  {
170  oAssimp->onUpdateBuffers_all();
171  }
172  auto* oRSWF =
173  dynamic_cast<mrpt::opengl::CRenderizableShaderWireFrame*>(&obj);
174  if (oRSWF)
175  {
176  oRSWF->onUpdateBuffers_Wireframe();
177  }
178  auto* oRST =
179  dynamic_cast<mrpt::opengl::CRenderizableShaderTriangles*>(&obj);
180  if (oRST)
181  {
182  oRST->onUpdateBuffers_Triangles();
183  }
184  auto* oRSTT =
185  dynamic_cast<mrpt::opengl::CRenderizableShaderTexturedTriangles*>(&obj);
186  if (oRSTT)
187  {
188  oRSTT->onUpdateBuffers_TexturedTriangles();
189  }
190  auto* oRP = dynamic_cast<mrpt::opengl::CRenderizableShaderPoints*>(&obj);
191  if (oRP)
192  {
193  oRP->onUpdateBuffers_Points();
194  }
195 
196  // Slice bbox in z up to a given relevant height:
197  size_t numTotalPts = 0, numPassedPts = 0;
198 
199  auto rawBB = obj.getBoundingBox();
200  // transform and scale BBox too:
201  rawBB.max *= modelScale;
202  rawBB.min *= modelScale;
203  const auto coarseBB = rawBB.compose(modelPose);
204  ret.buildInit(
205  mrpt::math::TPoint2Df(coarseBB.min.x, coarseBB.min.y),
206  mrpt::math::TPoint2Df(coarseBB.max.x, coarseBB.max.y));
207 
208  auto lambdaUpdatePt = [&](const mrpt::math::TPoint3Df& orgPt) {
209  numTotalPts++;
210  auto pt = modelPose.composePoint(orgPt * modelScale);
211  if (pt.z < zMin || pt.z > zMax) return; // skip
212  ret.buildAddPoint(pt);
213  numPassedPts++;
214  };
215  auto lambdaUpdateTri = [&](const mrpt::opengl::TTriangle& tri) {
216  numTotalPts += 3;
217  // transform the whole triangle, then compare with [z,z] limits:
218  mrpt::opengl::TTriangle t = tri;
219  for (int i = 0; i < 3; i++)
220  t.vertex(i) = modelPose.composePoint(t.vertex(i) * modelScale);
221 
222  // does any of the point lie within the valid Z range, or is the
223  // triangle going all the way down to top?
224  bool outDown = false, outUp = false, anyIn = false;
225  for (int i = 0; i < 3; i++)
226  {
227  const auto& p = t.vertex(i);
228  if (p.z >= zMin && p.z <= zMax)
229  {
230  anyIn = true;
231  break;
232  }
233  if (p.z > zMax) outUp = true;
234  if (p.z < zMin) outDown = true;
235  }
236  if (!(anyIn || (outUp && outDown))) return; // skip triangle
237 
238  ret.buildAddTriangle(t);
239  numPassedPts += 3;
240  };
241 
242  if (oRST)
243  {
244  auto lck = mrpt::lockHelper(oRST->shaderTrianglesBufferMutex().data);
245  const auto& tris = oRST->shaderTrianglesBuffer();
246  for (const auto& tri : tris) lambdaUpdateTri(tri);
247  }
248  if (oRSTT)
249  {
250  auto lck =
251  mrpt::lockHelper(oRSTT->shaderTexturedTrianglesBufferMutex().data);
252  const auto& tris = oRSTT->shaderTexturedTrianglesBuffer();
253  for (const auto& tri : tris) lambdaUpdateTri(tri);
254  }
255  if (oRP)
256  {
257  auto lck = mrpt::lockHelper(oRP->shaderPointsBuffersMutex().data);
258  const auto& pts = oRP->shaderPointsVertexPointBuffer();
259  for (const auto& pt : pts) lambdaUpdatePt(pt);
260  }
261  if (oRSWF)
262  {
263  auto lck = mrpt::lockHelper(oRSWF->shaderWireframeBuffersMutex().data);
264  const auto& pts = oRSWF->shaderWireframeVertexPointBuffer();
265  for (const auto& pt : pts) lambdaUpdatePt(pt);
266  }
267 
268 #if MRPT_VERSION >= 0x260
269  if (oAssimp)
270  {
271  const auto& txtrdObjs = oAssimp->texturedObjects(); // mrpt>=2.6.0
272  for (const auto& o : txtrdObjs)
273  {
274  if (!o) continue;
275 
276  auto lck =
277  mrpt::lockHelper(o->shaderTexturedTrianglesBufferMutex().data);
278  const auto& tris = o->shaderTexturedTrianglesBuffer();
279  for (const auto& tri : tris) lambdaUpdateTri(tri);
280  }
281  }
282 #endif
283 
284  // Convert all points into an actual 2.5D volume:
285  // ---------------------------------------------------------
286  ret.clipZMin(zMin);
287  ret.clipZMax(zMax);
288 
289  ret.getContour(); // evalute it now
290 
291  return ret;
292 }
293 
295  const size_t actualEdgeCount, double actualRadius, float zMin, float zMax,
296  const mrpt::poses::CPose3D& modelPose, const float modelScale)
297 {
298  const size_t maxEdges = b2_maxPolygonVertices;
299 
300  const auto nFaces = std::min<size_t>(maxEdges, actualEdgeCount);
301 
302  const bool isApprox = actualEdgeCount > maxEdges;
303  if (isApprox)
304  {
305  const int i = mrpt::round(nFaces / 4);
306  double newR = actualRadius;
307  for (int j = -1; j <= 1; j++)
308  {
309  const double ang = (i + j) * 2 * M_PI / nFaces;
310  const double angp1 = (i + j + 1) * 2 * M_PI / nFaces;
311  const mrpt::math::TPoint2D pt0 = {cos(ang), sin(ang)};
312  const mrpt::math::TPoint2D pt1 = {cos(angp1), sin(angp1)};
313 
314  const double midDist = ((pt0 + pt1) * 0.5).norm();
315 
316  newR = std::max(newR, actualRadius * (1.0 / midDist));
317  }
318  actualRadius = newR;
319  }
320 
321  mrpt::math::TPolygon2D contour;
322  for (size_t i = 0; i < nFaces; i++)
323  {
324  const double ang = i * 2 * M_PI / nFaces;
325  const mrpt::math::TPoint2D localPt = {
326  cos(ang) * actualRadius, sin(ang) * actualRadius};
327  const auto pt = modelPose.composePoint(localPt * modelScale);
328  contour.emplace_back(pt.x, pt.y);
329  }
330 
331  Shape2p5 s;
332  s.setShapeManual(contour, zMin, zMax);
333  return {s};
334 }
const mrpt::math::TPolygon2D & getContour() const
Definition: Shape2p5.cpp:95
std::map< std::string, Entry > cache
XmlRpcServer s
void clipZMin(float v)
Definition: Shape2p5.cpp:638
#define M_PI
std::optional< Shape2p5 > processSimpleGeometries(const mrpt::opengl::CRenderizable &obj, float zMin, float zMax, const mrpt::poses::CPose3D &modelPose, const float modelScale)
geometry_msgs::TransformStamped t
Shape2p5 processCylinderLike(const size_t actualEdgeCount, double actualRadius, float zMin, float zMax, const mrpt::poses::CPose3D &modelPose, const float modelScale)
void setShapeManual(const mrpt::math::TPolygon2D &contour, const float zMin, const float zMax)
Definition: Shape2p5.cpp:219
Shape2p5 processGenericGeometry(mrpt::opengl::CRenderizable &obj, float zMin, float zMax, const mrpt::poses::CPose3D &modelPose, const float modelScale)
void buildAddPoint(const mrpt::math::TPoint3Df &pt)
Definition: Shape2p5.cpp:127
static CollisionShapeCache & Instance()
double volume() const
Definition: Shape2p5.cpp:42
void buildAddTriangle(const mrpt::opengl::TTriangle &t)
Definition: Shape2p5.cpp:136
#define b2_maxPolygonVertices
Definition: b2_settings.h:53
INLINE Rall1d< T, V, S > cos(const Rall1d< T, V, S > &arg)
Shape2p5 get(mrpt::opengl::CRenderizable &obj, float zMin, float zMax, const mrpt::poses::CPose3D &modelPose, const float modelScale, const std::optional< std::string > &modelFile=std::nullopt)
void clipZMax(float v)
Definition: Shape2p5.cpp:643
INLINE Rall1d< T, V, S > sin(const Rall1d< T, V, S > &arg)
void buildInit(const mrpt::math::TPoint2Df &bbMin, const mrpt::math::TPoint2Df &bbMax, int numCells=100)
Definition: Shape2p5.cpp:106


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