diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 55fcdca89e..c09cd3091b 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -2,6 +2,8 @@ // http://en.wikipedia.org/wiki/Wavefront_.obj_file +// http://www.scratchapixel.com/old/lessons/3d-advanced-lessons/obj-file-format/obj-file-format/ +// http://paulbourke.net/dataformats/obj/ #include @@ -28,6 +30,7 @@ private: int _pushedBackToken; }; + int OBJTokenizer::nextToken() { if (_pushedBackToken != -1) { int token = _pushedBackToken; @@ -78,194 +81,99 @@ int OBJTokenizer::nextToken() { } - -bool parseOBJMesh(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, int& indexStart) { - FBXMesh mesh; - FBXMeshPart meshPart; +bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry) { + FBXMesh &mesh = geometry.meshes[0]; + mesh.parts.append(FBXMeshPart()); + FBXMeshPart &meshPart = mesh.parts.last(); bool sawG = false; - bool meshHasData = true; bool result = true; - - // QString materialID; - // model::MaterialPointer _material; - - meshPart.materialID = "dontknow"; - // meshPart._material = { glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(), 96.0f, 1.0f }; - meshPart._material = model::MaterialPointer(new model::Material()); + meshPart.materialID = QString("dontknow") + QString::number(mesh.parts.count()); meshPart.opacity = 1.0; + meshPart._material = model::MaterialPointer(new model::Material()); + meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0)); + meshPart._material->setOpacity(1.0); + meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0)); + meshPart._material->setShininess(96.0); + meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); - // material._material->setEmissive(material.emissive); - // material._material->setDiffuse(material.diffuse); - // material._material->setSpecular(material.specular); - // material._material->setShininess(material.shininess); - // material._material->setOpacity(material.opacity); - - - try { // XXX move this out/up - while (true) { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { - result = false; + 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; } - QByteArray token = tokenizer.getDatum(); - if (token == "g") { - if (sawG) { - // we've encountered the beginning of the next group. + sawG = true; + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + QByteArray groupName = tokenizer.getDatum(); + meshPart.materialID = groupName; + } else if (token == "v") { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + float x = std::stof(tokenizer.getDatum().data()); + // notice the order of z and y -- in OBJ files, up is the 3rd value + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } + float z = 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; } + // the spec gets vague here. might be w, might be a color... chop it off. + tokenizer.skipLine(); + mesh.vertices.append(glm::vec3(x, y, z)); + mesh.colors.append(glm::vec3(1, 1, 1)); + } else if (token == "f") { + // a face can have 3 or more vertices + QVector indices; + while (true) { + if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { goto done; } + try { + int vertexIndex = std::stoi(tokenizer.getDatum().data()); + // negative indexes count backward from the current end of the vertex list + vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1); + // obj index is 1 based + assert(vertexIndex >= 1); + indices.append(vertexIndex - 1); + } + catch(const std::exception& e) { + // wasn't a number, but it back. 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()); - // notice the order of z and y -- in OBJ files, up is the 3rd value - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { break; } - float z = 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; } - - // the spec gets vague here. might be w, might be a color... chop it off. - tokenizer.skipLine(); - - mesh.vertices.append(glm::vec3(x, y, z)); - mesh.colors.append(glm::vec3(1, 1, 1)); - meshHasData = true; - } else if (token == "f") { - // a face can have 3 or more vertices - QVector indices; - while (true) { - if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { goto done; } - try { - int vertexIndex = std::stoi(tokenizer.getDatum().data()); - // negative indexes count backward from the current end of the vertex list - vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1); - // obj index is 1 based - vertexIndex = vertexIndex - 1 - indexStart; - assert(vertexIndex >= 0); - indices.append(vertexIndex); - } - catch(const std::exception& e) { - // wasn't a number, but it back. - tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN); - break; - } - } - - if (indices.count() == 3) { - meshPart.triangleIndices << indices; - } else if (indices.count() == 4) { - meshPart.quadIndices << indices; - } else { - qDebug() << "no support for more than 4 vertices on a face in OBJ files"; - } - meshHasData = true; - } else { - // something we don't (yet) care about - // vp - // mtllib - // usemtl - // s 1, s off - - qDebug() << "skipping line with" << token; - tokenizer.skipLine(); } + + if (indices.count() == 3) { + // flip these around (because of the y/z swap above) so our triangles face outward + meshPart.triangleIndices.append(indices[0]); + meshPart.triangleIndices.append(indices[2]); + meshPart.triangleIndices.append(indices[1]); + } else if (indices.count() == 4) { + meshPart.quadIndices << indices; + } else { + qDebug() << "no support for more than 4 vertices on a face in OBJ files"; + } + } else { + // something we don't (yet) care about + qDebug() << "OBJ parser is skipping a line with" << token; + tokenizer.skipLine(); } } - catch(const std::exception& e) { - // something went wrong, stop here. - qDebug() << "something went wrong in obj reader"; - return false; - } done: - -#if 0 - // add bogus normal data for this mesh - // mesh.normals.resize(mesh.vertices.count()); - mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count()); - mesh.tangents.fill(glm::vec3(0,0,0), mesh.vertices.count()); - int triCount = meshPart.triangleIndices.count() / 3; - for (int i = 0; i < triCount; i++) { - int p0Index = meshPart.triangleIndices[ i*3 ]; - int p1Index = meshPart.triangleIndices[ i*3+1 ]; - int p2Index = meshPart.triangleIndices[ i*3+2 ]; - - glm::vec3 p0 = mesh.vertices[ p0Index ]; - glm::vec3 p1 = mesh.vertices[ p1Index ]; - glm::vec3 p2 = mesh.vertices[ p2Index ]; - - glm::vec3 n = glm::cross(p2 - p0, p1 - p0); - glm::vec3 t = glm::cross(p2 - p0, n); - - mesh.normals[ p0Index ] = n; - mesh.normals[ p1Index ] = n; - mesh.normals[ p2Index ] = n; - - mesh.tangents[ p0Index ] = t; - mesh.tangents[ p1Index ] = t; - mesh.tangents[ p2Index ] = t; - } -#endif - - - if (! meshHasData) - return result; - - - mesh.meshExtents.reset(); - foreach (const glm::vec3& vertex, mesh.vertices) { - mesh.meshExtents.addPoint(vertex); - geometry.meshExtents.addPoint(vertex); - } - - mesh.parts.append(meshPart); - geometry.meshes.append(mesh); - - - geometry.joints.resize(1); - geometry.joints[ 0 ].isFree = false; - // geometry.joints[ 0 ].freeLineage; - geometry.joints[ 0 ].parentIndex = -1; - geometry.joints[ 0 ].distanceToParent = 0; - geometry.joints[ 0 ].boneRadius = 0; - geometry.joints[ 0 ].translation = glm::vec3(0, 0, 0); - // geometry.joints[ 0 ].preTransform = ; - geometry.joints[ 0 ].preRotation = glm::quat(0, 0, 0, 1); - geometry.joints[ 0 ].rotation = glm::quat(0, 0, 0, 1); - geometry.joints[ 0 ].postRotation = glm::quat(0, 0, 0, 1); - // geometry.joints[ 0 ].postTransform = ; - // geometry.joints[ 0 ].transform = ; - geometry.joints[ 0 ].rotationMin = glm::vec3(0, 0, 0); - geometry.joints[ 0 ].rotationMax = glm::vec3(0, 0, 0); - geometry.joints[ 0 ].inverseDefaultRotation = glm::quat(0, 0, 0, 1); - geometry.joints[ 0 ].inverseBindRotation = glm::quat(0, 0, 0, 1); - // geometry.joints[ 0 ].bindTransform = ; - geometry.joints[ 0 ].name = "OBJ"; - geometry.joints[ 0 ].shapePosition = glm::vec3(0, 0, 0); - geometry.joints[ 0 ].shapeRotation = glm::quat(0, 0, 0, 1); - geometry.joints[ 0 ].shapeType = SPHERE_SHAPE; - geometry.joints[ 0 ].isSkeletonJoint = false; - - 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); @@ -276,16 +184,76 @@ FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) { FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) { FBXGeometry geometry; OBJTokenizer tokenizer(device); - int indexStart = 0; geometry.meshExtents.reset(); + geometry.meshes.append(FBXMesh()); - while (true) { - if (!parseOBJMesh(tokenizer, mapping, geometry, indexStart)) { - break; + try { + while (parseOBJGroup(tokenizer, mapping, geometry)) { } - qDebug() << "indexStart is" << indexStart; + FBXMesh &mesh = geometry.meshes[0]; + + mesh.meshExtents.reset(); + foreach (const glm::vec3& vertex, mesh.vertices) { + mesh.meshExtents.addPoint(vertex); + geometry.meshExtents.addPoint(vertex); + } + + geometry.joints.resize(1); + geometry.joints[ 0 ].isFree = false; + // geometry.joints[ 0 ].freeLineage; + geometry.joints[ 0 ].parentIndex = -1; + geometry.joints[ 0 ].distanceToParent = 0; + geometry.joints[ 0 ].boneRadius = 0; + geometry.joints[ 0 ].translation = glm::vec3(0, 0, 0); + // geometry.joints[ 0 ].preTransform = ; + geometry.joints[ 0 ].preRotation = glm::quat(0, 0, 0, 1); + geometry.joints[ 0 ].rotation = glm::quat(0, 0, 0, 1); + geometry.joints[ 0 ].postRotation = glm::quat(0, 0, 0, 1); + // geometry.joints[ 0 ].postTransform = ; + // geometry.joints[ 0 ].transform = ; + geometry.joints[ 0 ].rotationMin = glm::vec3(0, 0, 0); + geometry.joints[ 0 ].rotationMax = glm::vec3(0, 0, 0); + geometry.joints[ 0 ].inverseDefaultRotation = glm::quat(0, 0, 0, 1); + geometry.joints[ 0 ].inverseBindRotation = glm::quat(0, 0, 0, 1); + // geometry.joints[ 0 ].bindTransform = ; + geometry.joints[ 0 ].name = "OBJ"; + geometry.joints[ 0 ].shapePosition = glm::vec3(0, 0, 0); + geometry.joints[ 0 ].shapeRotation = glm::quat(0, 0, 0, 1); + geometry.joints[ 0 ].shapeType = SPHERE_SHAPE; + geometry.joints[ 0 ].isSkeletonJoint = false; + + // add bogus normal data for this mesh + mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count()); + mesh.tangents.fill(glm::vec3(0,0,0), mesh.vertices.count()); + + foreach (FBXMeshPart meshPart, mesh.parts) { + int triCount = meshPart.triangleIndices.count() / 3; + for (int i = 0; i < triCount; i++) { + int p0Index = meshPart.triangleIndices[ i*3 ]; + int p1Index = meshPart.triangleIndices[ i*3+1 ]; + int p2Index = meshPart.triangleIndices[ i*3+2 ]; + + glm::vec3 p0 = mesh.vertices[ p0Index ]; + glm::vec3 p1 = mesh.vertices[ p1Index ]; + glm::vec3 p2 = mesh.vertices[ p2Index ]; + + glm::vec3 n = glm::cross(p1 - p0, p2 - p0); + glm::vec3 t = glm::cross(p2 - p0, n); + + mesh.normals[ p0Index ] = n; + mesh.normals[ p1Index ] = n; + mesh.normals[ p2Index ] = n; + + mesh.tangents[ p0Index ] = t; + mesh.tangents[ p1Index ] = t; + mesh.tangents[ p2Index ] = t; + } + } + } + catch(const std::exception& e) { + qDebug() << "something went wrong in OBJ reader"; } return geometry;