iMSTK
Interactive Medical Simulation Toolkit
imstkSurfaceMeshTextureProject.cpp
1 /*
2 ** This file is part of the Interactive Medical Simulation Toolkit (iMSTK)
3 ** iMSTK is distributed under the Apache License, Version 2.0.
4 ** See accompanying NOTICE for details.
5 */
6 
7 #include "imstkSurfaceMeshTextureProject.h"
8 #include "imstkLogger.h"
9 #include "imstkParallelUtils.h"
10 #include "imstkSurfaceMesh.h"
11 #include "imstkVecDataArray.h"
12 
13 namespace imstk
14 {
15 static Vec3d
16 closestPointOnTriangle(const Vec3d& p, const Vec3d& a, const Vec3d& b, const Vec3d& c, int& caseType)
17 {
18  const Vec3d ab = b - a;
19  const Vec3d ac = c - a;
20  const Vec3d ap = p - a;
21 
22  const double d1 = ab.dot(ap);
23  const double d2 = ac.dot(ap);
24  if (d1 <= 0.0 && d2 <= 0.0)
25  {
26  caseType = 0;
27  return a; // barycentric coordinates (1,0,0)
28  }
29 
30  // Check if P in vertex region outside B
31  const Vec3d bp = p - b;
32  const double d3 = ab.dot(bp);
33  const double d4 = ac.dot(bp);
34  if (d3 >= 0.0 && d4 <= d3)
35  {
36  caseType = 1;
37  return b; // barycentric coordinates (0,1,0)
38  }
39  // Check if P in edge region of AB, if so return projection of P onto AB
40  const double vc = d1 * d4 - d3 * d2;
41  if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0)
42  {
43  caseType = 3;
44  double v = d1 / (d1 - d3);
45  return a + v * ab; // barycentric coordinates (1-v,v,0)
46  }
47 
48  // Check if P in vertex region outside C
49  const Vec3d cp = p - c;
50  const double d5 = ab.dot(cp);
51  const double d6 = ac.dot(cp);
52  if (d6 >= 0.0 && d5 <= d6)
53  {
54  caseType = 2;
55  return c; // barycentric coordinates (0,0,1)
56  }
57 
58  // Check if P in edge region of AC, if so return projection of P onto AC
59  const double vb = d5 * d2 - d1 * d6;
60  if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0)
61  {
62  caseType = 5;
63  double w = d2 / (d2 - d6);
64  return a + w * ac; // barycentric coordinates (1-w,0,w)
65  }
66 
67  // Check if P in edge region of BC, if so return projection of P onto BC
68  const double va = d3 * d6 - d5 * d4;
69  if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0)
70  {
71  caseType = 4;
72  double w = (d4 - d3) / ((d4 - d3) + (d5 - d6));
73  return b + w * (c - b); // barycentric coordinates (0,1-w,w)
74  }
75 
76  // P inside face region. Compute Q through its barycentric coordinates (u,v,w)
77  const double denom = 1.0 / (va + vb + vc);
78  const double v = vb * denom;
79  const double w = vc * denom;
80  caseType = 6;
81  return a + ab * v + ac * w; // = u*a + v*b + w*c, u = va * denom = 1.0f-v-w
82 }
83 
84 template<typename T>
85 static T
86 baryInterpolate(T v1, T v2, T v3, Vec3d uvw)
87 {
88  return v1 * uvw[0] + v2 * uvw[1] + v3 * uvw[2];
89 }
90 
91 SurfaceMeshTextureProject::SurfaceMeshTextureProject()
92 {
94  setRequiredInputType<SurfaceMesh>(0);
95  setRequiredInputType<SurfaceMesh>(1);
96 
98  setOutput(std::make_shared<SurfaceMesh>(), 0);
99 }
100 
101 void
102 SurfaceMeshTextureProject::setSourceMesh(std::shared_ptr<SurfaceMesh> srcMesh)
103 {
104  setInput(srcMesh, 0);
105 }
106 
107 void
108 SurfaceMeshTextureProject::setDestMesh(std::shared_ptr<SurfaceMesh> destMesh)
109 {
110  setInput(destMesh, 1);
111 }
112 
113 std::shared_ptr<SurfaceMesh>
115 {
116  return std::dynamic_pointer_cast<SurfaceMesh>(getOutput(0));
117 }
118 
119 void
121 {
122  std::shared_ptr<SurfaceMesh> inputSrcMesh = std::dynamic_pointer_cast<SurfaceMesh>(getInput(0));
123  std::shared_ptr<SurfaceMesh> inputDestMesh = std::dynamic_pointer_cast<SurfaceMesh>(getInput(1));
124  std::shared_ptr<SurfaceMesh> outputDestMesh = std::dynamic_pointer_cast<SurfaceMesh>(getOutput(0));
125  *outputDestMesh = *inputDestMesh->clone();
126 
127  if (inputSrcMesh == nullptr || inputDestMesh == nullptr)
128  {
129  LOG(WARNING) << "Missing input surface mesh";
130  return;
131  }
132 
133  std::shared_ptr<VecDataArray<double, 3>> srcVerticesPtr = inputSrcMesh->getVertexPositions();
134  const VecDataArray<double, 3>& srcVertices = *srcVerticesPtr;
135  std::shared_ptr<VecDataArray<int, 3>> srcCellsPtr = inputSrcMesh->getCells();
136  const VecDataArray<int, 3>& srcCells = *srcCellsPtr;
137  std::shared_ptr<VecDataArray<float, 2>> srcTCoordsPtr = inputSrcMesh->getVertexTCoords();
138  if (srcTCoordsPtr == nullptr)
139  {
140  LOG(WARNING) << "inputSrcMesh does not have texture coordinates";
141  return;
142  }
143  const VecDataArray<float, 2>& inputTexCoords = *srcTCoordsPtr;
144 
145  std::shared_ptr<VecDataArray<double, 3>> destVerticesPtr = inputDestMesh->getVertexPositions();
146  const VecDataArray<double, 3>& destVertices = *destVerticesPtr;
147  auto outputDestTCoordsPtr = std::make_shared<VecDataArray<float, 2>>(destVertices.size());
148  outputDestMesh->setVertexTCoords(inputSrcMesh->getActiveVertexTCoords(), outputDestTCoordsPtr);
149  VecDataArray<float, 2>& outputTexCoords = *outputDestTCoordsPtr;
150 
151  // For every vertex of the destination mesh
152  for (int i = 0; i < destVertices.size(); i++)
153  {
154  const Vec3d& pos = destVertices[i];
155  double minDistSqr = IMSTK_DOUBLE_MAX;
156  int closestCellI = -1;
157  Vec3d closestPt = Vec3d::Zero();
158 
159  // Find the closest point on the other mesh
160  for (int j = 0; j < srcCells.size(); j++)
161  {
162  const Vec3i& cell = srcCells[j];
163  const Vec3d& a = srcVertices[cell[0]];
164  const Vec3d& b = srcVertices[cell[1]];
165  const Vec3d& c = srcVertices[cell[2]];
166 
167  int caseType = -1;
168  const Vec3d ptOnTri = closestPointOnTriangle(pos, a, b, c, caseType);
169  const double sqrDist = (ptOnTri - pos).squaredNorm();
170  if (sqrDist < minDistSqr)
171  {
172  minDistSqr = sqrDist;
173  closestPt = ptOnTri;
174  closestCellI = j;
175  }
176  }
177 
178  // Compute interpolate value
179  const Vec3i& closestCell = srcCells[closestCellI];
180  const Vec2f& va = inputTexCoords[closestCell[0]];
181  const Vec2f& vb = inputTexCoords[closestCell[1]];
182  const Vec2f& vc = inputTexCoords[closestCell[2]];
183  const Vec3d& a = srcVertices[closestCell[0]];
184  const Vec3d& b = srcVertices[closestCell[1]];
185  const Vec3d& c = srcVertices[closestCell[2]];
186  const Vec3d uvw = baryCentric(closestPt, a, b, c);
187  const Vec2f val = uvw[0] * va + uvw[1] * vb + uvw[2] * vc;
188  outputTexCoords[i] = val;
189  }
190 }
191 } // namespace imstk
std::shared_ptr< Geometry > getInput(size_t port=0) const
Returns input geometry given port, returns nullptr if doesn&#39;t exist.
Compound Geometry.
void setDestMesh(std::shared_ptr< SurfaceMesh > destMesh)
The mesh to recieve the attribute.
void requestUpdate() override
Users can implement this for the logic to be run.
std::unique_ptr< SurfaceMesh > clone()
Polymorphic clone, hides the declaration in superclass return own type.
void setNumOutputPorts(const size_t numPorts)
Get/Set the amount of output ports.
std::shared_ptr< Geometry > getOutput(size_t port=0) const
Returns output geometry given port, returns nullptr if doesn&#39;t exist.
void setInput(std::shared_ptr< Geometry > inputGeometry, size_t port=0)
Set the input at the port.
Represents a set of triangles & vertices via an array of Vec3d double vertices & Vec3i integer indice...
void setSourceMesh(std::shared_ptr< SurfaceMesh > surfMesh)
The mesh with attribute to put on the other.
void setOutput(std::shared_ptr< Geometry > inputGeometry, const size_t port=0)
Set the output at the port.
void setNumInputPorts(const size_t numPorts)
Get/Set the amount of input ports.
std::shared_ptr< SurfaceMesh > getOutputMesh()
destMesh copy with the attribute