Skip to content

Commit

Permalink
Merge branch 'master' of github.com:gwaldron/osgearth
Browse files Browse the repository at this point in the history
  • Loading branch information
gwaldron committed May 16, 2024
2 parents 997fd6f + 4a788c1 commit dee9a65
Show file tree
Hide file tree
Showing 33 changed files with 290 additions and 49 deletions.
4 changes: 4 additions & 0 deletions src/applications/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ IF(NOT OSGEARTH_BUILD_PLATFORM_IPHONE)
if(OSGEARTH_BUILD_LEGACY_SPLAT_NODEKIT)
add_subdirectory(osgearth_exportgroundcover_splat)
endif()

if (OSGEARTH_HAVE_MESH_OPTIMIZER)
add_subdirectory(osgearth_lod)
endif()
ENDIF()

# Examples:
Expand Down
7 changes: 7 additions & 0 deletions src/applications/osgearth_lod/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
INCLUDE_DIRECTORIES(${OSG_INCLUDE_DIRS} )
SET(TARGET_LIBRARIES_VARS OSG_LIBRARY OSGDB_LIBRARY OSGUTIL_LIBRARY OSGSIM_LIBRARY OSGSHADOW_LIBRARY OSGVIEWER_LIBRARY OSGGA_LIBRARY OSGTEXT_LIBRARY OSGMANIPULATOR_LIBRARY OPENTHREADS_LIBRARY)

SET(TARGET_SRC osgearth_lod.cpp )

