Something went wrong. Try again.
OpenMW is an open implementation of the Morrowind engine
Something went wrong. Try again.
8.5 kB · 228 lines
C++
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229#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; }
bool isS3TC(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: return true; } return false; }
bool checkSupported(osg::Image* image) { // not bothering with checks for other compression formats right now if (!isS3TC(image)) return true;
// hashtag yolo (CS might not have context when loading assets) if (!SceneUtil::glExtensionsReady()) return true;
return SceneUtil::getGLExtensions().isTextureCompressionS3TCSupported; }
}
namespace Resource{
ImageManager::ImageManager(const VFS::Manager* vfs, double expiryDelay) : ResourceManager(vfs, expiryDelay) , mWarningImage(createWarningImage()) , mOptions(new osgDB::Options("dds_dxt1_detect_rgba ignoreTga2Fields")) { }
ImageManager::~ImageManager() {}
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; }
std::string ext(Misc::getFileExtension(path.value()));
// Non-standard, but Morrowind supports this if (ext == "targa") ext = "tga";
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, 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->setOrigin(image->getOrigin()); 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->setOrigin(image->getOrigin()); 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; }
// OSG might not set the right origin for DDS if (ext == "dds") image->setOrigin(osg::Image::TOP_LEFT);
// Convert the image to the convention we expect if (image->getOrigin() == osg::Image::BOTTOM_LEFT && !disableFlip) { if (image->isCompressed() && !isS3TC(image)) { // This is most likely a KTX texture that OSG can't flip // We don't want it to be corrupted or displayed incorrectly, so bail // OSGoS *can* flip RGTC, but we can't verify that (yet?) Log(Debug::Error) << "Error loading " << path << ": cannot flip non-S3TC compressed texture"; mCache->addEntryToObjectCache(path.value(), mWarningImage); return mWarningImage; }
image->flipVertical(); image->setOrigin(osg::Image::TOP_LEFT); }
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); }
}