diff options
Diffstat (limited to 'src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp')
-rw-r--r-- | src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp | 255 |
1 files changed, 230 insertions, 25 deletions
diff --git a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp index 43f7541..87fe54c 100644 --- a/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp +++ b/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp @@ -54,6 +54,207 @@ using namespace qt3ds::render; +namespace qt3ds { +namespace render { + +static const QString QT3DSTUDIO_TAG = QStringLiteral("qt3dstudio"); + +static QFileInfo matchCaseInsensitiveFile(const QString& file) +{ + const auto searchFromPaths = [](QString file) -> QFileInfo { + QFileInfo fileInfo(file); + if (fileInfo.exists()) + return fileInfo; + + const QStringList searchDirectories = QDir::searchPaths(QT3DSTUDIO_TAG); + for (const auto &directoryPath : searchDirectories) { + auto path = QDir::cleanPath(directoryPath + '/' + file); + QFileInfo info(path); + if (info.exists()) + return info; + } + return fileInfo; + }; + + QFileInfo info = searchFromPaths(file); + if (info.exists()) + return info; + + QString searchPath = file; + + // Trying relative to search directories. + // First remove any ../ and ./ from the path + if (searchPath.startsWith(QLatin1String("../"))) + searchPath = searchPath.right(searchPath.length() - 3); + else if (searchPath.startsWith(QLatin1String("./"))) + searchPath = searchPath.right(searchPath.length() - 2); + + int loops = 0; + while (++loops <= 4) { + info = searchFromPaths(searchPath); + if (info.exists()) + return info; + searchPath.prepend(QLatin1String("../")); + } + + return info; +} + +static bool checkFileExists(const QString &tryFile, bool relative, QString &outFile) +{ + QFileInfo info; + if (relative) + info = matchCaseInsensitiveFile(tryFile); + else + info.setFile(tryFile); + if (info.exists()) { + outFile = info.filePath(); + return true; + } + if (!relative) { + // Some textures, for example environment maps for custom materials, + // have absolute path at this point. It points to the wrong place with + // the new project structure, so we need to split it up and construct + // the new absolute path here. + QString wholePath = tryFile; + QStringList splitPath = wholePath.split(QLatin1String("../")); + if (splitPath.size() > 1) { + QString searchPath = splitPath.first() + splitPath.last(); + int loops = 0; + while (++loops <= 3) { + info.setFile(searchPath); + if (info.exists()) { + outFile = info.filePath(); + return true; + } + searchPath = splitPath.at(0); + for (int i = 0; i < loops; i++) + searchPath.append(QLatin1String("../")); + searchPath.append(splitPath.last()); + } + } + } + return false; +} + +// Locate existing file by adding a supported suffix to localFile. +static bool existingImageFileForPath(const QString &localFile, bool relative, bool preferKTX, + QString &outPath) +{ + // Do nothing if given filepath exists without suffix + QFileInfo fi(localFile); + if (fi.exists()) { + outPath = localFile; + return true; + } + + // Lists of supported image formats in preferred-first order. + const QStringList compressedFormats {"ktx", "astc", "dds"}; + const QStringList nonCompressedFormats {"hdr", "png", "jpg", "jpeg", "gif"}; + + // Depending on preferKTX, check compressed formats before or after non-compressed ones. + QStringList supportedFormats = preferKTX ? compressedFormats + nonCompressedFormats + : nonCompressedFormats + compressedFormats; + + // Check first if file exists from resources as that + // is common and optimal for integrity case. + for (const QString &suffix : supportedFormats) { + QString tryFile = ":/" + localFile + "." + suffix; + if (checkFileExists(tryFile, relative, outPath)) + return true; + } + // If not found, check still file path as-is + for (const QString &suffix : supportedFormats) { + QString tryFile = localFile + "." + suffix; + if (checkFileExists(tryFile, relative, outPath)) + return true; + } + + outPath = localFile; + return false; +} + +// Return resolved image path for unresolved source path. +// Handle preferKtx, search paths, empty suffixes, schemas etc. +QString IBufferManager::resolveImagePath(const QString &sourcePath, bool preferKtx) +{ + QString path = sourcePath; + QUrl urlPath(path); + + // Do nothing for image providers + if (urlPath.scheme() == QLatin1String("image")) + return sourcePath; + + // Normalize the path + path = CFileTools::NormalizePathForQtUsage(path); + urlPath.setUrl(path); + + const bool hasSuffix = urlPath.fileName().contains(QLatin1Char('.')); + const bool relative = urlPath.isRelative(); + + // Find an image file corresponding to only filename without suffix + if (!hasSuffix) { + QString outPath; + if (!existingImageFileForPath(path, relative, preferKtx, outPath)) { + qCWarning(WARNING, "Failed to resolve path %s", qPrintable(sourcePath)); + return {}; + } + return outPath; + } + + // First check ktx files if preferKtx is set + if (preferKtx) { + QString ktxSource = path; + const bool originalIsKtx = path.endsWith(QLatin1String("ktx"), Qt::CaseInsensitive); + if (!originalIsKtx) { + const int index = ktxSource.lastIndexOf(QLatin1Char('.')); + ktxSource = ktxSource.left(index); + ktxSource.append(QLatin1String(".ktx")); + } + QString result; + if (checkFileExists(ktxSource, relative, result)) + return result; + if (originalIsKtx) { + qCWarning(WARNING, "Failed to resolve path %s", qPrintable(sourcePath)); + return {}; + } + } + + QString result; + if (checkFileExists(path, relative, result)) + return result; + + qCWarning(WARNING, "Failed to resolve path %s", qPrintable(sourcePath)); + return {}; +} + +QSet<QString> IBufferManager::resolveImageSet(const QSet<QString> &set, bool preferKtx) +{ + QSet<QString> ret; + for (auto &sourcePath : set) { + auto unified = resolveImagePath(sourcePath, preferKtx); + if (!unified.isEmpty()) + ret.insert(unified); + } + return ret; +} + +QVector<CRegisteredString> IBufferManager::resolveSourcePaths( + IStringTable &strTable, const QVector<CRegisteredString> &sourcePaths, bool preferKtx) +{ + QVector<CRegisteredString> ret; + for (int i = 0; i < sourcePaths.size(); i++) { + QString path = QString::fromLatin1(sourcePaths[i].c_str()); + auto unified = strTable.RegisterStr(IBufferManager::resolveImagePath(path, preferKtx)); + if (unified.IsValid()) + ret.append(unified); + } + return ret; +} + +} +} + namespace { using eastl::hash; @@ -122,6 +323,7 @@ struct SBufferManager : public IBufferManager QHash<QString, QSharedPointer<QQmlImageProviderBase> > m_imageProviders; QHash<QString, ReloadableTexturePtr> m_reloadableTextures; + ReloadableTexturePtr m_nullTexture; static const char8_t *GetPrimitivesDirectory() { return "res//primitives"; } @@ -296,24 +498,25 @@ struct SBufferManager : public IBufferManager void loadSet(const QSet<QString> &imageSet, bool flipCompressed) override { - for (const auto &x : imageSet) { - if (!m_reloadableTextures.contains(x)) { - auto img = CreateReloadableImage(m_StrTable->RegisterStr(qPrintable(x)), false, - false, flipCompressed); - img->m_initialized = false; - loadTextureImage(*m_reloadableTextures[x]); - } else if (!m_reloadableTextures[x]->m_loaded) { - loadTextureImage(*m_reloadableTextures[x]); + for (const auto &sourcePath : imageSet) { + if (!m_reloadableTextures.contains(sourcePath)) { + auto img = CreateReloadableImage(sourcePath, false, false, flipCompressed); + if (img != m_nullTexture) { + img->m_initialized = false; + loadTextureImage(*m_reloadableTextures[sourcePath]); + } + } else if (!m_reloadableTextures[sourcePath]->m_loaded) { + loadTextureImage(*m_reloadableTextures[sourcePath]); } } } void unloadSet(const QSet<QString> &imageSet) override { - for (const auto &x : imageSet) { - if (m_reloadableTextures.contains(x)) { - if (m_reloadableTextures[x]->m_loaded) - unloadTextureImage(*m_reloadableTextures[x]); + for (const auto &sourcePath : imageSet) { + if (m_reloadableTextures.contains(sourcePath)) { + if (m_reloadableTextures[sourcePath]->m_loaded) + unloadTextureImage(*m_reloadableTextures[sourcePath]); } } } @@ -328,18 +531,20 @@ struct SBufferManager : public IBufferManager } } - virtual ReloadableTexturePtr CreateReloadableImage(CRegisteredString inSourcePath, + virtual ReloadableTexturePtr CreateReloadableImage(const QString &inSourcePath, bool inForceScanForTransparency, bool inBsdfMipmaps, bool flipCompressed) override { - // Remove schema and root from keys. To prevent loading twice file "qrc:///foo" and "foo" - QString path = QString::fromLatin1(inSourcePath.c_str()); - path = QUrl(path).path(); - if (path.startsWith(QLatin1String("/"))) - path = path.right(path.length() - 1); - + const auto path = inSourcePath; const bool inserted = m_reloadableTextures.contains(path); + if (path.isNull()) { + if (m_nullTexture.isNull()) { + m_nullTexture = ReloadableTexturePtr::create(); + m_nullTexture->m_initialized = true; + } + return m_nullTexture; + } if (!inserted || (inserted && m_reloadableTextures[path]->m_initialized == false)) { if (!inserted) m_reloadableTextures.insert(path, ReloadableTexturePtr::create()); @@ -361,13 +566,13 @@ struct SBufferManager : public IBufferManager // Failed to look up image from map, perhaps external of UIP/UIA image // Try with full URI and load from filesystem. if (theIter == m_ImageMap.end()) - theIter = m_ImageMap.find(getImagePath(QString::fromLatin1(inSourcePath.c_str()))); + theIter = m_ImageMap.find(getImagePath(inSourcePath)); if (theIter == m_ImageMap.end()) { loadTextureImage(*m_reloadableTextures[path], flipCompressed); imagePath = getImagePath(path); theIter = m_ImageMap.find(imagePath); if (theIter == m_ImageMap.end()) - theIter = m_ImageMap.find(getImagePath(QString::fromLatin1(inSourcePath.c_str()))); + theIter = m_ImageMap.find(getImagePath(inSourcePath)); } #endif if (theIter != m_ImageMap.end()) { @@ -390,7 +595,7 @@ struct SBufferManager : public IBufferManager QT3DS_PERF_SCOPED_TIMER(m_PerfTimer, "BufferManager: Image Decompression") theLoadedImage = SLoadedTexture::Load( inImagePath.c_str(), m_Context->GetFoundation(), *m_InputStreamFactory, - true, inFlipCompressed, m_Context->GetRenderContextType(), false, this); + true, inFlipCompressed, m_Context->GetRenderContextType(), this); // Hackish solution to custom materials not finding their textures if they are used // in sub-presentations. if (!theLoadedImage) { @@ -403,7 +608,7 @@ struct SBufferManager : public IBufferManager theLoadedImage = SLoadedTexture::Load( searchPathRel.toUtf8(), m_Context->GetFoundation(), *m_InputStreamFactory, true, false, - m_Context->GetRenderContextType(), false, this); + m_Context->GetRenderContextType(), this); } if (!theLoadedImage) { @@ -415,7 +620,7 @@ struct SBufferManager : public IBufferManager theLoadedImage = SLoadedTexture::Load( searchPath.toUtf8(), m_Context->GetFoundation(), *m_InputStreamFactory, true, false, - m_Context->GetRenderContextType(), false, this); + m_Context->GetRenderContextType(), this); searchPath.prepend(QLatin1String("../")); } } @@ -435,7 +640,7 @@ struct SBufferManager : public IBufferManager theLoadedImage = SLoadedTexture::Load( searchPath.toUtf8(), m_Context->GetFoundation(), *m_InputStreamFactory, true, false, - m_Context->GetRenderContextType(), false, this); + m_Context->GetRenderContextType(), this); searchPath = splitPath.at(0); for (int i = 0; i < loops; i++) searchPath.append(QLatin1String("../")); |