From d71b0802a7181a744721e54776125275a80d5767 Mon Sep 17 00:00:00 2001 From: trent Date: Thu, 20 Jul 2017 11:38:42 -0400 Subject: [PATCH 1/3] Added vertex color support to OBJReader.h|cpp. --- libraries/fbx/src/OBJReader.cpp | 60 ++++++++++++++++++++++++++++++--- libraries/fbx/src/OBJReader.h | 8 +++-- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 417901b9ab..5beec4c1f4 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -123,6 +123,33 @@ glm::vec3 OBJTokenizer::getVec3() { } return v; } +bool OBJTokenizer::getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor) { + // Used for vertices which may also have a vertex color (RGB [0,1]) to follow. + // NOTE: Returns true if there is a vertex color. + auto x = getFloat(); // N.B.: getFloat() has side-effect + auto y = getFloat(); // And order of arguments is different on Windows/Linux. + auto z = getFloat(); + vertex = glm::vec3(x, y, z); + + auto r = 1.0f, g = 1.0f, b = 1.0f; + bool hasVertexColor = false; + if (isNextTokenFloat()) { + // If there's another float it's one of two things: a W component or an R component. The standard OBJ spec doesn't output a W component, + // so we're making the assumption that if a float follows (unless it's only a single value) that it's a vertex color. + r = getFloat(); + if(isNextTokenFloat()) { + // Safe to assume the following values are the green/blue components. + g = getFloat(); + b = getFloat(); + + hasVertexColor = true; + } + + vertexColor = glm::vec3(r, g, b); + } + + return hasVertexColor; +} glm::vec2 OBJTokenizer::getVec2() { float uCoord = getFloat(); float vCoord = 1.0f - getFloat(); @@ -140,7 +167,8 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { } // OBJFace -bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices) { +// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just pairing it with the vertices vector for consistency. +bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors) { bool ok; int index = vertexIndex.toInt(&ok); if (!ok) { @@ -382,7 +410,14 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi #endif } } else if (token == "v") { - vertices.append(tokenizer.getVec3()); + glm::vec3 vertex, vertexColor; + + bool hasVertexColor = tokenizer.getVec3Vec3(vertex, vertexColor); + vertices.append(vertex); + + if(hasVertexColor) { + vertexColors.append(vertexColor); + } } else if (token == "vn") { normals.append(tokenizer.getVec3()); } else if (token == "vt") { @@ -410,7 +445,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi assert(parts.count() >= 1); assert(parts.count() <= 3); const QByteArray noData {}; - face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices); + face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices, vertexColors); face.groupName = currentGroup; face.materialName = currentMaterialName; } @@ -540,6 +575,15 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); + glm::vec3 vc0, vc1, vc2; + bool hasVertexColors = ( vertexColors.size( ) > 0 ); + if(hasVertexColors) { + // If there are any vertex colors, it's safe to assume all meshes had them exported. + vc0 = checked_at(vertexColors, face.vertexIndices[0]); + vc1 = checked_at(vertexColors, face.vertexIndices[1]); + vc2 = checked_at(vertexColors, face.vertexIndices[2]); + } + // Scale the vertices if the OBJ file scale is specified as non-one. if (scaleGuess != 1.0f) { v0 *= scaleGuess; @@ -555,6 +599,13 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, meshPart.triangleIndices.append(mesh.vertices.count()); mesh.vertices << v2; + if( hasVertexColors ) { + // Add vertex colors. + mesh.colors << vc0; + mesh.colors << vc1; + mesh.colors << vc2; + } + glm::vec3 n0, n1, n2; if (face.normalIndices.count()) { n0 = checked_at(normals, face.normalIndices[0]); @@ -689,7 +740,8 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " offset =" << fbxgeo.offset; qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count(); foreach (FBXMesh mesh, fbxgeo.meshes) { - qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); + qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); + qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); /*if (mesh.normals.count() == mesh.vertices.count()) { for (int i = 0; i < mesh.normals.count(); i++) { diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 18a4b89f1e..6765be9076 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -19,7 +19,8 @@ public: void pushBackToken(int token) { _pushedBackToken = token; } void ungetChar(char ch) { _device->ungetChar(ch); } const QString getComment() const { return _comment; } - glm::vec3 getVec3(); + glm::vec3 getVec3(); + bool getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor); glm::vec2 getVec2(); float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } @@ -38,7 +39,7 @@ public: QString groupName; // We don't make use of hierarchical structure, but it can be preserved for debugging and future use. QString materialName; // Add one more set of vertex data. Answers true if successful - bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices); + bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate(); @@ -65,7 +66,8 @@ class OBJReader: public QObject { // QObject so we can make network requests. Q_OBJECT public: typedef QVector FaceGroup; - QVector vertices; // all that we ever encounter while reading + QVector vertices; + QVector vertexColors; QVector textureUVs; QVector normals; QVector faceGroups; From ff3e9263efa1cee146d262b80f686c8d64ffc628 Mon Sep 17 00:00:00 2001 From: trent Date: Thu, 20 Jul 2017 11:40:42 -0400 Subject: [PATCH 2/3] Stupid tabs. --- libraries/fbx/src/OBJReader.cpp | 86 ++++++++++++++++----------------- libraries/fbx/src/OBJReader.h | 8 +-- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 5beec4c1f4..d711ef87a3 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -124,31 +124,31 @@ glm::vec3 OBJTokenizer::getVec3() { return v; } bool OBJTokenizer::getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor) { - // Used for vertices which may also have a vertex color (RGB [0,1]) to follow. - // NOTE: Returns true if there is a vertex color. - auto x = getFloat(); // N.B.: getFloat() has side-effect - auto y = getFloat(); // And order of arguments is different on Windows/Linux. - auto z = getFloat(); - vertex = glm::vec3(x, y, z); + // Used for vertices which may also have a vertex color (RGB [0,1]) to follow. + // NOTE: Returns true if there is a vertex color. + auto x = getFloat(); // N.B.: getFloat() has side-effect + auto y = getFloat(); // And order of arguments is different on Windows/Linux. + auto z = getFloat(); + vertex = glm::vec3(x, y, z); - auto r = 1.0f, g = 1.0f, b = 1.0f; - bool hasVertexColor = false; - if (isNextTokenFloat()) { - // If there's another float it's one of two things: a W component or an R component. The standard OBJ spec doesn't output a W component, - // so we're making the assumption that if a float follows (unless it's only a single value) that it's a vertex color. - r = getFloat(); - if(isNextTokenFloat()) { - // Safe to assume the following values are the green/blue components. - g = getFloat(); - b = getFloat(); + auto r = 1.0f, g = 1.0f, b = 1.0f; + bool hasVertexColor = false; + if (isNextTokenFloat()) { + // If there's another float it's one of two things: a W component or an R component. The standard OBJ spec doesn't output a W component, + // so we're making the assumption that if a float follows (unless it's only a single value) that it's a vertex color. + r = getFloat(); + if(isNextTokenFloat()) { + // Safe to assume the following values are the green/blue components. + g = getFloat(); + b = getFloat(); - hasVertexColor = true; - } + hasVertexColor = true; + } - vertexColor = glm::vec3(r, g, b); - } + vertexColor = glm::vec3(r, g, b); + } - return hasVertexColor; + return hasVertexColor; } glm::vec2 OBJTokenizer::getVec2() { float uCoord = getFloat(); @@ -410,14 +410,14 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi #endif } } else if (token == "v") { - glm::vec3 vertex, vertexColor; + glm::vec3 vertex, vertexColor; - bool hasVertexColor = tokenizer.getVec3Vec3(vertex, vertexColor); + bool hasVertexColor = tokenizer.getVec3Vec3(vertex, vertexColor); vertices.append(vertex); - - if(hasVertexColor) { - vertexColors.append(vertexColor); - } + + if(hasVertexColor) { + vertexColors.append(vertexColor); + } } else if (token == "vn") { normals.append(tokenizer.getVec3()); } else if (token == "vt") { @@ -575,14 +575,14 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, glm::vec3 v1 = checked_at(vertices, face.vertexIndices[1]); glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); - glm::vec3 vc0, vc1, vc2; - bool hasVertexColors = ( vertexColors.size( ) > 0 ); - if(hasVertexColors) { - // If there are any vertex colors, it's safe to assume all meshes had them exported. - vc0 = checked_at(vertexColors, face.vertexIndices[0]); - vc1 = checked_at(vertexColors, face.vertexIndices[1]); - vc2 = checked_at(vertexColors, face.vertexIndices[2]); - } + glm::vec3 vc0, vc1, vc2; + bool hasVertexColors = ( vertexColors.size( ) > 0 ); + if(hasVertexColors) { + // If there are any vertex colors, it's safe to assume all meshes had them exported. + vc0 = checked_at(vertexColors, face.vertexIndices[0]); + vc1 = checked_at(vertexColors, face.vertexIndices[1]); + vc2 = checked_at(vertexColors, face.vertexIndices[2]); + } // Scale the vertices if the OBJ file scale is specified as non-one. if (scaleGuess != 1.0f) { @@ -599,12 +599,12 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, meshPart.triangleIndices.append(mesh.vertices.count()); mesh.vertices << v2; - if( hasVertexColors ) { - // Add vertex colors. - mesh.colors << vc0; - mesh.colors << vc1; - mesh.colors << vc2; - } + if( hasVertexColors ) { + // Add vertex colors. + mesh.colors << vc0; + mesh.colors << vc1; + mesh.colors << vc2; + } glm::vec3 n0, n1, n2; if (face.normalIndices.count()) { @@ -740,8 +740,8 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) { qCDebug(modelformat) << " offset =" << fbxgeo.offset; qCDebug(modelformat) << " meshes.count() =" << fbxgeo.meshes.count(); foreach (FBXMesh mesh, fbxgeo.meshes) { - qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); - qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); + qCDebug(modelformat) << " vertices.count() =" << mesh.vertices.count(); + qCDebug(modelformat) << " colors.count() =" << mesh.colors.count(); qCDebug(modelformat) << " normals.count() =" << mesh.normals.count(); /*if (mesh.normals.count() == mesh.vertices.count()) { for (int i = 0; i < mesh.normals.count(); i++) { diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 6765be9076..2b81986361 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -19,8 +19,8 @@ public: void pushBackToken(int token) { _pushedBackToken = token; } void ungetChar(char ch) { _device->ungetChar(ch); } const QString getComment() const { return _comment; } - glm::vec3 getVec3(); - bool getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor); + glm::vec3 getVec3(); + bool getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor); glm::vec2 getVec2(); float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } @@ -66,8 +66,8 @@ class OBJReader: public QObject { // QObject so we can make network requests. Q_OBJECT public: typedef QVector FaceGroup; - QVector vertices; - QVector vertexColors; + QVector vertices; + QVector vertexColors; QVector textureUVs; QVector normals; QVector faceGroups; From 559e2e0ebb62071ef2885b984630e5ebaf2483b1 Mon Sep 17 00:00:00 2001 From: trent Date: Thu, 20 Jul 2017 13:09:17 -0400 Subject: [PATCH 3/3] Fixing formatting issues; renamed ::getVec3Vec3 to ::getVertex. --- libraries/fbx/src/OBJReader.cpp | 23 +++++++++++++---------- libraries/fbx/src/OBJReader.h | 5 +++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index d711ef87a3..a171f92907 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -123,7 +123,7 @@ glm::vec3 OBJTokenizer::getVec3() { } return v; } -bool OBJTokenizer::getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor) { +bool OBJTokenizer::getVertex(glm::vec3& vertex, glm::vec3& vertexColor) { // Used for vertices which may also have a vertex color (RGB [0,1]) to follow. // NOTE: Returns true if there is a vertex color. auto x = getFloat(); // N.B.: getFloat() has side-effect @@ -134,10 +134,11 @@ bool OBJTokenizer::getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor) { auto r = 1.0f, g = 1.0f, b = 1.0f; bool hasVertexColor = false; if (isNextTokenFloat()) { - // If there's another float it's one of two things: a W component or an R component. The standard OBJ spec doesn't output a W component, - // so we're making the assumption that if a float follows (unless it's only a single value) that it's a vertex color. + // If there's another float it's one of two things: a W component or an R component. The standard OBJ spec + // doesn't output a W component, so we're making the assumption that if a float follows (unless it's + // only a single value) that it's a vertex color. r = getFloat(); - if(isNextTokenFloat()) { + if (isNextTokenFloat()) { // Safe to assume the following values are the green/blue components. g = getFloat(); b = getFloat(); @@ -167,7 +168,8 @@ void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) { } // OBJFace -// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just pairing it with the vertices vector for consistency. +// NOTE (trent, 7/20/17): The vertexColors vector being passed-in isn't necessary here, but I'm just +// pairing it with the vertices vector for consistency. bool OBJFace::add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors) { bool ok; int index = vertexIndex.toInt(&ok); @@ -412,7 +414,7 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi } else if (token == "v") { glm::vec3 vertex, vertexColor; - bool hasVertexColor = tokenizer.getVec3Vec3(vertex, vertexColor); + bool hasVertexColor = tokenizer.getVertex(vertex, vertexColor); vertices.append(vertex); if(hasVertexColor) { @@ -445,7 +447,8 @@ bool OBJReader::parseOBJGroup(OBJTokenizer& tokenizer, const QVariantHash& mappi assert(parts.count() >= 1); assert(parts.count() <= 3); const QByteArray noData {}; - face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, vertices, vertexColors); + face.add(parts[0], (parts.count() > 1) ? parts[1] : noData, (parts.count() > 2) ? parts[2] : noData, + vertices, vertexColors); face.groupName = currentGroup; face.materialName = currentMaterialName; } @@ -576,8 +579,8 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, glm::vec3 v2 = checked_at(vertices, face.vertexIndices[2]); glm::vec3 vc0, vc1, vc2; - bool hasVertexColors = ( vertexColors.size( ) > 0 ); - if(hasVertexColors) { + bool hasVertexColors = (vertexColors.size() > 0); + if (hasVertexColors) { // If there are any vertex colors, it's safe to assume all meshes had them exported. vc0 = checked_at(vertexColors, face.vertexIndices[0]); vc1 = checked_at(vertexColors, face.vertexIndices[1]); @@ -599,7 +602,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, meshPart.triangleIndices.append(mesh.vertices.count()); mesh.vertices << v2; - if( hasVertexColors ) { + if (hasVertexColors) { // Add vertex colors. mesh.colors << vc0; mesh.colors << vc1; diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 2b81986361..9a32871590 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -20,7 +20,7 @@ public: void ungetChar(char ch) { _device->ungetChar(ch); } const QString getComment() const { return _comment; } glm::vec3 getVec3(); - bool getVec3Vec3(glm::vec3& vertex, glm::vec3& vertexColor); + bool getVertex(glm::vec3& vertex, glm::vec3& vertexColor); glm::vec2 getVec2(); float getFloat() { return std::stof((nextToken() != OBJTokenizer::DATUM_TOKEN) ? nullptr : getDatum().data()); } @@ -39,7 +39,8 @@ public: QString groupName; // We don't make use of hierarchical structure, but it can be preserved for debugging and future use. QString materialName; // Add one more set of vertex data. Answers true if successful - bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, const QVector& vertices, const QVector& vertexColors); + bool add(const QByteArray& vertexIndex, const QByteArray& textureIndex, const QByteArray& normalIndex, + const QVector& vertices, const QVector& vertexColors); // Return a set of one or more OBJFaces from this one, in which each is just a triangle. // Even though FBXMeshPart can handle quads, it would be messy to try to keep track of mixed-size faces, so we treat everything as triangles. QVector triangulate();