[Git][debian-gis-team/osgearth][experimental] 5 commits: New upstream version 2.9~rc3+dfsg
Sebastiaan Couwenberg
gitlab at salsa.debian.org
Fri Feb 2 09:28:46 UTC 2018
Sebastiaan Couwenberg pushed to branch experimental at Debian GIS Project / osgearth
Commits:
0d45333a by Bas Couwenberg at 2018-02-02T07:21:28+01:00
New upstream version 2.9~rc3+dfsg
- - - - -
12ecb125 by Bas Couwenberg at 2018-02-02T07:22:32+01:00
Merge tag 'upstream/2.9_rc3+dfsg' into experimental
Upstream version 2.9~rc3+dfsg
- - - - -
57078423 by Bas Couwenberg at 2018-02-02T07:22:55+01:00
New upstream release candidate.
- - - - -
74f162ea by Bas Couwenberg at 2018-02-02T08:59:24+01:00
Update symbols for amd64.
- - - - -
6b46af98 by Bas Couwenberg at 2018-02-02T08:59:24+01:00
Set distribution to experimental.
- - - - -
28 changed files:
- debian/changelog
- debian/libosgearth5.symbols
- debian/libosgearthutil5.symbols
- src/osgEarth/ImageUtils
- src/osgEarth/ImageUtils.cpp
- src/osgEarth/MapNode
- src/osgEarth/MapNode.cpp
- src/osgEarth/ModelLayer.cpp
- src/osgEarth/TerrainLayer
- src/osgEarth/Version
- src/osgEarth/VirtualProgram
- src/osgEarth/VirtualProgram.cpp
- src/osgEarthDrivers/cache_leveldb/LevelDBCacheBin.cpp
- src/osgEarthDrivers/cache_rocksdb/RocksDBCacheBin.cpp
- src/osgEarthDrivers/engine_mp/MPGeometry
- src/osgEarthDrivers/engine_mp/MPGeometry.cpp
- src/osgEarthDrivers/engine_rex/GeometryPool
- src/osgEarthDrivers/engine_rex/GeometryPool.cpp
- src/osgEarthDrivers/engine_rex/MaskGenerator
- src/osgEarthDrivers/engine_rex/MaskGenerator.cpp
- src/osgEarthDrivers/engine_rex/RexEngine.elevation.glsl
- src/osgEarthDrivers/engine_rex/RexTerrainEngineNode
- src/osgEarthDrivers/engine_rex/RexTerrainEngineNode.cpp
- src/osgEarthDrivers/mbtiles/MBTilesTileSource.cpp
- src/osgEarthSymbology/Geometry.cpp
- src/osgEarthUtil/Controls.cpp
- src/osgEarthUtil/TopologyGraph
- src/osgEarthUtil/TopologyGraph.cpp
Changes:
=====================================
debian/changelog
=====================================
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+osgearth (2.9~rc3+dfsg-1~exp1) experimental; urgency=medium
+
+ * New upstream release candidate.
+ * Update symbols for amd64.
+
+ -- Bas Couwenberg <sebastic at debian.org> Fri, 02 Feb 2018 07:23:55 +0100
+
osgearth (2.9~rc2+dfsg-1~exp1) experimental; urgency=medium
* New upstream release candidate.
=====================================
debian/libosgearth5.symbols
=====================================
--- a/debian/libosgearth5.symbols
+++ b/debian/libosgearth5.symbols
@@ -1,4 +1,4 @@
-# SymbolsHelper-Confirmed: 2.9~rc2 amd64
+# SymbolsHelper-Confirmed: 2.9~rc3 amd64
libosgEarth.so.5 #PACKAGE# #MINVER#
_Z10TiXmlFOpenPKcS0_ at Base 2.4.0
_ZGVZN13DeclutterSort18sortImplementationEPN7osgUtil9RenderBinEE8s_zero_w at Base 2.5.0
@@ -448,6 +448,7 @@ libosgEarth.so.5 #PACKAGE# #MINVER#
_ZN8osgEarth10ImageLayerD2Ev at Base 2.4.0
_ZN8osgEarth10ImageUtils10canConvertEPKN3osg5ImageEjj at Base 2.4.0
_ZN8osgEarth10ImageUtils10cloneImageEPKN3osg5ImageE at Base 2.4.0
+ _ZN8osgEarth10ImageUtils10readStreamERSiPKN5osgDB7OptionsE at Base 2.9~rc3
_ZN8osgEarth10ImageUtils10sameFormatEPKN3osg5ImageES4_ at Base 2.6.0
_ZN8osgEarth10ImageUtils10upSampleNNEPKN3osg5ImageEi at Base 2.7.0
_ZN8osgEarth10ImageUtils11PixelReader8setImageEPKN3osg5ImageE at Base 2.9~rc1
@@ -483,6 +484,7 @@ libosgEarth.so.5 #PACKAGE# #MINVER#
_ZN8osgEarth10ImageUtils20createSharpenedImageEPKN3osg5ImageE at Base 2.5.0
_ZN8osgEarth10ImageUtils22textureArrayCompatibleEPKN3osg5ImageES4_ at Base 2.7.0
_ZN8osgEarth10ImageUtils24createMipmapBlendedImageEPKN3osg5ImageES4_ at Base 2.4.0
+ _ZN8osgEarth10ImageUtils24getReaderWriterForStreamERSi at Base 2.9~rc3
_ZN8osgEarth10ImageUtils27buildNearestNeighborMipmapsEPKN3osg5ImageE at Base 2.7.0
_ZN8osgEarth10ImageUtils27convertToPremultipliedAlphaEPN3osg5ImageE at Base 2.4.0
_ZN8osgEarth10ImageUtils29computeTextureCompressionModeEPKN3osg5ImageERNS1_7Texture18InternalFormatModeE at Base 2.6.0
@@ -2868,6 +2870,7 @@ libosgEarth.so.5 #PACKAGE# #MINVER#
_ZN8osgEarth7MapNode15removeExtensionEPNS_9ExtensionE at Base 2.7.0
_ZN8osgEarth7MapNode17getDrapingManagerEv at Base 2.9~rc1
_ZN8osgEarth7MapNode18getClampingManagerEv at Base 2.9~rc1
+ _ZN8osgEarth7MapNode21resizeGLObjectBuffersEj at Base 2.9~rc3
_ZN8osgEarth7MapNode4initEv at Base 2.4.0
_ZN8osgEarth7MapNode4loadERN3osg14ArgumentParserE at Base 2.4.0
_ZN8osgEarth7MapNode4loadERN3osg14ArgumentParserERKNS_14MapNodeOptionsE at Base 2.8~rc1
@@ -3810,6 +3813,7 @@ libosgEarth.so.5 #PACKAGE# #MINVER#
_ZNK8osgEarth10PatchLayer5cloneERKN3osg6CopyOpE at Base 2.9~rc1
_ZNK8osgEarth10PatchLayer9classNameEv at Base 2.9~rc1
_ZNK8osgEarth10PatchLayer9cloneTypeEv at Base 2.9~rc1
+ _ZNK8osgEarth10PolyShader16releaseGLObjectsEPN3osg5StateE at Base 2.9~rc3
_ZNK8osgEarth10PolyShader9getShaderEj at Base 2.8~rc1
_ZNK8osgEarth10Revisioned10inSyncWithERKNS_8RevisionE at Base 2.4.0
_ZNK8osgEarth10Revisioned4syncERNS_8RevisionE at Base 2.4.0
@@ -4495,6 +4499,7 @@ libosgEarth.so.5 #PACKAGE# #MINVER#
_ZNK8osgEarth7MapNode12getLayerNodeEPNS_5LayerE at Base 2.9~rc1
_ZNK8osgEarth7MapNode12isGeocentricEv at Base 2.4.0
_ZNK8osgEarth7MapNode16getTerrainEngineEv at Base 2.4.0
+ _ZNK8osgEarth7MapNode16releaseGLObjectsEPN3osg5StateE at Base 2.9~rc3
_ZNK8osgEarth7MapNode17getLayerNodeGroupEv at Base 2.9~rc1
_ZNK8osgEarth7MapNode17getMapNodeOptionsEv at Base 2.4.0
_ZNK8osgEarth7MapNode19getResourceReleaserEv at Base 2.9~rc1
=====================================
debian/libosgearthutil5.symbols
=====================================
--- a/debian/libosgearthutil5.symbols
+++ b/debian/libosgearthutil5.symbols
@@ -1,4 +1,4 @@
-# SymbolsHelper-Confirmed: 2.9~rc2 amd64
+# SymbolsHelper-Confirmed: 2.9~rc3 amd64
libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_Z10intersectsRKdS0_S0_S0_S0_S0_S0_S0_ at Base 2.4.0
_Z17getHorizSRSStringB5cxx11PKN8osgEarth16SpatialReferenceE at Base 2.7.0
@@ -559,9 +559,12 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_ZN8osgEarth4Util13TMSBackFiller9writeTileERKNS_7TileKeyEPN3osg5ImageE at Base 2.4.0
_ZN8osgEarth4Util13TMSBackFillerC1Ev at Base 2.4.0
_ZN8osgEarth4Util13TMSBackFillerC2Ev at Base 2.4.0
- _ZN8osgEarth4Util13TopologyGraph12dumpBoundaryERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE at Base 2.9~rc1
+ _ZN8osgEarth4Util13TopologyGraph12dumpBoundaryERKSt6vectorISt23_Rb_tree_const_iteratorINS1_6VertexEESaIS5_EERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE at Base 2.9~rc3
_ZN8osgEarth4Util13TopologyGraphC1Ev at Base 2.9~rc1
_ZN8osgEarth4Util13TopologyGraphC2Ev at Base 2.9~rc1
+ _ZN8osgEarth4Util13TopologyGraphD0Ev at Base 2.9~rc3
+ _ZN8osgEarth4Util13TopologyGraphD1Ev at Base 2.9~rc3
+ _ZN8osgEarth4Util13TopologyGraphD2Ev at Base 2.9~rc3
_ZN8osgEarth4Util13VerticalScale11mergeConfigERKNS_6ConfigE at Base 2.5.0
_ZN8osgEarth4Util13VerticalScale11onUninstallEPNS_17TerrainEngineNodeE at Base 2.5.0
_ZN8osgEarth4Util13VerticalScale4initEv at Base 2.5.0
@@ -651,7 +654,9 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_ZN8osgEarth4Util15TFSReaderWriter4readERSiRNS0_8TFSLayerE at Base 2.4.0
_ZN8osgEarth4Util15TFSReaderWriter5writeERKNS0_8TFSLayerERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE at Base 2.7.0
_ZN8osgEarth4Util15TFSReaderWriter5writeERKNS0_8TFSLayerERSo at Base 2.4.0
+ _ZN8osgEarth4Util15TopologyBuilder18assignAndPropagateERSt23_Rb_tree_const_iteratorINS0_13TopologyGraph6VertexEEj at Base 2.9~rc3
_ZN8osgEarth4Util15TopologyBuilder3addEj at Base 2.9~rc1
+ _ZN8osgEarth4Util15TopologyBuilder6createEPKN3osg13TemplateArrayINS2_5Vec3fELNS2_5Array4TypeE28ELi3ELi5126EEEPKNS2_12PrimitiveSetERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE at Base 2.9~rc3
_ZN8osgEarth4Util15TopologyBuilderC1Ev at Base 2.9~rc1
_ZN8osgEarth4Util15TopologyBuilderC2Ev at Base 2.9~rc1
_ZN8osgEarth4Util15TopologyBuilderclEjjj at Base 2.9~rc1
@@ -2079,7 +2084,13 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_ZNK8osgEarth4Util13MapNodeHelper5parseEPNS_7MapNodeERN3osg14ArgumentParserEPN9osgViewer4ViewEPNS4_5GroupEPNS0_8Controls12LabelControlE at Base 2.7.0
_ZNK8osgEarth4Util13MapNodeHelper5parseEPNS_7MapNodeERN3osg14ArgumentParserEPN9osgViewer4ViewEPNS4_5GroupEPNS0_8Controls9ContainerE at Base 2.7.0
_ZNK8osgEarth4Util13MapNodeHelper5usageB5cxx11Ev at Base 2.7.0
- _ZNK8osgEarth4Util13TopologyGraph14createBoundaryERSt6vectorISt23_Rb_tree_const_iteratorINS1_6VertexEESaIS5_EE at Base 2.9~rc1
+ _ZNK8osgEarth4Util13TopologyGraph11libraryNameEv at Base 2.9~rc3
+ _ZNK8osgEarth4Util13TopologyGraph12isSameKindAsEPKN3osg6ObjectE at Base 2.9~rc3
+ _ZNK8osgEarth4Util13TopologyGraph14createBoundaryEjRSt6vectorISt23_Rb_tree_const_iteratorINS1_6VertexEESaIS5_EE at Base 2.9~rc3
+ _ZNK8osgEarth4Util13TopologyGraph16getNumBoundariesEv at Base 2.9~rc3
+ _ZNK8osgEarth4Util13TopologyGraph5cloneERKN3osg6CopyOpE at Base 2.9~rc3
+ _ZNK8osgEarth4Util13TopologyGraph9classNameEv at Base 2.9~rc3
+ _ZNK8osgEarth4Util13TopologyGraph9cloneTypeEv at Base 2.9~rc3
_ZNK8osgEarth4Util13VerticalScale9getConfigEv at Base 2.5.0
_ZNK8osgEarth4Util14HSLColorFilter12getHSLOffsetEv at Base 2.4.0
_ZNK8osgEarth4Util14HSLColorFilter25getEntryPointFunctionNameB5cxx11Ev at Base 2.7.0
@@ -2410,7 +2421,6 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
(optional=templinst)_ZNSt8_Rb_treeIN3osg12observer_ptrIN5osgGA15GUIEventHandlerEEESt4pairIKS4_NS1_IN9osgViewer4ViewEEEESt10_Select1stISA_ESt4lessIS4_ESaISA_EE8_M_eraseEPSt13_Rb_tree_nodeISA_E at Base 2.4.0
(optional=templinst)_ZNSt8_Rb_treeIN7osgUtil22LineSegmentIntersector12IntersectionES2_St9_IdentityIS2_ESt4lessIS2_ESaIS2_EE8_M_eraseEPSt13_Rb_tree_nodeIS2_E at Base 2.4.0
(optional=templinst)_ZNSt8_Rb_treeIN8osgEarth20PrimitiveIntersector12IntersectionES2_St9_IdentityIS2_ESt4lessIS2_ESaIS2_EE8_M_eraseEPSt13_Rb_tree_nodeIS2_E at Base 2.5.0
- (optional=templinst)_ZNSt8_Rb_treeIN8osgEarth4Util13TopologyGraph6VertexES3_St9_IdentityIS3_ESt4lessIS3_ESaIS3_EE16_M_insert_uniqueIRKS3_EESt4pairISt17_Rb_tree_iteratorIS3_EbEOT_ at Base 2.9~rc1
(optional=templinst)_ZNSt8_Rb_treeIN8osgEarth4Util13TopologyGraph6VertexES3_St9_IdentityIS3_ESt4lessIS3_ESaIS3_EE8_M_eraseEPSt13_Rb_tree_nodeIS3_E at Base 2.9~rc1
(optional=templinst)_ZNSt8_Rb_treeIN8osgEarth4Util16EarthManipulator9InputSpecESt4pairIKS3_NS2_6ActionEESt10_Select1stIS7_ESt4lessIS3_ESaIS7_EE24_M_get_insert_unique_posERS5_ at Base 2.4.0
(optional=templinst)_ZNSt8_Rb_treeIN8osgEarth4Util16EarthManipulator9InputSpecESt4pairIKS3_NS2_6ActionEESt10_Select1stIS7_ESt4lessIS3_ESaIS7_EE29_M_get_insert_hint_unique_posESt23_Rb_tree_const_iteratorIS7_ERS5_ at Base 2.4.0
@@ -2473,7 +2483,6 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
(optional=templinst)_ZNSt8_Rb_treeIfSt4pairIKfN3osg7ref_ptrIN8osgEarth4Util8Controls11ControlNodeEEEESt10_Select1stIS9_ESt4lessIfESaIS9_EE8_M_eraseEPSt13_Rb_tree_nodeIS9_E at Base 2.4.0
(optional=templinst|arch=hurd-i386 i386 kfreebsd-i386)_ZNSt8_Rb_treeIfSt4pairIKfN3osg7ref_ptrIN8osgEarth4Util9GeoObjectEEEESt10_Select1stIS8_ESt4lessIfESaIS8_EE15_M_insert_equalIS0_IfPS6_EEESt17_Rb_tree_iteratorIS8_EOT_ at Base 2.8~rc2
(optional=templinst)_ZNSt8_Rb_treeIfSt4pairIKfN3osg7ref_ptrIN8osgEarth4Util9GeoObjectEEEESt10_Select1stIS8_ESt4lessIfESaIS8_EE8_M_eraseEPSt13_Rb_tree_nodeIS8_E at Base 2.4.0
- (optional=templinst)_ZNSt8_Rb_treeIjSt4pairIKjSt23_Rb_tree_const_iteratorIN8osgEarth4Util13TopologyGraph6VertexEEESt10_Select1stIS8_ESt4lessIjESaIS8_EE29_M_get_insert_hint_unique_posES2_IS8_ERS1_ at Base 2.9~rc1
(optional=templinst)_ZNSt8_Rb_treeIjSt4pairIKjSt23_Rb_tree_const_iteratorIN8osgEarth4Util13TopologyGraph6VertexEEESt10_Select1stIS8_ESt4lessIjESaIS8_EE8_M_eraseEPSt13_Rb_tree_nodeIS8_E at Base 2.9~rc1
(optional=templinst|arch=ia64)_ZSt22__uninitialized_move_aIPN8osgEarth4Util3TMS7TileSetES4_SaIS3_EET0_T_S7_S6_RT1_ at Base 2.4.0
(optional=templinst)_ZSt4copyIN3osg7ref_ptrIN8osgEarth4Util8Controls7ControlEEEESt15_Deque_iteratorIT_RS8_PS8_ES7_IS8_RKS8_PSC_ESF_SB_ at Base 2.9~rc1
@@ -2565,6 +2574,7 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_ZTIN8osgEarth4Util13GARSGraticuleE at Base 2.9~rc1
_ZTIN8osgEarth4Util13MGRSFormatterE at Base 2.4.0
_ZTIN8osgEarth4Util13MGRSGraticuleE at Base 2.4.0
+ _ZTIN8osgEarth4Util13TopologyGraphE at Base 2.9~rc3
_ZTIN8osgEarth4Util13VerticalScaleE at Base 2.5.0
_ZTIN8osgEarth4Util14HSLColorFilterE at Base 2.4.0
_ZTIN8osgEarth4Util14RGBColorFilterE at Base 2.4.0
@@ -2794,6 +2804,7 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_ZTSN8osgEarth4Util13GARSGraticuleE at Base 2.9~rc1
_ZTSN8osgEarth4Util13MGRSFormatterE at Base 2.4.0
_ZTSN8osgEarth4Util13MGRSGraticuleE at Base 2.4.0
+ _ZTSN8osgEarth4Util13TopologyGraphE at Base 2.9~rc3
_ZTSN8osgEarth4Util13VerticalScaleE at Base 2.5.0
_ZTSN8osgEarth4Util14HSLColorFilterE at Base 2.4.0
_ZTSN8osgEarth4Util14RGBColorFilterE at Base 2.4.0
@@ -3028,6 +3039,7 @@ libosgEarthUtil.so.5 #PACKAGE# #MINVER#
_ZTVN8osgEarth4Util13GARSGraticuleE at Base 2.9~rc1
_ZTVN8osgEarth4Util13MGRSFormatterE at Base 2.4.0
_ZTVN8osgEarth4Util13MGRSGraticuleE at Base 2.4.0
+ _ZTVN8osgEarth4Util13TopologyGraphE at Base 2.9~rc3
_ZTVN8osgEarth4Util13VerticalScaleE at Base 2.5.0
_ZTVN8osgEarth4Util14HSLColorFilterE at Base 2.4.0
_ZTVN8osgEarth4Util14RGBColorFilterE at Base 2.4.0
=====================================
src/osgEarth/ImageUtils
=====================================
--- a/src/osgEarth/ImageUtils
+++ b/src/osgEarth/ImageUtils
@@ -26,6 +26,7 @@
#include <osg/Texture>
#include <osg/GL>
#include <osg/NodeVisitor>
+#include <osgDB/ReaderWriter>
#include <vector>
//These formats were not added to OSG until after 2.8.3 so we need to define them to use them.
@@ -347,6 +348,18 @@ namespace osgEarth
static void activateMipMaps(osg::Texture* texture);
/**
+ * Gets an osgDB::ReaderWriter for the given input stream.
+ * Returns NULL if no ReaderWriter can be found.
+ */
+ static osgDB::ReaderWriter* getReaderWriterForStream(std::istream& stream);
+
+ /**
+ * Reads an osg::Image from the given input stream.
+ * Returns NULL if the image could not be read.
+ */
+ static osg::Image* readStream(std::istream& stream, const osgDB::Options* options);
+
+ /**
* Reads color data out of an image, regardles of its internal pixel format.
*/
class OSGEARTH_EXPORT PixelReader
=====================================
src/osgEarth/ImageUtils.cpp
=====================================
--- a/src/osgEarth/ImageUtils.cpp
+++ b/src/osgEarth/ImageUtils.cpp
@@ -687,6 +687,82 @@ ImageUtils::createMipmapBlendedImage( const osg::Image* primary, const osg::Imag
return result.release();
}
+osgDB::ReaderWriter*
+ImageUtils::getReaderWriterForStream(std::istream& stream) {
+ // Modified from https://oroboro.com/image-format-magic-bytes/
+
+ // Get the length of the stream
+ stream.seekg(0, std::ios::end);
+ unsigned int len = stream.tellg();
+ stream.seekg(0, std::ios::beg);
+
+ if (len < 16) return 0;
+
+ //const char* data = input.c_str();
+ // Read a 16 byte header
+ char data[16];
+ stream.read(data, 16);
+ // Reset reading
+ stream.seekg(0, std::ios::beg);
+
+ // .jpg: FF D8 FF
+ // .png: 89 50 4E 47 0D 0A 1A 0A
+ // .gif: GIF87a
+ // GIF89a
+ // .tiff: 49 49 2A 00
+ // 4D 4D 00 2A
+ // .bmp: BM
+ // .webp: RIFF ???? WEBP
+ // .ico 00 00 01 00
+ // 00 00 02 00 ( cursor files )
+ switch (data[0])
+ {
+ case '\xFF':
+ return (!strncmp((const char*)data, "\xFF\xD8\xFF", 3)) ?
+ osgDB::Registry::instance()->getReaderWriterForExtension("jpg") : 0;
+
+ case '\x89':
+ return (!strncmp((const char*)data,
+ "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8)) ?
+ osgDB::Registry::instance()->getReaderWriterForExtension("png") : 0;
+
+ case 'G':
+ return (!strncmp((const char*)data, "GIF87a", 6) ||
+ !strncmp((const char*)data, "GIF89a", 6)) ?
+ osgDB::Registry::instance()->getReaderWriterForExtension("gif") : 0;
+
+ case 'I':
+ return (!strncmp((const char*)data, "\x49\x49\x2A\x00", 4)) ?
+ osgDB::Registry::instance()->getReaderWriterForExtension("tif") : 0;
+
+ case 'M':
+ return (!strncmp((const char*)data, "\x4D\x4D\x00\x2A", 4)) ?
+ osgDB::Registry::instance()->getReaderWriterForExtension("tif") : 0;
+
+ case 'B':
+ return ((data[1] == 'M')) ?
+ osgDB::Registry::instance()->getReaderWriterForExtension("bmp") : 0;
+
+ default:
+ return 0;
+ }
+}
+
+osg::Image*
+ImageUtils::readStream(std::istream& stream, const osgDB::Options* options) {
+
+ osgDB::ReaderWriter* rw = getReaderWriterForStream(stream);
+ if (!rw) {
+ return 0;
+ }
+
+ osgDB::ReaderWriter::ReadResult rr = rw->readImage(stream, options);
+ if (rr.validImage()) {
+ return rr.takeImage();
+ }
+ return 0;
+}
+
namespace
{
struct MixImage
=====================================
src/osgEarth/MapNode
=====================================
--- a/src/osgEarth/MapNode
+++ b/src/osgEarth/MapNode
@@ -233,6 +233,10 @@ namespace osgEarth
virtual void traverse( class osg::NodeVisitor& nv );
+ virtual void resizeGLObjectBuffers(unsigned maxSize);
+
+ virtual void releaseGLObjects(osg::State* state) const;
+
protected:
virtual ~MapNode();
=====================================
src/osgEarth/MapNode.cpp
=====================================
--- a/src/osgEarth/MapNode.cpp
+++ b/src/osgEarth/MapNode.cpp
@@ -803,6 +803,32 @@ MapNode::traverse( osg::NodeVisitor& nv )
}
}
+void
+MapNode::resizeGLObjectBuffers(unsigned maxSize)
+{
+ LayerVector layers;
+ getMap()->getLayers(layers);
+ for (LayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i)
+ {
+ if ((*i)->getStateSet()) {
+ (*i)->getStateSet()->resizeGLObjectBuffers(maxSize);
+ }
+ }
+}
+
+void
+MapNode::releaseGLObjects(osg::State* state) const
+{
+ LayerVector layers;
+ getMap()->getLayers(layers);
+ for (LayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i)
+ {
+ if ((*i)->getStateSet()) {
+ (*i)->getStateSet()->releaseGLObjects(state);
+ }
+ }
+}
+
DrapingManager*
MapNode::getDrapingManager()
{
=====================================
src/osgEarth/ModelLayer.cpp
=====================================
--- a/src/osgEarth/ModelLayer.cpp
+++ b/src/osgEarth/ModelLayer.cpp
@@ -391,10 +391,6 @@ ModelLayer::getOrCreateSceneGraph(const Map* map,
ss->setRenderBinDetails( 99999, "RenderBin" ); //TODO: configure this bin ...
}
- else
- {
- groupSS->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
- }
// save it.
_graphs[map->getUID()] = node;
=====================================
src/osgEarth/TerrainLayer
=====================================
--- a/src/osgEarth/TerrainLayer
+++ b/src/osgEarth/TerrainLayer
@@ -262,6 +262,9 @@ namespace osgEarth
* Given a TileKey, returns a TileKey representing the best known available.
* For example, if the input TileKey exceeds the layer's max LOD, the return
* value will be an ancestor key at that max LOD.
+ *
+ * If a setting that effects the visible range of this layer is set (minLevel, maxLevel, minResolution or maxResolution)
+ * then any key passed in that falls outside of the valid range for the layer will return TileKey::INVALID.
*/
virtual TileKey getBestAvailableTileKey(const TileKey& key) const;
=====================================
src/osgEarth/Version
=====================================
--- a/src/osgEarth/Version
+++ b/src/osgEarth/Version
@@ -31,7 +31,7 @@ extern "C" {
#define OSGEARTH_MINOR_VERSION 9
#define OSGEARTH_PATCH_VERSION 0
#define OSGEARTH_SOVERSION 0
-#define OSGEARTH_RC_VERSION 2
+#define OSGEARTH_RC_VERSION 3
#define OSGEARTH_DEVEL_VERSION 0 // 0 = release; >0 = interim devel version
/* Convenience macro that can be used to decide whether a feature is present or not i.e.
=====================================
src/osgEarth/VirtualProgram
=====================================
--- a/src/osgEarth/VirtualProgram
+++ b/src/osgEarth/VirtualProgram
@@ -178,9 +178,12 @@ namespace osgEarth
/** Generates the shaders. */
void prepare();
+ public:
/** Called from the draw context to resize shader buffers as necessary (OSG) */
- virtual void resizeGLObjectBuffers(unsigned maxSize);
+ void resizeGLObjectBuffers(unsigned maxSize);
+ /** Called by OSG to release GPu memory associated with the object */
+ void releaseGLObjects(osg::State* state) const;
protected:
virtual ~PolyShader() { }
@@ -199,11 +202,6 @@ namespace osgEarth
osg::ref_ptr<osg::Shader> _geomShader;
osg::ref_ptr<osg::Shader> _tessevalShader;
- // shader source before running thru the preprocessor. Keep this around so that
- // someone can call VirtualProgram::getShaders and have access to code that has
- // not been set up for ShaderFactory.
- //std::string _originalSource;
-
bool _dirty;
};
=====================================
src/osgEarth/VirtualProgram.cpp
=====================================
--- a/src/osgEarth/VirtualProgram.cpp
+++ b/src/osgEarth/VirtualProgram.cpp
@@ -926,11 +926,6 @@ VirtualProgram::resizeGLObjectBuffers(unsigned maxSize)
}
}
- // Resize the buffered_object
- //_apply.resize(maxSize);
-
- //_vpStackMemory._item.resize(maxSize);
-
_programCacheMutex.unlock();
}
@@ -941,8 +936,15 @@ VirtualProgram::releaseGLObjects(osg::State* state) const
for (ProgramMap::const_iterator i = _programCache.begin(); i != _programCache.end(); ++i)
{
- //if ( i->second->referenceCount() == 1 )
- i->second._program->releaseGLObjects(state);
+ i->second._program->releaseGLObjects(state);
+ }
+
+ for (ShaderMap::const_iterator i = _shaderMap.begin(); i != _shaderMap.end(); ++i)
+ {
+ if (i->data()._shader.valid())
+ {
+ i->data()._shader->releaseGLObjects(state);
+ }
}
_programCache.clear();
@@ -1949,6 +1951,24 @@ void PolyShader::resizeGLObjectBuffers(unsigned maxSize)
}
}
+void PolyShader::releaseGLObjects(osg::State* state) const
+{
+ if (_nominalShader.valid())
+ {
+ _nominalShader->releaseGLObjects(state);
+ }
+
+ if (_geomShader.valid())
+ {
+ _geomShader->releaseGLObjects(state);
+ }
+
+ if (_tessevalShader.valid())
+ {
+ _tessevalShader->releaseGLObjects(state);
+ }
+}
+
//.......................................................................
// SERIALIZERS for VIRTUALPROGRAM
=====================================
src/osgEarthDrivers/cache_leveldb/LevelDBCacheBin.cpp
=====================================
--- a/src/osgEarthDrivers/cache_leveldb/LevelDBCacheBin.cpp
+++ b/src/osgEarthDrivers/cache_leveldb/LevelDBCacheBin.cpp
@@ -59,7 +59,7 @@ namespace
for(unsigned i=0; i<paddedSize/4; ++i, ++ptr)
(*ptr) ^= prng.next(INT_MAX);
data = std::string(buf, data.size());
- delete buf;
+ delete [] buf;
}
void unblend(std::string& data, unsigned seed)
=====================================
src/osgEarthDrivers/cache_rocksdb/RocksDBCacheBin.cpp
=====================================
--- a/src/osgEarthDrivers/cache_rocksdb/RocksDBCacheBin.cpp
+++ b/src/osgEarthDrivers/cache_rocksdb/RocksDBCacheBin.cpp
@@ -59,7 +59,7 @@ namespace
for(unsigned i=0; i<paddedSize/4; ++i, ++ptr)
(*ptr) ^= prng.next(INT_MAX);
data = std::string(buf, data.size());
- delete buf;
+ delete [] buf;
}
void unblend(std::string& data, unsigned seed)
=====================================
src/osgEarthDrivers/engine_mp/MPGeometry
=====================================
--- a/src/osgEarthDrivers/engine_mp/MPGeometry
+++ b/src/osgEarthDrivers/engine_mp/MPGeometry
@@ -153,7 +153,9 @@ namespace osgEarth { namespace Drivers { namespace MPTerrainEngine
#endif
protected:
-#if OSG_MIN_VERSION_REQUIRED(3,5,6)
+#if OSG_MIN_VERSION_REQUIRED(3,5,9)
+ virtual osg::VertexArrayState* createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const;
+#elif OSG_MIN_VERSION_REQUIRED(3,5,6)
virtual osg::VertexArrayState* createVertexArrayState(osg::RenderInfo& renderInfo) const;
#endif
=====================================
src/osgEarthDrivers/engine_mp/MPGeometry.cpp
=====================================
--- a/src/osgEarthDrivers/engine_mp/MPGeometry.cpp
+++ b/src/osgEarthDrivers/engine_mp/MPGeometry.cpp
@@ -565,11 +565,17 @@ MPGeometry::compileGLObjects( osg::RenderInfo& renderInfo ) const
}
#if OSG_MIN_VERSION_REQUIRED(3,5,6)
+
osg::VertexArrayState*
+#if OSG_MIN_VERSION_REQUIRED(3,5,9)
+MPGeometry::createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const
+{
+ osg::VertexArrayState* vas = osg::Geometry::createVertexArrayStateImplementation(renderInfo);
+#else
MPGeometry::createVertexArrayState(osg::RenderInfo& renderInfo) const
{
osg::VertexArrayState* vas = osg::Geometry::createVertexArrayState(renderInfo);
-
+#endif
// make sure we have array dispatchers for the multipass coords
vas->assignTexCoordArrayDispatcher(_texCoordList.size() + 2);
=====================================
src/osgEarthDrivers/engine_rex/GeometryPool
=====================================
--- a/src/osgEarthDrivers/engine_rex/GeometryPool
+++ b/src/osgEarthDrivers/engine_rex/GeometryPool
@@ -71,7 +71,11 @@ namespace osgEarth { namespace Drivers { namespace RexTerrainEngine
const osg::DrawElements* getMaskElements() const { return _maskElements.get(); }
#ifdef SUPPORTS_VAO
+ #if OSG_MIN_VERSION_REQUIRED(3,5,9)
+ osg::VertexArrayState* createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const;
+ #else
osg::VertexArrayState* createVertexArrayState(osg::RenderInfo& renderInfo) const;
+ #endif
#endif
void compileGLObjects(osg::RenderInfo& renderInfo) const;
=====================================
src/osgEarthDrivers/engine_rex/GeometryPool.cpp
=====================================
--- a/src/osgEarthDrivers/engine_rex/GeometryPool.cpp
+++ b/src/osgEarthDrivers/engine_rex/GeometryPool.cpp
@@ -148,6 +148,26 @@ namespace
if ( (col & 0x1)==1 ) return 2;
return 1;
}
+
+ struct Sort_by_X {
+ osg::Vec3Array& _verts;
+ Sort_by_X(osg::Vec3Array* verts) : _verts(*verts) { }
+ bool operator()(unsigned lhs, unsigned rhs) const {
+ if (_verts[lhs].x() < _verts[rhs].x()) return true;
+ if (_verts[lhs].x() > _verts[rhs].x()) return false;
+ return _verts[lhs].y() < _verts[rhs].y();
+ }
+ };
+
+ struct Sort_by_Y {
+ osg::Vec3Array& _verts;
+ Sort_by_Y(osg::Vec3Array* verts) : _verts(*verts) { }
+ bool operator()(unsigned lhs, unsigned rhs) const {
+ if (_verts[lhs].y() < _verts[rhs].y()) return true;
+ if (_verts[lhs].y() > _verts[rhs].y()) return false;
+ return _verts[lhs].x() < _verts[rhs].x();
+ }
+ };
}
#define addSkirtDataForIndex(INDEX, HEIGHT) \
@@ -229,20 +249,20 @@ GeometryPool::createGeometry(const TileKey& tileKey,
geom->setDrawElements(primSet);
// the vertex locations:
- osg::Vec3Array* verts = new osg::Vec3Array();
+ osg::ref_ptr<osg::Vec3Array> verts = new osg::Vec3Array();
verts->setVertexBufferObject(vbo.get());
verts->reserve( numVerts );
verts->setBinding(verts->BIND_PER_VERTEX);
- geom->setVertexArray( verts );
+ geom->setVertexArray( verts.get() );
// the surface normals (i.e. extrusion vectors)
- osg::Vec3Array* normals = new osg::Vec3Array();
+ osg::ref_ptr<osg::Vec3Array> normals = new osg::Vec3Array();
normals->setVertexBufferObject(vbo.get());
normals->reserve( numVerts );
normals->setBinding(normals->BIND_PER_VERTEX);
- geom->setNormalArray( normals );
+ geom->setNormalArray( normals.get() );
- osg::Vec3Array* neighbors = 0L;
+ osg::ref_ptr<osg::Vec3Array> neighbors = 0L;
if ( _options.morphTerrain() == true )
{
// neighbor positions (for morphing)
@@ -250,8 +270,7 @@ GeometryPool::createGeometry(const TileKey& tileKey,
neighbors->setBinding(neighbors->BIND_PER_VERTEX);
neighbors->setVertexBufferObject(vbo.get());
neighbors->reserve( numVerts );
- geom->setNeighborArray(neighbors);
- //geom->setTexCoordArray( 1, neighbors );
+ geom->setNeighborArray(neighbors.get());
}
// tex coord is [0..1] across the tile. The 3rd dimension tracks whether the
@@ -267,13 +286,13 @@ GeometryPool::createGeometry(const TileKey& tileKey,
osg::Vec3Array* texCoords = _sharedTexCoords.get();
#else
bool populateTexCoords = true;
- osg::Vec3Array* texCoords = new osg::Vec3Array();
+ osg::ref_ptr<osg::Vec3Array> texCoords = new osg::Vec3Array();
texCoords->setBinding(texCoords->BIND_PER_VERTEX);
texCoords->setVertexBufferObject(vbo.get());
texCoords->reserve( numVerts );
#endif
- geom->setTexCoordArray(texCoords);
+ geom->setTexCoordArray(texCoords.get());
float delta = 1.0/(tileSize-1);
osg::Vec3d tdelta(delta,0,0);
@@ -317,148 +336,164 @@ GeometryPool::createGeometry(const TileKey& tileKey,
}
}
- // Now tessellate the surface.
-
- // TODO: do we really need this??
- bool swapOrientation = !locator->orientationOpenGL();
-
- for(unsigned j=0; j<tileSize-1; ++j)
+ // By default we tessellate the surface, but if there's a masking set
+ // it might replace some or all of our surface geometry.
+ bool tessellateSurface = true;
+
+ if (maskSet)
{
- for(unsigned i=0; i<tileSize-1; ++i)
+ // The mask generator adds to the passed-in arrays as necessary,
+ // and then returns a new primtive set containing all the new triangles.
+ osg::ref_ptr<osg::DrawElementsUInt> maskElements;
+
+ MaskGenerator::Result r = maskSet->createMaskPrimitives(
+ mapInfo,
+ verts.get(), texCoords.get(), normals.get(), neighbors.get(),
+ maskElements);
+
+ if (r == MaskGenerator::R_BOUNDARY_INTERSECTS_TILE &&
+ maskElements.valid() &&
+ maskElements->size() > 0)
{
- int i00;
- int i01;
- if (swapOrientation)
- {
- i01 = j*tileSize + i;
- i00 = i01+tileSize;
- }
- else
- {
- i00 = j*tileSize + i;
- i01 = i00+tileSize;
- }
+ // Share the same EBO as the surface geometry
+ maskElements->setElementBufferObject(primSet->getElementBufferObject());
+ geom->setMaskElements(maskElements.get());
- int i10 = i00+1;
- int i11 = i01+1;
+ // Build a skirt for the mask geometry?
+ if (createSkirt)
+ {
+ // calculate the skirt extrusion height
+ double height = tileBound.radius() * _options.heightFieldSkirtRatio().get();
- // skip any triangles that have a discarded vertex:
- bool discard = maskSet && (
- maskSet->isMasked( (*texCoords)[i00] ) ||
- maskSet->isMasked( (*texCoords)[i11] )
- );
+ // Construct a node+edge graph out of the masking geometry:
+ osg::ref_ptr<TopologyGraph> graph = TopologyBuilder::create(verts.get(), maskElements.get(), tileKey.str());
- if ( !discard )
- {
- discard = maskSet && maskSet->isMasked( (*texCoords)[i01] );
- if ( !discard )
+ // Extract the boundaries (if the topology is discontinuous,
+ // there will be more than one)
+ for (unsigned i = 0; i<graph->getNumBoundaries(); ++i)
{
- primSet->addElement(i01);
- primSet->addElement(i00);
- primSet->addElement(i11);
- }
-
- discard = maskSet && maskSet->isMasked( (*texCoords)[i10] );
- if ( !discard )
- {
- primSet->addElement(i00);
- primSet->addElement(i10);
- primSet->addElement(i11);
+ TopologyGraph::IndexVector boundary;
+ graph->createBoundary(i, boundary);
+
+ if (boundary.size() >= 3)
+ {
+ unsigned skirtIndex = verts->size();
+
+ for (TopologyGraph::IndexVector::const_iterator i = boundary.begin(); i != boundary.end(); ++i)
+ {
+ addSkirtDataForIndex((*i)->index(), height);
+ }
+
+ // then create the elements:
+ int i;
+ for (i = skirtIndex; i < (int)verts->size() - 2; i += 2)
+ addSkirtTriangles(i, i + 2);
+
+ addSkirtTriangles(i, skirtIndex);
+ }
}
}
}
- }
- // create mask geometry
- bool skirtCreated = false;
+ // If the boundary doesn't intersect the tile, draw the entire tile
+ // as we normally would. Need to reset the masking marker.
+ else if (r == MaskGenerator::R_BOUNDARY_DOES_NOT_INTERSECT_TILE)
+ {
+ maskSet = 0L;
+ for (osg::Vec3Array::iterator i = texCoords->begin(); i != texCoords->end(); ++i)
+ i->z() = MASK_MARKER_NORMAL;
+ }
+
+ // If the boundary contains the entire tile, draw nothing!
+ else // if (r == MaskGenerator::R_BOUNDARY_CONTAINS_ENTIRE_TILE)
+ {
+ tessellateSurface = false;
+ }
+ }
- if (maskSet)
+ // Now tessellate the (unmasked) surface.
+
+ if (tessellateSurface)
{
- int s = verts->size();
- osg::ref_ptr<osg::DrawElementsUInt> maskPrim = maskSet->createMaskPrimitives(mapInfo, verts, texCoords, normals, neighbors);
- if (maskPrim && maskPrim->size() > 0)
- {
- maskPrim->setElementBufferObject(primSet->getElementBufferObject());
- geom->setMaskElements(maskPrim.get());
+ // TODO: do we really need this??
+ bool swapOrientation = !locator->orientationOpenGL();
- if (createSkirt)
+ for(unsigned j=0; j<tileSize-1; ++j)
+ {
+ for(unsigned i=0; i<tileSize-1; ++i)
{
- // Skirts for masking geometries are complicated. There are two parts.
- // The first part is the "perimeter" of the tile, i.e the outer edge of the
- // tessellation. This code will detect that outer boundary and create skrits
- // for it.
- // The second part (NYI) detects the actual inner boundary ("patch geometry")
- // that patches the tile tessellation to the masking boundary. TDB.
- TopologyGraph topo;
- BuildTopologyVisitor visitor(topo);
- visitor.apply(geom.get(), verts);
-
- if (topo._verts.empty() == false)
+ int i00;
+ int i01;
+ if (swapOrientation)
{
- TopologyGraph::IndexVector boundary;
- topo.createBoundary(boundary);
-
- double height = tileBound.radius() * _options.heightFieldSkirtRatio().get();
- unsigned skirtIndex = verts->size();
+ i01 = j*tileSize + i;
+ i00 = i01+tileSize;
+ }
+ else
+ {
+ i00 = j*tileSize + i;
+ i01 = i00+tileSize;
+ }
+
+ int i10 = i00+1;
+ int i11 = i01+1;
- unsigned matches = 0;
- for (TopologyGraph::IndexVector::const_iterator i = boundary.begin(); i != boundary.end(); ++i)
+ // skip any triangles that have a discarded vertex:
+ bool discard = maskSet && (
+ maskSet->isMasked( (*texCoords)[i00] ) ||
+ maskSet->isMasked( (*texCoords)[i11] )
+ );
+
+ if ( !discard )
+ {
+ discard = maskSet && maskSet->isMasked( (*texCoords)[i01] );
+ if ( !discard )
{
- int k;
- for (k = 0; k<skirtIndex; ++k)
- {
- if ((*verts)[k].x() == (*i)->x() && (*verts)[k].y() == (*i)->y())
- {
- addSkirtDataForIndex(k, height);
- matches++;
- break;
- }
- }
+ primSet->addElement(i01);
+ primSet->addElement(i00);
+ primSet->addElement(i11);
}
-
- if (matches != boundary.size()) {
- OE_WARN << LC << "matches != boundary size" << std::endl;
+
+ discard = maskSet && maskSet->isMasked( (*texCoords)[i10] );
+ if ( !discard )
+ {
+ primSet->addElement(i00);
+ primSet->addElement(i10);
+ primSet->addElement(i11);
}
-
- int n;
- for (n = skirtIndex; n<(int)verts->size()-2; n+=2)
- addMaskSkirtTriangles(n, n+2);
-
- addMaskSkirtTriangles(n, skirtIndex);
-
- skirtCreated = true;
}
}
}
- }
- if ( createSkirt && !skirtCreated )
- {
- // SKIRTS:
- // calculate the skirt extrusion height
- double height = tileBound.radius() * _options.heightFieldSkirtRatio().get();
+ // Build skirts for the tile geometry
+ if ( createSkirt )
+ {
+ // SKIRTS:
+ // calculate the skirt extrusion height
+ double height = tileBound.radius() * _options.heightFieldSkirtRatio().get();
- unsigned skirtIndex = verts->size();
+ unsigned skirtIndex = verts->size();
- // first, create all the skirt verts, normals, and texcoords.
- for(int c=0; c<(int)tileSize-1; ++c)
- addSkirtDataForIndex( c, height ); //top
+ // first, create all the skirt verts, normals, and texcoords.
+ for(int c=0; c<(int)tileSize-1; ++c)
+ addSkirtDataForIndex( c, height ); //top
- for(int r=0; r<(int)tileSize-1; ++r)
- addSkirtDataForIndex( r*tileSize+(tileSize-1), height ); //right
+ for(int r=0; r<(int)tileSize-1; ++r)
+ addSkirtDataForIndex( r*tileSize+(tileSize-1), height ); //right
- for(int c=tileSize-1; c>=0; --c)
- addSkirtDataForIndex( (tileSize-1)*tileSize+c, height ); //bottom
+ for(int c=tileSize-1; c>=0; --c)
+ addSkirtDataForIndex( (tileSize-1)*tileSize+c, height ); //bottom
- for(int r=tileSize-1; r>=0; --r)
- addSkirtDataForIndex( r*tileSize, height ); //left
+ for(int r=tileSize-1; r>=0; --r)
+ addSkirtDataForIndex( r*tileSize, height ); //left
- // then create the elements indices:
- int i;
- for(i=skirtIndex; i<(int)verts->size()-2; i+=2)
- addSkirtTriangles( i, i+2 );
+ // then create the elements indices:
+ int i;
+ for(i=skirtIndex; i<(int)verts->size()-2; i+=2)
+ addSkirtTriangles( i, i+2 );
- addSkirtTriangles( i, skirtIndex );
+ addSkirtTriangles( i, skirtIndex );
+ }
}
return geom.release();
@@ -592,7 +627,11 @@ SharedGeometry::empty() const
#ifdef SUPPORTS_VAO
+#if OSG_MIN_VERSION_REQUIRED(3,5,9)
+osg::VertexArrayState* SharedGeometry::createVertexArrayStateImplementation(osg::RenderInfo& renderInfo) const
+#else
osg::VertexArrayState* SharedGeometry::createVertexArrayState(osg::RenderInfo& renderInfo) const
+#endif
{
osg::State& state = *renderInfo.getState();
=====================================
src/osgEarthDrivers/engine_rex/MaskGenerator
=====================================
--- a/src/osgEarthDrivers/engine_rex/MaskGenerator
+++ b/src/osgEarthDrivers/engine_rex/MaskGenerator
@@ -26,7 +26,7 @@
#define MASK_MARKER_DISCARD 0.0f // do not draw
#define MASK_MARKER_NORMAL 1.0f // normal vertex
-#define MASK_MARKER_SKIRT 2.0f // not subject to morphing
+#define MASK_MARKER_PATCH 2.0f // not subject to morphing
#define MASK_MARKER_BOUNDARY 3.0f // not subject to elevation texture
namespace osgEarth { namespace Drivers { namespace RexTerrainEngine
@@ -52,47 +52,58 @@ namespace osgEarth { namespace Drivers { namespace RexTerrainEngine
/**
* Creates geometry for the part of a tile containing mask data.
+ * Used internally by GeometryPool.
*/
class MaskGenerator : public osg::Referenced
{
public:
- MaskGenerator(const TileKey& key, unsigned tileSize, const Map* map);
+ enum Result {
+ R_BOUNDARY_DOES_NOT_INTERSECT_TILE,
+ R_BOUNDARY_CONTAINS_ENTIRE_TILE,
+ R_BOUNDARY_INTERSECTS_TILE
+ };
+ public:
+ MaskGenerator(const TileKey& key, unsigned tileSize, const Map* map);
+ //! True if this tile has masking data at all
bool hasMasks() const
{
return _maskRecords.size() > 0;
}
- /** whether a texcoord indicates that the corresponding vert is masked. */
+ //! whether a texcoord indicates that the corresponding vert is masked.
bool isMasked(const osg::Vec3f& texCoord) const
{
return texCoord.z() == MASK_MARKER_DISCARD;
}
- /** whether the masking geometry contains a unit location. */
- /* 0.0 - contains */
- /* 1.0 - does not contain */
- /* 2.0 - does not contain but is a tile vert on the outer */
- /* masking skirt boundary */
- float getMarker(float nx, float ny) const;
-
- bool containedByQuadAtColRow(int col, int row, int tileSize) const
+ //! True if the texcoord indicates a bounary vertex
+ bool isBoundary(const osg::Vec3f& texCoord) const
{
- // Placeholder for now.
- return false;
+ return texCoord.z() == MASK_MARKER_BOUNDARY;
}
+ //! returns once of the MASK_MARKER_* defines for the given NDC location
+ float getMarker(float nx, float ny) const;
+
//! Gets the LL and UR corners of the "patch rectangle" in NDC space
void getMinMax(osg::Vec3d& min, osg::Vec3d& max);
- osg::DrawElementsUInt* createMaskPrimitives(const MapInfo& mapInfo, osg::Vec3Array* verts, osg::Vec3Array* texCoords, osg::Vec3Array* normals, osg::Vec3Array* neighbors);
+ //! Generates all the masking geometry and appened it to the passed-in arrays.
+ Result createMaskPrimitives(
+ const MapInfo& mapInfo,
+ osg::Vec3Array* verts,
+ osg::Vec3Array* texCoords,
+ osg::Vec3Array* normals,
+ osg::Vec3Array* neighbors,
+ osg::ref_ptr<osg::DrawElementsUInt>& out_elements);
protected:
void setupMaskRecord(const MapInfo& mapInfo, osg::Vec3dArray* boundary);
protected:
- const TileKey _key;
+ const TileKey _key;
unsigned _tileSize;
MaskRecordVector _maskRecords;
osg::Vec3d _ndcMin, _ndcMax;
=====================================
src/osgEarthDrivers/engine_rex/MaskGenerator.cpp
=====================================
--- a/src/osgEarthDrivers/engine_rex/MaskGenerator.cpp
+++ b/src/osgEarthDrivers/engine_rex/MaskGenerator.cpp
@@ -33,6 +33,120 @@ using namespace osgEarth::Symbology;
#define MATCH_TOLERANCE 0.000001
+#define EQUIVALENT(X,Y) (osg::equivalent((double)(X), (double)(Y), MATCH_TOLERANCE))
+
+#define EQUIVALENT_2D(A, B) (EQUIVALENT(A->x(), B->x()) && EQUIVALENT(A->y(), B->y()))
+
+namespace
+{
+ void resample(Geometry* geom, double maxLen)
+ {
+ GeometryIterator i(geom);
+ while (i.hasMore())
+ {
+ Geometry* part = i.next();
+ if (part->size() < 2) continue;
+
+ std::vector<osg::Vec3d> newGeom;
+ ConstSegmentIterator csi(part, true);
+ while(csi.hasMore())
+ {
+ Segment seg = csi.next();
+ newGeom.push_back(seg.first);
+ osg::Vec3d vec3d = seg.second - seg.first;
+ osg::Vec2d vec2d(vec3d.x(), vec3d.y());
+ double len2d = vec2d.length();
+
+ if (len2d > maxLen)
+ {
+ double numNewPoints = ::floor(len2d/maxLen);
+ double interval = len2d/(numNewPoints+1.0);
+ for (double d=interval; d<len2d; d+=interval)
+ {
+ double t = d/len2d;
+
+ osg::Vec3d newPoint(
+ seg.first.x() + vec2d.x()*t,
+ seg.first.y() + vec2d.y()*t,
+ seg.first.z() + vec3d.z()*t);
+
+ if (newGeom.empty() || newPoint != newGeom.back())
+ {
+ newGeom.push_back(newPoint);
+ }
+
+ }
+ }
+ }
+
+ if (newGeom.empty() == false)
+ {
+ part->swap(newGeom);
+ }
+ }
+
+ geom->removeDuplicates();
+ }
+
+ //! Compares two 3D points ignoring the Z value.
+ struct less_2d
+ {
+ bool operator()(const osg::Vec3& lhs, const osg::Vec3& rhs) const
+ {
+ if (lhs[0] < rhs[0]) return true;
+ else if (lhs[0] > rhs[0]) return false;
+ else return lhs[1] < rhs[1];
+ }
+ };
+
+ //! Removes all duplicate points in a vertex array.
+ void removeDupes(osg::Vec3Array* verts)
+ {
+ unsigned finalSize = verts->size();
+ std::set<osg::Vec3, less_2d> unique;
+ for (unsigned i = 0; i<finalSize; ++i)
+ {
+ if (unique.find( (*verts)[i] ) == unique.end())
+ {
+ unique.insert((*verts)[i]);
+ }
+ else
+ {
+ (*verts)[i] = verts->back();
+ --finalSize;
+ }
+ }
+
+ if (finalSize != verts->size())
+ {
+ verts->resize(finalSize);
+ }
+ }
+
+ //! Removes verts from the vec array that appear in the unique set.
+ void removeDupes(osg::Vec3Array* verts, const std::set<osg::Vec3, less_2d>& unique)
+ {
+ unsigned finalSize = verts->size();
+
+ for (unsigned i = 0; i<finalSize; ++i)
+ {
+ if (unique.find( (*verts)[i] ) != unique.end())
+ {
+ (*verts)[i] = verts->back();
+ --finalSize;
+ }
+ }
+
+ if (finalSize != verts->size())
+ {
+ verts->resize(finalSize);
+ //OE_INFO << LC << "Removed " << verts->size() - finalSize << " duplicates.\n";
+ }
+ }
+}
+
+
+
MaskGenerator::MaskGenerator(const TileKey& key, unsigned tileSize, const Map* map) :
_key( key ), _tileSize(tileSize)
@@ -55,12 +169,15 @@ _key( key ), _tileSize(tileSize)
void
MaskGenerator::setupMaskRecord(const MapInfo& mapInfo, osg::Vec3dArray* boundary)
{
+ // Make a "locator" for this key so we can do coordinate conversion:
osg::ref_ptr<osgEarth::GeoLocator> geoLocator = GeoLocator::createForKey(_key, mapInfo);
+
if (geoLocator->getCoordinateSystemType() == GeoLocator::GEOCENTRIC)
geoLocator = geoLocator->getGeographicFromGeocentric();
if ( boundary )
{
+ // Calculate the axis-aligned bounding box of the boundary polygon:
osg::Vec3d min, max;
min = max = boundary->front();
@@ -79,20 +196,25 @@ MaskGenerator::setupMaskRecord(const MapInfo& mapInfo, osg::Vec3dArray* boundary
max.y() = it->y();
}
+ // convert that bounding box to "unit" space (0..1 across the tile)
osg::Vec3d min_ndc, max_ndc;
geoLocator->modelToUnit(min, min_ndc);
geoLocator->modelToUnit(max, max_ndc);
+ // true if boundary overlaps tile in X dimension:
bool x_match = ((min_ndc.x() >= 0.0 && max_ndc.x() <= 1.0) ||
(min_ndc.x() <= 0.0 && max_ndc.x() > 0.0) ||
(min_ndc.x() < 1.0 && max_ndc.x() >= 1.0));
+ // true if boundary overlaps tile in Y dimension:
bool y_match = ((min_ndc.y() >= 0.0 && max_ndc.y() <= 1.0) ||
(min_ndc.y() <= 0.0 && max_ndc.y() > 0.0) ||
(min_ndc.y() < 1.0 && max_ndc.y() >= 1.0));
if (x_match && y_match)
{
+ // yes, boundary overlaps tile, so expand the global NDC bounding
+ // box to include the new mask:
if (_maskRecords.size() == 0)
{
_ndcMin = min_ndc;
@@ -106,34 +228,40 @@ MaskGenerator::setupMaskRecord(const MapInfo& mapInfo, osg::Vec3dArray* boundary
if (max_ndc.y() > _ndcMax.y()) _ndcMax.y() = max_ndc.y();
}
+ // and add this mask to the list.
_maskRecords.push_back( MaskRecord(boundary, min_ndc, max_ndc, 0L) );
}
}
}
-osg::DrawElementsUInt*
-MaskGenerator::createMaskPrimitives(const MapInfo& mapInfo, osg::Vec3Array* verts, osg::Vec3Array* texCoords, osg::Vec3Array* normals, osg::Vec3Array* neighbors)
+MaskGenerator::Result
+MaskGenerator::createMaskPrimitives(const MapInfo& mapInfo,
+ osg::Vec3Array* verts, osg::Vec3Array* texCoords,
+ osg::Vec3Array* normals, osg::Vec3Array* neighbors,
+ osg::ref_ptr<osg::DrawElementsUInt>& out_elements)
{
if (_maskRecords.size() <= 0)
- return 0L;
+ {
+ return R_BOUNDARY_DOES_NOT_INTERSECT_TILE;
+ }
osg::ref_ptr<osgEarth::GeoLocator> geoLocator = GeoLocator::createForKey(_key, mapInfo);
if (geoLocator->getCoordinateSystemType() == GeoLocator::GEOCENTRIC)
geoLocator = geoLocator->getGeographicFromGeocentric();
+ // Configure up a local tangent plane at the centroid of the tile:
GeoPoint centroid;
_key.getExtent().getCentroid( centroid );
-
osg::Matrix world2local, local2world;
centroid.createWorldToLocal( world2local );
local2world.invert( world2local );
- osg::ref_ptr<osgUtil::DelaunayTriangulator> trig=new osgUtil::DelaunayTriangulator();
-
- std::vector<osg::ref_ptr<osgUtil::DelaunayConstraint> > alldcs;
-
- osg::ref_ptr<osg::Vec3Array> coordsArray = new osg::Vec3Array;
+ // This array holds the NDC grid of points inside the patch polygon
+ // (built later in this method)
+ osg::ref_ptr<osg::Vec3Array> coordsArray = new osg::Vec3Array();
+ // Calculate the combined axis-aligned NDC bounding box for all masks:
+ // (gw: didn't we already do this in setupMaskRecord?)
double minndcx = _maskRecords[0]._ndcMin.x();
double minndcy = _maskRecords[0]._ndcMin.y();
double maxndcx = _maskRecords[0]._ndcMax.x();
@@ -158,6 +286,8 @@ MaskGenerator::createMaskPrimitives(const MapInfo& mapInfo, osg::Vec3Array* vert
}
}
+ // Figure out the indices representing the area we need to "cut out"
+ // of the tile to accommadate the mask:
int min_i = (int)floor(minndcx * (double)(_tileSize-1));
if (min_i < 0) min_i = 0;
if (min_i >= (int)_tileSize) min_i = _tileSize - 1;
@@ -174,340 +304,374 @@ MaskGenerator::createMaskPrimitives(const MapInfo& mapInfo, osg::Vec3Array* vert
if (max_j < 0) max_j = 0;
if (max_j >= (int)_tileSize) max_j = _tileSize - 1;
- if (min_i >= 0 && max_i >= 0 && min_j >= 0 && max_j >= 0)
+ if (min_i < 0 || max_i < 0 || min_j < 0 || max_j < 0)
{
- int num_i = max_i - min_i + 1;
- int num_j = max_j - min_j + 1;
+ return R_BOUNDARY_DOES_NOT_INTERSECT_TILE;
+ }
- // The "patch polygon" is the region that stitches the normal tile geometry to the mask boundary.
- osg::ref_ptr<Polygon> patchPoly = new Polygon();
- patchPoly->resize(num_i * 2 + num_j * 2 - 4);
+
+ // The "patch polygon" is the region that stitches the normal tile geometry to the mask boundary.
+ // The patch will be in NDC coordinates:
- for (int i = 0; i < num_i; i++)
+ // Number of verts wide (i) and height(i) of the patch polygon:
+ int num_i = max_i - min_i + 1;
+ int num_j = max_j - min_j + 1;
+
+ osg::ref_ptr<Polygon> patchPoly = new Polygon();
+ patchPoly->resize(num_i * 2 + num_j * 2 - 4);
+
+ // top and bottom verts:
+ for (int i = 0; i < num_i; i++)
+ {
{
- {
- osg::Vec3d ndc( ((double)(i + min_i))/(double)(_tileSize-1), ((double)min_j)/(double)(_tileSize-1), 0.0);
- (*patchPoly)[i] = ndc;
- }
+ osg::Vec3d ndc( ((double)(i + min_i))/(double)(_tileSize-1), ((double)min_j)/(double)(_tileSize-1), 0.0);
+ (*patchPoly)[i] = ndc;
+ }
- {
- osg::Vec3d ndc( ((double)(i + min_i))/(double)(_tileSize-1), ((double)max_j)/(double)(_tileSize-1), 0.0);
- (*patchPoly)[i + (2 * num_i + num_j - 3) - 2 * i] = ndc;
- }
+ {
+ // bottom:
+ osg::Vec3d ndc( ((double)(i + min_i))/(double)(_tileSize-1), ((double)max_j)/(double)(_tileSize-1), 0.0);
+ (*patchPoly)[i + (2 * num_i + num_j - 3) - 2 * i] = ndc;
}
- for (int j = 0; j < num_j - 2; j++)
+ }
+
+ // left and right verts:
+ for (int j = 0; j < num_j - 2; j++)
+ {
{
- {
- osg::Vec3d ndc( ((double)max_i)/(double)(_tileSize-1), ((double)(min_j + j + 1))/(double)(_tileSize-1), 0.0);
- (*patchPoly)[j + num_i] = ndc;
- }
+ // right:
+ osg::Vec3d ndc( ((double)max_i)/(double)(_tileSize-1), ((double)(min_j + j + 1))/(double)(_tileSize-1), 0.0);
+ (*patchPoly)[j + num_i] = ndc;
+ }
- {
- osg::Vec3d ndc( ((double)min_i)/(double)(_tileSize-1), ((double)(min_j + j + 1))/(double)(_tileSize-1), 0.0);
- (*patchPoly)[j + (2 * num_i + 2 * num_j - 5) - 2 * j] = ndc;
- }
+ {
+ osg::Vec3d ndc( ((double)min_i)/(double)(_tileSize-1), ((double)(min_j + j + 1))/(double)(_tileSize-1), 0.0);
+ (*patchPoly)[j + (2 * num_i + 2 * num_j - 5) - 2 * j] = ndc;
}
+ }
- for (int j = 0; j < num_j; j++)
+ // create a grid of points making up the inside of the patch polygon.
+ for (int j = 0; j < num_j; j++)
+ {
+ for (int i = 0; i < num_i; i++)
{
- for (int i = 0; i < num_i; i++)
{
- {
- osg::Vec3d ndc( ((double)(i + min_i))/(double)(_tileSize-1), ((double)(j+min_j))/(double)(_tileSize-1), 0.0);
- coordsArray->push_back(ndc) ;
- }
- }
+ osg::Vec3d ndc( ((double)(i + min_i))/(double)(_tileSize-1), ((double)(j+min_j))/(double)(_tileSize-1), 0.0);
+ coordsArray->push_back(ndc) ;
+ }
}
+ }
+ double patchArea = patchPoly->getSignedArea2D();
- //
- osg::ref_ptr<osg::Vec3dArray> boundaryVerts = new osg::Vec3dArray;
+ std::set<osg::Vec3d, less_2d> boundaryVerts;
- // Use delaunay triangulation for stitching:
- for (MaskRecordVector::iterator mr = _maskRecords.begin();mr != _maskRecords.end();mr++)
+ osg::ref_ptr<osgUtil::DelaunayConstraint> dc = new osgUtil::DelaunayConstraint();
+ osg::Vec3Array* constraintVerts = new osg::Vec3Array();
+ dc->setVertexArray(constraintVerts);
+
+ // Use delaunay triangulation for stitching:
+ for (MaskRecordVector::iterator mr = _maskRecords.begin();mr != _maskRecords.end();mr++)
+ {
+ //Create local polygon representing mask
+ osg::ref_ptr<Polygon> boundaryPoly = new Polygon();
+ boundaryPoly->reserve(mr->_boundary->size());
+ for (osg::Vec3dArray::iterator it = (*mr)._boundary->begin(); it != (*mr)._boundary->end(); ++it)
{
- //Create local polygon representing mask
- osg::ref_ptr<Polygon> maskPoly = new Polygon();
- for (osg::Vec3dArray::iterator it = (*mr)._boundary->begin(); it != (*mr)._boundary->end(); ++it)
- {
- osg::Vec3d local;
- geoLocator->convertModelToLocal(*it, local);
- maskPoly->push_back(local);
- }
+ osg::Vec3d local;
+ geoLocator->convertModelToLocal(*it, local);
+ boundaryPoly->push_back(local);
+ }
+
+ // Resample the masking polygon to closely match the resolution of the
+ // current tile grid, which will result in a better tessellation.
+ // Ideally we would do this after cropping, but that is causing some
+ // triangulation errors. TODO -gw
+ if (!boundaryPoly->empty())
+ {
+ const double interval = 1.0 / double(_tileSize-1);
+ resample(boundaryPoly.get(), interval);
+ }
- // Add mask bounds as a triangulation constraint
- osg::ref_ptr<osgUtil::DelaunayConstraint> newdc=new osgUtil::DelaunayConstraint;
- osg::Vec3Array* maskConstraint = new osg::Vec3Array();
- newdc->setVertexArray(maskConstraint);
+ // Crop the boundary to the patch polygon (i.e. the bounding box)
+ // for case where mask crosses tile edges
+ osg::ref_ptr<Geometry> boundaryPolyCroppedToTile;
+ boundaryPoly->crop(patchPoly.get(), boundaryPolyCroppedToTile);
- //Crop the mask to the stitching poly (for case where mask crosses tile edge)
- osg::ref_ptr<Geometry> maskCrop;
- maskPoly->crop(patchPoly.get(), maskCrop);
+ // See the comment for the call to resample above. -gw
+ //if (boundaryPolyCroppedToTile.valid() && !boundaryPolyCroppedToTile->empty())
+ //{
+ // const double interval = 1.0 / double(_tileSize-1);
+ // resample(boundaryPolyCroppedToTile.get(), interval);
+ //}
- GeometryIterator i( maskCrop.get(), false );
- while( i.hasMore() )
- {
- Geometry* part = i.next();
- if (!part)
- continue;
+ // Add the cropped boundary geometry as a Triangulation Constraint.
+ unsigned start = constraintVerts->size();
- if (part->getType() == Geometry::TYPE_POLYGON)
- {
- osg::ref_ptr<osg::Vec3Array> partVerts = part->createVec3Array();
- maskConstraint->insert(maskConstraint->end(), partVerts->begin(), partVerts->end());
- newdc->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, maskConstraint->size() - partVerts->size(), partVerts->size()));
- }
- }
+ GeometryIterator i( boundaryPolyCroppedToTile.get(), false );
+ while( i.hasMore() )
+ {
+ Geometry* part = i.next();
+ if (!part)
+ continue;
- // Cropping strips z-values so need reassign
- std::vector<int> isZSet;
- for (osg::Vec3Array::iterator it = maskConstraint->begin(); it != maskConstraint->end(); ++it)
+ if (part->getType() == Geometry::TYPE_POLYGON)
{
- int zSet = 0;
+ osg::ref_ptr<osg::Vec3Array> partVerts = part->createVec3Array();
+ int offset = constraintVerts->size();
+ constraintVerts->reserve(constraintVerts->size() + partVerts->size());
+ constraintVerts->insert(constraintVerts->end(), partVerts->begin(), partVerts->end());
+ dc->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, offset, partVerts->size()));
+ }
+ }
- //Look for verts that belong to the original mask patch polygon
- for (Polygon::iterator mit = patchPoly->begin(); mit != patchPoly->end(); ++mit)
- {
- if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE)
- {
- //(*it).z() = (*mit).z();
- zSet += 1;
+ // Cropping strips z-values! so we need reassign them.
+ osg::Vec3Array::iterator it_start = constraintVerts->begin() + start;
+ std::vector<int> isZSet;
+ for (osg::Vec3Array::iterator it = it_start; it != constraintVerts->end(); ++it)
+ {
+ int zSet = 0;
- // Remove duplicate point from coordsArray to avoid duplicate point warnings
- osg::Vec3Array::iterator caIt;
- for (caIt = coordsArray->begin(); caIt != coordsArray->end(); ++caIt)
- {
- if (osg::absolute((*caIt).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*caIt).y() - (*it).y()) < MATCH_TOLERANCE)
- break;
- }
- if (caIt != coordsArray->end())
- coordsArray->erase(caIt);
+ // Search the patch (bounding box) polygon for matching points:
+ for (Polygon::iterator mit = patchPoly->begin(); mit != patchPoly->end(); ++mit)
+ {
+ if (EQUIVALENT_2D(mit, it))
+ {
+ //(*it).z() = (*mit).z(); // commented out by Jeff...why?
+ zSet += 1;
- break;
+ // Remove duplicate point from coordsArray to avoid duplicate point warnings
+ osg::Vec3Array::iterator caIt;
+ for (caIt = coordsArray->begin(); caIt != coordsArray->end(); ++caIt)
+ {
+ if (EQUIVALENT_2D(caIt, it))
+ break;
}
+ if (caIt != coordsArray->end())
+ coordsArray->erase(caIt);
+
+ break;
}
+ }
- //Look for verts that belong to the mask polygon
- for (Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit)
+ // Search the original uncropped boundary polygon for matching points,
+ // and build a set of boundary vertices.
+ for (Polygon::iterator mit = boundaryPoly->begin(); mit != boundaryPoly->end(); ++mit)
+ {
+ if (EQUIVALENT_2D(mit, it))
{
- if (osg::absolute((*mit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*mit).y() - (*it).y()) < MATCH_TOLERANCE)
- {
- (*it).z() = (*mit).z();
- zSet += 2;
+ (*it).z() = (*mit).z();
+ zSet += 2;
- boundaryVerts->push_back((*it));
- break;
- }
+ boundaryVerts.insert( *it );
+ break;
}
-
- isZSet.push_back(zSet);
}
- //Any mask skirt verts that are still unset are newly created verts where the skirt
- //meets the mask. Find the mask segment the point lies along and calculate the
- //appropriate z value for the point.
- int count = 0;
- for (osg::Vec3Array::iterator it = maskConstraint->begin(); it != maskConstraint->end(); ++it)
+ isZSet.push_back(zSet);
+ }
+
+ // Any mask patch verts that are still unset are newly created verts where the patch
+ // meets the mask. (Do you mean, where the boundary crosses the tile edge? -gw)
+ // Find the mask segment the point lies along and calculate the
+ // appropriate z value for the point.
+ int count = 0;
+ for (osg::Vec3Array::iterator it = it_start; it != constraintVerts->end(); ++it)
+ {
+ //If the z-value was set from a mask vertex there is no need to change it. If
+ //it was set from a vertex from the patch polygon it may need to be overriden if
+ //the vertex lies along a mask edge. Or if it is unset, it will need to be set.
+ //if (isZSet[count] < 2)
+ if (!isZSet[count])
{
- //If the z-value was set from a mask vertex there is no need to change it. If
- //it was set from a vertex from the stitching polygon it may need overriden if
- //the vertex lies along a mask edge. Or if it is unset, it will need to be set.
- //if (isZSet[count] < 2)
- if (!isZSet[count])
+ osg::Vec3d p2 = *it;
+ double closestZ = 0.0;
+ double closestRatio = DBL_MAX;
+ for (Polygon::iterator mit = boundaryPoly->begin(); mit != boundaryPoly->end(); ++mit)
{
- osg::Vec3d p2 = *it;
- double closestZ = 0.0;
- double closestRatio = DBL_MAX;
- for (Polygon::iterator mit = maskPoly->begin(); mit != maskPoly->end(); ++mit)
- {
- osg::Vec3d p1 = *mit;
- osg::Vec3d p3 = mit == --maskPoly->end() ? maskPoly->front() : (*(mit + 1));
+ osg::Vec3d p1 = *mit;
+ osg::Vec3d p3 = mit == --boundaryPoly->end() ? boundaryPoly->front() : (*(mit + 1));
+
+ //Truncated vales to compensate for accuracy issues
+ double p1x = ((int)(p1.x() * 1000000)) / 1000000.0L;
+ double p3x = ((int)(p3.x() * 1000000)) / 1000000.0L;
+ double p2x = ((int)(p2.x() * 1000000)) / 1000000.0L;
- //Truncated vales to compensate for accuracy issues
- double p1x = ((int)(p1.x() * 1000000)) / 1000000.0L;
- double p3x = ((int)(p3.x() * 1000000)) / 1000000.0L;
- double p2x = ((int)(p2.x() * 1000000)) / 1000000.0L;
+ double p1y = ((int)(p1.y() * 1000000)) / 1000000.0L;
+ double p3y = ((int)(p3.y() * 1000000)) / 1000000.0L;
+ double p2y = ((int)(p2.y() * 1000000)) / 1000000.0L;
- double p1y = ((int)(p1.y() * 1000000)) / 1000000.0L;
- double p3y = ((int)(p3.y() * 1000000)) / 1000000.0L;
- double p2y = ((int)(p2.y() * 1000000)) / 1000000.0L;
+ if ((p1x < p3x ? p2x >= p1x && p2x <= p3x : p2x >= p3x && p2x <= p1x) &&
+ (p1y < p3y ? p2y >= p1y && p2y <= p3y : p2y >= p3y && p2y <= p1y))
+ {
+ double l1 =(osg::Vec2d(p2.x(), p2.y()) - osg::Vec2d(p1.x(), p1.y())).length();
+ double lt = (osg::Vec2d(p3.x(), p3.y()) - osg::Vec2d(p1.x(), p1.y())).length();
+ double zmag = p3.z() - p1.z();
+
+ double foundZ = (l1 / lt) * zmag + p1.z();
- if ((p1x < p3x ? p2x >= p1x && p2x <= p3x : p2x >= p3x && p2x <= p1x) &&
- (p1y < p3y ? p2y >= p1y && p2y <= p3y : p2y >= p3y && p2y <= p1y))
+ double mRatio = 1.0;
+ if (EQUIVALENT(p1x, p3x))
{
- double l1 =(osg::Vec2d(p2.x(), p2.y()) - osg::Vec2d(p1.x(), p1.y())).length();
- double lt = (osg::Vec2d(p3.x(), p3.y()) - osg::Vec2d(p1.x(), p1.y())).length();
- double zmag = p3.z() - p1.z();
-
- double foundZ = (l1 / lt) * zmag + p1.z();
-
- double mRatio = 1.0;
- if (osg::absolute(p1x - p3x) < MATCH_TOLERANCE)
- {
- if (osg::absolute(p1x-p2x) < MATCH_TOLERANCE)
- mRatio = 0.0;
- }
- else
- {
- double m1 = p1x == p2x ? 0.0 : (p2y - p1y) / (p2x - p1x);
- double m2 = p1x == p3x ? 0.0 : (p3y - p1y) / (p3x - p1x);
- mRatio = m2 == 0.0 ? m1 : osg::absolute(1.0L - m1 / m2);
- }
-
- if (mRatio < 0.01)
- {
- (*it).z() = foundZ;
- isZSet[count] = 2;
-
- boundaryVerts->push_back((*it));
- break;
- }
- else if (mRatio < closestRatio)
- {
- closestRatio = mRatio;
- closestZ = foundZ;
- }
+ if (EQUIVALENT(p1x, p2x))
+ mRatio = 0.0;
+ }
+ else
+ {
+ double m1 = p1x == p2x ? 0.0 : (p2y - p1y) / (p2x - p1x);
+ double m2 = p1x == p3x ? 0.0 : (p3y - p1y) / (p3x - p1x);
+ mRatio = m2 == 0.0 ? m1 : osg::absolute(1.0L - m1 / m2);
}
- }
- if (!isZSet[count] && closestRatio < DBL_MAX)
- {
- (*it).z() = closestZ;
- isZSet[count] = 2;
+ if (mRatio < 0.01)
+ {
+ (*it).z() = foundZ;
+ isZSet[count] = 2;
- boundaryVerts->push_back((*it));
+ boundaryVerts.insert( *it );
+ break;
+ }
+ else if (mRatio < closestRatio)
+ {
+ closestRatio = mRatio;
+ closestZ = foundZ;
+ }
}
}
- if (!isZSet[count])
- OE_WARN << LC << "Z-value not set for mask constraint vertex" << std::endl;
-
- count++;
+ if (!isZSet[count] && closestRatio < DBL_MAX)
+ {
+ (*it).z() = closestZ;
+ isZSet[count] = 2;
+ boundaryVerts.insert( *it );
+ }
}
- alldcs.push_back(newdc);
+ if (!isZSet[count])
+ OE_WARN << LC << "Z-value not set for mask constraint vertex" << std::endl;
+
+ count++;
}
+ }
- trig->setInputPointArray(coordsArray.get());
+ // If we collected no constraints, that means the boundary geometry
+ // does not intersect the tile at all. Bail out now.
+ if (constraintVerts->empty())
+ {
+ return R_BOUNDARY_DOES_NOT_INTERSECT_TILE;
+ }
- for (int dcnum =0; dcnum < alldcs.size();dcnum++)
- {
- trig->addInputConstraint(alldcs[dcnum].get());
- }
+ // Set up a triangulator with the patch coordinates:
+ osg::ref_ptr<osgUtil::DelaunayTriangulator> trig = new osgUtil::DelaunayTriangulator();
+ trig->setInputPointArray(coordsArray.get());
+ trig->addInputConstraint(dc.get());
- // Create array to hold vertex normals
- osg::Vec3Array *norms=new osg::Vec3Array;
- trig->setOutputNormalArray(norms);
+ // Create array to hold vertex normals
+ //osg::Vec3Array* norms = new osg::Vec3Array();
+ //trig->setOutputNormalArray(norms);
+ // Triangulate!
+ trig->triangulate();
- // Triangulate vertices and remove triangles that lie within the contraint loop
- trig->triangulate();
- for (int dcnum =0; dcnum < alldcs.size();dcnum++)
- {
- trig->removeInternalTriangles(alldcs[dcnum].get());
- }
+ // Remove any triangles interior to the boundaries.
+ // Note: an alternative here would be to flatten them all to a common height -gw
+ trig->removeInternalTriangles(dc.get());
+
+ // Now build the new geometry.
+ const osg::Vec3Array* trigPoints = trig->getInputPointArray();
- verts->reserve(verts->size() + trig->getInputPointArray()->size());
- texCoords->reserve(texCoords->size() + trig->getInputPointArray()->size());
- normals->reserve(normals->size() + trig->getInputPointArray()->size());
- if ( neighbors )
- neighbors->reserve(neighbors->size() + trig->getInputPointArray()->size());
+ // Reserve space; pre-allocating space is faster
+ verts->reserve(verts->size() + trigPoints->size());
+ texCoords->reserve(texCoords->size() + trigPoints->size());
+ normals->reserve(normals->size() + trigPoints->size());
+ if ( neighbors )
+ neighbors->reserve(neighbors->size() + trigPoints->size());
- // Iterate through point to convert to model coords, calculate normals, and set up tex coords
- osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( _key, mapInfo );
+ // Iterate through point to convert to model coords, calculate normals, and set up tex coords
+ osg::ref_ptr<GeoLocator> locator = GeoLocator::createForKey( _key, mapInfo );
- //int norm_i = -1;
- unsigned vertsOffset = verts->size();
+ unsigned vertsOffset = verts->size();
- for (osg::Vec3Array::iterator it = trig->getInputPointArray()->begin(); it != trig->getInputPointArray()->end(); ++it)
- {
- // check to see if point is a part of the original mask boundary
- bool isBoundary = false;
- for (osg::Vec3dArray::iterator bit = boundaryVerts->begin(); bit != boundaryVerts->end(); ++bit)
- {
- if (osg::absolute((*bit).x() - (*it).x()) < MATCH_TOLERANCE && osg::absolute((*bit).y() - (*it).y()) < MATCH_TOLERANCE)
- {
- isBoundary = true;
- break;
- }
- }
+ for (osg::Vec3Array::const_iterator it = trigPoints->begin(); it != trigPoints->end(); ++it)
+ {
+ // check to see if point is a part of the original mask boundary
+ bool isBoundary = boundaryVerts.find(*it) != boundaryVerts.end();
- // get model coords
- osg::Vec3d model;
- locator->unitToModel(osg::Vec3d(it->x(), it->y(), 0.0f), model);
- model = model * world2local;
+ // get local coords
+ osg::Vec3d local;
+ locator->unitToModel(osg::Vec3d(it->x(), it->y(), 0.0f), local);
+ local = local * world2local;
- // calc normals
- osg::Vec3d modelPlusOne;
- locator->unitToModel(osg::Vec3d(it->x(), it->y(), 1.0f), modelPlusOne);
- osg::Vec3d normal = (modelPlusOne*world2local)-model;
- normal.normalize();
- normals->push_back( normal );
+ // calc normals
+ osg::Vec3d localPlusOne;
+ locator->unitToModel(osg::Vec3d(it->x(), it->y(), 1.0f), localPlusOne);
+ osg::Vec3d normal = (localPlusOne*world2local)-local;
+ normal.normalize();
+ normals->push_back( normal );
- // set elevation if this is a point along the mask boundary
- if (isBoundary)
- model += normal*it->z();
+ // set elevation if this is a point along the mask boundary
+ if (isBoundary)
+ local += normal*it->z();
- verts->push_back(model);
+ verts->push_back(local);
- // use same vert for neighbor to prevent morphing
- if ( neighbors )
- neighbors->push_back( model );
+ // use same vert for neighbor to prevent morphing
+ if ( neighbors )
+ neighbors->push_back( local );
- // set up text coords
- texCoords->push_back( osg::Vec3f(it->x(), it->y(), isBoundary ? MASK_MARKER_BOUNDARY : MASK_MARKER_SKIRT) );
- }
+ // set up text coords
+ texCoords->push_back( osg::Vec3f(it->x(), it->y(), isBoundary ? MASK_MARKER_BOUNDARY : MASK_MARKER_PATCH) );
+ }
- // Get triangles from triangulator and add as primative set to the geometry
- osg::DrawElementsUInt* tris = trig->getTriangles();
- if ( tris && tris->getNumIndices() >= 3 )
- {
- osg::ref_ptr<osg::DrawElementsUInt> elems = new osg::DrawElementsUInt(tris->getMode());
- elems->reserve(tris->size());
+ // Get triangles from triangulator and add as primitive set to the geometry
+ osg::DrawElementsUInt* tris = trig->getTriangles();
- const osg::MixinVector<GLuint> ins = tris->asVector();
- for (osg::MixinVector<GLuint>::const_iterator it = ins.begin(); it != ins.end(); ++it)
- {
- elems->push_back((*it) + vertsOffset);
- }
+ // If something went wrong, just bail out. This should never happen
+ if (tris == 0L || tris->getNumIndices() < 3)
+ {
+ //OE_INFO << LC << "* Triangulation resulted in no geometry\n";
+ return R_BOUNDARY_CONTAINS_ENTIRE_TILE;
+ }
+
+ // Construct the output triangle set.
+ out_elements = new osg::DrawElementsUInt(tris->getMode());
+ out_elements->reserve(tris->size());
+
+ const osg::MixinVector<GLuint> ins = tris->asVector();
- return elems.release();
+ for (osg::MixinVector<GLuint>::const_iterator it = ins.begin(); it != ins.end(); ++it)
+ {
+ unsigned i0 = vertsOffset + *it++;
+ unsigned i1 = vertsOffset + *it++;
+ unsigned i2 = vertsOffset + *it;
+
+ const osg::Vec3d& v0 = (*verts)[i0];
+ const osg::Vec3d& v1 = (*verts)[i1];
+ const osg::Vec3d& v2 = (*verts)[i2];
+
+ // check the winding order. Triangles don't always come out in the right orientation
+ if (((v0 - v1) ^ (v2 - v1)).z() < 0)
+ {
+ out_elements->push_back(i0);
+ out_elements->push_back(i1);
+ out_elements->push_back(i2);
+ }
+ else
+ {
+ out_elements->push_back(i0);
+ out_elements->push_back(i2);
+ out_elements->push_back(i1);
}
}
- return 0L;
+ return R_BOUNDARY_INTERSECTS_TILE;
}
void
MaskGenerator::getMinMax(osg::Vec3d& min, osg::Vec3d& max)
{
-#if 1
min = _ndcMin;
max = _ndcMax;
-
-#else
- if (_maskRecords.size() > 0)
- {
- min.x() = _maskRecords[0]._ndcMin.x();
- min.y() = _maskRecords[0]._ndcMin.y();
- min.z() = _maskRecords[0]._ndcMin.z();
-
- max.x() = _maskRecords[0]._ndcMax.x();
- max.y() = _maskRecords[0]._ndcMax.y();
- max.z() = _maskRecords[0]._ndcMax.z();
-
- for (MaskRecordVector::const_iterator it = _maskRecords.begin(); it != _maskRecords.end(); ++it)
- {
- if (it->_ndcMin.x() < min.x()) min.x() = it->_ndcMin.x();
- if (it->_ndcMin.y() < min.y()) min.y() = it->_ndcMin.y();
- if (it->_ndcMin.z() < min.z()) min.z() = it->_ndcMin.z();
-
- if (it->_ndcMax.x() > max.x()) max.x() = it->_ndcMax.x();
- if (it->_ndcMax.y() > max.y()) max.y() = it->_ndcMax.y();
- if (it->_ndcMax.z() > max.z()) max.z() = it->_ndcMax.z();
- }
- }
-#endif
}
float
@@ -527,14 +691,14 @@ MaskGenerator::getMarker(float nx, float ny) const
if (i > min_i && i < max_i && j > min_j && j < max_j)
{
- marker = MASK_MARKER_DISCARD; // contained by mask
+ marker = MASK_MARKER_DISCARD; // contained by boundary
}
else if ((i == min_i && j >= min_j && j <= max_j) ||
(i == max_i && j >= min_j && j <= max_j) ||
(j == min_j && i >= min_i && i <= max_i) ||
(j == max_j && i >= min_i && i <= max_i))
{
- marker = MASK_MARKER_SKIRT; // tile vert on outer mask skirt boundary
+ marker = MASK_MARKER_PATCH;
}
}
=====================================
src/osgEarthDrivers/engine_rex/RexEngine.elevation.glsl
=====================================
--- a/src/osgEarthDrivers/engine_rex/RexEngine.elevation.glsl
+++ b/src/osgEarthDrivers/engine_rex/RexEngine.elevation.glsl
@@ -10,7 +10,7 @@
// Vertex Markers:
#define MASK_MARKER_DISCARD 0.0
#define MASK_MARKER_NORMAL 1.0
-#define MASK_MARKER_SKIRT 2.0
+#define MASK_MARKER_PATCH 2.0
#define MASK_MARKER_BOUNDARY 3.0
// stage
=====================================
src/osgEarthDrivers/engine_rex/RexTerrainEngineNode
=====================================
--- a/src/osgEarthDrivers/engine_rex/RexTerrainEngineNode
+++ b/src/osgEarthDrivers/engine_rex/RexTerrainEngineNode
@@ -93,6 +93,10 @@ namespace osgEarth { namespace Drivers { namespace RexTerrainEngine
osg::BoundingSphere computeBound() const;
+ void resizeGLObjectBuffers(unsigned maxSize);
+
+ void releaseGLObjects(osg::State* state) const;
+
public: // MapCallback adapter functions
void onMapModelChanged( const MapModelChange& change ); // not virtual!
=====================================
src/osgEarthDrivers/engine_rex/RexTerrainEngineNode.cpp
=====================================
--- a/src/osgEarthDrivers/engine_rex/RexTerrainEngineNode.cpp
+++ b/src/osgEarthDrivers/engine_rex/RexTerrainEngineNode.cpp
@@ -190,6 +190,46 @@ RexTerrainEngineNode::~RexTerrainEngineNode()
OE_DEBUG << LC << "~RexTerrainEngineNode\n";
}
+void
+RexTerrainEngineNode::resizeGLObjectBuffers(unsigned maxSize)
+{
+ getStateSet()->resizeGLObjectBuffers(maxSize);
+
+ _terrain->getStateSet()->resizeGLObjectBuffers(maxSize);
+
+ _imageLayerStateSet.get()->resizeGLObjectBuffers(maxSize);
+
+ // TODO: where should this live? MapNode?
+ LayerVector layers;
+ getMap()->getLayers(layers);
+ for (LayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i)
+ {
+ if ((*i)->getStateSet()) {
+ (*i)->getStateSet()->resizeGLObjectBuffers(maxSize);
+ }
+ }
+}
+
+void
+RexTerrainEngineNode::releaseGLObjects(osg::State* state) const
+{
+ getStateSet()->releaseGLObjects(state);
+
+ _terrain->getStateSet()->releaseGLObjects(state);
+
+ _imageLayerStateSet.get()->releaseGLObjects(state);
+
+ // TODO: where should this live? MapNode?
+ LayerVector layers;
+ getMap()->getLayers(layers);
+ for (LayerVector::const_iterator i = layers.begin(); i != layers.end(); ++i)
+ {
+ if ((*i)->getStateSet()) {
+ (*i)->getStateSet()->releaseGLObjects(state);
+ }
+ }
+}
+
void
RexTerrainEngineNode::setMap(const Map* map, const TerrainOptions& options)
{
=====================================
src/osgEarthDrivers/mbtiles/MBTilesTileSource.cpp
=====================================
--- a/src/osgEarthDrivers/mbtiles/MBTilesTileSource.cpp
+++ b/src/osgEarthDrivers/mbtiles/MBTilesTileSource.cpp
@@ -193,10 +193,6 @@ MBTilesTileSource::initialize(const osgDB::Options* dbOptions)
if ( _tileFormat.empty() )
return Status::Error(Status::ConfigurationError, "Required format not in metadata, nor specified in the options.");
- _rw = getReaderWriter( _tileFormat );
- if ( !_rw.valid() )
- return Status::Error(Status::ServiceUnavailable, "No plugin to load format \"" + _tileFormat + "\"");
-
// check for compression.
std::string compression;
getMetaData("compression", compression);
@@ -361,7 +357,10 @@ MBTilesTileSource::createImage(const TileKey& key,
std::string value;
if ( !_compressor->decompress(inputStream, value) )
{
- OE_WARN << LC << "Decompression failed" << std::endl;
+ if ( _options.filename().isSet() )
+ OE_WARN << LC << "Decompression failed: " << _options.filename()->base() << std::endl;
+ else
+ OE_WARN << LC << "Decompression failed" << std::endl;
valid = false;
}
else
@@ -374,11 +373,7 @@ MBTilesTileSource::createImage(const TileKey& key,
if ( valid )
{
std::istringstream inputStream(dataBuffer);
- osgDB::ReaderWriter::ReadResult rr = _rw->readImage( inputStream, _dbOptions.get() );
- if (rr.validImage())
- {
- result = rr.takeImage();
- }
+ result = ImageUtils::readStream(inputStream, _dbOptions.get());
}
}
else
=====================================
src/osgEarthSymbology/Geometry.cpp
=====================================
--- a/src/osgEarthSymbology/Geometry.cpp
+++ b/src/osgEarthSymbology/Geometry.cpp
@@ -633,6 +633,9 @@ Geometry::getOrientation() const
double
Geometry::getLength() const
{
+ if (empty())
+ return 0.0;
+
double length = 0;
for (unsigned int i = 0; i < size()-1; ++i)
{
@@ -746,6 +749,9 @@ Ring::cloneAs( const Geometry::Type& newType ) const
double
Ring::getLength() const
{
+ if (empty())
+ return 0.0;
+
double length = Geometry::getLength();
if ( isOpen() )
{
=====================================
src/osgEarthUtil/Controls.cpp
=====================================
--- a/src/osgEarthUtil/Controls.cpp
+++ b/src/osgEarthUtil/Controls.cpp
@@ -2741,12 +2741,14 @@ ControlCanvas::init()
_controlNodeBin = new ControlNodeBin();
this->addChild( _controlNodeBin->getControlGroup() );
+#if 0
#if defined(OSG_GL_FIXED_FUNCTION_AVAILABLE)
// don't use shaders unless we have to.
this->getOrCreateStateSet()->setAttributeAndModes(
new osg::Program(),
osg::StateAttribute::OFF|osg::StateAttribute::OVERRIDE);
#endif
+#endif
}
ControlCanvas::~ControlCanvas()
=====================================
src/osgEarthUtil/TopologyGraph
=====================================
--- a/src/osgEarthUtil/TopologyGraph
+++ b/src/osgEarthUtil/TopologyGraph
@@ -39,86 +39,134 @@ namespace osgEarth { namespace Util
//! Stores the noded topology of a model with unique verticies and edge definitions.
//! The verticies are stored rotated into the XY plane so that we can properly find
//! the "bottom-most" vert and walk the boundary.
- class OSGEARTHUTIL_EXPORT TopologyGraph
+ class OSGEARTHUTIL_EXPORT TopologyGraph : public osg::Object
{
public:
- struct Vertex {
- Vertex(const Vertex& rhs) : _drawable(rhs._drawable), _verts(rhs._verts), _index(rhs._index) { }
- Vertex(osg::Drawable* drawable, osg::Vec3Array* verts, unsigned index) : _drawable(drawable), _verts(verts), _index(index) { }
- osg::Drawable* _drawable;
- osg::Vec3Array* _verts;
- unsigned _index;
+ //! Represents a single vertex in the topology graph
+ struct Vertex
+ {
+ Vertex(const Vertex& rhs) : _verts(rhs._verts), _index(rhs._index), _graphID(0u) { }
+
+ Vertex(const osg::Vec3Array* verts, unsigned index) : _verts(verts), _index(index), _graphID(0u) { }
+
const osg::Vec3& vertex() const { return (*_verts)[_index]; }
+
+ unsigned index() const { return _index; }
+
float x() const { return (*_verts)[_index].x(); }
float y() const { return (*_verts)[_index].y(); }
+
bool operator < (const Vertex& rhs) const {
- double dx = x() - rhs.x();
- if (dx < 0.0) return true;
- if (dx > 0.0) return false;
- double dy = y() - rhs.y();
- return dy < 0.0;
+ //if (_verts < rhs._verts) return true;
+ //if (_verts > rhs._verts) return false;
+ //return _index < rhs._index;
+ if (x() < rhs.x()) return true;
+ if (x() > rhs.x()) return false;
+ return y() < rhs.y();
}
+
+ const osg::Vec3Array* _verts; //! vertex array from which this vertex originated
+ unsigned _index; //! index into the source vertex array
+ mutable unsigned _graphID; //! which graph does this vertex belong to (in the event of multiple graphs)
};
+ //! One or more Vertex objects
typedef std::set<Vertex> VertexSet;
+ //! Iterator into a VertexSet.
typedef VertexSet::iterator Index;
- // custom comparator for Index so we can use it at a std::map key
+ //! Custom comparator for Index so we can use it at a std::map key
struct IndexLess : public std::less<Index> {
bool operator()(const Index& lhs, const Index& rhs) const {
return (*lhs) < (*rhs);
}
};
+ //! Sortable set of indices
typedef std::set<Index, IndexLess> IndexSet;
- typedef std::map<Index, IndexSet, IndexLess> EdgeMap;
+ //! Maps a Vertex Index to a collection of Vertex indices (edges)
+ typedef std::map<Index, IndexSet, IndexLess> EdgeMap;
+ //! Vector of Vertex Indexes
typedef std::vector<Index> IndexVector;
public:
+ META_Object(osgEarthUtil, TopologyGraph);
+
+ //! CTOR
TopologyGraph();
- void createBoundary(IndexVector& output) const;
+ //! How many disconnected graphs are there in this topology?
+ unsigned getNumBoundaries() const;
+
+ //! Creates the boundary of the nth disconnected graph
+ void createBoundary(unsigned boundaryNum, IndexVector& output) const;
+
+ public:
+ void addTriangle(const osg::Vec3Array* verts, unsigned v0, unsigned v1, unsigned v2);
+
+ Index add(const osg::Vec3Array* verts, unsigned v);
+
+ void assignAndPropagate(TopologyGraph::Index& vertex, unsigned graphID);
public:
unsigned _totalVerts; // total number of verts encountered
- //VertexSet _vertsWorld; //
VertexSet _verts; // set of unique verts in the topology (rotated into XY plane)
EdgeMap _edgeMap; // maps each vert to all the verts with which it shares an edge
- Index _minY; // points to the vert with the minimum Y coordinate (in XY plane)
- osg::Matrixd _world2plane; // matrix that transforms into a localized XY plane
- const osgEarth::SpatialReference* _srs;
+ unsigned _maxGraphID; // maximum graph id from builder (1=one graph)
friend class TopologyBuilder;
friend class BuildTopologyVisitor;
- void dumpBoundary(const std::string& filename);
+ void dumpBoundary(const IndexVector& boundary, const std::string& filename);
+
+ protected:
+ // no copy ctor
+ TopologyGraph(const TopologyGraph& rhs, const osg::CopyOp& copy) { }
+
+ virtual ~TopologyGraph() { }
+
+ unsigned recomputeSubgraphs();
};
+ typedef std::vector< osg::ref_ptr<TopologyGraph> > TopologyGraphVector;
// A TriangleIndexFunctor that traverses a stream of triangles and builds a
// topology graph from their points and edges.
class OSGEARTHUTIL_EXPORT TopologyBuilder
{
public:
+ //! CTOR
TopologyBuilder();
+ //! Creates a new topology graph from a single set of triangles.
+ //! @param[in ] verts Vertex array input
+ //! @param[in ] elems Elements defining triangle set
+ //! @param[in ] name Optional name for error/warning output
+ //! @return New TopologyGraph object
+ static TopologyGraph* create(
+ const osg::Vec3Array* verts,
+ const osg::PrimitiveSet* elems,
+ const std::string& name = std::string());
+
typedef std::map<unsigned, TopologyGraph::Index> UniqueMap;
- TopologyGraph* _topology; // topology to which to append point and edge data
- osg::Drawable* _drawable; // source geometry
- osg::Vec3Array* _verts; // source vertex list
- osg::Matrix _local2world; // transforms source verts into world coordinates
- UniqueMap _uniqueMap; // prevents duplicates
+ TopologyGraph* _graph; // topology to which to append point and edge data
+ const osg::Drawable* _drawable; // source geometry
+ const osg::Vec3Array* _verts; // source vertex list
+ UniqueMap _uniqueMap; // prevents duplicates
+ // entry point for the TriangleIndexFunctor
void operator()( unsigned v0, unsigned v1, unsigned v2 );
TopologyGraph::Index add( unsigned v );
+ void assignAndPropagate(TopologyGraph::Index& vertex, unsigned graphID);
+
friend class TopologyBuilderVisitor;
};
@@ -136,10 +184,11 @@ namespace osgEarth { namespace Util
// add the contents of a Geometry to the topology
void apply( osg::Drawable& drawable );
+ // ???
void apply( osg::Drawable* drawable, osg::Vec3Array* verts );
std::vector<osg::Matrixd> _matrixStack;
- TopologyGraph& _topology;
+ TopologyGraph& _graph;
};
} }
=====================================
src/osgEarthUtil/TopologyGraph.cpp
=====================================
--- a/src/osgEarthUtil/TopologyGraph.cpp
+++ b/src/osgEarthUtil/TopologyGraph.cpp
@@ -36,62 +36,56 @@ using namespace osgEarth::Util;
#include <vector>
#include <set>
+#define LC "[TopologyGraph] "
+
TopologyGraph::TopologyGraph() :
-_minY(_verts.end()),
-_totalVerts(0),
-_srs(0L)
+_maxGraphID(0u)
{
//nop
}
+unsigned
+TopologyGraph::getNumBoundaries() const
+{
+ return _maxGraphID;
+}
void
-TopologyGraph::createBoundary(TopologyGraph::IndexVector& output) const
+TopologyGraph::createBoundary(unsigned graphNum, TopologyGraph::IndexVector& output) const
{
- // the normal defines the XY plane in which to search for a boundary
- osg::Vec3d normal(0,0,1);
- osg::Vec3d center;
-
-#if 0
- if ( geocentric )
+ // nothing to do - bail
+ if (_verts.empty() || graphNum+1 > _maxGraphID)
+ return;
+
+ // graph ID is one more than the graph number passed in:
+ unsigned graphID = graphNum + 1u;
+
+ // Find the starting point (vertex with minimum Y) for this graph ID.
+ // By the nature of disconnected graphs, that start point is all we need
+ // to ensure we are walking a single connected mesh.
+ Index vstart = _verts.end();
+ for (VertexSet::const_iterator vert = _verts.begin(); vert != _verts.end(); ++vert)
{
- // define the XY plane based on the normal to the center of the dataset:
- osg::BoundingSphere bs = node->getBound();
- center = bs.center();
- normal = center;
- normal.normalize();
- OE_DEBUG << "Normal = " << normal.x() << ", " << normal.y() << ", " << normal.z() << std::endl;
- }
-#endif
-
-
-#if 0
- // set up a transform that will localize geometry into an XY plane
- if ( geocentric )
- {
- topology._world2plane.makeRotate(normal, osg::Vec3d(0,0,1));
- topology._world2plane.preMultTranslate(-center);
-
- // if this is set, use mercator projection instead of a simple geolocation
- //topology._srs = osgEarth::SpatialReference::get("spherical-mercator");
+ if (vert->_graphID == graphID)
+ {
+ if (vstart == _verts.end() || vert->y() < vstart->y())
+ {
+ vstart = vert;
+ }
+ }
}
-#endif
-
- // build the topology
- //BuildTopologyVisitor buildTopoVisitor(topology);
- //node->accept( buildTopoVisitor );
- //OE_DEBUG << "Found " << topology._verts.size() << " unique verts" << std::endl;
- //dumpPointCloud(topology);
+ // couldn't find a start point - bail (should never happen)
+ if (vstart == _verts.end())
+ return;
// starting with the minimum-Y vertex (which is guaranteed to be in the boundary)
// traverse the outside of the point set. Do this by sorting all the edges by
- // their angle relative to the vector to the previous point. The vector with the
- // smallest angle represents the edge connecting the current point to the next
- // boundary point. Walk the edge until we return to the beginning.
-
- Index vptr = _minY;
+ // their angle relative to the vector from the previous point. The "leftest" turn
+ // represents the edge connecting the current point to the next boundary point.
+ // Thusly we walk the boundary counterclockwise until we return to the start point.
+ Index vptr = vstart;
Index vptr_prev = _verts.end();
IndexSet visited;
@@ -105,29 +99,36 @@ TopologyGraph::createBoundary(TopologyGraph::IndexVector& output) const
osg::Vec2d vert ( vptr->x(), vptr->y() );
// construct the "base" vector that points from the previous
- // point to the current point; or to -X in the initial case
+ // point to the current point; or to +X in the initial case
osg::Vec2d base;
if ( vptr_prev == _verts.end() )
- base.set( -1, 0 );
+ {
+ base.set(1, 0);
+ }
else
+ {
base = vert - osg::Vec2d( vptr_prev->x(), vptr_prev->y() );
+ base.normalize();
+ }
// pull up the edge set for this vertex:
EdgeMap::const_iterator ei = _edgeMap.find(vptr);
if (ei == _edgeMap.end())
continue; // should be impossible
- const IndexSet& edges = ei->second; //_edgeMap[vptr];
+ const IndexSet& edges = ei->second;
// find the edge with the minimun delta angle to the base vector
- double bestScore = DBL_MAX;
+ double bestScore = -DBL_MAX;
Index bestEdge = _verts.end();
- //OE_DEBUG << "VERTEX (" <<
- // vptr->x() << ", " << vptr->y() << ", " << vptr->z()
+ //OE_NOTICE << "VERTEX (" <<
+ // vptr->x() << ", " << vptr->y() << ", "
// << ") has " << edges.size() << " edges..."
// << std::endl;
+ unsigned possibleEdges = 0u;
+
for( IndexSet::iterator e = edges.begin(); e != edges.end(); ++e )
{
// don't go back from whence we just came
@@ -138,28 +139,28 @@ TopologyGraph::createBoundary(TopologyGraph::IndexVector& output) const
if ( visited.find(*e) != visited.end() )
continue;
+ ++possibleEdges;
+
// calculate the angle between the base vector and the current edge:
osg::Vec2d edgeVert( (*e)->x(), (*e)->y() );
osg::Vec2d edge = edgeVert - vert;
-
- base.normalize();
edge.normalize();
+
double cross = base.x()*edge.y() - base.y()*edge.x();
double dot = base * edge;
- double score = dot;
- if ( cross < 0.0 )
- {
- double diff = 2.0-(score+1.0);
- score = 1.0 + diff;
- }
+ double score;
+ if (cross <= 0.0)
+ score = 1.0-dot; // [0..2]
+ else
+ score = dot-1.0; // [-2..0]
- //OE_DEBUG << " check: " << (*e)->x() << ", " << (*e)->y() << ", " << (*e)->z() << std::endl;
- //OE_DEBUG << " base = " << base.x() << ", " << base.y() << std::endl;
- //OE_DEBUG << " edge = " << edge.x() << ", " << edge.y() << std::endl;
- //OE_DEBUG << " crs = " << cross << ", dot = " << dot << ", score = " << score << std::endl;
+ //OE_NOTICE << " check: " << (*e)->x() << ", " << (*e)->y() << std::endl;
+ //OE_NOTICE << " base = " << base.x() << ", " << base.y() << std::endl;
+ //OE_NOTICE << " edge = " << edge.x() << ", " << edge.y() << std::endl;
+ //OE_NOTICE << " crs = " << cross << ", dot = " << dot << ", score = " << score << std::endl;
- if ( score < bestScore )
+ if (score > bestScore)
{
bestScore = score;
bestEdge = *e;
@@ -168,8 +169,11 @@ TopologyGraph::createBoundary(TopologyGraph::IndexVector& output) const
if ( bestEdge == _verts.end() )
{
- // this will probably never happen
- osg::notify(osg::WARN) << "Illegal state - reached a dead end!" << std::endl;
+ // this should never happen
+ // but sometimes does anyway
+ OE_WARN << LC << getName() << " - Illegal state - reached a dead end during boundary detection. Vertex ("
+ << vptr->x() << ", " << vptr->y() << ") has " << possibleEdges << " possible edges.\n"
+ << std::endl;
break;
}
@@ -184,35 +188,16 @@ TopologyGraph::createBoundary(TopologyGraph::IndexVector& output) const
visited.insert( vptr );
// once we make it all the way around, we're done:
- if ( vptr == _minY )
+ if ( vptr == vstart )
break;
}
-
-#if 0
- // un-rotate the results from the XY plane back to their original frame:
- if ( _srs )
- {
- const osgEarth::SpatialReference* ecef = _srs->getECEF();
- topology._srs->transform(_result->asVector(), ecef);
- }
- else
- {
- osg::Matrix plane2world;
- plane2world.invert( _world2plane );
- for( osg::Vec3dArray::iterator i = _result->begin(); i != _result->end(); ++i )
- (*i) = (*i) * plane2world;
- }
-
- return _result.release();
-#endif
}
-
void
-TopologyGraph::dumpBoundary(const std::string& filename)
+TopologyGraph::dumpBoundary(const IndexVector& boundary, const std::string& filename)
{
- IndexVector boundary;
- createBoundary(boundary);
+ if (boundary.empty())
+ return;
osg::Vec3Array* v = new osg::Vec3Array();
@@ -224,13 +209,12 @@ TopologyGraph::dumpBoundary(const std::string& filename)
osg::ref_ptr<osg::Geometry> g = new osg::Geometry();
g->setVertexArray(v);
- g->addPrimitiveSet(new osg::DrawArrays(GL_LINE_STRIP, 0, v->size()));
+ g->addPrimitiveSet(new osg::DrawArrays(GL_LINE_LOOP, 0, v->size()));
g->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, v->size()));
g->getOrCreateStateSet()->setAttributeAndModes(new osg::Point(3));
osgDB::writeNodeFile(*(g.get()), filename);
}
-
TopologyBuilder::TopologyBuilder()
{
//nop
@@ -239,17 +223,44 @@ TopologyBuilder::TopologyBuilder()
void
TopologyBuilder::operator()(unsigned v0, unsigned v1, unsigned v2)
{
+ // Add three verts to the graph. Any of them may already exist
+ // in the graph. If so, make sure the existing graph IDs get
+ // properly propagated.
TopologyGraph::Index i0 = add(v0);
TopologyGraph::Index i1 = add(v1);
TopologyGraph::Index i2 = add(v2);
+ if (i0->_graphID == 0u && i1->_graphID == 0u && i2->_graphID == 0u)
+ {
+ unsigned newGraphID = ++_graph->_maxGraphID;
+ i0->_graphID = i1->_graphID = i2->_graphID = newGraphID;
+ }
+ else
+ {
+ if (i0->_graphID != 0u)
+ {
+ assignAndPropagate(i1, i0->_graphID);
+ assignAndPropagate(i2, i0->_graphID);
+ }
+ else if (i1->_graphID != 0u)
+ {
+ assignAndPropagate(i0, i1->_graphID);
+ assignAndPropagate(i2, i1->_graphID);
+ }
+ else
+ {
+ assignAndPropagate(i0, i2->_graphID);
+ assignAndPropagate(i1, i2->_graphID);
+ }
+ }
+
// add to the edge list for each of these verts
- if (i0 != i1) _topology->_edgeMap[i0].insert(i1);
- if (i0 != i2) _topology->_edgeMap[i0].insert(i2);
- if (i1 != i0) _topology->_edgeMap[i1].insert(i0);
- if (i1 != i2) _topology->_edgeMap[i1].insert(i2);
- if (i2 != i0) _topology->_edgeMap[i2].insert(i0);
- if (i2 != i1) _topology->_edgeMap[i2].insert(i1);
+ if (i0 != i1) _graph->_edgeMap[i0].insert(i1);
+ if (i0 != i2) _graph->_edgeMap[i0].insert(i2);
+ if (i1 != i0) _graph->_edgeMap[i1].insert(i0);
+ if (i1 != i2) _graph->_edgeMap[i1].insert(i2);
+ if (i2 != i0) _graph->_edgeMap[i2].insert(i0);
+ if (i2 != i1) _graph->_edgeMap[i2].insert(i1);
}
TopologyGraph::Index
@@ -259,51 +270,8 @@ TopologyBuilder::add(unsigned v)
UniqueMap::iterator i = _uniqueMap.find(v);
if (i == _uniqueMap.end())
{
-#if 0
- // no, so transform it into world coordinates, and rotate it into the XY plane
- osg::Vec3d vert = (*_verts)[v];
- osg::Vec3d world = vert * _local2world;
- osg::Vec3d plane = world;
-
- if (_topology->_srs)
- {
- const osgEarth::SpatialReference* ecef = _topology->_srs->getECEF();
- ecef->transform(world, _topology->_srs, plane);
- }
- else
- {
- plane = world * _topology->_world2plane;
- }
-#endif
-
- TopologyGraph::Vertex vertex(_drawable, _verts, v);
- std::pair<TopologyGraph::Index, bool> f = _topology->_verts.insert(vertex);
- if (f.second == true) // insert succeeded
- {
- // this is a new location, so check it to see if it is the new "southernmost" point:
- if (_topology->_minY == _topology->_verts.end() || vertex.y() < _topology->_minY->y())
- {
- _topology->_minY = f.first;
- }
- }
-
-#if 0
- // insert it into the unique vert list
- std::pair<TopologyGraph::VertexSet::iterator, bool> f = _topology->_verts.insert(plane);
- if (f.second) // insert succedded
- {
- // this is a new location, so check it to see if it is the new "southernmost" point:
- if (_topology->_minY == _topology->_verts.end() || plane.y() < _topology->_minY->y())
- {
- _topology->_minY = f.first;
- }
- }
-#endif
-
- // store in the uniqueness map so we don't process the same index again
- _uniqueMap[v] = f.first;
-
- // return the index of the vert.
+ TopologyGraph::Vertex vertex(_verts, v);
+ std::pair<TopologyGraph::Index, bool> f = _graph->_verts.insert(vertex);
return f.first;
}
else
@@ -312,12 +280,49 @@ TopologyBuilder::add(unsigned v)
}
}
+void
+TopologyBuilder::assignAndPropagate(TopologyGraph::Index& vertex, unsigned graphID)
+{
+ // already set, so no need to continue propagating
+ if (vertex->_graphID == graphID)
+ return;
+
+ // assign
+ vertex->_graphID = graphID;
+
+ // propagate along all edges
+ TopologyGraph::EdgeMap::iterator edges = _graph->_edgeMap.find(vertex);
+ if (edges != _graph->_edgeMap.end())
+ {
+ TopologyGraph::IndexSet& endPoints = edges->second;
+ for (TopologyGraph::IndexSet::iterator endPoint = endPoints.begin();
+ endPoint != endPoints.end();
+ ++endPoint)
+ {
+ TopologyGraph::Index vertex = *endPoint;
+ assignAndPropagate(vertex, graphID);
+ }
+ }
+}
+
+TopologyGraph*
+TopologyBuilder::create(const osg::Vec3Array* verts, const osg::PrimitiveSet* elems, const std::string& name)
+{
+ osg::ref_ptr<TopologyGraph> graph = new TopologyGraph();
+ graph->setName(name);
+ osg::TriangleIndexFunctor<TopologyBuilder> builder;
+ builder.setVertexArray(verts->getNumElements(), static_cast<const osg::Vec3*>(verts->getDataPointer()));
+ builder._verts = verts;
+ builder._graph = graph.get();
+ elems->accept(builder);
+ return graph.release();
+}
BuildTopologyVisitor::BuildTopologyVisitor(TopologyGraph& graph) :
osg::NodeVisitor(),
-_topology(graph)
+_graph(graph)
{
setTraversalMode(TRAVERSE_ALL_CHILDREN);
setNodeMaskOverride(~0);
@@ -352,629 +357,10 @@ void
BuildTopologyVisitor::apply(osg::Drawable* drawable, osg::Vec3Array* verts)
{
osg::TriangleIndexFunctor<TopologyBuilder> builder;
- builder._topology = &_topology;
+ builder._graph = &_graph;
builder._drawable = drawable;
builder._verts = verts;
- if (!_matrixStack.empty())
- builder._local2world = _matrixStack.back();
- _topology._totalVerts += verts->size();
+ //if (!_matrixStack.empty())
+ // builder._local2world = _matrixStack.back();
drawable->accept(builder);
}
-
-#if 0
-
-/* Comparator used to sort osg::Vec3d's first by x and then by y */
-bool presortCompare (osg::Vec3d i, osg::Vec3d j)
-{
- if (i.x() == j.x())
- return i.y() < j.y();
-
- return i.x() < j.x();
-}
-
-double BoundaryUtil::_tolerance = 0.005;
-
-void
-BoundaryUtil::setTolerance(double value)
-{
- _tolerance = value;
-}
-
-/* Use the vertices of the given node to calculate a boundary via the
- * findHull() method.
- */
-osg::Vec3dArray* BoundaryUtil::getBoundary(osg::Node* modelNode, bool geocentric, bool convexHull)
-{
- if (!modelNode)
- return 0;
-
- if ( convexHull )
- {
- VertexCollectionVisitor v(geocentric);
- modelNode->accept(v);
-
- osg::ref_ptr<osg::Vec3dArray> verts = v.getVertices();
- verts = findHull(*verts);
-
- osg::EllipsoidModel em;
- for( osg::Vec3dArray::iterator i = verts->begin(); i != verts->end(); ++i )
- {
- em.convertLatLongHeightToXYZ( osg::DegreesToRadians(i->y()), osg::DegreesToRadians(i->x()), i->z(),
- i->x(), i->y(), i->z() );
- }
-
- return verts.release();
- }
- else
- {
- return findMeshBoundary( modelNode, geocentric );
- }
-}
-
-/* Finds the convex hull for the given points using the Andrew's monotone
- * chian algorithm. Returns an ordered set of points defining the hull
- *
- * Implementation based on chainHull_2D() method from
- * softSurfer (www.softsurfer.com)
- */
-osg::Vec3dArray* BoundaryUtil::findHull(osg::Vec3dArray& points)
-{
- if (points.size() == 0)
- return 0;
-
- // the output array hull will be used as the stack
- osg::Vec3dArray* hull = new osg::Vec3dArray(points.size());
-
- // Presort the points as required by the algorithm
- osg::ref_ptr<osg::Vec3dArray> sorted = hullPresortPoints(points);
-
- int bot=0, top=(-1); // indices for bottom and top of the stack
- int i; // array scan index
- int n = sorted->size();
-
- // Get the indices of points with min x-coord and min|max y-coord
- int minmin = 0, minmax;
- double xmin = (*sorted)[0].x();
- for (i=1; i<n; i++)
- if ((*sorted)[i].x() != xmin) break;
- minmax = i-1;
-
- //if the points at minmin and minmax have the same value, shift minmin
- //to ignore the duplicate points
- if ((*sorted)[minmin] == (*sorted)[minmax])
- minmin = minmax;
-
- // degenerate case: all x-coords == xmin
- if (minmax == n-1)
- {
- (*hull)[++top] = (*sorted)[minmin];
- if ((*sorted)[minmax].y() != (*sorted)[minmin].y()) // a nontrivial segment
- (*hull)[++top] = (*sorted)[minmax];
- (*hull)[++top] = (*sorted)[minmin]; // add polygon endpoint
-
- hull->resize(top + 1);
- return hull;
- }
-
- // Get the indices of points with max x-coord and min|max y-coord
- int maxmin, maxmax = n-1;
- double xmax = (*sorted)[n-1].x();
- for (i=n-2; i>=0; i--)
- if ((*sorted)[i].x() != xmax) break;
- maxmin = i+1;
-
- // Compute the lower hull on the stack
- (*hull)[++top] = (*sorted)[minmin]; // push minmin point onto stack
- i = minmax;
- while (++i <= maxmin)
- {
- // the lower line joins (*sorted)[minmin] with (*sorted)[maxmin]
-
- // if multiple points have the same x/y values, go with the lowest z value
- if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*sorted)[i].y())
- {
- if ((*sorted)[i].z() < (*hull)[top].z())
- (*hull)[top].z() = (*sorted)[i].z();
-
- continue;
- }
-
- if (isLeft((*sorted)[minmin], (*sorted)[maxmin], (*sorted)[i]) > 0 && i < maxmin)
- continue; // ignore (*sorted)[i] above the lower line. NOTE: Differs from original CH algorithm in that it keeps collinear points
-
- while (top > 0) // there are at least 2 points on the stack
- {
- // test if (*sorted)[i] is left of or on the line at the stack top. NOTE: Differs from original CH algorithm in that it keeps collinear points
- if (isLeft((*hull)[top-1], (*hull)[top], (*sorted)[i]) >= 0)
- break; // (*sorted)[i] is a new hull vertex
- else
- top--; // pop top point off stack
- }
- (*hull)[++top] = (*sorted)[i]; // push (*sorted)[i] onto stack
- }
-
- if (maxmax != maxmin) // if distinct xmax points
- {
- // Push all points between maxmin and maxmax onto stack. NOTE: Differs from original CH algorithm in that it keeps collinear points
- while (i <= maxmax)
- {
- if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*hull)[top].y())
- {
- if ((*sorted)[i].z() < (*hull)[top].z())
- (*hull)[top].z() = (*sorted)[i].z();
- }
- else
- {
- (*hull)[++top] = (*sorted)[i];
- }
-
- i++;
- }
- }
-
- // Next, compute the upper hull on the stack H above the bottom hull
-
- bot = top; // the bottom point of the upper hull stack
- i = maxmin;
- while (--i >= minmax)
- {
- // the upper line joins (*sorted)[maxmax] with (*sorted)[minmax]
-
- // if multiple points have the same x/y values, go with the lowest z value
- if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*sorted)[i].y())
- {
- if ((*sorted)[i].z() < (*hull)[top].z())
- (*hull)[top].z() = (*sorted)[i].z();
-
- continue;
- }
-
- if (isLeft((*sorted)[maxmax], (*sorted)[minmax], (*sorted)[i]) > 0 && i > minmax)
- continue; // ignore (*sorted)[i] below the upper line. NOTE: Differs from original CH algorithm in that it keeps collinear points
-
- while (top > bot) // at least 2 points on the upper stack
- {
- // test if (*sorted)[i] is left of or on the line at the stack top. NOTE: Differs from original CH algorithm in that it keeps collinear points
- if (isLeft((*hull)[top-1], (*hull)[top], (*sorted)[i]) >= 0)
- break; // (*sorted)[i] is a new hull vertex
- else
- top--; // pop top point off stack
- }
- (*hull)[++top] = (*sorted)[i]; // push (*sorted)[i] onto stack
- }
-
- // If minmax and minmin are the same, remove the duplicate point
- if (minmax == minmin)
- {
- top--;
- }
- else
- {
- // Push all points between minmax and minmin onto stack. NOTE: Differs from original CH algorithm in that it keeps collinear points
- while (i > minmin)
- {
- if ((*hull)[top].x() == (*sorted)[i].x() && (*hull)[top].y() == (*hull)[top].y())
- {
- if ((*sorted)[i].z() < (*hull)[top].z())
- (*hull)[top].z() = (*sorted)[i].z();
- }
- else
- {
- (*hull)[++top] = (*sorted)[i];
- }
-
- i--;
- }
- }
-
- hull->resize(top + 1);
- return hull;
-}
-
-/* Returns an array containing the points sorted first by x and then by y */
-osg::Vec3dArray* BoundaryUtil::hullPresortPoints(osg::Vec3dArray& points)
-{
- osg::Vec3dArray* sorted = new osg::Vec3dArray(points.begin(), points.end());
- std::sort(sorted->begin(), sorted->end(), presortCompare);
-
- return sorted;
-}
-
-//---------------------------------------------------------------------------
-
-namespace
-{
- // custom comparator for VertexSet.
- struct VertexLess
- {
- bool operator()(const osg::Vec3d& lhs, const osg::Vec3d& rhs) const
- {
- double dx = lhs.x() - rhs.x();
- if ( dx < 0.0 && dx < -BoundaryUtil::getTolerance() ) return true;
- if ( dx > 0.0 && dx > BoundaryUtil::getTolerance() ) return false;
-
- double dy = lhs.y() - rhs.y();
- return (dy < 0.0 && dy < -BoundaryUtil::getTolerance());
- }
- };
-
- typedef std::set<osg::Vec3d, VertexLess> VertexSet;
- typedef VertexSet::iterator Index;
-
- // custom comparator for Index so we can use it at a std::map key
- struct IndexLess : public std::less<Index> {
- bool operator()(const Index& lhs, const Index& rhs ) const {
- return (*lhs) < (*rhs);
- }
- };
-
- typedef std::set<Index, IndexLess> IndexSet;
- typedef std::map<Index, IndexSet, IndexLess> EdgeMap;
-
- // Stores the noded topology of a model with unique verticies and edge definitions.
- // The verticies are stored rotated into the XY plane so that we can properly find
- // the "bottom-most" vert and walk the boundary.
- struct TopologyGraph
- {
- TopologyGraph()
- : _minY( _verts.end() ), _totalVerts(0), _srs(0L) { }
-
- unsigned _totalVerts; // total number of verts encountered
- VertexSet _vertsWorld; //
- VertexSet _verts; // set of unique verts in the topology (rotated into XY plane)
- EdgeMap _edgeMap; // maps each vert to all the verts with which it shares an edge
- Index _minY; // points to the vert with the minimum Y coordinate (in XY plane)
- osg::Matrixd _world2plane; // matrix that transforms into a localized XY plane
- const osgEarth::SpatialReference* _srs;
- };
-
- typedef std::map<unsigned,Index> UniqueMap;
-
- // A TriangleIndexFunctor that traverses a stream of triangles and builds a
- // topology graph from their points and edges.
- struct TopologyBuilder
- {
- TopologyGraph* _topology; // topology to which to append point and edge data
- osg::Vec3Array* _vertexList; // source vertex list
- osg::Matrixd _local2world; // transforms source verts into world coordinates
- UniqueMap _uniqueMap; // prevents duplicates
-
- void operator()( unsigned v0, unsigned v1, unsigned v2 )
- {
- Index i0 = add( v0 );
- Index i1 = add( v1 );
- Index i2 = add( v2 );
-
- // add to the edge list for each of these verts
- if ( i0 != i1 ) _topology->_edgeMap[i0].insert( i1 );
- if ( i0 != i2 ) _topology->_edgeMap[i0].insert( i2 );
- if ( i1 != i0 ) _topology->_edgeMap[i1].insert( i0 );
- if ( i1 != i2 ) _topology->_edgeMap[i1].insert( i2 );
- if ( i2 != i0 ) _topology->_edgeMap[i2].insert( i0 );
- if ( i2 != i1 ) _topology->_edgeMap[i2].insert( i1 );
- }
-
- Index add( unsigned v )
- {
- // first see if we already added the vert at this index.
- UniqueMap::iterator i = _uniqueMap.find( v );
- if ( i == _uniqueMap.end() )
- {
- // no, so transform it into world coordinates, and rotate it into the XY plane
- osg::Vec3d vert = (*_vertexList)[v];
- osg::Vec3d world = vert * _local2world;
- osg::Vec3d plane = world;
-
- if ( _topology->_srs )
- {
- const osgEarth::SpatialReference* ecef = _topology->_srs->getECEF();
- ecef->transform(world, _topology->_srs, plane);
- }
- else
- {
- plane = world * _topology->_world2plane;
- }
-
- // insert it into the unique vert list
- std::pair<VertexSet::iterator,bool> f = _topology->_verts.insert( plane );
- if ( f.second ) // insert succedded
- {
- // this is a new location, so check it to see if it is the new "southernmost" point:
- if ( _topology->_minY == _topology->_verts.end() || plane.y() < _topology->_minY->y() )
- {
- _topology->_minY = f.first;
- }
- }
-
- // store in the uniqueness map so we don't process the same index again
- _uniqueMap[ v ] = f.first;
-
- // return the index of the vert.
- return f.first;
- }
- else
- {
- return i->second;
- }
- }
- };
-
- // Visits a scene graph and builds a topology graph from the verts and edges
- // found within.
- struct BuildTopologyVisitor : public osg::NodeVisitor
- {
- BuildTopologyVisitor( TopologyGraph& topology )
- : osg::NodeVisitor( osg::NodeVisitor::TRAVERSE_ALL_CHILDREN ),
- _topology( topology )
- {
- //nop
- }
-
- // track local transforms so we can build a topology in world coords
- void apply( osg::Transform& xform )
- {
- osg::Matrix matrix;
- if ( !_matrixStack.empty() ) matrix = _matrixStack.back();
- xform.computeLocalToWorldMatrix( matrix, this );
- _matrixStack.push_back( matrix );
- traverse( xform );
- _matrixStack.pop_back();
- }
-
- // add the contents of a geode to the topology
- void apply( osg::Geode& geode )
- {
- for( unsigned i=0; i<geode.getNumDrawables(); ++i )
- {
- osg::Drawable* drawable = geode.getDrawable( i );
- if ( drawable->asGeometry() )
- {
- apply( drawable->asGeometry() );
- }
- }
- }
-
- // add the contents of a Geometry to the topology
- void apply( osg::Geometry* geometry )
- {
- osg::Vec3Array* vertexList = dynamic_cast<osg::Vec3Array*>(geometry->getVertexArray());
- if ( vertexList )
- {
- osg::TriangleIndexFunctor<TopologyBuilder> builder;
- builder._topology = &_topology;
- builder._vertexList = vertexList;
- if ( !_matrixStack.empty() )
- builder._local2world = _matrixStack.back();
- _topology._totalVerts += vertexList->size();
- geometry->accept( builder );
- }
- }
-
- std::vector<osg::Matrixd> _matrixStack;
- TopologyGraph& _topology;
- };
-
- void dumpPointCloud(TopologyGraph& t)
- {
- osg::Vec3Array* v = new osg::Vec3Array();
- osg::DrawElementsUInt* lines = new osg::DrawElementsUInt(GL_LINES);
- unsigned index = 0;
- unsigned minyindex = 0;
- std::map<Index,unsigned, IndexLess> order;
- for(Index i = t._verts.begin(); i != t._verts.end(); ++i, ++index)
- {
- v->push_back( *i );
- if ( i == t._minY )
- minyindex = index;
- order[i] = index;
- }
- index = 0;
- for(Index i = t._verts.begin(); i != t._verts.end(); ++i, ++index)
- {
- IndexSet& edges = t._edgeMap[i];
- for(IndexSet::iterator j=edges.begin(); j!=edges.end(); ++j)
- {
- lines->push_back(order[i]);
- lines->push_back(order[*j]);
- }
- }
- osg::Geometry* g = new osg::Geometry();
- g->setVertexArray(v);
- g->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, 0, v->size()));
- g->addPrimitiveSet(lines);
- g->getOrCreateStateSet()->setAttributeAndModes(new osg::Point(3));
- osg::Geode* n = new osg::Geode();
- n->addDrawable(g);
- osg::Geometry* g2 = new osg::Geometry();
- g2->setVertexArray(v);
- g2->addPrimitiveSet(new osg::DrawArrays(GL_POINTS, minyindex, 1));
- g2->getOrCreateStateSet()->setAttributeAndModes(new osg::Point(10));
- n->addDrawable(g2);
- osgDB::writeNodeFile(*n, "mesh.osg");
- n->unref();
- }
-}
-
-//------------------------------------------------------------------------
-
-osg::Vec3dArray*
-BoundaryUtil::findMeshBoundary( osg::Node* node, bool geocentric )
-{
- // the normal defines the XY plane in which to search for a boundary
- osg::Vec3d normal(0,0,1);
- osg::Vec3d center;
-
- if ( geocentric )
- {
- // define the XY plane based on the normal to the center of the dataset:
- osg::BoundingSphere bs = node->getBound();
- center = bs.center();
- normal = center;
- normal.normalize();
- OE_DEBUG << "Normal = " << normal.x() << ", " << normal.y() << ", " << normal.z() << std::endl;
- }
-
- osg::ref_ptr<osg::Vec3dArray> _result = new osg::Vec3dArray();
-
- // first build a topology graph from the node.
- TopologyGraph topology;
-
- // set up a transform that will localize geometry into an XY plane
- if ( geocentric )
- {
- topology._world2plane.makeRotate(normal, osg::Vec3d(0,0,1));
- topology._world2plane.preMultTranslate(-center);
-
- // if this is set, use mercator projection instead of a simple geolocation
- //topology._srs = osgEarth::SpatialReference::get("spherical-mercator");
- }
-
- // build the topology
- BuildTopologyVisitor buildTopoVisitor(topology);
- node->accept( buildTopoVisitor );
-
- OE_DEBUG << "Found " << topology._verts.size() << " unique verts" << std::endl;
- //dumpPointCloud(topology);
-
- // starting with the minimum-Y vertex (which is guaranteed to be in the boundary)
- // traverse the outside of the point set. Do this by sorting all the edges by
- // their angle relative to the vector to the previous point. The vector with the
- // smallest angle represents the edge connecting the current point to the next
- // boundary point. Walk the edge until we return to the beginning.
-
- Index vptr = topology._minY;
- Index vptr_prev = topology._verts.end();
-
- IndexSet visited;
-
- while( true )
- {
- // store this vertex in the result set:
- _result->push_back( *vptr );
-
- if ( _result->size() == 56 )
- {
- int asd=0;
- }
-
- // pull up the next 2D vertex (XY plane):
- osg::Vec2d vert ( vptr->x(), vptr->y() );
-
- // construct the "base" vector that points from the previous
- // point to the current point; or to -X in the initial case
- osg::Vec2d base;
- if ( vptr_prev == topology._verts.end() )
- base.set( -1, 0 );
- else
- base = vert - osg::Vec2d( vptr_prev->x(), vptr_prev->y() );
-
- // pull up the edge set for this vertex:
- IndexSet& edges = topology._edgeMap[vptr];
-
- // find the edge with the minimun delta angle to the base vector
- double bestScore = DBL_MAX;
- Index bestEdge = topology._verts.end();
-
- OE_DEBUG << "VERTEX (" <<
- vptr->x() << ", " << vptr->y() << ", " << vptr->z()
- << ") has " << edges.size() << " edges..."
- << std::endl;
-
- for( IndexSet::iterator e = edges.begin(); e != edges.end(); ++e )
- {
- // don't go back from whence we just came
- if ( *e == vptr_prev )
- continue;
-
- // never return to a vert we've already visited
- if ( visited.find(*e) != visited.end() )
- continue;
-
- // calculate the angle between the base vector and the current edge:
- osg::Vec2d edgeVert( (*e)->x(), (*e)->y() );
- osg::Vec2d edge = edgeVert - vert;
-
- base.normalize();
- edge.normalize();
- double cross = base.x()*edge.y() - base.y()*edge.x();
- double dot = base * edge;
- double score = dot;
-
- if ( cross < 0.0 )
- {
- double diff = 2.0-(score+1.0);
- score = 1.0 + diff;
- }
-
- OE_DEBUG << " check: " << (*e)->x() << ", " << (*e)->y() << ", " << (*e)->z() << std::endl;
- OE_DEBUG << " base = " << base.x() << ", " << base.y() << std::endl;
- OE_DEBUG << " edge = " << edge.x() << ", " << edge.y() << std::endl;
- OE_DEBUG << " crs = " << cross << ", dot = " << dot << ", score = " << score << std::endl;
-
- if ( score < bestScore )
- {
- bestScore = score;
- bestEdge = *e;
- }
- }
-
- if ( bestEdge == topology._verts.end() )
- {
- // this will probably never happen
- osg::notify(osg::WARN) << "Illegal state - reached a dead end!" << std::endl;
- break;
- }
-
- // store the previous:
- vptr_prev = vptr;
-
- // follow the chosen edge around the outside of the geometry:
- OE_DEBUG << " BEST SCORE = " << bestScore << std::endl;
- vptr = bestEdge;
-
- // record this vert so we don't visit it again.
- visited.insert( vptr );
-
- // once we make it all the way around, we're done:
- if ( vptr == topology._minY )
- break;
- }
-
- // un-rotate the results from the XY plane back to their original frame:
- if ( topology._srs )
- {
- const osgEarth::SpatialReference* ecef = topology._srs->getECEF();
- topology._srs->transform(_result->asVector(), ecef);
- }
- else
- {
- osg::Matrix plane2world;
- plane2world.invert( topology._world2plane );
- for( osg::Vec3dArray::iterator i = _result->begin(); i != _result->end(); ++i )
- (*i) = (*i) * plane2world;
- }
-
- return _result.release();
-}
-
-//------------------------------------------------------------------------
-
-bool
-BoundaryUtil::simpleBoundaryTest(const osg::Vec3dArray& boundary)
-{
- osg::ref_ptr<osgEarth::Symbology::Polygon> boundsPoly = new osgEarth::Symbology::Polygon();
- for (int i=0; i < (int)boundary.size(); i++)
- boundsPoly->push_back(boundary[i]);
-
- osgEarth::Bounds boundsBounds = boundsPoly->getBounds();
-
- osg::ref_ptr<osgEarth::Symbology::Polygon> outterPoly = new osgEarth::Symbology::Polygon();
- outterPoly->push_back(osg::Vec3d(boundsBounds.xMin() - 10.0, boundsBounds.yMin() - 10.0, boundsBounds.zMin()));
- outterPoly->push_back(osg::Vec3d(boundsBounds.xMax() + 10.0, boundsBounds.yMin() - 10.0, boundsBounds.zMin()));
- outterPoly->push_back(osg::Vec3d(boundsBounds.xMax() + 10.0, boundsBounds.yMax() + 10.0, boundsBounds.zMin()));
- outterPoly->push_back(osg::Vec3d(boundsBounds.xMin() - 10.0, boundsBounds.yMax() + 10.0, boundsBounds.zMin()));
-
- osg::ref_ptr<osgEarth::Symbology::Geometry> outPoly;
- return outterPoly->difference(boundsPoly, outPoly);
-}
-#endif
View it on GitLab: https://salsa.debian.org/debian-gis-team/osgearth/compare/e0c952b2697f03805c19a784386a3613383a3aba...6b46af98a1ad0c9c1f3ad4d5559a0faade53cf1b
---
View it on GitLab: https://salsa.debian.org/debian-gis-team/osgearth/compare/e0c952b2697f03805c19a784386a3613383a3aba...6b46af98a1ad0c9c1f3ad4d5559a0faade53cf1b
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.alioth.debian.org/pipermail/pkg-grass-devel/attachments/20180202/abdcea9b/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list