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  */
168 bool PfmTrans::
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  */
287 void PfmTrans::
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 }
This is intended to be the base class for most general-purpose utility programs in the PANDATOOL tree...
Definition: programBase.h:34
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
void set_vis_inverse(bool vis_inverse)
Sets the vis_inverse flag.
Definition: pfmVizzer.I:44
bool has_point(int x, int y) const
Returns true if there is a valid point at x, y.
Definition: pfmFile.I:44
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_name
Changes the name of the referenced node.
Definition: nodePath.h:946
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_num_channels
Returns the number of channels in the image.
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_...
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
Operates on a pfm file.
Definition: pfmTrans.h:29
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
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
void set_text()
Indicates that the filename represents a text file.
Definition: filename.I:424
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_vis_2d(bool vis_2d)
Sets the vis_2d flag.
Definition: pfmVizzer.I:101
int get_y_size() const
Returns the number of pixels in the Y direction.
int get_x_size() const
Returns the number of pixels in the X direction.
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:2871
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
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 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:391
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
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition: pfmFile.h:31
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:5601
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
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
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
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.
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
bool process_pfm(const Filename &input_filename, PfmFile &file)
Handles a single pfm file.
Definition: pfmTrans.cxx:169
void set_transparency(TransparencyAttrib::Mode mode, int priority=0)
Specifically sets or disables transparent rendering mode on this particular node.
Definition: nodePath.cxx:4836
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void add_transform_options()
Adds -TS, -TT, etc.
Definition: pfmTrans.cxx:288
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
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
void set_no_data_nan(int num_channels)
Sets the no_data_nan flag.
Definition: pfmFile.cxx:858
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
This class aids in the visualization and manipulation of PfmFile objects.
Definition: pfmVizzer.h:30
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.