return non-zero on error, add verbose option

This commit is contained in:
Andrew Meadows 2016-05-23 14:20:25 -07:00
parent 7d7c991447
commit 78357057b6
5 changed files with 107 additions and 42 deletions

View file

@ -28,13 +28,16 @@ void reSortFBXGeometryMeshes(FBXGeometry& geometry) {
// Read all the meshes from provided FBX file // Read all the meshes from provided FBX file
bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
if (_verbose) {
qDebug() << "reading FBX file =" << filename << "...";
}
// open the fbx file // open the fbx file
QFile fbx(filename); QFile fbx(filename);
if (!fbx.open(QIODevice::ReadOnly)) { if (!fbx.open(QIODevice::ReadOnly)) {
qWarning() << "unable to open FBX file =" << filename;
return false; return false;
} }
qDebug() << "reading FBX file =" << filename << "...";
try { try {
QByteArray fbxContents = fbx.readAll(); QByteArray fbxContents = fbx.readAll();
FBXGeometry* geom; FBXGeometry* geom;
@ -43,14 +46,14 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) {
} else if (filename.toLower().endsWith(".fbx")) { } else if (filename.toLower().endsWith(".fbx")) {
geom = readFBX(fbxContents, QVariantHash(), filename); geom = readFBX(fbxContents, QVariantHash(), filename);
} else { } else {
qDebug() << "unknown file extension"; qWarning() << "unknown file extension";
return false; return false;
} }
result = *geom; result = *geom;
reSortFBXGeometryMeshes(result); reSortFBXGeometryMeshes(result);
} catch (const QString& error) { } catch (const QString& error) {
qDebug() << "Error reading" << filename << ":" << error; qWarning() << "error reading" << filename << ":" << error;
return false; return false;
} }
@ -230,10 +233,12 @@ bool isClosedManifold(const std::vector<int>& triangles) {
return true; return true;
} }
void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) { void vhacd::VHACDUtil::getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const {
// Number of hulls for this input meshPart // Number of hulls for this input meshPart
unsigned int numHulls = convexifier->GetNConvexHulls(); unsigned int numHulls = convexifier->GetNConvexHulls();
qDebug() << " hulls =" << numHulls; if (_verbose) {
qDebug() << " hulls =" << numHulls;
}
// create an output meshPart for each convex hull // create an output meshPart for each convex hull
for (unsigned int j = 0; j < numHulls; j++) { for (unsigned int j = 0; j < numHulls; j++) {
@ -259,9 +264,11 @@ void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) {
resultMeshPart.triangleIndices.append(index1); resultMeshPart.triangleIndices.append(index1);
resultMeshPart.triangleIndices.append(index2); resultMeshPart.triangleIndices.append(index2);
} }
qDebug() << " hull" << j << " vertices =" << hull.m_nPoints if (_verbose) {
<< " triangles =" << hull.m_nTriangles qDebug() << " hull" << j << " vertices =" << hull.m_nPoints
<< " FBXMeshVertices =" << resultMesh.vertices.size(); << " triangles =" << hull.m_nTriangles
<< " FBXMeshVertices =" << resultMesh.vertices.size();
}
} }
} }
@ -273,14 +280,18 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
VHACD::IVHACD::Parameters params, VHACD::IVHACD::Parameters params,
FBXGeometry& result, FBXGeometry& result,
float minimumMeshSize, float maximumMeshSize) { float minimumMeshSize, float maximumMeshSize) {
qDebug() << "meshes =" << geometry.meshes.size(); if (_verbose) {
qDebug() << "meshes =" << geometry.meshes.size();
}
// count the mesh-parts // count the mesh-parts
int numParts = 0; int numParts = 0;
foreach (const FBXMesh& mesh, geometry.meshes) { foreach (const FBXMesh& mesh, geometry.meshes) {
numParts += mesh.parts.size(); numParts += mesh.parts.size();
} }
qDebug() << "total parts =" << numParts; if (_verbose) {
qDebug() << "total parts =" << numParts;
}
VHACD::IVHACD * convexifier = VHACD::CreateVHACD(); VHACD::IVHACD * convexifier = VHACD::CreateVHACD();
@ -315,9 +326,11 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
} }
auto numVertices = vertices.size(); auto numVertices = vertices.size();
qDebug() << "mesh" << meshIndex << ": " if (_verbose) {
<< " parts =" << mesh.parts.size() << " clusters =" << mesh.clusters.size() qDebug() << "mesh" << meshIndex << ": "
<< " vertices =" << numVertices; << " parts =" << mesh.parts.size() << " clusters =" << mesh.clusters.size()
<< " vertices =" << numVertices;
}
++meshIndex; ++meshIndex;
std::vector<int> openParts; std::vector<int> openParts;
@ -329,7 +342,9 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
// only process meshes with triangles // only process meshes with triangles
if (triangles.size() <= 0) { if (triangles.size() <= 0) {
qDebug() << " skip part" << partIndex << "(zero triangles)"; if (_verbose) {
qDebug() << " skip part" << partIndex << "(zero triangles)";
}
++partIndex; ++partIndex;
continue; continue;
} }
@ -343,13 +358,17 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
const float largestDimension = aaBox.getLargestDimension(); const float largestDimension = aaBox.getLargestDimension();
if (largestDimension < minimumMeshSize) { if (largestDimension < minimumMeshSize) {
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too small)"; if (_verbose) {
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too small)";
}
++partIndex; ++partIndex;
continue; continue;
} }
if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) { if (maximumMeshSize > 0.0f && largestDimension > maximumMeshSize) {
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too large)"; if (_verbose) {
qDebug() << " skip part" << partIndex << ": dimension =" << largestDimension << "(too large)";
}
++partIndex; ++partIndex;
continue; continue;
} }
@ -358,18 +377,21 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
bool closed = isClosedManifold(triangles); bool closed = isClosedManifold(triangles);
if (closed) { if (closed) {
unsigned int triangleCount = triangles.size() / 3; unsigned int triangleCount = triangles.size() / 3;
qDebug() << " process closed part" << partIndex << ": " if (_verbose) {
<< " triangles =" << triangleCount; qDebug() << " process closed part" << partIndex << ": " << " triangles =" << triangleCount;
}
// compute approximate convex decomposition // compute approximate convex decomposition
bool success = convexifier->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params); bool success = convexifier->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params);
if (success) { if (success) {
getConvexResults(convexifier, resultMesh); getConvexResults(convexifier, resultMesh);
} else { } else if (_verbose) {
qDebug() << " failed to convexify"; qDebug() << " failed to convexify";
} }
} else { } else {
qDebug() << " postpone open part" << partIndex; if (_verbose) {
qDebug() << " postpone open part" << partIndex;
}
openParts.push_back(partIndex); openParts.push_back(partIndex);
} }
++partIndex; ++partIndex;
@ -391,14 +413,16 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry,
// this time we don't care if the parts are close or not // this time we don't care if the parts are close or not
unsigned int triangleCount = triangles.size() / 3; unsigned int triangleCount = triangles.size() / 3;
qDebug() << " process remaining open parts =" << openParts.size() << ": " if (_verbose) {
<< " triangles =" << triangleCount; qDebug() << " process remaining open parts =" << openParts.size() << ": "
<< " triangles =" << triangleCount;
}
// compute approximate convex decomposition // compute approximate convex decomposition
bool success = convexifier->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params); bool success = convexifier->Compute(&vertices[0].x, 3, (uint)numVertices, &triangles[0], 3, triangleCount, params);
if (success) { if (success) {
getConvexResults(convexifier, resultMesh); getConvexResults(convexifier, resultMesh);
} else { } else if (_verbose) {
qDebug() << " failed to convexify"; qDebug() << " failed to convexify";
} }
} }

