DART  6.10.1
HeightmapShapeNode.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2011-2021, The DART development contributors
3  * All rights reserved.
4  *
5  * The list of contributors can be found at:
6  * https://github.com/dartsim/dart/blob/master/LICENSE
7  *
8  * This file is provided under the following "BSD-style" License:
9  * Redistribution and use in source and binary forms, with or
10  * without modification, are permitted provided that the following
11  * conditions are met:
12  * * Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  * * Redistributions in binary form must reproduce the above
15  * copyright notice, this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided
17  * with the distribution.
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
29  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #ifndef DART_GUI_OSG_RENDER_HEIGHTMAPSHAPENODE_HPP_
34 #define DART_GUI_OSG_RENDER_HEIGHTMAPSHAPENODE_HPP_
35 
36 #include "dart/config.hpp"
37 
38 #include <osg/CullFace>
39 #include <osg/Geode>
40 #include <osg/Geometry>
41 #include <osg/Light>
42 #include <osg/Material>
43 #include <osg/MatrixTransform>
44 #include <osg/ShapeDrawable>
45 
48 #include "dart/gui/osg/Utils.hpp"
50 
51 namespace dart {
52 namespace gui {
53 namespace osg {
54 namespace render {
55 
56 template <typename S>
57 class HeightmapShapeGeode;
58 
59 template <typename S_>
60 class HeightmapShapeNode : public ShapeNode, public ::osg::MatrixTransform
61 {
62 public:
63  using S = S_;
64 
66  std::shared_ptr<dynamics::HeightmapShape<S>> shape,
67  ShapeFrameNode* parent);
68 
69  void refresh() override;
70  void extractData(bool firstTime);
71 
72 protected:
73  virtual ~HeightmapShapeNode() override;
74 
75  std::shared_ptr<dynamics::HeightmapShape<S>> mHeightmapShape;
77  std::size_t mHeightmapVersion;
78 };
79 
80 //==============================================================================
81 template <typename S>
82 class HeightmapShapeDrawable : public ::osg::Geometry
83 {
84 public:
85  using Vector3 = Eigen::Matrix<S, 3, 1>;
86 
87  using osgVec3 = typename std::conditional<
88  std::is_same<S, float>::value,
89  ::osg::Vec3f,
90  ::osg::Vec3d>::type;
91  using Vec3Array = typename std::conditional<
92  std::is_same<S, float>::value,
93  ::osg::Vec3Array,
94  ::osg::Vec3dArray>::type;
95  using Vec4Array = typename std::conditional<
96  std::is_same<S, float>::value,
97  ::osg::Vec4Array,
98  ::osg::Vec4dArray>::type;
99 
102  dynamics::VisualAspect* visualAspect,
103  HeightmapShapeGeode<S>* parent);
104 
105  void refresh(bool firstTime);
106 
107 protected:
108  ~HeightmapShapeDrawable() override = default;
109 
113 
114 private:
115  ::osg::ref_ptr<Vec3Array> mVertices;
116  ::osg::ref_ptr<::osg::DrawElementsUInt> mElements;
117  ::osg::ref_ptr<Vec3Array> mNormals;
118  ::osg::ref_ptr<::osg::Vec4Array> mColors;
119 };
120 
121 //==============================================================================
122 template <typename S>
123 class HeightmapShapeGeode : public ShapeNode, public ::osg::Geode
124 {
125 public:
128  ShapeFrameNode* parentShapeFrame,
129  HeightmapShapeNode<S>* parentNode);
130 
131  void refresh();
132  void extractData();
133 
134 protected:
135  virtual ~HeightmapShapeGeode();
136 
140 };
141 
142 //==============================================================================
143 template <typename S>
145  std::shared_ptr<dynamics::HeightmapShape<S>> shape, ShapeFrameNode* parent)
146  : ShapeNode(shape, parent, this),
147  mHeightmapShape(shape),
148  mGeode(nullptr),
149  mHeightmapVersion(dynamics::INVALID_INDEX)
150 {
151  extractData(true);
152  setNodeMask(mVisualAspect->isHidden() ? 0x0u : ~0x0u);
153 }
154 
155 //==============================================================================
156 template <typename S>
158 {
159  mUtilized = true;
160 
161  setNodeMask(mVisualAspect->isHidden() ? 0x0u : ~0x0u);
162 
163  if (mShape->getDataVariance() == dynamics::Shape::STATIC
164  && mHeightmapVersion == mHeightmapShape->getVersion())
165  return;
166 
167  extractData(false);
168 
169  mHeightmapVersion = mHeightmapShape->getVersion();
170 }
171 
172 //==============================================================================
173 template <typename S>
174 void HeightmapShapeNode<S>::extractData(bool /*firstTime*/)
175 {
176  if (nullptr == mGeode)
177  {
178  mGeode = new HeightmapShapeGeode<S>(
179  mHeightmapShape.get(), mParentShapeFrameNode, this);
180  addChild(mGeode);
181  return;
182  }
183 
184  mGeode->refresh();
185 }
186 
187 //==============================================================================
188 template <typename S>
190 {
191  // Do nothing
192 }
193 
194 //==============================================================================
195 template <typename S>
198  ShapeFrameNode* parentShapeFrame,
199  HeightmapShapeNode<S>* parentNode)
200  : ShapeNode(parentNode->getShape(), parentShapeFrame, this),
201  mParentNode(parentNode),
202  mHeightmapShape(shape),
203  mDrawable(nullptr)
204 {
205  getOrCreateStateSet()->setMode(GL_BLEND, ::osg::StateAttribute::ON);
206  getOrCreateStateSet()->setRenderingHint(::osg::StateSet::TRANSPARENT_BIN);
207  getOrCreateStateSet()->setAttributeAndModes(
208  new ::osg::CullFace(::osg::CullFace::BACK));
209  getOrCreateStateSet()->setMode(GL_LIGHTING, ::osg::StateAttribute::ON);
210  extractData();
211 }
212 
213 //==============================================================================
214 template <typename S>
216 {
217  mUtilized = true;
218 
219  extractData();
220 }
221 
222 //==============================================================================
223 template <typename S>
225 {
226  if (nullptr == mDrawable)
227  {
228  mDrawable
229  = new HeightmapShapeDrawable<S>(mHeightmapShape, mVisualAspect, this);
230  addDrawable(mDrawable);
231  return;
232  }
233 
234  mDrawable->refresh(false);
235 }
236 
237 //==============================================================================
238 template <typename S>
240 {
241  // Do nothing
242 }
243 
244 //==============================================================================
245 template <typename S>
248  dynamics::VisualAspect* visualAspect,
249  HeightmapShapeGeode<S>* parent)
250  : mHeightmapShape(shape), mVisualAspect(visualAspect), mParent(parent)
251 {
252  static_assert(
253  std::is_same<S, float>::value || std::is_same<S, double>::value,
254  "Scalar type should be float or double");
255 
256  // See:
257  // https://osg-users.openscenegraph.narkive.com/VY16YIMs/crash-due-to-triangle-functor-does-not-support-vec3d-vertex-arrays
258  // https://github.com/openscenegraph/OpenSceneGraph/blob/5b688eb99dd5db94f7068ee18fb94f120720e3d1/include/osg/TriangleFunctor#L73
259  static_assert(
260  !std::is_same<S, double>::value,
261  "OpenSceneGraph currently doesn't support double precision for "
262  "Heightmap");
263 
264  mVertices = new Vec3Array;
265  mNormals = new Vec3Array;
266  mColors = new Vec4Array;
267  // TODO(JS): Switch to TRIANGLE_STRIP to save storage for indicies
268  mElements = new ::osg::DrawElementsUInt(::osg::PrimitiveSet::TRIANGLES);
269  addPrimitiveSet(mElements);
270  refresh(true);
271 }
272 
273 //==============================================================================
274 template <typename S>
275 Eigen::Matrix<S, 3, 1> getNormal(
276  const Eigen::Matrix<S, 3, 1>& p1,
277  const Eigen::Matrix<S, 3, 1>& p2,
278  const Eigen::Matrix<S, 3, 1>& p3)
279 {
280  return (p2 - p1).cross(p3 - p1).normalized();
281 }
282 
283 //==============================================================================
284 inline ::osg::Vec3f getNormal(
285  const ::osg::Vec3f& p1, const ::osg::Vec3f& p2, const ::osg::Vec3f& p3)
286 {
287  auto normal = (p2 - p1) ^ (p3 - p1);
288  normal.normalize();
289  return normal;
290 }
291 
292 //==============================================================================
293 inline ::osg::Vec3d getNormal(
294  const ::osg::Vec3d& p1, const ::osg::Vec3d& p2, const ::osg::Vec3d& p3)
295 {
296  auto normal = (p2 - p1) ^ (p3 - p1);
297  normal.normalize();
298  return normal;
299 }
300 
301 //==============================================================================
302 template <typename S>
304  const typename dynamics::HeightmapShape<S>::HeightField& heightmap,
305  typename HeightmapShapeDrawable<S>::Vec3Array& vertices,
306  ::osg::DrawElementsUInt& faces,
307  typename HeightmapShapeDrawable<S>::Vec3Array& normals,
308  typename HeightmapShapeDrawable<S>::Vector3 scale)
309 {
310  // Returns an index array for a GL_TRIANGLES heightmap
311 
312  const auto rows = heightmap.rows();
313  const auto cols = heightmap.cols();
314 
315  faces.clear();
316  normals.clear();
317  if (rows < 2 || cols < 2)
318  {
319  vertices.clear();
320  return;
321  }
322 
323  vertices.resize(static_cast<std::size_t>(heightmap.size()));
324 
325  // Note that heightmap(i, j) represents the height value at (j, -i) in XY
326  // coordinates.
327  for (auto i = 0; i < rows; ++i)
328  {
329  for (auto j = 0; j < cols; ++j)
330  {
331  const auto index = cols * i + j;
332  vertices[index].set(
333  j * scale.x(), -(i * scale.y()), heightmap(i, j) * scale.z());
334  }
335  }
336 
337  //
338  // X
339  // +----------------------------------------------->
340  // |
341  // | | | |
342  // | | | |
343  // | -----o-----------o------------o-----
344  // | p1(i-1,j-1) | upper / | p2(i-1,j) |
345  // | | / | |
346  // | | / | |
347  // | | / | |
348  // | | / lower | |
349  // | -----o-----------o------------o-----
350  // | p3(i, j-1)| | curr(i, j) | p4(i,j+1)
351  // | | | |
352  // | | | |
353  // | | | |
354  // | | | |
355  // | -----o-----------o------------o-----
356  // | | | p5(i+1,j) |
357  // -Y | | | |
358  // V
359  //
360  //
361 
362  // For row-major matrix
363  faces.reserve(6 * (rows - 1) * (cols - 1));
364  for (auto i = 1; i < rows; ++i)
365  {
366  for (auto j = 1; j < cols; ++j)
367  {
368  // Indices for matrix
369  const auto p1i = i - 1;
370  const auto p1j = j - 1;
371 
372  const auto p2i = i - 1;
373  const auto p2j = j;
374 
375  const auto p3i = i;
376  const auto p3j = j - 1;
377 
378  // Indices for vector
379  const auto p1 = p1i * cols + p1j;
380  const auto p2 = p2i * cols + p2j;
381  const auto p3 = p3i * cols + p3j;
382  const auto curr = i * cols + j;
383 
384  // Upper triangle
385  faces.push_back(p1);
386  faces.push_back(p3);
387  faces.push_back(p2);
388 
389  // Lower triangle
390  faces.push_back(p2);
391  faces.push_back(p3);
392  faces.push_back(curr);
393  }
394  }
395 
396  normals.reserve(heightmap.size());
397  for (auto i = 0; i < rows; ++i)
398  {
399  for (auto j = 0; j < cols; ++j)
400  {
401  // Indices for matrix
402  const auto p2i = i - 1;
403  const auto p2j = j;
404 
405  const auto p3i = i;
406  const auto p3j = j - 1;
407 
408  const auto p4i = i;
409  const auto p4j = j + 1;
410 
411  const auto p5i = i + 1;
412  const auto p5j = j;
413 
414  // Indices for vector
415  const auto p2 = p2i * cols + p2j;
416  const auto p3 = p3i * cols + p3j;
417  const auto p4 = p4i * cols + p4j;
418  const auto p5 = p5i * cols + p5j;
419  const auto curr = i * cols + j;
420 
421  const auto& ptCurr = vertices[curr];
422 
423  auto sum = typename HeightmapShapeDrawable<S>::osgVec3();
424 
425  if (i > 0 && j > 0)
426  sum += getNormal(ptCurr, vertices[p2], vertices[p3]);
427 
428  if (i + 1 < rows && j > 0)
429  sum += getNormal(ptCurr, vertices[p3], vertices[p5]);
430 
431  if (i + 1 < rows && j + 1 < cols)
432  sum += getNormal(ptCurr, vertices[p5], vertices[p4]);
433 
434  if (i > 0 && j + 1 < cols)
435  sum += getNormal(ptCurr, vertices[p4], vertices[p2]);
436 
437  sum.normalize();
438 
439  normals.push_back(sum);
440  }
441  }
442 }
443 
444 //==============================================================================
445 template <typename S>
446 void HeightmapShapeDrawable<S>::refresh(bool /*firstTime*/)
447 {
448  if (mHeightmapShape->getDataVariance() == dynamics::Shape::STATIC)
449  setDataVariance(::osg::Object::STATIC);
450  else
451  setDataVariance(::osg::Object::DYNAMIC);
452 
453  // Row major matrix where top left corner is the height at (0, 0), and bottom
454  // right corner is the height at (rows, -cols) in (x, y) coordinates.
455  const auto& heightmap = mHeightmapShape->getHeightField();
456 
457  // This function is called whenever the heightmap version is increased, and
458  // the heightmap could be updated in the version up. So we always update the
459  // heightmap.
460  {
461  assert(mElements);
462  assert(mNormals);
463  setVertices<S>(
464  heightmap,
465  *mVertices,
466  *mElements,
467  *mNormals,
468  mHeightmapShape->getScale());
469  addPrimitiveSet(mElements);
470 
471  setVertexArray(mVertices);
472  setNormalArray(mNormals, ::osg::Array::BIND_PER_VERTEX);
473  }
474 
475  // This function is called whenever the heightmap version is increased, and
476  // the color could be updated in the version up. So we always update the
477  // color.
478  {
479  if (mColors->size() != 1)
480  mColors->resize(1);
481 
482  (*mColors)[0] = eigToOsgVec4d(mVisualAspect->getRGBA());
483 
484  setColorArray(mColors, ::osg::Array::BIND_OVERALL);
485  }
486 }
487 
488 } // namespace render
489 } // namespace osg
490 } // namespace gui
491 } // namespace dart
492 
493 #endif // DART_GUI_OSG_RENDER_HEIGHTMAPSHAPENODE_HPP_
std::string type
Definition: SdfParser.cpp:82
std::size_t index
Definition: SkelParser.cpp:1672
Eigen::Matrix< S, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > HeightField
Definition: HeightmapShape.hpp:53
@ STATIC
Definition: Shape.hpp:83
Definition: ShapeFrame.hpp:54
bool isHidden() const
True iff the ShapeNode is set to be hidden.
Definition: ShapeFrame.cpp:177
Definition: ShapeFrameNode.hpp:61
Definition: HeightmapShapeNode.hpp:83
Eigen::Matrix< S, 3, 1 > Vector3
Definition: HeightmapShapeNode.hpp:85
typename std::conditional< std::is_same< S, float >::value, ::osg::Vec3f, ::osg::Vec3d >::type osgVec3
Definition: HeightmapShapeNode.hpp:90
void refresh(bool firstTime)
Definition: HeightmapShapeNode.hpp:446
dynamics::VisualAspect * mVisualAspect
Definition: HeightmapShapeNode.hpp:111
HeightmapShapeGeode< S > * mParent
Definition: HeightmapShapeNode.hpp:112
typename std::conditional< std::is_same< S, float >::value, ::osg::Vec4Array, ::osg::Vec4dArray >::type Vec4Array
Definition: HeightmapShapeNode.hpp:98
dynamics::HeightmapShape< S > * mHeightmapShape
Definition: HeightmapShapeNode.hpp:110
::osg::ref_ptr<::osg::DrawElementsUInt > mElements
Definition: HeightmapShapeNode.hpp:116
::osg::ref_ptr< Vec3Array > mNormals
Definition: HeightmapShapeNode.hpp:117
::osg::ref_ptr<::osg::Vec4Array > mColors
Definition: HeightmapShapeNode.hpp:118
HeightmapShapeDrawable(dynamics::HeightmapShape< S > *shape, dynamics::VisualAspect *visualAspect, HeightmapShapeGeode< S > *parent)
Definition: HeightmapShapeNode.hpp:246
typename std::conditional< std::is_same< S, float >::value, ::osg::Vec3Array, ::osg::Vec3dArray >::type Vec3Array
Definition: HeightmapShapeNode.hpp:94
::osg::ref_ptr< Vec3Array > mVertices
Definition: HeightmapShapeNode.hpp:115
Definition: HeightmapShapeNode.hpp:124
dynamics::HeightmapShape< S > * mHeightmapShape
Definition: HeightmapShapeNode.hpp:138
HeightmapShapeDrawable< S > * mDrawable
Definition: HeightmapShapeNode.hpp:139
void refresh()
Update all rendering data for this ShapeNode.
Definition: HeightmapShapeNode.hpp:215
void extractData()
Definition: HeightmapShapeNode.hpp:224
virtual ~HeightmapShapeGeode()
Definition: HeightmapShapeNode.hpp:239
HeightmapShapeNode< S > * mParentNode
Definition: HeightmapShapeNode.hpp:137
HeightmapShapeGeode(dynamics::HeightmapShape< S > *shape, ShapeFrameNode *parentShapeFrame, HeightmapShapeNode< S > *parentNode)
Definition: HeightmapShapeNode.hpp:196
Definition: HeightmapShapeNode.hpp:61
HeightmapShapeNode(std::shared_ptr< dynamics::HeightmapShape< S >> shape, ShapeFrameNode *parent)
Definition: HeightmapShapeNode.hpp:144
void extractData(bool firstTime)
Definition: HeightmapShapeNode.hpp:174
S_ S
Definition: HeightmapShapeNode.hpp:63
std::shared_ptr< dynamics::HeightmapShape< S > > mHeightmapShape
Definition: HeightmapShapeNode.hpp:75
virtual ~HeightmapShapeNode() override
Definition: HeightmapShapeNode.hpp:189
HeightmapShapeGeode< S > * mGeode
Definition: HeightmapShapeNode.hpp:76
void refresh() override
Update all rendering data for this ShapeNode.
Definition: HeightmapShapeNode.hpp:157
std::size_t mHeightmapVersion
Definition: HeightmapShapeNode.hpp:77
Definition: ShapeNode.hpp:59
dart::dynamics::VisualAspect * mVisualAspect
Pointer to the VisualAspect associated with this ShapeNode.
Definition: ShapeNode.hpp:106
::osg::Vec4d eigToOsgVec4d(const Eigen::MatrixBase< Derived > &vec)
Definition: Utils.hpp:138
constexpr std::size_t INVALID_INDEX
Definition: InvalidIndex.hpp:41
Eigen::Matrix< S, 3, 1 > getNormal(const Eigen::Matrix< S, 3, 1 > &p1, const Eigen::Matrix< S, 3, 1 > &p2, const Eigen::Matrix< S, 3, 1 > &p3)
Definition: HeightmapShapeNode.hpp:275
void setVertices(const typename dynamics::HeightmapShape< S >::HeightField &heightmap, typename HeightmapShapeDrawable< S >::Vec3Array &vertices, ::osg::DrawElementsUInt &faces, typename HeightmapShapeDrawable< S >::Vec3Array &normals, typename HeightmapShapeDrawable< S >::Vector3 scale)
Definition: HeightmapShapeNode.hpp:303
Definition: BulletCollisionDetector.cpp:65