Radium Engine  1.5.20
Loading...
Searching...
No Matches
LoopSubdivider.cpp
1#include <Core/Geometry/LoopSubdivider.hpp>
2
3namespace Ra {
4namespace Core {
5namespace Geometry {
6
7bool 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
23bool 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
30bool 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
102void 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
117void 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
184void 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
253void 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
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
292void 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
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
349void 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
@ 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 resize(T... args)