Developer Documentation
Loading...
Searching...
No Matches
mkbalancedpm.cc
1/* ========================================================================= *
2 * *
3 * OpenMesh *
4 * Copyright (c) 2001-2025, 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
61typedef OpenMesh::Decimater::DecimaterT<Mesh> DecimaterProgMesh;
62typedef OpenMesh::Decimater::ModNormalFlippingT<Mesh> ModNormalFlipping;
65typedef OpenMesh::Decimater::ModIndependentSetsT<Mesh> ModIndependentSets;
66
67// ----------------------------------------------------------------------------
68
69using namespace OpenMesh::Decimater;
70
71template <class D>
73{
74public:
75
77
78 DECIMATING_MODULE( ModBalancerT, D, Balancer );
79
80public:
81
82 typedef size_t level_t;
83
84public:
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
101public:
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
108public: // 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
150public:
151
152 level_t max_level(void) const { return max_level_; }
153 level_t bits_for_roots(void) const { return n_roots_; }
154
155private:
156
158
159 level_t max_level_; // maximum level reached
160 level_t n_roots_; // minimum bits for root nodes
161 size_t n_vertices_;// number of potential root nodes
162
163 static const double inv_log2_;
164
165};
166
167template <typename D>
168const double ModBalancerT<D>::inv_log2_ = 1.0/std::log(2.0);
169
171
172
173// ----------------------------------------------------------------------------
174
175inline
176std::string&
177replace_extension( std::string& _s, const std::string& _e )
178{
179 std::string::size_type dot = _s.rfind(".");
180 if (dot == std::string::npos)
181 {
182 // The name does not include a dot and therefore no extension. Adding both.
183 _s += "." + _e;
184 }
185 else
186 {
187 // get everything of the name including the dot but remove the extension.
188 const std::string name = _s.substr(0,dot+1);
189
190 // Add the new extenion
191 _s = name + _e;
192 }
193 return _s;
194}
195
196inline
197std::string
198basename(const std::string& _f)
199{
200 std::string::size_type dot = _f.rfind("/");
201 if (dot == std::string::npos)
202 return _f;
203 return _f.substr(dot+1, _f.length()-(dot+1));
204}
205
206// ----------------------------------------------------------------------------
207
208void usage_and_exit(int xcode)
209{
210 using namespace std;
211
212 cout << endl
213 << "Usage: mkbalancedpm [-n <decimation-steps>] [-o <output>] [-N <max. normal deviation>]"
214 << "<input.ext>\n"
215 << endl
216 << " Create a balanced progressive mesh from an input file.\n"
217 << " By default decimate as much as possible and write the result\n"
218 << " to <input>.pm\n"
219 << endl
220 << "Options:\n"
221 << endl
222 << " -n <decimation-steps>\n"
223 << "\tDetermines the maximum number of decimation steps.\n"
224 << "\tDecimate as much as possible if the value is equal zero\n"
225 << "\tDefault value: 0\n"
226 << endl
227 << " -o <output>\n"
228 << "\tWrite resulting progressive mesh to the file named <output>\n"
229 << endl
230 << " -N <max. normal Deviation>\n"
231 << "\tEnable Normal Flipping\n"
232 << endl
233 << " -I\n"
234 << "\tEnable Independent Sets\n"
235 << endl;
236 exit(xcode);
237}
238
239// ----------------------------------------------------------------------------
240
241int main(int argc, char **argv)
242{
243 Mesh mesh;
244
245 int c;
246 std::string ifname, ofname;
247 size_t decstep=0;
248 float normalDev=90.0;
249 bool enable_modNF = false;
250 bool enable_modIS = false;
251
252 while ((c=getopt(argc, argv, "n:o:N:Ih"))!=-1)
253 {
254 switch (c)
255 {
256 case 'o': ofname = optarg; break;
257 case 'n': { std::stringstream str; str << optarg; str >> decstep; } break;
258 case 'N': { enable_modNF = true;
259 std::stringstream str; str << optarg; str >> normalDev; } break;
260 case 'I': enable_modIS = true; break;
261 case 'h':
262 usage_and_exit(0);
263 break;
264 default:
265 usage_and_exit(1);
266 }
267 }
268
269 if (optind >= argc)
270 usage_and_exit(1);
271
272 ifname = argv[optind];
273
274 if (!OpenMesh::IO::read_mesh(mesh, ifname))
275 {
276 std::cerr << "Error loading mesh from file '" << ifname << "'!\n";
277 return 1;
278 }
279
280 {
282
283 DecimaterProgMesh decimater(mesh);
284
285 ModProgMesh::Handle modPM;
286 ModBalancer::Handle modB;
287 ModNormalFlipping::Handle modNF;
288 ModIndependentSets::Handle modIS;
289
290
291 decimater.add(modPM);
292 std::cout << "w/ progressive mesh module\n";
293 decimater.add(modB);
294 std::cout << "w/ balancer module\n";
295
296 if ( enable_modNF )
297 {
298 decimater.add(modNF);
299 decimater.module(modNF).set_max_normal_deviation(normalDev);
300 }
301 std::cout << "w/" << (modNF.is_valid() ? ' ' : 'o')
302 << " normal flipping module (max. normal deviation: " << normalDev << ")\n";
303
304 if ( enable_modIS )
305 decimater.add(modIS);
306 std::cout << "w/" << (modIS.is_valid() ? ' ' : 'o')
307 << " independent sets module\n";
308
309 std::cout << "Initialize decimater\n";
310 t.start();
311 if ( !decimater.initialize() )
312 {
313 std::cerr << " Initialization failed!\n";
314 return 1;
315 }
316 t.stop();
317 std::cout << " done [" << t.as_string() << "]\n";
318 t.reset();
319
320 size_t rc;
321 size_t nv = mesh.n_vertices();
322
323 std::cout << "Begin decimation (#V " << nv << ")\n";
324 t.start();
325 do
326 {
327 if (modIS.is_valid())
328 {
329 Mesh::VertexIter v_it;
330 Mesh::FaceIter f_it;
331
332 for (f_it = mesh.faces_begin(); f_it != mesh.faces_end(); ++f_it)
333 if ( !mesh.status(*f_it).deleted() )
334 mesh.update_normal(*f_it);
335
336 for (v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
337 if ( !mesh.status(*v_it).deleted() )
338 {
339 mesh.status(*v_it).set_locked(false);
340 mesh.update_normal(*v_it);
341 }
342
343 }
344
345 rc = decimater.decimate(decstep);
346 t.stop();
347 std::cout << '\r'
348 << (nv-=rc) << " (-" << rc << ") " << std::flush;
349 t.cont();
350 } while (rc > 0);
351 t.stop();
352
353 std::cout << "\n done [" << t.as_string() << "]\n";
354
355 std::cout << "Bits for <tree-id, node-id>: <"
356 << decimater.module(modB).bits_for_roots() << ", "
357 << decimater.module(modB).max_level() << ">"
358 << std::endl;
359
360 std::cout << "Maximum level reached: "
361 << decimater.module(modB).max_level() << std::endl;
362
363 if (ofname == "." || ofname == ".." )
364 ofname += "/" + basename(ifname);
365 std::string pmfname = ofname.empty() ? ifname : ofname;
366 replace_extension(pmfname, "pm");
367
368 std::cout << "Write progressive mesh data to file "
369 << pmfname << std::endl;
370 decimater.module(modPM).write( pmfname );
371 }
372
373
374 return 0;
375}
#define DECIMATING_MODULE(Classname, MeshT, Name)
Definition ModBaseT.hh:149
virtual ~ModBalancerT()
Destructor.
ModBalancerT(D &_dec)
Constructor.
void postprocess_collapse(const CollapseInfo &_ci) override
post-process halfedge collapse (accumulate quadrics)
void initialize(void) override
Initalize the module and prepare the mesh for decimation.
virtual float collapse_priority(const CollapseInfo &_ci) override
Mesh decimation module computing collapse priority based on error quadrics.
Kernel::FaceIter FaceIter
Scalar type.
Definition PolyMeshT.hh:146
void update_normal(FaceHandle _fh)
Update normal for face _fh.
Definition PolyMeshT.hh:278
Kernel::VertexIter VertexIter
Scalar type.
Definition PolyMeshT.hh:143
void reset(void)
Reset the timer.
void cont(void)
Continue measurement.
void stop(void)
Stop measurement.
std::string as_string(Format format=Automatic)
void start(void)
Start measurement.
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition MeshIO.hh:95
Mesh::VertexHandle v1
Remaining vertex.
Mesh::VertexHandle v0
Vertex to be removed.