#include #include #include #include #define ENABLE_VHACD_IMPLEMENTATION 1 #define VHACD_DISABLE_THREADING 0 #include "VHACD.h" #define STRINGIFY(x) #x #define MACRO_STRINGIFY(x) STRINGIFY(x) namespace py = pybind11; // Return a list of convex hulls // Each convex hull is a tuple of (vertices, indices) // vertices is a numpy array of shape (n, 3) // indices is a numpy array of shape (m,) std::vector, py::array_t>> compute_vhacd(py::array_t points, py::array_t faces, uint32_t maxConvexHulls, uint32_t resolution, double minimumVolumePercentErrorAllowed, uint32_t maxRecursionDepth, bool shrinkWrap, std::string fillMode, uint32_t maxNumVerticesPerCH, bool asyncACD, uint32_t minEdgeLength, bool findBestPlane) { /* read input arrays buffer_info */ auto buf_points = points.request(); auto buf_faces = faces.request(); /* variables */ double *ptr_points = (double *)buf_points.ptr; uint32_t *ptr_faces = (uint32_t *)buf_faces.ptr; size_t num_points = buf_points.shape[0]; size_t num_faces = buf_faces.shape[0] / 4; size_t num_triangle_indices = num_faces * 3; // Prepare our input arrays for VHACD uint32_t *triangles = new uint32_t[num_triangle_indices]; for (uint32_t i = 0; i < num_faces; i++) { // We skip the first index of the face array, which is the number of // vertices in the face triangles[3 * i] = ptr_faces[4 * i + 1]; triangles[3 * i + 1] = ptr_faces[4 * i + 2]; triangles[3 * i + 2] = ptr_faces[4 * i + 3]; } // Create the VHACD parameters VHACD::IVHACD::Parameters p; p.m_maxConvexHulls = maxConvexHulls; p.m_resolution = resolution; p.m_minimumVolumePercentErrorAllowed = minimumVolumePercentErrorAllowed; p.m_maxRecursionDepth = maxRecursionDepth; p.m_shrinkWrap = shrinkWrap; p.m_maxNumVerticesPerCH = maxNumVerticesPerCH; p.m_asyncACD = asyncACD; p.m_minEdgeLength = minEdgeLength; p.m_findBestPlane = findBestPlane; if (fillMode == "flood") { p.m_fillMode = VHACD::FillMode::FLOOD_FILL; } else if (fillMode == "raycast") { p.m_fillMode = VHACD::FillMode::RAYCAST_FILL; } else if (fillMode == "surface") { p.m_fillMode = VHACD::FillMode::SURFACE_ONLY; } else { printf("Invalid fill mode, only valid options are 'flood', 'raycast', and " "'surface'\n"); } #if VHACD_DISABLE_THREADING VHACD::IVHACD *iface = VHACD::CreateVHACD(); #else VHACD::IVHACD *iface = p.m_asyncACD ? VHACD::CreateVHACD_ASYNC() : VHACD::CreateVHACD(); #endif /// The main computation /////////////////////////////////////////////// iface->Compute(ptr_points, num_points, triangles, num_faces, p); while (!iface->IsReady()) { std::this_thread::sleep_for(std::chrono::nanoseconds(10000)); // s } // Build our output arrays from VHACD outputs std::vector, py::array_t>> res; const int nConvexHulls = iface->GetNConvexHulls(); res.reserve(nConvexHulls); if (nConvexHulls) { // Exporting Convex Decomposition results of convex hulls for (uint32_t i = 0; i < iface->GetNConvexHulls(); i++) { VHACD::IVHACD::ConvexHull ch; iface->GetConvexHull(i, ch); /* allocate the output buffers */ py::array_t res_vertices = py::array_t(ch.m_points.size() * 3); py::array_t res_faces = py::array_t(ch.m_triangles.size() * 3); py::buffer_info buf_res_vertices = res_vertices.request(); py::buffer_info buf_res_faces = res_faces.request(); double *ptr_res_vertices = (double *)buf_res_vertices.ptr; uint32_t *ptr_res_faces = (uint32_t *)buf_res_faces.ptr; for (uint32_t j = 0; j < ch.m_points.size(); j++) { const VHACD::Vertex &pos = ch.m_points[j]; ptr_res_vertices[3 * j] = pos.mX; ptr_res_vertices[3 * j + 1] = pos.mY; ptr_res_vertices[3 * j + 2] = pos.mZ; } for (uint32_t j = 0; j < ch.m_triangles.size(); j++) { uint32_t i1 = ch.m_triangles[j].mI0; uint32_t i2 = ch.m_triangles[j].mI1; uint32_t i3 = ch.m_triangles[j].mI2; ptr_res_faces[3 * j] = i1; ptr_res_faces[3 * j + 1] = i2; ptr_res_faces[3 * j + 2] = i3; } // Reshape the vertices array to be (n, 3) const long width = 3; res_vertices.resize({(long)ch.m_points.size(), width}); // Resize faces to be (m, 3) res_faces.resize({(long)ch.m_triangles.size(), width}); // Push on our list res.emplace_back(std::move(res_vertices), std::move(res_faces)); } } iface->Release(); delete[] triangles; return res; } /* Wrapping routines with PyBind */ PYBIND11_MODULE(vhacdx, m) { m.doc() = "Python bindings for the V-HACD algorithm"; // optional module docstring m.def("compute_vhacd", &compute_vhacd, "Compute convex hulls", py::arg("points"), py::arg("faces"), py::arg("maxConvexHulls") = 64, py::arg("resolution") = 400000, py::arg("minimumVolumePercentErrorAllowed") = 1.0, py::arg("maxRecursionDepth") = 10, py::arg("shrinkWrap") = true, py::arg("fillMode") = "flood", py::arg("maxNumVerticesPerCH") = 64, py::arg("asyncACD") = true, py::arg("minEdgeLength") = 2, py::arg("findBestPlane") = false); #ifdef VERSION_INFO m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO); #else m.attr("__version__") = "dev"; #endif }