@@ -40,11 +40,11 @@ int main(int argc, char* argv[]) {
40
40
return EXIT_SUCCESS;
41
41
}
42
42
if (args.opt (' h' , " help" ) || argc < 3 ) {
43
- std::cerr << " Usage: " << args.app () << " PARAM [PARAM...] FILE" << std::endl;
43
+ std::cerr << " Usage: " << args.app () << " PARAM [PARAM...] FILE [FILE...] " << std::endl;
44
44
std::cerr << " Parameters:" << std::endl;
45
45
std::cerr << " -h --help print this help and exit" << std::endl;
46
46
std::cerr << " -v --version print version and exit" << std::endl;
47
- std::cerr << " -o --out FILE put output to FILE instead of stdout" << std::endl;
47
+ std::cerr << " -o --out FILE put output to FILE instead of stdout (if 1 input given) " << std::endl;
48
48
std::cerr << " -O --overwrite edit input file directly, overwriting it" << std::endl;
49
49
std::cerr << " -i --info print info about the object and exit" << std::endl;
50
50
std::cerr << " -n --normalize-normals renormalize all normals" << std::endl;
@@ -59,6 +59,7 @@ int main(int argc, char* argv[]) {
59
59
std::cerr << " --fit[xyz] AMOUNT uniformly scale to fit AMOUNT in dimension" << std::endl;
60
60
std::cerr << " --resize[xyz] AMOUNT non-uniformly scale to fit AMOUNT in dimension" << std::endl;
61
61
std::cerr << std::endl;
62
+ std::cerr << " Multiple input files will force --overwrite mode." << std::endl;
62
63
std::cerr << " [xyz] - long option suffixed with x, y or z operates only on that axis." << std::endl;
63
64
std::cerr << " No suffix (or short form) assumes all axes." << std::endl;
64
65
std::cerr << " Example: " << args.app () << " --scale 0.5 model.obj" << std::endl;
@@ -71,12 +72,23 @@ int main(int argc, char* argv[]) {
71
72
vec3 normal_scale = args.opt (' ' , " invert-normals" ) ? vec3 (-1 .0f ) : vec3 (1.0 );
72
73
73
74
// Output stream handling
74
- std::string infile = argv[argc-1 ];
75
+ std::vector<std::string> files = args.orphans ();
76
+ auto removed_files_it = std::remove_if (files.begin (), files.end (), [](const std::string& file) { return file.find (" .obj" ) == std::string::npos; });
77
+ files.erase (removed_files_it, files.end ());
78
+ if (files.empty ()) {
79
+ std::cerr << " Need at least one input file!" << std::endl;
80
+ return EXIT_FAILURE;
81
+ }
75
82
std::string outfile = args.arg <std::string>(' o' , " out" );
76
83
std::ofstream fout;
77
- std::stringstream sout;
78
84
bool inPlaceOutput = false ;
79
- if (outfile == infile || args.opt (' O' , " overwrite" )) { // In-place
85
+ if (files.size () > 1 ) {
86
+ inPlaceOutput = !info;
87
+ if (!outfile.empty ()) {
88
+ std::cerr << " Can't use -o / --out option with multiple input files." << std::endl;
89
+ return EXIT_FAILURE;
90
+ }
91
+ } else if (outfile == files[0 ] || args.opt (' O' , " overwrite" )) { // In-place
80
92
inPlaceOutput = true ;
81
93
} else if (!outfile.empty ()) {
82
94
fout.open (outfile.c_str ());
@@ -85,7 +97,6 @@ int main(int argc, char* argv[]) {
85
97
return EXIT_FAILURE;
86
98
}
87
99
}
88
- std::ostream& out = inPlaceOutput ? sout : (outfile.empty () ? std::cout : fout);
89
100
90
101
vec3 scale (args.arg (' s' , " scale" , 1 .0f ));
91
102
scale.x *= args.arg (' ' , " scalex" , 1 .0f );
@@ -140,134 +151,145 @@ int main(int argc, char* argv[]) {
140
151
if (rotangles.z != 0 .0f ) temprot = rotate (temprot, rotangles.z , vec3 (0 ,0 ,1 ));
141
152
mat3 rotation (temprot);
142
153
143
- std::ifstream file (infile.c_str (), std::ios::binary);
144
- if (!file.is_open ()) {
145
- std::cerr << " Failed to open file " << infile << std::endl;
146
- return EXIT_FAILURE;
147
- }
154
+ bool infoHeaderDone = false ;
155
+ for (const std::string& infile : files) {
156
+ std::stringstream sout;
157
+ std::ifstream file (infile.c_str (), std::ios::binary);
158
+ std::ostream& out = inPlaceOutput ? sout : (outfile.empty () ? std::cout : fout);
159
+
160
+ if (!file.is_open ()) {
161
+ std::cerr << " Failed to open file " << infile << std::endl;
162
+ return EXIT_FAILURE;
163
+ }
164
+
165
+ std::string row;
166
+ // Analyzing pass
167
+ bool analyze = info || (center.length () > 0 .0f ) || (fit.length () > 0 .0f ) || (resize.length () > 0 .0f );
168
+ if (analyze) {
169
+ vec3 lbound (std::numeric_limits<float >::max ());
170
+ vec3 ubound (-std::numeric_limits<float >::max ());
171
+ std::map<std::string, unsigned > materials;
172
+ unsigned long long v_count = 0 , vt_count = 0 , vn_count = 0 , f_count = 0 , p_count = 0 , l_count = 0 , o_count = 0 ;
173
+ while (getline (file, row)) {
174
+ std::istringstream srow (row);
175
+ vec3 in;
176
+ std::string tempst;
177
+ if (row.substr (0 ,2 ) == " v " ) { // Vertices
178
+ srow >> tempst >> in.x >> in.y >> in.z ;
179
+ lbound = min (in, lbound);
180
+ ubound = max (in, ubound);
181
+ ++v_count;
182
+ }
183
+ else if (row.substr (0 ,3 ) == " vt " ) ++vt_count;
184
+ else if (row.substr (0 ,3 ) == " vn " ) ++vn_count;
185
+ else if (row.substr (0 ,2 ) == " p " ) ++p_count;
186
+ else if (row.substr (0 ,2 ) == " l " ) ++l_count;
187
+ else if (row.substr (0 ,2 ) == " f " ) ++f_count;
188
+ else if (row.substr (0 ,2 ) == " o " ) ++o_count;
189
+ else if (row.substr (0 ,7 ) == " usemtl " ) materials[row.substr (7 )]++;
190
+ }
191
+ center *= (lbound + ubound) * 0 .5f ;
192
+ // Output info?
193
+ if (info) {
194
+ if (!infoHeaderDone) {
195
+ out << APPNAME << " " << VERSION << std::endl;
196
+ infoHeaderDone = true ;
197
+ } else out << std::endl;
198
+ out << std::endl;
199
+ out << " Filename: " << infile << std::endl;
200
+ out << " Vertices: " << v_count << std::endl;
201
+ out << " TexCoords: " << vt_count << std::endl;
202
+ out << " Normals: " << vn_count << std::endl;
203
+ out << " Faces: " << f_count << std::endl;
204
+ out << " Points: " << p_count << std::endl;
205
+ out << " Lines: " << l_count << std::endl;
206
+ out << " Named objects: " << o_count << std::endl;
207
+ out << " Materials: " << materials.size () << std::endl;
208
+ out << " " << std::right << std::setw (W) << " x" << std::setw (W) << " y" << std::setw (W) << " z" << std::endl;
209
+ out << " Center: " << toString ((lbound + ubound) * 0 .5f ) << std::endl;
210
+ out << " Size: " << toString (ubound - lbound) << std::endl;
211
+ out << " Lower bounds: " << toString (lbound) << std::endl;
212
+ out << " Upper bounds: " << toString (ubound) << std::endl;
213
+ continue ;
214
+ }
215
+ if (fit.length ()) {
216
+ vec3 size = ubound - lbound;
217
+ float fitScale = 1 .f ;
218
+ if (args.arg (' ' , " fit" , 0 .f )) fitScale = args.arg (' ' , " fit" , 0 .f ) / compMax (size);
219
+ else if (fit.x ) fitScale = fit.x / size.x ;
220
+ else if (fit.y ) fitScale = fit.y / size.y ;
221
+ else if (fit.z ) fitScale = fit.z / size.z ;
222
+ scale *= fitScale;
223
+ }
224
+ if (resize.length ()) {
225
+ vec3 size = ubound - lbound;
226
+ vec3 resizeScale (1 , 1 , 1 );
227
+ if (resize.x ) resizeScale.x = resize.x / size.x ;
228
+ if (resize.y ) resizeScale.y = resize.y / size.y ;
229
+ if (resize.z ) resizeScale.z = resize.z / size.z ;
230
+ scale *= resizeScale;
231
+ }
232
+ }
233
+
234
+ auto outputUnmodifiedRow = [](std::ostream& out, const std::string& row) {
235
+ // getline stops at \n, so there might be \r hiding in there if we are reading CRLF files
236
+ int last = row.size () - 1 ;
237
+ if (last >= 0 && row[last] == ' \r ' )
238
+ out << row.substr (0 , last) << std::endl;
239
+ else out << row << std::endl;
240
+ };
148
241
149
- std::string row;
150
- // Analyzing pass
151
- bool analyze = info || (center.length () > 0 .0f ) || (fit.length () > 0 .0f ) || (resize.length () > 0 .0f );
152
- if (analyze) {
153
- vec3 lbound (std::numeric_limits<float >::max ());
154
- vec3 ubound (-std::numeric_limits<float >::max ());
155
- std::map<std::string, unsigned > materials;
156
- unsigned long long v_count = 0 , vt_count = 0 , vn_count = 0 , f_count = 0 , p_count = 0 , l_count = 0 , o_count = 0 ;
242
+ // Output pass
243
+ file.clear ();
244
+ file.seekg (0 , std::ios::beg);
157
245
while (getline (file, row)) {
158
246
std::istringstream srow (row);
159
247
vec3 in;
160
248
std::string tempst;
161
249
if (row.substr (0 ,2 ) == " v " ) { // Vertices
162
250
srow >> tempst >> in.x >> in.y >> in.z ;
163
- lbound = min (in, lbound);
164
- ubound = max (in, ubound);
165
- ++v_count;
251
+ vec3 old = in;
252
+ in -= center;
253
+ in *= mirror;
254
+ in *= scale;
255
+ in = rotation * in;
256
+ in += translate;
257
+ if (old != in)
258
+ out << " v " << in.x << " " << in.y << " " << in.z << std::endl;
259
+ else outputUnmodifiedRow (out, row);
260
+ } else if (row.substr (0 ,3 ) == " vt " ) { // Tex coords
261
+ srow >> tempst >> in.x >> in.y ;
262
+ vec3 old = in;
263
+ if (flipUvX) in.x = 1 .0f - in.x ;
264
+ if (flipUvY) in.y = 1 .0f - in.y ;
265
+ in.x *= scaleUv.x ;
266
+ in.y *= scaleUv.y ;
267
+ if (old != in)
268
+ out << " vt " << in.x << " " << in.y << std::endl;
269
+ else outputUnmodifiedRow (out, row);
270
+ } else if (row.substr (0 ,3 ) == " vn " ) { // Normals
271
+ srow >> tempst >> in.x >> in.y >> in.z ;
272
+ vec3 old = in;
273
+ in *= normal_scale;
274
+ if (normalize_normals) in = normalize (in);
275
+ if (old != in)
276
+ out << " vn " << in.x << " " << in.y << " " << in.z << std::endl;
277
+ else outputUnmodifiedRow (out, row);
278
+ } else {
279
+ outputUnmodifiedRow (out, row);
166
280
}
167
- else if (row.substr (0 ,3 ) == " vt " ) ++vt_count;
168
- else if (row.substr (0 ,3 ) == " vn " ) ++vn_count;
169
- else if (row.substr (0 ,2 ) == " p " ) ++p_count;
170
- else if (row.substr (0 ,2 ) == " l " ) ++l_count;
171
- else if (row.substr (0 ,2 ) == " f " ) ++f_count;
172
- else if (row.substr (0 ,2 ) == " o " ) ++o_count;
173
- else if (row.substr (0 ,7 ) == " usemtl " ) materials[row.substr (7 )]++;
174
- }
175
- center *= (lbound + ubound) * 0 .5f ;
176
- // Output info?
177
- if (info) {
178
- out << APPNAME << " " << VERSION << std::endl;
179
- out << " Filename: " << infile << std::endl;
180
- out << " Vertices: " << v_count << std::endl;
181
- out << " TexCoords: " << vt_count << std::endl;
182
- out << " Normals: " << vn_count << std::endl;
183
- out << " Faces: " << f_count << std::endl;
184
- out << " Points: " << p_count << std::endl;
185
- out << " Lines: " << l_count << std::endl;
186
- out << " Named objects: " << o_count << std::endl;
187
- out << " Materials: " << materials.size () << std::endl;
188
- out << " " << std::right << std::setw (W) << " x" << std::setw (W) << " y" << std::setw (W) << " z" << std::endl;
189
- out << " Center: " << toString ((lbound + ubound) * 0 .5f ) << std::endl;
190
- out << " Size: " << toString (ubound - lbound) << std::endl;
191
- out << " Lower bounds: " << toString (lbound) << std::endl;
192
- out << " Upper bounds: " << toString (ubound) << std::endl;
193
- return EXIT_SUCCESS;
194
- }
195
- if (fit.length ()) {
196
- vec3 size = ubound - lbound;
197
- float fitScale = 1 .f ;
198
- if (args.arg (' ' , " fit" , 0 .f )) fitScale = args.arg (' ' , " fit" , 0 .f ) / compMax (size);
199
- else if (fit.x ) fitScale = fit.x / size.x ;
200
- else if (fit.y ) fitScale = fit.y / size.y ;
201
- else if (fit.z ) fitScale = fit.z / size.z ;
202
- scale *= fitScale;
203
- }
204
- if (resize.length ()) {
205
- vec3 size = ubound - lbound;
206
- vec3 resizeScale (1 , 1 , 1 );
207
- if (resize.x ) resizeScale.x = resize.x / size.x ;
208
- if (resize.y ) resizeScale.y = resize.y / size.y ;
209
- if (resize.z ) resizeScale.z = resize.z / size.z ;
210
- scale *= resizeScale;
211
- }
212
- }
213
-
214
- auto outputUnmodifiedRow = [](std::ostream& out, const std::string& row) {
215
- // getline stops at \n, so there might be \r hiding in there if we are reading CRLF files
216
- int last = row.size () - 1 ;
217
- if (last >= 0 && row[last] == ' \r ' )
218
- out << row.substr (0 , last) << std::endl;
219
- else out << row << std::endl;
220
- };
221
-
222
- // Output pass
223
- file.clear ();
224
- file.seekg (0 , std::ios::beg);
225
- while (getline (file, row)) {
226
- std::istringstream srow (row);
227
- vec3 in;
228
- std::string tempst;
229
- if (row.substr (0 ,2 ) == " v " ) { // Vertices
230
- srow >> tempst >> in.x >> in.y >> in.z ;
231
- vec3 old = in;
232
- in -= center;
233
- in *= mirror;
234
- in *= scale;
235
- in = rotation * in;
236
- in += translate;
237
- if (old != in)
238
- out << " v " << in.x << " " << in.y << " " << in.z << std::endl;
239
- else outputUnmodifiedRow (out, row);
240
- } else if (row.substr (0 ,3 ) == " vt " ) { // Tex coords
241
- srow >> tempst >> in.x >> in.y ;
242
- vec3 old = in;
243
- if (flipUvX) in.x = 1 .0f - in.x ;
244
- if (flipUvY) in.y = 1 .0f - in.y ;
245
- in.x *= scaleUv.x ;
246
- in.y *= scaleUv.y ;
247
- if (old != in)
248
- out << " vt " << in.x << " " << in.y << std::endl;
249
- else outputUnmodifiedRow (out, row);
250
- } else if (row.substr (0 ,3 ) == " vn " ) { // Normals
251
- srow >> tempst >> in.x >> in.y >> in.z ;
252
- vec3 old = in;
253
- in *= normal_scale;
254
- if (normalize_normals) in = normalize (in);
255
- if (old != in)
256
- out << " vn " << in.x << " " << in.y << " " << in.z << std::endl;
257
- else outputUnmodifiedRow (out, row);
258
- } else {
259
- outputUnmodifiedRow (out, row);
260
281
}
261
- }
262
282
263
- if (inPlaceOutput) {
264
- file.close ();
265
- fout.open (infile.c_str ());
266
- if (fout.fail ()) {
267
- std::cerr << " Failed to open file " << infile << " for output" << std::endl;
268
- return EXIT_FAILURE;
283
+ if (inPlaceOutput) {
284
+ file.close ();
285
+ std::ofstream finplaceout;
286
+ finplaceout.open (infile.c_str ());
287
+ if (finplaceout.fail ()) {
288
+ std::cerr << " Failed to open file " << infile << " for output" << std::endl;
289
+ return EXIT_FAILURE;
290
+ }
291
+ finplaceout << sout.rdbuf ();
269
292
}
270
- fout << sout.rdbuf ();
271
293
}
272
294
273
295
return EXIT_SUCCESS;
0 commit comments