SurfaceExtractorMC.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/MarchingCubes.h>
  9. #include <Mathematics/Image3.h>
  10. #include <Mathematics/UniqueVerticesTriangles.h>
  11. #include <Mathematics/Vector3.h>
  12. namespace WwiseGTE
  13. {
  14. template <typename Real>
  15. class SurfaceExtractorMC : public MarchingCubes
  16. {
  17. public:
  18. // Construction and destruction.
  19. virtual ~SurfaceExtractorMC()
  20. {
  21. }
  22. SurfaceExtractorMC(Image3<Real> const& image)
  23. :
  24. mImage(image)
  25. {
  26. }
  27. // Object copies are not allowed.
  28. SurfaceExtractorMC() = delete;
  29. SurfaceExtractorMC(SurfaceExtractorMC const&) = delete;
  30. SurfaceExtractorMC const& operator=(SurfaceExtractorMC const&) = delete;
  31. struct Mesh
  32. {
  33. // All members are set to zeros.
  34. Mesh()
  35. {
  36. std::array<Real, 3> zero = { (Real)0, (Real)0, (Real)0 };
  37. std::fill(vertices.begin(), vertices.end(), zero);
  38. }
  39. Topology topology;
  40. std::array<Vector3<Real>, MAX_VERTICES> vertices;
  41. };
  42. // Extract the triangle mesh approximating F = 0 for a single voxel.
  43. // The input function values must be stored as
  44. // F[0] = function(0,0,0), F[4] = function(0,0,1),
  45. // F[1] = function(1,0,0), F[5] = function(1,0,1),
  46. // F[2] = function(0,1,0), F[6] = function(0,1,1),
  47. // F[3] = function(1,1,0), F[7] = function(1,1,1).
  48. // Thus, F[k] = function(k & 1, (k & 2) >> 1, (k & 4) >> 2).
  49. // The return value is 'true' iff the F[] values are all nonzero.
  50. // If they are not, the returned 'mesh' has no vertices and no
  51. // triangles--as if F[] had all positive or all negative values.
  52. bool Extract(std::array<Real, 8> const& F, Mesh& mesh) const
  53. {
  54. int entry = 0;
  55. for (int i = 0, mask = 1; i < 8; ++i, mask <<= 1)
  56. {
  57. if (F[i] < (Real)0)
  58. {
  59. entry |= mask;
  60. }
  61. else if (F[i] == (Real)0)
  62. {
  63. return false;
  64. }
  65. }
  66. mesh.topology = GetTable(entry);
  67. for (int i = 0; i < mesh.topology.numVertices; ++i)
  68. {
  69. int j0 = mesh.topology.vpair[i][0];
  70. int j1 = mesh.topology.vpair[i][1];
  71. Real corner0[3];
  72. corner0[0] = static_cast<Real>(j0 & 1);
  73. corner0[1] = static_cast<Real>((j0 & 2) >> 1);
  74. corner0[2] = static_cast<Real>((j0 & 4) >> 2);
  75. Real corner1[3];
  76. corner1[0] = static_cast<Real>(j1 & 1);
  77. corner1[1] = static_cast<Real>((j1 & 2) >> 1);
  78. corner1[2] = static_cast<Real>((j1 & 4) >> 2);
  79. Real invDenom = ((Real)1) / (F[j0] - F[j1]);
  80. for (int k = 0; k < 3; ++k)
  81. {
  82. Real numer = F[j0] * corner1[k] - F[j1] * corner0[k];
  83. mesh.vertices[i][k] = numer * invDenom;
  84. }
  85. }
  86. return true;
  87. }
  88. // Extract the triangle mesh approximating F = 0 for all the voxels in
  89. // a 3D image. The input image must be stored in a 1-dimensional
  90. // array with lexicographical order; that is, image[i] corresponds to
  91. // voxel location (x,y,z) where i = x + bound0 * (y + bound1 * z).
  92. // The output 'indices' consists indices.size()/3 triangles, each a
  93. // triple of indices into 'vertices'
  94. bool Extract(Real level, std::vector<Vector3<Real>>& vertices, std::vector<int>& indices) const
  95. {
  96. vertices.clear();
  97. indices.clear();
  98. for (int z = 0; z + 1 < mImage.GetDimension(2); ++z)
  99. {
  100. for (int y = 0; y + 1 < mImage.GetDimension(1); ++y)
  101. {
  102. for (int x = 0; x + 1 < mImage.GetDimension(0); ++x)
  103. {
  104. std::array<size_t, 8> corners;
  105. mImage.GetCorners(x, y, z, corners);
  106. std::array<Real, 8> F;
  107. for (int k = 0; k < 8; ++k)
  108. {
  109. F[k] = mImage[corners[k]] - level;
  110. }
  111. Mesh mesh;
  112. if (Extract(F, mesh))
  113. {
  114. int vbase = static_cast<int>(vertices.size());
  115. for (int i = 0; i < mesh.topology.numVertices; ++i)
  116. {
  117. Vector3<float> position = mesh.vertices[i];
  118. position[0] += static_cast<Real>(x);
  119. position[1] += static_cast<Real>(y);
  120. position[2] += static_cast<Real>(z);
  121. vertices.push_back(position);
  122. }
  123. for (int i = 0; i < mesh.topology.numTriangles; ++i)
  124. {
  125. for (int j = 0; j < 3; ++j)
  126. {
  127. indices.push_back(vbase + mesh.topology.itriple[i][j]);
  128. }
  129. }
  130. }
  131. else
  132. {
  133. vertices.clear();
  134. indices.clear();
  135. return false;
  136. }
  137. }
  138. }
  139. }
  140. return true;
  141. }
  142. // The extraction has duplicate vertices on edges shared by voxels.
  143. // This function will eliminate the duplication.
  144. void MakeUnique(std::vector<Vector3<Real>>& vertices, std::vector<int>& indices) const
  145. {
  146. std::vector<Vector3<Real>> outVertices;
  147. std::vector<int> outIndices;
  148. UniqueVerticesTriangles<Vector3<Real>>(vertices, indices, outVertices, outIndices);
  149. vertices = std::move(outVertices);
  150. indices = std::move(outIndices);
  151. }
  152. // The extraction does not use any topological information about the
  153. // level surface. The triangles can be a mixture of clockwise-ordered
  154. // and counterclockwise-ordered. This function is an attempt to give
  155. // the triangles a consistent ordering by selecting a normal in
  156. // approximately the same direction as the average gradient at the
  157. // vertices (when sameDir is true), or in the opposite direction (when
  158. // sameDir is false). This might not always produce a consistent
  159. // order, but is fast. A consistent order can be computed if you
  160. // build a table of vertex, edge, and face adjacencies, but the
  161. // resulting data structure is somewhat expensive to process to
  162. // reorient triangles.
  163. void OrientTriangles(std::vector<Vector3<Real>> const& vertices, std::vector<int>& indices, bool sameDir) const
  164. {
  165. int const numTriangles = static_cast<int>(indices.size() / 3);
  166. int* triangle = indices.data();
  167. for (int t = 0; t < numTriangles; ++t, triangle += 3)
  168. {
  169. // Get triangle vertices.
  170. Vector3<Real> v0 = vertices[triangle[0]];
  171. Vector3<Real> v1 = vertices[triangle[1]];
  172. Vector3<Real> v2 = vertices[triangle[2]];
  173. // Construct triangle normal based on current orientation.
  174. Vector3<Real> edge1 = v1 - v0;
  175. Vector3<Real> edge2 = v2 - v0;
  176. Vector3<Real> normal = Cross(edge1, edge2);
  177. // Get the image gradient at the vertices.
  178. Vector3<Real> gradient0 = GetGradient(v0);
  179. Vector3<Real> gradient1 = GetGradient(v1);
  180. Vector3<Real> gradient2 = GetGradient(v2);
  181. // Compute the average gradient.
  182. Vector3<Real> gradientAvr = (gradient0 + gradient1 + gradient2) / (Real)3;
  183. // Compute the dot product of normal and average gradient.
  184. Real dot = Dot(gradientAvr, normal);
  185. // Choose triangle orientation based on gradient direction.
  186. if (sameDir)
  187. {
  188. if (dot < (Real)0)
  189. {
  190. // Wrong orientation, reorder it.
  191. std::swap(triangle[1], triangle[2]);
  192. }
  193. }
  194. else
  195. {
  196. if (dot > (Real)0)
  197. {
  198. // Wrong orientation, reorder it.
  199. std::swap(triangle[1], triangle[2]);
  200. }
  201. }
  202. }
  203. }
  204. // Compute vertex normals for the mesh.
  205. void ComputeNormals(std::vector<Vector3<Real>> const& vertices, std::vector<int> const& indices,
  206. std::vector<Vector3<Real>>& normals) const
  207. {
  208. // Maintain a running sum of triangle normals at each vertex.
  209. int const numVertices = static_cast<int>(vertices.size());
  210. normals.resize(numVertices);
  211. Vector3<Real> zero = Vector3<Real>::Zero();
  212. std::fill(normals.begin(), normals.end(), zero);
  213. int const numTriangles = static_cast<int>(indices.size() / 3);
  214. int const* current = indices.data();
  215. for (int i = 0; i < numTriangles; ++i)
  216. {
  217. int i0 = *current++;
  218. int i1 = *current++;
  219. int i2 = *current++;
  220. Vector3<Real> v0 = vertices[i0];
  221. Vector3<Real> v1 = vertices[i1];
  222. Vector3<Real> v2 = vertices[i2];
  223. // Construct triangle normal.
  224. Vector3<Real> edge1 = v1 - v0;
  225. Vector3<Real> edge2 = v2 - v0;
  226. Vector3<Real> normal = Cross(edge1, edge2);
  227. // Maintain the sum of normals at each vertex.
  228. normals[i0] += normal;
  229. normals[i1] += normal;
  230. normals[i2] += normal;
  231. }
  232. // The normal vector storage was used to accumulate the sum of
  233. // triangle normals. Now these vectors must be rescaled to be
  234. // unit length.
  235. for (auto& normal : normals)
  236. {
  237. Normalize(normal);
  238. }
  239. }
  240. protected:
  241. Vector3<Real> GetGradient(Vector3<Real> position) const
  242. {
  243. int x = static_cast<int>(std::floor(position[0]));
  244. if (x < 0 || x >= mImage.GetDimension(0) - 1)
  245. {
  246. return Vector3<Real>::Zero();
  247. }
  248. int y = static_cast<int>(std::floor(position[1]));
  249. if (y < 0 || y >= mImage.GetDimension(1) - 1)
  250. {
  251. return Vector3<Real>::Zero();
  252. }
  253. int z = static_cast<int>(std::floor(position[2]));
  254. if (z < 0 || z >= mImage.GetDimension(2) - 1)
  255. {
  256. return Vector3<Real>::Zero();
  257. }
  258. position[0] -= static_cast<Real>(x);
  259. position[1] -= static_cast<Real>(y);
  260. position[2] -= static_cast<Real>(z);
  261. Real oneMX = (Real)1 - position[0];
  262. Real oneMY = (Real)1 - position[1];
  263. Real oneMZ = (Real)1 - position[2];
  264. // Get image values at corners of voxel.
  265. std::array<size_t, 8> corners;
  266. mImage.GetCorners(x, y, z, corners);
  267. Real f000 = mImage[corners[0]];
  268. Real f100 = mImage[corners[1]];
  269. Real f010 = mImage[corners[2]];
  270. Real f110 = mImage[corners[3]];
  271. Real f001 = mImage[corners[4]];
  272. Real f101 = mImage[corners[5]];
  273. Real f011 = mImage[corners[6]];
  274. Real f111 = mImage[corners[7]];
  275. Vector3<Real> gradient;
  276. Real tmp0 = oneMY * (f100 - f000) + position[1] * (f110 - f010);
  277. Real tmp1 = oneMY * (f101 - f001) + position[1] * (f111 - f011);
  278. gradient[0] = oneMZ * tmp0 + position[2] * tmp1;
  279. tmp0 = oneMX * (f010 - f000) + position[0] * (f110 - f100);
  280. tmp1 = oneMX * (f011 - f001) + position[0] * (f111 - f101);
  281. gradient[1] = oneMZ * tmp0 + position[2] * tmp1;
  282. tmp0 = oneMX * (f001 - f000) + position[0] * (f101 - f100);
  283. tmp1 = oneMX * (f011 - f010) + position[0] * (f111 - f110);
  284. gradient[2] = oneMY * tmp0 + position[1] * tmp1;
  285. return gradient;
  286. }
  287. Image3<Real> const& mImage;
  288. };
  289. }