Panda3D
Loading...
Searching...
No Matches
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
26using std::max;
27using 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 */
36PfmVizzer(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 */
52project(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 */
103extrude(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 */
234add_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 */
246generate_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) {
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 {
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 */
327generate_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 */
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 */
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 */
436make_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 */
530make_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 */
601bool PfmVizzer::
602uses_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 */
625void PfmVizzer::
626r_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 */
666void PfmVizzer::
667r_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 */
701void PfmVizzer::
702make_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 */
874void PfmVizzer::
875add_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 */
899void PfmVizzer::
900build_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 */
942CPT(GeomVertexFormat) PfmVizzer::
943make_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 */
1013bool PfmVizzer::VisColumn::
1014add_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 */
1175bool PfmVizzer::VisColumn::
1176transform_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 */
1188bool PfmVizzer::VisColumn::
1189transform_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 */
1224bool PfmVizzer::VisColumn::
1225transform_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:612
Defines a series of disconnected points.
Definition geomPoints.h:23
Defines a series of disconnected triangles.
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.
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...
void clear_vis_columns()
Removes all of the previously-added vis columns in preparation for building a new list.
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...
double calc_max_u_displacement() const
Computes the maximum amount of shift, in pixels either left or right, of any pixel in the distortion ...
void extrude(const Lens *lens)
Converts each (u, v, depth) point of the Pfm file to an (x, y, z) point, by reversing project().
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...
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...
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 ...
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.