Merge pull request #4609 from sethalves/island

physics collision model extents, more vhacd changes
This commit is contained in:
Andrew Meadows 2015-04-08 09:02:36 -07:00
commit ad33bf0dfb
5 changed files with 183 additions and 94 deletions

View file

@ -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);
}
}

View file

@ -29,10 +29,9 @@ QString& PathUtils::resourcesPath() {
QString fileNameWithoutExtension(const QString& fileName, const QVector<QString> 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);
}
}

View file

@ -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<FBXMeshPart> 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<int> 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){

View file

@ -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

View file

@ -13,6 +13,7 @@
#include <VHACD.h>
#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<int> triangles; // array of indexes
vector<float> 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<std::chrono::nanoseconds>(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<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++;
if (parser.isSet(splitOption)) {
QVector<QString> 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<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() {