Developer Documentation
mkbalancedpm.cc
1 /* ========================================================================= *
2  * *
3  * OpenMesh *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openmesh.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenMesh. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39  * *
40  * ========================================================================= */
41 
42 
43 
44 // -------------------- STL
45 #include <iostream>
46 #include <sstream>
47 #include <cmath>
48 // -------------------- OpenMesh
49 #include <OpenMesh/Core/IO/MeshIO.hh>
50 #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
51 #include <OpenMesh/Tools/Utils/getopt.h>
58 #include <OpenMesh/Tools/Decimater/ModIndependentSetsT.hh>
59 
61 typedef OpenMesh::Decimater::DecimaterT<Mesh> DecimaterProgMesh;
62 typedef OpenMesh::Decimater::ModNormalFlippingT<Mesh> ModNormalFlipping;
64 typedef OpenMesh::Decimater::ModProgMeshT<Mesh> ModProgMesh;
65 typedef OpenMesh::Decimater::ModIndependentSetsT<Mesh> ModIndependentSets;
66 
67 // ----------------------------------------------------------------------------
68 
69 using namespace OpenMesh::Decimater;
70 
71 template <class D>
73 {
74 public:
75 
76  typedef OpenMesh::Decimater::ModQuadricT<D> BaseModQ;
77 
78  DECIMATING_MODULE( ModBalancerT, D, Balancer );
79 
80 public:
81 
82  typedef size_t level_t;
83 
84 public:
85 
87  explicit ModBalancerT( D &_dec )
88  : BaseModQ( _dec ),
89  max_level_(0), n_roots_(0), n_vertices_(0)
90  {
91  BaseModQ::mesh().add_property( level_ );
92  }
93 
94 
96  virtual ~ModBalancerT()
97  {
98  BaseModQ::mesh().remove_property( level_ );
99  }
100 
101 public:
102 
103  static level_t calc_bits_for_roots( size_t _n_vertices )
104  {
105  return level_t(std::ceil(std::log((double)_n_vertices)*inv_log2_));
106  }
107 
108 public: // inherited
109 
110  void initialize(void) override
111  {
112  BaseModQ::initialize();
113  n_vertices_ = BaseModQ::mesh().n_vertices();
114  n_roots_ = calc_bits_for_roots(n_vertices_);
115  }
116 
117  virtual float collapse_priority(const CollapseInfo& _ci) override
118  {
119  level_t newlevel = std::max( BaseModQ::mesh().property( level_, _ci.v0 ),
120  BaseModQ::mesh().property( level_, _ci.v1 ) )+1;
121  level_t newroots = calc_bits_for_roots(n_vertices_-1);
122 
123  if ( (newroots + newlevel) < 32 )
124  {
125  double err = BaseModQ::collapse_priority( _ci );
126 
127  if (err!=BaseModQ::ILLEGAL_COLLAPSE)
128  {
129  return float(newlevel + err/(err+1.0));
130  }
131 
132 
133  }
134  return BaseModQ::ILLEGAL_COLLAPSE;
135  }
136 
138  void postprocess_collapse(const CollapseInfo& _ci) override
139  {
140  BaseModQ::postprocess_collapse( _ci );
141 
142  BaseModQ::mesh().property( level_, _ci.v1 ) =
143  std::max( BaseModQ::mesh().property( level_, _ci.v0 ),
144  BaseModQ::mesh().property( level_, _ci.v1 ) ) + 1;
145 
146  max_level_ = std::max( BaseModQ::mesh().property( level_, _ci.v1 ), max_level_ );
147  n_roots_ = calc_bits_for_roots(--n_vertices_);
148  }
149 
150 public:
151 
152  level_t max_level(void) const { return max_level_; }
153  level_t bits_for_roots(void) const { return n_roots_; }
154 
155 private:
156 
158  void set_binary(bool _b) {}
159 
161 
162  level_t max_level_; // maximum level reached
163  level_t n_roots_; // minimum bits for root nodes
164  size_t n_vertices_;// number of potential root nodes
165 
166  static const double inv_log2_;
167 
168 };
169 
170 template <typename D>
171 const double ModBalancerT<D>::inv_log2_ = 1.0/std::log(2.0);
172 
174 
175 
176 // ----------------------------------------------------------------------------
177 
178 inline
179 std::string&
180 replace_extension( std::string& _s, const std::string& _e )
181 {
182  std::string::size_type dot = _s.rfind(".");
183  if (dot == std::string::npos)
184  { _s += "." + _e; }
185  else
186  { _s = _s.substr(0,dot+1)+_e; }
187  return _s;
188 }
189 
190 inline
191 std::string
192 basename(const std::string& _f)
193 {
194  std::string::size_type dot = _f.rfind("/");
195  if (dot == std::string::npos)
196  return _f;
197  return _f.substr(dot+1, _f.length()-(dot+1));
198 }
199 
200 // ----------------------------------------------------------------------------
201 
202 void usage_and_exit(int xcode)
203 {
204  using namespace std;
205 
206  cout << endl
207  << "Usage: mkbalancedpm [-n <decimation-steps>] [-o <output>] [-N <max. normal deviation>]"
208  << "<input.ext>\n"
209  << endl
210  << " Create a balanced progressive mesh from an input file.\n"
211  << " By default decimate as much as possible and write the result\n"
212  << " to <input>.pm\n"
213  << endl
214  << "Options:\n"
215  << endl
216  << " -n <decimation-steps>\n"
217  << "\tDetermines the maximum number of decimation steps.\n"
218  << "\tDecimate as much as possible if the value is equal zero\n"
219  << "\tDefault value: 0\n"
220  << endl
221  << " -o <output>\n"
222  << "\tWrite resulting progressive mesh to the file named <output>\n"
223  << endl
224  << " -N <max. normal Deviation>\n"
225  << "\tEnable Normal Flipping\n"
226  << endl
227  << " -I\n"
228  << "\tEnable Independent Sets\n"
229  << endl;
230  exit(xcode);
231 }
232 
233 // ----------------------------------------------------------------------------
234 
235 int main(int argc, char **argv)
236 {
237  Mesh mesh;
238 
239  int c;
240  std::string ifname, ofname;
241  size_t decstep=0;
242  float normalDev=90.0;
243  bool enable_modNF = false;
244  bool enable_modIS = false;
245 
246  while ((c=getopt(argc, argv, "n:o:N:Ih"))!=-1)
247  {
248  switch (c)
249  {
250  case 'o': ofname = optarg; break;
251  case 'n': { std::stringstream str; str << optarg; str >> decstep; } break;
252  case 'N': { enable_modNF = true;
253  std::stringstream str; str << optarg; str >> normalDev; } break;
254  case 'I': enable_modIS = true; break;
255  case 'h':
256  usage_and_exit(0);
257  break;
258  default:
259  usage_and_exit(1);
260  }
261  }
262 
263  if (optind >= argc)
264  usage_and_exit(1);
265 
266  ifname = argv[optind];
267 
268  if (!OpenMesh::IO::read_mesh(mesh, ifname))
269  {
270  std::cerr << "Error loading mesh from file '" << ifname << "'!\n";
271  return 1;
272  }
273 
274  {
276 
277  DecimaterProgMesh decimater(mesh);
278 
279  ModProgMesh::Handle modPM;
280  ModBalancer::Handle modB;
281  ModNormalFlipping::Handle modNF;
282  ModIndependentSets::Handle modIS;
283 
284 
285  decimater.add(modPM);
286  std::cout << "w/ progressive mesh module\n";
287  decimater.add(modB);
288  std::cout << "w/ balancer module\n";
289 
290  if ( enable_modNF )
291  {
292  decimater.add(modNF);
293  decimater.module(modNF).set_max_normal_deviation(normalDev);
294  }
295  std::cout << "w/" << (modNF.is_valid() ? ' ' : 'o')
296  << " normal flipping module (max. normal deviation: " << normalDev << ")\n";
297 
298  if ( enable_modIS )
299  decimater.add(modIS);
300  std::cout << "w/" << (modIS.is_valid() ? ' ' : 'o')
301  << " independent sets module\n";
302 
303  std::cout << "Initialize decimater\n";
304  t.start();
305  if ( !decimater.initialize() )
306  {
307  std::cerr << " Initialization failed!\n";
308  return 1;
309  }
310  t.stop();
311  std::cout << " done [" << t.as_string() << "]\n";
312  t.reset();
313 
314  size_t rc;
315  size_t nv = mesh.n_vertices();
316 
317  std::cout << "Begin decimation (#V " << nv << ")\n";
318  t.start();
319  do
320  {
321  if (modIS.is_valid())
322  {
323  Mesh::VertexIter v_it;
324  Mesh::FaceIter f_it;
325 
326  for (f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it)
327  if ( !mesh.status(*f_it).deleted() )
328  mesh.update_normal(*f_it);
329 
330  for (v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
331  if ( !mesh.status(*v_it).deleted() )
332  {
333  mesh.status(*v_it).set_locked(false);
334  mesh.update_normal(*v_it);
335  }
336 
337  }
338 
339  rc = decimater.decimate(decstep);
340  t.stop();
341  std::cout << '\r'
342  << (nv-=rc) << " (-" << rc << ") " << std::flush;
343  t.cont();
344  } while (rc > 0);
345  t.stop();
346 
347  std::cout << "\n done [" << t.as_string() << "]\n";
348 
349  std::cout << "Bits for <tree-id, node-id>: <"
350  << decimater.module(modB).bits_for_roots() << ", "
351  << decimater.module(modB).max_level() << ">"
352  << std::endl;
353 
354  std::cout << "Maximum level reached: "
355  << decimater.module(modB).max_level() << std::endl;
356 
357  if (ofname == "." || ofname == ".." )
358  ofname += "/" + basename(ifname);
359  std::string pmfname = ofname.empty() ? ifname : ofname;
360  replace_extension(pmfname, "pm");
361 
362  std::cout << "Write progressive mesh data to file "
363  << pmfname << std::endl;
364  decimater.module(modPM).write( pmfname );
365  }
366 
367 
368  return 0;
369 }
ModBalancerT(D &_dec)
Constructor.
Definition: mkbalancedpm.cc:87
void reset(void)
Reset the timer.
void initialize(void) override
Initalize the module and prepare the mesh for decimation.
STL namespace.
std::string as_string(Format format=Automatic)
void set_binary(bool _b)
hide this method
void stop(void)
Stop measurement.
void cont(void)
Continue measurement.
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition: MeshIO.hh:112
virtual ~ModBalancerT()
Destructor.
Definition: mkbalancedpm.cc:96
Mesh decimation module computing collapse priority based on error quadrics.
Definition: ModQuadricT.hh:75
virtual float collapse_priority(const CollapseInfo &_ci) override
Mesh::VertexHandle v0
Vertex to be removed.
Mesh::VertexHandle v1
Remaining vertex.
#define DECIMATING_MODULE(Classname, MeshT, Name)
Definition: ModBaseT.hh:149
void postprocess_collapse(const CollapseInfo &_ci) override
post-process halfedge collapse (accumulate quadrics)
void start(void)
Start measurement.
osg::Vec3f::ValueType dot(const osg::Vec3f &_v1, const osg::Vec3f &_v2)
Adapter for osg vector member computing a scalar product.