mirror of
https://github.com/lubosz/overte.git
synced 2025-08-07 19:41:20 +02:00
added code to split an fbx into one obj file per mesh-part
This commit is contained in:
parent
9ef9452cf6
commit
2dc4410f7e
1 changed files with 123 additions and 67 deletions
|
@ -13,6 +13,7 @@
|
||||||
#include <VHACD.h>
|
#include <VHACD.h>
|
||||||
#include "VHACDUtilApp.h"
|
#include "VHACDUtilApp.h"
|
||||||
#include "VHACDUtil.h"
|
#include "VHACDUtil.h"
|
||||||
|
#include "PathUtils.h"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace VHACD;
|
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);
|
QFile file(outFileName);
|
||||||
if (!file.open(QIODevice::WriteOnly)) {
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
qDebug() << "Unable to write to " << outFileName;
|
qDebug() << "Unable to write to " << outFileName;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextStream out(&file);
|
QTextStream out(&file);
|
||||||
|
|
||||||
unsigned int nth = 0;
|
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) {
|
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";
|
out << "g hull-" << nth++ << "\n";
|
||||||
int triangleCount = meshPart.triangleIndices.size() / 3;
|
int triangleCount = meshPart.triangleIndices.size() / 3;
|
||||||
for (int i = 0; i < triangleCount; i++) {
|
for (int i = 0; i < triangleCount; i++) {
|
||||||
out << "f ";
|
out << "f ";
|
||||||
out << meshPart.triangleIndices[i*3] + 1 << " ";
|
out << vertexIndexOffset + meshPart.triangleIndices[i*3] + 1 << " ";
|
||||||
out << meshPart.triangleIndices[i*3+1] + 1 << " ";
|
out << vertexIndexOffset + meshPart.triangleIndices[i*3+1] + 1 << " ";
|
||||||
out << meshPart.triangleIndices[i*3+2] + 1 << "\n";
|
out << vertexIndexOffset + meshPart.triangleIndices[i*3+2] + 1 << "\n";
|
||||||
}
|
}
|
||||||
out << "\n";
|
out << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (verticesHaveBeenOutput) {
|
||||||
|
vertexIndexOffset += mesh.vertices.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -72,12 +92,7 @@ bool writeOBJ(QString outFileName, FBXGeometry& geometry) {
|
||||||
VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
QCoreApplication(argc, argv)
|
QCoreApplication(argc, argv)
|
||||||
{
|
{
|
||||||
vector<int> triangles; // array of indexes
|
|
||||||
vector<float> points; // array of coordinates
|
|
||||||
vhacd::VHACDUtil vUtil;
|
vhacd::VHACDUtil vUtil;
|
||||||
VHACD::IVHACD::Parameters params;
|
|
||||||
vhacd::ProgressCallback pCallBack;
|
|
||||||
|
|
||||||
|
|
||||||
// parse command-line
|
// parse command-line
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
|
@ -86,9 +101,15 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
|
|
||||||
const QCommandLineOption helpOption = parser.addHelpOption();
|
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");
|
const QCommandLineOption fattenFacesOption("f", "fatten faces");
|
||||||
parser.addOption(fattenFacesOption);
|
parser.addOption(fattenFacesOption);
|
||||||
|
|
||||||
|
const QCommandLineOption generateHullsOption("g", "output convex hull approximations");
|
||||||
|
parser.addOption(generateHullsOption);
|
||||||
|
|
||||||
const QCommandLineOption inputFilenameOption("i", "input file", "filename.fbx");
|
const QCommandLineOption inputFilenameOption("i", "input file", "filename.fbx");
|
||||||
parser.addOption(inputFilenameOption);
|
parser.addOption(inputFilenameOption);
|
||||||
|
|
||||||
|
@ -104,6 +125,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
const QCommandLineOption minimumMeshSizeOption("m", "minimum mesh (diagonal) size to consider", "0");
|
const QCommandLineOption minimumMeshSizeOption("m", "minimum mesh (diagonal) size to consider", "0");
|
||||||
parser.addOption(minimumMeshSizeOption);
|
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 "
|
const QCommandLineOption vHacdResolutionOption("resolution", "Maximum number of voxels generated during the "
|
||||||
"voxelization stage (range=10,000-16,000,000)", "100000");
|
"voxelization stage (range=10,000-16,000,000)", "100000");
|
||||||
parser.addOption(vHacdResolutionOption);
|
parser.addOption(vHacdResolutionOption);
|
||||||
|
@ -140,7 +164,6 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
parser.addOption(vHacdMaxVerticesPerCHOption);
|
parser.addOption(vHacdMaxVerticesPerCHOption);
|
||||||
|
|
||||||
// minVolumePerCH
|
// minVolumePerCH
|
||||||
|
|
||||||
// convexhullApproximation
|
// convexhullApproximation
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,6 +180,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
|
|
||||||
bool fattenFaces = parser.isSet(fattenFacesOption);
|
bool fattenFaces = parser.isSet(fattenFacesOption);
|
||||||
|
|
||||||
|
bool generateHulls = parser.isSet(generateHullsOption);
|
||||||
|
|
||||||
|
|
||||||
QString inputFilename;
|
QString inputFilename;
|
||||||
if (parser.isSet(inputFilenameOption)) {
|
if (parser.isSet(inputFilenameOption)) {
|
||||||
inputFilename = parser.value(inputFilenameOption);
|
inputFilename = parser.value(inputFilenameOption);
|
||||||
|
@ -195,6 +221,11 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
minimumMeshSize = parser.value(minimumMeshSizeOption).toFloat();
|
minimumMeshSize = parser.value(minimumMeshSizeOption).toFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float maximumMeshSize = 0.0f;
|
||||||
|
if (parser.isSet(maximumMeshSizeOption)) {
|
||||||
|
maximumMeshSize = parser.value(maximumMeshSizeOption).toFloat();
|
||||||
|
}
|
||||||
|
|
||||||
int vHacdResolution = 100000;
|
int vHacdResolution = 100000;
|
||||||
if (parser.isSet(vHacdResolutionOption)) {
|
if (parser.isSet(vHacdResolutionOption)) {
|
||||||
vHacdResolution = parser.value(vHacdResolutionOption).toInt();
|
vHacdResolution = parser.value(vHacdResolutionOption).toInt();
|
||||||
|
@ -230,28 +261,13 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
vHacdMaxVerticesPerCH = parser.value(vHacdMaxVerticesPerCHOption).toInt();
|
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
|
// load the mesh
|
||||||
|
|
||||||
FBXGeometry fbx;
|
FBXGeometry fbx;
|
||||||
|
@ -262,40 +278,80 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) :
|
||||||
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();
|
||||||
|
|
||||||
//perform vhacd computation
|
|
||||||
begin = std::chrono::high_resolution_clock::now();
|
|
||||||
|
|
||||||
FBXGeometry result;
|
if (parser.isSet(splitOption)) {
|
||||||
if (!vUtil.computeVHACD(fbx, params, result, startMeshIndex, endMeshIndex, minimumMeshSize, fattenFaces)) {
|
QVector<QString> infileExtensions = {"fbx", "obj"};
|
||||||
cout << "Compute Failed...";
|
QString baseFileName = fileNameWithoutExtension(inputFilename, infileExtensions);
|
||||||
}
|
int count = 0;
|
||||||
end = std::chrono::high_resolution_clock::now();
|
foreach (const FBXMesh& mesh, fbx.meshes) {
|
||||||
auto computeDuration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
|
foreach (const FBXMeshPart &meshPart, mesh.parts) {
|
||||||
|
QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj";
|
||||||
int totalVertices = 0;
|
writeOBJ(outputFileName, fbx, count);
|
||||||
int totalTriangles = 0;
|
count++;
|
||||||
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();
|
if (generateHulls) {
|
||||||
cout << endl << "Summary of V-HACD Computation..................." << endl;
|
VHACD::IVHACD::Parameters params;
|
||||||
cout << "File Path : " << inputFilename.toStdString() << endl;
|
vhacd::ProgressCallback pCallBack;
|
||||||
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);
|
//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<std::chrono::nanoseconds>(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() {
|
VHACDUtilApp::~VHACDUtilApp() {
|
||||||
|
|
Loading…
Reference in a new issue