Radium Engine  1.5.20
Loading...
Searching...
No Matches
CatmullClarkSubdivider.cpp
1#include <Core/Geometry/CatmullClarkSubdivider.hpp>
2
3namespace Ra {
4namespace Core {
5namespace Geometry {
6
7bool 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
27bool 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
37bool 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
167void 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
250void 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
321void 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
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
367void 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
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
431void 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
T clear(T... args)
@ Geometry
"Geometry" render objects are those loaded using Radium::IO and generated by GeometrySystem
hepler function to manage enum as underlying types in VariableSet
Definition Cage.cpp:3
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T size(T... args)