From 9ef9452cf6b5034d0be3c978b6fc3fbb399bb6d8 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Apr 2015 16:39:45 -0700 Subject: [PATCH 1/4] make filename extension checking less wrong --- libraries/shared/src/PathUtils.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index 545183e8f5..bf846c0bf2 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -29,10 +29,9 @@ QString& PathUtils::resourcesPath() { QString fileNameWithoutExtension(const QString& fileName, const QVector possibleExtensions) { + QString fileNameLowered = fileName.toLower(); foreach (const QString possibleExtension, possibleExtensions) { - if (fileName.endsWith(possibleExtension) || - fileName.endsWith(possibleExtension.toUpper()) || - fileName.endsWith(possibleExtension.toLower())) { + if (fileNameLowered.endsWith(possibleExtension.toLower())) { return fileName.left(fileName.count() - possibleExtension.count() - 1); } } From 2dc4410f7ebf65e192dcae1f1d47483153413a84 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Apr 2015 19:29:45 -0700 Subject: [PATCH 2/4] added code to split an fbx into one obj file per mesh-part --- tools/vhacd/src/VHACDUtilApp.cpp | 190 ++++++++++++++++++++----------- 1 file changed, 123 insertions(+), 67 deletions(-) diff --git a/tools/vhacd/src/VHACDUtilApp.cpp b/tools/vhacd/src/VHACDUtilApp.cpp index 199f9a9cec..ba3fbb94bd 100644 --- a/tools/vhacd/src/VHACDUtilApp.cpp +++ b/tools/vhacd/src/VHACDUtilApp.cpp @@ -13,6 +13,7 @@ #include #include "VHACDUtilApp.h" #include "VHACDUtil.h" +#include "PathUtils.h" using namespace std; using namespace VHACD; @@ -32,35 +33,54 @@ QString formatFloat(double n) { } -bool writeOBJ(QString outFileName, FBXGeometry& geometry) { +bool writeOBJ(QString outFileName, FBXGeometry& geometry, int whichMeshPart = -1) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Unable to write to " << outFileName; return false; } - QTextStream out(&file); unsigned int nth = 0; - foreach (const FBXMesh& mesh, geometry.meshes) { - for (int i = 0; i < mesh.vertices.size(); i++) { - out << "v "; - out << formatFloat(mesh.vertices[i][0]) << " "; - out << formatFloat(mesh.vertices[i][1]) << " "; - out << formatFloat(mesh.vertices[i][2]) << "\n"; - } + // vertex indexes in obj files span the entire file + // vertex indexes in a mesh span just that mesh + + int vertexIndexOffset = 0; + + foreach (const FBXMesh& mesh, geometry.meshes) { + bool verticesHaveBeenOutput = false; foreach (const FBXMeshPart &meshPart, mesh.parts) { + if (whichMeshPart >= 0 && nth != (unsigned int) whichMeshPart) { + nth++; + continue; + } + + 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; + } + out << "g hull-" << nth++ << "\n"; int triangleCount = meshPart.triangleIndices.size() / 3; for (int i = 0; i < triangleCount; i++) { out << "f "; - out << meshPart.triangleIndices[i*3] + 1 << " "; - out << meshPart.triangleIndices[i*3+1] + 1 << " "; - out << meshPart.triangleIndices[i*3+2] + 1 << "\n"; + 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"; } + + if (verticesHaveBeenOutput) { + vertexIndexOffset += mesh.vertices.size(); + } } return true; @@ -72,12 +92,7 @@ bool writeOBJ(QString outFileName, FBXGeometry& geometry) { VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : QCoreApplication(argc, argv) { - vector triangles; // array of indexes - vector points; // array of coordinates vhacd::VHACDUtil vUtil; - VHACD::IVHACD::Parameters params; - vhacd::ProgressCallback pCallBack; - // parse command-line QCommandLineParser parser; @@ -86,9 +101,15 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : const QCommandLineOption helpOption = parser.addHelpOption(); + const QCommandLineOption splitOption("split", "split input-file into one mesh per output-file"); + parser.addOption(splitOption); + const QCommandLineOption fattenFacesOption("f", "fatten faces"); parser.addOption(fattenFacesOption); + const QCommandLineOption generateHullsOption("g", "output convex hull approximations"); + parser.addOption(generateHullsOption); + const QCommandLineOption inputFilenameOption("i", "input file", "filename.fbx"); parser.addOption(inputFilenameOption); @@ -104,6 +125,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : const QCommandLineOption minimumMeshSizeOption("m", "minimum mesh (diagonal) size to consider", "0"); parser.addOption(minimumMeshSizeOption); + const QCommandLineOption maximumMeshSizeOption("x", "maximum mesh (diagonal) size to consider", "0"); + parser.addOption(maximumMeshSizeOption); + const QCommandLineOption vHacdResolutionOption("resolution", "Maximum number of voxels generated during the " "voxelization stage (range=10,000-16,000,000)", "100000"); parser.addOption(vHacdResolutionOption); @@ -140,7 +164,6 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : parser.addOption(vHacdMaxVerticesPerCHOption); // minVolumePerCH - // convexhullApproximation @@ -157,6 +180,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : bool fattenFaces = parser.isSet(fattenFacesOption); + bool generateHulls = parser.isSet(generateHullsOption); + + QString inputFilename; if (parser.isSet(inputFilenameOption)) { inputFilename = parser.value(inputFilenameOption); @@ -195,6 +221,11 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : minimumMeshSize = parser.value(minimumMeshSizeOption).toFloat(); } + float maximumMeshSize = 0.0f; + if (parser.isSet(maximumMeshSizeOption)) { + maximumMeshSize = parser.value(maximumMeshSizeOption).toFloat(); + } + int vHacdResolution = 100000; if (parser.isSet(vHacdResolutionOption)) { vHacdResolution = parser.value(vHacdResolutionOption).toInt(); @@ -230,28 +261,13 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : vHacdMaxVerticesPerCH = parser.value(vHacdMaxVerticesPerCHOption).toInt(); } + if (!parser.isSet(splitOption) && !generateHulls) { + cerr << "\nNothing to do! Use -g or --split\n\n"; + parser.showHelp(); + Q_UNREACHABLE(); + } - //set parameters for V-HACD - params.m_callback = &pCallBack; //progress callback - params.m_resolution = vHacdResolution; - params.m_depth = vHacdDepth; - params.m_concavity = vHacdConcavity; - params.m_delta = vHacdDelta; - params.m_planeDownsampling = vHacdPlanedownsampling; - params.m_convexhullDownsampling = vHacdConvexhulldownsampling; - params.m_alpha = 0.05; // 0.05 // controls the bias toward clipping along symmetry planes - params.m_beta = 0.05; // 0.05 - params.m_gamma = 0.0005; // 0.0005 - params.m_pca = 0; // 0 enable/disable normalizing the mesh before applying the convex decomposition - params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based - params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH; - params.m_minVolumePerCH = 0.0001; // 0.0001 - params.m_callback = 0; // 0 - params.m_logger = 0; // 0 - params.m_convexhullApproximation = true; // true - params.m_oclAcceleration = true; // true - // load the mesh FBXGeometry fbx; @@ -262,40 +278,80 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : auto end = std::chrono::high_resolution_clock::now(); auto loadDuration = std::chrono::duration_cast(end - begin).count(); - //perform vhacd computation - begin = std::chrono::high_resolution_clock::now(); - FBXGeometry result; - if (!vUtil.computeVHACD(fbx, params, result, startMeshIndex, endMeshIndex, minimumMeshSize, fattenFaces)) { - cout << "Compute Failed..."; - } - end = std::chrono::high_resolution_clock::now(); - auto computeDuration = std::chrono::duration_cast(end - begin).count(); - - int totalVertices = 0; - int totalTriangles = 0; - int totalMeshParts = 0; - foreach (const FBXMesh& mesh, result.meshes) { - totalVertices += mesh.vertices.size(); - foreach (const FBXMeshPart &meshPart, mesh.parts) { - totalTriangles += meshPart.triangleIndices.size() / 3; - // each quad was made into two triangles - totalTriangles += 2 * meshPart.quadIndices.size() / 4; - totalMeshParts++; + if (parser.isSet(splitOption)) { + QVector infileExtensions = {"fbx", "obj"}; + QString baseFileName = fileNameWithoutExtension(inputFilename, infileExtensions); + int count = 0; + foreach (const FBXMesh& mesh, fbx.meshes) { + foreach (const FBXMeshPart &meshPart, mesh.parts) { + QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj"; + writeOBJ(outputFileName, fbx, count); + count++; + } } } - int totalHulls = result.meshes[0].parts.size(); - cout << endl << "Summary of V-HACD Computation..................." << endl; - cout << "File Path : " << inputFilename.toStdString() << endl; - cout << "Number Of Meshes : " << totalMeshParts << endl; - cout << "Total vertices : " << totalVertices << endl; - cout << "Total Triangles : " << totalTriangles << endl; - cout << "Total Convex Hulls : " << totalHulls << endl; - cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl; - cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl; + if (generateHulls) { + VHACD::IVHACD::Parameters params; + vhacd::ProgressCallback pCallBack; - writeOBJ(outputFilename, result); + //set parameters for V-HACD + params.m_callback = &pCallBack; //progress callback + params.m_resolution = vHacdResolution; + params.m_depth = vHacdDepth; + params.m_concavity = vHacdConcavity; + params.m_delta = vHacdDelta; + params.m_planeDownsampling = vHacdPlanedownsampling; + params.m_convexhullDownsampling = vHacdConvexhulldownsampling; + params.m_alpha = 0.05; // 0.05 // controls the bias toward clipping along symmetry planes + params.m_beta = 0.05; // 0.05 + params.m_gamma = 0.0005; // 0.0005 + params.m_pca = 0; // 0 enable/disable normalizing the mesh before applying the convex decomposition + params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based + params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH; + params.m_minVolumePerCH = 0.0001; // 0.0001 + params.m_callback = 0; // 0 + params.m_logger = 0; // 0 + params.m_convexhullApproximation = true; // true + params.m_oclAcceleration = true; // true + + //perform vhacd computation + begin = std::chrono::high_resolution_clock::now(); + + FBXGeometry result; + if (!vUtil.computeVHACD(fbx, params, result, startMeshIndex, endMeshIndex, + minimumMeshSize, maximumMeshSize, fattenFaces)) { + cout << "Compute Failed..."; + } + end = std::chrono::high_resolution_clock::now(); + auto computeDuration = std::chrono::duration_cast(end - begin).count(); + + int totalVertices = 0; + int totalTriangles = 0; + int totalMeshParts = 0; + foreach (const FBXMesh& mesh, result.meshes) { + totalVertices += mesh.vertices.size(); + foreach (const FBXMeshPart &meshPart, mesh.parts) { + totalTriangles += meshPart.triangleIndices.size() / 3; + // each quad was made into two triangles + totalTriangles += 2 * meshPart.quadIndices.size() / 4; + totalMeshParts++; + } + } + + int totalHulls = result.meshes[0].parts.size(); + cout << endl << "Summary of V-HACD Computation..................." << endl; + cout << "File Path : " << inputFilename.toStdString() << endl; + cout << "Number Of Meshes : " << totalMeshParts << endl; + cout << "Total vertices : " << totalVertices << endl; + cout << "Total Triangles : " << totalTriangles << endl; + cout << "Total Convex Hulls : " << totalHulls << endl; + cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl; + cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl; + + writeOBJ(outputFilename, result); + } } VHACDUtilApp::~VHACDUtilApp() { From 1a7bdee180ca7a486c464f2fc4676daf08677add Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Apr 2015 19:30:05 -0700 Subject: [PATCH 3/4] added code to split an fbx into one obj file per mesh-part --- tools/vhacd/src/VHACDUtil.cpp | 69 ++++++++++++++++++++++++----------- tools/vhacd/src/VHACDUtil.h | 5 ++- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp index 9f20e5e706..c05f5327ca 100644 --- a/tools/vhacd/src/VHACDUtil.cpp +++ b/tools/vhacd/src/VHACDUtil.cpp @@ -89,15 +89,51 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { +AABox getAABoxForMeshPart(const FBXMesh& mesh, const FBXMeshPart &meshPart) { + AABox aaBox; + unsigned int triangleCount = meshPart.triangleIndices.size() / 3; + for (unsigned int i = 0; i < triangleCount; i++) { + glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[i * 3]]; + glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[i * 3 + 1]]; + glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[i * 3 + 2]]; + aaBox += p0; + aaBox += p1; + aaBox += p2; + } + + unsigned int quadCount = meshPart.quadIndices.size() / 4; + for (unsigned int i = 0; i < quadCount; i++) { + unsigned int p0Index = meshPart.quadIndices[i * 4]; + unsigned int p1Index = meshPart.quadIndices[i * 4 + 1]; + unsigned int p2Index = meshPart.quadIndices[i * 4 + 2]; + unsigned int p3Index = meshPart.quadIndices[i * 4 + 3]; + glm::vec3 p0 = mesh.vertices[p0Index]; + glm::vec3 p1 = mesh.vertices[p1Index + 1]; + glm::vec3 p2 = mesh.vertices[p2Index + 2]; + glm::vec3 p3 = mesh.vertices[p3Index + 3]; + aaBox += p0; + aaBox += p1; + aaBox += p2; + aaBox += p3; + } + + return aaBox; +} + + + bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, VHACD::IVHACD::Parameters params, FBXGeometry& result, int startMeshIndex, - int endMeshIndex, float minimumMeshSize, + int endMeshIndex, + float minimumMeshSize, float maximumMeshSize, bool fattenFaces) { // count the mesh-parts - QVector meshParts; int meshCount = 0; + foreach (const FBXMesh& mesh, geometry.meshes) { + meshCount += mesh.parts.size(); + } VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD(); @@ -134,32 +170,16 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, std::vector triangles = meshPart.triangleIndices.toStdVector(); - AABox aaBox; - unsigned int triangleCount = meshPart.triangleIndices.size() / 3; - for (unsigned int i = 0; i < triangleCount; i++) { - glm::vec3 p0 = mesh.vertices[meshPart.triangleIndices[i * 3]]; - glm::vec3 p1 = mesh.vertices[meshPart.triangleIndices[i * 3 + 1]]; - glm::vec3 p2 = mesh.vertices[meshPart.triangleIndices[i * 3 + 2]]; - aaBox += p0; - aaBox += p1; - aaBox += p2; - } + AABox aaBox = getAABoxForMeshPart(mesh, meshPart); // convert quads to triangles + unsigned int triangleCount = meshPart.triangleIndices.size() / 3; unsigned int quadCount = meshPart.quadIndices.size() / 4; for (unsigned int i = 0; i < quadCount; i++) { unsigned int p0Index = meshPart.quadIndices[i * 4]; unsigned int p1Index = meshPart.quadIndices[i * 4 + 1]; unsigned int p2Index = meshPart.quadIndices[i * 4 + 2]; unsigned int p3Index = meshPart.quadIndices[i * 4 + 3]; - glm::vec3 p0 = mesh.vertices[p0Index]; - glm::vec3 p1 = mesh.vertices[p1Index + 1]; - glm::vec3 p2 = mesh.vertices[p2Index + 2]; - glm::vec3 p3 = mesh.vertices[p3Index + 3]; - aaBox += p0; - aaBox += p1; - aaBox += p2; - aaBox += p3; // split each quad into two triangles triangles.push_back(p0Index); triangles.push_back(p1Index); @@ -183,12 +203,19 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, qDebug() << "Mesh " << count << " -- " << nPoints << " points, " << triangleCount << " triangles, " << "size =" << largestDimension; - if (largestDimension < minimumMeshSize /* || largestDimension > 1000 */) { + if (largestDimension < minimumMeshSize) { qDebug() << " Skipping (too small)..."; count++; continue; } + if (maximumMeshSize > 0.0 && largestDimension > maximumMeshSize) { + qDebug() << " Skipping (too large)..."; + count++; + continue; + } + + // compute approximate convex decomposition bool res = interfaceVHACD->Compute(&vertices[0].x, 3, nPoints, &triangles[0], 3, triangleCount, params); if (!res){ diff --git a/tools/vhacd/src/VHACDUtil.h b/tools/vhacd/src/VHACDUtil.h index 37031a2f7b..8d0c6acc32 100644 --- a/tools/vhacd/src/VHACDUtil.h +++ b/tools/vhacd/src/VHACDUtil.h @@ -32,7 +32,7 @@ namespace vhacd { VHACD::IVHACD::Parameters params, FBXGeometry& result, int startMeshIndex, int endMeshIndex, - float minimumMeshSize, + float minimumMeshSize, float maximumMeshSize, bool fattenFaces); ~VHACDUtil(); }; @@ -47,4 +47,7 @@ namespace vhacd { const char * const stage, const char * const operation); }; } + +AABox getAABoxForMeshPart(const FBXMeshPart &meshPart); + #endif //hifi_VHACDUtil_h From e8ff727c61323143eaa3b709d71510a324c81268 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Tue, 7 Apr 2015 15:47:13 -0700 Subject: [PATCH 4/4] when populating shape-info, give the dimensions of the collision model rather than those of the visual model --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 4cd625c93f..73968607f7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -394,17 +394,21 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // collision model's extents). glm::vec3 scale = _dimensions / renderGeometry.getUnscaledMeshExtents().size(); - // multiply each point by scale before handing the point-set off to the physics engine + // multiply each point by scale before handing the point-set off to the physics engine. + // also determine the extents of the collision model. + AABox box; for (int i = 0; i < _points.size(); i++) { for (int j = 0; j < _points[i].size(); j++) { // compensate for registraion _points[i][j] += _model->getOffset(); // scale so the collision points match the model points _points[i][j] *= scale; + box += _points[i][j]; } } - info.setParams(getShapeType(), _dimensions, _collisionModelURL); + glm::vec3 collisionModelDimensions = box.getDimensions(); + info.setParams(getShapeType(), collisionModelDimensions, _collisionModelURL); info.setConvexHulls(_points); } }