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  */
51 void PfmVizzer::
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  */
102 void PfmVizzer::
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  */
215 void PfmVizzer::
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  */
233 void PfmVizzer::
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  */
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  */
366 double PfmVizzer::
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  */
394 double PfmVizzer::
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  */
435 void PfmVizzer::
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  */
529 void PfmVizzer::
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 }
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
virtual bool is_linear() const
Returns true if the lens represents a linear projection (e.g.
Definition: lens.cxx:540
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:216
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_point(int x, int y) const
Returns true if there is a valid point at x, y.
Definition: pfmFile.I:44
Indicates a coordinate-system transform on vertices.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
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
get_num_channels
Returns the number of channels in the image.
Defines a series of disconnected points.
Definition: geomPoints.h:23
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:266
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
const LPoint3f & get_point(int x, int y) const
Returns the 3-component point value at the indicated point.
Definition: pfmFile.I:158
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:785
void set_xel_val(int x, int y, const xel &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:330
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_point3(int x, int y, const LVecBase3f &point)
Replaces the 3-component point value at the indicated point.
Definition: pfmFile.I:204
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:96
PN_float32 get_point1(int x, int y) const
Returns the 1-component point value at the indicated point.
Definition: pfmFile.I:74
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
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.
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
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition: pfmFile.h:31
void set_blue_val(int x, int y, xelval b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:441
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
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:383
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:1582
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
bool has_no_data_value() const
Returns whether a "no data" value has been established by set_no_data_value().
Definition: pfmFile.I:433
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).
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void set_point(int x, int y, const LVecBase3f &point)
Replaces the 3-component point value at the indicated point.
Definition: pfmFile.I:167
Encodes a string name in a hash table, mapping it to a pointer.
Definition: internalName.h:38
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:63
PfmFile & get_pfm()
Returns the reference to the PfmFile manipulated by this PfmVizzer.
Definition: pfmVizzer.I:18
This class defines the physical layout of the vertex data stored within a Geom.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition: pnmImage.cxx:48
LPoint3f & modify_point(int x, int y)
Returns a modifiable 3-component point value at the indicated point.
Definition: pfmFile.I:184
void set_zero_special(bool zero_special)
Sets the zero_special flag.
Definition: pfmFile.I:371
This describes the structure of a single array within a Geom data.
Defines a series of disconnected triangles.
Definition: geomTriangles.h:23
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:1513
static const GeomVertexFormat * get_v3t2()
Returns a standard vertex format with a 2-component texture coordinate pair and a 3-component vertex ...
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
const PfmFile * get_aux_pfm() const
Returns the reference to the auxiliary PfmFile queried by this PfmVizzer.
Definition: pfmVizzer.I:193
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
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
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
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
void clear()
Eliminates all data in the file.
Definition: pfmFile.cxx:77
void add_geom(Geom *geom, const RenderState *state=RenderState::make_empty())
Adds a new Geom to the node.
Definition: geomNode.cxx:584
PfmVizzer(PfmFile &pfm)
The PfmVizzer constructor receives a reference to a PfmFile which it will operate on.
Definition: pfmVizzer.cxx:36
This class aids in the visualization and manipulation of PfmFile objects.
Definition: pfmVizzer.h:30
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.