1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
|
#include "imagemanager.hpp"
#include <cassert>
#include <osgDB/Registry>
#include <components/debug/debuglog.hpp>
#include <components/misc/pathhelpers.hpp>
#include <components/sceneutil/glextensions.hpp>
#include <components/vfs/manager.hpp>
#include <components/vfs/pathutil.hpp>
#include "objectcache.hpp"
#ifdef OSG_LIBRARY_STATIC
// This list of plugins should match with the list in the top-level CMakelists.txt.
USE_OSGPLUGIN(png)
USE_OSGPLUGIN(tga)
USE_OSGPLUGIN(dds)
USE_OSGPLUGIN(jpeg)
USE_OSGPLUGIN(bmp)
USE_OSGPLUGIN(osg)
USE_SERIALIZER_WRAPPER_LIBRARY(osg)
#endif
namespace
{
osg::ref_ptr<osg::Image> createWarningImage()
{
osg::ref_ptr<osg::Image> warningImage = new osg::Image;
int width = 8, height = 8;
warningImage->allocateImage(width, height, 1, GL_RGB, GL_UNSIGNED_BYTE);
assert(warningImage->isDataContiguous());
unsigned char* data = warningImage->data();
for (int i = 0; i < width * height; ++i)
{
data[3 * i] = (255);
data[3 * i + 1] = (0);
data[3 * i + 2] = (255);
}
return warningImage;
}
}
namespace Resource
{
ImageManager::ImageManager(const VFS::Manager* vfs, double expiryDelay)
: ResourceManager(vfs, expiryDelay)
, mWarningImage(createWarningImage())
, mOptions(new osgDB::Options("dds_flip dds_dxt1_detect_rgba ignoreTga2Fields"))
, mOptionsNoFlip(new osgDB::Options("dds_dxt1_detect_rgba ignoreTga2Fields"))
{
}
ImageManager::~ImageManager() {}
bool checkSupported(osg::Image* image)
{
switch (image->getPixelFormat())
{
case (GL_COMPRESSED_RGB_S3TC_DXT1_EXT):
case (GL_COMPRESSED_RGBA_S3TC_DXT1_EXT):
case (GL_COMPRESSED_RGBA_S3TC_DXT3_EXT):
case (GL_COMPRESSED_RGBA_S3TC_DXT5_EXT):
{
if (!SceneUtil::glExtensionsReady())
return true; // hashtag yolo (CS might not have context when loading assets)
osg::GLExtensions& exts = SceneUtil::getGLExtensions();
if (!exts.isTextureCompressionS3TCSupported
// This one works too. Should it be included in isTextureCompressionS3TCSupported()? Submitted as a
// patch to OSG.
&& !osg::isGLExtensionSupported(exts.contextID, "GL_S3_s3tc"))
{
return false;
}
break;
}
// not bothering with checks for other compression formats right now
default:
return true;
}
return true;
}
osg::ref_ptr<osg::Image> ImageManager::getImage(VFS::Path::NormalizedView path, bool disableFlip)
{
osg::ref_ptr<osg::Object> obj = mCache->getRefFromObjectCache(path);
if (obj)
return osg::ref_ptr<osg::Image>(static_cast<osg::Image*>(obj.get()));
else
{
Files::IStreamPtr stream;
try
{
stream = mVFS->get(path);
}
catch (std::exception& e)
{
Log(Debug::Error) << "Failed to open image: " << e.what();
mCache->addEntryToObjectCache(path.value(), mWarningImage);
return mWarningImage;
}
const std::string ext(Misc::getFileExtension(path.value()));
osgDB::ReaderWriter* reader = osgDB::Registry::instance()->getReaderWriterForExtension(ext);
if (!reader)
{
Log(Debug::Error) << "Error loading " << path << ": no readerwriter for '" << ext << "' found";
mCache->addEntryToObjectCache(path.value(), mWarningImage);
return mWarningImage;
}
bool killAlpha = false;
if (reader->supportedExtensions().count("tga"))
{
// Morrowind ignores the alpha channel of 16bpp TGA files even when the header says not to
unsigned char header[18];
stream->read((char*)header, 18);
if (stream->gcount() != 18)
{
Log(Debug::Error) << "Error loading " << path << ": couldn't read TGA header";
mCache->addEntryToObjectCache(path.value(), mWarningImage);
return mWarningImage;
}
int type = header[2];
int depth;
if (type == 1 || type == 9)
depth = header[7];
else
depth = header[16];
int alphaBPP = header[17] & 0x0F;
killAlpha = depth == 16 && alphaBPP == 1;
stream->seekg(0);
}
osgDB::ReaderWriter::ReadResult result
= reader->readImage(*stream, disableFlip ? mOptionsNoFlip : mOptions);
if (!result.success())
{
Log(Debug::Error) << "Error loading " << path << ": " << result.message() << " code "
<< result.status();
mCache->addEntryToObjectCache(path.value(), mWarningImage);
return mWarningImage;
}
osg::ref_ptr<osg::Image> image = result.getImage();
image->setFileName(std::string(path.value()));
if (!checkSupported(image))
{
static bool uncompress = (getenv("OPENMW_DECOMPRESS_TEXTURES") != nullptr);
if (!uncompress)
{
Log(Debug::Error) << "Error loading " << path << ": no S3TC texture compression support installed";
mCache->addEntryToObjectCache(path.value(), mWarningImage);
return mWarningImage;
}
else
{
// decompress texture in software if not supported by GPU
// requires update to getColor() to be released with OSG 3.6
osg::ref_ptr<osg::Image> newImage = new osg::Image;
newImage->setFileName(image->getFileName());
newImage->allocateImage(image->s(), image->t(), image->r(),
image->isImageTranslucent() ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE);
for (int s = 0; s < image->s(); ++s)
for (int t = 0; t < image->t(); ++t)
for (int r = 0; r < image->r(); ++r)
newImage->setColor(image->getColor(s, t, r), s, t, r);
image = newImage;
}
}
else if (killAlpha)
{
osg::ref_ptr<osg::Image> newImage = new osg::Image;
newImage->setFileName(image->getFileName());
newImage->allocateImage(image->s(), image->t(), image->r(), GL_RGB, GL_UNSIGNED_BYTE);
// OSG just won't write the alpha as there's nowhere to put it.
for (int s = 0; s < image->s(); ++s)
for (int t = 0; t < image->t(); ++t)
for (int r = 0; r < image->r(); ++r)
newImage->setColor(image->getColor(s, t, r), s, t, r);
image = newImage;
}
mCache->addEntryToObjectCache(path.value(), image);
return image;
}
}
osg::Image* ImageManager::getWarningImage()
{
return mWarningImage;
}
void ImageManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const
{
Resource::reportStats("Image", frameNumber, mCache->getStats(), *stats);
}
}
|