Add support for atp and file urls in OBJReader

This commit is contained in:
Ryan Huffman 2017-05-01 13:21:52 -07:00
parent a057400a92
commit 074a11306c
5 changed files with 167 additions and 158 deletions

View file

@ -24,6 +24,7 @@
#include <shared/NsightHelpers.h> #include <shared/NsightHelpers.h>
#include <NetworkAccessManager.h> #include <NetworkAccessManager.h>
#include <ResourceManager.h>
#include "FBXReader.h" #include "FBXReader.h"
#include "ModelFormatLogging.h" #include "ModelFormatLogging.h"
@ -165,6 +166,7 @@ bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex,
} }
return true; return true;
} }
QVector<OBJFace> OBJFace::triangulate() { QVector<OBJFace> OBJFace::triangulate() {
QVector<OBJFace> newFaces; QVector<OBJFace> newFaces;
const int nVerticesInATriangle = 3; const int nVerticesInATriangle = 3;
@ -183,6 +185,7 @@ QVector<OBJFace> OBJFace::triangulate() {
} }
return newFaces; return newFaces;
} }
void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f at index i
vertexIndices.append(face->vertexIndices[index]); vertexIndices.append(face->vertexIndices[index]);
if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent. if (face->textureUVIndices.count() > 0) { // Any at all. Runtime error if not consistent.
@ -193,24 +196,13 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f
} }
} }
static bool replyOK(QNetworkReply* netReply, QUrl url) { // This will be reworked when we make things asynchronous
return (netReply && netReply->isFinished() &&
(url.toString().startsWith("file", Qt::CaseInsensitive) ? // file urls don't have http status codes
netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString().isEmpty() :
(netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)));
}
bool OBJReader::isValidTexture(const QByteArray &filename) { bool OBJReader::isValidTexture(const QByteArray &filename) {
if (_url.isEmpty()) { if (_url.isEmpty()) {
return false; return false;
} }
QUrl candidateUrl = _url.resolved(QUrl(filename)); QUrl candidateUrl = _url.resolved(QUrl(filename));
QNetworkReply *netReply = request(candidateUrl, true);
bool isValid = replyOK(netReply, candidateUrl); return ResourceManager::resourceExists(candidateUrl);
if (netReply) {
netReply->deleteLater();
}
return isValid;
} }
void OBJReader::parseMaterialLibrary(QIODevice* device) { void OBJReader::parseMaterialLibrary(QIODevice* device) {
@ -274,7 +266,28 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
} }
} }
QNetworkReply* OBJReader::request(QUrl& url, bool isTest) { std::tuple<bool, QByteArray> requestData(QUrl& url) {
auto request = ResourceManager::createResourceRequest(nullptr, url);
if (!request) {
return std::make_tuple(false, QByteArray());
}
request->send();
QEventLoop loop;
QObject::connect(request, &ResourceRequest::finished, &loop, &QEventLoop::quit);
loop.exec();
if (request->getResult() == ResourceRequest::Success) {
return std::make_tuple(true, request->getData());
} else {
return std::make_tuple(false, QByteArray());
}
}
QNetworkReply* request(QUrl& url, bool isTest) {
if (!qApp) { if (!qApp) {
return nullptr; return nullptr;
} }
@ -293,10 +306,7 @@ QNetworkReply* OBJReader::request(QUrl& url, bool isTest) {
QEventLoop loop; // Create an event loop that will quit when we get the finished signal QEventLoop loop; // Create an event loop that will quit when we get the finished signal
QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit())); QObject::connect(netReply, SIGNAL(finished()), &loop, SLOT(quit()));
loop.exec(); // Nothing is going to happen on this whole run thread until we get this loop.exec(); // Nothing is going to happen on this whole run thread until we get this
static const int WAIT_TIMEOUT_MS = 500;
while (!aboutToQuit && qApp && !netReply->isReadable()) {
netReply->waitForReadyRead(WAIT_TIMEOUT_MS); // so we might as well block this thread waiting for the response, rather than
}
QObject::disconnect(connection); QObject::disconnect(connection);
return netReply; // trying to sync later on. return netReply; // trying to sync later on.
} }
@ -624,15 +634,15 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping,
// Throw away any path part of libraryName, and merge against original url. // Throw away any path part of libraryName, and merge against original url.
QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName()); QUrl libraryUrl = _url.resolved(QUrl(libraryName).fileName());
qCDebug(modelformat) << "OBJ Reader material library" << libraryName << "used in" << _url; qCDebug(modelformat) << "OBJ Reader material library" << libraryName << "used in" << _url;
QNetworkReply* netReply = request(libraryUrl, false); bool success;
if (replyOK(netReply, libraryUrl)) { QByteArray data;
parseMaterialLibrary(netReply); std::tie<bool, QByteArray>(success, data) = requestData(libraryUrl);
if (success) {
QBuffer buffer { &data };
buffer.open(QIODevice::ReadOnly);
parseMaterialLibrary(&buffer);
} else { } else {
qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer. Got" qCDebug(modelformat) << "OBJ Reader WARNING:" << libraryName << "did not answer";
<< (!netReply ? "aborted" : netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
}
if (netReply) {
netReply->deleteLater();
} }
} }
} }

