#include "../public/VHACD.h" #include #include #include #include #include #include #include #include #define ENABLE_ASYNC 1 #define HACD_ALLOC(x) malloc(x) #define HACD_FREE(x) free(x) #define HACD_ASSERT(x) assert(x) namespace VHACD { class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger { public: MyHACD_API(void) { mVHACD = VHACD::CreateVHACD(); } virtual ~MyHACD_API(void) { releaseHACD(); Cancel(); mVHACD->Release(); } virtual bool Compute(const double* const _points, const uint32_t countPoints, const uint32_t* const _triangles, const uint32_t countTriangles, const Parameters& _desc) final { #if ENABLE_ASYNC Cancel(); // if we previously had a solution running; cancel it. releaseHACD(); // We need to copy the input vertices and triangles into our own buffers so we can operate // on them safely from the background thread. mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3); memcpy(mVertices, _points, sizeof(double)*countPoints * 3); memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3); mRunning = true; mThread = new std::thread([this, countPoints, countTriangles, _desc]() { ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc); mRunning = false; }); #else releaseHACD(); ComputeNow(_points, countPoints, _triangles, countTriangles, _desc); #endif return true; } bool ComputeNow(const double* const points, const uint32_t countPoints, const uint32_t* const triangles, const uint32_t countTriangles, const Parameters& _desc) { uint32_t ret = 0; mHullCount = 0; mCallback = _desc.m_callback; mLogger = _desc.m_logger; IVHACD::Parameters desc = _desc; // Set our intercepting callback interfaces if non-null desc.m_callback = desc.m_callback ? this : nullptr; desc.m_logger = desc.m_logger ? this : nullptr; if ( countPoints ) { bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc); if (ok) { ret = mVHACD->GetNConvexHulls(); mHulls = new IVHACD::ConvexHull[ret]; for (uint32_t i = 0; i < ret; i++) { VHACD::IVHACD::ConvexHull vhull; mVHACD->GetConvexHull(i, vhull); VHACD::IVHACD::ConvexHull h; h.m_nPoints = vhull.m_nPoints; h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints); memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints); h.m_nTriangles = vhull.m_nTriangles; h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles); memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles); h.m_volume = vhull.m_volume; h.m_center[0] = vhull.m_center[0]; h.m_center[1] = vhull.m_center[1]; h.m_center[2] = vhull.m_center[2]; mHulls[i] = h; if (mCancel) { ret = 0; break; } } } } mHullCount = ret; return ret ? true : false; } void releaseHull(VHACD::IVHACD::ConvexHull &h) { HACD_FREE((void *)h.m_triangles); HACD_FREE((void *)h.m_points); h.m_triangles = nullptr; h.m_points = nullptr; } virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final { if ( index < mHullCount ) { ch = mHulls[index]; } } void releaseHACD(void) // release memory associated with the last HACD request { for (uint32_t i=0; iCancel(); // Set the cancel signal to the base VHACD } if (mThread) { mThread->join(); // Wait for the thread to fully exit before we delete the instance delete mThread; mThread = nullptr; Log("Convex Decomposition thread canceled\n"); } mCancel = false; // clear the cancel semaphore } virtual bool Compute(const float* const points, const uint32_t countPoints, const uint32_t* const triangles, const uint32_t countTriangles, const Parameters& params) final { double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3); const float *source = points; double *dest = vertices; for (uint32_t i = 0; i < countPoints; i++) { dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest += 3; source += 3; } bool ret = Compute(vertices, countPoints, triangles, countTriangles, params); HACD_FREE(vertices); return ret; } virtual uint32_t GetNConvexHulls() const final { processPendingMessages(); return mHullCount; } virtual void Clean(void) final // release internally allocated memory { Cancel(); releaseHACD(); mVHACD->Clean(); } virtual void Release(void) final // release IVHACD { delete this; } virtual bool OCLInit(void* const oclDevice, IVHACD::IUserLogger* const logger = 0) final { return mVHACD->OCLInit(oclDevice, logger); } virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final { return mVHACD->OCLRelease(logger); } virtual void Update(const double overallProgress, const double stageProgress, const double operationProgress, const char* const stage, const char* const operation) final { mMessageMutex.lock(); mHaveUpdateMessage = true; mOverallProgress = overallProgress; mStageProgress = stageProgress; mOperationProgress = operationProgress; mStage = std::string(stage); mOperation = std::string(operation); mMessageMutex.unlock(); } virtual void Log(const char* const msg) final { mMessageMutex.lock(); mHaveLogMessage = true; mMessage = std::string(msg); mMessageMutex.unlock(); } virtual bool IsReady(void) const final { processPendingMessages(); return !mRunning; } // As a convenience for the calling application we only send it update and log messages from it's own main // thread. This reduces the complexity burden on the caller by making sure it only has to deal with log // messages in it's main application thread. void processPendingMessages(void) const { // If we have a new update message and the user has specified a callback we send the message and clear the semaphore if (mHaveUpdateMessage && mCallback) { mMessageMutex.lock(); mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str()); mHaveUpdateMessage = false; mMessageMutex.unlock(); } // If we have a new log message and the user has specified a callback we send the message and clear the semaphore if (mHaveLogMessage && mLogger) { mMessageMutex.lock(); mLogger->Log(mMessage.c_str()); mHaveLogMessage = false; mMessageMutex.unlock(); } } // Will compute the center of mass of the convex hull decomposition results and return it // in 'centerOfMass'. Returns false if the center of mass could not be computed. virtual bool ComputeCenterOfMass(double centerOfMass[3]) const { bool ret = false; centerOfMass[0] = 0; centerOfMass[1] = 0; centerOfMass[2] = 0; if (mVHACD && IsReady() ) { ret = mVHACD->ComputeCenterOfMass(centerOfMass); } return ret; } private: double *mVertices{ nullptr }; uint32_t *mIndices{ nullptr }; std::atomic< uint32_t> mHullCount{ 0 }; VHACD::IVHACD::ConvexHull *mHulls{ nullptr }; VHACD::IVHACD::IUserCallback *mCallback{ nullptr }; VHACD::IVHACD::IUserLogger *mLogger{ nullptr }; VHACD::IVHACD *mVHACD{ nullptr }; std::thread *mThread{ nullptr }; std::atomic< bool > mRunning{ false }; std::atomic mCancel{ false }; // Thread safe caching mechanism for messages and update status. // This is so that caller always gets messages in his own thread // Member variables are marked as 'mutable' since the message dispatch function // is called from const query methods. mutable std::mutex mMessageMutex; mutable std::atomic< bool > mHaveUpdateMessage{ false }; mutable std::atomic< bool > mHaveLogMessage{ false }; mutable double mOverallProgress{ 0 }; mutable double mStageProgress{ 0 }; mutable double mOperationProgress{ 0 }; mutable std::string mStage; mutable std::string mOperation; mutable std::string mMessage; }; IVHACD* CreateVHACD_ASYNC(void) { MyHACD_API *m = new MyHACD_API; return static_cast(m); } }; // end of VHACD namespace