From bb3155ee6100fd9ba73c60667737fd8286e620f2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 31 Jan 2017 14:29:42 -0800 Subject: [PATCH] obj file is now produced in /tmp --- .../src/RenderablePolyVoxEntityItem.cpp | 8 + libraries/fbx/src/OBJWriter.cpp | 157 ++++++++++++------ libraries/fbx/src/OBJWriter.h | 22 ++- libraries/script-engine/src/MeshProxy.h | 39 +++++ .../src/ModelScriptingInterface.cpp | 20 ++- .../src/ModelScriptingInterface.h | 25 +-- 6 files changed, 194 insertions(+), 77 deletions(-) create mode 100644 libraries/script-engine/src/MeshProxy.h diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 0179814641..78a9e6a84a 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1167,6 +1167,14 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { vertexBufferPtr->getSize() , sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); + + std::vector parts; + parts.emplace_back(model::Mesh::Part(0, // startIndex + vecIndices.size(), // numIndices + 0, // baseVertex + model::Mesh::TRIANGLES)); // topology + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); entity->setMesh(mesh); }); } diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index ab84e7b0fd..d33d767e5d 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -1,5 +1,5 @@ // -// FBXReader.h +// OBJWriter.cpp // libraries/fbx/src/ // // Created by Seth Alves on 2017-1-27. @@ -9,63 +9,126 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +#include +#include "model/Geometry.h" #include "OBJWriter.h" -bool writeOBJ(QString outFileName, QVector) { -//bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) { - // QFile file(outFileName); - // if (!file.open(QIODevice::WriteOnly)) { - // qWarning() << "unable to write to" << outFileName; - // return false; - // } - // QTextStream out(&file); - // if (outputCentimeters) { - // out << "# This file uses centimeters as units\n\n"; - // } +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); + } + return s; +} - // unsigned int nth = 0; - // // vertex indexes in obj files span the entire file - // // vertex indexes in a mesh span just that mesh - // int vertexIndexOffset = 0; +bool writeOBJToTextStream(QTextStream& out, std::vector meshes) { + qDebug() << "writeOBJToTextStream mesh count is" << meshes.size(); - // foreach (const FBXMesh& mesh, geometry.meshes) { - // bool verticesHaveBeenOutput = false; - // foreach (const FBXMeshPart &meshPart, mesh.parts) { - // if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { - // nth++; - // continue; - // } + // 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; - // if (!verticesHaveBeenOutput) { - // for (int i = 0; i < mesh.vertices.size(); i++) { - // glm::vec4 v = mesh.modelTransform * glm::vec4(mesh.vertices[i], 1.0f); - // out << "v "; - // out << formatFloat(v[0]) << " "; - // out << formatFloat(v[1]) << " "; - // out << formatFloat(v[2]) << "\n"; - // } - // verticesHaveBeenOutput = true; - // } + // 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"; - // out << "g hull-" << nth++ << "\n"; - // int triangleCount = meshPart.triangleIndices.size() / 3; - // for (int i = 0; i < triangleCount; i++) { - // out << "f "; - // out << vertexIndexOffset + meshPart.triangleIndices[i*3] + 1 << " "; - // out << vertexIndexOffset + meshPart.triangleIndices[i*3+1] + 1 << " "; - // out << vertexIndexOffset + meshPart.triangleIndices[i*3+2] + 1 << "\n"; - // } - // out << "\n"; - // } + // write out faces + int nth = 0; + foreach (const MeshPointer& mesh, meshes) { + currentVertexStartOffset = meshVertexStartOffset.takeFirst(); - // if (verticesHaveBeenOutput) { - // vertexIndexOffset += mesh.vertices.size(); - // } - // } + const gpu::BufferView& partBuffer = mesh->getPartBuffer(); + const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); + // const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); + + int partCount = mesh->getNumParts(); + qDebug() << "writeOBJToTextStream part count is" << partCount; + for (int partIndex = 0; partIndex < partCount; partIndex++) { + const model::Mesh::Part& part = partBuffer.get(partIndex); + + out << "g part-" << nth++ << "\n"; + + // model::Mesh::TRIANGLES + // XXX handle other formats + gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); + indexItr += part._startIndex; + + int indexCount = 0; + while (indexItr != indexBuffer.cend() && indexItr != part._numIndices) { + uint32_t index0 = *indexItr; + indexItr++; + indexCount++; + if (indexItr == indexBuffer.cend() || indexItr == part._numIndices) { + qDebug() << "OBJWriter -- index buffer length isn't multiple of 3"; + break; + } + uint32_t index1 = *indexItr; + indexItr++; + indexCount++; + if (indexItr == indexBuffer.cend() || indexItr == part._numIndices) { + qDebug() << "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, MeshPointer mesh) { + if (QFileInfo(path).exists() && !QFile::remove(path)) { + qDebug() << "OBJ writer failed, file exists:" << path; // XXX qCDebug + return false; + } + + QFile file(path); + if (!file.open(QIODevice::WriteOnly)) { + qDebug() << "OBJ writer failed to open output file:" << path; // XXX qCDebug + return false; + } + + QTextStream outStream(&file); + + bool success; + std::vector meshes { mesh }; + success = writeOBJToTextStream(outStream, meshes); + + file.close(); + return success; +} diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h index c0400b3d8e..58dc93b84a 100644 --- a/libraries/fbx/src/OBJWriter.h +++ b/libraries/fbx/src/OBJWriter.h @@ -1,6 +1,26 @@ +// +// OBJWriter.h +// 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 +// + +#ifndef hifi_objwriter_h +#define hifi_objwriter_h + #include #include #include -bool writeOBJ(QString outFileName, QVector); +using MeshPointer = std::shared_ptr; + +bool writeOBJToTextStream(QTextStream& out, std::vector meshes); +bool writeOBJToFile(QString path, MeshPointer mesh); + + +#endif // hifi_objwriter_h diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/script-engine/src/MeshProxy.h new file mode 100644 index 0000000000..3226e0f467 --- /dev/null +++ b/libraries/script-engine/src/MeshProxy.h @@ -0,0 +1,39 @@ +// +// MeshProxy.h +// libraries/script-engine/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 +// + +#ifndef hifi_MeshProxy_h +#define hifi_MeshProxy_h + +#include + +using MeshPointer = std::shared_ptr; + +class MeshProxy : public QObject { + Q_OBJECT + +public: + MeshProxy(MeshPointer mesh) : _mesh(mesh) {} + ~MeshProxy() {} + + MeshPointer getMeshPointer() const { return _mesh; } + + Q_INVOKABLE int getNumVertices() const { return _mesh->getNumVertices(); } + Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); } + + +protected: + MeshPointer _mesh; +}; + + +Q_DECLARE_METATYPE(MeshProxy*); + +#endif // hifi_MeshProxy_h diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 30ef9a713e..1fb8e8f193 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -12,18 +12,12 @@ #include #include #include "ModelScriptingInterface.h" +#include "OBJWriter.h" + ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { } - -MeshProxy::MeshProxy(MeshPointer mesh) : _mesh(mesh) { -} - -MeshProxy::~MeshProxy() { -} - - QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); @@ -32,3 +26,13 @@ QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { out = qobject_cast(value.toQObject()); } + +QString ModelScriptingInterface::meshToOBJ(MeshProxy* const &in) { + bool success; + QString filename = "/tmp/okokok.obj"; + success = writeOBJToFile(filename, in->getMeshPointer()); + if (!success) { + return ""; + } + return filename; +} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 4cc493b257..4d8f7ad999 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -17,6 +17,7 @@ #include #include #include +#include "MeshProxy.h" using MeshPointer = std::shared_ptr; @@ -25,30 +26,12 @@ class ModelScriptingInterface : public QObject { public: ModelScriptingInterface(QObject* parent); + + Q_INVOKABLE QString meshToOBJ(MeshProxy* const &in); }; -class MeshProxy : public QObject { - Q_OBJECT - -public: - MeshProxy(MeshPointer mesh); - // MeshProxy(const MeshProxy& meshProxy) { _mesh = meshProxy.getMeshPointer(); } - ~MeshProxy(); - - MeshPointer getMeshPointer() const { return _mesh; } - - Q_INVOKABLE int getNumVertices() const { return _mesh->getNumVertices(); } - -protected: - MeshPointer _mesh; -}; - - -Q_DECLARE_METATYPE(MeshProxy*); - - QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); -// QScriptValue meshToScriptValue(QScriptEngine* engine, const MeshPointer& in); void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); + #endif // hifi_ModelScriptingInterface_h