PolyVox  0.2.0
Open source voxel management library
MeshDecimator.inl
Go to the documentation of this file.
1 /*******************************************************************************
2 Copyright (c) 2005-2009 David Williams
3 
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7 
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11 
12  1. The origin of this software must not be misrepresented; you must not
13  claim that you wrote the original software. If you use this software
14  in a product, an acknowledgment in the product documentation would be
15  appreciated but is not required.
16 
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19 
20  3. This notice may not be removed or altered from any source
21  distribution.
22 *******************************************************************************/
23 
24 namespace PolyVox
25 {
36  template <typename VertexType>
37  MeshDecimator<VertexType>::MeshDecimator(const SurfaceMesh<VertexType>* pInputMesh, SurfaceMesh<VertexType>* pOutputMesh, float fEdgeCollapseThreshold)
38  :m_pInputMesh(pInputMesh)
39  ,m_pOutputMesh(pOutputMesh)
40  ,m_fMinDotProductForCollapse(fEdgeCollapseThreshold)
41  {
42  *m_pOutputMesh = *m_pInputMesh;
43  }
44 
45  template <typename VertexType>
47  {
48  //Sanity check.
49  if((m_pOutputMesh->m_vecVertices.empty()) || (m_pOutputMesh->m_vecTriangleIndices.empty()))
50  {
51  return;
52  }
53 
54  buildConnectivityData();
55  fillInitialVertexMetadata(m_vecInitialVertexMetadata);
56 
57  uint32_t noOfEdgesCollapsed;
58  do
59  {
60  noOfEdgesCollapsed = performDecimationPass(m_fMinDotProductForCollapse);
61  m_pOutputMesh->removeDegenerateTris();
62  if(noOfEdgesCollapsed > 0)
63  {
64  //Build the connectivity data for the next pass. If this is slow, then look
65  //at adjusting it (based on vertex mapper?) rather than bulding from scratch.
66  buildConnectivityData();
67  }
68  }while(noOfEdgesCollapsed > 0);
69 
70  m_pOutputMesh->removeUnusedVertices();
71 
72  //Decimation will have invalidated LOD levels.
73  m_pOutputMesh->m_vecLodRecords.clear();
74  LodRecord lodRecord;
75  lodRecord.beginIndex = 0;
76  lodRecord.endIndex = m_pOutputMesh->getNoOfIndices();
77  m_pOutputMesh->m_vecLodRecords.push_back(lodRecord);
78  }
79 
80  template <typename VertexType>
82  {
83  //Build a list of all the triangles, complete with face normals.
84  m_vecTriangles.clear();
85  m_vecTriangles.resize(m_pOutputMesh->m_vecTriangleIndices.size() / 3);
86  for(uint32_t triCt = 0; triCt < m_vecTriangles.size(); triCt++)
87  {
88  m_vecTriangles[triCt].v0 = m_pOutputMesh->m_vecTriangleIndices[triCt * 3 + 0];
89  m_vecTriangles[triCt].v1 = m_pOutputMesh->m_vecTriangleIndices[triCt * 3 + 1];
90  m_vecTriangles[triCt].v2 = m_pOutputMesh->m_vecTriangleIndices[triCt * 3 + 2];
91 
92  Vector3DFloat v0Pos = m_pOutputMesh->m_vecVertices[m_vecTriangles[triCt].v0].position;
93  Vector3DFloat v1Pos = m_pOutputMesh->m_vecVertices[m_vecTriangles[triCt].v1].position;
94  Vector3DFloat v2Pos = m_pOutputMesh->m_vecVertices[m_vecTriangles[triCt].v2].position;
95 
96  Vector3DFloat v0v1 = v1Pos - v0Pos;
97  Vector3DFloat v0v2 = v2Pos - v0Pos;
98  Vector3DFloat normal = v0v1.cross(v0v2);
99  normal.normalise();
100 
101  m_vecTriangles[triCt].normal = normal;
102  }
103 
104  //For each vertex, determine which triangles are using it.
105  trianglesUsingVertex.clear();
106  trianglesUsingVertex.resize(m_pOutputMesh->m_vecVertices.size());
107  for(uint32_t ct = 0; ct < trianglesUsingVertex.size(); ct++)
108  {
109  trianglesUsingVertex[ct].reserve(6);
110  }
111  for(uint32_t ct = 0; ct < m_vecTriangles.size(); ct++)
112  {
113  trianglesUsingVertex[m_vecTriangles[ct].v0].push_back(ct);
114  trianglesUsingVertex[m_vecTriangles[ct].v1].push_back(ct);
115  trianglesUsingVertex[m_vecTriangles[ct].v2].push_back(ct);
116  }
117  }
118 
119  template <typename VertexType>
120  uint32_t MeshDecimator<VertexType>::performDecimationPass(float /*m_fMinDotProductForCollapse*/)
121  {
122  // Count how many edges we have collapsed
123  uint32_t noOfEdgesCollapsed = 0;
124 
125  // The vertex mapper track whick vertices collapse onto which.
126  vertexMapper.clear();
127  vertexMapper.resize(m_pOutputMesh->m_vecVertices.size());
128 
129  // Once a vertex is involved in a collapse (either because it
130  // moves onto a different vertex, or because a different vertex
131  // moves onto it) it is forbidden to take part in another collapse
132  // this pass. We enforce this by setting the vertex locked flag.
133  vertexLocked.clear();
134  vertexLocked.resize(m_pOutputMesh->m_vecVertices.size());
135 
136  // Initialise the vectors
137  for(uint32_t ct = 0; ct < m_pOutputMesh->m_vecVertices.size(); ct++)
138  {
139  // Initiall all vertices points to themselves
140  vertexMapper[ct] = ct;
141  // All vertices are initially unlocked
142  vertexLocked[ct] = false;
143  }
144 
145  //For each triangle...
146  for(uint32_t ctIter = 0; ctIter < m_vecTriangles.size(); ctIter++)
147  {
148  if(attemptEdgeCollapse(m_vecTriangles[ctIter].v0, m_vecTriangles[ctIter].v1))
149  {
150  ++noOfEdgesCollapsed;
151  }
152 
153  if(attemptEdgeCollapse(m_vecTriangles[ctIter].v1, m_vecTriangles[ctIter].v2))
154  {
155  ++noOfEdgesCollapsed;
156  }
157 
158  if(attemptEdgeCollapse(m_vecTriangles[ctIter].v2, m_vecTriangles[ctIter].v0))
159  {
160  ++noOfEdgesCollapsed;
161  }
162  }
163 
164  if(noOfEdgesCollapsed > 0)
165  {
166  //Fix up the indices
167  for(uint32_t triCt = 0; triCt < m_pOutputMesh->m_vecTriangleIndices.size(); triCt++)
168  {
169  uint32_t before = m_pOutputMesh->m_vecTriangleIndices[triCt];
170  uint32_t after = vertexMapper[m_pOutputMesh->m_vecTriangleIndices[triCt]];
171  if(before != after)
172  {
173  m_pOutputMesh->m_vecTriangleIndices[triCt] = vertexMapper[m_pOutputMesh->m_vecTriangleIndices[triCt]];
174  }
175  }
176  }
177 
178  return noOfEdgesCollapsed;
179  }
180 
181  template <typename VertexType>
182  bool MeshDecimator<VertexType>::attemptEdgeCollapse(uint32_t uSrc, uint32_t uDst)
183  {
184  //A vertex will be locked if it has already been involved in a collapse this pass.
185  if(vertexLocked[uSrc] || vertexLocked[uDst])
186  {
187  return false;
188  }
189 
190  if(canCollapseEdge(uSrc, uDst))
191  {
192  //Move v0 onto v1
193  vertexMapper[uSrc] = uDst; //vertexMapper[v1];
194  vertexLocked[uSrc] = true;
195  vertexLocked[uDst] = true;
196 
197  //Increment the counter
198  return true;
199  }
200 
201  return false;
202  }
203 
204  template <typename VertexType>
205  bool MeshDecimator<VertexType>::canCollapseEdge(uint32_t uSrc, uint32_t uDst)
206  {
207  bool bCanCollapse = true;
208 
209  if(m_vecInitialVertexMetadata[uSrc].isOnMaterialEdge)
210  {
211  bCanCollapse &= canCollapseMaterialEdge(uSrc, uDst);
212  }
213 
214  if(m_vecInitialVertexMetadata[uSrc].isOnRegionFace.any())
215  {
216  bCanCollapse &= canCollapseRegionEdge(uSrc, uDst);
217  }
218 
219  if(bCanCollapse) //Only bother with this if the earlier tests passed.
220  {
221  bCanCollapse &= canCollapseNormalEdge(uSrc, uDst);
222  }
223 
224  return bCanCollapse;
225  }
226 
227  template <typename VertexType>
228  bool MeshDecimator<VertexType>::canCollapseRegionEdge(uint32_t uSrc, uint32_t uDst)
229  {
230  // We can collapse normal vertices onto edge vertices, and edge vertices
231  // onto corner vertices, but not vice-versa. Hence we check whether all
232  // the edge flags in the source vertex are also set in the destination vertex.
233  if(isSubset(m_vecInitialVertexMetadata[uSrc].isOnRegionFace, m_vecInitialVertexMetadata[uDst].isOnRegionFace) == false)
234  {
235  return false;
236  }
237 
238  // In general adjacent regions surface meshes may collapse differently
239  // and this can cause cracks. We solve this by only allowing the collapse
240  // is the normals are exactly the same. We do not use the user provided
241  // tolerence here (but do allow for floating point error).
242  if(m_vecInitialVertexMetadata[uSrc].normal.dot(m_vecInitialVertexMetadata[uDst].normal) < 0.999f)
243  {
244  return false;
245  }
246 
247  return true;
248  }
249 
250  template <typename VertexType>
251  bool MeshDecimator<VertexType>::canCollapseMaterialEdge(uint32_t /*uSrc*/, uint32_t /*uDst*/)
252  {
253  return false;
254  }
255 
256  //This function should really use some work. For a start we already have the
257  //faces normals for the input mesh yet we are computing them on the fly here.
258  template <typename VertexType>
259  bool MeshDecimator<VertexType>::collapseChangesFaceNormals(uint32_t uSrc, uint32_t uDst, float fThreshold)
260  {
261  bool faceFlipped = false;
262  std::vector<uint32_t>& triangles = trianglesUsingVertex[uSrc];
263 
264  for(std::vector<uint32_t>::iterator triIter = triangles.begin(); triIter != triangles.end(); triIter++)
265  {
266  uint32_t tri = *triIter;
267 
268  const uint32_t& v0Old = m_pOutputMesh->m_vecTriangleIndices[tri * 3];
269  const uint32_t& v1Old = m_pOutputMesh->m_vecTriangleIndices[tri * 3 + 1];
270  const uint32_t& v2Old = m_pOutputMesh->m_vecTriangleIndices[tri * 3 + 2];
271 
272  //Check if degenerate
273  if((v0Old == v1Old) || (v1Old == v2Old) || (v2Old == v0Old))
274  {
275  continue;
276  }
277 
278  uint32_t v0New = v0Old;
279  uint32_t v1New = v1Old;
280  uint32_t v2New = v2Old;
281 
282  if(v0New == uSrc)
283  v0New = uDst;
284  if(v1New == uSrc)
285  v1New = uDst;
286  if(v2New == uSrc)
287  v2New = uDst;
288 
289  //Check if degenerate
290  if((v0New == v1New) || (v1New == v2New) || (v2New == v0New))
291  {
292  continue;
293  }
294 
295  const Vector3DFloat& v0OldPos = m_pOutputMesh->m_vecVertices[vertexMapper[v0Old]].getPosition(); //Note: we need the vertex mapper here. These neighbouring vertices may have been moved.
296  const Vector3DFloat& v1OldPos = m_pOutputMesh->m_vecVertices[vertexMapper[v1Old]].getPosition();
297  const Vector3DFloat& v2OldPos = m_pOutputMesh->m_vecVertices[vertexMapper[v2Old]].getPosition();
298 
299  const Vector3DFloat& v0NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v0New]].getPosition();
300  const Vector3DFloat& v1NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v1New]].getPosition();
301  const Vector3DFloat& v2NewPos = m_pOutputMesh->m_vecVertices[vertexMapper[v2New]].getPosition();
302 
303  Vector3DFloat OldNormal = (v1OldPos - v0OldPos).cross(v2OldPos - v1OldPos);
304  Vector3DFloat NewNormal = (v1NewPos - v0NewPos).cross(v2NewPos - v1NewPos);
305 
306  OldNormal.normalise();
307  NewNormal.normalise();
308 
309  float dotProduct = OldNormal.dot(NewNormal);
310  //NOTE: I don't think we should be using the threshold here, we're just checking for a complete face flip
311  if(dotProduct < fThreshold)
312  {
313  //cout << " Face flipped!!" << endl;
314 
315  faceFlipped = true;
316 
317  /*vertexLocked[v0] = true;
318  vertexLocked[v1] = true;*/
319 
320  break;
321  }
322  }
323 
324  return faceFlipped;
325  }
326 
327  // Returns true if every bit which is set in 'a' is also set in 'b'. The reverse does not need to be true.
328  template <typename VertexType>
329  bool MeshDecimator<VertexType>::isSubset(std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> a, std::bitset<RFF_NO_OF_REGION_FACE_FLAGS> b)
330  {
331  bool result = true;
332 
333  for(int ct = 0; ct < RFF_NO_OF_REGION_FACE_FLAGS; ct++)
334  {
335  if(a.test(ct))
336  {
337  if(b.test(ct) == false)
338  {
339  result = false;
340  break;
341  }
342  }
343  }
344 
345  return result;
346  }
347 }