decimater.cc 17.1 KB
Newer Older
1 2 3
/*===========================================================================*\
 *                                                                           *
 *                               OpenMesh                                    *
4
 *      Copyright (C) 2001-2014 by Computer Graphics Group, RWTH Aachen      *
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
 *                           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 <http://www.gnu.org/licenses/>.                                      *
 *                                                                           *
\*===========================================================================*/ 

/*===========================================================================*\
 *                                                                           *             
 *   $Revision$                                                         *
 *   $Date$                   *
 *                                                                           *
\*===========================================================================*/
Jan Möbius's avatar
Jan Möbius committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66

#if !defined(OM_USE_OSG)
#  define OM_USE_OSG 0
#endif

// ----------------------------------------------------------------------------

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <memory>
#include <map>
//--------------------
#include <OpenMesh/Core/IO/MeshIO.hh>
//--------------------
#if OM_USE_OSG
#  include <OpenMesh/Tools/Kernel_OSG/TriMesh_OSGArrayKernelT.hh>
#else
#  include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#endif
#include <OpenMesh/Core/Utils/vector_cast.hh>
//--------------------
#include <OpenMesh/Tools/Utils/getopt.h>
#include <OpenMesh/Tools/Utils/Timer.hh>
#include <OpenMesh/Tools/Decimater/DecimaterT.hh>
67 68 69 70
#include <OpenMesh/Tools/Decimater/ModAspectRatioT.hh>
#include <OpenMesh/Tools/Decimater/ModEdgeLengthT.hh>
#include <OpenMesh/Tools/Decimater/ModHausdorffT.hh>
#include <OpenMesh/Tools/Decimater/ModNormalDeviationT.hh>
Jan Möbius's avatar
Jan Möbius committed
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
#include <OpenMesh/Tools/Decimater/ModNormalFlippingT.hh>
#include <OpenMesh/Tools/Decimater/ModQuadricT.hh>
#include <OpenMesh/Tools/Decimater/ModProgMeshT.hh>
#include <OpenMesh/Tools/Decimater/ModIndependentSetsT.hh>
#include <OpenMesh/Tools/Decimater/ModRoundnessT.hh>

//----------------------------------------------------------------- traits ----

#if OM_USE_OSG
typedef OpenMesh::Kernel_OSG::Traits MyTraits;
#else
typedef OpenMesh::DefaultTraits MyTraits;
#endif

//------------------------------------------------------------------- mesh ----

#if OM_USE_OSG
typedef OpenMesh::Kernel_OSG::TriMesh_OSGArrayKernelT<MyTraits> ArrayTriMesh;
#else
typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> ArrayTriMesh;
#endif


//-------------------------------------------------------------- decimator ----

typedef OpenMesh::Decimater::DecimaterT<ArrayTriMesh>   Decimater;


//---------------------------------------------------------------- globals ----

int gverbose = 0;
int gdebug   = 0;


//--------------------------------------------------------------- forwards ----

void usage_and_exit(int xcode);


//--------------------------------------------------- decimater arguments  ----

#include "CmdOption.hh"


struct DecOptions
{
  DecOptions()
118
  : n_collapses(0)
Jan Möbius's avatar
Jan Möbius committed
119 120 121 122
  { }

  CmdOption<bool>        decorate_name;
  CmdOption<float>       n_collapses;
123 124 125 126

  CmdOption<float>       AR;   // Aspect ratio
  CmdOption<float>       EL;   // Edge length
  CmdOption<float>       HD;   // Hausdorff distance
Jan Möbius's avatar
Jan Möbius committed
127
  CmdOption<bool>        IS;   // Independent Sets
128 129
  CmdOption<float>       ND;   // Normal deviation
  CmdOption<float>       NF;   // Normal flipping
Jan Möbius's avatar
Jan Möbius committed
130
  CmdOption<std::string> PM;   // Progressive Mesh
131
  CmdOption<float>       Q;    // Quadrics
Jan Möbius's avatar
Jan Möbius committed
132 133 134 135 136 137 138 139 140 141 142 143 144 145
  CmdOption<float>       R;    // Roundness

