diff --git a/CMakeLists.txt b/CMakeLists.txt index 93283adcd..e8ac0f1d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ set(IGN_MATH_VER ${ignition-math7_VERSION_MAJOR}) #-------------------------------------- # Find ignition-common ign_find_package(ignition-common5 REQUIRED - COMPONENTS graphics events) + COMPONENTS graphics events geospatial) set(IGN_COMMON_VER ${ignition-common5_VERSION_MAJOR}) #-------------------------------------- diff --git a/examples/heightmap/Main.cc b/examples/heightmap/Main.cc index ac7306d3d..4218d11c9 100644 --- a/examples/heightmap/Main.cc +++ b/examples/heightmap/Main.cc @@ -28,8 +28,9 @@ #include #include -#include -#include +#include +#include +#include #include #include "example_config.hh" @@ -42,7 +43,178 @@ const std::string RESOURCE_PATH = common::joinPaths(std::string(PROJECT_BINARY_PATH), "media"); ////////////////////////////////////////////////// -void buildScene(ScenePtr _scene) +void createImageHeightmaps(const ScenePtr _scene, VisualPtr _root) +{ + //! [create an image heightmap] + auto data = std::make_shared(); + data->Load(common::joinPaths(RESOURCE_PATH, "heightmap_bowl.png")); + + HeightmapDescriptor desc; + desc.SetName("example_bowl"); + desc.SetData(data); + desc.SetSize({17, 17, 10}); + desc.SetSampling(2u); + desc.SetUseTerrainPaging(false); + + HeightmapTexture textureA; + textureA.SetSize(1.0); + textureA.SetDiffuse("../media/dirt_diffusespecular.png"); + textureA.SetNormal("../media/flat_normal.png"); + desc.AddTexture(textureA); + + HeightmapBlend blendA; + blendA.SetMinHeight(2.0); + blendA.SetFadeDistance(5.0); + desc.AddBlend(blendA); + + HeightmapTexture textureB; + textureB.SetSize(1.0); + textureB.SetDiffuse("../media/grass_diffusespecular.png"); + textureB.SetNormal("../media/flat_normal.png"); + desc.AddTexture(textureB); + + HeightmapBlend blendB; + blendB.SetMinHeight(4.0); + blendB.SetFadeDistance(5.0); + desc.AddBlend(blendB); + + HeightmapTexture textureC; + textureC.SetSize(1.0); + textureC.SetDiffuse("../media/fungus_diffusespecular.png"); + textureC.SetNormal("../media/flat_normal.png"); + desc.AddTexture(textureC); + + auto heightmapGeom = _scene->CreateHeightmap(desc); + + auto vis = _scene->CreateVisual(); + vis->AddGeometry(heightmapGeom); + _root->AddChild(vis); + //! [create an image heightmap] + + //! [create another image heightmap] + auto data2 = std::make_shared(); + data2->Load(common::joinPaths(RESOURCE_PATH, "city_terrain.jpg")); + + HeightmapDescriptor desc2; + desc2.SetName("example_city"); + desc2.SetData(data2); + desc2.SetSize({26, 26, 20}); + desc2.SetSampling(2u); + desc2.SetUseTerrainPaging(true); + + HeightmapTexture textureA2; + textureA2.SetSize(1.0); + textureA2.SetDiffuse("../media/fungus_diffusespecular.png"); + textureA2.SetNormal("../media/flat_normal.png"); + desc2.AddTexture(textureA2); + + HeightmapBlend blendA2; + blendA2.SetMinHeight(2.0); + blendA2.SetFadeDistance(5.0); + desc2.AddBlend(blendA2); + + HeightmapTexture textureB2; + textureB2.SetSize(1.0); + textureB2.SetDiffuse("../media/grass_diffusespecular.png"); + textureB2.SetNormal("../media/flat_normal.png"); + desc2.AddTexture(textureB2); + + HeightmapBlend blendB2; + blendB2.SetMinHeight(8.0); + blendB2.SetFadeDistance(5.0); + desc2.AddBlend(blendB2); + + HeightmapTexture textureC2; + textureC2.SetSize(1.0); + textureC2.SetDiffuse("../media/dirt_diffusespecular.png"); + textureC2.SetNormal("../media/flat_normal.png"); + desc2.AddTexture(textureC2); + desc2.SetPosition({30, 0, 0}); + auto heightmapGeom2 = _scene->CreateHeightmap(desc2); + + auto vis2 = _scene->CreateVisual(); + vis2->AddGeometry(heightmapGeom2); + _root->AddChild(vis2); + //! [create another image heightmap] +} + +////////////////////////////////////////////////// +void createDemHeightmaps(const ScenePtr _scene, VisualPtr _root) +{ + //! [create a dem heightmap] + auto data = std::make_shared(); + data->Load(common::joinPaths(RESOURCE_PATH, "volcano.tif")); + + HeightmapDescriptor desc; + desc.SetName("example_volcano"); + desc.SetData(data); + desc.SetSize({20, 20, 18}); + desc.SetSampling(2u); + desc.SetUseTerrainPaging(true); + + HeightmapTexture textureA; + textureA.SetSize(1.0); + textureA.SetDiffuse("../media/dirt_diffusespecular.png"); + textureA.SetNormal("../media/flat_normal.png"); + desc.AddTexture(textureA); + + HeightmapBlend blendA; + blendA.SetMinHeight(2.0); + blendA.SetFadeDistance(5.0); + desc.AddBlend(blendA); + + HeightmapTexture textureB; + textureB.SetSize(1.0); + textureB.SetDiffuse("../media/grass_diffusespecular.png"); + textureB.SetNormal("../media/flat_normal.png"); + desc.AddTexture(textureB); + + HeightmapBlend blendB; + blendB.SetMinHeight(4.0); + blendB.SetFadeDistance(5.0); + desc.AddBlend(blendB); + + HeightmapTexture textureC; + textureC.SetSize(1.0); + textureC.SetDiffuse("../media/fungus_diffusespecular.png"); + textureC.SetNormal("../media/flat_normal.png"); + desc.AddTexture(textureC); + desc.SetPosition({30, 0, 0}); + + auto heightmapGeom = _scene->CreateHeightmap(desc); + + auto vis = _scene->CreateVisual(); + vis->AddGeometry(heightmapGeom); + _root->AddChild(vis); + //! [create a dem heightmap] + + //! [create another dem heightmap] + auto data2 = std::make_shared(); + data2->Load(common::joinPaths(RESOURCE_PATH, "moon.tif")); + + HeightmapDescriptor desc2; + desc2.SetName("example_moon"); + desc2.SetData(data2); + desc2.SetSize({20, 20, 6.85}); + desc2.SetSampling(2u); + desc2.SetUseTerrainPaging(false); + + HeightmapTexture textureA2; + textureA2.SetSize(20.0); + textureA2.SetDiffuse("../media/moon_diffuse.png"); + textureA2.SetNormal("../media/moon_normal.png"); + desc2.AddTexture(textureA2); + desc2.SetPosition({0, 0, std::abs(data2->MinElevation())}); + auto heightmapGeom2 = _scene->CreateHeightmap(desc2); + + auto vis2 = _scene->CreateVisual(); + vis2->AddGeometry(heightmapGeom2); + _root->AddChild(vis2); + //! [create another dem heightmap] +} + +////////////////////////////////////////////////// +void buildScene(ScenePtr _scene, bool _buildDemScene) { // initialize _scene _scene->SetAmbientLight(0.3, 0.3, 0.3); @@ -68,97 +240,10 @@ void buildScene(ScenePtr _scene) light1->SetCastShadows(true); root->AddChild(light1); -//! [create a heightmap] - auto data = std::make_shared(); - data->Load(common::joinPaths(RESOURCE_PATH, "heightmap_bowl.png")); - - HeightmapDescriptor desc; - desc.SetName("example_bowl"); - desc.SetData(data); - desc.SetSize({17, 17, 10}); - desc.SetSampling(2u); - desc.SetUseTerrainPaging(false); - - HeightmapTexture textureA; - textureA.SetSize(1.0); - textureA.SetDiffuse("../media/dirt_diffusespecular.png"); - textureA.SetNormal("../media/flat_normal.png"); - desc.AddTexture(textureA); - - HeightmapBlend blendA; - blendA.SetMinHeight(2.0); - blendA.SetFadeDistance(5.0); - desc.AddBlend(blendA); - - HeightmapTexture textureB; - textureB.SetSize(1.0); - textureB.SetDiffuse("../media/grass_diffusespecular.png"); - textureB.SetNormal("../media/flat_normal.png"); - desc.AddTexture(textureB); - - HeightmapBlend blendB; - blendB.SetMinHeight(4.0); - blendB.SetFadeDistance(5.0); - desc.AddBlend(blendB); - - HeightmapTexture textureC; - textureC.SetSize(1.0); - textureC.SetDiffuse("../media/fungus_diffusespecular.png"); - textureC.SetNormal("../media/flat_normal.png"); - desc.AddTexture(textureC); - - auto heightmapGeom = _scene->CreateHeightmap(desc); - - auto vis = _scene->CreateVisual(); - vis->AddGeometry(heightmapGeom); - root->AddChild(vis); -//! [create a heightmap] - -//! [create another heightmap] - auto data2 = std::make_shared(); - data2->Load(common::joinPaths(RESOURCE_PATH, "city_terrain.jpg")); - - HeightmapDescriptor desc2; - desc2.SetName("example_city"); - desc2.SetData(data2); - desc2.SetSize({26, 26, 20}); - desc2.SetSampling(2u); - desc2.SetUseTerrainPaging(true); - - HeightmapTexture textureA2; - textureA2.SetSize(1.0); - textureA2.SetDiffuse("../media/fungus_diffusespecular.png"); - textureA2.SetNormal("../media/flat_normal.png"); - desc2.AddTexture(textureA2); - - HeightmapBlend blendA2; - blendA2.SetMinHeight(2.0); - blendA2.SetFadeDistance(5.0); - desc2.AddBlend(blendA2); - - HeightmapTexture textureB2; - textureB2.SetSize(1.0); - textureB2.SetDiffuse("../media/grass_diffusespecular.png"); - textureB2.SetNormal("../media/flat_normal.png"); - desc2.AddTexture(textureB2); - - HeightmapBlend blendB2; - blendB2.SetMinHeight(8.0); - blendB2.SetFadeDistance(5.0); - desc2.AddBlend(blendB2); - - HeightmapTexture textureC2; - textureC2.SetSize(1.0); - textureC2.SetDiffuse("../media/dirt_diffusespecular.png"); - textureC2.SetNormal("../media/flat_normal.png"); - desc2.AddTexture(textureC2); - desc2.SetPosition({30, 0, 0}); - auto heightmapGeom2 = _scene->CreateHeightmap(desc2); - - auto vis2 = _scene->CreateVisual(); - vis2->AddGeometry(heightmapGeom2); - root->AddChild(vis2); -//! [create another heightmap] + if (_buildDemScene) + createDemHeightmaps(_scene, root); + else + createImageHeightmaps(_scene, root); // create gray material MaterialPtr gray = _scene->CreateMaterial(); @@ -193,7 +278,8 @@ void buildScene(ScenePtr _scene) ////////////////////////////////////////////////// CameraPtr createCamera(const std::string &_engineName, - const std::map& _params) + const std::map& _params, + bool _buildDemScene) { // create and populate scene RenderEngine *engine = rendering::engine(_engineName, _params); @@ -204,7 +290,7 @@ CameraPtr createCamera(const std::string &_engineName, return CameraPtr(); } ScenePtr scene = engine->CreateScene("scene"); - buildScene(scene); + buildScene(scene, _buildDemScene); // return camera sensor SensorPtr sensor = scene->SensorByName("camera"); @@ -220,16 +306,26 @@ int main(int _argc, char** _argv) std::vector engineNames; std::vector cameras; + int buildDemScene = 0; + for (int i = 1; i < _argc; ++i) + { + if (std::string(_argv[i]) == "--dem") + { + buildDemScene = i; + break; + } + } + // Expose engine name to command line because we can't instantiate both // ogre and ogre2 at the same time std::string ogreEngineName("ogre2"); - if (_argc > 1) + if (_argc > 1 && buildDemScene != 1) { ogreEngineName = _argv[1]; } GraphicsAPI graphicsApi = GraphicsAPI::OPENGL; - if (_argc > 2) + if (_argc > 2 && buildDemScene != 2) { graphicsApi = GraphicsAPIUtils::Set(std::string(_argv[2])); } @@ -247,7 +343,7 @@ int main(int _argc, char** _argv) params["metal"] = "1"; } - CameraPtr camera = createCamera(engineName, params); + CameraPtr camera = createCamera(engineName, params, buildDemScene); if (camera) { cameras.push_back(camera); diff --git a/examples/heightmap/media/moon.tif b/examples/heightmap/media/moon.tif new file mode 100644 index 000000000..955f14da8 Binary files /dev/null and b/examples/heightmap/media/moon.tif differ diff --git a/examples/heightmap/media/moon_diffuse.png b/examples/heightmap/media/moon_diffuse.png new file mode 100644 index 000000000..3e8e2e6d0 Binary files /dev/null and b/examples/heightmap/media/moon_diffuse.png differ diff --git a/examples/heightmap/media/moon_normal.png b/examples/heightmap/media/moon_normal.png new file mode 100644 index 000000000..35dd4405b Binary files /dev/null and b/examples/heightmap/media/moon_normal.png differ diff --git a/examples/heightmap/media/volcano.tif b/examples/heightmap/media/volcano.tif new file mode 100644 index 000000000..fa51cbc23 Binary files /dev/null and b/examples/heightmap/media/volcano.tif differ diff --git a/include/ignition/rendering/HeightmapDescriptor.hh b/include/ignition/rendering/HeightmapDescriptor.hh index c1b65ce19..baf35577e 100644 --- a/include/ignition/rendering/HeightmapDescriptor.hh +++ b/include/ignition/rendering/HeightmapDescriptor.hh @@ -19,7 +19,7 @@ #include #include -#include +#include #include #include "ignition/rendering/config.hh" @@ -186,11 +186,11 @@ inline namespace IGNITION_RENDERING_VERSION_NAMESPACE { /// \param[in] _data New data. public: void SetData(const std::shared_ptr &_data); - /// \brief Get the heightmap's scaling factor. + /// \brief Get the heightmap's final size in world units. /// \return The heightmap's size. public: ignition::math::Vector3d Size() const; - /// \brief Set the heightmap's scaling factor. Defaults to 1x1x1. + /// \brief Set the heightmap's final size in world units. Defaults to 1x1x1. /// \return The heightmap's size factor. public: void SetSize(const ignition::math::Vector3d &_size); diff --git a/ogre/src/OgreHeightmap.cc b/ogre/src/OgreHeightmap.cc index d43f4da3f..197bf8078 100644 --- a/ogre/src/OgreHeightmap.cc +++ b/ogre/src/OgreHeightmap.cc @@ -459,11 +459,9 @@ void OgreHeightmap::Init() // There is an issue with OGRE terrain LOD if heights are not relative to 0. // So we move the heightmap so that its min elevation = 0 before feeding to // ogre. It is later translated back by the setOrigin call. - double minElevation = 0.0; - - // TODO(chapulina) add a virtual HeightmapData::MinElevation function to - // avoid the ifdef check. i.e. heightmapSizeZ = MaxElevation - MinElevation - double heightmapSizeZ = this->descriptor.Data()->MaxElevation(); + double minElevation = this->descriptor.Data()->MinElevation(); + double heightmapSizeZ = + this->descriptor.Data()->MaxElevation() - minElevation; // \todo These parameters shouldn't be hardcoded, and instead parametrized so // that they can be made consistent across different libraries (like diff --git a/ogre2/src/Ogre2Heightmap.cc b/ogre2/src/Ogre2Heightmap.cc index 04273b12b..e89d638f9 100644 --- a/ogre2/src/Ogre2Heightmap.cc +++ b/ogre2/src/Ogre2Heightmap.cc @@ -158,8 +158,8 @@ void Ogre2Heightmap::Init() // Obtain min and max elevation and bring everything to range [0; 1] // Terra should support non-normalized ranges but there are a couple // bugs preventing that, so it's just easier to normalize the data - float minElevation = 0.0; - float maxElevation = 0.0; + double minElevation = this->descriptor.Data()->MinElevation(); + double maxElevation = this->descriptor.Data()->MaxElevation(); for (unsigned int y = 0; y < newWidth; ++y) { @@ -167,8 +167,12 @@ void Ogre2Heightmap::Init() { const size_t index = y * srcWidth + x; const float heightVal = lookup[index]; - minElevation = std::min(minElevation, heightVal); - maxElevation = std::max(maxElevation, heightVal); + if (heightVal < minElevation || heightVal > maxElevation) + { + ignerr << "Internal error: height [" << heightVal + << "] is out of bounds [" << minElevation << " / " + << maxElevation << "]" << std::endl; + } this->dataPtr->heights.push_back(heightVal); } } @@ -205,13 +209,12 @@ void Ogre2Heightmap::Init() 1u, Ogre::TextureTypes::Type2D, Ogre::PFG_R32_FLOAT, false); - const math::Vector3d newSize = this->descriptor.Size() * - math::Vector3d(1.0, 1.0, heightDiff); + const math::Vector3d size = this->descriptor.Size(); math::Vector3d center( this->descriptor.Position().X(), this->descriptor.Position().Y(), - this->descriptor.Position().Z() + newSize.Z() * 0.5 + minElevation); + this->descriptor.Position().Z() + size.Z() * 0.5 + minElevation); Ogre::Root *ogreRoot = Ogre2RenderEngine::Instance()->OgreRoot(); Ogre::SceneManager *ogreSceneManager = ogreScene->OgreSceneManager(); @@ -230,7 +233,7 @@ void Ogre2Heightmap::Init() this->dataPtr->terra->load( image, Ogre2Conversions::Convert(center), - Ogre2Conversions::Convert(newSize), + Ogre2Conversions::Convert(size), this->descriptor.Name()); this->dataPtr->autoSkirtValue = this->dataPtr->terra->getCustomSkirtMinHeight(); @@ -271,8 +274,8 @@ void Ogre2Heightmap::Init() using namespace Ogre; const HeightmapTexture *texture0 = this->descriptor.TextureByIndex(0); if (texture0->Normal().empty() && - abs(newSize.X() - texture0->Size()) < 1e-6 && - abs(newSize.Y() - texture0->Size()) < 1e-6 ) + abs(size.X() - texture0->Size()) < 1e-6 && + abs(size.Y() - texture0->Size()) < 1e-6 ) { bCanUseFirstAsBase = true; } @@ -302,9 +305,9 @@ void Ogre2Heightmap::Init() texture0->Normal(), &samplerblock); const float sizeX = - static_cast(newSize.X() / texture0->Size()); + static_cast(size.X() / texture0->Size()); const float sizeY = - static_cast(newSize.Y() / texture0->Size()); + static_cast(size.Y() / texture0->Size()); if (!texture0->Diffuse().empty() || !texture0->Normal().empty()) datablock->setDetailMapOffsetScale(0, Vector4(0, 0, sizeX, sizeY)); } @@ -323,9 +326,9 @@ void Ogre2Heightmap::Init() texture->Normal(), &samplerblock); const float sizeX = - static_cast(newSize.X() / texture->Size()); + static_cast(size.X() / texture->Size()); const float sizeY = - static_cast(newSize.Y() / texture->Size()); + static_cast(size.Y() / texture->Size()); if (!texture->Diffuse().empty() || !texture->Normal().empty()) { datablock->setDetailMapOffsetScale( diff --git a/src/Heightmap_TEST.cc b/src/Heightmap_TEST.cc index b7f17387a..84b626d64 100644 --- a/src/Heightmap_TEST.cc +++ b/src/Heightmap_TEST.cc @@ -18,7 +18,7 @@ #include #include -#include +#include #include #include "test_config.h" // NOLINT(build/include) diff --git a/tutorials/21_heightmap.md b/tutorials/21_heightmap.md index 8a2431d95..ee9adef14 100644 --- a/tutorials/21_heightmap.md +++ b/tutorials/21_heightmap.md @@ -1,8 +1,8 @@ \page heightmap Heightmap -This example shows how to add a heigntmap to the scene. +This example shows how to add a heightmap to the scene. -It loads 2 different heightmaps with different parameters. +It loads 2 different heightmaps (image and Digital Elevation Model (DEM)) with different parameters. ## Compile and run the example @@ -16,7 +16,7 @@ cd build cmake .. make ``` -Execute the example: +Example 1 (image heightmap): ```{.sh} ./heightmap @@ -35,11 +35,21 @@ You'll see: ``` @image html img/heightmaps.png +Example 2 (DEM heightmap): + +```{.sh} +./heightmap --dem +``` + +@image html img/heightmaps_dem.png + + ## Code -A heightmap is a terrain defined by a 2D grid of height values. This demo -loads the heights from a grayscale image, where the color black represents -the lowest point, and white represents the highest. +A heightmap is a terrain defined by a 2D grid of height values. +The example 1 demo, loads the heights from a grayscale image, where the color +black represents the lowest point and white represents the heights. +Example 2, loads the heights from the DEM file itself. The heightmap's information is stored in the `HeightmapDescriptor` class. In addition to the height data, the heightmap descriptor also exposes @@ -50,7 +60,17 @@ functionality such as: * The textures to use according to the height. * How to blend these textures. -Here's the snippet of code from `examples/heightmap/Main.cc` that adds a heightmap -to the scene: +Here's the snippet of code from `examples/heightmap/Main.cc` that adds an +image heightmap to the scene: + +\snippet examples/heightmap/Main.cc create an image heightmap + +And a snippet that adds a DEM heightmap to the scene: + +\snippet examples/heightmap/Main.cc create another dem heightmap + +Note, since DEMs encode elevation data, the minimum elevation for the `moon.tif` +DEM is `-212.296`. We need to translate the DEM for it to be at `z = 0` by +setting the descriptor's z position to be `212.296` +or `desc2.SetPosition({0, 0, std::abs(data2->MinElevation())});`. -\snippet examples/heightmap/Main.cc create a heightmap diff --git a/tutorials/img/heightmaps_dem.png b/tutorials/img/heightmaps_dem.png new file mode 100644 index 000000000..4727166d4 Binary files /dev/null and b/tutorials/img/heightmaps_dem.png differ