Loading [MathJax]/extensions/TeX/AMSmath.js
Radium Engine  1.5.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Modules Pages
CatmullClarkSubdivider.cpp
1 #include <Core/Geometry/CatmullClarkSubdivider.hpp>
2 
3 namespace Ra {
4 namespace Core {
5 namespace Geometry {
6 
7 bool CatmullClarkSubdivider::prepare( deprecated::TopologicalMesh& mesh ) {
8  mesh.add_property( m_vpPos );
9  mesh.add_property( m_epH );
10  mesh.add_property( m_fpH );
11  mesh.add_property( m_creaseWeights );
12  mesh.createAllPropsOnFaces(
13  m_normalPropF, m_floatPropsF, m_vec2PropsF, m_vec3PropsF, m_vec4PropsF );
14  mesh.add_property( m_hV );
15  for ( uint i = 0; i < mesh.n_halfedges(); ++i ) {
16  auto h = mesh.halfedge_handle( i );
17  if ( !mesh.is_boundary( h ) ) { mesh.property( m_hV, h ) = mesh.to_vertex_handle( h ); }
18  }
19 
20  // initialize all weights to 0 (= smooth edge)
21  for ( auto e_it = mesh.edges_begin(); e_it != mesh.edges_end(); ++e_it )
22  mesh.property( m_creaseWeights, *e_it ) = 0.0;
23 
24  return true;
25 }
26 
27 bool CatmullClarkSubdivider::cleanup( deprecated::TopologicalMesh& mesh ) {
28  mesh.remove_property( m_vpPos );
29  mesh.remove_property( m_epH );
30  mesh.remove_property( m_fpH );
31  mesh.remove_property( m_creaseWeights );
32  mesh.clearAllProps( m_normalPropF, m_floatPropsF, m_vec2PropsF, m_vec3PropsF, m_vec4PropsF );
33  mesh.remove_property( m_hV );
34  return true;
35 }
36 
37 bool CatmullClarkSubdivider::subdivide( deprecated::TopologicalMesh& mesh,
38  size_t n,
39  const bool update_points ) {
40  m_oldVertexOps.clear();
41  m_newFaceVertexOps.clear();
42  m_newEdgeVertexOps.clear();
43  m_newEdgePropOps.clear();
44  m_newFacePropOps.clear();
45  m_oldVertexOps.resize( n );
46  m_newFaceVertexOps.resize( n );
47  m_newEdgeVertexOps.resize( n );
48  m_newEdgePropOps.resize( n );
49  m_newFacePropOps.resize( n );
50  // Do n subdivisions
51  for ( size_t iter = 0; iter < n; ++iter ) {
52  // Compute face centroid
53  const size_t NV = mesh.n_vertices();
54  m_newFaceVertexOps[iter].reserve( mesh.n_faces() );
55 #pragma omp parallel for
56  for ( int i = 0; i < int( mesh.n_faces() ); ++i ) {
57  const auto& fh = mesh.face_handle( i );
58  // compute centroid
59  deprecated::TopologicalMesh::Point centroid;
60  mesh.calc_face_centroid( fh, centroid );
61  deprecated::TopologicalMesh::VertexHandle vh;
62 #pragma omp critical
63  { vh = mesh.new_vertex( centroid ); }
64  mesh.property( m_fpH, fh ) = vh;
65  // register operation
66  const uint v = mesh.valence( fh );
67  const Scalar inv_v = 1.f / v;
68  std::vector<V_OP> ops( v );
69  auto heh = mesh.halfedge_handle( fh );
70  for ( uint j = 0; j < v; ++j ) {
71  ops[j] = V_OP( inv_v, mesh.to_vertex_handle( heh ) );
72  heh = mesh.next_halfedge_handle( heh );
73  }
74 #pragma omp critical
75  { m_newFaceVertexOps[iter].push_back( V_OPS( vh, ops ) ); }
76  // deal with properties
77  mesh.interpolateAllPropsOnFaces(
78  fh, m_normalPropF, m_floatPropsF, m_vec2PropsF, m_vec3PropsF, m_vec4PropsF );
79  }
80 
81  // Compute position for new (edge-) vertices and store them in the edge property
82  m_newEdgeVertexOps[iter].reserve( mesh.n_edges() );
83 #pragma omp parallel for
84  for ( int i = 0; i < int( mesh.n_edges() ); ++i ) {
85  const auto& eh = mesh.edge_handle( i );
86  compute_midpoint( mesh, eh, update_points, iter );
87  }
88 
89  // position updates activated?
90  if ( update_points ) {
91  // compute new positions for old vertices
92  m_oldVertexOps[iter].reserve( NV );
93 #pragma omp parallel for
94  for ( int i = 0; i < int( NV ); ++i ) {
95  const auto& vh = mesh.vertex_handle( i );
96  update_vertex( mesh, vh, iter );
97  }
98 
99  // Commit changes in geometry
100 #pragma omp parallel for
101  for ( int i = 0; i < int( NV ); ++i ) {
102  const auto& vh = mesh.vertex_handle( i );
103  mesh.set_point( vh, mesh.property( m_vpPos, vh ) );
104  }
105  }
106 
107  // Split each edge at midpoint stored in edge property m_epPos;
108  // Attention! Creating new edges, hence make sure the loop ends correctly.
109  auto e_end = mesh.edges_end();
110  m_newEdgePropOps[iter].reserve( 3 * mesh.n_edges() );
111  for ( auto e_itr = mesh.edges_begin(); e_itr != e_end; ++e_itr ) {
112  split_edge( mesh, *e_itr, iter );
113  }
114  m_newEdgePropOps[iter].shrink_to_fit();
115 
116  // Commit changes in topology and reconsitute consistency
117  // Attention! Creating new faces, hence make sure the loop ends correctly.
118  auto f_end = mesh.faces_end();
119  m_newFacePropOps[iter].reserve( 2 * 4 * mesh.n_faces() );
120  for ( auto f_itr = mesh.faces_begin(); f_itr != f_end; ++f_itr ) {
121  split_face( mesh, *f_itr, iter );
122  }
123  m_newFacePropOps[iter].shrink_to_fit();
124 
125  CORE_ASSERT( OpenMesh::Utils::MeshCheckerT<deprecated::TopologicalMesh>( mesh ).check(),
126  "CatmullClarkSubdivision ended with a bad topology." );
127  }
128 
129  // ###########################################################################
130  // FIXME: THIS IS ONLY THERE BECAUSE CORE ONLY MANAGES TRIANGLE MESHES FOR NOW
131 
132  m_triangulationPropOps.clear();
133  m_triangulationPropOps.reserve( 2 * mesh.n_faces() );
134  // triangulate resulting quads
135  auto f_end = mesh.faces_end();
136  for ( auto f_itr = mesh.faces_begin(); f_itr != f_end; ++f_itr ) {
137  // get all needed halfedges
138  auto heh1 = mesh.halfedge_handle( *f_itr );
139  auto heh2 = mesh.next_halfedge_handle( heh1 );
140  auto heh3 = mesh.next_halfedge_handle( heh2 );
141  auto heh4 = mesh.next_halfedge_handle( heh3 );
142  auto heh5 = mesh.new_edge( mesh.to_vertex_handle( heh1 ), mesh.to_vertex_handle( heh3 ) );
143  auto heh6 = mesh.opposite_halfedge_handle( heh5 );
144 
145  // triangulate
146  auto fnew = mesh.new_face();
147  mesh.set_halfedge_handle( fnew, heh2 );
148  mesh.set_face_handle( heh5, *f_itr );
149  mesh.set_face_handle( heh2, fnew );
150  mesh.set_face_handle( heh3, fnew );
151  mesh.set_face_handle( heh6, fnew );
152  mesh.set_next_halfedge_handle( heh1, heh5 );
153  mesh.set_next_halfedge_handle( heh5, heh4 );
154  mesh.set_next_halfedge_handle( heh3, heh6 );
155  mesh.set_next_halfedge_handle( heh6, heh2 );
156 
157  // deal with properties
158  mesh.copyAllProps( heh3, heh5 );
159  mesh.copyAllProps( heh1, heh6 );
160  m_triangulationPropOps.push_back( { heh5, { { 1, heh3 } } } );
161  m_triangulationPropOps.push_back( { heh6, { { 1, heh1 } } } );
162  }
163 
164  return true;
165 }
166 
167 void CatmullClarkSubdivider::split_face( deprecated::TopologicalMesh& mesh,
168  const deprecated::TopologicalMesh::FaceHandle& fh,
169  size_t iter ) {
170  /*
171  Split an n-gon into n quads by connecting
172  each vertex of fh to vh.
173 
174  - fh will remain valid (it will become one of the quads)
175  - the halfedge handles of the new quads will point to the old halfedges
176  */
177 
178  // Since edges are already refined (valence*2)
179  size_t valence = mesh.valence( fh ) / 2;
180 
181  // Add new mesh vertex from face centroid
182  auto vh = mesh.property( m_fpH, fh );
183 
184  // init new topology with first face
185  auto hend = mesh.halfedge_handle( fh );
186  auto hh = mesh.next_halfedge_handle( hend );
187  auto hold = mesh.new_edge( mesh.to_vertex_handle( hend ), vh );
188  mesh.set_next_halfedge_handle( hend, hold );
189  mesh.set_face_handle( hold, fh );
190 
191  // deal with properties for vh
192  mesh.copyAllPropsFromFace(
193  fh, hold, m_normalPropF, m_floatPropsF, m_vec2PropsF, m_vec3PropsF, m_vec4PropsF );
194 
195  // go around new vertex to build topology
196  hold = mesh.opposite_halfedge_handle( hold );
197 
198  // deal with properties for hold
199  mesh.copyAllProps( hend, hold );
200  m_newFacePropOps[iter].push_back( { hold, { { 1, hend } } } );
201 
202  const Scalar inv_val = Scalar( 1 ) / valence;
203  std::vector<P_OP> p_ops( valence );
204  p_ops[0] = { inv_val, hh };
205 
206  for ( size_t i = 1; i < valence; i++ ) {
207  // go to next mid-edge vertex
208  auto hnext = mesh.next_halfedge_handle( hh );
209  auto hnew = mesh.new_edge( mesh.to_vertex_handle( hnext ), vh );
210 
211  // create new face
212  auto fnew = mesh.new_face();
213  mesh.set_halfedge_handle( fnew, hh );
214  mesh.set_face_handle( hnew, fnew );
215  mesh.set_face_handle( hold, fnew );
216  mesh.set_face_handle( hh, fnew );
217  mesh.set_face_handle( hnext, fnew );
218  mesh.set_next_halfedge_handle( hnew, hold );
219  mesh.set_next_halfedge_handle( hold, hh );
220 
221  // deal with properties for hnew
222  mesh.copyAllPropsFromFace(
223  fh, hnew, m_normalPropF, m_floatPropsF, m_vec2PropsF, m_vec3PropsF, m_vec4PropsF );
224 
225  // prepare for next face
226  hh = mesh.next_halfedge_handle( hnext );
227  hold = mesh.opposite_halfedge_handle( hnew );
228  mesh.set_next_halfedge_handle( hnext, hnew ); // this has to be done after hh !
229 
230  // deal with properties for hold
231  mesh.copyAllProps( hnext, hold );
232  m_newFacePropOps[iter].push_back( { hold, { { 1, hnext } } } );
233  p_ops[i] = { inv_val, hh };
234  }
235 
236  // finish topology
237  mesh.set_next_halfedge_handle( hold, hh );
238  mesh.set_next_halfedge_handle( hh, hend );
239  hh = mesh.next_halfedge_handle( hend );
240  mesh.set_next_halfedge_handle( hh, hold );
241  mesh.set_face_handle( hold, fh );
242  mesh.set_halfedge_handle( vh, hold );
243 
244  // deal with property operations on centroid
245  for ( auto vh_iter = mesh.vih_iter( vh ); vh_iter.is_valid(); ++vh_iter ) {
246  m_newFacePropOps[iter].push_back( { *vh_iter, p_ops } );
247  }
248 }
249 
250 void CatmullClarkSubdivider::split_edge( deprecated::TopologicalMesh& mesh,
251  const deprecated::TopologicalMesh::EdgeHandle& eh,
252  size_t iter ) {
253  using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
254  using VHandle = deprecated::TopologicalMesh::VertexHandle;
255 
256  // prepare data
257  HeHandle heh = mesh.halfedge_handle( eh, 0 );
258  HeHandle opp_heh = mesh.halfedge_handle( eh, 1 );
259  HeHandle new_heh;
260  HeHandle opp_new_heh;
261  HeHandle t_heh;
262  VHandle vh;
263  VHandle vh1( mesh.to_vertex_handle( heh ) );
264 
265  // new vertex
266  vh = mesh.property( m_epH, eh );
267 
268  // Re-link mesh entities
269  if ( mesh.is_boundary( eh ) ) {
270  // spin around vh1
271  for ( t_heh = heh; mesh.next_halfedge_handle( t_heh ) != opp_heh;
272  t_heh = mesh.opposite_halfedge_handle( mesh.next_halfedge_handle( t_heh ) ) )
273  ;
274  }
275  else {
276  // spin inside opp_heh's face
277  for ( t_heh = mesh.next_halfedge_handle( opp_heh );
278  mesh.next_halfedge_handle( t_heh ) != opp_heh;
279  t_heh = mesh.next_halfedge_handle( t_heh ) )
280  ;
281  }
282 
283  new_heh = mesh.new_edge( vh, vh1 );
284  opp_new_heh = mesh.opposite_halfedge_handle( new_heh );
285  mesh.set_vertex_handle( heh, vh );
286 
287  mesh.set_next_halfedge_handle( t_heh, opp_new_heh );
288  mesh.set_next_halfedge_handle( new_heh, mesh.next_halfedge_handle( heh ) );
289  mesh.set_next_halfedge_handle( heh, new_heh );
290  mesh.set_next_halfedge_handle( opp_new_heh, opp_heh );
291 
292  if ( mesh.face_handle( opp_heh ).is_valid() ) {
293  mesh.set_face_handle( opp_new_heh, mesh.face_handle( opp_heh ) );
294  mesh.set_halfedge_handle( mesh.face_handle( opp_new_heh ), opp_new_heh );
295 
296  // deal with custom properties
297  mesh.interpolateAllProps( t_heh, opp_heh, opp_new_heh, 0.5 );
298  m_newEdgePropOps[iter].push_back( { opp_new_heh, { { 0.5, t_heh }, { 0.5, opp_heh } } } );
299  }
300 
301  if ( mesh.face_handle( heh ).is_valid() ) {
302  mesh.set_face_handle( new_heh, mesh.face_handle( heh ) );
303  mesh.set_halfedge_handle( mesh.face_handle( heh ), heh );
304 
305  // deal with custom properties
306  mesh.copyAllProps( heh, new_heh );
307  m_newEdgePropOps[iter].push_back( { new_heh, { { 1, heh } } } );
308  HeHandle heh_prev = mesh.prev_halfedge_handle( heh );
309  mesh.interpolateAllProps( heh_prev, new_heh, heh, 0.5 );
310  m_newEdgePropOps[iter].push_back( { heh, { { 0.5, heh_prev }, { 0.5, new_heh } } } );
311  }
312 
313  mesh.set_halfedge_handle( vh, new_heh );
314  mesh.set_halfedge_handle( vh1, opp_new_heh );
315 
316  // Never forget this, when playing with the topology
317  mesh.adjust_outgoing_halfedge( vh );
318  mesh.adjust_outgoing_halfedge( vh1 );
319 }
320 
321 void CatmullClarkSubdivider::compute_midpoint( deprecated::TopologicalMesh& mesh,
322  const deprecated::TopologicalMesh::EdgeHandle& eh,
323  const bool update_points,
324  size_t iter ) {
325  deprecated::TopologicalMesh::HalfedgeHandle heh = mesh.halfedge_handle( eh, 0 );
326  deprecated::TopologicalMesh::HalfedgeHandle opp_heh = mesh.halfedge_handle( eh, 1 );
327 
328  deprecated::TopologicalMesh::Point pos = mesh.point( mesh.to_vertex_handle( heh ) );
329  pos += mesh.point( mesh.to_vertex_handle( opp_heh ) );
330 
331  // prepare operations
332  std::vector<V_OP> ops;
333 
334  // boundary edge: just average vertex positions
335  // this yields the [1/2 1/2] mask
336  if ( mesh.is_boundary( eh ) || !update_points ) {
337  pos *= 0.5;
338  // register operations
339  ops.resize( 2 );
340  ops[0] = V_OP( 0.5, mesh.to_vertex_handle( heh ) );
341  ops[1] = V_OP( 0.5, mesh.to_vertex_handle( opp_heh ) );
342  }
343  else // inner edge: add neighbouring Vertices to sum
344  // this yields the [1/16 1/16; 3/8 3/8; 1/16 1/16] mask
345  {
346  pos += mesh.point( mesh.property( m_fpH, mesh.face_handle( heh ) ) );
347  pos += mesh.point( mesh.property( m_fpH, mesh.face_handle( opp_heh ) ) );
348  pos *= 0.25;
349  // register operations
350  ops.resize( 4 );
351  ops[0] = V_OP( 0.25, mesh.to_vertex_handle( heh ) );
352  ops[1] = V_OP( 0.25, mesh.to_vertex_handle( opp_heh ) );
353  ops[2] = V_OP( 0.25, mesh.property( m_fpH, mesh.face_handle( heh ) ) );
354  ops[3] = V_OP( 0.25, mesh.property( m_fpH, mesh.face_handle( opp_heh ) ) );
355  }
356 
357 #pragma omp critical
358  {
359  // add new vertex and save into property
360  auto vh = mesh.new_vertex( pos );
361  mesh.property( m_epH, eh ) = vh;
362  // register operations
363  m_newEdgeVertexOps[iter].push_back( V_OPS( vh, ops ) );
364  }
365 }
366 
367 void CatmullClarkSubdivider::update_vertex( deprecated::TopologicalMesh& mesh,
368  const deprecated::TopologicalMesh::VertexHandle& vh,
369  size_t iter ) {
370  deprecated::TopologicalMesh::Point pos( 0.0, 0.0, 0.0 );
371 
372  // prepare operations
373  std::vector<V_OP> ops;
374 
375  // TODO boundary, Extraordinary Vertex and Creased Surfaces
376  // see "A Factored Approach to Subdivision Surfaces"
377  // http://faculty.cs.tamu.edu/schaefer/research/tutorial.pdf
378  // and http://www.cs.utah.edu/~lacewell/subdeval
379  if ( mesh.is_boundary( vh ) ) {
380  pos = mesh.point( vh );
381  ops.resize( 3 );
382  ops[0] = V_OP( 1.f / 3.f, vh );
383  int i = 1;
384  for ( auto ve_itr = mesh.ve_iter( vh ); ve_itr.is_valid(); ++ve_itr ) {
385  if ( mesh.is_boundary( *ve_itr ) ) {
386  pos += mesh.point( mesh.property( m_epH, *ve_itr ) );
387  ops[i++] = V_OP( 1.f / 3.f, mesh.property( m_epH, *ve_itr ) );
388  }
389  }
390  pos /= 3.0;
391  }
392  else // inner vertex
393  {
394  /* For each (non boundary) vertex V, introduce a new vertex whose
395  position is F/n + 2E/n + (n-3)V/n where F is the average of
396  the new face vertices of all faces adjacent to the old vertex
397  V, E is the average of the midpoints of all edges incident
398  on the old vertex V, and n is the number of edges incident on
399  the vertex.
400  */
401  const uint valence = mesh.valence( vh );
402  const Scalar inv_v2 = 1.f / ( valence * valence );
403  ops.resize( valence * 2 + 1 );
404 
405  int i = 0;
406  for ( auto voh_it = mesh.voh_iter( vh ); voh_it.is_valid(); ++voh_it ) {
407  pos += mesh.point( mesh.to_vertex_handle( *voh_it ) );
408  ops[i++] = V_OP( inv_v2, mesh.to_vertex_handle( *voh_it ) );
409  }
410  pos *= inv_v2;
411 
412  deprecated::TopologicalMesh::Point Q( 0, 0, 0 );
413  for ( auto vf_itr = mesh.vf_iter( vh ); vf_itr.is_valid(); ++vf_itr ) {
414  Q += mesh.point( mesh.property( m_fpH, *vf_itr ) );
415  ops[i++] = V_OP( inv_v2, mesh.property( m_fpH, *vf_itr ) );
416  }
417 
418  Q *= inv_v2;
419 
420  pos += mesh.point( vh ) * ( valence - 2.0 ) / valence + Q;
421  ops[i] = V_OP( ( valence - 2.0 ) / valence, vh );
422  }
423 
424  // don't set yet since would be needed for other vertices;
425  mesh.property( m_vpPos, vh ) = pos;
426 
427 #pragma omp critical
428  { m_oldVertexOps[iter].push_back( V_OPS( vh, ops ) ); }
429 }
430 
431 void CatmullClarkSubdivider::recompute( const Vector3Array& newCoarseVertices,
432  const Vector3Array& newCoarseNormals,
433  Vector3Array& newSubdivVertices,
434  Vector3Array& newSubdivNormals,
436  // update vertices
437  auto inTriIndexProp = mesh.getInputTriangleMeshIndexPropHandle();
438  auto hNormalProp = mesh.halfedge_normals_pph();
439 #pragma omp parallel for
440  for ( int i = 0; i < int( mesh.n_halfedges() ); ++i ) {
441  auto h = mesh.halfedge_handle( i );
442  // set position on coarse mesh vertices
443  auto vh = mesh.property( m_hV, h );
444  if ( vh.idx() != -1 ) // avoid both boundary and non-coarse halfedges
445  {
446  auto idx = mesh.property( inTriIndexProp, h );
447  mesh.set_point( vh, newCoarseVertices[idx] );
448  mesh.property( hNormalProp, h ) = newCoarseNormals[idx];
449  }
450  }
451  // for each subdiv step
452  for ( int i = 0; i < int( m_oldVertexOps.size() ); ++i ) {
453  // reapply newFaceVertexOps
454 #pragma omp parallel for
455  for ( int j = 0; j < int( m_newFaceVertexOps[i].size() ); ++j ) {
456  Ra::Core::Vector3 pos( 0, 0, 0 );
457  const auto& ops = m_newFaceVertexOps[i][j];
458  for ( const auto& op : ops.second ) {
459  pos += op.first * mesh.point( op.second );
460  }
461  mesh.set_point( ops.first, pos );
462  }
463  // reapply newEdgeVertexOps
464 #pragma omp parallel for
465  for ( int j = 0; j < int( m_newEdgeVertexOps[i].size() ); ++j ) {
466  Ra::Core::Vector3 pos( 0, 0, 0 );
467  const auto& ops = m_newEdgeVertexOps[i][j];
468  for ( const auto& op : ops.second ) {
469  pos += op.first * mesh.point( op.second );
470  }
471  mesh.set_point( ops.first, pos );
472  }
473  // reapply oldVertexOps
474  std::vector<Ra::Core::Vector3> pos( m_oldVertexOps[i].size() );
475 #pragma omp parallel for
476  for ( int j = 0; j < int( m_oldVertexOps[i].size() ); ++j ) {
477  pos[j] = Ra::Core::Vector3( 0, 0, 0 );
478  const auto& ops = m_oldVertexOps[i][j];
479  for ( const auto& op : ops.second ) {
480  pos[j] += op.first * mesh.point( op.second );
481  }
482  }
483  // then commit pos for old vertices
484 #pragma omp parallel for schedule( static )
485  for ( int j = 0; j < int( m_oldVertexOps[i].size() ); ++j ) {
486  mesh.set_point( m_oldVertexOps[i][j].first, pos[j] );
487  }
488  // deal with normal on edges centers (other non-static properties can be updated the same
489  // way)
490  // This loop should not be parallelized!
491  for ( int j = 0; j < int( m_newEdgePropOps[i].size() ); ++j ) {
492  Ra::Core::Vector3 nor( 0, 0, 0 );
493  const auto& ops = m_newEdgePropOps[i][j];
494  for ( const auto& op : ops.second ) {
495  nor += op.first * mesh.property( hNormalProp, op.second );
496  }
497  mesh.property( hNormalProp, ops.first ) = nor.normalized();
498  }
499  // deal with normal on faces centers (other non-static properties can be updated the same
500  // way)
501 #pragma omp parallel for
502  for ( int j = 0; j < int( m_newFacePropOps[i].size() ); ++j ) {
503  Ra::Core::Vector3 nor( 0, 0, 0 );
504  const auto& ops = m_newFacePropOps[i][j];
505  for ( const auto& op : ops.second ) {
506  nor += op.first * mesh.property( hNormalProp, op.second );
507  }
508  mesh.property( hNormalProp, ops.first ) = nor.normalized();
509  }
510  }
511  // deal with normals from triangulation (other non-static properties can be updated the same
512  // way)
513 #pragma omp parallel for
514  for ( int j = 0; j < int( m_triangulationPropOps.size() ); ++j ) {
515  Ra::Core::Vector3 nor( 0, 0, 0 );
516  const auto& ops = m_triangulationPropOps[j];
517  for ( const auto& op : ops.second ) {
518  nor += op.first * mesh.property( hNormalProp, op.second );
519  }
520  mesh.property( hNormalProp, ops.first ) = nor.normalized();
521  }
522  // update subdivided TriangleMesh vertices and normals
523  auto outTriIndexProp = mesh.getOutputTriangleMeshIndexPropHandle();
524 #pragma omp parallel for
525  for ( int i = 0; i < int( mesh.n_halfedges() ); ++i ) {
526  auto h = mesh.halfedge_handle( i );
527  if ( !mesh.is_boundary( h ) ) {
528  auto idx = mesh.property( outTriIndexProp, h );
529  newSubdivVertices[idx] = mesh.point( mesh.to_vertex_handle( h ) );
530  newSubdivNormals[idx] = mesh.property( hNormalProp, h );
531  }
532  }
533 }
534 
535 } // namespace Geometry
536 } // namespace Core
537 } // namespace Ra
void recompute(const Vector3Array &newCoarseVertices, const Vector3Array &newCoarseNormals, Vector3Array &newSubdivVertices, Vector3Array &newSubdivNormals, deprecated::TopologicalMesh &mesh)
const OpenMesh::HPropHandleT< Index > & getInputTriangleMeshIndexPropHandle() const
const OpenMesh::HPropHandleT< Index > & getOutputTriangleMeshIndexPropHandle() const
HalfedgeHandle halfedge_handle(VertexHandle vh, FaceHandle fh) const
Definition: Cage.cpp:3