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