#### end var setup ###
SETUP_APPLICATION(osgearth_lod)
60 changes: 60 additions & 0 deletions src/applications/osgearth_lod/osgearth_lod.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* -*-c++-*- */
/* osgEarth - Geospatial SDK for OpenSceneGraph
* Copyright 2020 Pelican Mapping
* http://osgearth.org
*
* osgEarth is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
#define LC "[osgearth_lod] "

#include <osgEarth/Notify>
#include <osgEarth/LODGenerator>

#include <osg/ArgumentParser>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>

using namespace osgEarth;

int
main(int argc, char** argv)
{
osg::ArgumentParser args(&argc, argv);

// Read the ouptut filename
std::string outFilename = "out.osgb";
args.read("--out", outFilename);

// Read the levels from the command line, formatted like --level threshold,range,aggressive
std::vector< LODGenerator::LODOptions> options;
float threshold = 0.0f;
float range = 0.0f;
bool aggressive = false;
while (args.read("--level", threshold, range, aggressive))
{
OE_NOTICE << "Adding level " << threshold << ", " << range << ", " << aggressive << std::endl;
options.push_back({ threshold, range, aggressive });
}

osg::ref_ptr<osg::Node> root = osgDB::readRefNodeFiles(args);

LODGenerator generator;
osg::ref_ptr< osg::Node> result = generator.generateLODs(root.get(), options);

osgDB::writeNodeFile(*result, outFilename);
return 0;
}
2 changes: 2 additions & 0 deletions src/osgEarth/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ SET(LIB_PUBLIC_HEADERS
LocalGeometryNode
LocalTangentPlane
Locators
LODGenerator
LogarithmicDepthBuffer
Map
MapboxGLGlyphManager
Expand Down Expand Up @@ -694,6 +695,7 @@ set(TARGET_SRC
LineSymbol.cpp
LocalGeometryNode.cpp
LocalTangentPlane.cpp
LODGenerator.cpp
LogarithmicDepthBuffer.cpp
Map.cpp
MapboxGLGlyphManager.cpp
Expand Down
12 changes: 4 additions & 8 deletions src/osgEarth/CompressedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,7 @@ namespace osgEarth {
if (g.getQuantization() == osgEarth::CompressedVec3Array::QUANTIZE_VERTEX)
{
std::vector<osg::Vec4us> decoded(size);
int resvb = meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(osg::Vec4us), &vbuf[0], vbuf.size());
assert(resvb == 0 && resib == 0);
meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(osg::Vec4us), &vbuf[0], vbuf.size());

for (unsigned int i = 0; i < size; ++i)
{
Expand All @@ -166,8 +165,7 @@ namespace osgEarth {
if (g.getQuantization() == osgEarth::CompressedVec3Array::QUANTIZE_NORMAL)
{
std::vector<unsigned int> decoded(size);
int resvb = meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(unsigned int), &vbuf[0], vbuf.size());
assert(resvb == 0 && resib == 0);
meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(unsigned int), &vbuf[0], vbuf.size());

for (unsigned int i = 0; i < size; ++i)
{
Expand All @@ -189,8 +187,7 @@ namespace osgEarth {
else if (g.getQuantization() == osgEarth::CompressedVec3Array::QUANTIZE_HALF)
{
std::vector<osg::Vec4us> decoded(size);
int resvb = meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(osg::Vec4us), &vbuf[0], vbuf.size());
assert(resvb == 0 && resib == 0);
meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(osg::Vec4us), &vbuf[0], vbuf.size());
for (unsigned int i = 0; i < decoded.size(); ++i)
{
osg::Vec3 v(
Expand Down Expand Up @@ -334,8 +331,7 @@ namespace osgEarth {


std::vector<osg::Vec2us> decoded(size);
int resvb = meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(osg::Vec2us), &vbuf[0], vbuf.size());
assert(resvb == 0 && resib == 0);
meshopt_decodeVertexBuffer(&decoded[0], decoded.size(), sizeof(osg::Vec2us), &vbuf[0], vbuf.size());
for (unsigned int i = 0; i < decoded.size(); ++i)
{
osg::Vec2 v(
Expand Down
33 changes: 33 additions & 0 deletions src/osgEarth/LODGenerator
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef OSGEARTH_LODGENERATOR_H
#define OSGEARTH_LODGENERATOR_H

#include <osgEarth/Common>
#include <osg/Node>

#ifdef OSGEARTH_HAVE_MESH_OPTIMIZER


namespace osgEarth
{
using namespace osgEarth;

class OSGEARTH_EXPORT LODGenerator
{
public:
struct LODOptions
{
float threshold;
float rangeFactor;
bool aggressive;
};

osg::Node* generateLODs(osg::Node* node, const std::vector<LODOptions>& options);
};


}
#else
#pragma message("Warning: MeshOptimizer not available. LODGenerator will not be available.")
#endif

#endif
140 changes: 140 additions & 0 deletions src/osgEarth/LODGenerator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#include <osgEarth/LODGenerator>

#ifdef OSGEARTH_HAVE_MESH_OPTIMIZER

#include <osgEarth/NodeUtils>
#include <osgUtil/Optimizer>

#include <meshoptimizer.h>

using namespace osgEarth;

osg::LOD* createLODFromGeometry(osg::Geometry* originalGeometry, const std::vector<LODGenerator::LODOptions>& options)
{
double radius = originalGeometry->getBound().radius();

// Turn the geometry into an indexed mesh.
osgUtil::Optimizer o;
o.optimize(originalGeometry,
o.INDEX_MESH |
o.VERTEX_PRETRANSFORM |
o.VERTEX_POSTTRANSFORM);

osg::ref_ptr<osg::LOD> lodNode = new osg::LOD();

osg::Vec3Array* vertices = dynamic_cast<osg::Vec3Array*>(originalGeometry->getVertexArray());
osg::DrawElements* drawElements = dynamic_cast<osg::DrawElements*>(originalGeometry->getPrimitiveSet(0));

if (!vertices || !drawElements) {
OE_WARN << "Invalid geometry data." << std::endl;
return nullptr;
}

size_t vertexCount = vertices->size();
size_t indexCount = drawElements->getNumIndices();
std::vector<unsigned int> indices(indexCount);
std::vector<float> vertexData(vertexCount * 3);

for (size_t i = 0; i < indexCount; ++i) {
indices[i] = drawElements->getElement(i);
}
for (size_t i = 0; i < vertexCount; ++i) {
vertexData[i * 3 + 0] = (*vertices)[i].x();
vertexData[i * 3 + 1] = (*vertices)[i].y();
vertexData[i * 3 + 2] = (*vertices)[i].z();
}


// Add the highest level of detail first
float minDistance = 0.0f;
float maxDistance = options[0].rangeFactor * radius;
lodNode->addChild(originalGeometry, minDistance, maxDistance);

float prevMaxDistance = maxDistance;

for (unsigned int level = 0; level < options.size(); ++level)
{
const LODGenerator::LODOptions& opt = options[level];

float target_error = 1e-2f;
float target_error_aggressive = 1e-1f;

size_t target_index_count = size_t(double(indexCount / 3) * opt.threshold) * 3;

unsigned int mesh_opt_flags = 0;

std::vector< unsigned int > newIndices(indices.size());
unsigned int newIndicesCount = meshopt_simplify(&newIndices[0], &indices[0], indexCount, &vertexData[0], vertexCount, sizeof(float) * 3, target_index_count, target_error, mesh_opt_flags);

if (opt.aggressive && newIndicesCount > target_index_count) {
newIndicesCount = meshopt_simplifySloppy(&newIndices[0], &indices[0], indexCount, &vertexData[0], vertexCount, sizeof(float) * 3, target_index_count, target_error_aggressive);
}

OE_INFO << "Simplified from " << indexCount / 3 << " triangles to target=" << target_index_count / 3 << " actual=" << newIndicesCount / 3 << " triangles" << std::endl;

newIndices.resize(newIndicesCount);

osg::ref_ptr<osg::Geometry> simplifiedGeometry = new osg::Geometry(*originalGeometry, osg::CopyOp::SHALLOW_COPY);
osg::ref_ptr<osg::DrawElementsUInt> newDrawElements = new osg::DrawElementsUInt(GL_TRIANGLES);


for (size_t i = 0; i < newIndices.size(); ++i) {
newDrawElements->push_back(newIndices[i]);
}

simplifiedGeometry->setVertexArray(vertices);
simplifiedGeometry->setPrimitiveSet(0, newDrawElements);

float minDistance = prevMaxDistance;
float maxDistance = level == options.size() - 1 ? FLT_MAX : options[level + 1].rangeFactor * radius;

lodNode->addChild(simplifiedGeometry, minDistance, maxDistance);
prevMaxDistance = maxDistance;
}

return lodNode.release();
}


osg::Node* LODGenerator::generateLODs(osg::Node* node, const std::vector<LODOptions>& options)
{
FindNodesVisitor<osg::Geometry> nv;
node->accept(nv);

for (osg::Geometry* geom : nv._results)
{
osg::LOD* lod = createLODFromGeometry(geom, options);
if (lod)
{
osg::Group* parent = geom->getParent(0);
if (parent)
{
osg::Geode* geode = parent->asGeode();
if (geode)
{
lod->setStateSet(geode->getStateSet());
if (geode->getNumParents() == 0)
{
return lod;
}
else
{
geode->getParent(0)->replaceChild(geode, lod);
}
}
else
{
parent->replaceChild(geom, lod);
}
}
else
{
return lod;
}
}
}

return node;
}

#endif // OSGEARTH_HAVE_MESH_OPTIMIZER
3 changes: 1 addition & 2 deletions src/osgEarth/TileVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,14 +286,13 @@ bool MultithreadedTileVisitor::handleTile(const TileKey& key)
}

// Add the tile to the task queue.
auto task = [this, key](Cancelable&)
auto task = [this, key]()
{
if ((_tileHandler.valid()) && (!_progress.valid() || !_progress->isCanceled()))
{
_tileHandler->handleTile(key, *this);
this->incrementProgress(1);
}
return true;
};

jobs::context job;
Expand Down
2 changes: 1 addition & 1 deletion tests/boston.earth
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ to extruded buildings.
<map name="Boston Demo">

<TMSImage name="Imagery">
<url>http://readymap.org/readymap/tiles/1.0.0/22/</url>
<url>https://readymap.org/readymap/tiles/1.0.0/22/</url>
</TMSImage>

<xi:include href="readymap_elevation.xml"/>
Expand Down
2 changes: 1 addition & 1 deletion tests/boston_projected.earth
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ osgEarth Sample. Like boston.earth, but on a projected map
</options>

<TMSImage name="Imagery">
<url>http://readymap.org/readymap/tiles/1.0.0/22/</url>
<url>https://readymap.org/readymap/tiles/1.0.0/22/</url>
</TMSImage>

<xi:include href="readymap_elevation.xml"/>
Expand Down
8 changes: 4 additions & 4 deletions tests/constraints.earth
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@
</options>

<TMSImage name="ReadyMap 15m Imagery" enabled="true">
<url>http://readymap.org/readymap/tiles/1.0.0/7/</url>
<url>https://readymap.org/readymap/tiles/1.0.0/7/</url>
</TMSImage>

<TMSElevation name="ReadyMap 90m Elevation" enabled="true">
<url>http://readymap.org/readymap/tiles/1.0.0/116/</url>
<url>https://readymap.org/readymap/tiles/1.0.0/116/</url>
<vdatum>egm96</vdatum>
</TMSElevation>

<XYZFeatures name="data:osm-water">
<url>http://readymap.org/readymap/mbtiles/osm-water/{z}/{x}/{-y}.pbf</url>
<url>https://readymap.org/readymap/mbtiles/osm-water/{z}/{x}/{-y}.pbf</url>
<min_level>0</min_level>
<max_level>12</max_level>
<profile>spherical-mercator</profile>
Expand All @@ -40,7 +40,7 @@
</FeatureImage>

<XYZFeatures name="data:osm-roads">
<url>http://readymap.org/readymap/mbtiles/osm/{z}/{x}/{-y}.pbf</url>
<url>https://readymap.org/readymap/mbtiles/osm/{z}/{x}/{-y}.pbf</url>
<min_level>10</min_level>
<max_level>14</max_level>
<profile>spherical-mercator</profile>
Expand Down
2 changes: 1 addition & 1 deletion tests/day_night.earth
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<xi:include href="readymap_imagery.xml"/>

<TMSImage name="EarthAtNight">
<url>http://readymap.org/readymap/tiles/1.0.0/26/</url>
<url>https://readymap.org/readymap/tiles/1.0.0/26/</url>
<shader>
<![CDATA[
#pragma vp_function dayNight, fragment
Expand Down
2 changes: 1 addition & 1 deletion tests/feature_tfs_scripting.earth
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ osgEarth Sample - TFS
<map name="Demo: TFS scripting">

<TFSFeatures name="buildings-data">
<url>http://readymap.org/readymap/features/tfs/4/</url>
<url>https://readymap.org/readymap/features/tfs/4/</url>
<format>json</format>
</TFSFeatures>

Expand Down

0 comments on commit dee9a65

Please sign in to comment.