From 207ddcea3863805597f066c23efe32192fcfa02d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Sat, 9 Jul 2016 22:25:28 -0700 Subject: [PATCH] wrap hull about each mesh part --- .../src/RenderableModelEntityItem.cpp | 84 ++++++++++++++++--- libraries/physics/src/ShapeFactory.cpp | 58 ++++++++++++- libraries/shared/src/ShapeInfo.h | 6 +- 3 files changed, 133 insertions(+), 15 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 416a38d27c..9f4ff63548 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -10,6 +10,7 @@ // #include +#include #include #include @@ -690,8 +691,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { glm::vec3 scaleToFit = dimensions / _model->getFBXGeometry().getUnscaledMeshExtents().size(); // multiply each point by scale before handing the point-set off to the physics engine. // also determine the extents of the collision model. - for (int i = 0; i < pointCollection.size(); i++) { - for (int j = 0; j < pointCollection[i].size(); j++) { + for (int32_t i = 0; i < pointCollection.size(); i++) { + for (int32_t j = 0; j < pointCollection[i].size(); j++) { // compensate for registration pointCollection[i][j] += _model->getOffset(); // scale so the collision points match the model points @@ -708,9 +709,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // compute meshPart local transforms QVector localTransforms; const FBXGeometry& fbxGeometry = _model->getFBXGeometry(); - int numberOfMeshes = fbxGeometry.meshes.size(); - int totalNumVertices = 0; - for (int i = 0; i < numberOfMeshes; i++) { + int32_t numMeshes = (int32_t)fbxGeometry.meshes.size(); + int32_t totalNumVertices = 0; + for (int32_t i = 0; i < numMeshes; i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.clusters.size() > 0) { const FBXCluster& cluster = mesh.clusters.at(0); @@ -722,7 +723,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } totalNumVertices += mesh.vertices.size(); } - const int MAX_VERTICES_PER_STATIC_MESH = 1e6; + const int32_t MAX_VERTICES_PER_STATIC_MESH = 1e6; if (totalNumVertices > MAX_VERTICES_PER_STATIC_MESH) { qWarning() << "model" << getModelURL() << "has too many vertices" << totalNumVertices << "and will collide as a box."; info.setParams(SHAPE_TYPE_BOX, 0.5f * dimensions); @@ -730,7 +731,9 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } auto& meshes = _model->getGeometry()->getGeometry()->getMeshes(); - int32_t numMeshes = (int32_t)(meshes.size()); + + // the render geometry's mesh count should match that of the FBXGeometry + assert(numMeshes == (int32_t)(meshes.size())); ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); pointCollection.clear(); @@ -741,8 +744,8 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } Extents extents; - int meshCount = 0; - int pointListIndex = 0; + int32_t meshCount = 0; + int32_t pointListIndex = 0; for (auto& mesh : meshes) { const gpu::BufferView& vertices = mesh->getVertexBuffer(); const gpu::BufferView& indices = mesh->getIndexBuffer(); @@ -775,6 +778,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { if (type == SHAPE_TYPE_STATIC_MESH) { // copy into triangleIndices ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); + triangleIndices.clear(); triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); gpu::BufferView::Iterator partItr = parts.cbegin(); while (partItr != parts.cend()) { @@ -823,6 +827,64 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { } ++partItr; } + } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { + // for each mesh copy unique part indices, separated by special bogus (flag) index values + ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); + triangleIndices.clear(); + gpu::BufferView::Iterator partItr = parts.cbegin(); + while (partItr != parts.cend()) { + // collect unique list of indices for this part + std::set uniqueIndices; + if (partItr->_topology == model::Mesh::TRIANGLES) { + assert(partItr->_numIndices % 3 == 0); + auto indexItr = indices.cbegin() + partItr->_startIndex; + auto indexEnd = indexItr + partItr->_numIndices; + while (indexItr != indexEnd) { + uniqueIndices.insert(*indexItr); + ++indexItr; + } + } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { + assert(partItr->_numIndices > 2); + auto indexItr = indices.cbegin() + partItr->_startIndex; + auto indexEnd = indexItr + (partItr->_numIndices - 2); + + // first triangle uses the first three indices + uniqueIndices.insert(*(indexItr++)); + uniqueIndices.insert(*(indexItr++)); + uniqueIndices.insert(*(indexItr++)); + + // the rest use previous and next index + uint32_t triangleCount = 1; + while (indexItr != indexEnd) { + if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { + if (triangleCount % 2 == 0) { + // even triangles use first two indices in order + uniqueIndices.insert(*(indexItr - 2)); + uniqueIndices.insert(*(indexItr - 1)); + } else { + // odd triangles swap order of first two indices + uniqueIndices.insert(*(indexItr - 1)); + uniqueIndices.insert(*(indexItr - 2)); + } + uniqueIndices.insert(*indexItr); + ++triangleCount; + } + ++indexItr; + } + } + + // store uniqueIndices in triangleIndices + triangleIndices.reserve(triangleIndices.size() + uniqueIndices.size()); + for (auto index : uniqueIndices) { + triangleIndices.push_back(index); + } + // flag end of part + triangleIndices.push_back(END_OF_MESH_PART); + + ++partItr; + } + // flag end of mesh + triangleIndices.push_back(END_OF_MESH); } ++meshCount; } @@ -830,13 +892,13 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) { // scale and shift glm::vec3 extentsSize = extents.size(); glm::vec3 scaleToFit = dimensions / extentsSize; - for (int i = 0; i < 3; ++i) { + for (int32_t i = 0; i < 3; ++i) { if (extentsSize[i] < 1.0e-6f) { scaleToFit[i] = 1.0f; } } for (auto points : pointCollection) { - for (int i = 0; i < points.size(); ++i) { + for (int32_t i = 0; i < points.size(); ++i) { points[i] = (points[i] * scaleToFit); } } diff --git a/libraries/physics/src/ShapeFactory.cpp b/libraries/physics/src/ShapeFactory.cpp index f71711eccd..e47a0fe8a2 100644 --- a/libraries/physics/src/ShapeFactory.cpp +++ b/libraries/physics/src/ShapeFactory.cpp @@ -69,6 +69,7 @@ static const btVector3 _unitSphereDirections[NUM_UNIT_SPHERE_DIRECTIONS] = { // util method btConvexHullShape* createConvexHull(const ShapeInfo::PointList& points) { + //std::cout << "adebug createConvexHull() points.size() = " << points.size() << std::endl; // adebug assert(points.size() > 0); btConvexHullShape* hull = new btConvexHullShape(); @@ -240,6 +241,7 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) { btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { btCollisionShape* shape = NULL; int type = info.getType(); + //std::cout << "adebug createShapeFromInfo() type = " << type << std::endl; // adebug switch(type) { case SHAPE_TYPE_BOX: { shape = new btBoxShape(glmToBullet(info.getHalfExtents())); @@ -258,8 +260,7 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { } break; case SHAPE_TYPE_COMPOUND: - case SHAPE_TYPE_SIMPLE_HULL: - case SHAPE_TYPE_SIMPLE_COMPOUND: { + case SHAPE_TYPE_SIMPLE_HULL: { const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); uint32_t numSubShapes = info.getNumSubShapes(); if (numSubShapes == 1) { @@ -270,12 +271,63 @@ btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info) { trans.setIdentity(); foreach (const ShapeInfo::PointList& hullPoints, pointCollection) { btConvexHullShape* hull = createConvexHull(hullPoints); - compound->addChildShape (trans, hull); + compound->addChildShape(trans, hull); } shape = compound; } } break; + case SHAPE_TYPE_SIMPLE_COMPOUND: { + const ShapeInfo::PointCollection& pointCollection = info.getPointCollection(); + const ShapeInfo::TriangleIndices& triangleIndices = info.getTriangleIndices(); + uint32_t numIndices = triangleIndices.size(); + uint32_t numMeshes = info.getNumSubShapes(); + const uint32_t MIN_NUM_SIMPLE_COMPOUND_INDICES = 2; // END_OF_MESH_PART + END_OF_MESH + if (numMeshes > 0 && numIndices > MIN_NUM_SIMPLE_COMPOUND_INDICES) { + uint32_t i = 0; + std::vector hulls; + for (auto& points : pointCollection) { + // build a hull around each part + while (i < numIndices) { + ShapeInfo::PointList hullPoints; + hullPoints.reserve(points.size()); + while (i < numIndices) { + int32_t j = triangleIndices[i]; + ++i; + if (j == END_OF_MESH_PART) { + // end of part + break; + } + hullPoints.push_back(points[j]); + } + if (hullPoints.size() > 0) { + btConvexHullShape* hull = createConvexHull(hullPoints); + hulls.push_back(hull); + } + + assert(i < numIndices); + if (triangleIndices[i] == END_OF_MESH) { + // end of mesh + ++i; + break; + } + } + } + uint32_t numHulls = hulls.size(); + if (numHulls == 1) { + shape = hulls[0]; + } else { + auto compound = new btCompoundShape(); + btTransform trans; + trans.setIdentity(); + for (auto hull : hulls) { + compound->addChildShape(trans, hull); + } + shape = compound; + } + } + } + break; case SHAPE_TYPE_STATIC_MESH: { btTriangleIndexVertexArray* dataArray = createStaticMeshArray(info); shape = new StaticMeshShape(dataArray); diff --git a/libraries/shared/src/ShapeInfo.h b/libraries/shared/src/ShapeInfo.h index 7bf145412a..a6ff8d6d4a 100644 --- a/libraries/shared/src/ShapeInfo.h +++ b/libraries/shared/src/ShapeInfo.h @@ -26,6 +26,10 @@ const float MIN_SHAPE_OFFSET = 0.001f; // offsets less than 1mm will be ignored // trim convex hulls with many points down to only 42 points. const int MAX_HULL_POINTS = 42; + +const int32_t END_OF_MESH_PART = -1; // bogus vertex index at end of mesh part +const int32_t END_OF_MESH = -2; // bogus vertex index at end of mesh + enum ShapeType { SHAPE_TYPE_NONE, SHAPE_TYPE_BOX, @@ -50,7 +54,7 @@ public: using PointList = QVector; using PointCollection = QVector; - using TriangleIndices = QVector; + using TriangleIndices = QVector; void clear();