View file

@ -25,6 +25,8 @@
namespace vhacd { namespace vhacd {
class VHACDUtil { class VHACDUtil {
public: public:
void setVerbose(bool verbose) { _verbose = verbose; }
bool loadFBX(const QString filename, FBXGeometry& result); bool loadFBX(const QString filename, FBXGeometry& result);
void fattenMeshes(const FBXMesh& mesh, FBXMesh& result, void fattenMeshes(const FBXMesh& mesh, FBXMesh& result,
@ -35,7 +37,13 @@ namespace vhacd {
VHACD::IVHACD::Parameters params, VHACD::IVHACD::Parameters params,
FBXGeometry& result, FBXGeometry& result,
float minimumMeshSize, float maximumMeshSize); float minimumMeshSize, float maximumMeshSize);
void getConvexResults(VHACD::IVHACD* convexifier, FBXMesh& resultMesh) const;
~VHACDUtil(); ~VHACDUtil();
private:
bool _verbose { false };
}; };
class ProgressCallback : public VHACD::IVHACD::IUserCallback { class ProgressCallback : public VHACD::IVHACD::IUserCallback {
@ -44,7 +52,7 @@ namespace vhacd {
~ProgressCallback(); ~ProgressCallback();
// Couldn't follow coding guideline here due to virtual function declared in IUserCallback // Couldn't follow coding guideline here due to virtual function declared in IUserCallback
void Update(const double overallProgress, const double stageProgress, const double operationProgress, void Update(const double overallProgress, const double stageProgress, const double operationProgress,
const char * const stage, const char * const operation); const char * const stage, const char * const operation);
}; };
} }

View file

@ -18,6 +18,9 @@
using namespace std; using namespace std;
using namespace VHACD; using namespace VHACD;
const int VHACD_RETURN_CODE_FAILURE_TO_READ = 1;
const int VHACD_RETURN_CODE_FAILURE_TO_WRITE = 2;
const int VHACD_RETURN_CODE_FAILURE_TO_CONVEXIFY = 3;
QString formatFloat(double n) { QString formatFloat(double n) {
@ -33,14 +36,15 @@ QString formatFloat(double n) {
} }
bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1) { bool VHACDUtilApp::writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart) {
QFile file(outFileName); QFile file(outFileName);
if (!file.open(QIODevice::WriteOnly)) { if (!file.open(QIODevice::WriteOnly)) {
qDebug() << "Unable to write to " << outFileName; qWarning() << "unable to write to" << outFileName;
_returnCode = VHACD_RETURN_CODE_FAILURE_TO_WRITE;
return false; return false;
} }
QTextStream out(&file);
QTextStream out(&file);
if (outputCentimeters) { if (outputCentimeters) {
out << "# This file uses centimeters as units\n\n"; out << "# This file uses centimeters as units\n\n";
} }
@ -105,6 +109,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption helpOption = parser.addHelpOption();
const QCommandLineOption verboseOutput("v", "verbose output");
parser.addOption(verboseOutput);
const QCommandLineOption splitOption("split", "split input-file into one mesh per output-file"); const QCommandLineOption splitOption("split", "split input-file into one mesh per output-file");
parser.addOption(splitOption); parser.addOption(splitOption);
@ -195,8 +202,10 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
bool outputCentimeters = parser.isSet(outputCentimetersOption); bool verbose = parser.isSet(verboseOutput);
vUtil.setVerbose(verbose);
bool outputCentimeters = parser.isSet(outputCentimetersOption);
bool fattenFaces = parser.isSet(fattenFacesOption); bool fattenFaces = parser.isSet(fattenFacesOption);
bool generateHulls = parser.isSet(generateHullsOption); bool generateHulls = parser.isSet(generateHullsOption);
bool splitModel = parser.isSet(splitOption); bool splitModel = parser.isSet(splitOption);
@ -305,13 +314,15 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
FBXGeometry fbx; FBXGeometry fbx;
auto begin = std::chrono::high_resolution_clock::now(); auto begin = std::chrono::high_resolution_clock::now();
if (!vUtil.loadFBX(inputFilename, fbx)){ if (!vUtil.loadFBX(inputFilename, fbx)){
qDebug() << "error reading input file" << inputFilename; _returnCode = VHACD_RETURN_CODE_FAILURE_TO_READ;
return; return;
} }
auto end = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now();
auto loadDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count(); auto loadDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
qDebug() << "load time =" << (double)loadDuration / 1000000000.00 << "seconds"; if (verbose) {
qDebug() << "load time =" << (double)loadDuration / 1000000000.00 << "seconds";
}
if (splitModel) { if (splitModel) {
QVector<QString> infileExtensions = {"fbx", "obj"}; QVector<QString> infileExtensions = {"fbx", "obj"};
@ -332,7 +343,11 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
vhacd::ProgressCallback progressCallback; vhacd::ProgressCallback progressCallback;
//set parameters for V-HACD //set parameters for V-HACD
params.m_callback = &progressCallback; //progress callback if (verbose) {
params.m_callback = &progressCallback; //progress callback
} else {
params.m_callback = nullptr;
}
params.m_resolution = vHacdResolution; params.m_resolution = vHacdResolution;
params.m_depth = vHacdDepth; params.m_depth = vHacdDepth;
params.m_concavity = vHacdConcavity; params.m_concavity = vHacdConcavity;
@ -346,12 +361,14 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based
params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH; params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH;
params.m_minVolumePerCH = 0.0001; // 0.0001 params.m_minVolumePerCH = 0.0001; // 0.0001
params.m_logger = 0; // 0 params.m_logger = nullptr;
params.m_convexhullApproximation = true; // true params.m_convexhullApproximation = true; // true
params.m_oclAcceleration = true; // true params.m_oclAcceleration = true; // true
//perform vhacd computation //perform vhacd computation
qDebug() << "running V-HACD algorithm ..."; if (verbose) {
qDebug() << "running V-HACD algorithm ...";
}
begin = std::chrono::high_resolution_clock::now(); begin = std::chrono::high_resolution_clock::now();
FBXGeometry result; FBXGeometry result;
@ -359,10 +376,15 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
end = std::chrono::high_resolution_clock::now(); end = std::chrono::high_resolution_clock::now();
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count(); auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
qDebug() << "run time =" << (double)computeDuration / 1000000000.00 << " seconds"; if (verbose) {
qDebug() << "run time =" << (double)computeDuration / 1000000000.00 << " seconds";
}
if (!success) { if (!success) {
qDebug() << "failed to convexify model"; if (verbose) {
qDebug() << "failed to convexify model";
}
_returnCode = VHACD_RETURN_CODE_FAILURE_TO_CONVEXIFY;
return; return;
} }
@ -377,11 +399,13 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
} }
} }
int totalHulls = result.meshes[0].parts.size(); if (verbose) {
qDebug() << "output file =" << outputFilename; int totalHulls = result.meshes[0].parts.size();
qDebug() << "vertices =" << totalVertices; qDebug() << "output file =" << outputFilename;
qDebug() << "triangles =" << totalTriangles; qDebug() << "vertices =" << totalVertices;
qDebug() << "hulls =" << totalHulls; qDebug() << "triangles =" << totalTriangles;
qDebug() << "hulls =" << totalHulls;
}
writeOBJ(outputFilename, result, outputCentimeters); writeOBJ(outputFilename, result, outputCentimeters);
} }

View file

@ -15,12 +15,21 @@
#include <QApplication> #include <QApplication>
#include <FBXReader.h>
class VHACDUtilApp : public QCoreApplication { class VHACDUtilApp : public QCoreApplication {
Q_OBJECT Q_OBJECT
public: public:
VHACDUtilApp(int argc, char* argv[]); VHACDUtilApp(int argc, char* argv[]);
~VHACDUtilApp(); ~VHACDUtilApp();
bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1);
int getReturnCode() const { return _returnCode; }
private:
int _returnCode { 0 };
}; };

View file

@ -23,5 +23,5 @@ using namespace VHACD;
int main(int argc, char * argv[]) { int main(int argc, char * argv[]) {
VHACDUtilApp app(argc, argv); VHACDUtilApp app(argc, argv);
return 0; return app.getReturnCode();
} }