// // OBJWriter.cpp // libraries/fbx/src/ // // Created by Seth Alves on 2017-1-27. // Copyright 2017 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include #include #include "model/Geometry.h" #include "OBJWriter.h" #include "ModelFormatLogging.h" static QString formatFloat(double n) { // limit precision to 6, but don't output trailing zeros. QString s = QString::number(n, 'f', 6); while (s.endsWith("0")) { s.remove(s.size() - 1, 1); } if (s.endsWith(".")) { s.remove(s.size() - 1, 1); } // check for non-numbers. if we get NaN or inf or scientific notation, just return 0 for (int i = 0; i < s.length(); i++) { auto c = s.at(i).toLatin1(); if (c != '-' && c != '.' && (c < '0' || c > '9')) { qCDebug(modelformat) << "OBJWriter zeroing bad vertex coordinate:" << s << "because of" << c; return QString("0"); } } return s; } bool writeOBJToTextStream(QTextStream& out, QList meshes) { // each mesh's vertices are numbered from zero. We're combining all their vertices into one list here, // so keep track of the start index for each mesh. QList meshVertexStartOffset; int currentVertexStartOffset = 0; // write out all vertices foreach (const MeshPointer& mesh, meshes) { meshVertexStartOffset.append(currentVertexStartOffset); const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); int vertexCount = 0; gpu::BufferView::Iterator vertexItr = vertexBuffer.cbegin(); while (vertexItr != vertexBuffer.cend()) { glm::vec3 v = *vertexItr; out << "v "; out << formatFloat(v[0]) << " "; out << formatFloat(v[1]) << " "; out << formatFloat(v[2]) << "\n"; vertexItr++; vertexCount++; } currentVertexStartOffset += vertexCount; } out << "\n"; // write out faces int nth = 0; foreach (const MeshPointer& mesh, meshes) { currentVertexStartOffset = meshVertexStartOffset.takeFirst(); const gpu::BufferView& partBuffer = mesh->getPartBuffer(); const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); model::Index partCount = (model::Index)mesh->getNumParts(); for (int partIndex = 0; partIndex < partCount; partIndex++) { const model::Mesh::Part& part = partBuffer.get(partIndex); out << "g part-" << nth++ << "\n"; // model::Mesh::TRIANGLES // TODO -- handle other formats gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); indexItr += part._startIndex; int indexCount = 0; while (indexItr != indexBuffer.cend() && indexCount < part._numIndices) { uint32_t index0 = *indexItr; indexItr++; indexCount++; if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; break; } uint32_t index1 = *indexItr; indexItr++; indexCount++; if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; break; } uint32_t index2 = *indexItr; indexItr++; indexCount++; out << "f "; out << currentVertexStartOffset + index0 + 1 << " "; out << currentVertexStartOffset + index1 + 1 << " "; out << currentVertexStartOffset + index2 + 1 << "\n"; } out << "\n"; } } return true; } bool writeOBJToFile(QString path, QList meshes) { if (QFileInfo(path).exists() && !QFile::remove(path)) { qCDebug(modelformat) << "OBJ writer failed, file exists:" << path; return false; } QFile file(path); if (!file.open(QIODevice::WriteOnly)) { qCDebug(modelformat) << "OBJ writer failed to open output file:" << path; return false; } QTextStream outStream(&file); bool success; success = writeOBJToTextStream(outStream, meshes); file.close(); return success; } QString writeOBJToString(QList meshes) { QString result; QTextStream outStream(&result, QIODevice::ReadWrite); bool success; success = writeOBJToTextStream(outStream, meshes); if (success) { return result; } return QString(""); }