Panda3D
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 
24 using std::string;
25 
26 /**
27  *
28  */
29 PfmTrans::
30 PfmTrans() {
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  */
137 void PfmTrans::
138 run() {
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  */
169 process_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  */
320 bool PfmTrans::
321 handle_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  */
351 bool PfmTrans::
352 dispatch_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  */
388 bool PfmTrans::
389 dispatch_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  */
398 bool PfmTrans::
399 ns_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  */
435 bool PfmTrans::
436 dispatch_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  */
445 bool PfmTrans::
446 ns_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  */
478 bool PfmTrans::
479 dispatch_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 
507 int 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:39
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
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
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...
Definition: nodePath.cxx:2938
set_name
Changes the name of the referenced node.
Definition: nodePath.h:945
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:395
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.
Definition: nodePath.cxx:5693
void set_transparency(TransparencyAttrib::Mode mode, int priority=0)
Specifically sets or disables transparent rendering mode on this particular node.
Definition: nodePath.cxx:4920
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...
Definition: pfmVizzer.cxx:327
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:47
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
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.