diff --git a/src/applications/osgearth_atlas/osgearth_atlas.cpp b/src/applications/osgearth_atlas/osgearth_atlas.cpp
index 47d1d312a8..5d688f07f8 100644
--- a/src/applications/osgearth_atlas/osgearth_atlas.cpp
+++ b/src/applications/osgearth_atlas/osgearth_atlas.cpp
@@ -263,7 +263,7 @@ show(osg::ArgumentParser& arguments)
if (drawLabels)
{
osgText::Text* label = new osgText::Text();
- label->setText(skins[k]->name());
+ label->setText(skins[k]->name().value());
label->setPosition(osg::Vec3(x+0.5*s, -0.005f, y+0.5*t));
label->setAlignment(label->CENTER_CENTER);
label->setAutoRotateToScreen(true);
diff --git a/src/osgEarth/BuildGeometryFilter b/src/osgEarth/BuildGeometryFilter
index bfe708e1c6..a3ea54fdeb 100644
--- a/src/osgEarth/BuildGeometryFilter
+++ b/src/osgEarth/BuildGeometryFilter
@@ -16,9 +16,7 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*/
-
-#ifndef OSGEARTHFEATURES_BUILD_GEOMETRY_FILTER_H
-#define OSGEARTHFEATURES_BUILD_GEOMETRY_FILTER_H 1
+#pragma once
#include
#include
@@ -119,14 +117,7 @@ namespace osgEarth { namespace Util
bool makeECEF,
bool tessellate,
osg::Geometry* osgGeom,
- const osg::Matrixd &world2local);
-
- void buildPolygon(
- Geometry* input,
- const SpatialReference* featureSRS,
- const SpatialReference* mapSRS,
- bool makeECEF,
- osg::Geometry* osgGeom,
+ const SkinResource* skinResource,
const osg::Matrixd &world2local);
osg::Geode* processPolygons (FeatureList& input, FilterContext& cx);
@@ -136,5 +127,3 @@ namespace osgEarth { namespace Util
osg::Geode* processMeshes(FeatureList& input, FilterContext& cx);
};
} }
-
-#endif // OSGEARTHFEATURES_BUILD_GEOMETRY_FILTER_H
diff --git a/src/osgEarth/BuildGeometryFilter.cpp b/src/osgEarth/BuildGeometryFilter.cpp
index 89a0be4b95..03e899ab16 100644
--- a/src/osgEarth/BuildGeometryFilter.cpp
+++ b/src/osgEarth/BuildGeometryFilter.cpp
@@ -34,6 +34,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -255,6 +256,43 @@ BuildGeometryFilter::processPolygons(FeatureList& features, FilterContext& conte
makeECEF = context.getOutputSRS()->isGeographic();
}
+ // if there's a skin, set that up.
+ // (TODO? only works for stylesheet-level symbols, not per-feature symbols)
+ auto* skin_symbol = _style.get();
+ osg::ref_ptr skin_res = nullptr;
+ osg::ref_ptr skin_stateset;
+
+ if (skin_symbol && skin_symbol->library().isSet())
+ {
+ auto sheet = context.getSession() ? context.getSession()->styles() : nullptr;
+ if (sheet)
+ {
+ osg::ref_ptr res_lib = sheet->getResourceLibrary(skin_symbol->library().value());
+ if (res_lib.valid())
+ {
+ // TODO: move this into the feature loop to support per-feature styling...
+ skin_res = res_lib->getSkin(skin_symbol->name()->eval(), context.getDBOptions());
+ if (skin_res)
+ {
+ context.resourceCache()->getOrCreateStateSet(skin_res, skin_stateset, context.getDBOptions());
+ }
+ else
+ {
+ OE_WARN << LC << "Unable to find skin '" << skin_symbol->name()->eval() << "'"
+ << "in library; geometry will have no textures." << std::endl;
+ skin_symbol = nullptr;
+ }
+ }
+ else
+ {
+ OE_WARN << LC << "Unable to load resource library '" << skin_symbol->library().value() << "'"
+ << "; geometry will have no textures." << std::endl;
+ skin_symbol = nullptr;
+ }
+ }
+ }
+
+ // ready, time to iterate the features.
for( FeatureList::iterator f = features.begin(); f != features.end(); ++f )
{
Feature* input = f->get();
@@ -306,6 +344,11 @@ BuildGeometryFilter::processPolygons(FeatureList& features, FilterContext& conte
osgGeom->setName( name );
}
+ // apply the skin if there is one.
+ if (skin_stateset.valid())
+ {
+ osgGeom->setStateSet(skin_stateset);
+ }
// compute localizing matrices or use globals
osg::Matrixd w2l, l2w;
@@ -327,7 +370,7 @@ BuildGeometryFilter::processPolygons(FeatureList& features, FilterContext& conte
hats->push_back( i->z() );
// build the geometry:
- tileAndBuildPolygon(part, featureSRS, outputSRS, makeECEF, true, osgGeom.get(), w2l);
+ tileAndBuildPolygon(part, featureSRS, outputSRS, makeECEF, true, osgGeom.get(), skin_res, w2l);
osg::Vec3Array* allPoints = static_cast(osgGeom->getVertexArray());
if (allPoints && allPoints->size() > 0)
@@ -378,7 +421,7 @@ BuildGeometryFilter::processPolygons(FeatureList& features, FilterContext& conte
}
else
{
- OE_TEST << LC << "Oh no. buildAndTilePolygon returned nothing.\n";
+ OE_TEST << LC << "Oh no. tileAndBuildPolygon returned nothing.\n";
}
}
}
@@ -1135,14 +1178,26 @@ BuildGeometryFilter::tileAndBuildPolygon(
bool makeECEF,
bool tessellate,
osg::Geometry* osgGeom,
+ const SkinResource* skin_res,
const osg::Matrixd& world2local)
{
OE_SOFT_ASSERT_AND_RETURN(input != nullptr, void());
OE_SOFT_ASSERT_AND_RETURN(input->getType() != Geometry::TYPE_MULTI, void());
+ const double circum = 40041000.0; //m
+ double ref_x = 0.0, ref_y = 0.0;
+
osg::ref_ptr verts = new osg::Vec3Array();
verts->reserve(input->getTotalPointCount());
+ osg::ref_ptr uvs;
+ if (skin_res)
+ {
+ uvs = new osg::Vec2Array();
+ uvs->reserve(verts->size());
+ osgGeom->setTexCoordArray(0, uvs.get());
+ }
+
// hard copy so we can project the values
osg::ref_ptr proj = input->clone();
@@ -1174,6 +1229,7 @@ BuildGeometryFilter::tileAndBuildPolygon(
// of the centroid) and that is way too slow.
auto local_geom = proj; // working copy
Bounds local_ex;
+ Bounds geo_ex;
double z = -DBL_MAX;
GeometryIterator iter(local_geom.get());
while (iter.hasMore())
@@ -1182,12 +1238,23 @@ BuildGeometryFilter::tileAndBuildPolygon(
inputSRS->transform(part->asVector(), outputSRS); // to geographic
for (auto& p : *part)
{
+ geo_ex.expandBy(p);
geo_to_gnomonic(p, centroid, gnomonic_scale);
local_ex.expandBy(p);
z = std::max(z, p.z());
}
}
+ // calculate a UV reference point close to the centroid of the geometry.
+ if (skin_res)
+ {
+ auto geo_ex_anchor = geo_ex.center();
+ auto x = circum * (geo_ex_anchor.x() + 180.0) / 360.0;
+ auto y = (0.5 * circum) * (geo_ex_anchor.y() + 90.0) / 180.0;
+ ref_x = x - fmod(x, skin_res->imageWidth().value());
+ ref_y = y - fmod(y, skin_res->imageHeight().value());
+ }
+
// start with a weemesh covering the feature extent.
weemesh::mesh_t m;
const int marker = 0;
@@ -1264,12 +1331,21 @@ BuildGeometryFilter::tileAndBuildPolygon(
// Finally we convert from gnomonic back to localized tile coordinates.
osg::ref_ptr new_verts = new osg::Vec3Array();
new_verts->reserve(m.verts.size());
- osg::Vec3d temp;
- for (auto& v : m.verts)
+ osg::Vec3d ecef;
+ for (auto& vert : m.verts)
{
- gnomonic_to_geo(v, centroid, gnomonic_scale); // to geographic
- outputSRS->transformToWorld(osg::Vec3d(v.x, v.y, v.z), temp); // to ECEF
- new_verts->push_back(temp * world2local); // localized to tile
+ gnomonic_to_geo(vert, centroid, gnomonic_scale); // to geographic
+ outputSRS->transformToWorld(osg::Vec3d(vert.x, vert.y, vert.z), ecef); // to ECEF
+ new_verts->push_back(ecef* world2local); // localized to tile
+
+ if (uvs)
+ {
+ auto x = circum * (vert.x + 180.0) / 360.0;
+ auto y = (0.5 * circum) * (vert.y + 90.0) / 180.0;
+ auto u = (x - ref_x) / skin_res->imageWidth().value();
+ auto v = (y - ref_y) / (skin_res->imageHeight().value());
+ uvs->push_back(osg::Vec2f(u, v));
+ }
}
// Assemble the final geometry.
@@ -1291,13 +1367,14 @@ BuildGeometryFilter::tileAndBuildPolygon(
{
// original tesselation approach
Tessellator::Plane plane = Tessellator::PLANE_XY;
+ Bounds geo_ex;
if (outputSRS)
{
// for geographic data we need to project into 2D before tessellating:
if (outputSRS->isGeographic())
{
- osg::Vec3d temp;
+ osg::Vec3d geo;
osg::BoundingBoxd ecef_bb;
bool allOnEquator = true;
@@ -1308,16 +1385,27 @@ BuildGeometryFilter::tileAndBuildPolygon(
part->open();
for (osg::Vec3d& p : *part)
{
- inputSRS->transform(p, outputSRS, temp);
- if (temp.y() != 0.0)
+ inputSRS->transform(p, outputSRS, geo);
+ if (geo.y() != 0.0)
{
allOnEquator = false;
}
- outputSRS->transformToWorld(temp, p);
+ geo_ex.expandBy(geo);
+ outputSRS->transformToWorld(geo, p);
ecef_bb.expandBy(p);
}
}
+ // calculate a UV reference point close to the centroid of the geometry.
+ if (skin_res)
+ {
+ auto geo_ex_anchor = geo_ex.center();
+ auto x = circum * (geo_ex_anchor.x() + 180.0) / 360.0;
+ auto y = (0.5*circum) * (geo_ex_anchor.y() + 90.0) / 180.0;
+ ref_x = x - fmod(x, skin_res->imageWidth().value());
+ ref_y = y - fmod(y, skin_res->imageHeight().value());
+ }
+
const osg::Vec3d& center = ecef_bb.center();
GeometryIterator proj_iter(proj.get(), true);
@@ -1371,16 +1459,26 @@ BuildGeometryFilter::tileAndBuildPolygon(
if (outputSRS && outputSRS->isGeographic())
{
+ osg::Vec3d geo;
ConstGeometryIterator verts_iter(input, true);
while (verts_iter.hasMore())
{
const Geometry* part = verts_iter.next();
for (const auto& p : *part)
{
- inputSRS->transform(p, outputSRS, temp);
- outputSRS->transformToWorld(temp, vert);
+ inputSRS->transform(p, outputSRS, geo);
+ outputSRS->transformToWorld(geo, vert);
vert = vert * world2local;
verts->push_back(vert);
+
+ if (uvs)
+ {
+ auto x = circum * (geo.x() + 180.0) / 360.0;
+ auto y = (0.5*circum) * (geo.y() + 90.0) / 180.0;
+ auto u = (x - ref_x) / skin_res->imageWidth().value();
+ auto v = (y - ref_y) / (skin_res->imageHeight().value());
+ uvs->push_back(osg::Vec2f(u, v));
+ }
}
}
}
@@ -1393,6 +1491,9 @@ BuildGeometryFilter::tileAndBuildPolygon(
for (const auto& p : *part)
{
verts->push_back(p * world2local);
+
+ //TODO
+ uvs->push_back(osg::Vec2f(0, 0));
}
}
}
@@ -1531,175 +1632,6 @@ BuildGeometryFilter::tileAndBuildPolygon(Geometry* ring,
}
#endif
-// builds and tessellates a polygon (with or without holes)
-void
-BuildGeometryFilter::buildPolygon(Geometry* ring,
- const SpatialReference* featureSRS,
- const SpatialReference* outputSRS,
- bool makeECEF,
- osg::Geometry* osgGeom,
- const osg::Matrixd &world2local)
-{
- if ( !ring->isValid() )
- return;
-
- ring->rewind(osgEarth::Geometry::ORIENTATION_CCW);
-
- osg::ref_ptr allPoints = new osg::Vec3Array();
- transformAndLocalize( ring->asVector(), featureSRS, allPoints.get(), outputSRS, world2local, makeECEF );
-
- Polygon* poly = dynamic_cast(ring);
- if ( poly )
- {
- RingCollection ordered(poly->getHoles().begin(), poly->getHoles().end());
- std::sort(ordered.begin(), ordered.end(), holeCompare);
-
- for( RingCollection::const_iterator h = ordered.begin(); h != ordered.end(); ++h )
- {
- Geometry* hole = h->get();
- if ( hole->isValid() )
- {
- hole->rewind(osgEarth::Geometry::ORIENTATION_CW);
-
- osg::ref_ptr holePoints = new osg::Vec3Array();
- transformAndLocalize( hole->asVector(), featureSRS, holePoints.get(), outputSRS, world2local, makeECEF );
-
- // find the point with the highest x value
- unsigned int hCursor = 0;
- for (unsigned int i=1; i < holePoints->size(); i++)
- {
- if ((*holePoints)[i].x() > (*holePoints)[hCursor].x())
- hCursor = i;
- }
-
- double x1 = (*holePoints)[hCursor].x();
- double y1 = (*holePoints)[hCursor].y();
- double y2 = (*holePoints)[hCursor].y();
-
- unsigned int edgeCursor = UINT_MAX;
- double edgeDistance = DBL_MAX;
- unsigned int foundPointCursor = UINT_MAX;
- for (unsigned int i=0; i < allPoints->size(); i++)
- {
- unsigned int next = i == allPoints->size() - 1 ? 0 : i + 1;
- double xMax = osg::maximum((*allPoints)[i].x(), (*allPoints)[next].x());
-
- if (xMax > (*holePoints)[hCursor].x())
- {
- double x2 = xMax + 1.0;
- double x3 = (*allPoints)[i].x();
- double y3 = (*allPoints)[i].y();
- double x4 = (*allPoints)[next].x();
- double y4 = (*allPoints)[next].y();
-
- double xi=0.0, yi=0.0;
- bool intersects = false;
- unsigned int hitPointCursor = UINT_MAX;
- if (y1 == y3 && x3 > x1)
- {
- xi = x3;
- hitPointCursor = i;
- intersects = true;
- }
- else if (y1 == y4 && x4 > x1)
- {
- xi = x4;
- hitPointCursor = next;
- intersects = true;
- }
- else if (segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4, xi, yi))
- {
- intersects = true;
- }
-
- double dist = (osg::Vec2d(xi, yi) - osg::Vec2d(x1, y1)).length();
- if (intersects && dist < edgeDistance)
- {
- foundPointCursor = hitPointCursor;
- edgeCursor = hitPointCursor != UINT_MAX ? hitPointCursor : (x3 >= x4 ? i : next);
- edgeDistance = dist;
- }
- }
- }
-
- if (foundPointCursor == UINT_MAX && edgeCursor != UINT_MAX)
- {
- // test for intersecting edges between x1 and x2
- // (skipping the two segments for which edgeCursor is a vert)
-
- double x2 = (*allPoints)[edgeCursor].x();
- y2 = (*allPoints)[edgeCursor].y();
-
- bool foundIntersection = false;
- for (unsigned int i=0; i < allPoints->size(); i++)
- {
- unsigned int next = i == allPoints->size() - 1 ? 0 : i + 1;
-
- if (i == edgeCursor || next == edgeCursor)
- continue;
-
- double x3 = (*allPoints)[i].x();
- double y3 = (*allPoints)[i].y();
- double x4 = (*allPoints)[next].x();
- double y4 = (*allPoints)[next].y();
-
- foundIntersection = foundIntersection || segmentsIntersect(x1, y1, x2, y2, x3, y3, x4, y4);
-
- if (foundIntersection)
- {
- unsigned int prev = i == 0 ? allPoints->size() - 1 : i - 1;
-
- if (!isCCW((*allPoints)[prev].x(), (*allPoints)[prev].y(), x3, y3, x4, y4))
- {
- edgeCursor = i;
- x2 = (*allPoints)[edgeCursor].x();
- y2 = (*allPoints)[edgeCursor].y();
- foundIntersection = false;
- }
- }
-
- }
- }
-
- if (edgeCursor != UINT_MAX)
- {
- // build array of correctly ordered new points to add to the outer loop
- osg::ref_ptr insertPoints = new osg::Vec3Array();
- insertPoints->reserve(holePoints->size() + 2);
-
- unsigned int p = hCursor;
- do
- {
- insertPoints->push_back((*holePoints)[p]);
- p = p == holePoints->size() - 1 ? 0 : p + 1;
- } while(p != hCursor);
-
- insertPoints->push_back((*holePoints)[hCursor]);
- insertPoints->push_back((*allPoints)[edgeCursor]);
-
- // insert new points into outer loop
- osg::Vec3Array::iterator it = edgeCursor == allPoints->size() - 1 ? allPoints->end() : allPoints->begin() + (edgeCursor + 1);
- allPoints->insert(it, insertPoints->begin(), insertPoints->end());
- }
- }
- }
- }
-
- GLenum mode = GL_LINE_LOOP;
- if ( osgGeom->getVertexArray() == 0L )
- {
- osgGeom->addPrimitiveSet( new osg::DrawArrays( mode, 0, allPoints->size() ) );
- osgGeom->setVertexArray( allPoints.get() );
- }
- else
- {
- osg::Vec3Array* v = static_cast(osgGeom->getVertexArray());
- osgGeom->addPrimitiveSet( new osg::DrawArrays( mode, v->size(), allPoints->size() ) );
- //v->reserve(v->size() + allPoints->size());
- std::copy(allPoints->begin(), allPoints->end(), std::back_inserter(*v));
- }
-}
-
namespace
{
diff --git a/src/osgEarth/FeatureRasterizer.cpp b/src/osgEarth/FeatureRasterizer.cpp
index c754467cea..cdb018e530 100644
--- a/src/osgEarth/FeatureRasterizer.cpp
+++ b/src/osgEarth/FeatureRasterizer.cpp
@@ -556,7 +556,7 @@ namespace osgEarth {
osg::ref_ptr< osg::Image > image = skin->image().get();
if (!image.valid())
{
- image = skin->createImage(nullptr);
+ image = skin->createColorImage(nullptr);
}
if (image.valid())
{
diff --git a/src/osgEarth/PBRMaterial b/src/osgEarth/PBRMaterial
index 0dbeae72c3..a55f39ed73 100644
--- a/src/osgEarth/PBRMaterial
+++ b/src/osgEarth/PBRMaterial
@@ -77,6 +77,18 @@ namespace osgEarth
conf.set("opacity", opacity());
return conf;
}
+
+ //! Returns true if the material is "simple" (i.e., has only a color map)
+ bool isSimple() const
+ {
+ return color().isSet() &&
+ !normal().isSet() &&
+ !roughness().isSet() &&
+ !ao().isSet() &&
+ !metal().isSet() &&
+ !displacement().isSet() &&
+ !opacity().isSet();
+ }
};
/**
diff --git a/src/osgEarth/PolygonSymbol b/src/osgEarth/PolygonSymbol
index 65591fa875..96bf9448d6 100644
--- a/src/osgEarth/PolygonSymbol
+++ b/src/osgEarth/PolygonSymbol
@@ -16,12 +16,11 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*/
-
-#ifndef OSGEARTHSYMBOLOGY_GEOMETRY_SYMBOL_H
-#define OSGEARTHSYMBOLOGY_GEOMETRY_SYMBOL_H 1
+#pragma once
#include
#include
+#include
namespace osgEarth
{
@@ -33,31 +32,26 @@ namespace osgEarth
public:
META_Object(osgEarth, PolygonSymbol);
- PolygonSymbol(const PolygonSymbol& rhs,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
- PolygonSymbol( const Config& conf =Config() );
+ PolygonSymbol(const PolygonSymbol& rhs, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
+ PolygonSymbol(const Config& conf = Config());
/** dtor */
virtual ~PolygonSymbol() { }
- /** Polygon fill properties. */
- optional& fill() { return _fill; }
- const optional& fill() const { return _fill; }
+ //! Polygon fill properties.
+ OE_OPTION(Fill, fill);
+
+ //! Whether to use a LineStyle (if one exists in the Style) as an outline.
+ //! This defaults to true, but you set this to false to suppress outlining
+ //! even when a LineStyle exists.
+ OE_OPTION(bool, outline, true);
- /** Whether to use a LineStyle (if one exists in the Style) as an outline.
- * This defaults to true, but you set this to false to suppress outlining
- * even when a LineStyle exists. */
- optional& outline() { return _outline; }
- const optional& outline() const { return _outline; }
+ //! URI of material to use to texture polygon geometries
+ OE_OPTION(URI, material);
public:
virtual Config getConfig() const;
virtual void mergeConfig(const Config& conf);
static void parseSLD(const Config& c, class Style& style);
-
- protected:
- optional _fill;
- optional _outline;
};
} // namespace osgEarth
-
-#endif // OSGEARTH_SYMBOLOGY_SYMBOL_H
diff --git a/src/osgEarth/PolygonSymbol.cpp b/src/osgEarth/PolygonSymbol.cpp
index fcba0eb792..9e9440fa43 100644
--- a/src/osgEarth/PolygonSymbol.cpp
+++ b/src/osgEarth/PolygonSymbol.cpp
@@ -23,49 +23,55 @@ using namespace osgEarth;
OSGEARTH_REGISTER_SIMPLE_SYMBOL(polygon, PolygonSymbol);
-PolygonSymbol::PolygonSymbol(const PolygonSymbol& rhs,const osg::CopyOp& copyop):
-Symbol(rhs, copyop),
-_fill(rhs._fill),
-_outline(rhs._outline)
+PolygonSymbol::PolygonSymbol(const PolygonSymbol& rhs, const osg::CopyOp& copyop) :
+ Symbol(rhs, copyop),
+ _fill(rhs._fill),
+ _outline(rhs._outline)
{
//nop
}
-PolygonSymbol::PolygonSymbol( const Config& conf ) :
-Symbol( conf ),
-_fill ( Fill() ),
-_outline( true )
+PolygonSymbol::PolygonSymbol(const Config& conf) :
+ Symbol(conf)
{
mergeConfig(conf);
}
-Config
+Config
PolygonSymbol::getConfig() const
{
Config conf = Symbol::getConfig();
conf.key() = "polygon";
- conf.set( "fill", _fill );
- conf.set("outline", _outline);
+ conf.set("fill", fill());
+ conf.set("outline", outline());
+ conf.set("material", material());
return conf;
}
-void
-PolygonSymbol::mergeConfig(const Config& conf )
+void
+PolygonSymbol::mergeConfig(const Config& conf)
{
- conf.get( "fill", _fill );
- conf.get("outline", _outline);
+ conf.get("fill", fill());
+ conf.get("outline", outline());
+ conf.get("material", material());
}
void
PolygonSymbol::parseSLD(const Config& c, Style& style)
{
- if ( match(c.key(), "fill") ) {
+ if (match(c.key(), "fill")) {
style.getOrCreate()->fill().mutable_value().color() = Color(c.value());
}
- else if ( match(c.key(), "fill-opacity") ) {
- style.getOrCreate()->fill().mutable_value().color().a() = as( c.value(), 1.0f );
+ else if (match(c.key(), "fill-opacity")) {
+ style.getOrCreate()->fill().mutable_value().color().a() = as(c.value(), 1.0f);
}
- else if ( match(c.key(), "fill-script") ) {
+ else if (match(c.key(), "fill-script")) {
style.getOrCreate()->script() = StringExpression(c.value());
}
+ else if (match(c.key(), "fill-outline")) {
+ style.getOrCreate()->outline() = as(c.value(), true);
+ }
+ else if (match(c.key(), "fill-material")) {
+ style.getOrCreate()->material() = URI(c.value(), c.referrer());
+ }
}
diff --git a/src/osgEarth/ResourceLibrary.cpp b/src/osgEarth/ResourceLibrary.cpp
index d58aab5187..d301c9b86c 100644
--- a/src/osgEarth/ResourceLibrary.cpp
+++ b/src/osgEarth/ResourceLibrary.cpp
@@ -259,7 +259,7 @@ ResourceLibrary::matches( const SkinSymbol* q, SkinResource* s ) const
{
if ( q->name().isSet() )
{
- return osgEarth::ciEquals(q->name()->eval(), s->name());
+ return osgEarth::ciEquals(q->name()->eval(), s->name().value());
}
if (q->objectHeight().isSet())
diff --git a/src/osgEarth/Skins b/src/osgEarth/Skins
index 75e4d86f8e..2cedd8e94d 100644
--- a/src/osgEarth/Skins
+++ b/src/osgEarth/Skins
@@ -16,151 +16,125 @@
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*/
-
-#ifndef OSGEARTHSYMBOLOGY_SKIN_RESOURCE_H
-#define OSGEARTHSYMBOLOGY_SKIN_RESOURCE_H 1
+#pragma once
#include
#include
#include
#include
+#include
#include
#include
-namespace osgEarth { namespace Util
+namespace osgEarth
{
- /**
- * A resource that points to a "skin", which is a Texture image paired with
- * a collection of metadata that describes its suitability for use in a scene.
- */
- class OSGEARTH_EXPORT SkinResource : public Resource
+ namespace Util
{
- public:
- /** Constructs a new skin resource. */
- SkinResource( const Config& conf =Config() );
-
- /** dtor */
- virtual ~SkinResource() { }
-
/**
- * Creates a new StateSet containing a Texture based on this Skin.
+ * A resource that points to a "skin", which is a Texture image paired with
+ * a collection of metadata that describes its suitability for use in a scene.
*/
- osg::StateSet* createStateSet( const osgDB::Options* dbOptions ) const;
+ class OSGEARTH_EXPORT SkinResource : public Resource
+ {
+ public:
+ /** Constructs a new skin resource. */
+ SkinResource(const Config& conf = Config());
- /**
- * Creates an image for this SkinResource.
- */
- osg::ref_ptr createImage( const osgDB::Options* options ) const;
-
- osg::Texture* createTexture(const osgDB::Options* readOptions) const;
+ /** dtor */
+ virtual ~SkinResource() { }
- osg::Texture* createTexture(osg::Image* image) const;
+ //! Creates a new StateSet containing a Texture based on this Skin.
+ osg::StateSet* createStateSet(const osgDB::Options* dbOptions) const;
- /**
- * A key string that can uniquely identify this object for the purposes
- * of creating its state set (e.g., for a cache)
- */
- std::string getUniqueID() const;
+ //! Creates a state attribute for this resource. This will either be
+ //! an osg::Texture (for a simple image), a PBRTexture (for a PBR material),
+ //! or nullptr.
+ osg::StateAttribute* createStateAttribute(const osgDB::Options* readOptions) const;
- public:
- /** inline image (not serializable) */
- osg::ref_ptr& image() { return _image; }
- const osg::ref_ptr& image() const { return _image; }
+ //! Creates a texture for the input image. Supports multilayer array images.
+ osg::Texture* createTexture(osg::Image* image) const;
- /** Source location of the actual texture image. */
- optional& imageURI() { return _imageURI; }
- const optional& imageURI() const { return _imageURI; }
+ //! Create an image from the color channel of the configured material.
+ //! TODO: expand to support other material channels.
+ osg::ref_ptr createColorImage(const osgDB::Options* options) const;
- /** Real-world width of the image, in meters */
- optional& imageWidth() { return _imageWidth; }
- const optional& imageWidth() const { return _imageWidth; }
+ //! A key string that can uniquely identify this object for the purposes
+ //! of creating its state set (e.g., for a cache)
+ std::string getUniqueID() const;
- /** Real-world height of the image, in meters */
- optional& imageHeight() { return _imageHeight; }
- const optional& imageHeight() const { return _imageHeight; }
+ public:
+ /** inline image (not serializable) */
+ OE_OPTION_REFPTR(osg::Image, image);
- /** Minimum acceptable real-world object height (meters) for which this image would make an appropriate texture */
- optional& minObjectHeight() { return _minObjHeight; }
- const optional& minObjectHeight() const { return _minObjHeight; }
+ OE_OPTION(std::string, name);
- /** Maximum acceptable real-world object height (meters) for which this image would make an appropriate texture */
- optional& maxObjectHeight() { return _maxObjHeight; }
- const optional& maxObjectHeight() const { return _maxObjHeight; }
+ //! Source data for the textures.
+ //! Use this OR imageURI.
+ OE_OPTION(PBRMaterial, material);
- /** Whether this image is suitable for use as a vertically repeating texture */
- optional& isTiled() { return _isTiled; }
- const optional& isTiled() const { return _isTiled; }
+ //! Source location of a texture image.
+ //! This is an alternative to using material() that will load just a color image.
+ //! This supports texture arrays and atlases, whereas material does not (yet).
+ OE_OPTION(URI, imageURI);
- /** Image offset within a source atlas (S dimension [0..1]) */
- optional& imageBiasS() { return _imageBiasS; }
- const optional& imageBiasS() const { return _imageBiasS; }
+ /** Real-world width of the image, in meters */
+ OE_OPTION(float, imageWidth, 10.0f);
- /** Image offset (pixels) within a source atlas (T dimension [0..1]) */
- optional& imageBiasT() { return _imageBiasT; }
- const optional& imageBiasT() const { return _imageBiasT; }
+ /** Real-world height of the image, in meters */
+ OE_OPTION(float, imageHeight, 3.0f);
- /** Image layer index within a source atlas (R dimension) */
- optional& imageLayer() { return _imageLayer; }
- const optional& imageLayer() const { return _imageLayer; }
+ /** Minimum acceptable real-world object height (meters) for which this image would make an appropriate texture */
+ OE_OPTION(float, minObjectHeight, 0.0f);
- /** Image scalke factor within a source atlas (S dimension) */
- optional& imageScaleS() { return _imageScaleS; }
- const optional& imageScaleS() const { return _imageScaleS; }
+ /** Maximum acceptable real-world object height (meters) for which this image would make an appropriate texture */
+ OE_OPTION(float, maxObjectHeight, FLT_MAX);
- /** Image scalke factor within a source atlas (T dimension) */
- optional& imageScaleT() { return _imageScaleT; }
- const optional& imageScaleT() const { return _imageScaleT; }
+ /** Whether this image is suitable for use as a vertically repeating texture */
+ OE_OPTION(bool, isTiled, false);
- /** GL texture application mode */
- optional& texEnvMode() { return _texEnvMode; }
- const optional& texEnvMode() const { return _texEnvMode; }
+ /** Image offset within a source atlas (S dimension [0..1]) */
+ OE_OPTION(float, imageBiasS, 0.0f);
- /** The maximum allowable size of a texture (in either dimension) that uses this image. */
- optional maxTextureSpan() { return _maxTexSpan; }
- const optional maxTextureSpan() const { return _maxTexSpan; }
+ /** Image offset (pixels) within a source atlas (T dimension [0..1]) */
+ OE_OPTION(float, imageBiasT, 0.0f);
- /** Whether this image is suitable for texture atlasing */
- optional& atlasHint() { return _atlasHint; }
- const optional& atlasHint() const { return _atlasHint; }
+ /** Image layer index within a source atlas (R dimension) */
+ OE_OPTION(unsigned, imageLayer, 0);
- /** Options string to pass in when reading the image */
- optional& readOptions() { return _readOptions; }
- const optional& readOptions() const { return _readOptions; }
+ /** Image scalke factor within a source atlas (S dimension) */
+ OE_OPTION(float, imageScaleS, 1.0f);
- public: // serialization methods
+ /** Image scalke factor within a source atlas (T dimension) */
+ OE_OPTION(float, imageScaleT, 1.0f);
- virtual Config getConfig() const;
- void mergeConfig( const Config& conf );
+ /** GL texture application mode */
+ OE_OPTION(osg::TexEnv::Mode, texEnvMode, osg::TexEnv::MODULATE);
- protected:
+ /** The maximum allowable size of a texture (in either dimension) that uses this image. */
+ OE_OPTION(unsigned, maxTexSpan, 1024);
- osg::StateSet* createStateSet( osg::Image* image ) const;
+ /** Whether this image is suitable for texture atlasing */
+ OE_OPTION(bool, atlasHint, true);
- protected:
+ /** Options string to pass in when reading the image */
+ OE_OPTION(std::string, readOptions);
- optional _name;
- optional _imageURI;
- optional _imageWidth;
- optional _imageHeight;
- optional _minObjHeight;
- optional _maxObjHeight;
- optional _isTiled;
- optional _texEnvMode;
- optional _maxTexSpan;
- optional _imageBiasS;
- optional _imageBiasT;
- optional _imageLayer;
- optional _imageScaleS;
- optional _imageScaleT;
- optional _atlasHint;
- optional _readOptions;
-
- osg::ref_ptr _image;
- };
+ public: // serialization methods
+
+ virtual Config getConfig() const;
+ void mergeConfig(const Config& conf);
+
+ protected:
+
+ osg::StateSet* createStateSet(osg::Image* image) const;
+ };
+
+ using SkinResourceVector = std::vector< osg::ref_ptr >;
+ }
/**
- * Query object that you can use to search for applicable Skin resources from the
+ * Query object that you can use to search for applicable Skin resources from the
* ResourceLibrary.
*/
class OSGEARTH_EXPORT SkinSymbol : public TaggableWithConfig
@@ -168,8 +142,8 @@ namespace osgEarth { namespace Util
public:
META_Object(osgEarth, SkinSymbol);
- SkinSymbol(const SkinSymbol& rhs,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY);
- SkinSymbol( const Config& conf =Config() );
+ SkinSymbol(const SkinSymbol& rhs, const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY);
+ SkinSymbol(const Config& conf = Config());
/** dtor */
virtual ~SkinSymbol() { }
@@ -218,8 +192,5 @@ namespace osgEarth { namespace Util
optional _randomSeed;
optional _name;
};
+}
- typedef std::vector< osg::ref_ptr > SkinResourceVector;
-} }
-
-#endif // OSGEARTHSYMBOLOGY_SKIN_RESOURCE_H
diff --git a/src/osgEarth/Skins.cpp b/src/osgEarth/Skins.cpp
index b880641820..66fa728a15 100644
--- a/src/osgEarth/Skins.cpp
+++ b/src/osgEarth/Skins.cpp
@@ -32,50 +32,38 @@ using namespace osgEarth;
//---------------------------------------------------------------------------
-SkinResource::SkinResource( const Config& conf ) :
-Resource ( conf ),
-_imageWidth ( 10.0f ),
-_imageHeight ( 3.0f ),
-_minObjHeight ( 0.0f ),
-_maxObjHeight ( FLT_MAX ),
-_isTiled ( false ),
-_texEnvMode ( osg::TexEnv::MODULATE ),
-_maxTexSpan ( 1024 ),
-_imageBiasS ( 0.0f ),
-_imageBiasT ( 0.0f ),
-_imageLayer ( 0 ),
-_imageScaleS ( 1.0f ),
-_imageScaleT ( 1.0f ),
-_atlasHint ( true )
+SkinResource::SkinResource(const Config& conf) :
+ Resource(conf)
{
- mergeConfig( conf );
+ mergeConfig(conf);
}
void
-SkinResource::mergeConfig( const Config& conf )
+SkinResource::mergeConfig(const Config& conf)
{
- conf.get( "url", _imageURI );
- conf.get( "image_width", _imageWidth );
- conf.get( "image_height", _imageHeight );
- conf.get( "min_object_height", _minObjHeight );
- conf.get( "max_object_height", _maxObjHeight );
- conf.get( "tiled", _isTiled );
- conf.get( "max_texture_span", _maxTexSpan );
-
- conf.get( "texture_mode", "decal", _texEnvMode, osg::TexEnv::DECAL );
- conf.get( "texture_mode", "modulate", _texEnvMode, osg::TexEnv::MODULATE );
- conf.get( "texture_mode", "replace", _texEnvMode, osg::TexEnv::REPLACE );
- conf.get( "texture_mode", "blend", _texEnvMode, osg::TexEnv::BLEND );
+ conf.get("material", material());
+ conf.get("url", imageURI());
+ conf.get("image_width", _imageWidth);
+ conf.get("image_height", _imageHeight);
+ conf.get("min_object_height", minObjectHeight());
+ conf.get("max_object_height", maxObjectHeight());
+ conf.get("tiled", _isTiled);
+ conf.get("max_texture_span", _maxTexSpan);
+
+ conf.get("texture_mode", "decal", _texEnvMode, osg::TexEnv::DECAL);
+ conf.get("texture_mode", "modulate", _texEnvMode, osg::TexEnv::MODULATE);
+ conf.get("texture_mode", "replace", _texEnvMode, osg::TexEnv::REPLACE);
+ conf.get("texture_mode", "blend", _texEnvMode, osg::TexEnv::BLEND);
// texture atlas support
- conf.get( "image_bias_s", _imageBiasS );
- conf.get( "image_bias_t", _imageBiasT );
- conf.get( "image_layer", _imageLayer );
- conf.get( "image_scale_s", _imageScaleS );
- conf.get( "image_scale_t", _imageScaleT );
-
- conf.get( "atlas", _atlasHint );
- conf.get( "read_options", _readOptions );
+ conf.get("image_bias_s", _imageBiasS);
+ conf.get("image_bias_t", _imageBiasT);
+ conf.get("image_layer", _imageLayer);
+ conf.get("image_scale_s", _imageScaleS);
+ conf.get("image_scale_t", _imageScaleT);
+
+ conf.get("atlas", _atlasHint);
+ conf.get("read_options", _readOptions);
}
Config
@@ -84,28 +72,29 @@ SkinResource::getConfig() const
Config conf = Resource::getConfig();
conf.key() = "skin";
- conf.set( "url", _imageURI );
- conf.set( "image_width", _imageWidth );
- conf.set( "image_height", _imageHeight );
- conf.set( "min_object_height", _minObjHeight );
- conf.set( "max_object_height", _maxObjHeight );
- conf.set( "tiled", _isTiled );
- conf.set( "max_texture_span", _maxTexSpan );
-
- conf.set( "texture_mode", "decal", _texEnvMode, osg::TexEnv::DECAL );
- conf.set( "texture_mode", "modulate", _texEnvMode, osg::TexEnv::MODULATE );
- conf.set( "texture_mode", "replace", _texEnvMode, osg::TexEnv::REPLACE );
- conf.set( "texture_mode", "blend", _texEnvMode, osg::TexEnv::BLEND );
+ conf.set("material", material());
+ conf.set("url", imageURI());
+ conf.set("image_width", _imageWidth);
+ conf.set("image_height", _imageHeight);
+ conf.set("min_object_height", minObjectHeight());
+ conf.set("max_object_height", maxObjectHeight());
+ conf.set("tiled", _isTiled);
+ conf.set("max_texture_span", _maxTexSpan);
+
+ conf.set("texture_mode", "decal", _texEnvMode, osg::TexEnv::DECAL);
+ conf.set("texture_mode", "modulate", _texEnvMode, osg::TexEnv::MODULATE);
+ conf.set("texture_mode", "replace", _texEnvMode, osg::TexEnv::REPLACE);
+ conf.set("texture_mode", "blend", _texEnvMode, osg::TexEnv::BLEND);
// texture atlas support
- conf.set( "image_bias_s", _imageBiasS );
- conf.set( "image_bias_t", _imageBiasT );
- conf.set( "image_layer", _imageLayer );
- conf.set( "image_scale_s", _imageScaleS );
- conf.set( "image_scale_t", _imageScaleT );
-
- conf.set( "atlas", _atlasHint );
- conf.set( "read_options", _readOptions );
+ conf.set("image_bias_s", _imageBiasS);
+ conf.set("image_bias_t", _imageBiasT);
+ conf.set("image_layer", _imageLayer);
+ conf.set("image_scale_s", _imageScaleS);
+ conf.set("image_scale_t", _imageScaleT);
+
+ conf.set("atlas", _atlasHint);
+ conf.set("read_options", _readOptions);
return conf;
}
@@ -113,15 +102,45 @@ SkinResource::getConfig() const
std::string
SkinResource::getUniqueID() const
{
- return imageURI()->full();
+ return
+ imageURI().isSet() ? imageURI()->full() :
+ material().isSet() ? material()->color()->full() :
+ std::string{};
}
-osg::Texture*
-SkinResource::createTexture(const osgDB::Options* readOptions) const
+osg::StateAttribute*
+SkinResource::createStateAttribute(const osgDB::Options* readOptions) const
{
- //OE_DEBUG << LC << "Creating skin texture for " << imageURI()->full() << std::endl;
- osg::ref_ptr image = createImage(readOptions);
- return createTexture(image.get());
+ if (material().isSet())
+ {
+ osg::ref_ptr pbr_texture = new PBRTexture();
+ auto status = pbr_texture->load(material().value(), readOptions);
+ if (!status.isOK())
+ {
+ OE_WARN << LC << "One or more errors loading material for skin " << name().value() << std::endl;
+ return nullptr;
+ }
+
+ if (isTiled() == true)
+ {
+ for (auto& tex : { pbr_texture->albedo, pbr_texture->normal, pbr_texture->pbr })
+ {
+ if (tex.valid())
+ {
+ tex->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
+ tex->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
+ }
+ }
+ }
+
+ return pbr_texture.release();
+ }
+
+ else
+ {
+ osg::ref_ptr image = createColorImage(readOptions);
+ return createTexture(image.get());
+ }
}
osg::Texture*
@@ -177,45 +196,48 @@ osg::StateSet*
SkinResource::createStateSet(const osgDB::Options* readOptions) const
{
OE_DEBUG << LC << "Creating skin state set for " << imageURI()->full() << std::endl;
- osg::ref_ptr image = createImage(readOptions);
- return createStateSet(image.get());
-}
-osg::StateSet*
-SkinResource::createStateSet( osg::Image* image ) const
-{
- osg::StateSet* stateSet = 0L;
- if ( image )
+ auto stateset = new osg::StateSet();
+ osg::Texture* albedo_texture = nullptr;
+
+ auto sa = createStateAttribute(readOptions);
+
+ auto pbr_texture = dynamic_cast(sa);
+ if (pbr_texture)
+ albedo_texture = pbr_texture->albedo.get();
+ else
+ albedo_texture = dynamic_cast(sa);
+
+ if (sa)
{
- stateSet = new osg::StateSet();
-
- osg::Texture* tex = createTexture(image);
- if ( tex )
- {
- stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON);
+ stateset->setTextureAttributeAndModes(0, sa, osg::StateAttribute::ON);
+ }
- if ( _texEnvMode.isSet() )
- {
- osg::TexEnv* texenv = new osg::TexEnv();
- texenv->setMode( *_texEnvMode );
- stateSet->setTextureAttributeAndModes( 0, texenv, osg::StateAttribute::ON );
- }
+ if (pbr_texture)
+ {
+ stateset->setTextureAttributeAndModes(0, pbr_texture, osg::StateAttribute::ON);
+ }
- if ( ImageUtils::hasAlphaChannel( image ) )
- {
- osg::BlendFunc* blendFunc = new osg::BlendFunc();
- blendFunc->setFunction( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
- stateSet->setAttributeAndModes( blendFunc, osg::StateAttribute::ON );
- stateSet->setRenderingHint( osg::StateSet::TRANSPARENT_BIN );
- }
- }
+ if (_texEnvMode.isSet())
+ {
+ osg::TexEnv* texenv = new osg::TexEnv();
+ texenv->setMode(*_texEnvMode);
+ stateset->setTextureAttributeAndModes(0, texenv, osg::StateAttribute::ON);
}
- return stateSet;
+ if (albedo_texture && ImageUtils::hasAlphaChannel(albedo_texture->getImage(0)))
+ {
+ auto* blendFunc = new osg::BlendFunc();
+ blendFunc->setFunction(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON);
+ stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
+ }
+
+ return stateset;
}
osg::ref_ptr
-SkinResource::createImage( const osgDB::Options* dbOptions ) const
+SkinResource::createColorImage( const osgDB::Options* dbOptions ) const
{
if (getStatus().isError())
return 0L;
@@ -225,11 +247,11 @@ SkinResource::createImage( const osgDB::Options* dbOptions ) const
{
osg::ref_ptr ro = Registry::cloneOrCreateOptions(dbOptions);
ro->setOptionString(Stringify() << _readOptions.get() << " " << ro->getOptionString());
- result = _imageURI->readImage(ro.get());
+ result = imageURI()->readImage(ro.get());
}
else
{
- result = _imageURI->readImage(dbOptions);
+ result = imageURI()->readImage(dbOptions);
}
if (result.failed())
@@ -245,31 +267,31 @@ SkinResource::createImage( const osgDB::Options* dbOptions ) const
OSGEARTH_REGISTER_SIMPLE_SYMBOL(skin, SkinSymbol);
-SkinSymbol::SkinSymbol(const SkinSymbol& rhs,const osg::CopyOp& copyop):
-TaggableWithConfig(rhs, copyop),
-_library(rhs._library),
-_objHeight(rhs._objHeight),
-_minObjHeight(rhs._minObjHeight),
-_maxObjHeight(rhs._maxObjHeight),
-_isTiled(rhs._isTiled),
-_randomSeed(rhs._randomSeed),
-_name(rhs._name)
+SkinSymbol::SkinSymbol(const SkinSymbol& rhs, const osg::CopyOp& copyop) :
+ TaggableWithConfig(rhs, copyop),
+ _library(rhs._library),
+ _objHeight(rhs._objHeight),
+ _minObjHeight(rhs._minObjHeight),
+ _maxObjHeight(rhs._maxObjHeight),
+ _isTiled(rhs._isTiled),
+ _randomSeed(rhs._randomSeed),
+ _name(rhs._name)
{
}
-SkinSymbol::SkinSymbol( const Config& conf ) :
-TaggableWithConfig(conf),
-_objHeight ( 0.0f ),
-_minObjHeight ( 0.0f ),
-_maxObjHeight ( FLT_MAX ),
-_isTiled ( false ),
-_randomSeed ( 0 )
+SkinSymbol::SkinSymbol(const Config& conf) :
+ TaggableWithConfig(conf),
+ _objHeight(0.0f),
+ _minObjHeight(0.0f),
+ _maxObjHeight(FLT_MAX),
+ _isTiled(false),
+ _randomSeed(0)
{
- if ( !conf.empty() )
- mergeConfig( conf );
+ if (!conf.empty())
+ mergeConfig(conf);
}
-void
+void
SkinSymbol::mergeConfig( const Config& conf )
{
conf.get( "library", _library );
@@ -330,7 +352,7 @@ SkinSymbol::parseSLD(const Config& c, Style& style)
else if (match(c.key(), "skin-random-seed") ) {
style.getOrCreate()->randomSeed() = as( c.value(), 0u );
}
- else if (match(c.key(), "skin-name")) {
+ else if (match(c.key(), "skin") || match(c.key(), "skin-name")) {
style.getOrCreate()->name() = StringExpression(c.value());
}
}
diff --git a/tests/feature_polygons_textured.earth b/tests/feature_polygons_textured.earth
new file mode 100644
index 0000000000..55d5510ea7
--- /dev/null
+++ b/tests/feature_polygons_textured.earth
@@ -0,0 +1,43 @@
+
+
+