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 | |
17 | namespace VHACD |
18 | { |
19 | |
20 | class MyHACD_API : public VHACD::IVHACD, public VHACD::IVHACD::IUserCallback, VHACD::IVHACD::IUserLogger |
21 | { |
22 | public: |
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 | |
299 | private: |
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 | |
326 | IVHACD* 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 | |