Panda3D
pfmVizzer.cxx
1 // Filename: pfmVizzer.cxx
2 // Created by: drose (30Sep12)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pfmVizzer.h"
16 #include "geomNode.h"
17 #include "geom.h"
18 #include "geomVertexData.h"
19 #include "geomVertexFormat.h"
20 #include "geomPoints.h"
21 #include "geomTriangles.h"
22 #include "geomVertexWriter.h"
23 #include "lens.h"
24 #include "pnmImage.h"
25 #include "config_grutil.h"
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: PfmVizzer::Constructor
29 // Access: Published
30 // Description: The PfmVizzer constructor receives a reference to a
31 // PfmFile which it will operate on. It does not keep
32 // ownership of this reference; it is your
33 // responsibility to ensure the PfmFile does not
34 // destruct during the lifetime of the PfmVizzer.
35 ////////////////////////////////////////////////////////////////////
37 PfmVizzer(PfmFile &pfm) : _pfm(pfm) {
38  _vis_inverse = false;
39  _vis_2d = false;
40  _keep_beyond_lens = false;
41  _vis_blend = NULL;
42  _aux_pfm = NULL;
43 }
44 
45 ////////////////////////////////////////////////////////////////////
46 // Function: PfmVizzer::project
47 // Access: Published
48 // Description: Adjusts each (x, y, z) point of the Pfm file by
49 // projecting it through the indicated lens, converting
50 // each point to a (u, v, w) texture coordinate. The
51 // resulting file can be generated to a mesh (with
52 // set_vis_inverse(true) and generate_vis_mesh())
53 // that will apply the lens distortion to an arbitrary
54 // texture image.
55 ////////////////////////////////////////////////////////////////////
56 void PfmVizzer::
57 project(const Lens *lens, const PfmFile *undist_lut) {
58  nassertv(_pfm.is_valid());
59 
60  static LMatrix4 to_uv(0.5f, 0.0f, 0.0f, 0.0f,
61  0.0f, 0.5f, 0.0f, 0.0f,
62  0.0f, 0.0f, 0.5f, 0.0f,
63  0.5f, 0.5f, 0.5f, 1.0f);
64 
65  for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
66  for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
67  if (!_pfm.has_point(xi, yi)) {
68  continue;
69  }
70  LPoint3f &p = _pfm.modify_point(xi, yi);
71 
72  LPoint3 film;
73  if (!lens->project(LCAST(PN_stdfloat, p), film) && !_keep_beyond_lens) {
74  if (_pfm.has_no_data_value()) {
75  _pfm.set_point4(xi, yi, _pfm.get_no_data_value());
76  } else {
77  _pfm.set_point4(xi, yi, LVecBase4f(0, 0, 0, 0));
78  }
79  } else {
80  // Now the lens gives us coordinates in the range [-1, 1].
81  // Rescale these to [0, 1].
82  LPoint3f uvw = LCAST(float, film * to_uv);
83 
84  if (undist_lut != NULL) {
85  // Apply the undistortion map, if given.
86  LPoint3f p2;
87  undist_lut->calc_bilinear_point(p2, uvw[0], 1.0 - uvw[1]);
88  uvw = p2;
89  uvw[1] = 1.0 - uvw[1];
90  }
91 
92  p = uvw;
93  }
94  }
95  }
96 }
97 
98 ////////////////////////////////////////////////////////////////////
99 // Function: PfmVizzer::extrude
100 // Access: Published
101 // Description: Converts each (u, v, depth) point of the Pfm file to
102 // an (x, y, z) point, by reversing project(). If the
103 // original file is only a 1-d file, assumes that it is
104 // a depth map with implicit (u, v) coordinates.
105 //
106 // This method is only valid for a linear lens (e.g. a
107 // PerspectiveLens or OrthographicLens). Non-linear
108 // lenses don't necessarily compute a sensible depth
109 // coordinate.
110 ////////////////////////////////////////////////////////////////////
111 void PfmVizzer::
112 extrude(const Lens *lens) {
113  nassertv(_pfm.is_valid());
114 
115  static LMatrix4 from_uv(2.0, 0.0, 0.0, 0.0,
116  0.0, 2.0, 0.0, 0.0,
117  0.0, 0.0, 2.0, 0.0,
118  -1.0, -1.0, -1.0, 1.0);
119 
120  PfmFile result;
121  result.clear(_pfm.get_x_size(), _pfm.get_y_size(), 3);
122  if (_pfm.has_no_data_value()) {
123  result.set_zero_special(true);
124  }
125 
126  if (lens->is_linear()) {
127  // If the lens is linear (Perspective or Orthographic), we can
128  // take the slightly faster approach of extruding all the points
129  // via a transform matrix.
130  const LMatrix4 &proj_mat_inv = lens->get_projection_mat_inv();
131 
132  if (_pfm.get_num_channels() == 1) {
133  // Create an implicit UV coordinate for each point.
134  LPoint2 uv_scale(1.0, 1.0);
135  if (_pfm.get_x_size() > 1) {
136  uv_scale[0] = 1.0 / PN_stdfloat(_pfm.get_x_size());
137  }
138  if (_pfm.get_y_size() > 1) {
139  uv_scale[1] = 1.0 / PN_stdfloat(_pfm.get_y_size());
140  }
141  for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
142  for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
143  if (!_pfm.has_point(xi, yi)) {
144  continue;
145  }
146  LPoint3 p, rp;
147  p.set(((PN_stdfloat)xi + 0.5) * uv_scale[0],
148  ((PN_stdfloat)yi + 0.5) * uv_scale[1],
149  (PN_stdfloat)_pfm.get_point1(xi, yi));
150 
151  from_uv.xform_point_in_place(p);
152  rp = proj_mat_inv.xform_point_general(p);
153  result.set_point(xi, yi, rp);
154  }
155  }
156  } else {
157  // Use the existing UV coordinate for each point.
158  for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
159  for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
160  if (!_pfm.has_point(xi, yi)) {
161  continue;
162  }
163  LPoint3 p, rp;
164  p = LCAST(PN_stdfloat, _pfm.get_point(xi, yi));
165 
166  from_uv.xform_point_in_place(p);
167  rp = proj_mat_inv.xform_point_general(p);
168  result.set_point(xi, yi, rp);
169  }
170  }
171  }
172  } else {
173  // If the lens is some non-linear specialty lens, we have to call
174  // Lens::extrude_depth() to correctly extrude each point.
175  if (_pfm.get_num_channels() == 1) {
176  // Create an implicit UV coordinate for each point.
177  LPoint2 uv_scale(1.0, 1.0);
178  if (_pfm.get_x_size() > 1) {
179  uv_scale[0] = 1.0 / PN_stdfloat(_pfm.get_x_size());
180  }
181  if (_pfm.get_y_size() > 1) {
182  uv_scale[1] = 1.0 / PN_stdfloat(_pfm.get_y_size());
183  }
184  for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
185  for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
186  if (!_pfm.has_point(xi, yi)) {
187  continue;
188  }
189  LPoint3 p, rp;
190  p.set(((PN_stdfloat)xi + 0.5) * uv_scale[0],
191  ((PN_stdfloat)yi + 0.5) * uv_scale[1],
192  (PN_stdfloat)_pfm.get_point1(xi, yi));
193 
194  from_uv.xform_point_in_place(p);
195  lens->extrude_depth(p, rp);
196  result.set_point(xi, yi, rp);
197  }
198  }
199  } else {
200  // Use the existing UV coordinate for each point.
201  for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
202  for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
203  if (!_pfm.has_point(xi, yi)) {
204  continue;
205  }
206  LPoint3 p, rp;
207  p = LCAST(PN_stdfloat, _pfm.get_point(xi, yi));
208 
209  from_uv.xform_point_in_place(p);
210  lens->extrude_depth(p, rp);
211  result.set_point(xi, yi, rp);
212  }
213  }
214  }
215  }
216 
217  _pfm = result;
218 }
219 
220 ////////////////////////////////////////////////////////////////////
221 // Function: PfmVizzer::clear_vis_columns
222 // Access: Published
223 // Description: Removes all of the previously-added vis columns in
224 // preparation for building a new list. See
225 // add_vis_column().
226 ////////////////////////////////////////////////////////////////////
227 void PfmVizzer::
229  _vis_columns.clear();
230 }
231 
232 ////////////////////////////////////////////////////////////////////
233 // Function: PfmVizzer::add_vis_column
234 // Access: Published
235 // Description: Adds a new vis column specification to the list of
236 // vertex data columns that will be generated at the
237 // next call to generate_vis_points() or
238 // generate_vis_mesh(). This advanced interface
239 // supercedes the higher-level set_vis_inverse(),
240 // set_flat_texcoord_name(), and set_vis_2d().
241 //
242 // If you use this advanced interface, you must specify
243 // explicitly the complete list of data columns to be
244 // created in the resulting GeomVertexData, by calling
245 // add_vis_column() each time. For each column, you
246 // specify the source of the column in the PFMFile, the
247 // target column and name in the GeomVertexData, and an
248 // optional transform matrix and/or lens to transform
249 // and project the point before generating it.
250 ////////////////////////////////////////////////////////////////////
251 void PfmVizzer::
252 add_vis_column(ColumnType source, ColumnType target,
253  InternalName *name, const TransformState *transform,
254  const Lens *lens, const PfmFile *undist_lut) {
255  add_vis_column(_vis_columns, source, target, name, transform, lens, undist_lut);
256 }
257 
258 ////////////////////////////////////////////////////////////////////
259 // Function: PfmVizzer::generate_vis_points
260 // Access: Published
261 // Description: Creates a point cloud with the points of the pfm as
262 // 3-d coordinates in space, and texture coordinates
263 // ranging from 0 .. 1 based on the position within the
264 // pfm grid.
265 ////////////////////////////////////////////////////////////////////
268  nassertr(_pfm.is_valid(), NodePath());
269 
270  bool check_aux_pfm = uses_aux_pfm();
271  nassertr(!check_aux_pfm || (_aux_pfm != NULL && _aux_pfm->is_valid()), NodePath());
272 
273  CPT(GeomVertexFormat) format;
274  if (_vis_inverse) {
275  if (_vis_2d) {
276  format = GeomVertexFormat::get_v3t2();
277  } else {
278  // We need a 3-d texture coordinate if we're inverting the vis
279  // and it's 3-d.
280  GeomVertexArrayFormat *v3t3 = new GeomVertexArrayFormat
281  (InternalName::get_vertex(), 3,
282  Geom::NT_stdfloat, Geom::C_point,
283  InternalName::get_texcoord(), 3,
284  Geom::NT_stdfloat, Geom::C_texcoord);
285  format = GeomVertexFormat::register_format(v3t3);
286  }
287  } else {
288  format = GeomVertexFormat::get_v3t2();
289  }
290 
291  PT(GeomVertexData) vdata = new GeomVertexData
292  ("points", format, Geom::UH_static);
293  vdata->set_num_rows(_pfm.get_x_size() * _pfm.get_y_size());
294  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
295  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
296 
297  LPoint2f uv_scale(1.0, 1.0);
298  if (_pfm.get_x_size() > 1) {
299  uv_scale[0] = 1.0f / PN_float32(_pfm.get_x_size());
300  }
301  if (_pfm.get_y_size() > 1) {
302  uv_scale[1] = 1.0f / PN_float32(_pfm.get_y_size());
303  }
304 
305  int num_points = 0;
306  for (int yi = 0; yi < _pfm.get_y_size(); ++yi) {
307  for (int xi = 0; xi < _pfm.get_x_size(); ++xi) {
308  if (!_pfm.has_point(xi, yi)) {
309  continue;
310  }
311  if (check_aux_pfm && !_aux_pfm->has_point(xi, yi)) {
312  continue;
313  }
314 
315  const LPoint3f &point = _pfm.get_point(xi, yi);
316  LPoint2f uv((PN_float32(xi) + 0.5) * uv_scale[0],
317  (PN_float32(yi) + 0.5) * uv_scale[1]);
318  if (_vis_inverse) {
319  vertex.add_data2f(uv);
320  texcoord.add_data3f(point);
321  } else if (_vis_2d) {
322  vertex.add_data2f(point[0], point[1]);
323  texcoord.add_data2f(uv);
324  } else {
325  vertex.add_data3f(point);
326  texcoord.add_data2f(uv);
327  }
328  ++num_points;
329  }
330  }
331 
332  PT(Geom) geom = new Geom(vdata);
333  PT(GeomPoints) points = new GeomPoints(Geom::UH_static);
334  points->add_next_vertices(num_points);
335  geom->add_primitive(points);
336 
337  PT(GeomNode) gnode = new GeomNode("");
338  gnode->add_geom(geom);
339  return NodePath(gnode);
340 }
341 
342 ////////////////////////////////////////////////////////////////////
343 // Function: PfmVizzer::generate_vis_mesh
344 // Access: Published
345 // Description: Creates a triangle mesh with the points of the pfm as
346 // 3-d coordinates in space, and texture coordinates
347 // ranging from 0 .. 1 based on the position within the
348 // pfm grid.
349 ////////////////////////////////////////////////////////////////////
351 generate_vis_mesh(MeshFace face) const {
352  nassertr(_pfm.is_valid(), NodePath());
353  bool check_aux_pfm = uses_aux_pfm();
354  nassertr(!check_aux_pfm || (_aux_pfm != NULL && _aux_pfm->is_valid()), NodePath());
355  nassertr(face != 0, NodePath());
356 
357  if (_pfm.get_num_channels() == 1 && _vis_columns.empty()) {
358  // If we're generating a default mesh from a one-channel pfm file,
359  // expand it to a three-channel pfm file to make the visualization
360  // useful.
361  PfmFile expanded;
362  expanded.clear_to_texcoords(_pfm.get_x_size(), _pfm.get_y_size());
363  expanded.copy_channel(2, _pfm, 0);
364  PfmVizzer exvizzer(expanded);
365  return exvizzer.generate_vis_mesh(face);
366  }
367 
368  if (_pfm.get_x_size() == 1 || _pfm.get_y_size() == 1) {
369  // Can't generate a 1-d mesh, so generate points in this case.
370  return generate_vis_points();
371  }
372 
373  PT(GeomNode) gnode = new GeomNode("");
374 
375  if (face & MF_front) {
376  make_vis_mesh_geom(gnode, false);
377  }
378 
379  if (face & MF_back) {
380  make_vis_mesh_geom(gnode, true);
381  }
382 
383  return NodePath(gnode);
384 }
385 
386 
387 ////////////////////////////////////////////////////////////////////
388 // Function: PfmVizzer::calc_max_u_displacement
389 // Access: Published
390 // Description: Computes the maximum amount of shift, in pixels
391 // either left or right, of any pixel in the distortion
392 // map. This can be passed to make_displacement(); see
393 // that function for more information.
394 ////////////////////////////////////////////////////////////////////
395 double PfmVizzer::
397  int x_size = _pfm.get_x_size();
398  int y_size = _pfm.get_y_size();
399 
400  double max_u = 0;
401 
402  for (int yi = 0; yi < y_size; ++yi) {
403  for (int xi = 0; xi < x_size; ++xi) {
404  if (!_pfm.has_point(xi, yi)) {
405  continue;
406  }
407 
408  const LPoint3f &point = _pfm.get_point(xi, yi);
409  double nxi = point[0] * (double)x_size - 0.5;
410 
411  max_u = max(max_u, cabs(nxi - (double)xi));
412  }
413  }
414 
415  return max_u;
416 }
417 
418 ////////////////////////////////////////////////////////////////////
419 // Function: PfmVizzer::calc_max_v_displacement
420 // Access: Published
421 // Description: Computes the maximum amount of shift, in pixels
422 // either up or down, of any pixel in the distortion
423 // map. This can be passed to make_displacement(); see
424 // that function for more information.
425 ////////////////////////////////////////////////////////////////////
426 double PfmVizzer::
428  int x_size = _pfm.get_x_size();
429  int y_size = _pfm.get_y_size();
430 
431  double max_v = 0;
432 
433  for (int yi = 0; yi < y_size; ++yi) {
434  for (int xi = 0; xi < x_size; ++xi) {
435  if (!_pfm.has_point(xi, yi)) {
436  continue;
437  }
438 
439  const LPoint3f &point = _pfm.get_point(xi, yi);
440  double nyi = point[1] * (double)y_size - 0.5;
441 
442  max_v = max(max_v, cabs(nyi - (double)yi));
443  }
444  }
445 
446  return max_v;
447 }
448 
449 ////////////////////////////////////////////////////////////////////
450 // Function: PfmVizzer::make_displacement
451 // Access: Published
452 // Description: Assuming the underlying PfmFile is a 2-d distortion
453 // mesh, with the U and V in the first two components
454 // and the third component unused, this computes an
455 // AfterEffects-style displacement map that represents
456 // the same distortion. The indicated PNMImage will be
457 // filled in with a displacement map image, with
458 // horizontal shift in the red channel and vertical
459 // shift in the green channel, where a fully bright (or
460 // fully black) pixel indicates a shift of max_u or
461 // max_v pixels.
462 //
463 // Use calc_max_u_displacement() and
464 // calc_max_v_displacement() to compute suitable values
465 // for max_u and max_v.
466 //
467 // This generates an integer 16-bit displacement image.
468 // It is a good idea, though not necessarily essential,
469 // to check "Preserve RGB" in the interpret footage
470 // section for each displacement image. Set
471 // for_32bit true if this is meant to be used in a
472 // 32-bit project file, and false if it is meant to be
473 // used in a 16-bit project file.
474 ////////////////////////////////////////////////////////////////////
475 void PfmVizzer::
476 make_displacement(PNMImage &result, double max_u, double max_v, bool for_32bit) const {
477  int x_size = _pfm.get_x_size();
478  int y_size = _pfm.get_y_size();
479  result.clear(x_size, y_size, 3, PNM_MAXMAXVAL);
480  result.fill_val(0, 0, PNM_MAXMAXVAL);
481 
482  // After Effects defines this as the zero (no-change) value. It's
483  // not exactly 0.5, because they round up.
484  static const int midval = (PNM_MAXMAXVAL + 1) / 2;
485 
486  double scale_factor;
487  if (for_32bit) {
488  // There doesn't appear to be an undershift needed on 32-bit
489  // projects, but we have the factor here anyway in case it
490  // develops.
491  scale_factor = ae_undershift_factor_32;
492  } else {
493  // Empirically, After Effects seems to undershift by precisely
494  // this amount (but only in a 16-bit project, not in a 32-bit
495  // project). Curiously, this value is very close to, but not
496  // exactly, 256 / 255.
497  scale_factor = ae_undershift_factor_16;
498  }
499 
500  double u_scale = scale_factor * 0.5 * PNM_MAXMAXVAL / max_u;
501  double v_scale = scale_factor * 0.5 * PNM_MAXMAXVAL / max_v;
502 
503  for (int yi = 0; yi < y_size; ++yi) {
504  for (int xi = 0; xi < x_size; ++xi) {
505  if (!_pfm.has_point(xi, yi)) {
506  continue;
507  }
508 
509  const LPoint3f &point = _pfm.get_point(xi, yi);
510  double nxi = point[0] * (double)x_size - 0.5;
511  double nyi = point[1] * (double)y_size - 0.5;
512 
513  double x_shift = (nxi - (double)xi);
514  double y_shift = (nyi - (double)yi);
515 
516  int u_val = midval + (int)cfloor(x_shift * u_scale + 0.5);
517  int v_val = midval + (int)cfloor(y_shift * v_scale + 0.5);
518 
519  // We use the blue channel to mark holes, so we can fill them in
520  // later.
521  result.set_xel_val(xi, yi,
522  min(max(u_val, 0), PNM_MAXMAXVAL),
523  min(max(v_val, 0), PNM_MAXMAXVAL),
524  0);
525  }
526  }
527 
528  // Now fill in holes.
529  for (int yi = 0; yi < y_size; ++yi) {
530  for (int xi = 0; xi < x_size; ++xi) {
531  if (!_pfm.has_point(xi, yi)) {
532  continue;
533  }
534 
535  const LPoint3f &point = _pfm.get_point(xi, yi);
536  double nxi = point[0] * (double)x_size - 0.5;
537  double nyi = point[1] * (double)y_size - 0.5;
538 
539  r_fill_displacement(result, xi - 1, yi, nxi, nyi, u_scale, v_scale, 1);
540  r_fill_displacement(result, xi + 1, yi, nxi, nyi, u_scale, v_scale, 1);
541  r_fill_displacement(result, xi, yi - 1, nxi, nyi, u_scale, v_scale, 1);
542  r_fill_displacement(result, xi, yi + 1, nxi, nyi, u_scale, v_scale, 1);
543  }
544  }
545 
546  // Finally, reset the blue channel for cleanliness.
547  for (int yi = 0; yi < y_size; ++yi) {
548  for (int xi = 0; xi < x_size; ++xi) {
549  result.set_blue_val(xi, yi, midval);
550  }
551  }
552 }
553 
554 ////////////////////////////////////////////////////////////////////
555 // Function: PfmVizzer::make_displacement
556 // Access: Published
557 // Description: Assuming the underlying PfmFile is a 2-d distortion
558 // mesh, with the U and V in the first two components
559 // and the third component unused, this computes an
560 // AfterEffects-style displacement map that represents
561 // the same distortion. The indicated PNMImage will be
562 // filled in with a displacement map image, with
563 // horizontal shift in the red channel and vertical
564 // shift in the green channel, where a fully bright (or
565 // fully black) pixel indicates a shift of max_u or
566 // max_v pixels.
567 //
568 // Use calc_max_u_displacement() and
569 // calc_max_v_displacement() to compute suitable values
570 // for max_u and max_v.
571 //
572 // This generates a 32-bit floating-point displacement
573 // image. It is essential to check "Preserve RGB" in
574 // the interpret footage section for each displacement
575 // image. Set for_32bit true if this is meant to
576 // be used in a 32-bit project file, and false if it is
577 // meant to be used in a 16-bit project file.
578 ////////////////////////////////////////////////////////////////////
579 void PfmVizzer::
580 make_displacement(PfmFile &result, double max_u, double max_v, bool for_32bit) const {
581  int x_size = _pfm.get_x_size();
582  int y_size = _pfm.get_y_size();
583  result.clear(x_size, y_size, 3);
584 
585  double scale_factor;
586  if (for_32bit) {
587  // There doesn't appear to be an undershift needed on 32-bit
588  // projects, but we have the factor here anyway in case it
589  // develops.
590  scale_factor = ae_undershift_factor_32;
591  } else {
592  // Empirically, After Effects seems to undershift by precisely
593  // this amount (but only in a 16-bit project, not in a 32-bit
594  // project). Curiously, this value is very close to, but not
595  // exactly, 256 / 255.
596  scale_factor = ae_undershift_factor_16;
597  }
598 
599  double u_scale = scale_factor * 0.5 / max_u;
600  double v_scale = scale_factor * 0.5 / max_v;
601 
602  for (int yi = 0; yi < y_size; ++yi) {
603  for (int xi = 0; xi < x_size; ++xi) {
604  if (!_pfm.has_point(xi, yi)) {
605  continue;
606  }
607 
608  const LPoint3f &point = _pfm.get_point(xi, yi);
609  double nxi = point[0] * (double)x_size - 0.5;
610  double nyi = point[1] * (double)y_size - 0.5;
611 
612  double x_shift = (nxi - (double)xi);
613  double y_shift = (nyi - (double)yi);
614 
615  float u_val = 0.5 + (float)(x_shift * u_scale);
616  float v_val = 0.5 + (float)(y_shift * v_scale);
617 
618  // We use the blue channel to mark holes, so we can fill them in
619  // later.
620  result.set_point3(xi, yi, LVecBase3f(u_val, v_val, 0));
621  }
622  }
623 
624  // Now fill in holes.
625  for (int yi = 0; yi < y_size; ++yi) {
626  for (int xi = 0; xi < x_size; ++xi) {
627  if (!_pfm.has_point(xi, yi)) {
628  continue;
629  }
630 
631  const LPoint3f &point = _pfm.get_point(xi, yi);
632  double nxi = point[0] * (double)x_size - 0.5;
633  double nyi = point[1] * (double)y_size - 0.5;
634 
635  r_fill_displacement(result, xi - 1, yi, nxi, nyi, u_scale, v_scale, 1);
636  r_fill_displacement(result, xi + 1, yi, nxi, nyi, u_scale, v_scale, 1);
637  r_fill_displacement(result, xi, yi - 1, nxi, nyi, u_scale, v_scale, 1);
638  r_fill_displacement(result, xi, yi + 1, nxi, nyi, u_scale, v_scale, 1);
639  }
640  }
641 
642  // Finally, reset the blue channel for cleanliness.
643  for (int yi = 0; yi < y_size; ++yi) {
644  for (int xi = 0; xi < x_size; ++xi) {
645  result.set_channel(xi, yi, 2, 0.5);
646  }
647  }
648 }
649 
650 ////////////////////////////////////////////////////////////////////
651 // Function: PfmVizzer::uses_aux_pfm
652 // Access: Private
653 // Description: Returns true if any of the vis_column tokens
654 // reference the aux_pfm file, false otherwise.
655 ////////////////////////////////////////////////////////////////////
656 bool PfmVizzer::
657 uses_aux_pfm() const {
658  for (VisColumns::const_iterator vci = _vis_columns.begin();
659  vci != _vis_columns.end();
660  ++vci) {
661  const VisColumn &column = *vci;
662  switch (column._source) {
663  case CT_aux_vertex1:
664  case CT_aux_vertex2:
665  case CT_aux_vertex3:
666  return true;
667  default:
668  break;
669  }
670  }
671 
672  return false;
673 }
674 
675 ////////////////////////////////////////////////////////////////////
676 // Function: PfmVizzer::r_fill_displacement
677 // Access: Private
678 // Description: Recursively fills in holes with the color of their
679 // nearest neighbor after processing the image. This
680 // avoids sudden discontinuities in the displacement map
681 // at the edge of the screen geometry.
682 ////////////////////////////////////////////////////////////////////
683 void PfmVizzer::
684 r_fill_displacement(PNMImage &result, int xi, int yi,
685  double nxi, double nyi, double u_scale, double v_scale,
686  int distance) const {
687  if (xi < 0 || yi < 0 ||
688  xi >= result.get_x_size() || yi >= result.get_y_size()) {
689  // Stop at the edge.
690  return;
691  }
692 
693  if (distance > 1000) {
694  // Avoid runaway recursion.
695  return;
696  }
697 
698  int val = result.get_blue_val(xi, yi);
699  if (val > distance) {
700  // We've found a point that's closer.
701  static const int midval = (PNM_MAXMAXVAL + 1) / 2;
702 
703  double x_shift = (nxi - (double)xi);
704  double y_shift = (nyi - (double)yi);
705  int u_val = midval + (int)cfloor(x_shift * u_scale + 0.5);
706  int v_val = midval + (int)cfloor(y_shift * v_scale + 0.5);
707  result.set_xel_val(xi, yi,
708  min(max(u_val, 0), PNM_MAXMAXVAL),
709  min(max(v_val, 0), PNM_MAXMAXVAL),
710  min(distance, PNM_MAXMAXVAL));
711 
712  r_fill_displacement(result, xi - 1, yi, nxi, nyi, u_scale, v_scale, distance + 1);
713  r_fill_displacement(result, xi + 1, yi, nxi, nyi, u_scale, v_scale, distance + 1);
714  r_fill_displacement(result, xi, yi - 1, nxi, nyi, u_scale, v_scale, distance + 1);
715  r_fill_displacement(result, xi, yi + 1, nxi, nyi, u_scale, v_scale, distance + 1);
716  }
717 }
718 
719 ////////////////////////////////////////////////////////////////////
720 // Function: PfmVizzer::r_fill_displacement
721 // Access: Private
722 // Description: Recursively fills in holes with the color of their
723 // nearest neighbor after processing the image. This
724 // avoids sudden discontinuities in the displacement map
725 // at the edge of the screen geometry.
726 ////////////////////////////////////////////////////////////////////
727 void PfmVizzer::
728 r_fill_displacement(PfmFile &result, int xi, int yi,
729  double nxi, double nyi, double u_scale, double v_scale,
730  int distance) const {
731  if (xi < 0 || yi < 0 ||
732  xi >= result.get_x_size() || yi >= result.get_y_size()) {
733  // Stop at the edge.
734  return;
735  }
736 
737  if (distance > 1000) {
738  // Avoid runaway recursion.
739  return;
740  }
741 
742  float val = result.get_channel(xi, yi, 2);
743  if (val > (float)distance) {
744  // We've found a point that's closer.
745  double x_shift = (nxi - (double)xi);
746  double y_shift = (nyi - (double)yi);
747  float u_val = 0.5 + (float)(x_shift * u_scale);
748  float v_val = 0.5 + (float)(y_shift * v_scale);
749  result.set_point3(xi, yi, LVecBase3f(u_val, v_val, distance));
750 
751  r_fill_displacement(result, xi - 1, yi, nxi, nyi, u_scale, v_scale, distance + 1);
752  r_fill_displacement(result, xi + 1, yi, nxi, nyi, u_scale, v_scale, distance + 1);
753  r_fill_displacement(result, xi, yi - 1, nxi, nyi, u_scale, v_scale, distance + 1);
754  r_fill_displacement(result, xi, yi + 1, nxi, nyi, u_scale, v_scale, distance + 1);
755  }
756 }
757 
758 ////////////////////////////////////////////////////////////////////
759 // Function: PfmVizzer::make_vis_mesh_geom
760 // Access: Private
761 // Description: Returns a triangle mesh for the pfm. If inverted is
762 // true, the mesh is facing the opposite direction.
763 ////////////////////////////////////////////////////////////////////
764 void PfmVizzer::
765 make_vis_mesh_geom(GeomNode *gnode, bool inverted) const {
766  static const bool keep_beyond_lens = true;
767  int num_x_cells = 1;
768  int num_y_cells = 1;
769 
770  int x_size = _pfm.get_x_size();
771  int y_size = _pfm.get_y_size();
772 
773  // This is the number of independent vertices we will require.
774  int num_vertices = x_size * y_size;
775  if (num_vertices == 0) {
776  // Trivial no-op.
777  return;
778  }
779 
780  bool reverse_normals = inverted;
781  bool reverse_faces = inverted;
782  if (!is_right_handed(get_default_coordinate_system())) {
783  reverse_faces = !reverse_faces;
784  }
785 
786  // This is the max number of vertex indices we might add to the
787  // GeomTriangles. (We might actually add fewer than this due to
788  // omitting the occasional missing data point.)
789  int max_indices = (x_size - 1) * (y_size - 1) * 6;
790 
791  while (num_vertices > pfm_vis_max_vertices || max_indices > pfm_vis_max_indices) {
792  // Too many vertices in one mesh. Subdivide the mesh into smaller
793  // pieces.
794  if (num_x_cells > num_y_cells) {
795  ++num_y_cells;
796  } else {
797  ++num_x_cells;
798  }
799 
800  x_size = (_pfm.get_x_size() + num_x_cells - 1) / num_x_cells + 1;
801  y_size = (_pfm.get_y_size() + num_y_cells - 1) / num_y_cells + 1;
802 
803  num_vertices = x_size * y_size;
804  max_indices = (x_size - 1) * (y_size - 1) * 6;
805  }
806 
807  // OK, now we know how many cells we need.
808  if (grutil_cat.is_debug()) {
809  grutil_cat.debug()
810  << "Generating mesh with " << num_x_cells << " x " << num_y_cells
811  << " pieces.\n";
812  }
813 
814  VisColumns vis_columns = _vis_columns;
815  if (vis_columns.empty()) {
816  build_auto_vis_columns(vis_columns, true);
817  }
818  bool check_aux_pfm = uses_aux_pfm();
819 
820  CPT(GeomVertexFormat) format = make_array_format(vis_columns);
821 
822  for (int yci = 0; yci < num_y_cells; ++yci) {
823  int y_begin = (yci * _pfm.get_y_size()) / num_y_cells;
824  int y_end = ((yci + 1) * _pfm.get_y_size()) / num_y_cells;
825 
826  // Include the first vertex from the next strip in this strip's
827  // vertices, so we are connected.
828  y_end = min(y_end + 1, _pfm.get_y_size());
829 
830  y_size = y_end - y_begin;
831  if (y_size == 0) {
832  continue;
833  }
834 
835  for (int xci = 0; xci < num_x_cells; ++xci) {
836  int x_begin = (xci * _pfm.get_x_size()) / num_x_cells;
837  int x_end = ((xci + 1) * _pfm.get_x_size()) / num_x_cells;
838  x_end = min(x_end + 1, _pfm.get_x_size());
839  x_size = x_end - x_begin;
840  if (x_size == 0) {
841  continue;
842  }
843 
844  num_vertices = x_size * y_size;
845  max_indices = (x_size - 1) * (y_size - 1) * 6;
846 
847  ostringstream mesh_name;
848  mesh_name << "mesh_" << xci << "_" << yci;
849  PT(GeomVertexData) vdata = new GeomVertexData
850  (mesh_name.str(), format, Geom::UH_static);
851 
852  vdata->set_num_rows(num_vertices);
853 
854  char *skip_points = new char[num_vertices];
855  memset(skip_points, 0, sizeof(char) * num_vertices);
856 
857  // Fill in all of the vertices.
858  for (VisColumns::const_iterator vci = vis_columns.begin();
859  vci != vis_columns.end();
860  ++vci) {
861  const VisColumn &column = *vci;
862  GeomVertexWriter vwriter(vdata, column._name);
863  vwriter.set_row(0);
864 
865  for (int yi = y_begin; yi < y_end; ++yi) {
866  for (int xi = x_begin; xi < x_end; ++xi) {
867  if (!column.add_data(*this, vwriter, xi, yi, reverse_normals)) {
868  skip_points[(yi - y_begin) * x_size + (xi - x_begin)] = (char)true;
869  }
870  }
871  }
872  }
873 
874  PT(Geom) geom = new Geom(vdata);
875  PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
876 
877  tris->reserve_num_vertices(max_indices);
878 
879  for (int yi = y_begin; yi < y_end - 1; ++yi) {
880  for (int xi = x_begin; xi < x_end - 1; ++xi) {
881 
882  if (!_pfm.has_point(xi, yi) ||
883  !_pfm.has_point(xi, yi + 1) ||
884  !_pfm.has_point(xi + 1, yi + 1) ||
885  !_pfm.has_point(xi + 1, yi)) {
886  continue;
887  }
888  if (check_aux_pfm && (!_aux_pfm->has_point(xi, yi) ||
889  !_aux_pfm->has_point(xi, yi + 1) ||
890  !_aux_pfm->has_point(xi + 1, yi + 1) ||
891  !_aux_pfm->has_point(xi + 1, yi))) {
892  continue;
893  }
894 
895  if (!keep_beyond_lens &&
896  (skip_points[(yi - y_begin) * x_size + (xi - x_begin)] ||
897  skip_points[(yi - y_begin + 1) * x_size + (xi - x_begin)] ||
898  skip_points[(yi - y_begin) * x_size + (xi - x_begin + 1)] ||
899  skip_points[(yi - y_begin + 1) * x_size + (xi - x_begin + 1)])) {
900  continue;
901  }
902 
903  int xi0 = xi - x_begin;
904  int yi0 = yi - y_begin;
905 
906  int vi0 = ((xi0) + (yi0) * x_size);
907  int vi1 = ((xi0) + (yi0 + 1) * x_size);
908  int vi2 = ((xi0 + 1) + (yi0 + 1) * x_size);
909  int vi3 = ((xi0 + 1) + (yi0) * x_size);
910 
911  if (reverse_faces) {
912  tris->add_vertices(vi2, vi0, vi1);
913  tris->close_primitive();
914 
915  tris->add_vertices(vi3, vi0, vi2);
916  tris->close_primitive();
917  } else {
918  tris->add_vertices(vi2, vi1, vi0);
919  tris->close_primitive();
920 
921  tris->add_vertices(vi3, vi2, vi0);
922  tris->close_primitive();
923  }
924  }
925  }
926  geom->add_primitive(tris);
927  gnode->add_geom(geom);
928 
929  delete[] skip_points;
930  }
931  }
932 }
933 
934 ////////////////////////////////////////////////////////////////////
935 // Function: PfmVizzer::add_vis_column
936 // Access: Private, Static
937 // Description: The private implementation of the public
938 // add_vis_column(), this adds the column to the
939 // indicated specific vector.
940 ////////////////////////////////////////////////////////////////////
941 void PfmVizzer::
942 add_vis_column(VisColumns &vis_columns, ColumnType source, ColumnType target,
943  InternalName *name, const TransformState *transform,
944  const Lens *lens, const PfmFile *undist_lut) {
945  VisColumn column;
946  column._source = source;
947  column._target = target;
948  column._name = name;
949  column._transform = transform;
950  if (transform == NULL) {
951  column._transform = TransformState::make_identity();
952  }
953  column._lens = lens;
954  if (undist_lut != NULL && undist_lut->is_valid()) {
955  column._undist_lut = undist_lut;
956  }
957  vis_columns.push_back(column);
958 }
959 
960 ////////////////////////////////////////////////////////////////////
961 // Function: PfmVizzer::build_auto_vis_columns
962 // Access: Private
963 // Description: This function is called internally to construct the
964 // list of vis_columns automatically from the high-level
965 // interfaces such as set_vis_inverse(),
966 // set_flat_texcoord_name(), and set_vis_2d(). It's not
967 // called if the list has been build explicitly.
968 ////////////////////////////////////////////////////////////////////
969 void PfmVizzer::
970 build_auto_vis_columns(VisColumns &vis_columns, bool for_points) const {
971  vis_columns.clear();
972 
973  if (_vis_2d) {
974  // No normals needed if we're just generating a 2-d mesh.
975  if (_vis_inverse) {
976  add_vis_column(vis_columns, CT_texcoord2, CT_vertex2, InternalName::get_vertex());
977  add_vis_column(vis_columns, CT_vertex2, CT_texcoord2, InternalName::get_texcoord());
978  } else {
979  add_vis_column(vis_columns, CT_vertex2, CT_vertex2, InternalName::get_vertex());
980  add_vis_column(vis_columns, CT_texcoord2, CT_texcoord2, InternalName::get_texcoord());
981  }
982 
983  } else {
984  if (_vis_inverse) {
985  // We need a 3-d texture coordinate if we're inverting the vis
986  // and it's 3-d. But we still don't need normals in that case.
987  add_vis_column(vis_columns, CT_texcoord3, CT_vertex3, InternalName::get_vertex());
988  add_vis_column(vis_columns, CT_vertex3, CT_texcoord3, InternalName::get_texcoord());
989  } else {
990  // Otherwise, we only need a 2-d texture coordinate, and we do
991  // want normals.
992  add_vis_column(vis_columns, CT_vertex3, CT_vertex3, InternalName::get_vertex());
993  add_vis_column(vis_columns, CT_normal3, CT_normal3, InternalName::get_normal());
994  add_vis_column(vis_columns, CT_texcoord2, CT_texcoord2, InternalName::get_texcoord());
995  }
996  }
997 
998  if (_flat_texcoord_name != (InternalName *)NULL) {
999  // We need an additional texcoord column for the flat texcoords.
1000  add_vis_column(vis_columns, CT_texcoord2, CT_texcoord2, _flat_texcoord_name);
1001  }
1002 
1003  if (_vis_blend != (PNMImage *)NULL) {
1004  // The blend map, if specified, also gets applied to the vertices.
1005  add_vis_column(vis_columns, CT_blend1, CT_blend1, InternalName::get_color());
1006  }
1007 }
1008 
1009 ////////////////////////////////////////////////////////////////////
1010 // Function: PfmVizzer::make_array_format
1011 // Access: Private
1012 // Description: Constructs a GeomVertexFormat that corresponds to the
1013 // vis_columns list.
1014 ////////////////////////////////////////////////////////////////////
1015 CPT(GeomVertexFormat) PfmVizzer::
1016 make_array_format(const VisColumns &vis_columns) const {
1017  PT(GeomVertexArrayFormat) array_format = new GeomVertexArrayFormat;
1018 
1019  for (VisColumns::const_iterator vci = vis_columns.begin();
1020  vci != vis_columns.end();
1021  ++vci) {
1022  const VisColumn &column = *vci;
1023  InternalName *name = column._name;
1024 
1025  int num_components = 0;
1026  GeomEnums::NumericType numeric_type = GeomEnums::NT_float32;
1027  GeomEnums::Contents contents = GeomEnums::C_point;
1028  switch (column._target) {
1029  case CT_texcoord2:
1030  num_components = 2;
1031  numeric_type = GeomEnums::NT_float32;
1032  contents = GeomEnums::C_texcoord;
1033  break;
1034 
1035  case CT_texcoord3:
1036  num_components = 3;
1037  numeric_type = GeomEnums::NT_float32;
1038  contents = GeomEnums::C_texcoord;
1039  break;
1040 
1041  case CT_vertex1:
1042  case CT_aux_vertex1:
1043  num_components = 1;
1044  numeric_type = GeomEnums::NT_float32;
1045  contents = GeomEnums::C_point;
1046  break;
1047 
1048  case CT_vertex2:
1049  case CT_aux_vertex2:
1050  num_components = 2;
1051  numeric_type = GeomEnums::NT_float32;
1052  contents = GeomEnums::C_point;
1053  break;
1054 
1055  case CT_vertex3:
1056  case CT_aux_vertex3:
1057  num_components = 3;
1058  numeric_type = GeomEnums::NT_float32;
1059  contents = GeomEnums::C_point;
1060  break;
1061 
1062  case CT_normal3:
1063  num_components = 3;
1064  numeric_type = GeomEnums::NT_float32;
1065  contents = GeomEnums::C_normal;
1066  break;
1067 
1068  case CT_blend1:
1069  num_components = 4;
1070  numeric_type = GeomEnums::NT_uint8;
1071  contents = GeomEnums::C_color;
1072  break;
1073  }
1074  nassertr(num_components != 0, NULL);
1075 
1076  array_format->add_column(name, num_components, numeric_type, contents);
1077  }
1078 
1079  return GeomVertexFormat::register_format(array_format);
1080 }
1081 
1082 ////////////////////////////////////////////////////////////////////
1083 // Function: PfmVizzer::VisColumn::add_data
1084 // Access: Public
1085 // Description: Adds the data for this column to the appropriate
1086 // column of the GeomVertexWriter. Returns true if the
1087 // point is valid, false otherwise.
1088 ////////////////////////////////////////////////////////////////////
1089 bool PfmVizzer::VisColumn::
1090 add_data(const PfmVizzer &vizzer, GeomVertexWriter &vwriter, int xi, int yi, bool reverse_normals) const {
1091  const PfmFile &pfm = vizzer.get_pfm();
1092  bool success = true;
1093 
1094  switch (_source) {
1095  case CT_texcoord2:
1096  {
1097  LPoint2f uv((PN_float32(xi) + 0.5) / PN_float32(pfm.get_x_size()),
1098  (PN_float32(yi) + 0.5) / PN_float32(pfm.get_y_size()));
1099  if (!transform_point(uv)) {
1100  success = false;
1101  }
1102  vwriter.set_data2f(uv);
1103  }
1104  break;
1105 
1106  case CT_texcoord3:
1107  {
1108  LPoint3f uv((PN_float32(xi) + 0.5) / PN_float32(pfm.get_x_size()),
1109  (PN_float32(yi) + 0.5) / PN_float32(pfm.get_y_size()),
1110  0.0f);
1111  if (!transform_point(uv)) {
1112  success = false;
1113  }
1114  vwriter.set_data3f(uv);
1115  }
1116  break;
1117 
1118  case CT_vertex1:
1119  {
1120  PN_float32 p = pfm.get_point1(xi, yi);
1121  LPoint2f point(p, 0.0);
1122  if (!transform_point(point)) {
1123  success = false;
1124  }
1125  vwriter.set_data2f(point);
1126  }
1127  break;
1128 
1129  case CT_aux_vertex1:
1130  {
1131  nassertr(vizzer.get_aux_pfm() != NULL, false);
1132  PN_float32 p = vizzer.get_aux_pfm()->get_point1(xi, yi);
1133  LPoint2f point(p, 0.0);
1134  if (!transform_point(point)) {
1135  success = false;
1136  }
1137  vwriter.set_data2f(point);
1138  }
1139  break;
1140 
1141  case CT_vertex2:
1142  {
1143  LPoint2f point = pfm.get_point2(xi, yi);
1144  if (!transform_point(point)) {
1145  success = false;
1146  }
1147  vwriter.set_data2f(point);
1148  }
1149  break;
1150 
1151  case CT_aux_vertex2:
1152  {
1153  nassertr(vizzer.get_aux_pfm() != NULL, false);
1154  LPoint2f point = vizzer.get_aux_pfm()->get_point2(xi, yi);
1155  if (!transform_point(point)) {
1156  success = false;
1157  }
1158  vwriter.set_data2f(point);
1159  }
1160  break;
1161 
1162  case CT_vertex3:
1163  {
1164  LPoint3f point = pfm.get_point(xi, yi);
1165  if (!transform_point(point)) {
1166  success = false;
1167  }
1168  vwriter.set_data3f(point);
1169  }
1170  break;
1171 
1172  case CT_aux_vertex3:
1173  {
1174  LPoint3f point = vizzer.get_aux_pfm()->get_point(xi, yi);
1175  if (!transform_point(point)) {
1176  success = false;
1177  }
1178  vwriter.set_data3f(point);
1179  }
1180  break;
1181 
1182  case CT_normal3:
1183  {
1184  // Calculate the normal based on two neighboring vertices.
1185  bool flip = reverse_normals;
1186 
1187  LPoint3f v[3];
1188  v[0] = pfm.get_point(xi, yi);
1189  v[1] = v[0];
1190  v[2] = v[0];
1191  if (pfm.has_point(xi + 1, yi)) {
1192  v[1] = pfm.get_point(xi + 1, yi);
1193  } else if (pfm.has_point(xi - 1, yi)) {
1194  v[1] = pfm.get_point(xi - 1, yi);
1195  flip = !flip;
1196  }
1197 
1198  if (pfm.has_point(xi, yi + 1)) {
1199  v[2] = pfm.get_point(xi, yi + 1);
1200  } else if (pfm.has_point(xi, yi - 1)) {
1201  v[2] = pfm.get_point(xi, yi - 1);
1202  flip = !flip;
1203  }
1204 
1205  LVector3f n = LVector3f::zero();
1206  for (int i = 0; i < 3; ++i) {
1207  const LPoint3f &v0 = v[i];
1208  const LPoint3f &v1 = v[(i + 1) % 3];
1209  n[0] += v0[1] * v1[2] - v0[2] * v1[1];
1210  n[1] += v0[2] * v1[0] - v0[0] * v1[2];
1211  n[2] += v0[0] * v1[1] - v0[1] * v1[0];
1212  }
1213  n.normalize();
1214  if (n.is_nan()) {
1215  /*
1216  cerr << "\nnan!\n"
1217  << " v[0] = " << v[0] << "\n"
1218  << " v[1] = " << v[1] << "\n"
1219  << " v[2] = " << v[2] << "\n";
1220  */
1221  n.set(0, 0, 0);
1222  success = false;
1223  }
1224  if (flip) {
1225  n = -n;
1226  }
1227  if (!transform_vector(n)) {
1228  success = false;
1229  }
1230  vwriter.set_data3f(n);
1231  }
1232  break;
1233 
1234  case CT_blend1:
1235  {
1236  const PNMImage *vis_blend = vizzer.get_vis_blend();
1237  if (vis_blend != NULL) {
1238  double gray = vis_blend->get_gray(xi, yi);
1239  vwriter.set_data3d(gray, gray, gray);
1240  }
1241  }
1242  break;
1243  }
1244 
1245  return success;
1246 }
1247 
1248 ////////////////////////////////////////////////////////////////////
1249 // Function: PfmVizzer::VisColumn::transform_point
1250 // Access: Public
1251 // Description: Transforms the indicated point as specified by the
1252 // VisColumn.
1253 ////////////////////////////////////////////////////////////////////
1254 bool PfmVizzer::VisColumn::
1255 transform_point(LPoint2f &point) const {
1256  bool success = true;
1257  if (!_transform->is_identity()) {
1258  LCAST(PN_float32, _transform->get_mat3()).xform_point_in_place(point);
1259  }
1260 
1261  return success;
1262 }
1263 
1264 ////////////////////////////////////////////////////////////////////
1265 // Function: PfmVizzer::VisColumn::transform_point
1266 // Access: Public
1267 // Description: Transforms the indicated point as specified by the
1268 // VisColumn.
1269 ////////////////////////////////////////////////////////////////////
1270 bool PfmVizzer::VisColumn::
1271 transform_point(LPoint3f &point) const {
1272  bool success = true;
1273  if (!_transform->is_identity()) {
1274  LCAST(PN_float32, _transform->get_mat()).xform_point_in_place(point);
1275  }
1276  if (_lens != (Lens *)NULL) {
1277  static LMatrix4f to_uv(0.5, 0.0, 0.0, 0.0,
1278  0.0, 0.5, 0.0, 0.0,
1279  0.0, 0.0, 1.0, 0.0,
1280  0.5, 0.5, 0.0, 1.0);
1281  LPoint3 film;
1282  if (!_lens->project(LCAST(PN_stdfloat, point), film)) {
1283  success = false;
1284  }
1285  point = to_uv.xform_point(LCAST(PN_float32, film));
1286  }
1287 
1288  if (_undist_lut != NULL) {
1289  LPoint3f p;
1290  if (!_undist_lut->calc_bilinear_point(p, point[0], 1.0 - point[1])) {
1291  // Point is missing.
1292  point.set(0, 0, 0);
1293  success = false;
1294  } else {
1295  point = p;
1296  point[1] = 1.0 - point[1];
1297  }
1298  }
1299 
1300  return success;
1301 }
1302 
1303 ////////////////////////////////////////////////////////////////////
1304 // Function: PfmVizzer::VisColumn::transform_vector
1305 // Access: Public
1306 // Description: Transforms the indicated vector as specified by the
1307 // VisColumn.
1308 ////////////////////////////////////////////////////////////////////
1309 bool PfmVizzer::VisColumn::
1310 transform_vector(LVector3f &vec) const {
1311  if (!_transform->is_identity()) {
1312  LCAST(PN_float32, _transform->get_mat()).xform_vec_in_place(vec);
1313  }
1314  return true;
1315 }
virtual bool is_linear() const
Returns true if the lens represents a linear projection (e.g.
Definition: lens.cxx:615
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void clear_vis_columns()
Removes all of the previously-added vis columns in preparation for building a new list...
Definition: pfmVizzer.cxx:228
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
bool has_point(int x, int y) const
Returns true if there is a valid point at x, y.
Definition: pfmFile.I:57
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
void make_displacement(PNMImage &result, double max_u, double max_v, bool for_32bit) const
Assuming the underlying PfmFile is a 2-d distortion mesh, with the U and V in the first two component...
Definition: pfmVizzer.cxx:476
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:45
NodePath generate_vis_points() const
Creates a point cloud with the points of the pfm as 3-d coordinates in space, and texture coordinates...
Definition: pfmVizzer.cxx:267
Defines a series of disconnected points.
Definition: geomPoints.h:25
void add_data2f(float x, float y)
Sets the write row to a particular 2-component value, and advances the write row. ...
void set_point4(int x, int y, const LVecBase4f &point)
Replaces the 4-component point value at the indicated point.
Definition: pfmFile.I:333
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:351
const LPoint3f & get_point(int x, int y) const
Returns the 3-component point value at the indicated point.
Definition: pfmFile.I:198
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:928
void set_xel_val(int x, int y, const xel &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:406
bool calc_bilinear_point(LPoint3f &result, PN_float32 x, PN_float32 y) const
Computes the weighted average of the four nearest points to the floating-point index (x...
Definition: pfmFile.cxx:708
static const LVector3f & zero()
Returns a zero-length vector.
Definition: lvector3.h:270
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
void extrude(const Lens *lens)
Converts each (u, v, depth) point of the Pfm file to an (x, y, z) point, by reversing project()...
Definition: pfmVizzer.cxx:112
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
Definition: lvecBase3.h:464
void add_vis_column(ColumnType source, ColumnType target, InternalName *name, const TransformState *transform=NULL, const Lens *lens=NULL, const PfmFile *undist_lut=NULL)
Adds a new vis column specification to the list of vertex data columns that will be generated at the ...
Definition: pfmVizzer.cxx:252
void set_point3(int x, int y, const LVecBase3f &point)
Replaces the 3-component point value at the indicated point.
Definition: pfmFile.I:259
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.
const LPoint2f & get_point2(int x, int y) const
Returns the 2-component point value at the indicated point.
Definition: pfmFile.I:124
PN_float32 get_point1(int x, int y) const
Returns the 1-component point value at the indicated point.
Definition: pfmFile.I:96
bool project(const LPoint3 &point3d, LPoint3 &point2d) const
Given a 3-d point in space, determine the 2-d point this maps to, in the range (-1,1) in both dimensions, where (0,0) is the center of the lens and (-1,-1) is the lower-left corner.
Definition: lens.I:168
void set_data3f(float x, float y, float z)
Sets the write row to a particular 3-component value, and advances the write row. ...
void set_data3d(double x, double y, double z)
Sets the write row to a particular 3-component value, and advances the write row. ...
void project(const Lens *lens, const PfmFile *undist_lut=NULL)
Adjusts each (x, y, z) point of the Pfm file by projecting it through the indicated lens...
Definition: pfmVizzer.cxx:57
const LMatrix4 & get_projection_mat_inv(StereoChannel channel=SC_mono) const
Returns the matrix that transforms from a 2-d point on the film to a 3-d vector in space...
Definition: lens.I:730
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component, or with a special extension, 2- or 4-component.
Definition: pfmFile.h:34
LVecBase3f xform_point(const LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
Definition: lmatrix.h:1667
void set_blue_val(int x, int y, xelval b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:548
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
const PNMImage * get_vis_blend() const
Returns the blending map set by the most recent call to set_vis_blend(), or NULL if there is no blend...
Definition: pfmVizzer.I:205
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:474
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
void clear_to_texcoords(int x_size, int y_size)
Replaces this PfmFile with a new PfmFile of size x_size x y_size x 3, containing the x y 0 values in ...
Definition: pfmFile.cxx:1555
A container for geometry primitives.
Definition: geom.h:58
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:68
bool has_no_data_value() const
Returns whether a "no data" value has been established by set_no_data_value().
Definition: pfmFile.I:534
bool set_num_rows(int n)
Sets the length of the array to n rows in all of the various arrays (presumably by adding rows)...
void set_point(int x, int y, const LVecBase3f &point)
Replaces the 3-component point value at the indicated point.
Definition: pfmFile.I:210
void add_data3f(float x, float y, float z)
Sets the write row to a particular 3-component value, and advances the write row. ...
void set_channel(int x, int y, int c, PN_float32 value)
Replaces the cth channel of the point value at the indicated point.
Definition: pfmFile.I:82
PfmFile & get_pfm()
Returns the reference to the PfmFile manipulated by this PfmVizzer.
Definition: pfmVizzer.I:23
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color, type, etc).
Definition: pnmImage.cxx:50
LPoint3f & modify_point(int x, int y)
Returns a modifiable 3-component point value at the indicated point.
Definition: pfmFile.I:233
void set_zero_special(bool zero_special)
Sets the zero_special flag.
Definition: pfmFile.I:459
int get_num_channels() const
Returns the number of channels in the image.
Defines a series of disconnected triangles.
Definition: geomTriangles.h:25
void set_data2f(float x, float y)
Sets the write row to a particular 2-component value, and advances the write row. ...
void copy_channel(int to_channel, const PfmFile &other, int from_channel)
Copies just the specified channel values from the indicated PfmFile (which could be same as this PfmF...
Definition: pfmFile.cxx:1477
void set_row(int row)
Sets the start row to the indicated value.
This is a two-component point in space.
Definition: lpoint2.h:92
bool extrude_depth(const LPoint3 &point2d, LPoint3 &point3d) const
Uses the depth component of the 3-d result from project() to compute the original point in 3-d space ...
Definition: lens.I:71
const PfmFile * get_aux_pfm() const
Returns the reference to the auxiliary PfmFile queried by this PfmVizzer.
Definition: pfmVizzer.I:248
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
double calc_max_v_displacement() const
Computes the maximum amount of shift, in pixels either up or down, of any pixel in the distortion map...
Definition: pfmVizzer.cxx:427
double calc_max_u_displacement() const
Computes the maximum amount of shift, in pixels either left or right, of any pixel in the distortion ...
Definition: pfmVizzer.cxx:396
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
LVecBase3f xform_point_general(const LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
Definition: lmatrix.h:1692
void fill_val(xelval red, xelval green, xelval blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.cxx:207
void clear()
Eliminates all data in the file.
Definition: pfmFile.cxx:81
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:642
PfmVizzer(PfmFile &pfm)
The PfmVizzer constructor receives a reference to a PfmFile which it will operate on...
Definition: pfmVizzer.cxx:37
This class aids in the visualization and manipulation of PfmFile objects.
Definition: pfmVizzer.h:33
const LPoint4f & get_no_data_value() const
If has_no_data_value() returns true, this returns the particular "no data" value. ...
Definition: pfmFile.I:556