GridMapVisual.cpp
Go to the documentation of this file.
1 /*
2  * GridMapVisual.cpp
3  *
4  * Created on: Aug 3, 2016
5  * Author: Philipp Krüsi, Péter Fankhauser
6  * Institute: ETH Zurich, ANYbotics
7  */
8 
10 
11 #include <OGRE/OgreManualObject.h>
12 #include <OGRE/OgreMaterialManager.h>
13 #include <OGRE/OgreSceneManager.h>
14 #include <OGRE/OgreSceneNode.h>
15 #include <OGRE/OgreTechnique.h>
16 #include <OGRE/OgreTextureManager.h>
17 #include <OGRE/OgreVector3.h>
20 #include <chrono>
21 
24 
25 namespace grid_map_rviz_plugin {
26 
27 GridMapVisual::GridMapVisual(Ogre::SceneManager* sceneManager, Ogre::SceneNode* parentNode) : manualObject_(0), haveMap_(false) {
28  sceneManager_ = sceneManager;
29  frameNode_ = parentNode->createChildSceneNode();
30 
31  // Create BillboardLine object.
33 }
34 
36  // Destroy the ManualObject.
37  sceneManager_->destroyManualObject(manualObject_);
38  material_->unload();
39  Ogre::MaterialManager::getSingleton().remove(material_->getName());
40 
41  // Destroy the frame node.
42  sceneManager_->destroySceneNode(frameNode_);
43 }
44 
45 void GridMapVisual::setMessage(const grid_map_msgs::GridMap::ConstPtr& msg) {
46  // Convert grid map message.
48  haveMap_ = true;
49 }
50 
51 void GridMapVisual::computeVisualization(float alpha, bool showGridLines, bool flatTerrain, std::string heightLayer, bool flatColor,
52  bool noColor, Ogre::ColourValue meshColor, bool mapLayerColor, std::string colorLayer,
53  bool useRainbow, bool invertRainbow, Ogre::ColourValue minColor, Ogre::ColourValue maxColor,
54  bool autocomputeIntensity, float minIntensity, float maxIntensity) {
55  const auto startTime = std::chrono::high_resolution_clock::now();
56  if (!haveMap_) {
57  ROS_DEBUG("Unable to visualize grid map, no map data. Use setMessage() first!");
58  return;
59  }
60 
61  // Get list of layers and check if the requested ones are present.
62  const std::vector<std::string> layerNames = map_.getLayers();
63  if (layerNames.size() < 1) {
64  ROS_DEBUG("Unable to visualize grid map, map must contain at least one layer.");
65  return;
66  }
67  if ((!flatTerrain && !map_.exists(heightLayer)) || (!noColor && !flatColor && !map_.exists(colorLayer))) {
68  ROS_DEBUG("Unable to visualize grid map, requested layer(s) not available.");
69  return;
70  }
71 
72  // Convert to simple format, makes things easier.
74 
75  // Basic grid map data.
76  const size_t rows = map_.getSize()(0);
77  const size_t cols = map_.getSize()(1);
78  if (rows < 2 || cols < 2) {
79  ROS_DEBUG("GridMap has not enough cells.");
80  return;
81  }
82  const double resolution = map_.getResolution();
83  const grid_map::Matrix& heightData = map_[flatTerrain ? layerNames[0] : heightLayer];
84  const grid_map::Matrix& colorData = map_[flatColor ? layerNames[0] : colorLayer];
85 
86  // Reset and begin the manualObject (mesh).
87  // For more information: https://www.ogre3d.org/docs/api/1.7/class_ogre_1_1_manual_object.html#details
88  const size_t nVertices = cols * rows;
90 
91  // Reset the mesh lines.
92  meshLines_->clear();
93  if (showGridLines) {
94  initializeMeshLines(cols, rows, resolution, alpha);
95  }
96 
97  // Compute a mask of valid cells.
98  auto basicLayers = map_.getBasicLayers();
99  if (std::find(basicLayers.begin(), basicLayers.end(), heightLayer) == basicLayers.end()) {
100  basicLayers.emplace_back(heightLayer);
101  }
102  const MaskArray isValid = computeIsValidMask(basicLayers);
103 
104  // Compute the display heights for each cell.
105  Eigen::ArrayXXf heightOrFlatData;
106  if (flatTerrain) {
107  heightOrFlatData = Eigen::ArrayXXf::Zero(heightData.rows(), heightData.cols());
108  } else {
109  heightOrFlatData = heightData.array();
110  }
111 
112  // Compute the color data for each cell.
113  ColoringMethod coloringMethod;
114  if (flatColor) {
115  coloringMethod = ColoringMethod::FLAT;
116  } else if(mapLayerColor) {
117  coloringMethod = ColoringMethod::COLOR_LAYER;
118  } else if (!useRainbow) {
120  } else if (!invertRainbow) {
122  } else {
124  }
125 
126  const auto colorValues = computeColorValues(heightData, colorData, coloringMethod, meshColor, minIntensity, maxIntensity,
127  autocomputeIntensity, minColor, maxColor);
128 
129  // Initialize loop constants.
130  grid_map::Position topLeft;
131  map_.getPosition(grid_map::Index(0, 0), topLeft);
132 
133  Eigen::ArrayXXi indexToOgreIndex;
134  indexToOgreIndex.setConstant(rows, cols, -1);
135 
136  int ogreIndex = 0;
137 
138  // Add vertices for mesh.
139  for (size_t i = 0; i < rows; ++i) {
140  for (size_t j = 0; j < cols; ++j) {
141  std::vector<int> vertices;
142  std::vector<Ogre::ColourValue> colors;
143 
144  // Add the vertex to the scene
145  grid_map::Index index(i, j);
146  if (!isValid(index(0), index(1))) {
147  continue;
148  }
149  grid_map::Position position = topLeft.array() - index.cast<double>() * resolution;
150  manualObject_->position(position(0), position(1), heightOrFlatData(index(0), index(1)));
151 
152  const Ogre::ColourValue& color = colorValues(index(0), index(1));
153  manualObject_->colour(color.r, color.g, color.b, alpha);
154 
155  indexToOgreIndex(index(0), index(1)) = ogreIndex;
156  ogreIndex++;
157 
158  // We can only add triangles to the top left side of the current vertex if we have data.
159  if (i == 0 || j == 0) {
160  continue;
161  }
162 
163  // Add triangles and grid to scene.
164  std::vector<int> vertexIndices;
165  std::vector<Ogre::Vector3> vertexPositions;
166  for (size_t k = 0; k < 2; k++) {
167  for (size_t l = 0; l < 2; l++) {
168  grid_map::Index index(i - k, j - l);
169  if (!isValid(index(0), index(1))) {
170  continue;
171  }
172  const grid_map::Position position = topLeft.array() - index.cast<double>() * resolution;
173  vertexIndices.emplace_back(indexToOgreIndex(index(0), index(1)));
174  vertexPositions.emplace_back(position(0), position(1), heightOrFlatData(index(0), index(1)));
175  }
176  }
177 
178  // Plot triangles if we have enough vertices.
179  if (vertexIndices.size() > 2) {
180  // Create one or two triangles from the vertices depending on how many vertices we have.
181  if (!noColor) {
182  if (vertexIndices.size() == 3) {
183  manualObject_->triangle(vertexIndices[0], vertexIndices[1], vertexIndices[2]);
184  } else {
185  manualObject_->quad(vertexIndices[0], vertexIndices[2], vertexIndices[3], vertexIndices[1]);
186  }
187  }
188 
189  // plot grid lines
190  if (showGridLines) {
191  meshLines_->addPoint(vertexPositions[0]);
192  meshLines_->addPoint(vertexPositions[1]);
193  meshLines_->newLine();
194 
195  if (vertexIndices.size() == 3) {
196  meshLines_->addPoint(vertexPositions[1]);
197  meshLines_->addPoint(vertexPositions[2]);
198  meshLines_->newLine();
199  } else {
200  meshLines_->addPoint(vertexPositions[1]);
201  meshLines_->addPoint(vertexPositions[3]);
202  meshLines_->newLine();
203 
204  meshLines_->addPoint(vertexPositions[3]);
205  meshLines_->addPoint(vertexPositions[2]);
206  meshLines_->newLine();
207  }
208 
209  meshLines_->addPoint(vertexPositions[2]);
210  meshLines_->addPoint(vertexPositions[0]);
211  meshLines_->newLine();
212  }
213  }
214  }
215  }
216 
217  manualObject_->end();
218  material_->getTechnique(0)->setLightingEnabled(false);
219 
220  if (alpha < 0.9998) {
221  material_->getTechnique(0)->setSceneBlending(Ogre::SBT_TRANSPARENT_ALPHA);
222  material_->getTechnique(0)->setDepthWriteEnabled(false);
223  } else {
224  material_->getTechnique(0)->setSceneBlending(Ogre::SBT_REPLACE);
225  material_->getTechnique(0)->setDepthWriteEnabled(true);
226  }
227 
228  const auto stopTime = std::chrono::high_resolution_clock::now();
229  const auto elapsedTime = std::chrono::duration_cast<std::chrono::milliseconds>(stopTime - startTime);
230  ROS_DEBUG_STREAM("Visualization of grid_map took: " << elapsedTime.count() << " ms.");
231 }
232 
234  if (!manualObject_) {
235  static uint32_t count = 0;
237  ss << "Mesh" << count++;
238  manualObject_ = sceneManager_->createManualObject(ss.str());
239  manualObject_->setDynamic(true);
240  frameNode_->attachObject(manualObject_);
241 
242  ss << "Material";
243  materialName_ = ss.str();
244  material_ = Ogre::MaterialManager::getSingleton().create(materialName_, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
245  material_->setReceiveShadows(false);
246  material_->getTechnique(0)->setLightingEnabled(true);
247  material_->setCullingMode(Ogre::CULL_NONE);
248  }
249 
250  manualObject_->clear();
251  manualObject_->estimateVertexCount(nVertices);
252  manualObject_->begin(materialName_, Ogre::RenderOperation::OT_TRIANGLE_LIST);
253 }
254 
255 GridMapVisual::ColorArray GridMapVisual::computeColorValues(Eigen::Ref<const grid_map::Matrix> heightData,
256  Eigen::Ref<const grid_map::Matrix> colorData,
257  GridMapVisual::ColoringMethod coloringMethod, Ogre::ColourValue flatColor,
258  double minIntensity, double maxIntensity, bool autocomputeIntensity,
259  Ogre::ColourValue minColor, Ogre::ColourValue maxColor) {
260  // Determine max and min intensity.
261  bool isIntensityColoringMethod = coloringMethod == ColoringMethod::INTENSITY_LAYER_INVERTED_RAINBOW ||
262  coloringMethod == ColoringMethod::INTENSITY_LAYER_RAINBOW ||
263  coloringMethod == ColoringMethod::INTENSITY_LAYER_MANUAL;
264  if (autocomputeIntensity && isIntensityColoringMethod) {
265  minIntensity = colorData.minCoeffOfFinites();
266  maxIntensity = minIntensity + std::max(colorData.maxCoeffOfFinites() - minIntensity, 0.2); // regularize the intensity range.
267  }
268 
269  switch (coloringMethod) {
271  return ColorArray::Constant(heightData.rows(), heightData.cols(), flatColor);
273  return colorData.unaryExpr([](float color) {
274  Eigen::Vector3f colorVectorRGB;
275  grid_map::colorValueToVector(color, colorVectorRGB);
276  return Ogre::ColourValue(colorVectorRGB(0), colorVectorRGB(1), colorVectorRGB(2));
277  });
279  return colorData.unaryExpr([&](float color) {
280  normalizeIntensity(color, minIntensity, maxIntensity);
281  return getInterpolatedColor(color, minColor, maxColor);
282  });
284  return colorData.unaryExpr([&](float color) {
285  normalizeIntensity(color, minIntensity, maxIntensity);
286  return getRainbowColor(color);
287  });
289  return colorData.unaryExpr([&](float color) {
290  normalizeIntensity(color, minIntensity, maxIntensity);
291  return getRainbowColor(1.0f - color);
292  });
293  default:
294  throw std::invalid_argument(std::string("An unknown coloring method was provided: ") +
295  std::to_string(static_cast<int>(coloringMethod)));
296  }
297 }
298 
299 void GridMapVisual::initializeMeshLines(size_t cols, size_t rows, double resolution, double alpha) {
300  meshLines_->setColor(0.0, 0.0, 0.0, alpha);
301  meshLines_->setLineWidth(resolution / 10.0);
302  meshLines_->setMaxPointsPerLine(2);
303  // In the algorithm below, we have to account for max. 4 lines per cell.
304  const size_t nLines = 2 * (rows * (cols - 1) + cols * (rows - 1));
305  meshLines_->setNumLines(nLines);
306 }
307 
308 GridMapVisual::MaskArray GridMapVisual::computeIsValidMask(std::vector<std::string> basicLayers) {
309  MaskArray isValid = MaskArray::Ones(map_.getSize()(0), map_.getSize()(1));
310  for (const std::string& layer : basicLayers) {
311  isValid = isValid && map_.get(layer).array().unaryExpr([](float v) { return std::isfinite(v); });
312  }
313  return isValid;
314 }
315 
316 void GridMapVisual::setFramePosition(const Ogre::Vector3& position) {
317  frameNode_->setPosition(position);
318 }
319 
320 void GridMapVisual::setFrameOrientation(const Ogre::Quaternion& orientation) {
321  frameNode_->setOrientation(orientation);
322 }
323 
324 std::vector<std::string> GridMapVisual::getLayerNames() {
325  return map_.getLayers();
326 }
327 
328 void GridMapVisual::normalizeIntensity(float& intensity, float min_intensity, float max_intensity) {
329  intensity = std::min(intensity, max_intensity);
330  intensity = std::max(intensity, min_intensity);
331  intensity = (intensity - min_intensity) / (max_intensity - min_intensity);
332 }
333 
334 Ogre::ColourValue GridMapVisual::getRainbowColor(float intensity) {
335  intensity = std::min(intensity, 1.0f);
336  intensity = std::max(intensity, 0.0f);
337 
338  float h = intensity * 5.0f + 1.0f;
339  int i = floor(h);
340  float f = h - i;
341  if (!(i & 1)) f = 1 - f; // if i is even
342  float n = 1 - f;
343 
344  Ogre::ColourValue color;
345  if (i <= 1)
346  color[0] = n, color[1] = 0, color[2] = 1;
347  else if (i == 2)
348  color[0] = 0, color[1] = n, color[2] = 1;
349  else if (i == 3)
350  color[0] = 0, color[1] = 1, color[2] = n;
351  else if (i == 4)
352  color[0] = n, color[1] = 1, color[2] = 0;
353  else if (i >= 5)
354  color[0] = 1, color[1] = n, color[2] = 0;
355 
356  return color;
357 }
358 
359 // Get interpolated color value.
360 Ogre::ColourValue GridMapVisual::getInterpolatedColor(float intensity, Ogre::ColourValue min_color, Ogre::ColourValue max_color) {
361  intensity = std::min(intensity, 1.0f);
362  intensity = std::max(intensity, 0.0f);
363 
364  Ogre::ColourValue color;
365  color.r = intensity * (max_color.r - min_color.r) + min_color.r;
366  color.g = intensity * (max_color.g - min_color.g) + min_color.g;
367  color.b = intensity * (max_color.b - min_color.b) + min_color.b;
368 
369  return color;
370 }
371 
372 } // namespace grid_map_rviz_plugin
Eigen::Array2i Index
boost::shared_ptr< rviz::BillboardLine > meshLines_
f
bool getPosition(const Index &index, Position &position) const
ColorArray computeColorValues(Eigen::Ref< const grid_map::Matrix > heightData, Eigen::Ref< const grid_map::Matrix > colorData, GridMapVisual::ColoringMethod coloringMethod, Ogre::ColourValue flatColor, double minIntensity, double maxIntensity, bool autocomputeIntensity, Ogre::ColourValue minColor, Ogre::ColourValue maxColor)
void setFramePosition(const Ogre::Vector3 &position)
Eigen::MatrixXf Matrix
void computeVisualization(float alpha, bool showGridLines, bool flatTerrain, std::string heightLayer, bool flatColor, bool noColor, Ogre::ColourValue meshColor, bool mapLayerColor, std::string colorLayer, bool useRainbow, bool invertRainbow, Ogre::ColourValue minColor, Ogre::ColourValue maxColor, bool autocomputeIntensity, float minIntensity, float maxIntensity)
static void normalizeIntensity(float &intensity, float minIntensity, float maxIntensity)
static Ogre::ColourValue getRainbowColor(float intensity)
void convertToDefaultStartIndex()
bool exists(const std::string &layer) const
Ogre::ColourValue getInterpolatedColor(float intensity, Ogre::ColourValue minColor, Ogre::ColourValue maxColor)
static bool fromMessage(const grid_map_msgs::GridMap &message, grid_map::GridMap &gridMap, const std::vector< std::string > &layers, bool copyBasicLayers=true, bool copyAllNonBasicLayers=true)
double getResolution() const
const std::vector< std::string > & getLayers() const
Eigen::Vector2d Position
std::vector< std::string > getLayerNames()
const Matrix & get(const std::string &layer) const
GridMapVisual(Ogre::SceneManager *sceneManager, Ogre::SceneNode *parentNode)
void initializeMeshLines(size_t cols, size_t rows, double resolution, double alpha)
#define ROS_DEBUG_STREAM(args)
void initializeAndBeginManualObject(size_t nVertices)
void setFrameOrientation(const Ogre::Quaternion &orientation)
MaskArray computeIsValidMask(std::vector< std::string > basicLayers)
bool colorValueToVector(const unsigned long &colorValue, Eigen::Vector3i &colorVector)
Eigen::Array< bool, Eigen::Dynamic, Eigen::Dynamic > MaskArray
const std::vector< std::string > & getBasicLayers() const
const Size & getSize() const
Eigen::Array< Ogre::ColourValue, Eigen::Dynamic, Eigen::Dynamic > ColorArray
void setMessage(const grid_map_msgs::GridMap::ConstPtr &msg)
#define ROS_DEBUG(...)


grid_map_rviz_plugin
Author(s): Philipp Krüsi , Péter Fankhauser
autogenerated on Tue Jun 1 2021 02:13:47