DART 6.10.1
Loading...
Searching...
No Matches
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
50
51namespace dart {
52namespace gui {
53namespace osg {
54namespace render {
55
56template <typename S>
57class HeightmapShapeGeode;
58
59template <typename S_>
60class HeightmapShapeNode : public ShapeNode, public ::osg::MatrixTransform
61{
62public:
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
72protected:
73 virtual ~HeightmapShapeNode() override;
74
75 std::shared_ptr<dynamics::HeightmapShape<S>> mHeightmapShape;
77 std::size_t mHeightmapVersion;
78};
79
80//==============================================================================
81template <typename S>
82class HeightmapShapeDrawable : public ::osg::Geometry
83{
84public:
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
107protected:
108 ~HeightmapShapeDrawable() override = default;
109
113
114private:
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//==============================================================================
122template <typename S>
123class HeightmapShapeGeode : public ShapeNode, public ::osg::Geode
124{
125public:
128 ShapeFrameNode* parentShapeFrame,
129 HeightmapShapeNode<S>* parentNode);
130
131 void refresh();
132 void extractData();
133
134protected:
135 virtual ~HeightmapShapeGeode();
136
140};
141
142//==============================================================================
143template <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//==============================================================================
156template <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//==============================================================================
173template <typename S>
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//==============================================================================
188template <typename S>
190{
191 // Do nothing
192}
193
194//==============================================================================
195template <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//==============================================================================
214template <typename S>
216{
217 mUtilized = true;
218
219 extractData();
220}
221
222//==============================================================================
223template <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//==============================================================================
238template <typename S>
243
244//==============================================================================
245template <typename S>
248 dynamics::VisualAspect* visualAspect,
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//==============================================================================
274template <typename S>
275Eigen::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//==============================================================================
284inline ::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//==============================================================================
293inline ::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//==============================================================================
302template <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,
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//==============================================================================
445template <typename S>
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
Shape for a height map.
Definition HeightmapShape.hpp:47
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
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