View file

@ -72,7 +72,6 @@ public:
QString currentMaterialName; QString currentMaterialName;
QHash<QString, OBJMaterial> materials; QHash<QString, OBJMaterial> materials;
QNetworkReply* request(QUrl& url, bool isTest);
FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl()); FBXGeometry* readOBJ(QByteArray& model, const QVariantHash& mapping, bool combineParts, const QUrl& url = QUrl());
private: private:

View file

@ -40,16 +40,16 @@ AssetResourceRequest::~AssetResourceRequest() {
} }
} }
bool AssetResourceRequest::urlIsAssetHash() const { bool AssetResourceRequest::urlIsAssetHash(const QUrl& url) {
static const QString ATP_HASH_REGEX_STRING { "^atp:([A-Fa-f0-9]{64})(\\.[\\w]+)?$" }; static const QString ATP_HASH_REGEX_STRING { "^atp:([A-Fa-f0-9]{64})(\\.[\\w]+)?$" };
QRegExp hashRegex { ATP_HASH_REGEX_STRING }; QRegExp hashRegex { ATP_HASH_REGEX_STRING };
return hashRegex.exactMatch(_url.toString()); return hashRegex.exactMatch(url.toString());
} }
void AssetResourceRequest::doSend() { void AssetResourceRequest::doSend() {
// We'll either have a hash or an ATP path to a file (that maps to a hash) // We'll either have a hash or an ATP path to a file (that maps to a hash)
if (urlIsAssetHash()) { if (urlIsAssetHash(_url)) {
// We've detected that this is a hash - simply use AssetClient to request that asset // We've detected that this is a hash - simply use AssetClient to request that asset
auto parts = _url.path().split(".", QString::SkipEmptyParts); auto parts = _url.path().split(".", QString::SkipEmptyParts);
auto hash = parts.length() > 0 ? parts[0] : ""; auto hash = parts.length() > 0 ? parts[0] : "";

View file

@ -32,7 +32,7 @@ private slots:
void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal); void onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
private: private:
bool urlIsAssetHash() const; static bool urlIsAssetHash(const QUrl& url);
void requestMappingForPath(const AssetPath& path); void requestMappingForPath(const AssetPath& path);
void requestHash(const AssetHash& hash); void requestHash(const AssetHash& hash);

View file

@ -14,10 +14,10 @@
#include <QNetworkDiskCache> #include <QNetworkDiskCache>
#include <QStandardPaths> #include <QStandardPaths>
#include <QThread> #include <QThread>
#include <QFileInfo>
#include <SharedUtil.h> #include <SharedUtil.h>
#include "AssetResourceRequest.h" #include "AssetResourceRequest.h"
#include "FileResourceRequest.h" #include "FileResourceRequest.h"
#include "HTTPResourceRequest.h" #include "HTTPResourceRequest.h"