  template <typename T>
  bool init( CmdOption<T>& _o, const std::string& _val )
  {
    if ( _val.empty() )
      _o.enable();
    else
    {
      std::istringstream istr( _val );

      T v;

      if ( (istr >> v).fail() )
146 147
        return false;

Jan Möbius's avatar
Jan Möbius committed
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
      _o = v;
    }
    return true;
  }


  bool parse_argument( const std::string& arg )
  {
    std::string::size_type pos = arg.find(':');

    std::string name;
    std::string value;

    if (pos == std::string::npos)
      name = arg;
    else
    {
      name  = arg.substr(0, pos);
      value = arg.substr(pos+1, arg.size());
    }
    strip(name);
    strip(value);
170 171 172 173 174 175

    if (name == "AR") return init(AR, value);
    if (name == "EL") return init(EL, value);
    if (name == "HD") return init(HD, value);
    if (name == "IS") return init(IS, value);
    if (name == "ND") return init(ND, value);
Jan Möbius's avatar
Jan Möbius committed
176 177
    if (name == "NF") return init(NF, value);
    if (name == "PM") return init(PM, value);
178
    if (name == "Q")  return init(Q,  value);
Jan Möbius's avatar
Jan Möbius committed
179 180 181 182 183 184 185
    if (name == "R")  return init(R,  value);
    return false;
  }

  std::string& strip(std::string & line)
  {
    std::string::size_type pos = 0;
186

Jan Möbius's avatar
Jan Möbius committed
187
    pos = line.find_last_not_of(" \t");
188

Jan Möbius's avatar
Jan Möbius committed
189 190 191 192 193
    if ( pos!=0 && pos!=std::string::npos )
    {
      ++pos;
      line.erase( pos, line.length()-pos );
    }
194

Jan Möbius's avatar
Jan Möbius committed
195 196 197 198 199 200 201 202 203 204 205 206 207
    pos = line.find_first_not_of(" \t");
    if ( pos!=0 && pos!=std::string::npos )
    {
      line.erase(0,pos);
    }

    return line;
  }

};

//----------------------------------------------------- decimater wrapper  ----
//
Jan Möbius's avatar
Jan Möbius committed
208
template <typename Mesh, typename DecimaterType>
Jan Möbius's avatar
Jan Möbius committed
209 210 211
bool
decimate(const std::string &_ifname,
         const std::string &_ofname,
212
         DecOptions        &_opt)
