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


mvsim
Author(s):
autogenerated on Wed May 28 2025 02:13:07