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