Jan Möbius's avatar
Jan Möbius committed
213 214 215 216 217 218 219 220 221 222 223 224
{
   using namespace std;

   Mesh                   mesh;   
   OpenMesh::IO::Options  opt;
   OpenMesh::Utils::Timer timer;

   // ---------------------------------------- read source mesh
   {
     if (gverbose)
       clog << "source mesh: ";
     bool rc;
225

Jan Möbius's avatar
Jan Möbius committed
226 227 228 229 230 231 232 233
     if (gverbose)
       clog << _ifname << endl;
     if ( !(rc = OpenMesh::IO::read_mesh(mesh, _ifname, opt)) )
     {
       cerr << "  ERROR: read failed!" << endl;
       return rc;
     }
   }
234

Jan Möbius's avatar
Jan Möbius committed
235 236 237 238 239 240 241
   // ---------------------------------------- do some decimation
   {
     // ---- 0 - For module NormalFlipping one needs face normals

     if ( !opt.check( OpenMesh::IO::Options::FaceNormal ) )
     {
       if ( !mesh.has_face_normals() )
242
         mesh.request_face_normals();
Jan Möbius's avatar
Jan Möbius committed
243 244

       if (gverbose)
245
         clog << "  updating face normals" << endl;
Jan Möbius's avatar
Jan Möbius committed
246 247
       mesh.update_face_normals();
     }
248

Jan Möbius's avatar
Jan Möbius committed
249
     // ---- 1 - create decimater instance
Jan Möbius's avatar
Jan Möbius committed
250
     DecimaterType decimater( mesh );
251 252

     // ---- 2 - register modules
Jan Möbius's avatar
Jan Möbius committed
253
     if (gverbose)
254
       clog << "  register modules" << endl;
Jan Möbius's avatar
Jan Möbius committed
255

256 257


258
     typename OpenMesh::Decimater::ModAspectRatioT<Mesh>::Handle modAR;
259 260

     if (_opt.AR.is_enabled())
Jan Möbius's avatar
Jan Möbius committed
261
     {
262 263 264 265 266
       decimater.add(modAR);
       if (_opt.AR.has_value())
         decimater.module( modAR ).set_aspect_ratio( _opt.AR ) ;
     }

267
     typename OpenMesh::Decimater::ModEdgeLengthT<Mesh>::Handle modEL;
268 269 270 271 272 273

     if (_opt.EL.is_enabled())
     {
       decimater.add(modEL);
       if (_opt.EL.has_value())
         decimater.module( modEL ).set_edge_length( _opt.EL ) ;
274
       decimater.module(modEL).set_binary(false);
275 276
     }

277
     typename OpenMesh::Decimater::ModHausdorffT <Mesh>::Handle modHD;
278 279 280 281 282 283 284 285 286

     if (_opt.HD.is_enabled())
     {
       decimater.add(modHD);
       if (_opt.HD.has_value())
         decimater.module( modHD ).set_tolerance( _opt.HD ) ;

     }

287
     typename OpenMesh::Decimater::ModIndependentSetsT<Mesh>::Handle modIS;
288 289 290 291

     if ( _opt.IS.is_enabled() )
       decimater.add(modIS);

292
     typename OpenMesh::Decimater::ModNormalDeviationT<Mesh>::Handle modND;
293 294 295 296 297 298

     if (_opt.ND.is_enabled())
     {
       decimater.add(modND);
       if (_opt.ND.has_value())
         decimater.module( modND ).set_normal_deviation( _opt.ND );
299
       decimater.module( modND ).set_binary(false);
Jan Möbius's avatar
Jan Möbius committed
300 301
     }

302
     typename OpenMesh::Decimater::ModNormalFlippingT<Mesh>::Handle modNF;
303

Jan Möbius's avatar
Jan Möbius committed
304 305 306 307
     if (_opt.NF.is_enabled())
     {
       decimater.add(modNF);
       if (_opt.NF.has_value())
308
         decimater.module( modNF ).set_max_normal_deviation( _opt.NF );
Jan Möbius's avatar
Jan Möbius committed
309 310
     }

311

312
     typename OpenMesh::Decimater::ModProgMeshT<Mesh>::Handle       modPM;
Jan Möbius's avatar
Jan Möbius committed
313 314 315 316

     if ( _opt.PM.is_enabled() )
       decimater.add(modPM);

317
     typename OpenMesh::Decimater::ModQuadricT<Mesh>::Handle        modQ;
Jan Möbius's avatar
Jan Möbius committed
318

319 320 321 322 323
     if (_opt.Q.is_enabled())
     {
       decimater.add(modQ);
       if (_opt.Q.has_value())
         decimater.module( modQ ).set_max_err( _opt.Q );
324
       decimater.module(modQ).set_binary(false);
325
     }
Jan Möbius's avatar
Jan Möbius committed
326

327
     typename OpenMesh::Decimater::ModRoundnessT<Mesh>::Handle      modR;
Jan Möbius's avatar
Jan Möbius committed
328 329 330 331 332

     if ( _opt.R.is_enabled() )
     {
       decimater.add( modR );
       if ( _opt.R.has_value() )
333 334 335
         decimater.module( modR ).set_min_angle( _opt.R,
             !modQ.is_valid() ||
             !decimater.module(modQ).is_binary());
Jan Möbius's avatar
Jan Möbius committed
336 337 338 339 340 341
     }

     // ---- 3 - initialize decimater

     if (gverbose)
       clog << "initializing mesh" << endl;
342

Jan Möbius's avatar
Jan Möbius committed
343 344 345 346 347 348 349
     {
       bool rc;
       timer.start();
       rc = decimater.initialize();
       timer.stop();
       if (!rc)
       {
350
         std::cerr << "  initializing failed!" << std::endl;
351
         std::cerr << "  maybe no priority module or more than one were defined!" << std::endl;
352
         return false;
Jan Möbius's avatar
Jan Möbius committed
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
       }
     }
     if (gverbose)
       std::clog << "  Elapsed time: " << timer.as_string() << std::endl;

     if (gverbose)
       decimater.info( clog );

     // ---- 4 - do it

     if (gverbose)
     {
       std::clog << "decimating" << std::endl;
       std::clog << "  # vertices: "       << mesh.n_vertices() << std::endl;
     }

     float nv_before = float(mesh.n_vertices());

     timer.start();
Jan Möbius's avatar
Jan Möbius committed
372
     size_t rc = 0;
Jan Möbius's avatar
Jan Möbius committed
373 374 375 376 377 378 379
     if (_opt.n_collapses < 0.0)
       rc = decimater.decimate_to( size_t(-_opt.n_collapses) );
     else if (_opt.n_collapses >= 1.0 || _opt.n_collapses == 0.0)
       rc = decimater.decimate( size_t(_opt.n_collapses) );
     else if (_opt.n_collapses > 0.0f)
       rc = decimater.decimate_to(size_t(mesh.n_vertices()*_opt.n_collapses));
     timer.stop();
380

Jan Möbius's avatar
Jan Möbius committed
381
     // ---- 5 - write progmesh file for progviewer (before garbage collection!)
382

Jan Möbius's avatar
Jan Möbius committed
383 384
     if ( _opt.PM.has_value() )
       decimater.module(modPM).write( _opt.PM );
385

Jan Möbius's avatar
Jan Möbius committed
386 387 388 389 390 391 392 393
     // ---- 6 - throw away all tagged edges

     mesh.garbage_collection();

     if (gverbose)
     {       
       std::clog << "  # executed collapses: " << rc << std::endl;
       std::clog << "  # vertices: " << mesh.n_vertices() << ", " 
394
           << ( 100.0*mesh.n_vertices()/nv_before ) << "%\n";
Jan Möbius's avatar
Jan Möbius committed
395 396 397 398 399
       std::clog << "  Elapsed time: " << timer.as_string() << std::endl;
       std::clog << "  collapses/s : " << rc/timer.seconds() << std::endl;
     }

   }
400

Jan Möbius's avatar
Jan Möbius committed
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
   // write resulting mesh
   if ( ! _ofname.empty() )
   {
     std::string ofname(_ofname);

     std::string::size_type pos = ofname.rfind('.');
     if (pos == std::string::npos)
     {
       ofname += ".off";
       pos = ofname.rfind('.');
     }

     if ( _opt.decorate_name.is_enabled() )
     {
       std::stringstream s; s << mesh.n_vertices();
       std::string       n; s >> n;
       ofname.insert(  pos, "-");
       ofname.insert(++pos, n  );
     }

     OpenMesh::IO::Options opt;

     //opt += OpenMesh::IO::Options::Binary;

     if ( !OpenMesh::IO::write_mesh(mesh, ofname, opt ) )
     {
       std::cerr << "  Cannot write decimated mesh to file '" 
428
           << ofname << "'\n";
Jan Möbius's avatar
Jan Möbius committed
429 430 431 432
       return false;
     }
     std::clog << "  Exported decimated mesh to file '" << ofname << "'\n";
   }
433

Jan Möbius's avatar
Jan Möbius committed
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
   return true;
}

