From 653b1b8f56c265a13acb26fde5d5bc277a7b2da2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Thu, 28 Jun 2012 09:32:20 +0000 Subject: [PATCH] Added catmull clark subdivider. Thanks to Leon Kos for the code. git-svn-id: http://www.openmesh.org/svnrepo/OpenMesh/trunk@609 fdac6126-5c0c-442c-9429-916003d36597 --- Doc/subdivider.docu | 18 + .../Apps/Subdivider/SubdivideWidget.cc | 10 +- .../Apps/Subdivider/SubdivideWidget.hh | 1 + src/OpenMesh/Apps/Subdivider/subdivider.cc | 10 +- .../Tools/Subdivider/Uniform/CatmullClarkT.cc | 397 ++++++++++++++++++ .../Tools/Subdivider/Uniform/CatmullClarkT.hh | 175 ++++++++ src/OpenMesh/Tools/Utils/MeshCheckerT.hh | 2 +- src/Unittests/unittests.cc | 1 + src/Unittests/unittests_subdivider.hh | 248 +++++++++++ 9 files changed, 857 insertions(+), 5 deletions(-) create mode 100644 src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.cc create mode 100644 src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.hh create mode 100644 src/Unittests/unittests_subdivider.hh diff --git a/Doc/subdivider.docu b/Doc/subdivider.docu index 1d98a91c..b2e9d3b5 100644 --- a/Doc/subdivider.docu +++ b/Doc/subdivider.docu @@ -2,6 +2,8 @@ /** \page subdivider_docu Sudivision Tools +\section Overview + The %OpenMesh library provides a few tools for uniform and adaptive subdivision: @@ -11,11 +13,27 @@ subdivision: -# OpenMesh::Subdivider::Uniform::ModifiedButterflyT -# OpenMesh::Subdivider::Uniform::InterpolatingSqrt3LGT -# OpenMesh::Subdivider::Uniform::CompositeT + -# OpenMesh::Subdivider::Uniform::CatmullClarkT -# Adaptive subdivision -# OpenMesh::Subdivider::Adaptive::CompositeT -# Simple subdivision -# OpenMesh::Subdivider::Uniform::LongestEdgeT +\section Usage +The subdividers directly work on an OpenMesh. The following example shows how to use them: + +\code + #include + + // Initialize subdivider + OpenMesh::Subdivider::Uniform::CatmullClarkT catmull; + + // Execute 3 subdivision steps + catmull.attach(mesh_); + catmull( 3 ); + catmull.detach(); +\endcode + */ //----------------------------------------------------------------------------- diff --git a/src/OpenMesh/Apps/Subdivider/SubdivideWidget.cc b/src/OpenMesh/Apps/Subdivider/SubdivideWidget.cc index f55eb2a5..7c3b661a 100644 --- a/src/OpenMesh/Apps/Subdivider/SubdivideWidget.cc +++ b/src/OpenMesh/Apps/Subdivider/SubdivideWidget.cc @@ -72,11 +72,10 @@ #include #include #include +#include -// My stuff #include -// using namespace OpenMesh::Subdivider; @@ -149,6 +148,7 @@ SubdivideWidget(QWidget* _parent, const char* _name) QRadioButton* radio4 = new QRadioButton( "Sqrt(3)" ); QRadioButton* radio5 = new QRadioButton( "Interpolating Sqrt3" ); QRadioButton* radio6 = new QRadioButton( "Modified Butterfly" ); + // QRadioButton* radio7 = new QRadioButton( "Catmull Clark" ); // Disabled, as it needs a quad mesh! radio3->setChecked( TRUE ); sel_topo_type = SOP_UniformLoop; @@ -158,6 +158,7 @@ SubdivideWidget(QWidget* _parent, const char* _name) buttonGroup->addButton(radio4, SOP_UniformSqrt3); buttonGroup->addButton(radio5, SOP_UniformInterpolatingSqrt3); buttonGroup->addButton(radio6, SOP_ModifiedButterfly); + //buttonGroup->addButton(radio7, SOP_CatmullClark); vbox->addWidget(radio1); vbox->addWidget(radio2); @@ -165,6 +166,7 @@ SubdivideWidget(QWidget* _parent, const char* _name) vbox->addWidget(radio4); vbox->addWidget(radio5); vbox->addWidget(radio6); + // vbox->addWidget(radio7); QObject::connect( buttonGroup, SIGNAL( buttonPressed(int) ), this, SLOT( slot_select_sop(int) ) ); @@ -191,6 +193,7 @@ SubdivideWidget(QWidget* _parent, const char* _name) subdivider_[SOP_UniformSqrt3] = new Uniform::Sqrt3T; subdivider_[SOP_UniformInterpolatingSqrt3] = new Uniform::InterpolatingSqrt3LGT< Mesh >; subdivider_[SOP_ModifiedButterfly] = new Uniform::ModifiedButterflyT; + subdivider_[SOP_CatmullClark] = new Uniform::CatmullClarkT; } @@ -204,9 +207,10 @@ void SubdivideWidget::slot_select_sop(int i) case SOP_UniformCompositeLoop: case SOP_UniformCompositeSqrt3: case SOP_UniformLoop: + case SOP_UniformSqrt3: case SOP_UniformInterpolatingSqrt3: case SOP_ModifiedButterfly: - case SOP_UniformSqrt3: sel_topo_type = (SOPType)i; break; + case SOP_CatmullClark: sel_topo_type = (SOPType)i; break; default: sel_topo_type = SOP_Undefined; } } diff --git a/src/OpenMesh/Apps/Subdivider/SubdivideWidget.hh b/src/OpenMesh/Apps/Subdivider/SubdivideWidget.hh index fae1043b..e6476233 100644 --- a/src/OpenMesh/Apps/Subdivider/SubdivideWidget.hh +++ b/src/OpenMesh/Apps/Subdivider/SubdivideWidget.hh @@ -77,6 +77,7 @@ public: SOP_UniformSqrt3, SOP_UniformInterpolatingSqrt3, SOP_ModifiedButterfly, + SOP_CatmullClark, SOP_Undefined }; diff --git a/src/OpenMesh/Apps/Subdivider/subdivider.cc b/src/OpenMesh/Apps/Subdivider/subdivider.cc index 3dfadc02..4f7ff98b 100644 --- a/src/OpenMesh/Apps/Subdivider/subdivider.cc +++ b/src/OpenMesh/Apps/Subdivider/subdivider.cc @@ -53,6 +53,7 @@ #include #include #include +#include // ---------------------------------------------------------------------------- @@ -68,6 +69,7 @@ typedef Uniform::CompositeSqrt3T< CMesh > CompositeSqrt3; typedef Uniform::CompositeLoopT< CMesh > CompositeLoop; typedef Uniform::InterpolatingSqrt3LGT< Mesh > InterpolatingSqrt3LG; typedef Uniform::ModifiedButterflyT< Mesh > ModifiedButterfly; +typedef Uniform::CatmullClarkT< Mesh > CatmullClark; using OpenMesh::Utils::Timer; @@ -195,7 +197,8 @@ int main(int argc, char **argv) TypeCompSqrt3, TypeCompLoop, TypeLabsikGreiner, - TypeModButterfly + TypeModButterfly, + TypeCatmullClark } st = TypeSqrt3; Timer::Format fmt = Timer::Automatic; @@ -211,6 +214,7 @@ int main(int argc, char **argv) case 'L': st = TypeCompLoop; break; case 'b': st = TypeLabsikGreiner; break; case 'B': st = TypeModButterfly; break; + case 'C': st = TypeCatmullClark; std::cerr << "Not yet supported, as it needs a poly mesh!"; break; case 'f': { switch(*optarg) @@ -255,6 +259,7 @@ int main(int argc, char **argv) rc += mainT ( n, ifname, "", fmt ); rc += mainT ( n, ifname, "", fmt ); rc += mainT ( n, ifname, "", fmt ); + rc += mainT ( n, ifname, "", fmt ); if (rc) return rc; @@ -294,6 +299,8 @@ int main(int argc, char **argv) return mainT ( n, ifname, ofname, fmt ); case TypeModButterfly: return mainT ( n, ifname, ofname, fmt ); + case TypeCatmullClark: + return mainT ( n, ifname, ofname, fmt ); } return 1; } @@ -312,6 +319,7 @@ void usage_and_exit(int _xcode) << " -S\tComposite Sqrt3\n" << " -b\tInterpolating Sqrt3 Labsik-Greiner\n" << " -B\tModified Butterfly\n" + // << " -C\tCatmullClark\n" << std::endl; exit(_xcode); } diff --git a/src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.cc b/src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.cc new file mode 100644 index 00000000..ff73c2ba --- /dev/null +++ b/src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.cc @@ -0,0 +1,397 @@ +/*===========================================================================*\ + * * + * OpenMesh * + * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * + * www.openmesh.org * + * * + *---------------------------------------------------------------------------* + * This file is part of OpenMesh. * + * * + * OpenMesh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of * + * the License, or (at your option) any later version with the * + * following exceptions: * + * * + * If other files instantiate templates or use macros * + * or inline functions from this file, or you compile this file and * + * link it with other files to produce an executable, this file does * + * not by itself cause the resulting executable to be covered by the * + * GNU Lesser General Public License. This exception does not however * + * invalidate any other reasons why the executable file might be * + * covered by the GNU Lesser General Public License. * + * * + * OpenMesh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU LesserGeneral Public * + * License along with OpenMesh. If not, * + * see . * + * * +\*===========================================================================*/ + +/*===========================================================================*\ + * * + * $Revision: 520 $ * + * $Date: 2012-01-20 15:29:31 +0100 (Fr, 20 Jan 2012) $ * + * * +\*===========================================================================*/ + +//============================================================================= +// +// CLASS CatmullClarkT - IMPLEMENTATION +// +//============================================================================= + +#define OPENMESH_SUBDIVIDER_UNIFORM_CATMULLCLARK_CC + +//== INCLUDES ================================================================= + +#include "CatmullClarkT.hh" +#include + +//== NAMESPACES =============================================================== + +namespace OpenMesh { // BEGIN_NS_OPENMESH +namespace Subdivider { // BEGIN_NS_SUBVIDER +namespace Uniform { // BEGIN_NS_UNIFORM + +//== IMPLEMENTATION ========================================================== + +template +bool +CatmullClarkT< MeshType, RealType >::prepare( MeshType& _m ) +{ + _m.add_property( vp_pos_ ); + _m.add_property( ep_pos_ ); + _m.add_property( fp_pos_ ); + _m.add_property( creaseWeights_ ); + + // initialize all weights to 0 (= smooth edge) + for( EdgeIter e_it = _m.edges_begin(); e_it != _m.edges_end(); ++e_it) + _m.property(creaseWeights_, e_it.handle() ) = 0.0; + + return true; +} + +//----------------------------------------------------------------------------- + +template +bool +CatmullClarkT::cleanup( MeshType& _m ) +{ + _m.remove_property( vp_pos_ ); + _m.remove_property( ep_pos_ ); + _m.remove_property( fp_pos_ ); + _m.remove_property( creaseWeights_ ); + return true; +} + +//----------------------------------------------------------------------------- + +template +bool +CatmullClarkT::subdivide( MeshType& _m , size_t _n , const bool _update_points) +{ + // Do _n subdivisions + for ( size_t i = 0; i < _n; ++i) + { + + // Compute face centroid + FaceIter f_itr = _m.faces_begin(); + FaceIter f_end = _m.faces_end(); + for ( ; f_itr != f_end; ++f_itr) + { + Point centroid; + _m.calc_face_centroid( f_itr.handle(), centroid); + _m.property( fp_pos_, f_itr.handle() ) = centroid; + } + + // Compute position for new (edge-) vertices and store them in the edge property + EdgeIter e_itr = _m.edges_begin(); + EdgeIter e_end = _m.edges_end(); + for ( ; e_itr != e_end; ++e_itr) + compute_midpoint( _m, e_itr.handle() ); + + // compute new positions for old vertices + VertexIter v_itr = _m.vertices_begin(); + VertexIter v_end = _m.vertices_end(); + for ( ; v_itr != v_end; ++v_itr) + update_vertex( _m, v_itr.handle() ); + + // Commit changes in geometry + v_itr = _m.vertices_begin(); + for ( ; v_itr != v_end; ++v_itr) + _m.set_point(v_itr, _m.property( vp_pos_, v_itr ) ); + + // Split each edge at midpoint stored in edge property ep_pos_; + // Attention! Creating new edges, hence make sure the loop ends correctly. + e_itr = _m.edges_begin(); + for ( ; e_itr != e_end; ++e_itr) + split_edge( _m, e_itr.handle() ); + + // Commit changes in topology and reconsitute consistency + // Attention! Creating new faces, hence make sure the loop ends correctly. + f_itr = _m.faces_begin(); + for ( ; f_itr != f_end; ++f_itr) + split_face( _m, f_itr.handle()); + + +#if defined(_DEBUG) || defined(DEBUG) + // Now we have an consistent mesh! + assert( OpenMesh::Utils::MeshCheckerT(_m).check() ); +#endif + } + + _m.update_normals(); + + return true; +} + +//----------------------------------------------------------------------------- + +template +void +CatmullClarkT::split_face( MeshType& _m, const FaceHandle& _fh) +{ + /* + Split an n-gon into n quads by connecting + each vertex of fh to vh. + + - _fh will remain valid (it will become one of the quads) + - the halfedge handles of the new quads will + point to the old halfedges + */ + + // Since edges already refined (valence*2) + size_t valence = _m.valence(_fh)/2; + + // new mesh vertex from face centroid + VertexHandle vh = _m.add_vertex(_m.property( fp_pos_, _fh )); + + HalfedgeHandle hend = _m.halfedge_handle(_fh); + HalfedgeHandle hh = _m.next_halfedge_handle(hend); + + HalfedgeHandle hold = _m.new_edge(_m.to_vertex_handle(hend), vh); + + _m.set_next_halfedge_handle(hend, hold); + _m.set_face_handle(hold, _fh); + + hold = _m.opposite_halfedge_handle(hold); + + for(size_t i = 1; i < valence; i++) + { + HalfedgeHandle hnext = _m.next_halfedge_handle(hh); + + FaceHandle fnew = _m.new_face(); + + _m.set_halfedge_handle(fnew, hh); + + HalfedgeHandle hnew = _m.new_edge(_m.to_vertex_handle(hnext), vh); + + _m.set_face_handle(hnew, fnew); + _m.set_face_handle(hold, fnew); + _m.set_face_handle(hh, fnew); + _m.set_face_handle(hnext, fnew); + + _m.set_next_halfedge_handle(hnew, hold); + _m.set_next_halfedge_handle(hold, hh); + _m.set_next_halfedge_handle(hh, hnext); + hh = _m.next_halfedge_handle(hnext); + _m.set_next_halfedge_handle(hnext, hnew); + + hold = _m.opposite_halfedge_handle(hnew); + } + + _m.set_next_halfedge_handle(hold, hh); + _m.set_next_halfedge_handle(hh, hend); + hh = _m.next_halfedge_handle(hend); + _m.set_next_halfedge_handle(hend, hh); + _m.set_next_halfedge_handle(hh, hold); + + _m.set_face_handle(hold, _fh); + + _m.set_halfedge_handle(vh, hold); +} + +//----------------------------------------------------------------------------- + +template +void +CatmullClarkT::split_edge( MeshType& _m, const EdgeHandle& _eh) +{ + HalfedgeHandle heh = _m.halfedge_handle(_eh, 0); + HalfedgeHandle opp_heh = _m.halfedge_handle(_eh, 1); + + HalfedgeHandle new_heh, opp_new_heh, t_heh; + VertexHandle vh; + VertexHandle vh1( _m.to_vertex_handle(heh)); + Point zero(0,0,0); + + // new vertex + vh = _m.new_vertex( zero ); + _m.set_point( vh, _m.property( ep_pos_, _eh ) ); + + // Re-link mesh entities + if (_m.is_boundary(_eh)) + { + for (t_heh = heh; + _m.next_halfedge_handle(t_heh) != opp_heh; + t_heh = _m.opposite_halfedge_handle(_m.next_halfedge_handle(t_heh))) + {} + } + else + { + for (t_heh = _m.next_halfedge_handle(opp_heh); + _m.next_halfedge_handle(t_heh) != opp_heh; + t_heh = _m.next_halfedge_handle(t_heh) ) + {} + } + + new_heh = _m.new_edge(vh, vh1); + opp_new_heh = _m.opposite_halfedge_handle(new_heh); + _m.set_vertex_handle( heh, vh ); + + _m.set_next_halfedge_handle(t_heh, opp_new_heh); + _m.set_next_halfedge_handle(new_heh, _m.next_halfedge_handle(heh)); + _m.set_next_halfedge_handle(heh, new_heh); + _m.set_next_halfedge_handle(opp_new_heh, opp_heh); + + if (_m.face_handle(opp_heh).is_valid()) + { + _m.set_face_handle(opp_new_heh, _m.face_handle(opp_heh)); + _m.set_halfedge_handle(_m.face_handle(opp_new_heh), opp_new_heh); + } + + if( _m.face_handle(heh).is_valid()) + { + _m.set_face_handle( new_heh, _m.face_handle(heh) ); + _m.set_halfedge_handle( _m.face_handle(heh), heh ); + } + + _m.set_halfedge_handle( vh, new_heh); + _m.set_halfedge_handle( vh1, opp_new_heh ); + + // Never forget this, when playing with the topology + _m.adjust_outgoing_halfedge( vh ); + _m.adjust_outgoing_halfedge( vh1 ); +} + +//----------------------------------------------------------------------------- + +template +void +CatmullClarkT::compute_midpoint( MeshType& _m, const EdgeHandle& _eh) +{ + HalfedgeHandle heh, opp_heh; + + heh = _m.halfedge_handle( _eh, 0); + opp_heh = _m.halfedge_handle( _eh, 1); + + Point pos( _m.point( _m.to_vertex_handle( heh))); + + pos += _m.point( _m.to_vertex_handle( opp_heh)); + + // boundary edge: just average vertex positions + // this yields the [1/2 1/2] mask + if (_m.is_boundary(_eh) ) + { + pos *= 0.5; + } +// else if (_m.status(_eh).selected() ) +// { +// pos *= 0.5; // change this +// } + + else // inner edge: add neighbouring Vertices to sum + // this yields the [1/16 1/16; 3/8 3/8; 1/16 1/16] mask + { + pos += _m.property(fp_pos_, _m.face_handle(heh)); + pos += _m.property(fp_pos_, _m.face_handle(opp_heh)); + pos *= 0.25; + } + _m.property( ep_pos_, _eh ) = pos; +} + +//----------------------------------------------------------------------------- + +template +void +CatmullClarkT::update_vertex( MeshType& _m, const VertexHandle& _vh) +{ + Point pos(0.0,0.0,0.0); + + // TODO boundary, Extraordinary Vertex and Creased Surfaces + // see "A Factored Approach to Subdivision Surfaces" + // http://faculty.cs.tamu.edu/schaefer/research/tutorial.pdf + // and http://www.cs.utah.edu/~lacewell/subdeval + if ( _m.is_boundary( _vh)) + { + Normal Vec; + pos = _m.point(_vh); + VertexEdgeIter ve_itr; + for ( ve_itr = _m.ve_iter( _vh); ve_itr; ++ve_itr) + if ( _m.is_boundary( ve_itr.handle())) + pos += _m.property( ep_pos_, ve_itr.handle()); + pos /= 3.0; + } + else // inner vertex + { + /* For each (non boundary) vertex V, introduce a new vertex whose + position is F/n + 2E/n + (n-3)V/n where F is the average of + the new face vertices of all faces adjacent to the old vertex + V, E is the average of the midpoints of all edges incident + on the old vertex V, and n is the number of edges incident on + the vertex. + */ + + /* + Normal Vec; + VertexEdgeIter ve_itr; + double valence(0.0); + + // R = Calculate Valence and sum of edge midpoints + for ( ve_itr = _m.ve_iter( _vh); ve_itr; ++ve_itr) + { + valence+=1.0; + pos += _m.property(ep_pos_, ve_itr.handle()); + } + pos /= valence*valence; + */ + + RealType valence(0.0); + VOHIter voh_it = _m.voh_iter( _vh ); + for( ; voh_it; ++voh_it ) + { + pos += _m.point( _m.to_vertex_handle( voh_it ) ); + valence+=1.0; + } + pos /= valence*valence; + + VertexFaceIter vf_itr; + Point Q(0, 0, 0); + double neigboring_faces(0.0); + + for ( vf_itr = _m.vf_iter( _vh); vf_itr; ++vf_itr) //, neigboring_faces += 1.0 ) + { + Q += _m.property(fp_pos_, vf_itr.handle()); + } + + Q /= valence*valence;//neigboring_faces; + + pos += _m.point(_vh) * (valence-2.0)/valence + Q; + // pos = vector_cast(_m.point(_vh)); + } + + _m.property( vp_pos_, _vh ) = pos; +} + +//----------------------------------------------------------------------------- + +//============================================================================= +} // END_NS_UNIFORM +} // END_NS_SUBDIVIDER +} // END_NS_OPENMESH +//============================================================================= diff --git a/src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.hh b/src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.hh new file mode 100644 index 00000000..fee0aa9e --- /dev/null +++ b/src/OpenMesh/Tools/Subdivider/Uniform/CatmullClarkT.hh @@ -0,0 +1,175 @@ +/*===========================================================================*\ + * * + * OpenMesh * + * Copyright (C) 2001-2011 by Computer Graphics Group, RWTH Aachen * + * www.openmesh.org * + * * + *---------------------------------------------------------------------------* + * This file is part of OpenMesh. * + * * + * OpenMesh is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License as * + * published by the Free Software Foundation, either version 3 of * + * the License, or (at your option) any later version with the * + * following exceptions: * + * * + * If other files instantiate templates or use macros * + * or inline functions from this file, or you compile this file and * + * link it with other files to produce an executable, this file does * + * not by itself cause the resulting executable to be covered by the * + * GNU Lesser General Public License. This exception does not however * + * invalidate any other reasons why the executable file might be * + * covered by the GNU Lesser General Public License. * + * * + * OpenMesh is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU LesserGeneral Public * + * License along with OpenMesh. If not, * + * see . * + * * +\*===========================================================================*/ + +/*===========================================================================*\ + * * + * $Revision: 520 $ * + * $Date: 2012-01-20 15:29:31 +0100 (Fr, 20 Jan 2012) $ * + * * +\*===========================================================================*/ + +/** \file CatmullClarkT.hh + */ + +//============================================================================= +// +// CLASS CatmullClarkT +// +//============================================================================= + + +#ifndef OPENMESH_SUBDIVIDER_UNIFORM_CATMULLCLARKT_HH +#define OPENMESH_SUBDIVIDER_UNIFORM_CATMULLCLARKT_HH + + +//== INCLUDES ================================================================= + +#include + +// -------------------- STL +#if defined(OM_CC_MIPS) +# include +#else +# include +#endif + +//== FORWARDDECLARATIONS ====================================================== + +//== NAMESPACES =============================================================== + +namespace OpenMesh { // BEGIN_NS_OPENMESH +namespace Subdivider { // BEGIN_NS_SUBVIDER +namespace Uniform { // BEGIN_NS_UNIFORM + +//== CLASS DEFINITION ========================================================= + + +/** \class CatmullClarkT CatmullClarkT.hh + Based on code from Leon Kos, CAD lab, Mech.Eng., University of Ljubljana, Slovenia + (http://www.lecad.fs.uni-lj.si/~leon) + + \note Needs a PolyMesh to work on! +*/ +template +class CatmullClarkT : public SubdividerT< MeshType, RealType > +{ +public: + + typedef typename MeshType::FaceHandle FaceHandle; + typedef typename MeshType::VertexHandle VertexHandle; + typedef typename MeshType::EdgeHandle EdgeHandle; + typedef typename MeshType::HalfedgeHandle HalfedgeHandle; + + typedef typename MeshType::Point Point; + typedef typename MeshType::Normal Normal; + typedef typename MeshType::FaceIter FaceIter; + typedef typename MeshType::EdgeIter EdgeIter; + typedef typename MeshType::VertexIter VertexIter; + + typedef typename MeshType::VertexEdgeIter VertexEdgeIter; + typedef typename MeshType::VertexFaceIter VertexFaceIter; + + typedef typename MeshType::VOHIter VOHIter; + + typedef SubdividerT< MeshType, RealType > parent_t; + + /// Constructor + CatmullClarkT( ) : parent_t() { } + + /// Constructor + CatmullClarkT(MeshType &_m) : parent_t(_m) { } + + virtual ~CatmullClarkT() {} + +public: + + const char *name() const { return "Uniform CatmullClark"; } + +protected: + + /// Initialize properties and weights + virtual bool prepare( MeshType& _m ); + + /// Remove properties and weights + virtual bool cleanup( MeshType& _m ); + + /** \brief Execute n subdivision steps + * + * @param _m Mesh to work on + * @param _n Number of iterations + * @param _update_points Unused here + * @return successful? + */ + virtual bool subdivide( MeshType& _m, size_t _n , const bool _update_points = true); + +private: + + //=========================================================================== + /** @name Topology helpers + * @{ */ + //=========================================================================== + + void split_edge( MeshType& _m, const EdgeHandle& _eh); + + void split_face( MeshType& _m, const FaceHandle& _fh); + + void compute_midpoint( MeshType& _m, const EdgeHandle& _eh); + + void update_vertex(MeshType& _m, const VertexHandle& _vh); + + /** @} */ + + +private: + OpenMesh::VPropHandleT< Point > vp_pos_; // next vertex pos + OpenMesh::EPropHandleT< Point > ep_pos_; // new edge pts + OpenMesh::FPropHandleT< Point > fp_pos_; // new face pts + OpenMesh::EPropHandleT creaseWeights_;// crease weights + +}; + + +//============================================================================= +} // END_NS_UNIFORM +} // END_NS_SUBDIVIDER +} // END_NS_OPENMESH +//============================================================================= +#if defined(OM_INCLUDE_TEMPLATES) && !defined(OPENMESH_SUBDIVIDER_UNIFORM_CATMULLCLARK_CC) +# define OPENMESH_SUBDIVIDER_TEMPLATES +# include "CatmullClarkT.cc" +#endif +//============================================================================= +#endif // OPENMESH_SUBDIVIDER_UNIFORM_CATMULLCLARKT_HH defined +//============================================================================= + diff --git a/src/OpenMesh/Tools/Utils/MeshCheckerT.hh b/src/OpenMesh/Tools/Utils/MeshCheckerT.hh index 6d1399fb..95972b32 100644 --- a/src/OpenMesh/Tools/Utils/MeshCheckerT.hh +++ b/src/OpenMesh/Tools/Utils/MeshCheckerT.hh @@ -85,7 +85,7 @@ public: CHECK_EDGES = 1, CHECK_VERTICES = 2, CHECK_FACES = 4, - CHECK_ALL = 255, + CHECK_ALL = 255 }; diff --git a/src/Unittests/unittests.cc b/src/Unittests/unittests.cc index 636de273..5721bbb3 100644 --- a/src/Unittests/unittests.cc +++ b/src/Unittests/unittests.cc @@ -7,6 +7,7 @@ #include "unittests_trimesh_collapse.hh" #include "unittests_trimesh_circulators.hh" #include "unittests_decimater.hh" +#include "unittests_subdivider.hh" #include "unittests_trimesh_normal_calculations.hh" #include "unittests_trimesh_others.hh" #include "unittests_add_face.hh" diff --git a/src/Unittests/unittests_subdivider.hh b/src/Unittests/unittests_subdivider.hh new file mode 100644 index 00000000..6772bddb --- /dev/null +++ b/src/Unittests/unittests_subdivider.hh @@ -0,0 +1,248 @@ +#ifndef INCLUDE_UNITTESTS_SUBIVIDER_HH +#define INCLUDE_UNITTESTS_SUBIVIDER_HH + +#include +#include +#include +#include + +class OpenMeshSubdivider_Poly : public OpenMeshBasePoly { + + protected: + + // This function is called before each test is run + virtual void SetUp() { + + // Do some initial stuff with the member data here... + } + + // This function is called after all tests are through + virtual void TearDown() { + + // Do some final stuff with the member data here... + } + + // Member already defined in OpenMeshBase + //Mesh mesh_; +}; + +class OpenMeshSubdivider_Triangle : public OpenMeshBase { + + protected: + + // This function is called before each test is run + virtual void SetUp() { + + // Do some initial stuff with the member data here... + } + + // This function is called after all tests are through + virtual void TearDown() { + + // Do some final stuff with the member data here... + } + + // Member already defined in OpenMeshBase + //Mesh mesh_; +}; + +/* + * ==================================================================== + * Define tests below + * ==================================================================== + */ + +/* + */ +TEST_F(OpenMeshSubdivider_Triangle, Subdivider_Loop) { + + mesh_.clear(); + + // Add some vertices + Mesh::VertexHandle vhandle[9]; + + vhandle[0] = mesh_.add_vertex(Mesh::Point(0, 0, 0)); + vhandle[1] = mesh_.add_vertex(Mesh::Point(0, 1, 0)); + vhandle[2] = mesh_.add_vertex(Mesh::Point(0, 2, 0)); + vhandle[3] = mesh_.add_vertex(Mesh::Point(1, 0, 0)); + vhandle[4] = mesh_.add_vertex(Mesh::Point(1, 1, 0)); + vhandle[5] = mesh_.add_vertex(Mesh::Point(1, 2, 0)); + vhandle[6] = mesh_.add_vertex(Mesh::Point(2, 0, 0)); + vhandle[7] = mesh_.add_vertex(Mesh::Point(2, 1, 0)); + vhandle[8] = mesh_.add_vertex(Mesh::Point(2, 2, 0)); + + // Add eight faces + std::vector face_vhandles; + + face_vhandles.push_back(vhandle[0]); + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[3]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[0]); + face_vhandles.push_back(vhandle[1]); + face_vhandles.push_back(vhandle[4]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[1]); + face_vhandles.push_back(vhandle[2]); + face_vhandles.push_back(vhandle[4]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[2]); + face_vhandles.push_back(vhandle[5]); + face_vhandles.push_back(vhandle[4]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[3]); + face_vhandles.push_back(vhandle[7]); + face_vhandles.push_back(vhandle[6]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[3]); + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[7]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[8]); + face_vhandles.push_back(vhandle[7]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[5]); + face_vhandles.push_back(vhandle[8]); + + mesh_.add_face(face_vhandles); + + // Test setup: + // 6 === 7 === 8 + // | / | / | + // | / | / | + // | / | / | + // 3 === 4 === 5 + // | / | \ | + // | / | \ | + // | / | \ | + // 0 === 1 === 2 + + // Initialize subdivider + OpenMesh::Subdivider::Uniform::Sqrt3T sqrt3; + + // Check setup + EXPECT_EQ(9, mesh_.n_vertices() ) << "Wrong number of vertices"; + EXPECT_EQ(8, mesh_.n_faces() ) << "Wrong number of faces"; + + // Execute 3 subdivision steps + sqrt3.attach(mesh_); + sqrt3( 3 ); + sqrt3.detach(); + + // Check setup + EXPECT_EQ(121, mesh_.n_vertices() ) << "Wrong number of vertices after subdivision with sqrt3"; + EXPECT_EQ(216, mesh_.n_faces() ) << "Wrong number of faces after subdivision with sqrt3"; + +} + +/* + * ==================================================================== + * Define tests below + * ==================================================================== + */ + +/* + */ +TEST_F(OpenMeshSubdivider_Poly, Subdivider_CatmullClark) { + + mesh_.clear(); + + // Add some vertices + Mesh::VertexHandle vhandle[9]; + + vhandle[0] = mesh_.add_vertex(Mesh::Point(0, 0, 0)); + vhandle[1] = mesh_.add_vertex(Mesh::Point(0, 1, 0)); + vhandle[2] = mesh_.add_vertex(Mesh::Point(0, 2, 0)); + vhandle[3] = mesh_.add_vertex(Mesh::Point(1, 0, 0)); + vhandle[4] = mesh_.add_vertex(Mesh::Point(1, 1, 0)); + vhandle[5] = mesh_.add_vertex(Mesh::Point(1, 2, 0)); + vhandle[6] = mesh_.add_vertex(Mesh::Point(2, 0, 0)); + vhandle[7] = mesh_.add_vertex(Mesh::Point(2, 1, 0)); + vhandle[8] = mesh_.add_vertex(Mesh::Point(2, 2, 0)); + + // Add four faces + std::vector face_vhandles; + + face_vhandles.push_back(vhandle[0]); + face_vhandles.push_back(vhandle[1]); + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[3]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[1]); + face_vhandles.push_back(vhandle[2]); + face_vhandles.push_back(vhandle[5]); + face_vhandles.push_back(vhandle[4]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[5]); + face_vhandles.push_back(vhandle[8]); + face_vhandles.push_back(vhandle[7]); + + mesh_.add_face(face_vhandles); + face_vhandles.clear(); + + face_vhandles.push_back(vhandle[3]); + face_vhandles.push_back(vhandle[4]); + face_vhandles.push_back(vhandle[7]); + face_vhandles.push_back(vhandle[6]); + + mesh_.add_face(face_vhandles); + + // Test setup: + // 6 === 7 === 8 + // | | | + // | | | + // | | | + // 3 === 4 === 5 + // | | | + // | | | + // | | | + // 0 === 1 === 2 + + // Initialize subdivider + OpenMesh::Subdivider::Uniform::CatmullClarkT catmull; + + // Check setup + EXPECT_EQ(9, mesh_.n_vertices() ) << "Wrong number of vertices"; + EXPECT_EQ(4, mesh_.n_faces() ) << "Wrong number of faces"; + + // Execute 3 subdivision steps + catmull.attach(mesh_); + catmull( 3 ); + catmull.detach(); + + EXPECT_EQ(289, mesh_.n_vertices() ) << "Wrong number of vertices after subdivision with catmull clark"; + EXPECT_EQ(256, mesh_.n_faces() ) << "Wrong number of faces after subdivision with catmull clark"; + +} + +#endif // INCLUDE GUARD