summaryrefslogtreecommitdiffstats
path: root/src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp')
-rw-r--r--src/runtimerender/resourcemanager/Qt3DSRenderBufferManager.cpp255
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("../"));