//------------------------------------------------------------------ main -----

int main(int argc, char* argv[])   
{
  std::string  ifname, ofname;
   
  DecOptions opt;

  //
#if OM_USE_OSG
  osg::osgInit( argc, argv );
#endif

  //---------------------------------------- parse command line
  {
    int c;
453

Jan Möbius's avatar
Jan Möbius committed
454 455 456 457 458
    while ( (c=getopt( argc, argv, "dDhi:M:n:o:v")) != -1 )
    {
      switch (c)
      {
        case 'D': opt.decorate_name = true;   break;
459 460 461 462 463 464 465 466 467 468 469 470
        case 'd': gdebug            = true;   break;
        case 'h': usage_and_exit(0);
        case 'i': ifname            = optarg; break;
        case 'M': opt.parse_argument( optarg ); break;
        case 'n': opt.n_collapses   = float(atof(optarg)); break;
        case 'o': ofname            = optarg; break;
        case 'v': gverbose          = true;   break;
        case '?':
        default:
          std::cerr << "FATAL: cannot process command line option!"
          << std::endl;
          exit(-1);
Jan Möbius's avatar
Jan Möbius committed
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
      }                  
    }
  }

  //----------------------------------------

  if ( (-1.0f < opt.n_collapses) &&  (opt.n_collapses < 0.0f) )
  {
    std::cerr << "Error: Option -n: invalid value argument!" << std::endl;
    usage_and_exit(2);
  }

  //----------------------------------------

  if (gverbose)
  {
    std::clog << "    Input file: " << ifname << std::endl;
    std::clog << "   Output file: " << ofname << std::endl;
    std::clog << "    #collapses: " << opt.n_collapses << std::endl;
  }


  //----------------------------------------


   
  if (gverbose)
  {
    std::clog << "Begin decimation" << std::endl;
  }
   
  bool rc = decimate<ArrayTriMesh, Decimater>( ifname, ofname, opt );

  if (gverbose)
  {
    if (!rc)
      std::clog << "Decimation failed!" << std::endl;
    else
      std::clog << "Decimation done." << std::endl;
  }

  //----------------------------------------
  return 0;
}


