Merge pull request #5441 from sethalves/fix-vhacd

if OBJ data isn't from a url, don't dereference null QUrl pointers
This commit is contained in:
Howard Stearns 2015-07-27 20:27:44 -07:00
commit 6433a48fdb
2 changed files with 46 additions and 33 deletions

View file

@ -195,7 +195,10 @@ void OBJFace::addFrom(const OBJFace* face, int index) { // add using data from f
} }
bool OBJReader::isValidTexture(const QByteArray &filename) { bool OBJReader::isValidTexture(const QByteArray &filename) {
QUrl candidateUrl = url->resolved(QUrl(filename)); if (!_url) {
return false;
}
QUrl candidateUrl = _url->resolved(QUrl(filename));
QNetworkReply *netReply = request(candidateUrl, true); QNetworkReply *netReply = request(candidateUrl, true);
bool isValid = netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200); bool isValid = netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200);
netReply->deleteLater(); netReply->deleteLater();
@ -242,7 +245,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
} else if ((token == "map_Kd") || (token == "map_Ks")) { } else if ((token == "map_Kd") || (token == "map_Ks")) {
QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8(); QByteArray filename = QUrl(tokenizer.getLineAsDatum()).fileName().toUtf8();
if (filename.endsWith(".tga")) { if (filename.endsWith(".tga")) {
qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << url; qCDebug(modelformat) << "OBJ Reader WARNING: currently ignoring tga texture " << filename << " in " << _url;
break; break;
} }
if (isValidTexture(filename)) { if (isValidTexture(filename)) {
@ -252,7 +255,7 @@ void OBJReader::parseMaterialLibrary(QIODevice* device) {
currentMaterial.specularTextureFilename = filename; currentMaterial.specularTextureFilename = filename;
} }
} else { } else {
qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " ignoring missing texture " << filename; qCDebug(modelformat) << "OBJ Reader WARNING: " << _url << " ignoring missing texture " << filename;
} }
} }
} }
@ -316,7 +319,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
QByteArray groupName = tokenizer.getDatum(); QByteArray groupName = tokenizer.getDatum();
currentGroup = groupName; currentGroup = groupName;
//qCDebug(modelformat) << "new group:" << groupName; //qCDebug(modelformat) << "new group:" << groupName;
} else if (token == "mtllib") { } else if (token == "mtllib" && _url) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break; break;
} }
@ -325,13 +328,15 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi
break; // Some files use mtllib over and over again for the same libraryName break; // Some files use mtllib over and over again for the same libraryName
} }
librariesSeen[libraryName] = true; librariesSeen[libraryName] = true;
QUrl libraryUrl = url->resolved(QUrl(libraryName).fileName()); // 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());
qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl; qCDebug(modelformat) << "OBJ Reader new library:" << libraryName << " at:" << libraryUrl;
QNetworkReply* netReply = request(libraryUrl, false); QNetworkReply* netReply = request(libraryUrl, false);
if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) { if (netReply->isFinished() && (netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200)) {
parseMaterialLibrary(netReply); parseMaterialLibrary(netReply);
} else { } else {
qCDebug(modelformat) << "OBJ Reader " << libraryName << " did not answer. Got " << netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); qCDebug(modelformat) << "OBJ Reader " << libraryName << " did not answer. Got "
<< netReply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
} }
netReply->deleteLater(); netReply->deleteLater();
} else if (token == "usemtl") { } else if (token == "usemtl") {
@ -406,10 +411,10 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q
OBJTokenizer tokenizer(device); OBJTokenizer tokenizer(device);
float scaleGuess = 1.0f; float scaleGuess = 1.0f;
this->url = url; _url = url;
geometry.meshExtents.reset(); geometry.meshExtents.reset();
geometry.meshes.append(FBXMesh()); geometry.meshes.append(FBXMesh());
try { try {
// call parseOBJGroup as long as it's returning true. Each successful call will // call parseOBJGroup as long as it's returning true. Each successful call will
// add a new meshPart to the geometry's single mesh. // add a new meshPart to the geometry's single mesh.
@ -417,7 +422,7 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q
FBXMesh& mesh = geometry.meshes[0]; FBXMesh& mesh = geometry.meshes[0];
mesh.meshIndex = 0; mesh.meshIndex = 0;
geometry.joints.resize(1); geometry.joints.resize(1);
geometry.joints[0].isFree = false; geometry.joints[0].isFree = false;
geometry.joints[0].parentIndex = -1; geometry.joints[0].parentIndex = -1;
@ -440,37 +445,44 @@ FBXGeometry OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping, Q
0, 0, 1, 0, 0, 0, 1, 0,
0, 0, 0, 1); 0, 0, 0, 1);
mesh.clusters.append(cluster); mesh.clusters.append(cluster);
// Some .obj files use the convention that a group with uv coordinates that doesn't define a material, should use a texture with the same basename as the .obj file. // Some .obj files use the convention that a group with uv coordinates that doesn't define a material, should use
QString filename = url->fileName(); // a texture with the same basename as the .obj file.
int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail if (url) {
QString basename = filename.remove(extIndex + 1, sizeof("obj")); QString filename = url->fileName();
OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME]; int extIndex = filename.lastIndexOf('.'); // by construction, this does not fail
preDefinedMaterial.diffuseColor = glm::vec3(1.0f); QString basename = filename.remove(extIndex + 1, sizeof("obj"));
QVector<QByteArray> extensions = {"jpg", "jpeg", "png", "tga"}; OBJMaterial& preDefinedMaterial = materials[SMART_DEFAULT_MATERIAL_NAME];
QByteArray base = basename.toUtf8(), textName = ""; preDefinedMaterial.diffuseColor = glm::vec3(1.0f);
for (int i = 0; i < extensions.count(); i++) { QVector<QByteArray> extensions = {"jpg", "jpeg", "png", "tga"};
QByteArray candidateString = base + extensions[i]; QByteArray base = basename.toUtf8(), textName = "";
if (isValidTexture(candidateString)) { for (int i = 0; i < extensions.count(); i++) {
textName = candidateString; QByteArray candidateString = base + extensions[i];
break; if (isValidTexture(candidateString)) {
textName = candidateString;
break;
}
} }
if (!textName.isEmpty()) {
preDefinedMaterial.diffuseTextureFilename = textName;
}
materials[SMART_DEFAULT_MATERIAL_NAME] = preDefinedMaterial;
} }
if (!textName.isEmpty()) {
preDefinedMaterial.diffuseTextureFilename = textName;
}
materials[SMART_DEFAULT_MATERIAL_NAME] = preDefinedMaterial;
for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) { for (int i = 0, meshPartCount = 0; i < mesh.parts.count(); i++, meshPartCount++) {
FBXMeshPart& meshPart = mesh.parts[i]; FBXMeshPart& meshPart = mesh.parts[i];
FaceGroup faceGroup = faceGroups[meshPartCount]; FaceGroup faceGroup = faceGroups[meshPartCount];
OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material. OBJFace leadFace = faceGroup[0]; // All the faces in the same group will have the same name and material.
QString groupMaterialName = leadFace.materialName; QString groupMaterialName = leadFace.materialName;
if (groupMaterialName.isEmpty() && (leadFace.textureUVIndices.count() > 0)) { if (groupMaterialName.isEmpty() && (leadFace.textureUVIndices.count() > 0)) {
qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " needs a texture that isn't specified. Using default mechanism."; qCDebug(modelformat) << "OBJ Reader WARNING: " << url
<< " needs a texture that isn't specified. Using default mechanism.";
groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; groupMaterialName = SMART_DEFAULT_MATERIAL_NAME;
} else if (!groupMaterialName.isEmpty() && !materials.contains(groupMaterialName)) { } else if (!groupMaterialName.isEmpty() && !materials.contains(groupMaterialName)) {
qCDebug(modelformat) << "OBJ Reader WARNING: " << url << " specifies a material " << groupMaterialName << " that is not defined. Using default mechanism."; qCDebug(modelformat) << "OBJ Reader WARNING: " << url
<< " specifies a material " << groupMaterialName
<< " that is not defined. Using default mechanism.";
groupMaterialName = SMART_DEFAULT_MATERIAL_NAME; groupMaterialName = SMART_DEFAULT_MATERIAL_NAME;
} }
if (!groupMaterialName.isEmpty()) { if (!groupMaterialName.isEmpty()) {

View file

@ -69,12 +69,13 @@ public:
QVector<FaceGroup> faceGroups; QVector<FaceGroup> faceGroups;
QString currentMaterialName; QString currentMaterialName;
QHash<QString, OBJMaterial> materials; QHash<QString, OBJMaterial> materials;
QUrl* url;
QNetworkReply* request(QUrl& url, bool isTest); QNetworkReply* request(QUrl& url, bool isTest);
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url); FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping, QUrl* url);
private: private:
QUrl* _url = nullptr;
QHash<QByteArray, bool> librariesSeen; QHash<QByteArray, bool> librariesSeen;
bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess); bool parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mapping, FBXGeometry& geometry, float& scaleGuess);
void parseMaterialLibrary(QIODevice* device); void parseMaterialLibrary(QIODevice* device);
@ -83,4 +84,4 @@ private:
// What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility. // What are these utilities doing here? One is used by fbx loading code in VHACD Utils, and the other a general debugging utility.
void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID); void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID);
void fbxDebugDump(const FBXGeometry& fbxgeo); void fbxDebugDump(const FBXGeometry& fbxgeo);