Developer Documentation
mconvert.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#include <iostream>
45#include <iterator>
46#include <fstream>
47#include <string>
48//
49#include <OpenMesh/Core/IO/MeshIO.hh>
50#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
53#include <OpenMesh/Tools/Utils/getopt.h>
54
55
57{
58 VertexAttributes ( OpenMesh::Attributes::Normal |
61 HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );
62 FaceAttributes ( OpenMesh::Attributes::Normal |
64};
65
66
68
69void usage_and_exit(int xcode)
70{
71 using std::cout;
72 using std::endl;
73
74 cout << "\nUsage: mconvert [option] <input> [<output>]\n\n";
75 cout << " Convert from one 3D geometry format to another.\n"
76 << " Or simply display some information about the object\n"
77 << " stored in <input>.\n"
78 << endl;
79 cout << "Options:\n"
80 << endl;
81 cout << " -b\tUse binary mode if supported by target format.\n" << endl;
82 cout << " -l\tStore least significant bit first (LSB, little endian).\n" << endl;
83 cout << " -m\tStore most significant bit first (MSB, big endian).\n" << endl;
84 cout << " -s\tSwap byte order.\n" << endl;
85 cout << " -B\tUse binary mode if supported by source format.\n" << endl;
86 cout << " -S\tSwap byte order of input data.\n" << endl;
87 cout << " -c\tCopy vertex color if provided by input.\n" << endl;
88 cout << " -d\tCopy face color if provided by input.\n" << endl;
89 cout << " -C\tTranslate object in its center-of-gravity.\n" << endl;
90 cout << " -n\tCopy vertex normals if provided by input. Else compute normals.\n" << endl;
91 cout << " -N\tReverse normal directions.\n" << endl;
92 cout << " -t\tCopy vertex texture coordinates if provided by input file.\n" << endl;
93 cout << " -T \"x y z\"\tTranslate object by vector (x, y, z)'\"\n"
94 << std::endl;
95 cout << endl;
96
97 exit(xcode);
98}
99
100// ----------------------------------------------------------------------------
101
102template <typename T> struct Option : std::pair< T, bool >
103{
104 typedef std::pair< T, bool > Base;
105
106 Option()
107 { Base::second = false; }
108
109 bool is_valid() const { return Base::second; }
110 bool is_empty() const { return !Base::second; }
111
112 operator T& () { return Base::first; }
113 operator const T& () const { return Base::first; }
114
115 Option& operator = ( const T& _rhs )
116 { Base::first = _rhs; Base::second=true; return *this; }
117
118 bool operator == ( const T& _rhs ) const
119 { return Base::first == _rhs; }
120
121 bool operator != ( const T& _rhs ) const
122 { return Base::first != _rhs; }
123};
124
125template <typename T>
126std::ostream& operator << (std::ostream& _os, const Option<T>& _opt )
127{
128 if (_opt.second) _os << _opt.first; else _os << "<null>";
129 return _os;
130}
131
132template <typename T>
133std::istream& operator >> (std::istream& _is, Option<T>& _opt )
134{
135 _is >> _opt.first; _opt.second = true;
136 return _is;
137}
138
139// ----------------------------------------------------------------------------
140
141int main(int argc, char *argv[] )
142{
143 // ------------------------------------------------------------ command line
144
145 int c;
146 std::string ifname, ofname;
147 bool rev_normals = false;
148 bool obj_center = false;
149 OpenMesh::IO::Options opt, ropt;
150
152
153 while ( (c=getopt(argc, argv, "bBcdCi:hlmnNo:sStT:"))!=-1 )
154 {
155 switch(c)
156 {
157 case 'b': opt += OpenMesh::IO::Options::Binary; break;
158 case 'B': ropt += OpenMesh::IO::Options::Binary; break;
159 case 'l': opt += OpenMesh::IO::Options::LSB; break;
160 case 'm': opt += OpenMesh::IO::Options::MSB; break;
161 case 's': opt += OpenMesh::IO::Options::Swap; break;
162 case 'S': ropt += OpenMesh::IO::Options::Swap; break;
163 case 'n': opt += OpenMesh::IO::Options::VertexNormal; break;
164 case 'N': rev_normals = true; break;
165 case 'C': obj_center = true; break;
166 case 'c': opt += OpenMesh::IO::Options::VertexColor; break;
167 case 'd': opt += OpenMesh::IO::Options::FaceColor; break;
168 case 't': opt += OpenMesh::IO::Options::VertexTexCoord; break;
169 case 'T':
170 {
171 std::cout << optarg << std::endl;
172 std::stringstream str; str << optarg;
173 str >> tvec;
174 std::cout << tvec << std::endl;
175 break;
176 }
177 case 'i': ifname = optarg; break;
178 case 'o': ofname = optarg; break;
179 case 'h':
180 usage_and_exit(0);
181 break;
182 case '?':
183 default:
184 usage_and_exit(1);
185 }
186 }
187
188 if (ifname.empty())
189 {
190 if (optind < argc)
191 ifname = argv[optind++];
192 else
193 usage_and_exit(1);
194 }
195
196 MyMesh mesh;
198
199 // ------------------------------------------------------------ read
200
201 std::cout << "reading.." << std::endl;
202 {
203 bool rc;
204 timer.start();
205 rc = OpenMesh::IO::read_mesh( mesh, ifname, ropt );
206 timer.stop();
207 if (rc)
208 std::cout << " read in " << timer.as_string() << std::endl;
209 else
210 {
211 std::cout << " read failed\n" << std::endl;
212 return 1;
213 }
214 timer.reset();
215 }
216
217
218 // ---------------------------------------- some information about input
219 std::cout << (ropt.check(OpenMesh::IO::Options::Binary)
220 ? " source is binary\n"
221 : " source is ascii\n");
222
223 std::cout << " #V " << mesh.n_vertices() << std::endl;
224 std::cout << " #E " << mesh.n_edges() << std::endl;
225 std::cout << " #F " << mesh.n_faces() << std::endl;
226
227 if (ropt.vertex_has_texcoord())
228 std::cout << " has texture coordinates" << std::endl;
229
230 if (ropt.vertex_has_normal())
231 std::cout << " has vertex normals" << std::endl;
232
233 if (ropt.vertex_has_color())
234 std::cout << " has vertex colors" << std::endl;
235
236 if (ropt.face_has_normal())
237 std::cout << " has face normals" << std::endl;
238
239 if (ropt.face_has_color())
240 std::cout << " has face colors" << std::endl;
241
242 //
243 if (ofname.empty())
244 {
245 if ( optind < argc )
246 ofname = argv[optind++];
247 else
248 return 0;
249 }
250
251 // ------------------------------------------------------------ features
252
253 // ---------------------------------------- compute normal feature
254 if ( opt.vertex_has_normal() && !ropt.vertex_has_normal())
255 {
256 std::cout << "compute normals" << std::endl;
257
258 timer.start();
259 mesh.update_face_normals();
260 timer.stop();
261 std::cout << " " << mesh.n_faces()
262 << " face normals in " << timer.as_string() << std::endl;
263 timer.reset();
264
265 timer.start();
267 timer.stop();
268 std::cout << " " << mesh.n_vertices()
269 << " vertex normals in " << timer.as_string() << std::endl;
270 timer.reset();
271 }
272
273
274 // ---------------------------------------- reverse normal feature
275 if ( rev_normals && ropt.vertex_has_normal() )
276 {
277 std::cout << "reverse normal directions" << std::endl;
278 timer.start();
279 MyMesh::VertexIter vit = mesh.vertices_begin();
280 for (; vit != mesh.vertices_end(); ++vit)
281 mesh.set_normal( *vit, -mesh.normal( *vit ) );
282 timer.stop();
283 std::cout << " " << mesh.n_vertices()
284 << " vertex normals in " << timer.as_string() << std::endl;
285 timer.reset();
286
287 }
288
289
290 // ---------------------------------------- centering feature
291 if ( obj_center )
292 {
293 OpenMesh::Vec3f cog(0,0,0);
294 size_t nv;
295 std::cout << "center object" << std::endl;
296 timer.start();
297 MyMesh::VertexIter vit = mesh.vertices_begin();
298 for (; vit != mesh.vertices_end(); ++vit)
299 cog += mesh.point( *vit );
300 timer.stop();
301 nv = mesh.n_vertices();
302 cog *= 1.0f/mesh.n_vertices();
303 std::cout << " cog = [" << cog << "]'" << std::endl;
304 if (cog.sqrnorm() > 0.8) // actually one should consider the size of object
305 {
306 vit = mesh.vertices_begin();
307 timer.cont();
308 for (; vit != mesh.vertices_end(); ++vit)
309 mesh.set_point( *vit , mesh.point( *vit )-cog );
310 timer.stop();
311 nv += mesh.n_vertices();
312 }
313 else
314 std::cout << " already centered!" << std::endl;
315 std::cout << " visited " << nv
316 << " vertices in " << timer.as_string() << std::endl;
317 timer.reset();
318 }
319
320
321 // ---------------------------------------- translate feature
322 if ( tvec.is_valid() )
323 {
324 std::cout << "Translate object by " << tvec << std::endl;
325
326 timer.start();
327 MyMesh::VertexIter vit = mesh.vertices_begin();
328 for (; vit != mesh.vertices_end(); ++vit)
329 mesh.set_point( *vit , mesh.point( *vit ) + tvec.first );
330 timer.stop();
331 std::cout << " moved " << mesh.n_vertices()
332 << " vertices in " << timer.as_string() << std::endl;
333 }
334
335 // ---------------------------------------- color vertices feature
336 if ( opt.check( OpenMesh::IO::Options::VertexColor ) &&
338 {
339 std::cout << "Color vertices" << std::endl;
340
341 double d = 256.0/double(mesh.n_vertices());
342 double d2 = d/2.0;
343 double r = 0.0, g = 0.0, b = 255.0;
344 timer.start();
345 MyMesh::VertexIter vit = mesh.vertices_begin();
346 for (; vit != mesh.vertices_end(); ++vit)
347 {
348 mesh.set_color( *vit , MyMesh::Color( std::min((int)(r+0.5),255),
349 std::min((int)(g+0.5),255),
350 std::max((int)(b+0.5),0) ) );
351 r += d;
352 g += d2;
353 b -= d;
354 }
355 timer.stop();
356 std::cout << " colored " << mesh.n_vertices()
357 << " vertices in " << timer.as_string() << std::endl;
358 }
359
360 // ---------------------------------------- color faces feature
361 if ( opt.check( OpenMesh::IO::Options::FaceColor ) &&
362 !ropt.check( OpenMesh::IO::Options::FaceColor ) )
363 {
364 std::cout << "Color faces" << std::endl;
365
366 double d = 256.0/double(mesh.n_faces());
367 double d2 = d/2.0;
368 double r = 0.0, g = 50.0, b = 255.0;
369 timer.start();
370 MyMesh::FaceIter it = mesh.faces_begin();
371 for (; it != mesh.faces_end(); ++it)
372 {
373 mesh.set_color( *it , MyMesh::Color( std::min((int)(r+0.5),255),
374 std::min((int)(g+0.5),255),
375 std::max((int)(b+0.5),0) ) );
376 r += d2;
377// g += d2;
378 b -= d;
379 }
380 timer.stop();
381 std::cout << " colored " << mesh.n_faces()
382 << " faces in " << timer.as_string() << std::endl;
383 }
384
385 // ------------------------------------------------------------ write
386
387 std::cout << "writing.." << std::endl;
388 {
389 bool rc;
390 timer.start();
391 rc = OpenMesh::IO::write_mesh( mesh, ofname, opt );
392 timer.stop();
393
394 if (!rc)
395 {
396 std::cerr << " error writing mesh!" << std::endl;
397 return 1;
398 }
399
400 // -------------------------------------- write output and some info
401 if ( opt.check(OpenMesh::IO::Options::Binary) )
402 {
403 std::cout << " "
404 << OpenMesh::IO::binary_size(mesh, ofname, opt)
405 << std::endl;
406 }
407 if ( opt.vertex_has_normal() )
408 std::cout << " with vertex normals" << std::endl;
409 if ( opt.vertex_has_color() )
410 std::cout << " with vertex colors" << std::endl;
411 if ( opt.vertex_has_texcoord() )
412 std::cout << " with vertex texcoord" << std::endl;
413 if ( opt.face_has_normal() )
414 std::cout << " with face normals" << std::endl;
415 if ( opt.face_has_color() )
416 std::cout << " with face colors" << std::endl;
417 std::cout << " wrote in " << timer.as_string() << std::endl;
418 timer.reset();
419 }
420
421 return 0;
422}
Set options for reader/writer modules.
Definition: Options.hh:92
@ Swap
Swap byte order in binary mode.
Definition: Options.hh:104
@ FaceColor
Has (r) / store (w) face colors.
Definition: Options.hh:110
@ MSB
Assume big endian byte ordering.
Definition: Options.hh:102
@ Binary
Set binary mode for r/w.
Definition: Options.hh:101
@ LSB
Assume little endian byte ordering.
Definition: Options.hh:103
@ VertexNormal
Has (r) / store (w) vertex normals.
Definition: Options.hh:105
@ VertexTexCoord
Has (r) / store (w) texture coordinates.
Definition: Options.hh:107
@ VertexColor
Has (r) / store (w) vertex colors.
Definition: Options.hh:106
Kernel::FaceIter FaceIter
Scalar type.
Definition: PolyMeshT.hh:146
void update_face_normals()
Update normal vectors for all faces.
void update_vertex_normals()
Update normal vectors for all vertices.
Kernel::Color Color
Color type.
Definition: PolyMeshT.hh:116
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::ostream & operator<<(std::ostream &_o, const Timer &_t)
Definition: Timer.hh:205
std::string as_string(Format format=Automatic)
void start(void)
Start measurement.
@ Normal
Add normals to mesh item (vertices/faces)
Definition: Attributes.hh:82
@ TexCoord2D
Add 2D texture coordinates (vertices, halfedges)
Definition: Attributes.hh:87
@ Color
Add colors to mesh item (vertices/faces/edges)
Definition: Attributes.hh:83
@ PrevHalfedge
Add storage for previous halfedge (halfedges). The bit is set by default in the DefaultTraits.
Definition: Attributes.hh:84
size_t binary_size(const Mesh &_mesh, const std::string &_ext, Options _opt=Options::Default)
Get binary size of data.
Definition: MeshIO.hh:251
bool write_mesh(const Mesh &_mesh, const std::string &_filename, Options _opt=Options::Default, std::streamsize _precision=6)
Write a mesh to the file _filename.
Definition: MeshIO.hh:190
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition: MeshIO.hh:95