Panda3D
Loading...
Searching...
No Matches
pfmTrans.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file pfmTrans.cxx
10 * @author drose
11 * @date 2010-12-23
12 */
13
14#include "pfmTrans.h"
15#include "config_pfmprogs.h"
16#include "pfmFile.h"
17#include "pfmVizzer.h"
18#include "texture.h"
19#include "texturePool.h"
20#include "pointerTo.h"
21#include "string_utils.h"
22#include "pandaFileStream.h"
23
24using std::string;
25
26/**
27 *
28 */
29PfmTrans::
30PfmTrans() {
31 _no_data_nan_num_channels = 0;
32 _got_transform = false;
33 _transform = LMatrix4::ident_mat();
34 _rotate = 0;
35
37
38 set_program_brief("transform .pfm files");
39 set_program_description
40 ("pfm-trans reads an pfm file and transforms it, filters it, "
41 "operates on it, writing the output to another pfm file. A pfm "
42 "file contains a 2-d table of floating-point values.");
43
44 add_option
45 ("z", "", 0,
46 "Treats (0,0,0) in the pfm file as a special don't-touch value.",
47 &PfmTrans::dispatch_none, &_got_zero_special);
48
49 add_option
50 ("nan", "num_channels", 0,
51 "Treats a NaN in any of the first num_channels channels as a special don't-touch value.",
52 &PfmTrans::dispatch_int, &_got_no_data_nan, &_no_data_nan_num_channels);
53
54 add_option
55 ("resize", "width,height", 0,
56 "Resamples the pfm file to scale it to the indicated grid size. "
57 "A simple box filter is applied during the scale. Don't confuse this "
58 "with -TS, which scales the individual point values, but doesn't "
59 "change the number of points.",
60 &PfmTrans::dispatch_int_pair, &_got_resize, &_resize);
61
62 add_option
63 ("crop", "xbegin,xend,ybegin,yend", 0,
64 "Crops the pfm file to the indicated subregion.",
65 &PfmTrans::dispatch_int_quad, &_got_crop, &_crop);
66
67 add_option
68 ("autocrop", "", 0,
69 "Automatically crops to the smallest possible rectangle that includes "
70 "all points. Requires -z or -nan.",
71 &PfmTrans::dispatch_none, &_got_autocrop);
72
73 add_option
74 ("rotate", "degrees", 0,
75 "Rotates the pfm file the specified number of degrees counterclockwise, "
76 "which must be a multiple of 90.",
77 &PfmTrans::dispatch_int, nullptr, &_rotate);
78
79 add_option
80 ("mirror_x", "", 0,
81 "Flips the pfm file about the x axis.",
82 &PfmTrans::dispatch_none, &_got_mirror_x);
83
84 add_option
85 ("mirror_y", "", 0,
86 "Flips the pfm file about the y axis.",
87 &PfmTrans::dispatch_none, &_got_mirror_y);
88
89 add_option
90 ("o", "filename", 50,
91 "Specify the filename to which the resulting pfm file will be written. "
92 "This is only valid when there is only one input pfm file on the command "
93 "line. If you want to process multiple files simultaneously, you must "
94 "use -d.",
95 &PfmTrans::dispatch_filename, &_got_output_filename, &_output_filename);
96
97 add_option
98 ("d", "dirname", 50,
99 "Specify the name of the directory in which to write the processed pfm "
100 "files. If you are processing only one pfm file, this may be omitted "
101 "in lieu of the -o option.",
102 &PfmTrans::dispatch_filename, &_got_output_dirname, &_output_dirname);
103
104 add_option
105 ("vis", "filename.bam", 60,
106 "Generates a bam file that represents a visualization of the pfm file "
107 "as a 3-D geometric mesh. If -vistex is specified, the mesh is "
108 "textured.",
109 &PfmTrans::dispatch_filename, &_got_vis_filename, &_vis_filename);
110
111 add_option
112 ("visinv", "", 60,
113 "Inverts the visualization, generating a uniform 2-d mesh with the "
114 "3-d depth values encoded in the texture coordinates.",
115 &PfmTrans::dispatch_none, &_got_vis_inverse);
116
117 add_option
118 ("vis2d", "", 60,
119 "Respect only the first two components of each depth value, ignoring z.",
120 &PfmTrans::dispatch_none, &_got_vis_2d);
121
122 add_option
123 ("vistex", "texture.jpg", 60,
124 "Specifies the name of the texture to apply to the visualization.",
125 &PfmTrans::dispatch_filename, &_got_vistex_filename, &_vistex_filename);
126
127 add_option
128 ("ls", "filename.txt", 60,
129 "Lists the points in the file to the indicated text file.",
130 &PfmTrans::dispatch_filename, &_got_ls_filename, &_ls_filename);
131}
132
133
134/**
135 *
136 */
137void PfmTrans::
138run() {
139 if ((int)(_rotate / 90) * 90 != _rotate) {
140 nout << "-rotate can only accept a multiple of 90 degrees.\n";
141 exit(1);
142 }
143
144 if (_got_vis_filename) {
145 _mesh_root = NodePath("mesh_root");
146 }
147
148 Filenames::const_iterator fi;
149 for (fi = _input_filenames.begin(); fi != _input_filenames.end(); ++fi) {
150 PfmFile file;
151 if (!file.read(*fi)) {
152 nout << "Cannot read " << *fi << "\n";
153 exit(1);
154 }
155 if (!process_pfm(*fi, file)) {
156 exit(1);
157 }
158 }
159
160 if (_got_vis_filename) {
161 _mesh_root.write_bam_file(_vis_filename);
162 }
163}
164
165/**
166 * Handles a single pfm file.
167 */
169process_pfm(const Filename &input_filename, PfmFile &file) {
170 PfmVizzer vizzer(file);
171 if (_got_no_data_nan) {
172 file.set_no_data_nan(_no_data_nan_num_channels);
173 } else if (_got_zero_special) {
174 file.set_zero_special(true);
175 }
176 vizzer.set_vis_inverse(_got_vis_inverse);
177 vizzer.set_vis_2d(_got_vis_2d);
178
179 if (_got_autocrop) {
180 _got_crop = file.calc_autocrop(_crop[0], _crop[1], _crop[2], _crop[3]);
181 }
182
183 if (_got_crop) {
184 file.apply_crop(_crop[0], _crop[1], _crop[2], _crop[3]);
185 }
186
187 if (_got_resize) {
188 file.resize(_resize[0], _resize[1]);
189 }
190
191 if (_rotate != 0) {
192 int r = (_rotate / 90) % 4;
193 if (r < 0) {
194 r += 4;
195 }
196 switch (r) {
197 case 0:
198 break;
199 case 1:
200 // Rotate 90 degrees ccw.
201 file.flip(true, false, true);
202 break;
203 case 2:
204 // Rotate 180 degrees.
205
206 // Not sure right now why we can't flip both axes at once. But it works
207 // if we do one at a time. file.flip(true, true, false);
208 file.flip(true, false, false);
209 file.flip(false, true, false);
210 break;
211 case 3:
212 // Rotate 90 degrees cw.
213 file.flip(false, true, true);
214 break;
215 default:
216 nassertr(false, false);
217 }
218 }
219
220 if (_got_mirror_x) {
221 file.flip(true, false, false);
222 }
223
224 if (_got_mirror_y) {
225 file.flip(false, true, false);
226 }
227
228 if (_got_transform) {
229 file.xform(LCAST(PN_float32, _transform));
230 }
231
232 if (_got_vis_filename) {
233 NodePath mesh = vizzer.generate_vis_mesh(PfmVizzer::MF_both);
234 if (_got_vistex_filename) {
235 PT(Texture) tex = TexturePool::load_texture(_vistex_filename);
236 if (tex == nullptr) {
237 nout << "Couldn't find " << _vistex_filename << "\n";
238 } else {
239 tex->set_minfilter(SamplerState::FT_linear_mipmap_linear);
240 mesh.set_texture(tex);
241 if (tex->has_alpha(tex->get_format())) {
242 mesh.set_transparency(TransparencyAttrib::M_dual);
243 }
244 }
245 }
246 mesh.set_name(input_filename.get_basename_wo_extension());
247 mesh.reparent_to(_mesh_root);
248 }
249
250 if (_got_ls_filename) {
251 pofstream out;
252 _ls_filename.set_text();
253 if (_ls_filename.open_write(out, true)) {
254 for (int yi = 0; yi < file.get_y_size(); ++yi) {
255 for (int xi = 0; xi < file.get_x_size(); ++xi) {
256 if (file.has_point(xi, yi)) {
257 out << "(" << xi << ", " << yi << "):";
258 for (int ci = 0; ci < file.get_num_channels(); ++ci) {
259 out << " " << file.get_channel(xi, yi, ci);
260 }
261 out << "\n";
262 }
263 }
264 }
265 }
266 }
267
268 Filename output_filename;
269 if (_got_output_filename) {
270 output_filename = _output_filename;
271 } else if (_got_output_dirname) {
272 output_filename = Filename(_output_dirname, input_filename.get_basename());
273 }
274
275 if (!output_filename.empty()) {
276 return file.write(output_filename);
277 }
278
279 return true;
280}
281
282/**
283 * Adds -TS, -TT, etc. as valid options for this program. If the user
284 * specifies one of the options on the command line, the data will be
285 * transformed when the egg file is written out.
286 */
289 add_option
290 ("TS", "sx[,sy,sz]", 49,
291 "Scale the model uniformly by the given factor (if only one number "
292 "is given) or in each axis by sx, sy, sz (if three numbers are given).",
293 &PfmTrans::dispatch_scale, &_got_transform, &_transform);
294
295 add_option
296 ("TR", "x,y,z", 49,
297 "Rotate the model x degrees about the x axis, then y degrees about the "
298 "y axis, and then z degrees about the z axis.",
299 &PfmTrans::dispatch_rotate_xyz, &_got_transform, &_transform);
300
301 add_option
302 ("TA", "angle,x,y,z", 49,
303 "Rotate the model angle degrees counterclockwise about the given "
304 "axis.",
305 &PfmTrans::dispatch_rotate_axis, &_got_transform, &_transform);
306
307 add_option
308 ("TT", "x,y,z", 49,
309 "Translate the model by the indicated amount.\n\n"
310 "All transformation options (-TS, -TR, -TA, -TT) are cumulative and are "
311 "applied in the order they are encountered on the command line.",
312 &PfmTrans::dispatch_translate, &_got_transform, &_transform);
313}
314
315/**
316 * Does something with the additional arguments on the command line (after all
317 * the -options have been parsed). Returns true if the arguments are good,
318 * false otherwise.
319 */
320bool PfmTrans::
321handle_args(ProgramBase::Args &args) {
322 if (args.empty()) {
323 nout << "You must specify the pfm file(s) to read on the command line.\n";
324 return false;
325 }
326
327 if (_got_output_filename && args.size() == 1) {
328 if (_got_output_dirname) {
329 nout << "Cannot specify both -o and -d.\n";
330 return false;
331 }
332
333 } else {
334 if (_got_output_filename) {
335 nout << "Cannot use -o when multiple pfm files are specified.\n";
336 return false;
337 }
338 }
339
340 Args::const_iterator ai;
341 for (ai = args.begin(); ai != args.end(); ++ai) {
342 _input_filenames.push_back(Filename::from_os_specific(*ai));
343 }
344
345 return true;
346}
347
348/**
349 * Handles -TS, which specifies a scale transform. Var is an LMatrix4.
350 */
351bool PfmTrans::
352dispatch_scale(const string &opt, const string &arg, void *var) {
353 LMatrix4 *transform = (LMatrix4 *)var;
354
355 vector_string words;
356 tokenize(arg, words, ",");
357
358 PN_stdfloat sx, sy, sz;
359
360 bool okflag = false;
361 if (words.size() == 3) {
362 okflag =
363 string_to_stdfloat(words[0], sx) &&
364 string_to_stdfloat(words[1], sy) &&
365 string_to_stdfloat(words[2], sz);
366
367 } else if (words.size() == 1) {
368 okflag =
369 string_to_stdfloat(words[0], sx);
370 sy = sz = sx;
371 }
372
373 if (!okflag) {
374 nout << "-" << opt
375 << " requires one or three numbers separated by commas.\n";
376 return false;
377 }
378
379 *transform = (*transform) * LMatrix4::scale_mat(sx, sy, sz);
380
381 return true;
382}
383
384/**
385 * Handles -TR, which specifies a rotate transform about the three cardinal
386 * axes. Var is an LMatrix4.
387 */
388bool PfmTrans::
389dispatch_rotate_xyz(ProgramBase *self, const string &opt, const string &arg, void *var) {
390 PfmTrans *base = (PfmTrans *)self;
391 return base->ns_dispatch_rotate_xyz(opt, arg, var);
392}
393
394/**
395 * Handles -TR, which specifies a rotate transform about the three cardinal
396 * axes. Var is an LMatrix4.
397 */
398bool PfmTrans::
399ns_dispatch_rotate_xyz(const string &opt, const string &arg, void *var) {
400 LMatrix4 *transform = (LMatrix4 *)var;
401
402 vector_string words;
403 tokenize(arg, words, ",");
404
405 LVecBase3 xyz;
406
407 bool okflag = false;
408 if (words.size() == 3) {
409 okflag =
410 string_to_stdfloat(words[0], xyz[0]) &&
411 string_to_stdfloat(words[1], xyz[1]) &&
412 string_to_stdfloat(words[2], xyz[2]);
413 }
414
415 if (!okflag) {
416 nout << "-" << opt
417 << " requires three numbers separated by commas.\n";
418 return false;
419 }
420
421 LMatrix4 mat =
422 LMatrix4::rotate_mat(xyz[0], LVector3(1.0, 0.0, 0.0)) *
423 LMatrix4::rotate_mat(xyz[1], LVector3(0.0, 1.0, 0.0)) *
424 LMatrix4::rotate_mat(xyz[2], LVector3(0.0, 0.0, 1.0));
425
426 *transform = (*transform) * mat;
427
428 return true;
429}
430
431/**
432 * Handles -TA, which specifies a rotate transform about an arbitrary axis.
433 * Var is an LMatrix4.
434 */
435bool PfmTrans::
436dispatch_rotate_axis(ProgramBase *self, const string &opt, const string &arg, void *var) {
437 PfmTrans *base = (PfmTrans *)self;
438 return base->ns_dispatch_rotate_axis(opt, arg, var);
439}
440
441/**
442 * Handles -TA, which specifies a rotate transform about an arbitrary axis.
443 * Var is an LMatrix4.
444 */
445bool PfmTrans::
446ns_dispatch_rotate_axis(const string &opt, const string &arg, void *var) {
447 LMatrix4 *transform = (LMatrix4 *)var;
448
449 vector_string words;
450 tokenize(arg, words, ",");
451
452 PN_stdfloat angle;
453 LVecBase3 axis;
454
455 bool okflag = false;
456 if (words.size() == 4) {
457 okflag =
458 string_to_stdfloat(words[0], angle) &&
459 string_to_stdfloat(words[1], axis[0]) &&
460 string_to_stdfloat(words[2], axis[1]) &&
461 string_to_stdfloat(words[3], axis[2]);
462 }
463
464 if (!okflag) {
465 nout << "-" << opt
466 << " requires four numbers separated by commas.\n";
467 return false;
468 }
469
470 *transform = (*transform) * LMatrix4::rotate_mat(angle, axis);
471
472 return true;
473}
474
475/**
476 * Handles -TT, which specifies a translate transform. Var is an LMatrix4.
477 */
478bool PfmTrans::
479dispatch_translate(const string &opt, const string &arg, void *var) {
480 LMatrix4 *transform = (LMatrix4 *)var;
481
482 vector_string words;
483 tokenize(arg, words, ",");
484
485 LVector3 trans;
486
487 bool okflag = false;
488 if (words.size() == 3) {
489 okflag =
490 string_to_stdfloat(words[0], trans[0]) &&
491 string_to_stdfloat(words[1], trans[1]) &&
492 string_to_stdfloat(words[2], trans[2]);
493 }
494
495 if (!okflag) {
496 nout << "-" << opt
497 << " requires three numbers separated by commas.\n";
498 return false;
499 }
500
501 *transform = (*transform) * LMatrix4::translate_mat(trans);
502
503 return true;
504}
505
506
507int main(int argc, char *argv[]) {
508 PfmTrans prog;
509 prog.parse_command_line(argc, argv);
510 prog.run();
511 return 0;
512}
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
std::string get_basename() const
Returns the basename part of the filename.
Definition filename.I:367
void set_text()
Indicates that the filename represents a text file.
Definition filename.I:424
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition filename.I:386
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
void set_texture(Texture *tex, int priority=0)
Adds the indicated texture to the list of textures that will be rendered on the default texture stage...
set_name
Changes the name of the referenced node.
Definition nodePath.h:951
void reparent_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread())
Removes the referenced node of the NodePath from its current parent and attaches it to the referenced...
Definition nodePath.cxx:394
bool write_bam_file(const Filename &filename) const
Writes the contents of this node and below out to a bam file with the indicated filename.
void set_transparency(TransparencyAttrib::Mode mode, int priority=0)
Specifically sets or disables transparent rendering mode on this particular node.
int get_x_size() const
Returns the number of pixels in the X direction.
get_num_channels
Returns the number of channels in the image.
int get_y_size() const
Returns the number of pixels in the Y direction.
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition pfmFile.h:31
void set_no_data_nan(int num_channels)
Sets the no_data_nan flag.
Definition pfmFile.cxx:858
bool read(const Filename &fullpath)
Reads the PFM data from the indicated file, returning true on success, false on failure.
Definition pfmFile.cxx:121
bool write(const Filename &fullpath)
Writes the PFM data to the indicated file, returning true on success, false on failure.
Definition pfmFile.cxx:204
void resize(int new_x_size, int new_y_size)
Applies a simple filter to resample the pfm file in-place to the indicated size.
Definition pfmFile.cxx:956
void xform(const LMatrix4f &transform)
Applies the indicated transform matrix to all points in-place.
Definition pfmFile.cxx:1222
void set_zero_special(bool zero_special)
Sets the zero_special flag.
Definition pfmFile.I:371
PN_float32 get_channel(int x, int y, int c) const
Returns the cth channel of the point value at the indicated point.
Definition pfmFile.I:52
void flip(bool flip_x, bool flip_y, bool transpose)
Reverses, transposes, and/or rotates the table in-place according to the specified parameters.
Definition pfmFile.cxx:1174
bool calc_autocrop(int &x_begin, int &x_end, int &y_begin, int &y_end) const
Computes the minimum range of x and y across the PFM file that include all points.
Definition pfmFile.cxx:775
void apply_crop(int x_begin, int x_end, int y_begin, int y_end)
Reduces the PFM file to the cells in the rectangle bounded by (x_begin, x_end, y_begin,...
Definition pfmFile.cxx:1551
bool has_point(int x, int y) const
Returns true if there is a valid point at x, y.
Definition pfmFile.I:44
Operates on a pfm file.
Definition pfmTrans.h:29
void add_transform_options()
Adds -TS, -TT, etc.
Definition pfmTrans.cxx:288
bool process_pfm(const Filename &input_filename, PfmFile &file)
Handles a single pfm file.
Definition pfmTrans.cxx:169
This class aids in the visualization and manipulation of PfmFile objects.
Definition pfmVizzer.h:30
NodePath generate_vis_mesh(MeshFace face=MF_front) const
Creates a triangle mesh with the points of the pfm as 3-d coordinates in space, and texture coordinat...
void set_vis_2d(bool vis_2d)
Sets the vis_2d flag.
Definition pfmVizzer.I:101
void set_vis_inverse(bool vis_inverse)
Sets the vis_inverse flag.
Definition pfmVizzer.I:44
This is intended to be the base class for most general-purpose utility programs in the PANDATOOL tree...
Definition programBase.h:34
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Definition texturePool.I:70
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void tokenize(const string &str, vector_string &words, const string &delimiters, bool discard_repeated_delimiters)
Chops the source string up into pieces delimited by any of the characters specified in delimiters.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.