Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
LoopSubdivider.cpp
1 #include <Core/Geometry/LoopSubdivider.hpp>
2 
3 namespace Ra {
4 namespace Core {
5 namespace Geometry {
6 
7 bool LoopSubdivider::prepare( deprecated::TopologicalMesh& mesh ) {
8  uint maxValence = 0;
9  for ( auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it ) {
10  if ( mesh.valence( *v_it ) > maxValence ) { maxValence = mesh.valence( *v_it ); }
11  }
12  init_weights( maxValence + 1 );
13  mesh.add_property( m_vpPos );
14  mesh.add_property( m_epPos );
15  mesh.add_property( m_hV );
16  for ( uint i = 0; i < mesh.n_halfedges(); ++i ) {
17  auto h = mesh.halfedge_handle( i );
18  if ( !mesh.is_boundary( h ) ) { mesh.property( m_hV, h ) = mesh.to_vertex_handle( h ); }
19  }
20  return true;
21 }
22 
23 bool LoopSubdivider::cleanup( deprecated::TopologicalMesh& mesh ) {
24  mesh.remove_property( m_vpPos );
25  mesh.remove_property( m_epPos );
26  mesh.remove_property( m_hV );
27  return true;
28 }
29 
30 bool LoopSubdivider::subdivide( deprecated::TopologicalMesh& mesh,
31  size_t n,
32  const bool updatePoints ) {
33  m_oldVertexOps.clear();
34  m_newVertexOps.clear();
35  m_newEdgePropOps.clear();
36  m_newFacePropOps.clear();
37  m_oldVertexOps.resize( n );
38  m_newVertexOps.resize( n );
39  m_newEdgePropOps.resize( n );
40  m_newFacePropOps.resize( n );
41 
42  deprecated::TopologicalMesh::FaceIter fit, f_end;
43  deprecated::TopologicalMesh::EdgeIter eit, e_end;
44 
45  // Do n subdivisions
46  for ( uint iter = 0; iter < n; ++iter ) {
47  const size_t NV = mesh.n_vertices();
48  if ( updatePoints ) {
49  // compute new positions for old vertices
50  m_oldVertexOps[iter].reserve( NV );
51 #pragma omp parallel for
52  for ( int i = 0; i < int( NV ); ++i ) {
53  const auto& vh = mesh.vertex_handle( i );
54  smooth( mesh, vh, iter );
55  }
56  }
57 
58  // Compute position for new vertices and store them in the edge property
59  m_newVertexOps[iter].reserve( mesh.n_edges() );
60 #pragma omp parallel for
61  for ( int i = 0; i < int( mesh.n_edges() ); ++i ) {
62  const auto& eh = mesh.edge_handle( i );
63  compute_midpoint( mesh, eh, iter );
64  }
65 
66  // Split each edge at midpoint and store precomputed positions (stored in
67  // edge property ep_pos_) in the vertex property vp_pos_;
68  // Attention! Creating new edges, hence make sure the loop ends correctly.
69  e_end = mesh.edges_end();
70  m_newEdgePropOps[iter].reserve( 3 * mesh.n_edges() );
71  for ( eit = mesh.edges_begin(); eit != e_end; ++eit ) {
72  split_edge( mesh, *eit, iter );
73  }
74  m_newEdgePropOps[iter].shrink_to_fit();
75 
76  // Commit changes in topology and reconsitute consistency
77  // Attention! Creating new faces, hence make sure the loop ends correctly.
78  f_end = mesh.faces_end();
79  m_newFacePropOps[iter].reserve( 6 * mesh.n_faces() );
80  for ( fit = mesh.faces_begin(); fit != f_end; ++fit ) {
81  split_face( mesh, *fit, iter );
82  }
83  m_newFacePropOps[iter].shrink_to_fit();
84 
85  if ( updatePoints ) {
86  // Commit changes in geometry
87 #pragma omp parallel for
88  for ( int i = 0; i < int( NV ); ++i ) {
89  const auto& vh = mesh.vertex_handle( i );
90  mesh.set_point( vh, mesh.property( m_vpPos, vh ) );
91  }
92  }
93 
94  // Now we have an consistent mesh!
95  CORE_ASSERT( OpenMesh::Utils::MeshCheckerT<deprecated::TopologicalMesh>( mesh ).check(),
96  "LoopSubdivision ended with a bad topology." );
97  }
98 
99  return true;
100 }
101 
102 void LoopSubdivider::split_face( deprecated::TopologicalMesh& mesh,
103  const deprecated::TopologicalMesh::FaceHandle& fh,
104  size_t iter ) {
105  using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
106  // get where to cut
107  HeHandle heh1( mesh.halfedge_handle( fh ) );
108  HeHandle heh2( mesh.next_halfedge_handle( mesh.next_halfedge_handle( heh1 ) ) );
109  HeHandle heh3( mesh.next_halfedge_handle( mesh.next_halfedge_handle( heh2 ) ) );
110 
111  // Cutting off every corner of the 6_gon
112  corner_cutting( mesh, heh1, iter );
113  corner_cutting( mesh, heh2, iter );
114  corner_cutting( mesh, heh3, iter );
115 }
116 
117 void LoopSubdivider::corner_cutting( deprecated::TopologicalMesh& mesh,
118  const deprecated::TopologicalMesh::HalfedgeHandle& he,
119  size_t iter ) {
120  using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
121  using VHandle = deprecated::TopologicalMesh::VertexHandle;
122  using FHandle = deprecated::TopologicalMesh::FaceHandle;
123 
124  // Define Halfedge Handles
125  HeHandle heh1( he );
126  HeHandle heh5( heh1 );
127  HeHandle heh6( mesh.next_halfedge_handle( heh1 ) );
128 
129  // Cycle around the polygon to find correct Halfedge
130  for ( ; mesh.next_halfedge_handle( mesh.next_halfedge_handle( heh5 ) ) != heh1;
131  heh5 = mesh.next_halfedge_handle( heh5 ) )
132  ;
133 
134  VHandle vh1 = mesh.to_vertex_handle( heh1 );
135  VHandle vh2 = mesh.to_vertex_handle( heh5 );
136 
137  HeHandle heh2( mesh.next_halfedge_handle( heh5 ) );
138  HeHandle heh3( mesh.new_edge( vh1, vh2 ) );
139  HeHandle heh4( mesh.opposite_halfedge_handle( heh3 ) );
140 
141  /* Intermediate result
142  *
143  * *
144  * 5 /|\
145  * /_ \
146  * vh2> * *
147  * /|\3 |\
148  * /_ \|4 \
149  * *----\*----\*
150  * 1 ^ 6
151  * vh1 (adjust_outgoing halfedge!)
152  */
153 
154  // Old and new Face
155  FHandle fh_old( mesh.face_handle( heh6 ) );
156  FHandle fh_new( mesh.new_face() );
157 
158  // Re-Set Handles around old Face
159  mesh.set_next_halfedge_handle( heh4, heh6 );
160  mesh.set_next_halfedge_handle( heh5, heh4 );
161 
162  mesh.set_face_handle( heh4, fh_old );
163  mesh.set_face_handle( heh5, fh_old );
164  mesh.set_face_handle( heh6, fh_old );
165  mesh.set_halfedge_handle( fh_old, heh4 );
166 
167  // Re-Set Handles around new Face
168  mesh.set_next_halfedge_handle( heh1, heh3 );
169  mesh.set_next_halfedge_handle( heh3, heh2 );
170 
171  mesh.set_face_handle( heh1, fh_new );
172  mesh.set_face_handle( heh2, fh_new );
173  mesh.set_face_handle( heh3, fh_new );
174 
175  mesh.set_halfedge_handle( fh_new, heh1 );
176 
177  // deal with custom properties
178  mesh.copyAllProps( heh1, heh4 );
179  mesh.copyAllProps( heh5, heh3 );
180  m_newFacePropOps[iter].push_back( { heh4, { { 1, heh1 } } } );
181  m_newFacePropOps[iter].push_back( { heh3, { { 1, heh5 } } } );
182 }
183 
184 void LoopSubdivider::split_edge( deprecated::TopologicalMesh& mesh,
185  const deprecated::TopologicalMesh::EdgeHandle& eh,
186  size_t iter ) {
187  using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
188  using VHandle = deprecated::TopologicalMesh::VertexHandle;
189 
190  // prepare data
191  HeHandle heh = mesh.halfedge_handle( eh, 0 );
192  HeHandle opp_heh = mesh.halfedge_handle( eh, 1 );
193  HeHandle new_heh;
194  HeHandle opp_new_heh;
195  HeHandle t_heh;
196  VHandle vh;
197  VHandle vh1( mesh.to_vertex_handle( heh ) );
198 
199  // new vertex
200  vh = mesh.property( m_epPos, eh );
201 
202  // Re-link mesh entities
203  if ( mesh.is_boundary( eh ) ) {
204  for ( t_heh = heh; mesh.next_halfedge_handle( t_heh ) != opp_heh;
205  t_heh = mesh.opposite_halfedge_handle( mesh.next_halfedge_handle( t_heh ) ) )
206  ;
207  }
208  else {
209  for ( t_heh = mesh.next_halfedge_handle( opp_heh );
210  mesh.next_halfedge_handle( t_heh ) != opp_heh;
211  t_heh = mesh.next_halfedge_handle( t_heh ) )
212  ;
213  }
214 
215  new_heh = mesh.new_edge( vh, vh1 );
216  opp_new_heh = mesh.opposite_halfedge_handle( new_heh );
217  mesh.set_vertex_handle( heh, vh );
218 
219  mesh.set_next_halfedge_handle( t_heh, opp_new_heh );
220  mesh.set_next_halfedge_handle( new_heh, mesh.next_halfedge_handle( heh ) );
221  mesh.set_next_halfedge_handle( heh, new_heh );
222  mesh.set_next_halfedge_handle( opp_new_heh, opp_heh );
223 
224  if ( mesh.face_handle( opp_heh ).is_valid() ) {
225  mesh.set_face_handle( opp_new_heh, mesh.face_handle( opp_heh ) );
226  mesh.set_halfedge_handle( mesh.face_handle( opp_new_heh ), opp_new_heh );
227 
228  // deal with custom properties
229  mesh.interpolateAllProps( t_heh, opp_heh, opp_new_heh, 0.5 );
230  m_newEdgePropOps[iter].push_back( { opp_new_heh, { { 0.5, t_heh }, { 0.5, opp_heh } } } );
231  }
232 
233  if ( mesh.face_handle( heh ).is_valid() ) {
234  mesh.set_face_handle( new_heh, mesh.face_handle( heh ) );
235  mesh.set_halfedge_handle( mesh.face_handle( heh ), heh );
236 
237  // deal with custom properties
238  mesh.copyAllProps( heh, new_heh );
239  m_newEdgePropOps[iter].push_back( { new_heh, { { 1, heh } } } );
240  HeHandle heh_prev = mesh.prev_halfedge_handle( heh );
241  mesh.interpolateAllProps( heh_prev, new_heh, heh, 0.5 );
242  m_newEdgePropOps[iter].push_back( { heh, { { 0.5, heh_prev }, { 0.5, new_heh } } } );
243  }
244 
245  mesh.set_halfedge_handle( vh, new_heh );
246  mesh.set_halfedge_handle( vh1, opp_new_heh );
247 
248  // Never forget this, when playing with the topology
249  mesh.adjust_outgoing_halfedge( vh );
250  mesh.adjust_outgoing_halfedge( vh1 );
251 }
252 
253 void LoopSubdivider::compute_midpoint( deprecated::TopologicalMesh& mesh,
254  const deprecated::TopologicalMesh::EdgeHandle& eh,
255  size_t iter ) {
256  deprecated::TopologicalMesh::HalfedgeHandle heh = mesh.halfedge_handle( eh, 0 );
257  deprecated::TopologicalMesh::HalfedgeHandle opp_heh = mesh.halfedge_handle( eh, 1 );
258 
259  deprecated::TopologicalMesh::Point pos = mesh.point( mesh.to_vertex_handle( heh ) );
260  pos += mesh.point( mesh.to_vertex_handle( opp_heh ) );
261 
262  std::vector<V_OP> ops;
263 
264  // boundary edge: just average vertex positions
265  if ( mesh.is_boundary( eh ) ) {
266  pos *= 0.5;
267  ops.resize( 2 );
268  ops[0] = V_OP( 0.5, mesh.to_vertex_handle( heh ) );
269  ops[1] = V_OP( 0.5, mesh.to_vertex_handle( opp_heh ) );
270  }
271  else // inner edge: add neighbouring Vertices to sum
272  {
273  pos *= 3.0;
274  pos += mesh.point( mesh.to_vertex_handle( mesh.next_halfedge_handle( heh ) ) );
275  pos += mesh.point( mesh.to_vertex_handle( mesh.next_halfedge_handle( opp_heh ) ) );
276  pos *= 1.0 / 8.0;
277  ops.resize( 4 );
278  ops[0] = V_OP( 3.f / 8.f, mesh.to_vertex_handle( heh ) );
279  ops[1] = V_OP( 3.f / 8.f, mesh.to_vertex_handle( opp_heh ) );
280  ops[2] = V_OP( 1.f / 8.f, mesh.to_vertex_handle( mesh.next_halfedge_handle( heh ) ) );
281  ops[3] = V_OP( 1.f / 8.f, mesh.to_vertex_handle( mesh.next_halfedge_handle( opp_heh ) ) );
282  }
283 
284 #pragma omp critical
285  {
286  auto vh = mesh.add_vertex( pos );
287  mesh.property( m_epPos, eh ) = vh;
288  m_newVertexOps[iter].push_back( V_OPS( vh, ops ) );
289  }
290 }
291 
292 void LoopSubdivider::smooth( deprecated::TopologicalMesh& mesh,
293  const deprecated::TopologicalMesh::VertexHandle& vh,
294  size_t iter ) {
295  using VHandle = deprecated::TopologicalMesh::VertexHandle;
296 
297  deprecated::TopologicalMesh::Point pos( 0.0, 0.0, 0.0 );
298 
299  std::vector<V_OP> ops;
300 
301  if ( mesh.is_boundary( vh ) ) // if boundary: Point 1-6-1
302  {
303  deprecated::TopologicalMesh::HalfedgeHandle heh;
304  deprecated::TopologicalMesh::HalfedgeHandle prev_heh;
305  heh = mesh.halfedge_handle( vh );
306 
307  assert( mesh.is_boundary( mesh.edge_handle( heh ) ) );
308 
309  prev_heh = mesh.prev_halfedge_handle( heh );
310 
311  VHandle to_vh = mesh.to_vertex_handle( heh );
312  VHandle from_vh = mesh.from_vertex_handle( prev_heh );
313 
314  // ( v_l + 6 v + v_r ) / 8
315  pos = mesh.point( vh );
316  pos *= 6.0;
317  pos += mesh.point( to_vh );
318  pos += mesh.point( from_vh );
319  pos *= 1.0 / 8.0;
320 
321  ops.resize( 3 );
322  ops[2] = V_OP( 6.f / 8.f, vh );
323  ops[0] = V_OP( 1.f / 8.f, to_vh );
324  ops[1] = V_OP( 1.f / 8.f, from_vh );
325  }
326  else // inner vertex: (1-a) * p + a/n * Sum q, q in one-ring of p
327  {
328  deprecated::TopologicalMesh::VertexVertexIter vvit;
329  const uint valence = mesh.valence( vh );
330  ops.resize( valence + 1 );
331  int i = 0;
332 
333  // Calculate Valence and sum up neighbour points
334  for ( vvit = mesh.cvv_iter( vh ); vvit.is_valid(); ++vvit ) {
335  pos += mesh.point( *vvit );
336  ops[i++] = V_OP( m_weights[valence].second, *vvit );
337  }
338  pos *= m_weights[valence].second; // alpha(n)/n * Sum q, q in one-ring of p
339  pos += m_weights[valence].first * mesh.point( vh ); // + (1-a)*p
340  ops[i] = V_OP( m_weights[valence].first, vh );
341  }
342 
343  mesh.property( m_vpPos, vh ) = pos;
344 
345 #pragma omp critical
346  { m_oldVertexOps[iter].push_back( V_OPS( vh, ops ) ); }
347 }
348 
349 void LoopSubdivider::recompute( const Vector3Array& newCoarseVertices,
350  const Vector3Array& newCoarseNormals,
351  Vector3Array& newSubdivVertices,
352  Vector3Array& newSubdivNormals,
354  // update vertices
355  auto inTriIndexProp = mesh.getInputTriangleMeshIndexPropHandle();
356  auto hNormalProp = mesh.halfedge_normals_pph();
357 #pragma omp parallel for
358  for ( int i = 0; i < int( mesh.n_halfedges() ); ++i ) {
359  auto h = mesh.halfedge_handle( i );
360  // set position on coarse mesh vertices
361  auto vh = mesh.property( m_hV, h );
362  if ( vh.idx() != -1 ) // avoid both boundary and non-coarse halfedges
363  {
364  auto idx = mesh.property( inTriIndexProp, h );
365  mesh.set_point( vh, newCoarseVertices[idx] );
366  mesh.property( hNormalProp, h ) = newCoarseNormals[idx];
367  }
368  }
369  // for each subdiv step
370  for ( int i = 0; i < int( m_oldVertexOps.size() ); ++i ) {
371  // first update new vertices
372 #pragma omp parallel for schedule( static )
373  for ( int j = 0; j < int( m_newVertexOps[i].size() ); ++j ) {
374  Ra::Core::Vector3 pos( 0, 0, 0 );
375  const auto& ops = m_newVertexOps[i][j];
376  for ( const auto& op : ops.second ) {
377  pos += op.first * mesh.point( op.second );
378  }
379  mesh.set_point( ops.first, pos );
380  }
381  // then compute old vertices
382  std::vector<Ra::Core::Vector3> pos( m_oldVertexOps[i].size() );
383 #pragma omp parallel for
384  for ( int j = 0; j < int( m_oldVertexOps[i].size() ); ++j ) {
385  pos[j] = Ra::Core::Vector3( 0, 0, 0 );
386  const auto& ops = m_oldVertexOps[i][j];
387  for ( const auto& op : ops.second ) {
388  pos[j] += op.first * mesh.point( op.second );
389  }
390  }
391  // then commit pos for old vertices
392 #pragma omp parallel for schedule( static )
393  for ( int j = 0; j < int( m_oldVertexOps[i].size() ); ++j ) {
394  mesh.set_point( m_oldVertexOps[i][j].first, pos[j] );
395  }
396  // deal with normal on edge centers (other non-static properties can be updated the same
397  // way)
398  // This loop should not be parallelized!
399  for ( int j = 0; j < int( m_newEdgePropOps[i].size() ); ++j ) {
400  Ra::Core::Vector3 nor( 0, 0, 0 );
401  const auto& ops = m_newEdgePropOps[i][j];
402  for ( const auto& op : ops.second ) {
403  nor += op.first * mesh.property( hNormalProp, op.second );
404  }
405  mesh.property( hNormalProp, ops.first ) = nor.normalized();
406  }
407  // deal with normal on faces centers (other non-static properties can be updated the same
408  // way)
409 #pragma omp parallel for
410  for ( int j = 0; j < int( m_newFacePropOps[i].size() ); ++j ) {
411  Ra::Core::Vector3 nor( 0, 0, 0 );
412  const auto& ops = m_newFacePropOps[i][j];
413  for ( const auto& op : ops.second ) {
414  nor += op.first * mesh.property( hNormalProp, op.second );
415  }
416  mesh.property( hNormalProp, ops.first ) = nor.normalized();
417  }
418  }
419  // update subdivided TriangleMesh vertices and normals
420  auto outTriIndexProp = mesh.getOutputTriangleMeshIndexPropHandle();
421 #pragma omp parallel for
422  for ( int i = 0; i < int( mesh.n_halfedges() ); ++i ) {
423  auto h = mesh.halfedge_handle( i );
424  if ( !mesh.is_boundary( h ) ) {
425  auto idx = mesh.property( outTriIndexProp, h );
426  newSubdivVertices[idx] = mesh.point( mesh.to_vertex_handle( h ) );
427  newSubdivNormals[idx] = mesh.property( hNormalProp, h );
428  }
429  }
430 }
431 
432 } // namespace Geometry
433 } // namespace Core
434 } // namespace Ra
void recompute(const Vector3Array &newCoarseVertices, const Vector3Array &newCoarseNormals, Vector3Array &newSubdivVertices, Vector3Array &newSubdivNormals, deprecated::TopologicalMesh &mesh)
void init_weights(size_t max_valence)
Pre-compute weights.
const OpenMesh::HPropHandleT< Index > & getInputTriangleMeshIndexPropHandle() const
const OpenMesh::HPropHandleT< Index > & getOutputTriangleMeshIndexPropHandle() const
HalfedgeHandle halfedge_handle(VertexHandle vh, FaceHandle fh) const
Definition: Cage.cpp:3