VHACD.cpp 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433
  1. /* Copyright (c) 2011 Khaled Mamou (kmamou at gmail dot com)
  2. All rights reserved.
  3. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  4. 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  5. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  6. 3. The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
  7. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  8. */
  9. #define _CRT_SECURE_NO_WARNINGS
  10. #include <algorithm>
  11. #include <fstream>
  12. #include <iomanip>
  13. #include <limits>
  14. #include <sstream>
  15. #if _OPENMP
  16. #include <omp.h>
  17. #endif // _OPENMP
  18. #include "../public/VHACD.h"
  19. #include "btConvexHullComputer.h"
  20. #include "vhacdICHull.h"
  21. #include "vhacdMesh.h"
  22. #include "vhacdSArray.h"
  23. #include "vhacdTimer.h"
  24. #include "vhacdVHACD.h"
  25. #include "vhacdVector.h"
  26. #include "vhacdVolume.h"
  27. #define MAX(a, b) (((a) > (b)) ? (a) : (b))
  28. #define MIN(a, b) (((a) < (b)) ? (a) : (b))
  29. #define ABS(a) (((a) < 0) ? -(a) : (a))
  30. #define ZSGN(a) (((a) < 0) ? -1 : (a) > 0 ? 1 : 0)
  31. #define MAX_DOUBLE (1.79769e+308)
  32. #ifdef USE_SSE
  33. #include <immintrin.h>
  34. const int SIMD_WIDTH = 4;
  35. inline int FindMinimumElement(const float* const d, float* const m, const int n)
  36. {
  37. // Min within vectors
  38. __m128 min_i = _mm_set1_ps(-1.0f);
  39. __m128 min_v = _mm_set1_ps(std::numeric_limits<float>::max());
  40. for (int i = 0; i <= n - SIMD_WIDTH; i += SIMD_WIDTH) {
  41. const __m128 data = _mm_load_ps(&d[i]);
  42. const __m128 pred = _mm_cmplt_ps(data, min_v);
  43. min_i = _mm_blendv_ps(min_i, _mm_set1_ps(i), pred);
  44. min_v = _mm_min_ps(data, min_v);
  45. }
  46. /* Min within vector */
  47. const __m128 min1 = _mm_shuffle_ps(min_v, min_v, _MM_SHUFFLE(1, 0, 3, 2));
  48. const __m128 min2 = _mm_min_ps(min_v, min1);
  49. const __m128 min3 = _mm_shuffle_ps(min2, min2, _MM_SHUFFLE(0, 1, 0, 1));
  50. const __m128 min4 = _mm_min_ps(min2, min3);
  51. float min_d = _mm_cvtss_f32(min4);
  52. // Min index
  53. const int min_idx = __builtin_ctz(_mm_movemask_ps(_mm_cmpeq_ps(min_v, min4)));
  54. int ret = min_i[min_idx] + min_idx;
  55. // Trailing elements
  56. for (int i = (n & ~(SIMD_WIDTH - 1)); i < n; ++i) {
  57. if (d[i] < min_d) {
  58. min_d = d[i];
  59. ret = i;
  60. }
  61. }
  62. *m = min_d;
  63. return ret;
  64. }
  65. inline int FindMinimumElement(const float* const d, float* const m, const int begin, const int end)
  66. {
  67. // Leading elements
  68. int min_i = -1;
  69. float min_d = std::numeric_limits<float>::max();
  70. const int aligned = (begin & ~(SIMD_WIDTH - 1)) + ((begin & (SIMD_WIDTH - 1)) ? SIMD_WIDTH : 0);
  71. for (int i = begin; i < std::min(end, aligned); ++i) {
  72. if (d[i] < min_d) {
  73. min_d = d[i];
  74. min_i = i;
  75. }
  76. }
  77. // Middle and trailing elements
  78. float r_m = std::numeric_limits<float>::max();
  79. const int n = end - aligned;
  80. const int r_i = (n > 0) ? FindMinimumElement(&d[aligned], &r_m, n) : 0;
  81. // Pick the lowest
  82. if (r_m < min_d) {
  83. *m = r_m;
  84. return r_i + aligned;
  85. }
  86. else {
  87. *m = min_d;
  88. return min_i;
  89. }
  90. }
  91. #else
  92. inline int FindMinimumElement(const float* const d, float* const m, const int begin, const int end)
  93. {
  94. int idx = -1;
  95. float min = (std::numeric_limits<float>::max)();
  96. for (int i = begin; i < end; ++i) {
  97. if (d[i] < min) {
  98. idx = i;
  99. min = d[i];
  100. }
  101. }
  102. *m = min;
  103. return idx;
  104. }
  105. #endif
  106. //#define OCL_SOURCE_FROM_FILE
  107. #ifndef OCL_SOURCE_FROM_FILE
  108. const char* oclProgramSource = "\
  109. __kernel void ComputePartialVolumes(__global short4 * voxels, \
  110. const int numVoxels, \
  111. const float4 plane, \
  112. const float4 minBB, \
  113. const float4 scale, \
  114. __local uint4 * localPartialVolumes, \
  115. __global uint4 * partialVolumes) \
  116. { \
  117. int localId = get_local_id(0); \
  118. int groupSize = get_local_size(0); \
  119. int i0 = get_global_id(0) << 2; \
  120. float4 voxel; \
  121. uint4 v; \
  122. voxel = convert_float4(voxels[i0]); \
  123. v.s0 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 < numVoxels);\
  124. voxel = convert_float4(voxels[i0 + 1]); \
  125. v.s1 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 1 < numVoxels);\
  126. voxel = convert_float4(voxels[i0 + 2]); \
  127. v.s2 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 2 < numVoxels);\
  128. voxel = convert_float4(voxels[i0 + 3]); \
  129. v.s3 = (dot(plane, mad(scale, voxel, minBB)) >= 0.0f) * (i0 + 3 < numVoxels);\
  130. localPartialVolumes[localId] = v; \
  131. barrier(CLK_LOCAL_MEM_FENCE); \
  132. for (int i = groupSize >> 1; i > 0; i >>= 1) \
  133. { \
  134. if (localId < i) \
  135. { \
  136. localPartialVolumes[localId] += localPartialVolumes[localId + i]; \
  137. } \
  138. barrier(CLK_LOCAL_MEM_FENCE); \
  139. } \
  140. if (localId == 0) \
  141. { \
  142. partialVolumes[get_group_id(0)] = localPartialVolumes[0]; \
  143. } \
  144. } \
  145. __kernel void ComputePartialSums(__global uint4 * data, \
  146. const int dataSize, \
  147. __local uint4 * partialSums) \
  148. { \
  149. int globalId = get_global_id(0); \
  150. int localId = get_local_id(0); \
  151. int groupSize = get_local_size(0); \
  152. int i; \
  153. if (globalId < dataSize) \
  154. { \
  155. partialSums[localId] = data[globalId]; \
  156. } \
  157. else \
  158. { \
  159. partialSums[localId] = (0, 0, 0, 0); \
  160. } \
  161. barrier(CLK_LOCAL_MEM_FENCE); \
  162. for (i = groupSize >> 1; i > 0; i >>= 1) \
  163. { \
  164. if (localId < i) \
  165. { \
  166. partialSums[localId] += partialSums[localId + i]; \
  167. } \
  168. barrier(CLK_LOCAL_MEM_FENCE); \
  169. } \
  170. if (localId == 0) \
  171. { \
  172. data[get_group_id(0)] = partialSums[0]; \
  173. } \
  174. }";
  175. #endif //OCL_SOURCE_FROM_FILE
  176. namespace VHACD {
  177. IVHACD* CreateVHACD(void)
  178. {
  179. return new VHACD();
  180. }
  181. bool VHACD::OCLInit(void* const oclDevice, IUserLogger* const logger)
  182. {
  183. #ifdef CL_VERSION_1_1
  184. m_oclDevice = (cl_device_id*)oclDevice;
  185. cl_int error;
  186. m_oclContext = clCreateContext(NULL, 1, m_oclDevice, NULL, NULL, &error);
  187. if (error != CL_SUCCESS) {
  188. if (logger) {
  189. logger->Log("Couldn't create context\n");
  190. }
  191. return false;
  192. }
  193. #ifdef OCL_SOURCE_FROM_FILE
  194. std::string cl_files = OPENCL_CL_FILES;
  195. // read kernal from file
  196. #ifdef _WIN32
  197. std::replace(cl_files.begin(), cl_files.end(), '/', '\\');
  198. #endif // _WIN32
  199. FILE* program_handle = fopen(cl_files.c_str(), "rb");
  200. fseek(program_handle, 0, SEEK_END);
  201. size_t program_size = ftell(program_handle);
  202. rewind(program_handle);
  203. char* program_buffer = new char[program_size + 1];
  204. program_buffer[program_size] = '\0';
  205. fread(program_buffer, sizeof(char), program_size, program_handle);
  206. fclose(program_handle);
  207. // create program
  208. m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&program_buffer, &program_size, &error);
  209. delete[] program_buffer;
  210. #else
  211. size_t program_size = strlen(oclProgramSource);
  212. m_oclProgram = clCreateProgramWithSource(m_oclContext, 1, (const char**)&oclProgramSource, &program_size, &error);
  213. #endif
  214. if (error != CL_SUCCESS) {
  215. if (logger) {
  216. logger->Log("Couldn't create program\n");
  217. }
  218. return false;
  219. }
  220. /* Build program */
  221. error = clBuildProgram(m_oclProgram, 1, m_oclDevice, "-cl-denorms-are-zero", NULL, NULL);
  222. if (error != CL_SUCCESS) {
  223. size_t log_size;
  224. /* Find Size of log and print to std output */
  225. clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &log_size);
  226. char* program_log = new char[log_size + 2];
  227. program_log[log_size] = '\n';
  228. program_log[log_size + 1] = '\0';
  229. clGetProgramBuildInfo(m_oclProgram, *m_oclDevice, CL_PROGRAM_BUILD_LOG, log_size + 1, program_log, NULL);
  230. if (logger) {
  231. logger->Log("Couldn't build program\n");
  232. logger->Log(program_log);
  233. }
  234. delete[] program_log;
  235. return false;
  236. }
  237. delete[] m_oclQueue;
  238. delete[] m_oclKernelComputePartialVolumes;
  239. delete[] m_oclKernelComputeSum;
  240. m_oclQueue = new cl_command_queue[m_ompNumProcessors];
  241. m_oclKernelComputePartialVolumes = new cl_kernel[m_ompNumProcessors];
  242. m_oclKernelComputeSum = new cl_kernel[m_ompNumProcessors];
  243. const char nameKernelComputePartialVolumes[] = "ComputePartialVolumes";
  244. const char nameKernelComputeSum[] = "ComputePartialSums";
  245. for (int k = 0; k < m_ompNumProcessors; ++k) {
  246. m_oclKernelComputePartialVolumes[k] = clCreateKernel(m_oclProgram, nameKernelComputePartialVolumes, &error);
  247. if (error != CL_SUCCESS) {
  248. if (logger) {
  249. logger->Log("Couldn't create kernel\n");
  250. }
  251. return false;
  252. }
  253. m_oclKernelComputeSum[k] = clCreateKernel(m_oclProgram, nameKernelComputeSum, &error);
  254. if (error != CL_SUCCESS) {
  255. if (logger) {
  256. logger->Log("Couldn't create kernel\n");
  257. }
  258. return false;
  259. }
  260. }
  261. error = clGetKernelWorkGroupInfo(m_oclKernelComputePartialVolumes[0],
  262. *m_oclDevice,
  263. CL_KERNEL_WORK_GROUP_SIZE,
  264. sizeof(size_t),
  265. &m_oclWorkGroupSize,
  266. NULL);
  267. size_t workGroupSize = 0;
  268. error = clGetKernelWorkGroupInfo(m_oclKernelComputeSum[0],
  269. *m_oclDevice,
  270. CL_KERNEL_WORK_GROUP_SIZE,
  271. sizeof(size_t),
  272. &workGroupSize,
  273. NULL);
  274. if (error != CL_SUCCESS) {
  275. if (logger) {
  276. logger->Log("Couldn't query work group info\n");
  277. }
  278. return false;
  279. }
  280. if (workGroupSize < m_oclWorkGroupSize) {
  281. m_oclWorkGroupSize = workGroupSize;
  282. }
  283. for (int k = 0; k < m_ompNumProcessors; ++k) {
  284. m_oclQueue[k] = clCreateCommandQueue(m_oclContext, *m_oclDevice, 0 /*CL_QUEUE_PROFILING_ENABLE*/, &error);
  285. if (error != CL_SUCCESS) {
  286. if (logger) {
  287. logger->Log("Couldn't create queue\n");
  288. }
  289. return false;
  290. }
  291. }
  292. return true;
  293. #else //CL_VERSION_1_1
  294. return false;
  295. #endif //CL_VERSION_1_1
  296. }
  297. bool VHACD::OCLRelease(IUserLogger* const logger)
  298. {
  299. #ifdef CL_VERSION_1_1
  300. cl_int error;
  301. if (m_oclKernelComputePartialVolumes) {
  302. for (int k = 0; k < m_ompNumProcessors; ++k) {
  303. error = clReleaseKernel(m_oclKernelComputePartialVolumes[k]);
  304. if (error != CL_SUCCESS) {
  305. if (logger) {
  306. logger->Log("Couldn't release kernal\n");
  307. }
  308. return false;
  309. }
  310. }
  311. delete[] m_oclKernelComputePartialVolumes;
  312. }
  313. if (m_oclKernelComputeSum) {
  314. for (int k = 0; k < m_ompNumProcessors; ++k) {
  315. error = clReleaseKernel(m_oclKernelComputeSum[k]);
  316. if (error != CL_SUCCESS) {
  317. if (logger) {
  318. logger->Log("Couldn't release kernal\n");
  319. }
  320. return false;
  321. }
  322. }
  323. delete[] m_oclKernelComputeSum;
  324. }
  325. if (m_oclQueue) {
  326. for (int k = 0; k < m_ompNumProcessors; ++k) {
  327. error = clReleaseCommandQueue(m_oclQueue[k]);
  328. if (error != CL_SUCCESS) {
  329. if (logger) {
  330. logger->Log("Couldn't release queue\n");
  331. }
  332. return false;
  333. }
  334. }
  335. delete[] m_oclQueue;
  336. }
  337. error = clReleaseProgram(m_oclProgram);
  338. if (error != CL_SUCCESS) {
  339. if (logger) {
  340. logger->Log("Couldn't release program\n");
  341. }
  342. return false;
  343. }
  344. error = clReleaseContext(m_oclContext);
  345. if (error != CL_SUCCESS) {
  346. if (logger) {
  347. logger->Log("Couldn't release context\n");
  348. }
  349. return false;
  350. }
  351. return true;
  352. #else //CL_VERSION_1_1
  353. return false;
  354. #endif //CL_VERSION_1_1
  355. }
  356. void VHACD::ComputePrimitiveSet(const Parameters& params)
  357. {
  358. if (GetCancel()) {
  359. return;
  360. }
  361. m_timer.Tic();
  362. m_stage = "Compute primitive set";
  363. m_operation = "Convert volume to pset";
  364. std::ostringstream msg;
  365. if (params.m_logger) {
  366. msg << "+ " << m_stage << std::endl;
  367. params.m_logger->Log(msg.str().c_str());
  368. }
  369. Update(0.0, 0.0, params);
  370. if (params.m_mode == 0) {
  371. VoxelSet* vset = new VoxelSet;
  372. m_volume->Convert(*vset);
  373. m_pset = vset;
  374. }
  375. else {
  376. TetrahedronSet* tset = new TetrahedronSet;
  377. m_volume->Convert(*tset);
  378. m_pset = tset;
  379. }
  380. delete m_volume;
  381. m_volume = 0;
  382. if (params.m_logger) {
  383. msg.str("");
  384. msg << "\t # primitives " << m_pset->GetNPrimitives() << std::endl;
  385. msg << "\t # inside surface " << m_pset->GetNPrimitivesInsideSurf() << std::endl;
  386. msg << "\t # on surface " << m_pset->GetNPrimitivesOnSurf() << std::endl;
  387. params.m_logger->Log(msg.str().c_str());
  388. }
  389. m_overallProgress = 15.0;
  390. Update(100.0, 100.0, params);
  391. m_timer.Toc();
  392. if (params.m_logger) {
  393. msg.str("");
  394. msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
  395. params.m_logger->Log(msg.str().c_str());
  396. }
  397. }
  398. bool VHACD::Compute(const double* const points, const unsigned int stridePoints, const unsigned int nPoints,
  399. const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, const Parameters& params)
  400. {
  401. return ComputeACD(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params);
  402. }
  403. bool VHACD::Compute(const float* const points, const unsigned int stridePoints, const unsigned int nPoints,
  404. const int* const triangles, const unsigned int strideTriangles, const unsigned int nTriangles, const Parameters& params)
  405. {
  406. return ComputeACD(points, stridePoints, nPoints, triangles, strideTriangles, nTriangles, params);
  407. }
  408. double ComputePreferredCuttingDirection(const PrimitiveSet* const tset, Vec3<double>& dir)
  409. {
  410. double ex = tset->GetEigenValue(AXIS_X);
  411. double ey = tset->GetEigenValue(AXIS_Y);
  412. double ez = tset->GetEigenValue(AXIS_Z);
  413. double vx = (ey - ez) * (ey - ez);
  414. double vy = (ex - ez) * (ex - ez);
  415. double vz = (ex - ey) * (ex - ey);
  416. if (vx < vy && vx < vz) {
  417. double e = ey * ey + ez * ez;
  418. dir[0] = 1.0;
  419. dir[1] = 0.0;
  420. dir[2] = 0.0;
  421. return (e == 0.0) ? 0.0 : 1.0 - vx / e;
  422. }
  423. else if (vy < vx && vy < vz) {
  424. double e = ex * ex + ez * ez;
  425. dir[0] = 0.0;
  426. dir[1] = 1.0;
  427. dir[2] = 0.0;
  428. return (e == 0.0) ? 0.0 : 1.0 - vy / e;
  429. }
  430. else {
  431. double e = ex * ex + ey * ey;
  432. dir[0] = 0.0;
  433. dir[1] = 0.0;
  434. dir[2] = 1.0;
  435. return (e == 0.0) ? 0.0 : 1.0 - vz / e;
  436. }
  437. }
  438. void ComputeAxesAlignedClippingPlanes(const VoxelSet& vset, const short downsampling, SArray<Plane>& planes)
  439. {
  440. const Vec3<short> minV = vset.GetMinBBVoxels();
  441. const Vec3<short> maxV = vset.GetMaxBBVoxels();
  442. Vec3<double> pt;
  443. Plane plane;
  444. const short i0 = minV[0];
  445. const short i1 = maxV[0];
  446. plane.m_a = 1.0;
  447. plane.m_b = 0.0;
  448. plane.m_c = 0.0;
  449. plane.m_axis = AXIS_X;
  450. for (short i = i0; i <= i1; i += downsampling) {
  451. pt = vset.GetPoint(Vec3<double>(i + 0.5, 0.0, 0.0));
  452. plane.m_d = -pt[0];
  453. plane.m_index = i;
  454. planes.PushBack(plane);
  455. }
  456. const short j0 = minV[1];
  457. const short j1 = maxV[1];
  458. plane.m_a = 0.0;
  459. plane.m_b = 1.0;
  460. plane.m_c = 0.0;
  461. plane.m_axis = AXIS_Y;
  462. for (short j = j0; j <= j1; j += downsampling) {
  463. pt = vset.GetPoint(Vec3<double>(0.0, j + 0.5, 0.0));
  464. plane.m_d = -pt[1];
  465. plane.m_index = j;
  466. planes.PushBack(plane);
  467. }
  468. const short k0 = minV[2];
  469. const short k1 = maxV[2];
  470. plane.m_a = 0.0;
  471. plane.m_b = 0.0;
  472. plane.m_c = 1.0;
  473. plane.m_axis = AXIS_Z;
  474. for (short k = k0; k <= k1; k += downsampling) {
  475. pt = vset.GetPoint(Vec3<double>(0.0, 0.0, k + 0.5));
  476. plane.m_d = -pt[2];
  477. plane.m_index = k;
  478. planes.PushBack(plane);
  479. }
  480. }
  481. void ComputeAxesAlignedClippingPlanes(const TetrahedronSet& tset, const short downsampling, SArray<Plane>& planes)
  482. {
  483. const Vec3<double> minV = tset.GetMinBB();
  484. const Vec3<double> maxV = tset.GetMaxBB();
  485. const double scale = tset.GetSacle();
  486. const short i0 = 0;
  487. const short j0 = 0;
  488. const short k0 = 0;
  489. const short i1 = static_cast<short>((maxV[0] - minV[0]) / scale + 0.5);
  490. const short j1 = static_cast<short>((maxV[1] - minV[1]) / scale + 0.5);
  491. const short k1 = static_cast<short>((maxV[2] - minV[2]) / scale + 0.5);
  492. Plane plane;
  493. plane.m_a = 1.0;
  494. plane.m_b = 0.0;
  495. plane.m_c = 0.0;
  496. plane.m_axis = AXIS_X;
  497. for (short i = i0; i <= i1; i += downsampling) {
  498. double x = minV[0] + scale * i;
  499. plane.m_d = -x;
  500. plane.m_index = i;
  501. planes.PushBack(plane);
  502. }
  503. plane.m_a = 0.0;
  504. plane.m_b = 1.0;
  505. plane.m_c = 0.0;
  506. plane.m_axis = AXIS_Y;
  507. for (short j = j0; j <= j1; j += downsampling) {
  508. double y = minV[1] + scale * j;
  509. plane.m_d = -y;
  510. plane.m_index = j;
  511. planes.PushBack(plane);
  512. }
  513. plane.m_a = 0.0;
  514. plane.m_b = 0.0;
  515. plane.m_c = 1.0;
  516. plane.m_axis = AXIS_Z;
  517. for (short k = k0; k <= k1; k += downsampling) {
  518. double z = minV[2] + scale * k;
  519. plane.m_d = -z;
  520. plane.m_index = k;
  521. planes.PushBack(plane);
  522. }
  523. }
  524. void RefineAxesAlignedClippingPlanes(const VoxelSet& vset, const Plane& bestPlane, const short downsampling,
  525. SArray<Plane>& planes)
  526. {
  527. const Vec3<short> minV = vset.GetMinBBVoxels();
  528. const Vec3<short> maxV = vset.GetMaxBBVoxels();
  529. Vec3<double> pt;
  530. Plane plane;
  531. if (bestPlane.m_axis == AXIS_X) {
  532. const short i0 = MAX(minV[0], bestPlane.m_index - downsampling);
  533. const short i1 = MIN(maxV[0], bestPlane.m_index + downsampling);
  534. plane.m_a = 1.0;
  535. plane.m_b = 0.0;
  536. plane.m_c = 0.0;
  537. plane.m_axis = AXIS_X;
  538. for (short i = i0; i <= i1; ++i) {
  539. pt = vset.GetPoint(Vec3<double>(i + 0.5, 0.0, 0.0));
  540. plane.m_d = -pt[0];
  541. plane.m_index = i;
  542. planes.PushBack(plane);
  543. }
  544. }
  545. else if (bestPlane.m_axis == AXIS_Y) {
  546. const short j0 = MAX(minV[1], bestPlane.m_index - downsampling);
  547. const short j1 = MIN(maxV[1], bestPlane.m_index + downsampling);
  548. plane.m_a = 0.0;
  549. plane.m_b = 1.0;
  550. plane.m_c = 0.0;
  551. plane.m_axis = AXIS_Y;
  552. for (short j = j0; j <= j1; ++j) {
  553. pt = vset.GetPoint(Vec3<double>(0.0, j + 0.5, 0.0));
  554. plane.m_d = -pt[1];
  555. plane.m_index = j;
  556. planes.PushBack(plane);
  557. }
  558. }
  559. else {
  560. const short k0 = MAX(minV[2], bestPlane.m_index - downsampling);
  561. const short k1 = MIN(maxV[2], bestPlane.m_index + downsampling);
  562. plane.m_a = 0.0;
  563. plane.m_b = 0.0;
  564. plane.m_c = 1.0;
  565. plane.m_axis = AXIS_Z;
  566. for (short k = k0; k <= k1; ++k) {
  567. pt = vset.GetPoint(Vec3<double>(0.0, 0.0, k + 0.5));
  568. plane.m_d = -pt[2];
  569. plane.m_index = k;
  570. planes.PushBack(plane);
  571. }
  572. }
  573. }
  574. void RefineAxesAlignedClippingPlanes(const TetrahedronSet& tset, const Plane& bestPlane, const short downsampling,
  575. SArray<Plane>& planes)
  576. {
  577. const Vec3<double> minV = tset.GetMinBB();
  578. const Vec3<double> maxV = tset.GetMaxBB();
  579. const double scale = tset.GetSacle();
  580. Plane plane;
  581. if (bestPlane.m_axis == AXIS_X) {
  582. const short i0 = MAX(0, bestPlane.m_index - downsampling);
  583. const short i1 = static_cast<short>(MIN((maxV[0] - minV[0]) / scale + 0.5, bestPlane.m_index + downsampling));
  584. plane.m_a = 1.0;
  585. plane.m_b = 0.0;
  586. plane.m_c = 0.0;
  587. plane.m_axis = AXIS_X;
  588. for (short i = i0; i <= i1; ++i) {
  589. double x = minV[0] + scale * i;
  590. plane.m_d = -x;
  591. plane.m_index = i;
  592. planes.PushBack(plane);
  593. }
  594. }
  595. else if (bestPlane.m_axis == AXIS_Y) {
  596. const short j0 = MAX(0, bestPlane.m_index - downsampling);
  597. const short j1 = static_cast<short>(MIN((maxV[1] - minV[1]) / scale + 0.5, bestPlane.m_index + downsampling));
  598. plane.m_a = 0.0;
  599. plane.m_b = 1.0;
  600. plane.m_c = 0.0;
  601. plane.m_axis = AXIS_Y;
  602. for (short j = j0; j <= j1; ++j) {
  603. double y = minV[1] + scale * j;
  604. plane.m_d = -y;
  605. plane.m_index = j;
  606. planes.PushBack(plane);
  607. }
  608. }
  609. else {
  610. const short k0 = MAX(0, bestPlane.m_index - downsampling);
  611. const short k1 = static_cast<short>(MIN((maxV[2] - minV[2]) / scale + 0.5, bestPlane.m_index + downsampling));
  612. plane.m_a = 0.0;
  613. plane.m_b = 0.0;
  614. plane.m_c = 1.0;
  615. plane.m_axis = AXIS_Z;
  616. for (short k = k0; k <= k1; ++k) {
  617. double z = minV[2] + scale * k;
  618. plane.m_d = -z;
  619. plane.m_index = k;
  620. planes.PushBack(plane);
  621. }
  622. }
  623. }
  624. inline double ComputeLocalConcavity(const double volume, const double volumeCH)
  625. {
  626. return fabs(volumeCH - volume) / volumeCH;
  627. }
  628. inline double ComputeConcavity(const double volume, const double volumeCH, const double volume0)
  629. {
  630. return fabs(volumeCH - volume) / volume0;
  631. }
  632. //#define DEBUG_TEMP
  633. void VHACD::ComputeBestClippingPlane(const PrimitiveSet* inputPSet, const double volume, const SArray<Plane>& planes,
  634. const Vec3<double>& preferredCuttingDirection, const double w, const double alpha, const double beta,
  635. const int convexhullDownsampling, const double progress0, const double progress1, Plane& bestPlane,
  636. double& minConcavity, const Parameters& params)
  637. {
  638. if (GetCancel()) {
  639. return;
  640. }
  641. char msg[256];
  642. size_t nPrimitives = inputPSet->GetNPrimitives();
  643. bool oclAcceleration = (nPrimitives > OCL_MIN_NUM_PRIMITIVES && params.m_oclAcceleration && params.m_mode == 0) ? true : false;
  644. int iBest = -1;
  645. int nPlanes = static_cast<int>(planes.Size());
  646. bool cancel = false;
  647. int done = 0;
  648. double minTotal = MAX_DOUBLE;
  649. double minBalance = MAX_DOUBLE;
  650. double minSymmetry = MAX_DOUBLE;
  651. minConcavity = MAX_DOUBLE;
  652. SArray<Vec3<double> >* chPts = new SArray<Vec3<double> >[2 * m_ompNumProcessors];
  653. Mesh* chs = new Mesh[2 * m_ompNumProcessors];
  654. PrimitiveSet* onSurfacePSet = inputPSet->Create();
  655. inputPSet->SelectOnSurface(onSurfacePSet);
  656. PrimitiveSet** psets = 0;
  657. if (!params.m_convexhullApproximation) {
  658. psets = new PrimitiveSet*[2 * m_ompNumProcessors];
  659. for (int i = 0; i < 2 * m_ompNumProcessors; ++i) {
  660. psets[i] = inputPSet->Create();
  661. }
  662. }
  663. #ifdef CL_VERSION_1_1
  664. // allocate OpenCL data structures
  665. cl_mem voxels;
  666. cl_mem* partialVolumes = 0;
  667. size_t globalSize = 0;
  668. size_t nWorkGroups = 0;
  669. double unitVolume = 0.0;
  670. if (oclAcceleration) {
  671. VoxelSet* vset = (VoxelSet*)inputPSet;
  672. const Vec3<double> minBB = vset->GetMinBB();
  673. const float fMinBB[4] = { (float)minBB[0], (float)minBB[1], (float)minBB[2], 1.0f };
  674. const float fSclae[4] = { (float)vset->GetScale(), (float)vset->GetScale(), (float)vset->GetScale(), 0.0f };
  675. const int nVoxels = (int)nPrimitives;
  676. unitVolume = vset->GetUnitVolume();
  677. nWorkGroups = (nPrimitives + 4 * m_oclWorkGroupSize - 1) / (4 * m_oclWorkGroupSize);
  678. globalSize = nWorkGroups * m_oclWorkGroupSize;
  679. cl_int error;
  680. voxels = clCreateBuffer(m_oclContext,
  681. CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
  682. sizeof(Voxel) * nPrimitives,
  683. vset->GetVoxels(),
  684. &error);
  685. if (error != CL_SUCCESS) {
  686. if (params.m_logger) {
  687. params.m_logger->Log("Couldn't create buffer\n");
  688. }
  689. SetCancel(true);
  690. }
  691. partialVolumes = new cl_mem[m_ompNumProcessors];
  692. for (int i = 0; i < m_ompNumProcessors; ++i) {
  693. partialVolumes[i] = clCreateBuffer(m_oclContext,
  694. CL_MEM_WRITE_ONLY,
  695. sizeof(unsigned int) * 4 * nWorkGroups,
  696. NULL,
  697. &error);
  698. if (error != CL_SUCCESS) {
  699. if (params.m_logger) {
  700. params.m_logger->Log("Couldn't create buffer\n");
  701. }
  702. SetCancel(true);
  703. break;
  704. }
  705. error = clSetKernelArg(m_oclKernelComputePartialVolumes[i], 0, sizeof(cl_mem), &voxels);
  706. error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 1, sizeof(unsigned int), &nVoxels);
  707. error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 3, sizeof(float) * 4, fMinBB);
  708. error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 4, sizeof(float) * 4, &fSclae);
  709. error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 5, sizeof(unsigned int) * 4 * m_oclWorkGroupSize, NULL);
  710. error |= clSetKernelArg(m_oclKernelComputePartialVolumes[i], 6, sizeof(cl_mem), &(partialVolumes[i]));
  711. error |= clSetKernelArg(m_oclKernelComputeSum[i], 0, sizeof(cl_mem), &(partialVolumes[i]));
  712. error |= clSetKernelArg(m_oclKernelComputeSum[i], 2, sizeof(unsigned int) * 4 * m_oclWorkGroupSize, NULL);
  713. if (error != CL_SUCCESS) {
  714. if (params.m_logger) {
  715. params.m_logger->Log("Couldn't kernel atguments \n");
  716. }
  717. SetCancel(true);
  718. }
  719. }
  720. }
  721. #else // CL_VERSION_1_1
  722. oclAcceleration = false;
  723. #endif // CL_VERSION_1_1
  724. #ifdef DEBUG_TEMP
  725. Timer timerComputeCost;
  726. timerComputeCost.Tic();
  727. #endif // DEBUG_TEMP
  728. #if USE_THREAD == 1 && _OPENMP
  729. #pragma omp parallel for
  730. #endif
  731. for (int x = 0; x < nPlanes; ++x) {
  732. int threadID = 0;
  733. #if USE_THREAD == 1 && _OPENMP
  734. threadID = omp_get_thread_num();
  735. #pragma omp flush(cancel)
  736. #endif
  737. if (!cancel) {
  738. //Update progress
  739. if (GetCancel()) {
  740. cancel = true;
  741. #if USE_THREAD == 1 && _OPENMP
  742. #pragma omp flush(cancel)
  743. #endif
  744. }
  745. Plane plane = planes[x];
  746. if (oclAcceleration) {
  747. #ifdef CL_VERSION_1_1
  748. const float fPlane[4] = { (float)plane.m_a, (float)plane.m_b, (float)plane.m_c, (float)plane.m_d };
  749. cl_int error = clSetKernelArg(m_oclKernelComputePartialVolumes[threadID], 2, sizeof(float) * 4, fPlane);
  750. if (error != CL_SUCCESS) {
  751. if (params.m_logger) {
  752. params.m_logger->Log("Couldn't kernel atguments \n");
  753. }
  754. SetCancel(true);
  755. }
  756. error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputePartialVolumes[threadID],
  757. 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL);
  758. if (error != CL_SUCCESS) {
  759. if (params.m_logger) {
  760. params.m_logger->Log("Couldn't run kernel \n");
  761. }
  762. SetCancel(true);
  763. }
  764. int nValues = (int)nWorkGroups;
  765. while (nValues > 1) {
  766. error = clSetKernelArg(m_oclKernelComputeSum[threadID], 1, sizeof(int), &nValues);
  767. if (error != CL_SUCCESS) {
  768. if (params.m_logger) {
  769. params.m_logger->Log("Couldn't kernel atguments \n");
  770. }
  771. SetCancel(true);
  772. }
  773. size_t nWorkGroups = (nValues + m_oclWorkGroupSize - 1) / m_oclWorkGroupSize;
  774. size_t globalSize = nWorkGroups * m_oclWorkGroupSize;
  775. error = clEnqueueNDRangeKernel(m_oclQueue[threadID], m_oclKernelComputeSum[threadID],
  776. 1, NULL, &globalSize, &m_oclWorkGroupSize, 0, NULL, NULL);
  777. if (error != CL_SUCCESS) {
  778. if (params.m_logger) {
  779. params.m_logger->Log("Couldn't run kernel \n");
  780. }
  781. SetCancel(true);
  782. }
  783. nValues = (int)nWorkGroups;
  784. }
  785. #endif // CL_VERSION_1_1
  786. }
  787. Mesh& leftCH = chs[threadID];
  788. Mesh& rightCH = chs[threadID + m_ompNumProcessors];
  789. rightCH.ResizePoints(0);
  790. leftCH.ResizePoints(0);
  791. rightCH.ResizeTriangles(0);
  792. leftCH.ResizeTriangles(0);
  793. // compute convex-hulls
  794. #ifdef TEST_APPROX_CH
  795. double volumeLeftCH1;
  796. double volumeRightCH1;
  797. #endif //TEST_APPROX_CH
  798. if (params.m_convexhullApproximation) {
  799. SArray<Vec3<double> >& leftCHPts = chPts[threadID];
  800. SArray<Vec3<double> >& rightCHPts = chPts[threadID + m_ompNumProcessors];
  801. rightCHPts.Resize(0);
  802. leftCHPts.Resize(0);
  803. onSurfacePSet->Intersect(plane, &rightCHPts, &leftCHPts, convexhullDownsampling * 32);
  804. inputPSet->GetConvexHull().Clip(plane, rightCHPts, leftCHPts);
  805. rightCH.ComputeConvexHull((double*)rightCHPts.Data(), rightCHPts.Size());
  806. leftCH.ComputeConvexHull((double*)leftCHPts.Data(), leftCHPts.Size());
  807. #ifdef TEST_APPROX_CH
  808. Mesh leftCH1;
  809. Mesh rightCH1;
  810. VoxelSet right;
  811. VoxelSet left;
  812. onSurfacePSet->Clip(plane, &right, &left);
  813. right.ComputeConvexHull(rightCH1, convexhullDownsampling);
  814. left.ComputeConvexHull(leftCH1, convexhullDownsampling);
  815. volumeLeftCH1 = leftCH1.ComputeVolume();
  816. volumeRightCH1 = rightCH1.ComputeVolume();
  817. #endif //TEST_APPROX_CH
  818. }
  819. else {
  820. PrimitiveSet* const right = psets[threadID];
  821. PrimitiveSet* const left = psets[threadID + m_ompNumProcessors];
  822. onSurfacePSet->Clip(plane, right, left);
  823. right->ComputeConvexHull(rightCH, convexhullDownsampling);
  824. left->ComputeConvexHull(leftCH, convexhullDownsampling);
  825. }
  826. double volumeLeftCH = leftCH.ComputeVolume();
  827. double volumeRightCH = rightCH.ComputeVolume();
  828. // compute clipped volumes
  829. double volumeLeft = 0.0;
  830. double volumeRight = 0.0;
  831. if (oclAcceleration) {
  832. #ifdef CL_VERSION_1_1
  833. unsigned int volumes[4];
  834. cl_int error = clEnqueueReadBuffer(m_oclQueue[threadID], partialVolumes[threadID], CL_TRUE,
  835. 0, sizeof(unsigned int) * 4, volumes, 0, NULL, NULL);
  836. size_t nPrimitivesRight = volumes[0] + volumes[1] + volumes[2] + volumes[3];
  837. size_t nPrimitivesLeft = nPrimitives - nPrimitivesRight;
  838. volumeRight = nPrimitivesRight * unitVolume;
  839. volumeLeft = nPrimitivesLeft * unitVolume;
  840. if (error != CL_SUCCESS) {
  841. if (params.m_logger) {
  842. params.m_logger->Log("Couldn't read buffer \n");
  843. }
  844. SetCancel(true);
  845. }
  846. #endif // CL_VERSION_1_1
  847. }
  848. else {
  849. inputPSet->ComputeClippedVolumes(plane, volumeRight, volumeLeft);
  850. }
  851. double concavityLeft = ComputeConcavity(volumeLeft, volumeLeftCH, m_volumeCH0);
  852. double concavityRight = ComputeConcavity(volumeRight, volumeRightCH, m_volumeCH0);
  853. double concavity = (concavityLeft + concavityRight);
  854. // compute cost
  855. double balance = alpha * fabs(volumeLeft - volumeRight) / m_volumeCH0;
  856. double d = w * (preferredCuttingDirection[0] * plane.m_a + preferredCuttingDirection[1] * plane.m_b + preferredCuttingDirection[2] * plane.m_c);
  857. double symmetry = beta * d;
  858. double total = concavity + balance + symmetry;
  859. #if USE_THREAD == 1 && _OPENMP
  860. #pragma omp critical
  861. #endif
  862. {
  863. if (total < minTotal || (total == minTotal && x < iBest)) {
  864. minConcavity = concavity;
  865. minBalance = balance;
  866. minSymmetry = symmetry;
  867. bestPlane = plane;
  868. minTotal = total;
  869. iBest = x;
  870. }
  871. ++done;
  872. if (!(done & 127)) // reduce update frequency
  873. {
  874. double progress = done * (progress1 - progress0) / nPlanes + progress0;
  875. Update(m_stageProgress, progress, params);
  876. }
  877. }
  878. }
  879. }
  880. #ifdef DEBUG_TEMP
  881. timerComputeCost.Toc();
  882. printf_s("Cost[%i] = %f\n", nPlanes, timerComputeCost.GetElapsedTime());
  883. #endif // DEBUG_TEMP
  884. #ifdef CL_VERSION_1_1
  885. if (oclAcceleration) {
  886. clReleaseMemObject(voxels);
  887. for (int i = 0; i < m_ompNumProcessors; ++i) {
  888. clReleaseMemObject(partialVolumes[i]);
  889. }
  890. delete[] partialVolumes;
  891. }
  892. #endif // CL_VERSION_1_1
  893. if (psets) {
  894. for (int i = 0; i < 2 * m_ompNumProcessors; ++i) {
  895. delete psets[i];
  896. }
  897. delete[] psets;
  898. }
  899. delete onSurfacePSet;
  900. delete[] chPts;
  901. delete[] chs;
  902. if (params.m_logger) {
  903. sprintf(msg, "\n\t\t\t Best %04i T=%2.6f C=%2.6f B=%2.6f S=%2.6f (%1.1f, %1.1f, %1.1f, %3.3f)\n\n", iBest, minTotal, minConcavity, minBalance, minSymmetry, bestPlane.m_a, bestPlane.m_b, bestPlane.m_c, bestPlane.m_d);
  904. params.m_logger->Log(msg);
  905. }
  906. }
  907. void VHACD::ComputeACD(const Parameters& params)
  908. {
  909. if (GetCancel()) {
  910. return;
  911. }
  912. m_timer.Tic();
  913. m_stage = "Approximate Convex Decomposition";
  914. m_stageProgress = 0.0;
  915. std::ostringstream msg;
  916. if (params.m_logger) {
  917. msg << "+ " << m_stage << std::endl;
  918. params.m_logger->Log(msg.str().c_str());
  919. }
  920. SArray<PrimitiveSet*> parts;
  921. SArray<PrimitiveSet*> inputParts;
  922. SArray<PrimitiveSet*> temp;
  923. inputParts.PushBack(m_pset);
  924. m_pset = 0;
  925. SArray<Plane> planes;
  926. SArray<Plane> planesRef;
  927. int sub = 0;
  928. bool firstIteration = true;
  929. m_volumeCH0 = 1.0;
  930. while (sub++ < params.m_depth && inputParts.Size() > 0 && !m_cancel) {
  931. msg.str("");
  932. msg << "Subdivision level " << sub;
  933. m_operation = msg.str();
  934. if (params.m_logger) {
  935. msg.str("");
  936. msg << "\t Subdivision level " << sub << std::endl;
  937. params.m_logger->Log(msg.str().c_str());
  938. }
  939. double maxConcavity = 0.0;
  940. const size_t nInputParts = inputParts.Size();
  941. Update(m_stageProgress, 0.0, params);
  942. for (size_t p = 0; p < nInputParts && !m_cancel; ++p) {
  943. const double progress0 = p * 100.0 / nInputParts;
  944. const double progress1 = (p + 0.75) * 100.0 / nInputParts;
  945. const double progress2 = (p + 1.00) * 100.0 / nInputParts;
  946. Update(m_stageProgress, progress0, params);
  947. PrimitiveSet* pset = inputParts[p];
  948. inputParts[p] = 0;
  949. double volume = pset->ComputeVolume();
  950. pset->ComputeBB();
  951. pset->ComputePrincipalAxes();
  952. if (params.m_pca) {
  953. pset->AlignToPrincipalAxes();
  954. }
  955. pset->ComputeConvexHull(pset->GetConvexHull());
  956. double volumeCH = fabs(pset->GetConvexHull().ComputeVolume());
  957. if (firstIteration) {
  958. m_volumeCH0 = volumeCH;
  959. }
  960. double concavity = ComputeConcavity(volume, volumeCH, m_volumeCH0);
  961. double error = 1.01 * pset->ComputeMaxVolumeError() / m_volumeCH0;
  962. if (firstIteration) {
  963. firstIteration = false;
  964. }
  965. if (params.m_logger) {
  966. msg.str("");
  967. msg << "\t -> Part[" << p
  968. << "] C = " << concavity
  969. << ", E = " << error
  970. << ", VS = " << pset->GetNPrimitivesOnSurf()
  971. << ", VI = " << pset->GetNPrimitivesInsideSurf()
  972. << std::endl;
  973. params.m_logger->Log(msg.str().c_str());
  974. }
  975. if (concavity > params.m_concavity && concavity > error) {
  976. Vec3<double> preferredCuttingDirection;
  977. double w = ComputePreferredCuttingDirection(pset, preferredCuttingDirection);
  978. planes.Resize(0);
  979. if (params.m_mode == 0) {
  980. VoxelSet* vset = (VoxelSet*)pset;
  981. ComputeAxesAlignedClippingPlanes(*vset, params.m_planeDownsampling, planes);
  982. }
  983. else {
  984. TetrahedronSet* tset = (TetrahedronSet*)pset;
  985. ComputeAxesAlignedClippingPlanes(*tset, params.m_planeDownsampling, planes);
  986. }
  987. if (params.m_logger) {
  988. msg.str("");
  989. msg << "\t\t [Regular sampling] Number of clipping planes " << planes.Size() << std::endl;
  990. params.m_logger->Log(msg.str().c_str());
  991. }
  992. Plane bestPlane;
  993. double minConcavity = MAX_DOUBLE;
  994. ComputeBestClippingPlane(pset,
  995. volume,
  996. planes,
  997. preferredCuttingDirection,
  998. w,
  999. concavity * params.m_alpha,
  1000. concavity * params.m_beta,
  1001. params.m_convexhullDownsampling,
  1002. progress0,
  1003. progress1,
  1004. bestPlane,
  1005. minConcavity,
  1006. params);
  1007. if (!m_cancel && (params.m_planeDownsampling > 1 || params.m_convexhullDownsampling > 1)) {
  1008. planesRef.Resize(0);
  1009. if (params.m_mode == 0) {
  1010. VoxelSet* vset = (VoxelSet*)pset;
  1011. RefineAxesAlignedClippingPlanes(*vset, bestPlane, params.m_planeDownsampling, planesRef);
  1012. }
  1013. else {
  1014. TetrahedronSet* tset = (TetrahedronSet*)pset;
  1015. RefineAxesAlignedClippingPlanes(*tset, bestPlane, params.m_planeDownsampling, planesRef);
  1016. }
  1017. if (params.m_logger) {
  1018. msg.str("");
  1019. msg << "\t\t [Refining] Number of clipping planes " << planesRef.Size() << std::endl;
  1020. params.m_logger->Log(msg.str().c_str());
  1021. }
  1022. ComputeBestClippingPlane(pset,
  1023. volume,
  1024. planesRef,
  1025. preferredCuttingDirection,
  1026. w,
  1027. concavity * params.m_alpha,
  1028. concavity * params.m_beta,
  1029. 1, // convexhullDownsampling = 1
  1030. progress1,
  1031. progress2,
  1032. bestPlane,
  1033. minConcavity,
  1034. params);
  1035. }
  1036. if (GetCancel()) {
  1037. delete pset; // clean up
  1038. break;
  1039. }
  1040. else {
  1041. if (maxConcavity < minConcavity) {
  1042. maxConcavity = minConcavity;
  1043. }
  1044. PrimitiveSet* bestLeft = pset->Create();
  1045. PrimitiveSet* bestRight = pset->Create();
  1046. temp.PushBack(bestLeft);
  1047. temp.PushBack(bestRight);
  1048. pset->Clip(bestPlane, bestRight, bestLeft);
  1049. if (params.m_pca) {
  1050. bestRight->RevertAlignToPrincipalAxes();
  1051. bestLeft->RevertAlignToPrincipalAxes();
  1052. }
  1053. delete pset;
  1054. }
  1055. }
  1056. else {
  1057. if (params.m_pca) {
  1058. pset->RevertAlignToPrincipalAxes();
  1059. }
  1060. parts.PushBack(pset);
  1061. }
  1062. }
  1063. Update(95.0 * (1.0 - maxConcavity) / (1.0 - params.m_concavity), 100.0, params);
  1064. if (GetCancel()) {
  1065. const size_t nTempParts = temp.Size();
  1066. for (size_t p = 0; p < nTempParts; ++p) {
  1067. delete temp[p];
  1068. }
  1069. temp.Resize(0);
  1070. }
  1071. else {
  1072. inputParts = temp;
  1073. temp.Resize(0);
  1074. }
  1075. }
  1076. const size_t nInputParts = inputParts.Size();
  1077. for (size_t p = 0; p < nInputParts; ++p) {
  1078. parts.PushBack(inputParts[p]);
  1079. }
  1080. if (GetCancel()) {
  1081. const size_t nParts = parts.Size();
  1082. for (size_t p = 0; p < nParts; ++p) {
  1083. delete parts[p];
  1084. }
  1085. return;
  1086. }
  1087. m_overallProgress = 90.0;
  1088. Update(m_stageProgress, 100.0, params);
  1089. msg.str("");
  1090. msg << "Generate convex-hulls";
  1091. m_operation = msg.str();
  1092. size_t nConvexHulls = parts.Size();
  1093. if (params.m_logger) {
  1094. msg.str("");
  1095. msg << "+ Generate " << nConvexHulls << " convex-hulls " << std::endl;
  1096. params.m_logger->Log(msg.str().c_str());
  1097. }
  1098. Update(m_stageProgress, 0.0, params);
  1099. m_convexHulls.Resize(0);
  1100. for (size_t p = 0; p < nConvexHulls && !m_cancel; ++p) {
  1101. Update(m_stageProgress, p * 100.0 / nConvexHulls, params);
  1102. m_convexHulls.PushBack(new Mesh);
  1103. parts[p]->ComputeConvexHull(*m_convexHulls[p]);
  1104. size_t nv = m_convexHulls[p]->GetNPoints();
  1105. double x, y, z;
  1106. for (size_t i = 0; i < nv; ++i) {
  1107. Vec3<double>& pt = m_convexHulls[p]->GetPoint(i);
  1108. x = pt[0];
  1109. y = pt[1];
  1110. z = pt[2];
  1111. pt[0] = m_rot[0][0] * x + m_rot[0][1] * y + m_rot[0][2] * z + m_barycenter[0];
  1112. pt[1] = m_rot[1][0] * x + m_rot[1][1] * y + m_rot[1][2] * z + m_barycenter[1];
  1113. pt[2] = m_rot[2][0] * x + m_rot[2][1] * y + m_rot[2][2] * z + m_barycenter[2];
  1114. }
  1115. }
  1116. const size_t nParts = parts.Size();
  1117. for (size_t p = 0; p < nParts; ++p) {
  1118. delete parts[p];
  1119. parts[p] = 0;
  1120. }
  1121. parts.Resize(0);
  1122. if (GetCancel()) {
  1123. const size_t nConvexHulls = m_convexHulls.Size();
  1124. for (size_t p = 0; p < nConvexHulls; ++p) {
  1125. delete m_convexHulls[p];
  1126. }
  1127. m_convexHulls.Clear();
  1128. return;
  1129. }
  1130. m_overallProgress = 95.0;
  1131. Update(100.0, 100.0, params);
  1132. m_timer.Toc();
  1133. if (params.m_logger) {
  1134. msg.str("");
  1135. msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
  1136. params.m_logger->Log(msg.str().c_str());
  1137. }
  1138. }
  1139. void AddPoints(const Mesh* const mesh, SArray<Vec3<double> >& pts)
  1140. {
  1141. const int n = (int)mesh->GetNPoints();
  1142. for (int i = 0; i < n; ++i) {
  1143. pts.PushBack(mesh->GetPoint(i));
  1144. }
  1145. }
  1146. void ComputeConvexHull(const Mesh* const ch1, const Mesh* const ch2, SArray<Vec3<double> >& pts, Mesh* const combinedCH)
  1147. {
  1148. pts.Resize(0);
  1149. AddPoints(ch1, pts);
  1150. AddPoints(ch2, pts);
  1151. btConvexHullComputer ch;
  1152. ch.compute((double*)pts.Data(), 3 * sizeof(double), (int)pts.Size(), -1.0, -1.0);
  1153. combinedCH->ResizePoints(0);
  1154. combinedCH->ResizeTriangles(0);
  1155. for (int v = 0; v < ch.vertices.size(); v++) {
  1156. combinedCH->AddPoint(Vec3<double>(ch.vertices[v].getX(), ch.vertices[v].getY(), ch.vertices[v].getZ()));
  1157. }
  1158. const int nt = ch.faces.size();
  1159. for (int t = 0; t < nt; ++t) {
  1160. const btConvexHullComputer::Edge* sourceEdge = &(ch.edges[ch.faces[t]]);
  1161. int a = sourceEdge->getSourceVertex();
  1162. int b = sourceEdge->getTargetVertex();
  1163. const btConvexHullComputer::Edge* edge = sourceEdge->getNextEdgeOfFace();
  1164. int c = edge->getTargetVertex();
  1165. while (c != a) {
  1166. combinedCH->AddTriangle(Vec3<int>(a, b, c));
  1167. edge = edge->getNextEdgeOfFace();
  1168. b = c;
  1169. c = edge->getTargetVertex();
  1170. }
  1171. }
  1172. }
  1173. void VHACD::MergeConvexHulls(const Parameters& params)
  1174. {
  1175. if (GetCancel()) {
  1176. return;
  1177. }
  1178. m_timer.Tic();
  1179. m_stage = "Merge Convex Hulls";
  1180. std::ostringstream msg;
  1181. if (params.m_logger) {
  1182. msg << "+ " << m_stage << std::endl;
  1183. params.m_logger->Log(msg.str().c_str());
  1184. }
  1185. size_t nConvexHulls = m_convexHulls.Size();
  1186. int iteration = 0;
  1187. if (nConvexHulls > 1 && !m_cancel) {
  1188. const double threshold = params.m_gamma;
  1189. SArray<Vec3<double> > pts;
  1190. Mesh combinedCH;
  1191. // Populate the cost matrix
  1192. size_t idx = 0;
  1193. SArray<float> costMatrix;
  1194. costMatrix.Resize(((nConvexHulls * nConvexHulls) - nConvexHulls) >> 1);
  1195. for (size_t p1 = 1; p1 < nConvexHulls; ++p1) {
  1196. const float volume1 = m_convexHulls[p1]->ComputeVolume();
  1197. for (size_t p2 = 0; p2 < p1; ++p2) {
  1198. ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, &combinedCH);
  1199. costMatrix[idx++] = ComputeConcavity(volume1 + m_convexHulls[p2]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0);
  1200. }
  1201. }
  1202. // Until we cant merge below the maximum cost
  1203. size_t costSize = m_convexHulls.Size();
  1204. while (!m_cancel) {
  1205. msg.str("");
  1206. msg << "Iteration " << iteration++;
  1207. m_operation = msg.str();
  1208. // Search for lowest cost
  1209. float bestCost = (std::numeric_limits<float>::max)();
  1210. const size_t addr = FindMinimumElement(costMatrix.Data(), &bestCost, 0, costMatrix.Size());
  1211. // Check if we should merge these hulls
  1212. if (bestCost >= threshold) {
  1213. break;
  1214. }
  1215. double nr = 1 + (8 * addr);
  1216. const size_t addrI = (static_cast<int>(sqrt(nr)) - 1) >> 1;
  1217. const size_t p1 = addrI + 1;
  1218. const size_t p2 = addr - ((addrI * (addrI + 1)) >> 1);
  1219. assert(p1 >= 0);
  1220. assert(p2 >= 0);
  1221. assert(p1 < costSize);
  1222. assert(p2 < costSize);
  1223. if (params.m_logger) {
  1224. msg.str("");
  1225. msg << "\t\t Merging (" << p1 << ", " << p2 << ") " << bestCost << std::endl
  1226. << std::endl;
  1227. params.m_logger->Log(msg.str().c_str());
  1228. }
  1229. // Make the lowest cost row and column into a new hull
  1230. Mesh* cch = new Mesh;
  1231. ComputeConvexHull(m_convexHulls[p1], m_convexHulls[p2], pts, cch);
  1232. delete m_convexHulls[p2];
  1233. m_convexHulls[p2] = cch;
  1234. delete m_convexHulls[p1];
  1235. std::swap(m_convexHulls[p1], m_convexHulls[m_convexHulls.Size() - 1]);
  1236. m_convexHulls.PopBack();
  1237. costSize = costSize - 1;
  1238. // Calculate costs versus the new hull
  1239. size_t rowIdx = ((p2 - 1) * p2) >> 1;
  1240. const float volume1 = m_convexHulls[p2]->ComputeVolume();
  1241. for (size_t i = 0; (i < p2) && (!m_cancel); ++i) {
  1242. ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH);
  1243. costMatrix[rowIdx++] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0);
  1244. }
  1245. rowIdx += p2;
  1246. for (size_t i = p2 + 1; (i < costSize) && (!m_cancel); ++i) {
  1247. ComputeConvexHull(m_convexHulls[p2], m_convexHulls[i], pts, &combinedCH);
  1248. costMatrix[rowIdx] = ComputeConcavity(volume1 + m_convexHulls[i]->ComputeVolume(), combinedCH.ComputeVolume(), m_volumeCH0);
  1249. rowIdx += i;
  1250. assert(rowIdx >= 0);
  1251. }
  1252. // Move the top column in to replace its space
  1253. const size_t erase_idx = ((costSize - 1) * costSize) >> 1;
  1254. if (p1 < costSize) {
  1255. rowIdx = (addrI * p1) >> 1;
  1256. size_t top_row = erase_idx;
  1257. for (size_t i = 0; i < p1; ++i) {
  1258. if (i != p2) {
  1259. costMatrix[rowIdx] = costMatrix[top_row];
  1260. }
  1261. ++rowIdx;
  1262. ++top_row;
  1263. }
  1264. ++top_row;
  1265. rowIdx += p1;
  1266. for (size_t i = p1 + 1; i < (costSize + 1); ++i) {
  1267. costMatrix[rowIdx] = costMatrix[top_row++];
  1268. rowIdx += i;
  1269. assert(rowIdx >= 0);
  1270. }
  1271. }
  1272. costMatrix.Resize(erase_idx);
  1273. }
  1274. }
  1275. m_overallProgress = 99.0;
  1276. Update(100.0, 100.0, params);
  1277. m_timer.Toc();
  1278. if (params.m_logger) {
  1279. msg.str("");
  1280. msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
  1281. params.m_logger->Log(msg.str().c_str());
  1282. }
  1283. }
  1284. void SimplifyConvexHull(Mesh* const ch, const size_t nvertices, const double minVolume)
  1285. {
  1286. if (nvertices <= 4) {
  1287. return;
  1288. }
  1289. ICHull icHull;
  1290. icHull.AddPoints(ch->GetPointsBuffer(), ch->GetNPoints());
  1291. icHull.Process((unsigned int)nvertices, minVolume);
  1292. TMMesh& mesh = icHull.GetMesh();
  1293. const size_t nT = mesh.GetNTriangles();
  1294. const size_t nV = mesh.GetNVertices();
  1295. ch->ResizePoints(nV);
  1296. ch->ResizeTriangles(nT);
  1297. mesh.GetIFS(ch->GetPointsBuffer(), ch->GetTrianglesBuffer());
  1298. }
  1299. void VHACD::SimplifyConvexHulls(const Parameters& params)
  1300. {
  1301. if (m_cancel || params.m_maxNumVerticesPerCH < 4) {
  1302. return;
  1303. }
  1304. m_timer.Tic();
  1305. m_stage = "Simplify convex-hulls";
  1306. m_operation = "Simplify convex-hulls";
  1307. std::ostringstream msg;
  1308. const size_t nConvexHulls = m_convexHulls.Size();
  1309. if (params.m_logger) {
  1310. msg << "+ Simplify " << nConvexHulls << " convex-hulls " << std::endl;
  1311. params.m_logger->Log(msg.str().c_str());
  1312. }
  1313. Update(0.0, 0.0, params);
  1314. for (size_t i = 0; i < nConvexHulls && !m_cancel; ++i) {
  1315. if (params.m_logger) {
  1316. msg.str("");
  1317. msg << "\t\t Simplify CH[" << std::setfill('0') << std::setw(5) << i << "] " << m_convexHulls[i]->GetNPoints() << " V, " << m_convexHulls[i]->GetNTriangles() << " T" << std::endl;
  1318. params.m_logger->Log(msg.str().c_str());
  1319. }
  1320. SimplifyConvexHull(m_convexHulls[i], params.m_maxNumVerticesPerCH, m_volumeCH0 * params.m_minVolumePerCH);
  1321. }
  1322. m_overallProgress = 100.0;
  1323. Update(100.0, 100.0, params);
  1324. m_timer.Toc();
  1325. if (params.m_logger) {
  1326. msg.str("");
  1327. msg << "\t time " << m_timer.GetElapsedTime() / 1000.0 << "s" << std::endl;
  1328. params.m_logger->Log(msg.str().c_str());
  1329. }
  1330. }
  1331. }