From 858d15d0ba8191efd304589b09ff8860f47c6fc3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 6 Mar 2015 13:15:53 -0800 Subject: [PATCH] obj reader sort-of works --- libraries/fbx/src/OBJReader.cpp | 190 +++++++++++++++++++ libraries/fbx/src/OBJReader.h | 6 + libraries/render-utils/src/GeometryCache.cpp | 2 + libraries/render-utils/src/GeometryCache.h | 1 + tools/vhacd/src/VHACDUtil.cpp | 32 +++- tools/vhacd/src/VHACDUtil.h | 1 + tools/vhacd/src/VHACDUtilApp.cpp | 42 ++-- 7 files changed, 233 insertions(+), 41 deletions(-) create mode 100644 libraries/fbx/src/OBJReader.cpp create mode 100644 libraries/fbx/src/OBJReader.h diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp new file mode 100644 index 0000000000..1ff4f7a2ae --- /dev/null +++ b/libraries/fbx/src/OBJReader.cpp @@ -0,0 +1,190 @@ + + +#include +#include + + +#include "FBXReader.h" +#include "OBJReader.h" + + + +class OBJTokenizer { +public: + OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { } + enum SpecialToken { DATUM_TOKEN = 0x100 }; + int nextToken(); + const QByteArray& getDatum() const { return _datum; } + void skipLine() { _device->readLine(); } + void pushBackToken(int token) { _pushedBackToken = token; } + void ungetChar(char ch) { _device->ungetChar(ch); } + +private: + QIODevice* _device; + QByteArray _datum; + int _pushedBackToken; +}; + +int OBJTokenizer::nextToken() { + if (_pushedBackToken != -1) { + int token = _pushedBackToken; + _pushedBackToken = -1; + return token; + } + + char ch; + while (_device->getChar(&ch)) { + if (QChar(ch).isSpace()) { + continue; // skip whitespace + } + switch (ch) { + case '#': + _device->readLine(); // skip the comment + break; + + case '\"': + _datum = ""; + while (_device->getChar(&ch)) { + if (ch == '\"') { // end on closing quote + break; + } + if (ch == '\\') { // handle escaped quotes + if (_device->getChar(&ch) && ch != '\"') { + _datum.append('\\'); + } + } + _datum.append(ch); + } + return DATUM_TOKEN; + + default: + _datum = ""; + _datum.append(ch); + while (_device->getChar(&ch)) { + if (QChar(ch).isSpace() || ch == '\"') { + ungetChar(ch); // read until we encounter a special character, then replace it + break; + } + _datum.append(ch); + } + + return DATUM_TOKEN; + } + } + return -1; +} + + + +bool parseOBJMesh(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, int& indexStart) { + FBXMesh mesh; + FBXMeshPart meshPart; + bool sawG = false; + bool meshHasData = true; + bool result = true; + + try { // XXX move this out/up + while (true) { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { + result = false; + break; + } + QByteArray token = tokenizer.getDatum(); + if (token == "g") { + if (sawG) { + // we've encountered the beginning of the next group. + tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); + break; + } + sawG = true; + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + QByteArray groupName = tokenizer.getDatum(); + meshPart.materialID = groupName; + meshHasData = true; + } else if (token == "v") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + float x = std::stof(tokenizer.getDatum().data()); + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + float y = std::stof(tokenizer.getDatum().data()); + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + float z = std::stof(tokenizer.getDatum().data()); + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + + // float w = 1.0; + try{ + // w = + std::stof(tokenizer.getDatum().data()); + } + catch(const std::exception& e){ + // next token wasn't a number (the w field is optional), push it back + tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); + } + + mesh.vertices.append(glm::vec3(x, y, z)); + meshHasData = true; + } else if (token == "f") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + int p0 = std::stoi(tokenizer.getDatum().data()); + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + int p1 = std::stoi(tokenizer.getDatum().data()); + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + int p2 = std::stoi(tokenizer.getDatum().data()); + + // obj indexes is 1 based and index over the entire file. mesh indexes are 0 based + // and index within the mesh. + meshPart.triangleIndices.append(p0 - 1 - indexStart); // obj index is 1 based + meshPart.triangleIndices.append(p1 - 1 - indexStart); + meshPart.triangleIndices.append(p2 - 1 - indexStart); + meshHasData = true; + } else { + // something we don't (yet) care about + qDebug() << "skipping line with" << token; + tokenizer.skipLine(); + } + } + } + catch(const std::exception& e) { + // something went wrong, stop here. + qDebug() << "something went wrong"; + return false; + } + + if (meshHasData) { + mesh.parts.append(meshPart); + geometry.meshes.append(mesh); + } + + indexStart += mesh.vertices.count(); + + return result; +} + + + +FBXGeometry extractOBJGeometry(const FBXNode& node, const QVariantHash& mapping) { + FBXGeometry geometry; + return geometry; +} + + + +FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) { + QBuffer buffer(const_cast(&model)); + buffer.open(QIODevice::ReadOnly); + return readOBJ(&buffer, mapping); +} + + +FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) { + FBXGeometry geometry; + OBJTokenizer tokenizer(device); + int indexStart = 0; + + while (true) { + if (!parseOBJMesh(tokenizer, mapping, geometry, indexStart)) { + break; + } + } + + return geometry; +} diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h new file mode 100644 index 0000000000..2ff55a9a61 --- /dev/null +++ b/libraries/fbx/src/OBJReader.h @@ -0,0 +1,6 @@ + + +#include "FBXReader.h" + +FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); +FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 9ecdfa43e1..1b773f2bf9 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2101,6 +2101,8 @@ void GeometryReader::run() { lightmapLevel = 3.5f; } fbxgeo = readFBX(_reply, _mapping, grabLightmaps, lightmapLevel); + } else if (_url.path().toLower().endsWith(".obj")) { + fbxgeo = readOBJ(_reply, _mapping); } QMetaObject::invokeMethod(geometry.data(), "setGeometry", Q_ARG(const FBXGeometry&, fbxgeo)); } else { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 92b6d44b6c..ba7c16bc10 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -22,6 +22,7 @@ #include #include +#include #include diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp index caa213572b..4c8a9eb62d 100644 --- a/tools/vhacd/src/VHACDUtil.cpp +++ b/tools/vhacd/src/VHACDUtil.cpp @@ -13,11 +13,6 @@ #include "VHACDUtil.h" - - - - - //Read all the meshes from provided FBX file bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *results) { @@ -29,14 +24,31 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re std::cout << "Reading FBX.....\n"; QByteArray fbxContents = fbx.readAll(); - FBXGeometry geometry = readFBX(fbxContents, QVariantHash()); + + + FBXGeometry geometry; + + if (filename.toLower().endsWith(".obj")) { + geometry = readOBJ(fbxContents, QVariantHash()); + } else if (filename.toLower().endsWith(".fbx")) { + geometry = readFBX(fbxContents, QVariantHash()); + } else { + qDebug() << "unknown file extension"; + return false; + } + + //results->meshCount = geometry.meshes.count(); + qDebug() << "read in" << geometry.meshes.count() << "meshes"; + int count = 0; - foreach(FBXMesh mesh, geometry.meshes){ + foreach(FBXMesh mesh, geometry.meshes) { //get vertices for each mesh QVector vertices = mesh.vertices; + qDebug() << "vertex count is" << vertices.count(); + //get the triangle indices for each mesh QVector triangles; foreach(FBXMeshPart part, mesh.parts){ @@ -45,9 +57,9 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re } //only read meshes with triangles - if (triangles.count() <= 0){ - continue; - } + if (triangles.count() <= 0){ + continue; + } results->perMeshVertices.append(vertices); results->perMeshTriangleIndices.append(triangles); count++; diff --git a/tools/vhacd/src/VHACDUtil.h b/tools/vhacd/src/VHACDUtil.h index ce1c157025..b0b9da9720 100644 --- a/tools/vhacd/src/VHACDUtil.h +++ b/tools/vhacd/src/VHACDUtil.h @@ -19,6 +19,7 @@ #include //c++11 feature #include #include +#include #include namespace vhacd { diff --git a/tools/vhacd/src/VHACDUtilApp.cpp b/tools/vhacd/src/VHACDUtilApp.cpp index d4b0f48766..6d4bf5adcb 100644 --- a/tools/vhacd/src/VHACDUtilApp.cpp +++ b/tools/vhacd/src/VHACDUtilApp.cpp @@ -32,7 +32,7 @@ QString formatFloat(double n) { } -bool writeOBJ(QString outFileName, QVector>& convexHullList, bool outputOneMesh) { +bool writeOBJ(QString outFileName, QVector>& meshList, bool outputOneMesh) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Unable to write to " << outFileName; @@ -41,52 +41,32 @@ bool writeOBJ(QString outFileName, QVector>& QTextStream out(&file); + // if (meshList.size() != 1) { + // qDebug() << "unexpected number of meshes --" << meshList.size(); + // exit(1); + // } + // QVector hulls = meshList[0]; - if (convexHullList.size() != 1) { - qDebug() << "unexpected number of meshes --" << convexHullList.size(); - exit(1); - } + unsigned int pointStartOffset = 0; - QVector hulls = convexHullList[0]; - - - if (outputOneMesh) { + foreach (QVector hulls, meshList) { + unsigned int nth = 0; foreach (VHACD::IVHACD::ConvexHull hull, hulls) { + out << "g hull-" << nth++ << "\n"; for (unsigned int i = 0; i < hull.m_nPoints; i++) { out << "v "; out << formatFloat(hull.m_points[i*3]) << " "; out << formatFloat(hull.m_points[i*3+1]) << " "; out << formatFloat(hull.m_points[i*3+2]) << " 1\n"; } - } - - unsigned int pointStartOffset = 0; - foreach (VHACD::IVHACD::ConvexHull hull, hulls) { for (unsigned int i = 0; i < hull.m_nTriangles; i++) { out << "f "; out << hull.m_triangles[i*3] + 1 + pointStartOffset << " "; out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " "; out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n"; } - pointStartOffset += hull.m_nPoints; - } - } else { - unsigned int nth = 0; - foreach (VHACD::IVHACD::ConvexHull hull, hulls) { - out << "o hull-" << nth++ << "\n"; - for (unsigned int i = 0; i < hull.m_nPoints; i++) { - out << "v "; - out << formatFloat(hull.m_points[i*3]) << " "; - out << formatFloat(hull.m_points[i*3+1]) << " "; - out << formatFloat(hull.m_points[i*3+2]) << " 1\n"; - } - for (unsigned int i = 0; i < hull.m_nTriangles; i++) { - out << "f "; - out << hull.m_triangles[i*3] + 1 << " "; - out << hull.m_triangles[i*3+1] + 1 << " "; - out << hull.m_triangles[i*3+2] + 1 << "\n"; - } out << "\n"; + pointStartOffset += hull.m_nPoints; } }