1#include <Core/Geometry/LoopSubdivider.hpp>
7bool LoopSubdivider::prepare( deprecated::TopologicalMesh& mesh ) {
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 ); }
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 ); }
23bool LoopSubdivider::cleanup( deprecated::TopologicalMesh& mesh ) {
24 mesh.remove_property( m_vpPos );
25 mesh.remove_property( m_epPos );
26 mesh.remove_property( m_hV );
30bool LoopSubdivider::subdivide( deprecated::TopologicalMesh& mesh,
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 );
42 deprecated::TopologicalMesh::FaceIter fit, f_end;
43 deprecated::TopologicalMesh::EdgeIter eit, e_end;
46 for ( uint iter = 0; iter < n; ++iter ) {
47 const size_t NV = mesh.n_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 );
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 );
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 );
74 m_newEdgePropOps[iter].shrink_to_fit();
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 );
83 m_newFacePropOps[iter].shrink_to_fit();
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 ) );
95 CORE_ASSERT( OpenMesh::Utils::MeshCheckerT<deprecated::TopologicalMesh>( mesh ).check(),
96 "LoopSubdivision ended with a bad topology." );
102void LoopSubdivider::split_face( deprecated::TopologicalMesh& mesh,
103 const deprecated::TopologicalMesh::FaceHandle& fh,
105 using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
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 ) ) );
112 corner_cutting( mesh, heh1, iter );
113 corner_cutting( mesh, heh2, iter );
114 corner_cutting( mesh, heh3, iter );
117void LoopSubdivider::corner_cutting( deprecated::TopologicalMesh& mesh,
118 const deprecated::TopologicalMesh::HalfedgeHandle& he,
120 using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
121 using VHandle = deprecated::TopologicalMesh::VertexHandle;
122 using FHandle = deprecated::TopologicalMesh::FaceHandle;
126 HeHandle heh5( heh1 );
127 HeHandle heh6( mesh.next_halfedge_handle( heh1 ) );
130 for ( ; mesh.next_halfedge_handle( mesh.next_halfedge_handle( heh5 ) ) != heh1;
131 heh5 = mesh.next_halfedge_handle( heh5 ) )
134 VHandle vh1 = mesh.to_vertex_handle( heh1 );
135 VHandle vh2 = mesh.to_vertex_handle( heh5 );
137 HeHandle heh2( mesh.next_halfedge_handle( heh5 ) );
138 HeHandle heh3( mesh.new_edge( vh1, vh2 ) );
139 HeHandle heh4( mesh.opposite_halfedge_handle( heh3 ) );
155 FHandle fh_old( mesh.face_handle( heh6 ) );
156 FHandle fh_new( mesh.new_face() );
159 mesh.set_next_halfedge_handle( heh4, heh6 );
160 mesh.set_next_halfedge_handle( heh5, heh4 );
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 );
168 mesh.set_next_halfedge_handle( heh1, heh3 );
169 mesh.set_next_halfedge_handle( heh3, heh2 );
171 mesh.set_face_handle( heh1, fh_new );
172 mesh.set_face_handle( heh2, fh_new );
173 mesh.set_face_handle( heh3, fh_new );
175 mesh.set_halfedge_handle( fh_new, heh1 );
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 } } } );
184void LoopSubdivider::split_edge( deprecated::TopologicalMesh& mesh,
185 const deprecated::TopologicalMesh::EdgeHandle& eh,
187 using HeHandle = deprecated::TopologicalMesh::HalfedgeHandle;
188 using VHandle = deprecated::TopologicalMesh::VertexHandle;
191 HeHandle heh = mesh.halfedge_handle( eh, 0 );
192 HeHandle opp_heh = mesh.halfedge_handle( eh, 1 );
194 HeHandle opp_new_heh;
197 VHandle vh1( mesh.to_vertex_handle( heh ) );
200 vh = mesh.property( m_epPos, eh );
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 ) ) )
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 ) )
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 );
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 );
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 );
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 } } } );
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 );
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 } } } );
245 mesh.set_halfedge_handle( vh, new_heh );
246 mesh.set_halfedge_handle( vh1, opp_new_heh );
249 mesh.adjust_outgoing_halfedge( vh );
250 mesh.adjust_outgoing_halfedge( vh1 );
253void LoopSubdivider::compute_midpoint( deprecated::TopologicalMesh& mesh,
254 const deprecated::TopologicalMesh::EdgeHandle& eh,
256 deprecated::TopologicalMesh::HalfedgeHandle heh = mesh.halfedge_handle( eh, 0 );
257 deprecated::TopologicalMesh::HalfedgeHandle opp_heh = mesh.halfedge_handle( eh, 1 );
259 deprecated::TopologicalMesh::Point pos = mesh.point( mesh.to_vertex_handle( heh ) );
260 pos += mesh.point( mesh.to_vertex_handle( opp_heh ) );
265 if ( mesh.is_boundary( eh ) ) {
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 ) );
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 ) ) );
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 ) ) );
286 auto vh = mesh.add_vertex( pos );
287 mesh.property( m_epPos, eh ) = vh;
288 m_newVertexOps[iter].push_back( V_OPS( vh, ops ) );
292void LoopSubdivider::smooth( deprecated::TopologicalMesh& mesh,
293 const deprecated::TopologicalMesh::VertexHandle& vh,
295 using VHandle = deprecated::TopologicalMesh::VertexHandle;
297 deprecated::TopologicalMesh::Point pos( 0.0, 0.0, 0.0 );
301 if ( mesh.is_boundary( vh ) )
303 deprecated::TopologicalMesh::HalfedgeHandle heh;
304 deprecated::TopologicalMesh::HalfedgeHandle prev_heh;
305 heh = mesh.halfedge_handle( vh );
307 assert( mesh.is_boundary( mesh.edge_handle( heh ) ) );
309 prev_heh = mesh.prev_halfedge_handle( heh );
311 VHandle to_vh = mesh.to_vertex_handle( heh );
312 VHandle from_vh = mesh.from_vertex_handle( prev_heh );
315 pos = mesh.point( vh );
317 pos += mesh.point( to_vh );
318 pos += mesh.point( from_vh );
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 );
328 deprecated::TopologicalMesh::VertexVertexIter vvit;
329 const uint valence = mesh.valence( vh );
330 ops.
resize( valence + 1 );
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 );
338 pos *= m_weights[valence].second;
339 pos += m_weights[valence].first * mesh.point( vh );
340 ops[i] = V_OP( m_weights[valence].first, vh );
343 mesh.property( m_vpPos, vh ) = pos;
346 { m_oldVertexOps[iter].push_back( V_OPS( vh, ops ) ); }
350 const Vector3Array& newCoarseNormals,
351 Vector3Array& newSubdivVertices,
352 Vector3Array& newSubdivNormals,
356 auto hNormalProp = mesh.halfedge_normals_pph();
357#pragma omp parallel for
358 for (
int i = 0; i < int( mesh.n_halfedges() ); ++i ) {
361 auto vh = mesh.property( m_hV, h );
362 if ( vh.idx() != -1 )
364 auto idx = mesh.property( inTriIndexProp, h );
365 mesh.set_point( vh, newCoarseVertices[idx] );
366 mesh.property( hNormalProp, h ) = newCoarseNormals[idx];
370 for (
int i = 0; i < int( m_oldVertexOps.size() ); ++i ) {
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 );
379 mesh.set_point( ops.first, pos );
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 );
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] );
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 );
405 mesh.property( hNormalProp, ops.first ) = nor.normalized();
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 );
416 mesh.property( hNormalProp, ops.first ) = nor.normalized();
421#pragma omp parallel for
422 for (
int i = 0; i < int( mesh.n_halfedges() ); ++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 );
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