GteGenerateMeshUV.h
Go to the documentation of this file.
1 // David Eberly, Geometric Tools, Redmond WA 98052
2 // Copyright (c) 1998-2017
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
6 // File Version: 3.0.0 (2016/06/19)
7 
8 #pragma once
9 
11 #include <LowLevel/GteWrapper.h>
12 #include <Mathematics/GteVector2.h>
13 #include <Mathematics/GteVector3.h>
16 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU)
20 #endif
21 #include <algorithm>
22 #include <cstring>
23 #include <functional>
24 #include <limits>
25 #include <memory>
26 #include <set>
27 #include <thread>
28 #include <vector>
29 
30 // This class is an implementation of the barycentric mapping algorithm
31 // described in Section 5.3 of the book
32 // Polygon Mesh Processing
33 // Mario Botsch, Leif Kobbelt, Mark Pauly, Pierre Alliez, Bruno Levy
34 // AK Peters, Ltd., Natick MA, 2010
35 // It uses the mean value weights described in Section 5.3.1 to allow the mesh
36 // geometry to influence the texture coordinate generation, and it uses
37 // Gauss-Seidel iteration to solve the sparse linear system. The authors'
38 // advice is that the Gauss-Seidel approach works well for at most about 5000
39 // vertices, presumably the convergence rate degrading as the number of
40 // vertices increases.
41 //
42 // The algorithm implemented here has an additional preprocessing step that
43 // computes a topological distance transform of the vertices. The boundary
44 // texture coordinates are propagated inward by updating the vertices in
45 // topological distance order, leading to fast convergence for large numbers
46 // of vertices.
47 
48 namespace gte
49 {
50 
52 {
53 protected:
54  static std::string const msGLSLSource;
55  static std::string const msHLSLSource;
56  static std::string const* msSource[];
57 };
58 
59 template <typename Real>
61 {
62 public:
64  {
65  public:
66  virtual ~UVComputeModel();
67 
69 
70  UVComputeModel(unsigned int inNumThreads,
71  std::function<void(unsigned int)> const* inProgress);
72 
73 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU)
74  UVComputeModel(unsigned int inNumThreads,
75  std::shared_ptr<GraphicsEngine> const& inEngine,
76  std::shared_ptr<ProgramFactory> const& inFactory,
77  std::function<void(unsigned int)> const* inProgress);
78 #endif
79 
80  std::function<void(unsigned int)> const* progress;
81  };
82 
83  // Construction.
84  GenerateMeshUV(std::shared_ptr<UVComputeModel> const& cmodel);
85 
86  // The incoming mesh must be edge-triangle manifold and have rectangle
87  // topology (simply connected, closed polyline boundary). The arrays
88  // 'vertices' and 'tcoords' must both have 'numVertices' elements. Set
89  // 'useSquareTopology' to true for the generated coordinates to live
90  // in the uv-square [0,1]^2. Set it to false for the generated
91  // coordinates to live in a convex polygon that inscribes the uv-disk
92  // of center (1/2,1/2) and radius 1/2.
93  void operator()(unsigned int numIterations, bool useSquareTopology,
94  int numVertices, Vector3<Real> const* vertices, int numIndices,
95  int const* indices, Vector2<Real>* tcoords);
96 
97 private:
98  void TopologicalVertexDistanceTransform();
99  void AssignBoundaryTextureCoordinatesSquare();
100  void AssignBoundaryTextureCoordinatesDisk();
101  void ComputeMeanValueWeights();
102  void SolveSystem(unsigned int numIterations);
103  void SolveSystemCPUSingle(unsigned int numIterations);
104  void SolveSystemCPUMultiple(unsigned int numIterations);
105 
106  // Convenience members that store the input parameters to operator().
110 
111  // The edge-triangle manifold graph, where each edge is shared by at most
112  // two triangles.
114 
115  // The mVertexInfo array stores -1 for the interior vertices. For a
116  // boundary edge <v0,v1> that is counterclockwise, mVertexInfo[v0] = v1,
117  // which gives us an orded boundary polyline.
118  enum { INTERIOR_VERTEX = -1 };
119  std::vector<int> mVertexInfo;
120  int mNumBoundaryEdges, mBoundaryStart;
122  std::set<std::shared_ptr<Edge>> mInteriorEdges;
123 
124  // The vertex graph required to set up a sparse linear system of equations
125  // to determine the texture coordinates.
126  struct Vertex
127  {
128  // The topological distance from the boundary of the mesh.
129  int distance;
130 
131  // The value range[0] is the index into mVertexGraphData for the first
132  // adjacent vertex. The value range[1] is the number of adjacent
133  // vertices.
134  std::array<int, 2> range;
135 
136 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU) && defined(GTE_DEV_OPENGL)
137  int _padding; // GLSL will map the ivec3 Vertex to ivec4 in an array
138 #endif
139  };
140  std::vector<Vertex> mVertexGraph;
141  std::vector<std::pair<int, Real>> mVertexGraphData;
142 
143  // The vertices are listed in the order determined by a topological distance
144  // transform. Boundary vertices have 'distance' 0. Any vertices that are
145  // not boundary vertices but are edge-adjacent to boundary vertices have
146  // 'distance' 1. Neighbors of those have distance '2', and so on. The
147  // mOrderedVertices array stores distance-0 vertices first, distance-1
148  // vertices second, and so on.
149  std::vector<int> mOrderedVertices;
150 
151  std::shared_ptr<UVComputeModel> mCModel;
152 
153 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU)
154  // Support for solving the sparse linear system on the GPU.
155  void SolveSystemGPU(unsigned int numIterations);
156  std::shared_ptr<ComputeProgram> mSolveSystem;
157  std::shared_ptr<ConstantBuffer> mBoundBuffer;
158  std::shared_ptr<StructuredBuffer> mVGBuffer;
159  std::shared_ptr<StructuredBuffer> mVGDBuffer;
160  std::shared_ptr<StructuredBuffer> mOVBuffer;
161  std::shared_ptr<StructuredBuffer> mTCoordsBuffer[2];
162 #endif
163 };
164 
165 
166 template <typename Real>
168 {
169 }
170 
171 template <typename Real>
173  :
174  progress(nullptr)
175 {
176 }
177 
178 template <typename Real>
180  std::function<void(unsigned int)> const* inProgress)
181  :
182  ComputeModel(inNumThreads),
183  progress(inProgress)
184 {
185 }
186 
187 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU)
188 
189 template <typename Real>
191  std::shared_ptr<GraphicsEngine> const& inEngine,
192  std::shared_ptr<ProgramFactory> const& inFactory,
193  std::function<void(unsigned int)> const* inProgress)
194  :
195  ComputeModel(inNumThreads, inEngine, inFactory),
196  progress(inProgress)
197 {
198 }
199 
200 #endif
201 
202 template <typename Real>
203 GenerateMeshUV<Real>::GenerateMeshUV(std::shared_ptr<UVComputeModel> const& cmodel)
204  :
205  mCModel(cmodel),
206  mNumVertices(0),
207  mVertices(nullptr),
208  mTCoords(nullptr),
210  mBoundaryStart(0)
211 {
212 }
213 
214 template <typename Real>
215 void GenerateMeshUV<Real>::operator()(unsigned int numIterations,
216  bool useSquareTopology, int numVertices, Vector3<Real> const* vertices,
217  int numIndices, int const* indices, Vector2<Real>* tcoords)
218 {
219  // Ensure that numIterations is even, which avoids having a memory
220  // copy from the temporary ping-pong buffer to 'tcoords'.
221  if (numIterations & 1)
222  {
223  ++numIterations;
224  }
225 
226  mNumVertices = numVertices;
227  mVertices = vertices;
228  mTCoords = tcoords;
229 
230  // The linear system solver has a first pass to initialize the texture
231  // coordinates to ensure the Gauss-Seidel iteration converges rapidly.
232  // This requires the texture coordinates all start as (-1,-1).
233  for (int i = 0; i < numVertices; ++i)
234  {
235  mTCoords[i][0] = (Real)-1;
236  mTCoords[i][1] = (Real)-1;
237  }
238 
239  // Create the manifold mesh data structure.
240  mGraph.Clear();
241  int const numTriangles = numIndices / 3;
242  for (int t = 0; t < numTriangles; ++t)
243  {
244  int v0 = *indices++;
245  int v1 = *indices++;
246  int v2 = *indices++;
247  mGraph.Insert(v0, v1, v2);
248  }
249 
251 
252  if (useSquareTopology)
253  {
255  }
256  else
257  {
259  }
260 
262  SolveSystem(numIterations);
263 }
264 
265 template <typename Real>
267 {
268  // Initialize the graph information.
269  mVertexInfo.resize(mNumVertices);
270  std::fill(mVertexInfo.begin(), mVertexInfo.end(), INTERIOR_VERTEX);
271  mVertexGraph.resize(mNumVertices);
272  mVertexGraphData.resize(2 * mGraph.GetEdges().size());
273  std::pair<int, Real> initialData = std::make_pair(-1, (Real)-1);
274  std::fill(mVertexGraphData.begin(), mVertexGraphData.end(), initialData);
276  mInteriorEdges.clear();
277  mNumBoundaryEdges = 0;
278  mBoundaryStart = std::numeric_limits<int>::max();
279 
280  // Count the number of adjacent vertices for each vertex. For data sets
281  // with a large number of vertices, this is a preprocessing step to avoid
282  // a dynamic data structure that has a large number of std:map objects
283  // that take a very long time to destroy when a debugger is attached to
284  // the executable. Instead, we allocate a single array that stores all
285  // the adjacency information. It is also necessary to bundle the data
286  // this way for a GPU version of the algorithm.
287  std::vector<int> numAdjacencies(mNumVertices);
288  std::fill(numAdjacencies.begin(), numAdjacencies.end(), 0);
289 
290  for (auto const& element : mGraph.GetEdges())
291  {
292  ++numAdjacencies[element.first.V[0]];
293  ++numAdjacencies[element.first.V[1]];
294 
295  if (element.second->T[1].lock())
296  {
297  // This is an interior edge.
298  mInteriorEdges.insert(element.second);
299  }
300  else
301  {
302  // This is a boundary edge. Determine the ordering of the
303  // vertex indices to make the edge counterclockwise.
305  int v0 = element.second->V[0], v1 = element.second->V[1];
306  auto tri = element.second->T[0].lock();
307  int i;
308  for (i = 0; i < 3; ++i)
309  {
310  int v2 = tri->V[i];
311  if (v2 != v0 && v2 != v1)
312  {
313  // The vertex is opposite the boundary edge.
314  v0 = tri->V[(i + 1) % 3];
315  v1 = tri->V[(i + 2) % 3];
316  mVertexInfo[v0] = v1;
317  mBoundaryStart = std::min(mBoundaryStart, v0);
318  break;
319  }
320  }
321  }
322  }
323 
324  // Set the range data for each vertex.
325  for (int vIndex = 0, aIndex = 0; vIndex < mNumVertices; ++vIndex)
326  {
327  int numAdjacent = numAdjacencies[vIndex];
328  mVertexGraph[vIndex].range = { { aIndex, numAdjacent } };
329  aIndex += numAdjacent;
330 
331 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU) && defined(GTE_DEV_OPENGL)
332  // Initialize the padding, even though it is unused.
333  mVertexGraph[vIndex]._padding = 0;
334 #endif
335  }
336 
337  // Compute a topological distance transform of the vertices.
338  std::set<int> currFront;
339  for (auto const& element : mGraph.GetEdges())
340  {
341  int v0 = element.second->V[0], v1 = element.second->V[1];
342  for (int i = 0; i < 2; ++i)
343  {
344  if (mVertexInfo[v0] == INTERIOR_VERTEX)
345  {
346  mVertexGraph[v0].distance = -1;
347  }
348  else
349  {
350  mVertexGraph[v0].distance = 0;
351  currFront.insert(v0);
352  }
353 
354  // Insert v1 into the first available slot of the adjacency array.
355  std::array<int, 2> range = mVertexGraph[v0].range;
356  for (int j = 0; j < range[1]; ++j)
357  {
358  std::pair<int, Real>& data = mVertexGraphData[range[0] + j];
359  if (data.second == (Real)-1)
360  {
361  data.first = v1;
362  data.second = (Real)0;
363  break;
364  }
365  }
366 
367  std::swap(v0, v1);
368  }
369  }
370 
371  // Use a breadth-first search to propagate the distance information.
372  int currDistance = 0, nextDistance = 1;
373  size_t numFrontVertices = currFront.size();
374  std::copy(currFront.begin(), currFront.end(), mOrderedVertices.begin());
375  while (currFront.size() > 0)
376  {
377  std::set<int> nextFront;
378  for (auto v : currFront)
379  {
380  std::array<int, 2> range = mVertexGraph[v].range;
381  auto* current = &mVertexGraphData[range[0]];
382  for (int j = 0; j < range[1]; ++j, ++current)
383  {
384  int a = current->first;
385  if (mVertexGraph[a].distance == -1)
386  {
387  mVertexGraph[a].distance = nextDistance;
388  nextFront.insert(a);
389  }
390  }
391  }
392  std::copy(nextFront.begin(), nextFront.end(), mOrderedVertices.begin() + numFrontVertices);
393  numFrontVertices += nextFront.size();
394  currFront = std::move(nextFront);
395  currDistance = nextDistance++;
396  }
397 }
398 
399 template <typename Real>
401 {
402  // Map the boundary of the mesh to the unit square [0,1]^2. The selection
403  // of square vertices is such that the relative distances between boundary
404  // vertices and the relative distances between polygon vertices is
405  // preserved, except that the four corners of the square are required to
406  // have boundary points mapped to them. The first boundary point has an
407  // implied distance of zero. The value distance[i] is the length of the
408  // boundary polyline from vertex 0 to vertex i+1.
409  std::vector<Real> distance(mNumBoundaryEdges);
410  Real total = (Real)0;
411  int v0 = mBoundaryStart, v1, i;
412  for (i = 0; i < mNumBoundaryEdges; ++i)
413  {
414  v1 = mVertexInfo[v0];
415  total += Length(mVertices[v1] - mVertices[v0]);
416  distance[i] = total;
417  v0 = v1;
418  }
419 
420  Real invTotal = ((Real)1) / total;
421  for (auto& d : distance)
422  {
423  d *= invTotal;
424  }
425 
426  auto begin = distance.begin(), end = distance.end();
427  int endYMin = (int)(std::lower_bound(begin, end, (Real)0.25) - begin);
428  int endXMax = (int)(std::lower_bound(begin, end, (Real)0.50) - begin);
429  int endYMax = (int)(std::lower_bound(begin, end, (Real)0.75) - begin);
430  int endXMin = (int)distance.size() - 1;
431 
432  // The first polygon vertex is (0,0). The remaining vertices are chosen
433  // counterclockwise around the square.
434  v0 = mBoundaryStart;
435  mTCoords[v0][0] = (Real)0;
436  mTCoords[v0][1] = (Real)0;
437  for (i = 0; i < endYMin; ++i)
438  {
439  v1 = mVertexInfo[v0];
440  mTCoords[v1][0] = distance[i] * (Real)4;
441  mTCoords[v1][1] = (Real)0;
442  v0 = v1;
443  }
444 
445  v1 = mVertexInfo[v0];
446  mTCoords[v1][0] = (Real)1;
447  mTCoords[v1][1] = (Real)0;
448  v0 = v1;
449  for (++i; i < endXMax; ++i)
450  {
451  v1 = mVertexInfo[v0];
452  mTCoords[v1][0] = (Real)1;
453  mTCoords[v1][1] = distance[i] * (Real)4 - (Real)1;
454  v0 = v1;
455  }
456 
457  v1 = mVertexInfo[v0];
458  mTCoords[v1][0] = (Real)1;
459  mTCoords[v1][1] = (Real)1;
460  v0 = v1;
461  for (++i; i < endYMax; ++i)
462  {
463  v1 = mVertexInfo[v0];
464  mTCoords[v1][0] = (Real)3 - distance[i] * (Real)4;
465  mTCoords[v1][1] = (Real)1;
466  v0 = v1;
467  }
468 
469  v1 = mVertexInfo[v0];
470  mTCoords[v1][0] = (Real)0;
471  mTCoords[v1][1] = (Real)1;
472  v0 = v1;
473  for (++i; i < endXMin; ++i)
474  {
475  v1 = mVertexInfo[v0];
476  mTCoords[v1][0] = (Real)0;
477  mTCoords[v1][1] = (Real)4 - distance[i] * (Real)4;
478  v0 = v1;
479  }
480 }
481 
482 template <typename Real>
484 {
485  // Map the boundary of the mesh to a convex polygon. The selection of
486  // convex polygon vertices is such that the relative distances between
487  // boundary vertices and the relative distances between polygon vertices
488  // is preserved. The first boundary point has an implied distance of
489  // zero. The value distance[i] is the length of the boundary polyline
490  // from vertex 0 to vertex i+1.
491  std::vector<Real> distance(mNumBoundaryEdges);
492  Real total = (Real)0;
493  int v0 = mBoundaryStart;
494  for (int i = 0; i < mNumBoundaryEdges; ++i)
495  {
496  int v1 = mVertexInfo[v0];
497  total += Length(mVertices[v1] - mVertices[v0]);
498  distance[i] = total;
499  v0 = v1;
500  }
501 
502  // The convex polygon lives in [0,1]^2 and inscribes a circle with center
503  // (1/2,1/2) and radius 1/2. The polygon center is not necessarily the
504  // circle center! This is the case when a boundary edge has length larger
505  // than half the total length of the boundary polyline; we do not expect
506  // such data for our meshes. The first polygon vertex is (1/2,0). The
507  // remaining vertices are chosen counterclockwise around the polygon.
508  Real multiplier = ((Real)GTE_C_TWO_PI) / total;
509  v0 = mBoundaryStart;
510  mTCoords[v0][0] = (Real)1;
511  mTCoords[v0][1] = (Real)0.5;
512  for (int i = 1; i < mNumBoundaryEdges; ++i)
513  {
514  int v1 = mVertexInfo[v0];
515  Real angle = multiplier * distance[i - 1];
516  mTCoords[v1][0] = (cos(angle) + (Real)1) * (Real)0.5;
517  mTCoords[v1][1] = (sin(angle) + (Real)1) * (Real)0.5;
518  v0 = v1;
519  }
520 }
521 
522 template <typename Real>
524 {
525  for (auto const& edge : mInteriorEdges)
526  {
527  int v0 = edge->V[0], v1 = edge->V[1];
528  for (int i = 0; i < 2; ++i)
529  {
530  // Compute the direction from X0 to X1 and compute the length
531  // of the edge (X0,X1).
532  Vector3<Real> X0 = mVertices[v0];
533  Vector3<Real> X1 = mVertices[v1];
534  Vector3<Real> X1mX0 = X1 - X0;
535  Real x1mx0length = Normalize(X1mX0);
536  Real weight;
537  if (x1mx0length >(Real)0)
538  {
539  // Compute the weight for X0 associated with X1.
540  weight = (Real)0;
541  for (int j = 0; j < 2; ++j)
542  {
543  // Find the vertex of triangle T[j] opposite edge <X0,X1>.
544  auto tri = edge->T[j].lock();
545  int k;
546  for (k = 0; k < 3; ++k)
547  {
548  int v2 = tri->V[k];
549  if (v2 != v0 && v2 != v1)
550  {
551  Vector3<Real> X2 = mVertices[v2];
552  Vector3<Real> X2mX0 = X2 - X0;
553  Real x2mx0Length = Normalize(X2mX0);
554  if (x2mx0Length >(Real)0)
555  {
556  Real dot = Dot(X2mX0, X1mX0);
557  Real cs = std::min(std::max(dot, (Real)-1), (Real)1);
558  Real angle = acos(cs);
559  weight += tan(angle * (Real)0.5);
560  }
561  else
562  {
563  weight += (Real)1;
564  }
565  break;
566  }
567  }
568  }
569  weight /= x1mx0length;
570  }
571  else
572  {
573  weight = (Real)1;
574  }
575 
576  std::array<int, 2> range = mVertexGraph[v0].range;
577  for (int j = 0; j < range[1]; ++j)
578  {
579  std::pair<int, Real>& data = mVertexGraphData[range[0] + j];
580  if (data.first == v1)
581  {
582  data.second = weight;
583  }
584  }
585 
586  std::swap(v0, v1);
587  }
588  }
589 }
590 
591 template <typename Real>
592 void GenerateMeshUV<Real>::SolveSystem(unsigned int numIterations)
593 {
594  // On the first pass, average only neighbors whose texture coordinates
595  // have been computed. This is a good initial guess for the linear system
596  // and leads to relatively fast convergence of the Gauss-Seidel iterates.
597  Real zero = (Real)0;
598  for (int i = mNumBoundaryEdges; i < mNumVertices; ++i)
599  {
600  int v0 = mOrderedVertices[i];
601  std::array<int, 2> range = mVertexGraph[v0].range;
602  auto const* current = &mVertexGraphData[range[0]];
603  Vector2<Real> tcoord{ zero, zero };
604  Real weight, weightSum = zero;
605  for (int j = 0; j < range[1]; ++j, ++current)
606  {
607  int v1 = current->first;
608  if (mTCoords[v1][0] != -1.0f)
609  {
610  weight = current->second;
611  weightSum += weight;
612  tcoord += weight * mTCoords[v1];
613  }
614  }
615  tcoord /= weightSum;
616  mTCoords[v0] = tcoord;
617  }
618 
619 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU)
620  if (mCModel->engine)
621  {
622  SolveSystemGPU(numIterations);
623  }
624  else
625 #endif
626  {
627  if (mCModel->numThreads > 1)
628  {
629  SolveSystemCPUMultiple(numIterations);
630  }
631  else
632  {
633  SolveSystemCPUSingle(numIterations);
634  }
635  }
636 }
637 
638 template <typename Real>
639 void GenerateMeshUV<Real>::SolveSystemCPUSingle(unsigned int numIterations)
640 {
641  // Use ping-pong buffers for the texture coordinates.
642  std::vector<Vector2<Real>> tcoords(mNumVertices);
643  size_t numBytes = mNumVertices * sizeof(Vector2<Real>);
644  Memcpy(&tcoords[0], mTCoords, numBytes);
645  Vector2<Real>* inTCoords = mTCoords;
646  Vector2<Real>* outTCoords = &tcoords[0];
647 
648  // The value numIterations is even, so we always swap an even number
649  // of times. This ensures that on exit from the loop, outTCoords is
650  // tcoords.
651  for (unsigned int i = 1; i <= numIterations; ++i)
652  {
653  if (mCModel->progress)
654  {
655  (*mCModel->progress)(i);
656  }
657 
658  for (int j = mNumBoundaryEdges; j < mNumVertices; ++j)
659  {
660  int v0 = mOrderedVertices[j];
661  std::array<int, 2> range = mVertexGraph[v0].range;
662  auto const* current = &mVertexGraphData[range[0]];
663  Vector2<Real> tcoord{ (Real)0, (Real)0 };
664  Real weight, weightSum = (Real)0;
665  for (int k = 0; k < range[1]; ++k, ++current)
666  {
667  int v1 = current->first;
668  weight = current->second;
669  weightSum += weight;
670  tcoord += weight * inTCoords[v1];
671  }
672  tcoord /= weightSum;
673  outTCoords[v0] = tcoord;
674  }
675 
676  std::swap(inTCoords, outTCoords);
677  }
678 }
679 
680 template <typename Real>
681 void GenerateMeshUV<Real>::SolveSystemCPUMultiple(unsigned int numIterations)
682 {
683  // Use ping-pong buffers for the texture coordinates.
684  std::vector<Vector2<Real>> tcoords(mNumVertices);
685  size_t numBytes = mNumVertices * sizeof(Vector2<Real>);
686  Memcpy(&tcoords[0], mTCoords, numBytes);
687  Vector2<Real>* inTCoords = mTCoords;
688  Vector2<Real>* outTCoords = &tcoords[0];
689 
690  // Partition the data for multiple threads.
691  int numV = mNumVertices - mNumBoundaryEdges;
692  int numVPerThread = numV / mCModel->numThreads;
693  std::vector<int> vmin(mCModel->numThreads), vmax(mCModel->numThreads);
694  for (unsigned int t = 0; t < mCModel->numThreads; ++t)
695  {
696  vmin[t] = mNumBoundaryEdges + t * numVPerThread;
697  vmax[t] = vmin[t] + numVPerThread - 1;
698  }
699  vmax[mCModel->numThreads - 1] = mNumVertices - 1;
700 
701  // The value numIterations is even, so we always swap an even number
702  // of times. This ensures that on exit from the loop, outTCoords is
703  // tcoords.
704  for (unsigned int i = 1; i <= numIterations; ++i)
705  {
706  if (mCModel->progress)
707  {
708  (*mCModel->progress)(i);
709  }
710 
711  // Execute Gauss-Seidel iterations in multiple threads.
712  std::vector<std::thread> process(mCModel->numThreads);
713  for (unsigned int t = 0; t < mCModel->numThreads; ++t)
714  {
715  process[t] = std::thread([this, t, &vmin, &vmax, inTCoords,
716  outTCoords]()
717  {
718  for (int j = vmin[t]; j <= vmax[t]; ++j)
719  {
720  int v0 = mOrderedVertices[j];
721  std::array<int, 2> range = mVertexGraph[v0].range;
722  auto const* current = &mVertexGraphData[range[0]];
723  Vector2<Real> tcoord{ (Real)0, (Real)0 };
724  Real weight, weightSum = (Real)0;
725  for (int k = 0; k < range[1]; ++k, ++current)
726  {
727  int v1 = current->first;
728  weight = current->second;
729  weightSum += weight;
730  tcoord += weight * inTCoords[v1];
731  }
732  tcoord /= weightSum;
733  outTCoords[v0] = tcoord;
734  }
735  });
736  }
737 
738  // Wait for all threads to finish.
739  for (unsigned int t = 0; t < mCModel->numThreads; ++t)
740  {
741  process[t].join();
742  }
743 
744  std::swap(inTCoords, outTCoords);
745  }
746 }
747 
748 #if defined(GTE_COMPUTE_MODEL_ALLOW_GPGPU)
749 
750 template <typename Real>
751 void GenerateMeshUV<Real>::SolveSystemGPU(unsigned int numIterations)
752 {
753  mCModel->factory->defines.Set("NUM_X_THREADS", 8);
754  mCModel->factory->defines.Set("NUM_Y_THREADS", 8);
755  if (std::numeric_limits<Real>::max() == std::numeric_limits<float>::max())
756  {
757  mCModel->factory->defines.Set("Real", "float");
758 #if defined(GTE_DEV_OPENGL)
759  mCModel->factory->defines.Set("Real2", "vec2");
760 #else
761  mCModel->factory->defines.Set("Real2", "float2");
762 #endif
763  }
764  else
765  {
766  mCModel->factory->defines.Set("Real", "double");
767 #if defined(GTE_DEV_OPENGL)
768  mCModel->factory->defines.Set("Real2", "dvec2");
769 #else
770  mCModel->factory->defines.Set("Real2", "double2");
771 #endif
772  }
773 
774  // TODO: Test mSolveSystem for null and respond accordingly.
775  int api = mCModel->factory->GetAPI();
776  mSolveSystem = mCModel->factory->CreateFromSource(*msSource[api]);
777  std::shared_ptr<ComputeShader> cshader = mSolveSystem->GetCShader();
778 
779  // Compute the number of thread groups.
780  int numInputs = mNumVertices - mNumBoundaryEdges;
781  Real factor0 = ceil(sqrt((Real)numInputs));
782  Real factor1 = ceil((Real)numInputs / factor0);
783  int xElements = static_cast<int>(factor0);
784  int yElements = static_cast<int>(factor1);
785  int xRem = (xElements % 8);
786  if (xRem > 0)
787  {
788  xElements += 8 - xRem;
789  }
790  int yRem = (yElements % 8);
791  if (yRem > 0)
792  {
793  yElements += 8 - yRem;
794  }
795  unsigned int numXGroups = xElements / 8;
796  unsigned int numYGroups = yElements / 8;
797 
798  mBoundBuffer = std::make_shared<ConstantBuffer>(4 * sizeof(int), false);
799  int* data = mBoundBuffer->Get<int>();
800  data[0] = xElements;
801  data[1] = yElements;
802  data[2] = mNumBoundaryEdges;
803  data[3] = numInputs;
804  cshader->Set("Bounds", mBoundBuffer);
805 
806  unsigned int const vgSize = static_cast<unsigned int>(mVertexGraph.size());
807  mVGBuffer = std::make_shared<StructuredBuffer>(vgSize, sizeof(Vertex));
808  Memcpy(mVGBuffer->GetData(), &mVertexGraph[0], mVGBuffer->GetNumBytes());
809  cshader->Set("vertexGraph", mVGBuffer);
810 
811  unsigned int const vgdSize = static_cast<unsigned int>(mVertexGraphData.size());
812  mVGDBuffer = std::make_shared<StructuredBuffer>(vgdSize, sizeof(std::pair<int, Real>));
813  Memcpy(mVGDBuffer->GetData(), &mVertexGraphData[0], mVGDBuffer->GetNumBytes());
814  cshader->Set("vertexGraphData", mVGDBuffer);
815 
816  unsigned int const ovSize = static_cast<unsigned int>(mOrderedVertices.size());
817  mOVBuffer = std::make_shared<StructuredBuffer>(ovSize, sizeof(int));
818  Memcpy(mOVBuffer->GetData(), &mOrderedVertices[0], mOVBuffer->GetNumBytes());
819  cshader->Set("orderedVertices", mOVBuffer);
820 
821  for (int j = 0; j < 2; ++j)
822  {
823  mTCoordsBuffer[j] = std::make_shared<StructuredBuffer>(mNumVertices, sizeof(Vector2<Real>));
824  mTCoordsBuffer[j]->SetUsage(Resource::SHADER_OUTPUT);
825  Memcpy(mTCoordsBuffer[j]->GetData(), mTCoords, mTCoordsBuffer[j]->GetNumBytes());
826  }
827  mTCoordsBuffer[0]->SetCopyType(Resource::COPY_STAGING_TO_CPU);
828 
829  // The value numIterations is even, so we always swap an even number
830  // of times. This ensures that on exit from the loop,
831  // mTCoordsBuffer[0] has the final output.
832  for (unsigned int i = 1; i <= numIterations; ++i)
833  {
834  if (mCModel->progress)
835  {
836  (*mCModel->progress)(i);
837  }
838 
839  cshader->Set("inTCoords", mTCoordsBuffer[0]);
840  cshader->Set("outTCoords", mTCoordsBuffer[1]);
841  mCModel->engine->Execute(mSolveSystem, numXGroups, numYGroups, 1);
842  std::swap(mTCoordsBuffer[0], mTCoordsBuffer[1]);
843  }
844 
845  mCModel->engine->CopyGpuToCpu(mTCoordsBuffer[0]);
846  Memcpy(mTCoords, mTCoordsBuffer[0]->GetData(), mTCoordsBuffer[0]->GetNumBytes());
847 }
848 
849 #endif
850 
851 }
void AssignBoundaryTextureCoordinatesSquare()
static std::string const msHLSLSource
EMap const & GetEdges() const
COPY_STAGING_TO_CPU
Definition: GteResource.h:55
std::vector< int > mVertexInfo
std::set< std::shared_ptr< Edge > > mInteriorEdges
std::vector< int > mOrderedVertices
GLfloat GLfloat v1
Definition: glcorearb.h:812
static std::string const * msSource[]
GLfloat angle
Definition: glext.h:6466
void SolveSystem(unsigned int numIterations)
std::vector< std::pair< int, Real > > mVertexGraphData
std::function< void(unsigned int)> const * progress
std::array< int, 2 > range
GLuint GLuint GLfloat weight
Definition: glext.h:9668
GLsizei GLsizei GLfloat distance
Definition: glext.h:9704
Vector3< Real > const * mVertices
ETManifoldMesh::Edge Edge
void SolveSystemCPUMultiple(unsigned int numIterations)
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1217
void operator()(unsigned int numIterations, bool useSquareTopology, int numVertices, Vector3< Real > const *vertices, int numIndices, int const *indices, Vector2< Real > *tcoords)
GenerateMeshUV(std::shared_ptr< UVComputeModel > const &cmodel)
GLuint GLuint end
Definition: glcorearb.h:470
GLsizei const GLchar *const * string
Definition: glcorearb.h:809
GLsizei GLenum const void * indices
Definition: glcorearb.h:401
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
GLboolean * data
Definition: glcorearb.h:126
void AssignBoundaryTextureCoordinatesDisk()
DualQuaternion< Real > Dot(DualQuaternion< Real > const &d0, DualQuaternion< Real > const &d1)
virtual std::shared_ptr< Triangle > Insert(int v0, int v1, int v2)
Real Normalize(GVector< Real > &v, bool robust=false)
Definition: GteGVector.h:454
GLfloat v0
Definition: glcorearb.h:811
GLdouble GLdouble t
Definition: glext.h:239
std::shared_ptr< UVComputeModel > mCModel
static std::string const msGLSLSource
DualQuaternion< Real > Length(DualQuaternion< Real > const &d, bool robust=false)
const GLdouble * v
Definition: glcorearb.h:832
GLfloat GLfloat GLfloat v2
Definition: glcorearb.h:813
std::vector< Vertex > mVertexGraph
void Memcpy(void *target, void const *source, size_t count)
Definition: GteWrapper.cpp:16
GLfloat f
Definition: glcorearb.h:1921
GLenum GLint * range
Definition: glcorearb.h:1920
void SolveSystemCPUSingle(unsigned int numIterations)
void TopologicalVertexDistanceTransform()
#define GTE_C_TWO_PI
Definition: GteConstants.h:20
Vector2< Real > * mTCoords


geometric_tools_engine
Author(s): Yijiang Huang
autogenerated on Thu Jul 18 2019 03:59:59