VHACD-ASYNC.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #include "../public/VHACD.h"
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdarg.h>
  5. #include <thread>
  6. #include <atomic>
  7. #include <mutex>
  8. #include <string>
  9. #include <float.h>
  10. #define ENABLE_ASYNC 1
  11. #define HACD_ALLOC(x) malloc(x)
  12. #define HACD_FREE(x) free(x)
  13. #define HACD_ASSERT(x) assert(x)
  14. namespace VHACD
  15. {
  16. class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger
  17. {
  18. public:
  19. MyHACD_API(void)
  20. {
  21. mVHACD = VHACD::CreateVHACD();
  22. }
  23. virtual ~MyHACD_API(void)
  24. {
  25. releaseHACD();
  26. Cancel();
  27. mVHACD->Release();
  28. }
  29. virtual bool Compute(const double* const _points,
  30. const uint32_t countPoints,
  31. const uint32_t* const _triangles,
  32. const uint32_t countTriangles,
  33. const Parameters& _desc) final
  34. {
  35. #if ENABLE_ASYNC
  36. Cancel(); // if we previously had a solution running; cancel it.
  37. releaseHACD();
  38. // We need to copy the input vertices and triangles into our own buffers so we can operate
  39. // on them safely from the background thread.
  40. mVertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3);
  41. mIndices = (uint32_t *)HACD_ALLOC(sizeof(uint32_t)*countTriangles * 3);
  42. memcpy(mVertices, _points, sizeof(double)*countPoints * 3);
  43. memcpy(mIndices, _triangles, sizeof(uint32_t)*countTriangles * 3);
  44. mRunning = true;
  45. mThread = new std::thread([this, countPoints, countTriangles, _desc]()
  46. {
  47. ComputeNow(mVertices, countPoints, mIndices, countTriangles, _desc);
  48. mRunning = false;
  49. });
  50. #else
  51. releaseHACD();
  52. ComputeNow(_points, countPoints, _triangles, countTriangles, _desc);
  53. #endif
  54. return true;
  55. }
  56. bool ComputeNow(const double* const points,
  57. const uint32_t countPoints,
  58. const uint32_t* const triangles,
  59. const uint32_t countTriangles,
  60. const Parameters& _desc)
  61. {
  62. uint32_t ret = 0;
  63. mHullCount = 0;
  64. mCallback = _desc.m_callback;
  65. mLogger = _desc.m_logger;
  66. IVHACD::Parameters desc = _desc;
  67. // Set our intercepting callback interfaces if non-null
  68. desc.m_callback = desc.m_callback ? this : nullptr;
  69. desc.m_logger = desc.m_logger ? this : nullptr;
  70. if ( countPoints )
  71. {
  72. bool ok = mVHACD->Compute(points, countPoints, triangles, countTriangles, desc);
  73. if (ok)
  74. {
  75. ret = mVHACD->GetNConvexHulls();
  76. mHulls = new IVHACD::ConvexHull[ret];
  77. for (uint32_t i = 0; i < ret; i++)
  78. {
  79. VHACD::IVHACD::ConvexHull vhull;
  80. mVHACD->GetConvexHull(i, vhull);
  81. VHACD::IVHACD::ConvexHull h;
  82. h.m_nPoints = vhull.m_nPoints;
  83. h.m_points = (double *)HACD_ALLOC(sizeof(double) * 3 * h.m_nPoints);
  84. memcpy(h.m_points, vhull.m_points, sizeof(double) * 3 * h.m_nPoints);
  85. h.m_nTriangles = vhull.m_nTriangles;
  86. h.m_triangles = (uint32_t *)HACD_ALLOC(sizeof(uint32_t) * 3 * h.m_nTriangles);
  87. memcpy(h.m_triangles, vhull.m_triangles, sizeof(uint32_t) * 3 * h.m_nTriangles);
  88. h.m_volume = vhull.m_volume;
  89. h.m_center[0] = vhull.m_center[0];
  90. h.m_center[1] = vhull.m_center[1];
  91. h.m_center[2] = vhull.m_center[2];
  92. mHulls[i] = h;
  93. if (mCancel)
  94. {
  95. ret = 0;
  96. break;
  97. }
  98. }
  99. }
  100. }
  101. mHullCount = ret;
  102. return ret ? true : false;
  103. }
  104. void releaseHull(VHACD::IVHACD::ConvexHull &h)
  105. {
  106. HACD_FREE((void *)h.m_triangles);
  107. HACD_FREE((void *)h.m_points);
  108. h.m_triangles = nullptr;
  109. h.m_points = nullptr;
  110. }
  111. virtual void GetConvexHull(const uint32_t index, VHACD::IVHACD::ConvexHull& ch) const final
  112. {
  113. if ( index < mHullCount )
  114. {
  115. ch = mHulls[index];
  116. }
  117. }
  118. void releaseHACD(void) // release memory associated with the last HACD request
  119. {
  120. for (uint32_t i=0; i<mHullCount; i++)
  121. {
  122. releaseHull(mHulls[i]);
  123. }
  124. delete[]mHulls;
  125. mHulls = nullptr;
  126. mHullCount = 0;
  127. HACD_FREE(mVertices);
  128. mVertices = nullptr;
  129. HACD_FREE(mIndices);
  130. mIndices = nullptr;
  131. }
  132. virtual void release(void) // release the HACD_API interface
  133. {
  134. delete this;
  135. }
  136. virtual uint32_t getHullCount(void)
  137. {
  138. return mHullCount;
  139. }
  140. virtual void Cancel() final
  141. {
  142. if (mRunning)
  143. {
  144. mVHACD->Cancel(); // Set the cancel signal to the base VHACD
  145. }
  146. if (mThread)
  147. {
  148. mThread->join(); // Wait for the thread to fully exit before we delete the instance
  149. delete mThread;
  150. mThread = nullptr;
  151. Log("Convex Decomposition thread canceled\n");
  152. }
  153. mCancel = false; // clear the cancel semaphore
  154. }
  155. virtual bool Compute(const float* const points,
  156. const uint32_t countPoints,
  157. const uint32_t* const triangles,
  158. const uint32_t countTriangles,
  159. const Parameters& params) final
  160. {
  161. double *vertices = (double *)HACD_ALLOC(sizeof(double)*countPoints * 3);
  162. const float *source = points;
  163. double *dest = vertices;
  164. for (uint32_t i = 0; i < countPoints; i++)
  165. {
  166. dest[0] = source[0];
  167. dest[1] = source[1];
  168. dest[2] = source[2];
  169. dest += 3;
  170. source += 3;
  171. }
  172. bool ret = Compute(vertices, countPoints, triangles, countTriangles, params);
  173. HACD_FREE(vertices);
  174. return ret;
  175. }
  176. virtual uint32_t GetNConvexHulls() const final
  177. {
  178. processPendingMessages();
  179. return mHullCount;
  180. }
  181. virtual void Clean(void) final // release internally allocated memory
  182. {
  183. Cancel();
  184. releaseHACD();
  185. mVHACD->Clean();
  186. }
  187. virtual void Release(void) final // release IVHACD
  188. {
  189. delete this;
  190. }
  191. virtual bool OCLInit(void* const oclDevice,
  192. IVHACD::IUserLogger* const logger = 0) final
  193. {
  194. return mVHACD->OCLInit(oclDevice, logger);
  195. }
  196. virtual bool OCLRelease(IVHACD::IUserLogger* const logger = 0) final
  197. {
  198. return mVHACD->OCLRelease(logger);
  199. }
  200. virtual void Update(const double overallProgress,
  201. const double stageProgress,
  202. const double operationProgress,
  203. const char* const stage,
  204. const char* const operation) final
  205. {
  206. mMessageMutex.lock();
  207. mHaveUpdateMessage = true;
  208. mOverallProgress = overallProgress;
  209. mStageProgress = stageProgress;
  210. mOperationProgress = operationProgress;
  211. mStage = std::string(stage);
  212. mOperation = std::string(operation);
  213. mMessageMutex.unlock();
  214. }
  215. virtual void Log(const char* const msg) final
  216. {
  217. mMessageMutex.lock();
  218. mHaveLogMessage = true;
  219. mMessage = std::string(msg);
  220. mMessageMutex.unlock();
  221. }
  222. virtual bool IsReady(void) const final
  223. {
  224. processPendingMessages();
  225. return !mRunning;
  226. }
  227. // As a convenience for the calling application we only send it update and log messages from it's own main
  228. // thread. This reduces the complexity burden on the caller by making sure it only has to deal with log
  229. // messages in it's main application thread.
  230. void processPendingMessages(void) const
  231. {
  232. // If we have a new update message and the user has specified a callback we send the message and clear the semaphore
  233. if (mHaveUpdateMessage && mCallback)
  234. {
  235. mMessageMutex.lock();
  236. mCallback->Update(mOverallProgress, mStageProgress, mOperationProgress, mStage.c_str(), mOperation.c_str());
  237. mHaveUpdateMessage = false;
  238. mMessageMutex.unlock();
  239. }
  240. // If we have a new log message and the user has specified a callback we send the message and clear the semaphore
  241. if (mHaveLogMessage && mLogger)
  242. {
  243. mMessageMutex.lock();
  244. mLogger->Log(mMessage.c_str());
  245. mHaveLogMessage = false;
  246. mMessageMutex.unlock();
  247. }
  248. }
  249. // Will compute the center of mass of the convex hull decomposition results and return it
  250. // in 'centerOfMass'. Returns false if the center of mass could not be computed.
  251. virtual bool ComputeCenterOfMass(double centerOfMass[3]) const
  252. {
  253. bool ret = false;
  254. centerOfMass[0] = 0;
  255. centerOfMass[1] = 0;
  256. centerOfMass[2] = 0;
  257. if (mVHACD && IsReady() )
  258. {
  259. ret = mVHACD->ComputeCenterOfMass(centerOfMass);
  260. }
  261. return ret;
  262. }
  263. private:
  264. double *mVertices{ nullptr };
  265. uint32_t *mIndices{ nullptr };
  266. std::atomic< uint32_t> mHullCount{ 0 };
  267. VHACD::IVHACD::ConvexHull *mHulls{ nullptr };
  268. VHACD::IVHACD::IUserCallback *mCallback{ nullptr };
  269. VHACD::IVHACD::IUserLogger *mLogger{ nullptr };
  270. VHACD::IVHACD *mVHACD{ nullptr };
  271. std::thread *mThread{ nullptr };
  272. std::atomic< bool > mRunning{ false };
  273. std::atomic<bool> mCancel{ false };
  274. // Thread safe caching mechanism for messages and update status.
  275. // This is so that caller always gets messages in his own thread
  276. // Member variables are marked as 'mutable' since the message dispatch function
  277. // is called from const query methods.
  278. mutable std::mutex mMessageMutex;
  279. mutable std::atomic< bool > mHaveUpdateMessage{ false };
  280. mutable std::atomic< bool > mHaveLogMessage{ false };
  281. mutable double mOverallProgress{ 0 };
  282. mutable double mStageProgress{ 0 };
  283. mutable double mOperationProgress{ 0 };
  284. mutable std::string mStage;
  285. mutable std::string mOperation;
  286. mutable std::string mMessage;
  287. };
  288. IVHACD* CreateVHACD_ASYNC(void)
  289. {
  290. MyHACD_API *m = new MyHACD_API;
  291. return static_cast<IVHACD *>(m);
  292. }
  293. }; // end of VHACD namespace