//-----------------------------------------------------------------------------

void usage_and_exit(int xcode)
{
  std::string errmsg;
522

Jan Möbius's avatar
Jan Möbius committed
523 524 525 526 527
  switch(xcode)
  {
    case 1: errmsg = "Option not supported!"; break;
    case 2: errmsg = "Invalid output file format!"; break;
  }
528

Jan Möbius's avatar
Jan Möbius committed
529
  std::cerr << std::endl;
530
  if (xcode) {
Jan Möbius's avatar
Jan Möbius committed
531 532 533
    std::cerr << "Error " << xcode << ": " << errmsg << std::endl << std::endl;
  }
  std::cerr << "Usage: decimator [Options] -i input-file -o output-file\n"
534 535
            << "  Decimating a mesh using quadrics and normal flipping.\n" << std::endl;
  std::cerr << "Options\n"  << std::endl;
Jan Möbius's avatar
Jan Möbius committed
536
  std::cerr << " -M \"{Module-Name}[:Value]}\"\n"
537 538 539 540 541 542
            << "    Use named module with eventually given parameterization\n"
            << "    Several modules can also be used in order to introduce further constraints\n"
            << "    Note that -M has to be given before each new module \n"
            << "    An example with ModQuadric as a priority module\n"
            << "    and ModRoundness as a binary module could look like this:\n"
            << "    commandlineDecimater -M Q -M R:40.0 -n 0.1 -i inputfile.obj -o outputfile.obj\n" << std::endl;
Jan Möbius's avatar
Jan Möbius committed
543
  std::cerr << " -n <N>\n"
544 545 546
            << "    N >= 1: do N halfedge collapses.\n"
            << "    N <=-1: decimate down to |N| vertices.\n"
            << " 0 < N < 1: decimate down to N%.\n" << std::endl;
Jan Möbius's avatar
Jan Möbius committed
547 548
  std::cerr << std::endl;
  std::cerr << "Modules:\n\n";
549
  std::cerr << "  AR[:ratio]      - ModAspectRatio\n";
550
  std::cerr << "  EL[:legth]      - ModEdgeLength*\n";
551
  std::cerr << "  HD[:distance]   - ModHausdorff\n";
Jan Möbius's avatar
Jan Möbius committed
552
  std::cerr << "  IS              - ModIndependentSets\n";
553
  std::cerr << "  ND[:angle]      - ModNormalDeviation*\n";
Jan Möbius's avatar
Jan Möbius committed
554 555
  std::cerr << "  NF[:angle]      - ModNormalFlipping\n";
  std::cerr << "  PM[:file name]  - ModProgMesh\n";
556
  std::cerr << "  Q[:error]       - ModQuadric*\n";
Jan Möbius's avatar
Jan Möbius committed
557 558
  std::cerr << "  R[:angle]       - ModRoundness\n";
  std::cerr << "    0 < angle < 60\n";
559
  std::cerr << "  *: priority module. Decimater needs one of them (not more).\n";
560

Jan Möbius's avatar
Jan Möbius committed
561 562 563 564 565 566 567
  exit( xcode );
}



//                             end of file
//=============================================================================