From 4c0f6d7638682fc9783782a828b2daaf64cf6a7b Mon Sep 17 00:00:00 2001 From: Nathaniel Warner Date: Fri, 11 Dec 2020 18:39:20 -0800 Subject: [PATCH 1/3] Add support for dds, avoid creating non power of two textures --- simgear/scene/util/OrthophotoManager.cxx | 136 ++++++++--------------- simgear/scene/util/OrthophotoManager.hxx | 9 +- 2 files changed, 49 insertions(+), 96 deletions(-) diff --git a/simgear/scene/util/OrthophotoManager.cxx b/simgear/scene/util/OrthophotoManager.cxx index 73d15d31..111e8fd6 100644 --- a/simgear/scene/util/OrthophotoManager.cxx +++ b/simgear/scene/util/OrthophotoManager.cxx @@ -209,6 +209,15 @@ namespace simgear { } } + Texture2DRef textureFromImage(const ImageRef& image) { + Texture2DRef texture = new osg::Texture2D(image); + texture->setWrap(osg::Texture::WrapParameter::WRAP_S, osg::Texture::WrapMode::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture::WrapParameter::WRAP_T, osg::Texture::WrapMode::CLAMP_TO_EDGE); + texture->setWrap(osg::Texture::WrapParameter::WRAP_R, osg::Texture::WrapMode::CLAMP_TO_EDGE); + texture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter()); + return texture; + } + OrthophotoRef Orthophoto::fromBucket(const SGBucket& bucket, const PathList& scenery_paths) { const std::string bucket_path = bucket.gen_base_path(); @@ -216,133 +225,74 @@ namespace simgear { for (const auto& scenery_path : scenery_paths) { SGPath path = scenery_path / "Orthophotos" / bucket_path / std::to_string(bucket.gen_index()); - path.concat(".png"); - if (path.exists()) { - ImageRef image = osgDB::readRefImageFile(path.str()); + SGPath png_path = path; + png_path.concat(".png"); + if (png_path.exists()) { + ImageRef image = osgDB::readRefImageFile(png_path.str()); if (!image) { return nullptr; } image->flipVertical(); - OrthophotoBounds bbox = OrthophotoBounds::fromBucket(bucket); - return new Orthophoto(image, bbox); + const Texture2DRef texture = textureFromImage(image); + const OrthophotoBounds bbox = OrthophotoBounds::fromBucket(bucket); + return new Orthophoto(texture, bbox); + } + + SGPath dds_path = path; + dds_path.concat(".dds"); + if (dds_path.exists()) { + ImageRef image = osgDB::readRefImageFile(dds_path.str()); + if (!image) { + return nullptr; + } + const Texture2DRef texture = textureFromImage(image); + const OrthophotoBounds bbox = OrthophotoBounds::fromBucket(bucket); + return new Orthophoto(texture, bbox); } } return nullptr; } - // Handy image cropping function from osgEarth - osg::Image* cropImage(const osg::Image* image, - double src_minx, double src_miny, double src_maxx, double src_maxy, - double &dst_minx, double &dst_miny, double &dst_maxx, double &dst_maxy) { - if ( image == 0L ) - return 0L; + Orthophoto::Orthophoto(const std::vector& orthophotos) { - //Compute the desired cropping rectangle - int windowX = osg::clampBetween( (int)floor( (dst_minx - src_minx) / (src_maxx - src_minx) * (double)image->s()), 0, image->s()-1); - int windowY = osg::clampBetween( (int)floor( (dst_miny - src_miny) / (src_maxy - src_miny) * (double)image->t()), 0, image->t()-1); - int windowWidth = osg::clampBetween( (int)ceil( (dst_maxx - src_minx) / (src_maxx - src_minx) * (double)image->s()) - windowX, 0, image->s()); - int windowHeight = osg::clampBetween( (int)ceil( (dst_maxy - src_miny) / (src_maxy - src_miny) * (double)image->t()) - windowY, 0, image->t()); - - if (windowX + windowWidth > image->s()) - { - windowWidth = image->s() - windowX; - } - - if (windowY + windowHeight > image->t()) - { - windowHeight = image->t() - windowY; - } - - if ((windowWidth * windowHeight) == 0) - { - return NULL; - } - - //Compute the actual bounds of the area we are computing - double res_s = (src_maxx - src_minx) / (double)image->s(); - double res_t = (src_maxy - src_miny) / (double)image->t(); - - dst_minx = src_minx + (double)windowX * res_s; - dst_miny = src_miny + (double)windowY * res_t; - dst_maxx = dst_minx + (double)windowWidth * res_s; - dst_maxy = dst_miny + (double)windowHeight * res_t; - - //OE_NOTICE << "Copying from " << windowX << ", " << windowY << ", " << windowWidth << ", " << windowHeight << std::endl; - - //Allocate the croppped image - osg::Image* cropped = new osg::Image; - cropped->allocateImage(windowWidth, windowHeight, image->r(), image->getPixelFormat(), image->getDataType()); - cropped->setInternalTextureFormat( image->getInternalTextureFormat() ); - - for (int layer=0; layerr(); ++layer) - { - for (int src_row = windowY, dst_row=0; dst_row < windowHeight; src_row++, dst_row++) - { - if (src_row > image->t()-1) SG_LOG(SG_OSG, SG_INFO, "HeightBroke"); - const void* src_data = image->data(windowX, src_row, layer); - void* dst_data = cropped->data(0, dst_row, layer); - memcpy( dst_data, src_data, cropped->getRowSizeInBytes()); - } - } - return cropped; - } - - Orthophoto::Orthophoto(const std::vector& orthophotos, const OrthophotoBounds& needed_bbox) { - - OrthophotoBounds prelim_bbox; for (const auto& orthophoto : orthophotos) { - prelim_bbox.expandToInclude(orthophoto->getBbox()); + _bbox.expandToInclude(orthophoto->getBbox()); } const OrthophotoRef& some_orthophoto = orthophotos[0]; - const ImageRef& some_image = some_orthophoto->_image; + const ImageRef& some_image = some_orthophoto->_texture->getImage(); const OrthophotoBounds& some_bbox = some_orthophoto->getBbox(); const double degs_to_pixels = some_image->s() / some_bbox.getWidth(); - const int total_width = degs_to_pixels * prelim_bbox.getWidth(); - const int total_height = degs_to_pixels * prelim_bbox.getHeight(); + const int total_width = degs_to_pixels * _bbox.getWidth(); + const int total_height = degs_to_pixels * _bbox.getHeight(); const int depth = some_image->r(); GLenum pixel_format = some_image->getPixelFormat(); GLenum data_type = some_image->getDataType(); int packing = some_image->getPacking(); - ImageRef prelim_image = new osg::Image(); - prelim_image->allocateImage(total_width, total_height, depth, pixel_format, data_type, packing); + ImageRef composite_image = new osg::Image(); + composite_image->allocateImage(total_width, total_height, depth, pixel_format, data_type, packing); for (const auto& orthophoto : orthophotos) { const OrthophotoBounds& bounds = orthophoto->getBbox(); const int width = degs_to_pixels * bounds.getWidth(); const int height = degs_to_pixels * bounds.getHeight(); - const int s_offset = degs_to_pixels * prelim_bbox.getLonOffset(bounds); - const int t_offset = degs_to_pixels * prelim_bbox.getLatOffset(bounds); + const int s_offset = degs_to_pixels * _bbox.getLonOffset(bounds); + const int t_offset = degs_to_pixels * _bbox.getLatOffset(bounds); // Make a deep copy of the orthophoto's image so that we don't modify the original when scaling - ImageRef sub_image = new osg::Image(*orthophoto->_image, osg::CopyOp::DEEP_COPY_ALL); + ImageRef sub_image = new osg::Image(*orthophoto->_texture->getImage(), osg::CopyOp::DEEP_COPY_ALL); sub_image->scaleImage(width, height, depth); - prelim_image->copySubImage(s_offset, t_offset, 0, sub_image); + composite_image->copySubImage(s_offset, t_offset, 0, sub_image); } - double x_min = prelim_bbox.getLonOffset(needed_bbox) * degs_to_pixels; - double y_min = prelim_bbox.getLatOffset(needed_bbox) * degs_to_pixels; - double x_max = x_min + needed_bbox.getWidth() * degs_to_pixels; - double y_max = y_min + needed_bbox.getHeight() * degs_to_pixels; - - _image = cropImage(prelim_image, 0.0, 0.0, total_width, total_height, x_min, y_min, x_max, y_max); - _bbox = needed_bbox; // Theoretically, it would be good to adjust from how the bounds have changed from cropping - } - - osg::ref_ptr Orthophoto::getTexture() { - osg::ref_ptr texture = new osg::Texture2D(_image); - texture->setWrap(osg::Texture::WrapParameter::WRAP_S, osg::Texture::WrapMode::CLAMP_TO_EDGE); - texture->setWrap(osg::Texture::WrapParameter::WRAP_T, osg::Texture::WrapMode::CLAMP_TO_EDGE); - texture->setWrap(osg::Texture::WrapParameter::WRAP_R, osg::Texture::WrapMode::CLAMP_TO_EDGE); - texture->setMaxAnisotropy(SGSceneFeatures::instance()->getTextureFilter()); - return texture; + _texture = textureFromImage(composite_image); } OrthophotoManager* OrthophotoManager::instance() { @@ -397,8 +347,10 @@ namespace simgear { if (orthophotos.size() == 0) { return nullptr; + } else if (orthophotos.size() == 1) { + return orthophotos[0]; + } else { + return new Orthophoto(orthophotos); } - - return new Orthophoto(orthophotos, needed_bounds); } } diff --git a/simgear/scene/util/OrthophotoManager.hxx b/simgear/scene/util/OrthophotoManager.hxx index 752a4525..ba2cc880 100644 --- a/simgear/scene/util/OrthophotoManager.hxx +++ b/simgear/scene/util/OrthophotoManager.hxx @@ -40,6 +40,7 @@ namespace simgear { using ImageRef = osg::ref_ptr; + using Texture2DRef = osg::ref_ptr; class Orthophoto; using OrthophotoRef = osg::ref_ptr; @@ -73,16 +74,16 @@ namespace simgear { class Orthophoto : public osg::Referenced { private: - ImageRef _image; + Texture2DRef _texture; OrthophotoBounds _bbox; public: static OrthophotoRef fromBucket(const SGBucket& bucket, const PathList& scenery_paths); - Orthophoto(const ImageRef& image, const OrthophotoBounds& bbox) { _image = image; _bbox = bbox; } - Orthophoto(const std::vector& orthophotos, const OrthophotoBounds& needed_bbox); + Orthophoto(const Texture2DRef& texture, const OrthophotoBounds& bbox) { _texture = texture; _bbox = bbox; } + Orthophoto(const std::vector& orthophotos); - osg::ref_ptr getTexture(); + Texture2DRef getTexture() const { return _texture; }; OrthophotoBounds getBbox() const { return _bbox; }; }; From 88be3075409a2a2ac396f36bc086933a50917298 Mon Sep 17 00:00:00 2001 From: Nathaniel Warner Date: Sat, 12 Dec 2020 11:44:55 -0800 Subject: [PATCH 2/3] Gracefully handle pixel format mismatch --- simgear/scene/util/OrthophotoManager.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/simgear/scene/util/OrthophotoManager.cxx b/simgear/scene/util/OrthophotoManager.cxx index 111e8fd6..3a8a8429 100644 --- a/simgear/scene/util/OrthophotoManager.cxx +++ b/simgear/scene/util/OrthophotoManager.cxx @@ -287,6 +287,11 @@ namespace simgear { // Make a deep copy of the orthophoto's image so that we don't modify the original when scaling ImageRef sub_image = new osg::Image(*orthophoto->_texture->getImage(), osg::CopyOp::DEEP_COPY_ALL); + if (sub_image->getPixelFormat() != pixel_format) { + SG_LOG(SG_OSG, SG_ALERT, "Pixel format mismatch. Not creating part of composite orthophoto."); + continue; + } + sub_image->scaleImage(width, height, depth); composite_image->copySubImage(s_offset, t_offset, 0, sub_image); From 9e103fe7bfb581d8b170b6e34ec584fdef693951 Mon Sep 17 00:00:00 2001 From: Nathaniel Warner Date: Sat, 12 Dec 2020 13:59:52 -0800 Subject: [PATCH 3/3] Prefer DDS over PNG for orthophoto --- simgear/scene/util/OrthophotoManager.cxx | 26 ++++++++++++------------ 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/simgear/scene/util/OrthophotoManager.cxx b/simgear/scene/util/OrthophotoManager.cxx index 3a8a8429..51db95db 100644 --- a/simgear/scene/util/OrthophotoManager.cxx +++ b/simgear/scene/util/OrthophotoManager.cxx @@ -224,7 +224,19 @@ namespace simgear { for (const auto& scenery_path : scenery_paths) { SGPath path = scenery_path / "Orthophotos" / bucket_path / std::to_string(bucket.gen_index()); - + + SGPath dds_path = path; + dds_path.concat(".dds"); + if (dds_path.exists()) { + ImageRef image = osgDB::readRefImageFile(dds_path.str()); + if (!image) { + return nullptr; + } + const Texture2DRef texture = textureFromImage(image); + const OrthophotoBounds bbox = OrthophotoBounds::fromBucket(bucket); + return new Orthophoto(texture, bbox); + } + SGPath png_path = path; png_path.concat(".png"); if (png_path.exists()) { @@ -237,18 +249,6 @@ namespace simgear { const OrthophotoBounds bbox = OrthophotoBounds::fromBucket(bucket); return new Orthophoto(texture, bbox); } - - SGPath dds_path = path; - dds_path.concat(".dds"); - if (dds_path.exists()) { - ImageRef image = osgDB::readRefImageFile(dds_path.str()); - if (!image) { - return nullptr; - } - const Texture2DRef texture = textureFromImage(image); - const OrthophotoBounds bbox = OrthophotoBounds::fromBucket(bucket); - return new Orthophoto(texture, bbox); - } } return nullptr;