Panda3D
 All Classes Functions Variables Enumerations
pfmFile.cxx
00001 // Filename: pfmFile.cxx
00002 // Created by:  drose (23Dec10)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "config_grutil.h"
00016 #include "pfmFile.h"
00017 #include "virtualFileSystem.h"
00018 #include "pandaFileStream.h"
00019 #include "littleEndian.h"
00020 #include "bigEndian.h"
00021 #include "cmath.h"
00022 #include "geomNode.h"
00023 #include "geom.h"
00024 #include "geomVertexData.h"
00025 #include "geomVertexFormat.h"
00026 #include "geomPoints.h"
00027 #include "geomTriangles.h"
00028 #include "geomVertexWriter.h"
00029 #include "lens.h"
00030 #include "look_at.h"
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //     Function: PfmFile::Constructor
00034 //       Access: Published
00035 //  Description:
00036 ////////////////////////////////////////////////////////////////////
00037 PfmFile::
00038 PfmFile() {
00039   _zero_special = false;
00040   _vis_inverse = false;
00041   _vis_2d = false;
00042   clear();
00043 }
00044 
00045 ////////////////////////////////////////////////////////////////////
00046 //     Function: PfmFile::Copy Constructor
00047 //       Access: Published
00048 //  Description:
00049 ////////////////////////////////////////////////////////////////////
00050 PfmFile::
00051 PfmFile(const PfmFile &copy) :
00052   _table(copy._table),
00053   _x_size(copy._x_size),
00054   _y_size(copy._y_size),
00055   _scale(copy._scale),
00056   _num_channels(copy._num_channels),
00057   _zero_special(copy._zero_special),
00058   _vis_inverse(copy._vis_inverse),
00059   _vis_2d(copy._vis_2d)
00060 {
00061 }
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: PfmFile::Copy Assignment
00065 //       Access: Published
00066 //  Description:
00067 ////////////////////////////////////////////////////////////////////
00068 void PfmFile::
00069 operator = (const PfmFile &copy) {
00070   _table = copy._table;
00071   _x_size = copy._x_size;
00072   _y_size = copy._y_size;
00073   _scale = copy._scale;
00074   _num_channels = copy._num_channels;
00075   _zero_special = copy._zero_special;
00076   _vis_inverse = copy._vis_inverse;
00077   _vis_2d = copy._vis_2d;
00078 }
00079 
00080 ////////////////////////////////////////////////////////////////////
00081 //     Function: PfmFile::clear
00082 //       Access: Published
00083 //  Description: Eliminates all data in the file.
00084 ////////////////////////////////////////////////////////////////////
00085 void PfmFile::
00086 clear() {
00087   _x_size = 0;
00088   _y_size = 0;
00089   _scale = 1.0;
00090   _num_channels = 3;
00091   _table.clear();
00092 }
00093 
00094 ////////////////////////////////////////////////////////////////////
00095 //     Function: PfmFile::clear
00096 //       Access: Published
00097 //  Description: Resets to an empty table with a specific size.
00098 ////////////////////////////////////////////////////////////////////
00099 void PfmFile::
00100 clear(int x_size, int y_size, int num_channels) {
00101   nassertv(num_channels == 1 || num_channels == 3);
00102   nassertv(x_size >= 0 && y_size >= 0);
00103   _x_size = x_size;
00104   _y_size = y_size;
00105   _scale = 1.0;
00106   _num_channels = _num_channels;
00107 
00108   _table.clear();
00109   int size = _x_size * _y_size;
00110   _table.insert(_table.end(), size, LPoint3::zero());
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //     Function: PfmFile::read
00115 //       Access: Published
00116 //  Description: Reads the PFM data from the indicated file, returning
00117 //               true on success, false on failure.
00118 ////////////////////////////////////////////////////////////////////
00119 bool PfmFile::
00120 read(const Filename &fullpath) {
00121   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00122 
00123   Filename filename = Filename::binary_filename(fullpath);
00124   PT(VirtualFile) file = vfs->get_file(filename);
00125   if (file == (VirtualFile *)NULL) {
00126     // No such file.
00127     grutil_cat.error()
00128       << "Could not find " << fullpath << "\n";
00129     return false;
00130   }
00131 
00132   if (grutil_cat.is_debug()) {
00133     grutil_cat.debug()
00134       << "Reading PFM file " << filename << "\n";
00135   }
00136 
00137   istream *in = file->open_read_file(true);
00138   bool success = read(*in);
00139   vfs->close_read_file(in);
00140 
00141   return success;
00142 }
00143 
00144 ////////////////////////////////////////////////////////////////////
00145 //     Function: PfmFile::read
00146 //       Access: Published
00147 //  Description: Reads the PFM data from the indicated stream,
00148 //               returning true on success, false on failure.
00149 ////////////////////////////////////////////////////////////////////
00150 bool PfmFile::
00151 read(istream &in) {
00152   clear();
00153 
00154   string identifier;
00155   in >> identifier;
00156 
00157   if (identifier == "PF") {
00158     _num_channels = 3;
00159   } else if (identifier == "Pf") {
00160     _num_channels = 1;
00161   } else {
00162     grutil_cat.error()
00163       << "Not a pfm file.\n";
00164     return false;
00165   }
00166 
00167   int width, height;
00168   PN_stdfloat scale;
00169   in >> width >> height >> scale;
00170   if (!in) {
00171     grutil_cat.error()
00172       << "Error parsing pfm header.\n";
00173     return false;
00174   }
00175 
00176   // Skip the last newline/whitespace character before the raw data
00177   // begins.
00178   in.get();
00179 
00180   bool little_endian = false;
00181   if (scale < 0) {
00182     scale = -scale;
00183     little_endian = true;
00184   }
00185   if (pfm_force_littleendian) {
00186     little_endian = true;
00187   }
00188   if (pfm_reverse_dimensions) {
00189     int t = width;
00190     width = height;
00191     height = t;
00192   }
00193 
00194   _x_size = width;
00195   _y_size = height;
00196   _scale = scale;
00197 
00198   // So far, so good.  Now read the data.
00199   int size = _x_size * _y_size;
00200   _table.reserve(size);
00201 
00202   if (little_endian) {
00203     for (int i = 0; i < size; ++i) {
00204       LPoint3 point = LPoint3::zero();
00205       for (int ci = 0; ci < _num_channels; ++ci) {
00206         PN_float32 data;
00207         in.read((char *)&data, sizeof(data));
00208         LittleEndian value(&data, sizeof(data));
00209         PN_float32 result;
00210         value.store_value(&result, sizeof(result));
00211         if (!cnan(result)) {
00212           point[ci] = result;
00213         }
00214       }
00215       _table.push_back(point);
00216     }
00217   } else {
00218     for (int i = 0; i < size; ++i) {
00219       LPoint3 point = LPoint3::zero();
00220       for (int ci = 0; ci < _num_channels; ++ci) {
00221         PN_float32 data;
00222         in.read((char *)&data, sizeof(data));
00223         BigEndian value(&data, sizeof(data));
00224         PN_float32 result;
00225         value.store_value(&result, sizeof(result));
00226         if (!cnan(result)) {
00227           point[ci] = result;
00228         }
00229       }
00230       _table.push_back(point);
00231     }
00232   }
00233 
00234   if (in.fail() && !in.eof()) {
00235     return false;
00236   }
00237 
00238   nassertr(sizeof(PN_float32) == 4, false);
00239   return true;
00240 }
00241 
00242 ////////////////////////////////////////////////////////////////////
00243 //     Function: PfmFile::write
00244 //       Access: Published
00245 //  Description: Writes the PFM data to the indicated file, returning
00246 //               true on success, false on failure.
00247 ////////////////////////////////////////////////////////////////////
00248 bool PfmFile::
00249 write(const Filename &fullpath) {
00250   Filename filename = Filename::binary_filename(fullpath);
00251   pofstream out;
00252   if (!filename.open_write(out)) {
00253     grutil_cat.error()
00254       << "Unable to open " << filename << "\n";
00255     return false;
00256   }
00257 
00258   if (grutil_cat.is_debug()) {
00259     grutil_cat.debug()
00260       << "Writing PFM file " << filename << "\n";
00261   }
00262 
00263   return write(out);
00264 }
00265 
00266 ////////////////////////////////////////////////////////////////////
00267 //     Function: PfmFile::write
00268 //       Access: Published
00269 //  Description: Writes the PFM data to the indicated stream,
00270 //               returning true on success, false on failure.
00271 ////////////////////////////////////////////////////////////////////
00272 bool PfmFile::
00273 write(ostream &out) {
00274   nassertr(is_valid(), false);
00275 
00276   if (_num_channels == 1) {
00277     out << "Pf\n";
00278   } else {
00279     out << "PF\n";
00280   }
00281   out << _x_size << " " << _y_size << "\n";
00282 
00283   PN_stdfloat scale = cabs(_scale);
00284   if (scale == 0.0f) {
00285     scale = 1.0f;
00286   }
00287 #ifndef WORDS_BIGENDIAN
00288   // Little-endian computers must write a negative scale to indicate
00289   // the little-endian nature of the output.
00290   scale = -scale;
00291 #endif
00292   out << scale << "\n";
00293 
00294   int size = _x_size * _y_size;
00295   for (int i = 0; i < size; ++i) {
00296     const LPoint3 &point = _table[i];
00297     for (int ci = 0; ci < _num_channels; ++ci) {
00298       PN_float32 data = point[ci];
00299       out.write((const char *)&data, sizeof(data));
00300     }
00301   }
00302 
00303   if (out.fail()) {
00304     return false;
00305   }
00306   nassertr(sizeof(PN_float32) == 4, false);
00307   return true;
00308 }
00309 
00310 ////////////////////////////////////////////////////////////////////
00311 //     Function: PfmFile::calc_average_point
00312 //       Access: Published
00313 //  Description: Computes the unweighted average point of all points
00314 //               within the box centered at (x, y) with the indicated
00315 //               Manhattan-distance radius.  Missing points are
00316 //               assigned the value of their nearest neighbor.
00317 //               Returns true if successful, or false if the point
00318 //               value cannot be determined.
00319 ////////////////////////////////////////////////////////////////////
00320 bool PfmFile::
00321 calc_average_point(LPoint3 &result, PN_stdfloat x, PN_stdfloat y, PN_stdfloat radius) const {
00322   result = LPoint3::zero();
00323 
00324   int min_x = int(ceil(x - radius));
00325   int min_y = int(ceil(y - radius));
00326   int max_x = int(floor(x + radius));
00327   int max_y = int(floor(y + radius));
00328 
00329   // We first construct a mini-grid of x_size by y_size integer values
00330   // to index into the main table.  This indirection allows us to fill
00331   // in the holes in the mini-grid with the nearest known values
00332   // before we compute the average.
00333   int x_size = max_x - min_x + 1;
00334   int y_size = max_y - min_y + 1;
00335   int size = x_size * y_size;
00336   if (size == 0) {
00337     return false;
00338   }
00339 
00340   pvector<MiniGridCell> mini_grid;
00341   mini_grid.insert(mini_grid.end(), size, MiniGridCell());
00342 
00343   // Now collect the known data points and apply them to the
00344   // mini-grid.
00345   min_x = max(min_x, 0);
00346   min_y = max(min_y, 0);
00347   max_x = min(max_x, _x_size - 1);
00348   max_y = min(max_y, _y_size - 1);
00349 
00350   bool got_any = false;
00351   int xi, yi;
00352   for (yi = min_y; yi <= max_y; ++yi) {
00353     for (xi = min_x; xi <= max_x; ++xi) {
00354       const LPoint3 &p = _table[yi * _x_size + xi];
00355       if (_zero_special && p == LPoint3::zero()) {
00356         continue;
00357       }
00358 
00359       int gi = (yi - min_y) * y_size + (xi - min_x);
00360       nassertr(gi >= 0 && gi < size, false);
00361       mini_grid[gi]._ti = yi * _x_size + xi;
00362       mini_grid[gi]._dist = 0;
00363       got_any = true;
00364     }
00365   }
00366 
00367   if (!got_any) {
00368     return false;
00369   }
00370 
00371   // Now recursively fill in any missing holes in the mini-grid.
00372   for (yi = 0; yi < y_size; ++yi) {
00373     for (xi = 0; xi < x_size; ++xi) {
00374       int gi = yi * x_size + xi;
00375       if (mini_grid[gi]._dist == 0) {
00376         int ti = mini_grid[gi]._ti;
00377         fill_mini_grid(&mini_grid[0], x_size, y_size, xi + 1, yi, 1, ti);
00378         fill_mini_grid(&mini_grid[0], x_size, y_size, xi - 1, yi, 1, ti);
00379         fill_mini_grid(&mini_grid[0], x_size, y_size, xi, yi + 1, 1, ti);
00380         fill_mini_grid(&mini_grid[0], x_size, y_size, xi, yi - 1, 1, ti);
00381       }
00382     }
00383   }
00384 
00385   // Now the mini-grid is completely filled, so we can compute the
00386   // average.
00387   for (int gi = 0; gi < size; ++gi) {
00388     int ti = mini_grid[gi]._ti;
00389     nassertr(ti >= 0 && ti < (int)_table.size(), false);
00390     result += _table[ti];
00391   }
00392 
00393   result /= PN_stdfloat(size);
00394   return true;
00395 }
00396 
00397 ////////////////////////////////////////////////////////////////////
00398 //     Function: PfmFile::calc_min_max
00399 //       Access: Published
00400 //  Description: Calculates the minimum and maximum x, y, and z depth
00401 //               component values, representing the bounding box of
00402 //               depth values, and places them in the indicated
00403 //               vectors.  Returns true if successful, false if the
00404 //               mesh contains no points.
00405 ////////////////////////////////////////////////////////////////////
00406 bool PfmFile::
00407 calc_min_max(LVecBase3 &min_depth, LVecBase3 &max_depth) const {
00408   bool any_points = false;
00409 
00410   min_depth = LVecBase3::zero();
00411   max_depth = LVecBase3::zero();
00412 
00413   Table::const_iterator ti;
00414   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00415     const LPoint3 &p = (*ti);
00416     if (_zero_special && p == LPoint3::zero()) {
00417       continue;
00418     }
00419     
00420     if (!any_points) {
00421       min_depth = p;
00422       max_depth = p;
00423       any_points = true;
00424     } else {
00425       min_depth[0] = min(min_depth[0], p[0]);
00426       min_depth[1] = min(min_depth[1], p[1]);
00427       min_depth[2] = min(min_depth[2], p[2]);
00428       max_depth[0] = max(max_depth[0], p[0]);
00429       max_depth[1] = max(max_depth[1], p[1]);
00430       max_depth[2] = max(max_depth[2], p[2]);
00431     }
00432   }
00433 
00434   return any_points;
00435 }
00436 
00437 ////////////////////////////////////////////////////////////////////
00438 //     Function: PfmFile::resize
00439 //       Access: Published
00440 //  Description: Applies a simple filter to resample the pfm file
00441 //               in-place to the indicated size.  Don't confuse this
00442 //               with applying a scale to all of the points via
00443 //               xform().
00444 ////////////////////////////////////////////////////////////////////
00445 void PfmFile::
00446 resize(int new_x_size, int new_y_size) {
00447   if (_x_size == 0 || _y_size == 0 || new_x_size == 0 || new_y_size == 0) {
00448     clear(new_x_size, new_y_size, _num_channels);
00449     return;
00450   }
00451 
00452   if (new_x_size == _x_size && new_y_size == _y_size) {
00453     return;
00454   }
00455 
00456   Table new_data;
00457   new_data.reserve(new_x_size * new_y_size);
00458 
00459   PN_stdfloat from_x0, from_x1, from_y0, from_y1;
00460 
00461   PN_stdfloat x_scale = 1.0;
00462   PN_stdfloat y_scale = 1.0;
00463 
00464   if (new_x_size > 1) {
00465     x_scale = (PN_stdfloat)(_x_size - 1) / (PN_stdfloat)(new_x_size - 1);
00466   }
00467   if (new_y_size > 1) {
00468     y_scale = (PN_stdfloat)(_y_size - 1) / (PN_stdfloat)(new_y_size - 1);
00469   }
00470 
00471   from_y0 = 0.0;
00472   for (int to_y = 0; to_y < new_y_size; ++to_y) {
00473     from_y1 = (to_y + 0.5) * y_scale;
00474     from_y1 = min(from_y1, (PN_stdfloat) _y_size);
00475 
00476     from_x0 = 0.0;
00477     for (int to_x = 0; to_x < new_x_size; ++to_x) {
00478       from_x1 = (to_x + 0.5) * x_scale;
00479       from_x1 = min(from_x1, (PN_stdfloat) _x_size);
00480 
00481       // Now the box from (from_x0, from_y0) - (from_x1, from_y1)
00482       // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y).
00483       LPoint3 result;
00484       box_filter_region(result, from_x0, from_y0, from_x1, from_y1);
00485       new_data.push_back(result);
00486 
00487       from_x0 = from_x1;
00488     }
00489     from_y0 = from_y1;
00490   }
00491 
00492   _table.swap(new_data);
00493   _x_size = new_x_size;
00494   _y_size = new_y_size;
00495 }
00496 
00497 ////////////////////////////////////////////////////////////////////
00498 //     Function: PfmFile::reverse_rows
00499 //       Access: Published
00500 //  Description: Performs an in-place reversal of the row (y) data.
00501 ////////////////////////////////////////////////////////////////////
00502 void PfmFile::
00503 reverse_rows() {
00504   nassertv(is_valid());
00505 
00506   Table reversed;
00507   reversed.reserve(_table.size());
00508   for (int yi = 0; yi < _y_size; ++yi) {
00509     int source_yi = _y_size - 1 - yi;
00510     int start = source_yi * _x_size;
00511     reversed.insert(reversed.end(), 
00512                     _table.begin() + start, _table.begin() + start + _x_size);
00513   }
00514 
00515   nassertv(reversed.size() == _table.size());
00516   _table.swap(reversed);
00517 }
00518 
00519 ////////////////////////////////////////////////////////////////////
00520 //     Function: PfmFile::xform
00521 //       Access: Published
00522 //  Description: Applies the indicated transform matrix to all points
00523 //               in-place.
00524 ////////////////////////////////////////////////////////////////////
00525 void PfmFile::
00526 xform(const LMatrix4 &transform) {
00527   nassertv(is_valid());
00528 
00529   Table::iterator ti;
00530   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00531     if (_zero_special && (*ti) == LPoint3::zero()) {
00532       continue;
00533     }
00534 
00535     (*ti) = (*ti) * transform;
00536   }
00537 }
00538 
00539 ////////////////////////////////////////////////////////////////////
00540 //     Function: PfmFile::project
00541 //       Access: Published
00542 //  Description: Adjusts each (x, y, z) point of the Pfm file by
00543 //               projecting it through the indicated lens, converting
00544 //               each point to a (u, v, w) texture coordinate.  The
00545 //               resulting file can be generated to a mesh (with
00546 //               set_vis_inverse(true) and generate_vis_mesh())
00547 //               that will apply the lens distortion to an arbitrary
00548 //               texture image.
00549 ////////////////////////////////////////////////////////////////////
00550 void PfmFile::
00551 project(const Lens *lens) {
00552   nassertv(is_valid());
00553 
00554   static LMatrix4 to_uv(0.5, 0.0, 0.0, 0.0,
00555                         0.0, 0.5, 0.0, 0.0, 
00556                         0.0, 0.0, 1.0, 0.0, 
00557                         0.5, 0.5, 0.0, 1.0);
00558   
00559   Table::iterator ti;
00560   for (ti = _table.begin(); ti != _table.end(); ++ti) {
00561     if (_zero_special && (*ti) == LPoint3::zero()) {
00562       continue;
00563     }
00564 
00565     LPoint3 &p = (*ti);
00566     LPoint3 film;
00567     lens->project(p, film);
00568     p = to_uv.xform_point(film);
00569   }
00570 }
00571 
00572 ////////////////////////////////////////////////////////////////////
00573 //     Function: PfmFile::merge
00574 //       Access: Published
00575 //  Description: Wherever there is missing data in this PfmFile (that
00576 //               is, wherever has_point() returns false), copy data
00577 //               from the other PfmFile, which must be exactly the
00578 //               same dimensions as this one.
00579 ////////////////////////////////////////////////////////////////////
00580 void PfmFile::
00581 merge(const PfmFile &other) {
00582   nassertv(is_valid() && other.is_valid());
00583   nassertv(other._x_size == _x_size && other._y_size == _y_size);
00584 
00585   if (!_zero_special) {
00586     // Trivial no-op.
00587     return;
00588   }
00589 
00590   for (size_t i = 0; i < _table.size(); ++i) {
00591     if (_table[i] == LPoint3::zero()) {
00592       _table[i] = other._table[i];
00593     }
00594   }
00595 }
00596 
00597 ////////////////////////////////////////////////////////////////////
00598 //     Function: PfmFile::compute_planar_bounds
00599 //       Access: Published
00600 //  Description: This version of this method exists for temporary
00601 //               backward compatibility only.
00602 ////////////////////////////////////////////////////////////////////
00603 PT(BoundingHexahedron) PfmFile::
00604 compute_planar_bounds(PN_stdfloat point_dist, PN_stdfloat sample_radius) const {
00605   return compute_planar_bounds(LPoint2(0.5, 0.5), point_dist, sample_radius, false);
00606 }
00607 
00608 
00609 ////////////////////////////////////////////////////////////////////
00610 //     Function: PfmFile::compute_planar_bounds
00611 //       Access: Published
00612 //  Description: Computes the minmax bounding volume of the points in
00613 //               3-D space, assuming the points represent a
00614 //               mostly-planar surface.
00615 //
00616 //               This algorithm works by sampling the (square)
00617 //               sample_radius pixels at the four point_dist corners
00618 //               around the center (cx - pd, cx + pd) and so on, to
00619 //               approximate the plane of the surface.  Then all of
00620 //               the points are projected into that plane and the
00621 //               bounding volume of the entire mesh within that plane
00622 //               is determined.  If points_only is true, the bounding
00623 //               volume of only those four points is determined.
00624 //
00625 //               center, point_dist and sample_radius are in UV space,
00626 //               i.e. in the range 0..1.
00627 ////////////////////////////////////////////////////////////////////
00628 PT(BoundingHexahedron) PfmFile::
00629 compute_planar_bounds(const LPoint2 &center, PN_stdfloat point_dist, PN_stdfloat sample_radius, bool points_only) const {
00630   LPoint3 p0, p1, p2, p3;
00631   compute_sample_point(p0, center[0] + point_dist, center[1] - point_dist, sample_radius);
00632   compute_sample_point(p1, center[0] + point_dist, center[1] + point_dist, sample_radius);
00633   compute_sample_point(p2, center[0] - point_dist, center[1] + point_dist, sample_radius);
00634   compute_sample_point(p3, center[0] - point_dist, center[1] - point_dist, sample_radius);
00635 
00636   LPoint3 normal;
00637 
00638   normal[0] = p0[1] * p1[2] - p0[2] * p1[1];
00639   normal[1] = p0[2] * p1[0] - p0[0] * p1[2];
00640   normal[2] = p0[0] * p1[1] - p0[1] * p1[0];
00641 
00642   normal[0] += p1[1] * p2[2] - p1[2] * p2[1];
00643   normal[1] += p1[2] * p2[0] - p1[0] * p2[2];
00644   normal[2] += p1[0] * p2[1] - p1[1] * p2[0];
00645 
00646   normal[0] += p2[1] * p3[2] - p2[2] * p3[1];
00647   normal[1] += p2[2] * p3[0] - p2[0] * p3[2];
00648   normal[2] += p2[0] * p3[1] - p2[1] * p3[0];
00649 
00650   normal[0] += p3[1] * p0[2] - p3[2] * p0[1];
00651   normal[1] += p3[2] * p0[0] - p3[0] * p0[2];
00652   normal[2] += p3[0] * p0[1] - p3[1] * p0[0];
00653 
00654   normal.normalize();
00655 
00656   LVector3 up = (p1 - p0) + (p2 - p3);
00657   LPoint3 pcenter = ((p0 + p1 + p2 + p3) * 0.25);
00658 
00659   // Compute the transform necessary to rotate all of the points into
00660   // the Y = 0 plane.
00661   LMatrix4 rotate;
00662   look_at(rotate, normal, up);
00663 
00664   LMatrix4 rinv;
00665   rinv.invert_from(rotate);
00666 
00667   LPoint3 trans = pcenter * rinv;
00668   rinv.set_row(3, -trans);
00669   rotate.invert_from(rinv);
00670 
00671   // Now determine the minmax.
00672   PN_stdfloat min_x, min_y, min_z, max_x, max_y, max_z;
00673   bool got_point = false;
00674   if (points_only) {
00675     LPoint3 points[4] = {
00676       p0 * rinv,
00677       p1 * rinv,
00678       p2 * rinv,
00679       p3 * rinv,
00680     };
00681     for (int i = 0; i < 4; ++i) {
00682       const LPoint3 &point = points[i];
00683       if (!got_point) {
00684         min_x = point[0];
00685         min_y = point[1];
00686         min_z = point[2];
00687         max_x = point[0];
00688         max_y = point[1];
00689         max_z = point[2];
00690         got_point = true;
00691       } else {
00692         min_x = min(min_x, point[0]);
00693         min_y = min(min_y, point[1]);
00694         min_z = min(min_z, point[2]);
00695         max_x = max(max_x, point[0]);
00696         max_y = max(max_y, point[1]);
00697         max_z = max(max_z, point[2]);
00698       }
00699     }
00700 
00701   } else {
00702     Table::const_iterator ti;
00703     for (ti = _table.begin(); ti != _table.end(); ++ti) {
00704       if (_zero_special && (*ti) == LPoint3::zero()) {
00705         continue;
00706       }
00707       LPoint3 point = (*ti) * rinv;
00708       if (!got_point) {
00709         min_x = point[0];
00710         min_y = point[1];
00711         min_z = point[2];
00712         max_x = point[0];
00713         max_y = point[1];
00714         max_z = point[2];
00715         got_point = true;
00716       } else {
00717         min_x = min(min_x, point[0]);
00718         min_y = min(min_y, point[1]);
00719         min_z = min(min_z, point[2]);
00720         max_x = max(max_x, point[0]);
00721         max_y = max(max_y, point[1]);
00722         max_z = max(max_z, point[2]);
00723       }
00724     }
00725   }
00726 
00727   PT(BoundingHexahedron) bounds;
00728   
00729   // We create a BoundingHexahedron with the points in a particular
00730   // well-defined order, based on the current coordinate system.
00731   CoordinateSystem cs = get_default_coordinate_system();
00732   switch (cs) {
00733   case CS_yup_right:
00734     bounds = new BoundingHexahedron
00735       (LPoint3(min_x, min_y, min_z), LPoint3(max_x, min_y, min_z),
00736        LPoint3(min_x, max_y, min_z), LPoint3(max_x, max_y, min_z),
00737        LPoint3(min_x, min_y, max_z), LPoint3(max_x, min_y, max_z),
00738        LPoint3(min_x, max_y, max_z), LPoint3(max_x, max_y, max_z));
00739     break;
00740 
00741   case CS_zup_right:
00742     bounds = new BoundingHexahedron
00743       (LPoint3(min_x, min_y, min_z), LPoint3(max_x, min_y, min_z),
00744        LPoint3(min_x, min_y, max_z), LPoint3(max_x, min_y, max_z),
00745        LPoint3(min_x, max_y, min_z), LPoint3(max_x, max_y, min_z),
00746        LPoint3(min_x, max_y, max_z), LPoint3(max_x, max_y, max_z));
00747     break;
00748 
00749   case CS_yup_left:
00750     bounds = new BoundingHexahedron
00751       (LPoint3(max_x, min_y, max_z), LPoint3(min_x, min_y, max_z),
00752        LPoint3(max_x, max_y, max_z), LPoint3(min_x, max_y, max_z),
00753        LPoint3(max_x, min_y, min_z), LPoint3(min_x, min_y, min_z),
00754        LPoint3(max_x, max_y, min_z), LPoint3(min_x, max_y, min_z));
00755     break;
00756 
00757   case CS_zup_left:
00758     bounds = new BoundingHexahedron
00759       (LPoint3(max_x, max_y, min_z), LPoint3(min_x, max_y, min_z),
00760        LPoint3(max_x, max_y, max_z), LPoint3(min_x, max_y, max_z),
00761        LPoint3(max_x, min_y, min_z), LPoint3(min_x, min_y, min_z),
00762        LPoint3(max_x, min_y, max_z), LPoint3(min_x, min_y, max_z));
00763     break;
00764   }
00765 
00766   // Rotate the bounding volume back into the original space of the
00767   // screen.
00768   bounds->xform(rotate);
00769 
00770   return bounds;
00771 }
00772 
00773 ////////////////////////////////////////////////////////////////////
00774 //     Function: PfmFile::compute_sample_point
00775 //       Access: Published
00776 //  Description: Computes the average of all the point within
00777 //               sample_radius (manhattan distance) and the indicated
00778 //               point.
00779 //
00780 //               The point coordinates are given in UV space, in the
00781 //               range 0..1.
00782 ////////////////////////////////////////////////////////////////////
00783 void PfmFile::
00784 compute_sample_point(LPoint3 &result,
00785                      PN_stdfloat x, PN_stdfloat y, PN_stdfloat sample_radius) const {
00786   x *= _x_size;
00787   y *= _y_size;
00788   PN_stdfloat xr = sample_radius * _x_size;
00789   PN_stdfloat yr = sample_radius * _y_size;
00790   box_filter_region(result, x - xr, y - yr, x + xr, y + yr);
00791 }
00792 
00793 
00794 ////////////////////////////////////////////////////////////////////
00795 //     Function: PfmFile::generate_vis_points
00796 //       Access: Published
00797 //  Description: Creates a point cloud with the points of the pfm as
00798 //               3-d coordinates in space, and texture coordinates
00799 //               ranging from 0 .. 1 based on the position within the
00800 //               pfm grid.
00801 ////////////////////////////////////////////////////////////////////
00802 NodePath PfmFile::
00803 generate_vis_points() const {
00804   nassertr(is_valid(), NodePath());
00805 
00806   CPT(GeomVertexFormat) format;
00807   if (_vis_inverse) {
00808     if (_vis_2d) {
00809       format = GeomVertexFormat::get_v3t2();
00810     } else {
00811       // We need a 3-d texture coordinate if we're inverting the vis
00812       // and it's 3-d.
00813       GeomVertexArrayFormat *v3t3 = new GeomVertexArrayFormat
00814         (InternalName::get_vertex(), 3, 
00815          Geom::NT_stdfloat, Geom::C_point,
00816          InternalName::get_texcoord(), 3, 
00817          Geom::NT_stdfloat, Geom::C_texcoord);
00818       format = GeomVertexFormat::register_format(v3t3);
00819     }
00820   } else {
00821     format = GeomVertexFormat::get_v3t2();
00822   }
00823 
00824   PT(GeomVertexData) vdata = new GeomVertexData
00825     ("points", format, Geom::UH_static);
00826   vdata->set_num_rows(_x_size * _y_size);
00827   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00828   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00829 
00830   LPoint2 uv_scale(1.0, 1.0);
00831   if (_x_size > 1) {
00832     uv_scale[0] = 1.0f / PN_stdfloat(_x_size - 1);
00833   }
00834   if (_y_size > 1) {
00835     uv_scale[1] = 1.0f / PN_stdfloat(_y_size - 1);
00836   }
00837   
00838   for (int yi = 0; yi < _y_size; ++yi) {
00839     for (int xi = 0; xi < _x_size; ++xi) {
00840       const LPoint3 &point = get_point(xi, yi);
00841       LPoint2 uv(PN_stdfloat(xi) * uv_scale[0],
00842                  PN_stdfloat(yi) * uv_scale[1]);
00843       if (_vis_inverse) {
00844         vertex.add_data2(uv);
00845         texcoord.add_data3(point);
00846       } else if (_vis_2d) {
00847         vertex.add_data2(point[0], point[1]);
00848         texcoord.add_data2(uv);
00849       } else {
00850         vertex.add_data3(point);
00851         texcoord.add_data2(uv);
00852       }
00853     }
00854   }
00855   
00856   PT(Geom) geom = new Geom(vdata);
00857   PT(GeomPoints) points = new GeomPoints(Geom::UH_static);
00858   points->add_next_vertices(_x_size * _y_size);
00859   geom->add_primitive(points);
00860   
00861   PT(GeomNode) gnode = new GeomNode("");
00862   gnode->add_geom(geom);
00863   return NodePath(gnode);
00864 }
00865 
00866 ////////////////////////////////////////////////////////////////////
00867 //     Function: PfmFile::generate_vis_mesh
00868 //       Access: Published
00869 //  Description: Creates a triangle mesh with the points of the pfm as
00870 //               3-d coordinates in space, and texture coordinates
00871 //               ranging from 0 .. 1 based on the position within the
00872 //               pfm grid.
00873 ////////////////////////////////////////////////////////////////////
00874 NodePath PfmFile::
00875 generate_vis_mesh(MeshFace face) const {
00876   nassertr(is_valid(), NodePath());
00877   nassertr(face != 0, NodePath());
00878 
00879   if (_x_size == 1 || _y_size == 1) {
00880     // Can't generate a 1-d mesh, so generate points in this case.
00881     return generate_vis_points();
00882   }
00883   
00884   PT(GeomNode) gnode = new GeomNode("");
00885 
00886   if (face & MF_front) {
00887     make_vis_mesh_geom(gnode, false);
00888   }
00889 
00890   if (face & MF_back) {
00891     make_vis_mesh_geom(gnode, true);
00892   }
00893 
00894   return NodePath(gnode);
00895 }
00896 
00897 ////////////////////////////////////////////////////////////////////
00898 //     Function: PfmFile::make_vis_mesh_geom
00899 //       Access: Private
00900 //  Description: Returns a triangle mesh for the pfm.  If inverted is
00901 //               true, the mesh is facing the opposite direction.
00902 ////////////////////////////////////////////////////////////////////
00903 void PfmFile::
00904 make_vis_mesh_geom(GeomNode *gnode, bool inverted) const {
00905   int num_x_cells = 1;
00906   int num_y_cells = 1;
00907 
00908   int x_size = _x_size;
00909   int y_size = _y_size;
00910 
00911   // This is the number of independent vertices we will require.
00912   int num_vertices = x_size * y_size;
00913   if (num_vertices == 0) {
00914     // Trivial no-op.
00915     return;
00916   }
00917 
00918   bool reverse_normals = inverted;
00919   bool reverse_faces = inverted;
00920   if (!is_right_handed(get_default_coordinate_system())) {
00921     reverse_faces = !reverse_faces;
00922   }
00923 
00924   // This is the max number of vertex indices we might add to the
00925   // GeomTriangles.  (We might actually add fewer than this due to
00926   // omitting the occasional missing data point.)
00927   int max_indices = (x_size - 1) * (y_size - 1) * 6;
00928 
00929   while (num_vertices > pfm_vis_max_vertices || max_indices > pfm_vis_max_indices) {
00930     // Too many vertices in one mesh.  Subdivide the mesh into smaller
00931     // pieces.
00932     if (num_x_cells > num_y_cells) {
00933       ++num_y_cells;
00934     } else {
00935       ++num_x_cells;
00936     }
00937 
00938     x_size = (_x_size + num_x_cells - 1) / num_x_cells + 1;
00939     y_size = (_y_size + num_y_cells - 1) / num_y_cells + 1;
00940 
00941     num_vertices = x_size * y_size;
00942     max_indices = (x_size - 1) * (y_size - 1) * 6;
00943   }
00944 
00945   // OK, now we know how many cells we need.
00946   if (grutil_cat.is_debug()) {
00947     grutil_cat.debug()
00948       << "Generating mesh with " << num_x_cells << " x " << num_y_cells
00949       << " pieces.\n";
00950   }
00951 
00952   PT(GeomVertexArrayFormat) array_format;
00953 
00954   if (_vis_2d) {
00955     // No normals needed if we're just generating a 2-d mesh.
00956     array_format = new GeomVertexArrayFormat
00957       (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
00958        InternalName::get_texcoord(), 2, Geom::NT_stdfloat, Geom::C_texcoord);
00959 
00960   } else {
00961     if (_vis_inverse) {
00962       // We need a 3-d texture coordinate if we're inverting the vis
00963       // and it's 3-d.  But we still don't need normals in that case.
00964       array_format = new GeomVertexArrayFormat
00965         (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
00966          InternalName::get_texcoord(), 3, Geom::NT_stdfloat, Geom::C_texcoord);
00967     } else {
00968       // Otherwise, we only need a 2-d texture coordinate, and we do
00969       // want normals.
00970       array_format = new GeomVertexArrayFormat
00971         (InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point,
00972          InternalName::get_normal(), 3, Geom::NT_stdfloat, Geom::C_vector,
00973          InternalName::get_texcoord(), 2, Geom::NT_stdfloat, Geom::C_texcoord);
00974     }
00975   }
00976 
00977   if (_flat_texcoord_name != (InternalName *)NULL) {
00978     // We need an additional texcoord column for the flat texcoords.
00979     array_format->add_column(_flat_texcoord_name, 2, 
00980                              Geom::NT_stdfloat, Geom::C_texcoord);
00981   }
00982 
00983   CPT(GeomVertexFormat) format = GeomVertexFormat::register_format(array_format);
00984 
00985   for (int yci = 0; yci < num_y_cells; ++yci) {
00986     int y_begin = (yci * _y_size) / num_y_cells;
00987     int y_end = ((yci + 1) * _y_size) / num_y_cells;
00988 
00989     // Include the first vertex from the next strip in this strip's
00990     // vertices, so we are connected.
00991     y_end = min(y_end + 1, _y_size);
00992 
00993     y_size = y_end - y_begin;
00994     if (y_size == 0) {
00995       continue;
00996     }
00997 
00998     for (int xci = 0; xci < num_x_cells; ++xci) {
00999       int x_begin = (xci * _x_size) / num_x_cells;
01000       int x_end = ((xci + 1) * _x_size) / num_x_cells;
01001       x_end = min(x_end + 1, _x_size);
01002       x_size = x_end - x_begin;
01003       if (x_size == 0) {
01004         continue;
01005       }
01006 
01007       num_vertices = x_size * y_size;
01008       max_indices = (x_size - 1) * (y_size - 1) * 6;
01009 
01010       ostringstream mesh_name;
01011       mesh_name << "mesh_" << xci << "_" << yci;
01012       PT(GeomVertexData) vdata = new GeomVertexData
01013         (mesh_name.str(), format, Geom::UH_static);
01014 
01015       vdata->set_num_rows(num_vertices);
01016       GeomVertexWriter vertex(vdata, InternalName::get_vertex());
01017       GeomVertexWriter normal(vdata, InternalName::get_normal());
01018       GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
01019       GeomVertexWriter texcoord2(vdata, _flat_texcoord_name);
01020 
01021       for (int yi = y_begin; yi < y_end; ++yi) {
01022         for (int xi = x_begin; xi < x_end; ++xi) {
01023           const LPoint3 &point = get_point(xi, yi);
01024           LPoint2 uv(PN_stdfloat(xi) / PN_stdfloat(_x_size - 1),
01025                      PN_stdfloat(yi) / PN_stdfloat(_y_size - 1));
01026 
01027           if (_vis_inverse) {
01028             vertex.add_data2(uv);
01029             texcoord.add_data3(point);
01030           } else if (_vis_2d) {
01031             vertex.add_data2(point[0], point[1]);
01032             texcoord.add_data2(uv);
01033           } else {
01034             vertex.add_data3(point);
01035             texcoord.add_data2(uv);
01036             
01037             // Calculate the normal based on two neighboring vertices.
01038             LPoint3 v[3];
01039             v[0] = get_point(xi, yi);
01040             if (xi + 1 < _x_size) {
01041               v[1] = get_point(xi + 1, yi);
01042             } else {
01043               v[1] = v[0];
01044               v[0] = get_point(xi - 1, yi);
01045             }
01046             
01047             if (yi + 1 < _y_size) {
01048               v[2] = get_point(xi, yi + 1);
01049             } else {
01050               v[2] = v[0];
01051               v[0] = get_point(xi, yi - 1);
01052             }
01053         
01054             LVector3 n = LVector3::zero();
01055             for (int i = 0; i < 3; ++i) {
01056               const LPoint3 &v0 = v[i];
01057               const LPoint3 &v1 = v[(i + 1) % 3];
01058               n[0] += v0[1] * v1[2] - v0[2] * v1[1];
01059               n[1] += v0[2] * v1[0] - v0[0] * v1[2];
01060               n[2] += v0[0] * v1[1] - v0[1] * v1[0];
01061             }
01062             n.normalize();
01063             nassertv(!n.is_nan());
01064             if (reverse_normals) {
01065               n = -n;
01066             }
01067             normal.add_data3(n);
01068           }
01069 
01070           if (_flat_texcoord_name != (InternalName *)NULL) {
01071             texcoord2.add_data2(uv);
01072           }
01073         }
01074       }
01075   
01076       PT(Geom) geom = new Geom(vdata);
01077       PT(GeomTriangles) tris = new GeomTriangles(Geom::UH_static);
01078 
01079       tris->reserve_num_vertices(max_indices);
01080 
01081       for (int yi = y_begin; yi < y_end - 1; ++yi) {
01082         for (int xi = x_begin; xi < x_end - 1; ++xi) {
01083 
01084           if (_zero_special) {
01085             if (get_point(xi, yi) == LPoint3::zero() ||
01086                 get_point(xi, yi + 1) == LPoint3::zero() ||
01087                 get_point(xi + 1, yi + 1) == LPoint3::zero() ||
01088                 get_point(xi + 1, yi) == LPoint3::zero()) {
01089               continue;
01090             }
01091           }
01092 
01093           int xi0 = xi - x_begin;
01094           int yi0 = yi - y_begin;
01095 
01096           int vi0 = ((xi0) + (yi0) * x_size);
01097           int vi1 = ((xi0) + (yi0 + 1) * x_size);
01098           int vi2 = ((xi0 + 1) + (yi0 + 1) * x_size);
01099           int vi3 = ((xi0 + 1) + (yi0) * x_size);
01100           
01101           if (reverse_faces) {
01102             tris->add_vertices(vi2, vi0, vi1);
01103             tris->close_primitive();
01104             
01105             tris->add_vertices(vi3, vi0, vi2);
01106             tris->close_primitive();
01107           } else {
01108             tris->add_vertices(vi2, vi1, vi0);
01109             tris->close_primitive();
01110             
01111             tris->add_vertices(vi3, vi2, vi0);
01112             tris->close_primitive();
01113           }
01114         }
01115       }
01116       geom->add_primitive(tris);
01117       gnode->add_geom(geom);
01118     }
01119   }
01120 }
01121 
01122 
01123 ////////////////////////////////////////////////////////////////////
01124 //     Function: PfmFile::box_filter_region
01125 //       Access: Private
01126 //  Description: Averages all the points in the rectangle from x0
01127 //               .. y0 to x1 .. y1 into result.  The region may be
01128 //               defined by floating-point boundaries; the result will
01129 //               be weighted by the degree of coverage of each
01130 //               included point.
01131 ////////////////////////////////////////////////////////////////////
01132 void PfmFile::
01133 box_filter_region(LPoint3 &result,
01134                   PN_stdfloat x0, PN_stdfloat y0, PN_stdfloat x1, PN_stdfloat y1) const {
01135   result = LPoint3::zero();
01136   PN_stdfloat coverage = 0.0;
01137 
01138   if (x1 < x0 || y1 < y0) {
01139     return;
01140   }
01141   nassertv(y0 >= 0.0 && y1 >= 0.0);
01142 
01143   int y = (int)y0;
01144   // Get the first (partial) row
01145   box_filter_line(result, coverage, x0, y, x1, (PN_stdfloat)(y+1)-y0);
01146 
01147   int y_last = (int)y1;
01148   if (y < y_last) {
01149     y++;
01150     while (y < y_last) {
01151       // Get each consecutive (complete) row
01152       box_filter_line(result, coverage, x0, y, x1, 1.0);
01153       y++;
01154     }
01155 
01156     // Get the final (partial) row
01157     PN_stdfloat y_contrib = y1 - (PN_stdfloat)y_last;
01158     if (y_contrib > 0.0001) {
01159       box_filter_line(result, coverage, x0, y, x1, y_contrib);
01160     }
01161   }
01162 
01163   if (coverage != 0.0) {
01164     result /= coverage;
01165   }
01166 }
01167 
01168 ////////////////////////////////////////////////////////////////////
01169 //     Function: PfmFile::box_filter_line
01170 //       Access: Private
01171 //  Description: 
01172 ////////////////////////////////////////////////////////////////////
01173 void PfmFile::
01174 box_filter_line(LPoint3 &result, PN_stdfloat &coverage,
01175                 PN_stdfloat x0, int y, PN_stdfloat x1, PN_stdfloat y_contrib) const {
01176   int x = (int)x0;
01177   // Get the first (partial) xel
01178   box_filter_point(result, coverage, x, y, (PN_stdfloat)(x+1)-x0, y_contrib);
01179 
01180   int x_last = (int)x1;
01181   if (x < x_last) {
01182     x++;
01183     while (x < x_last) {
01184       // Get each consecutive (complete) xel
01185       box_filter_point(result, coverage, x, y, 1.0, y_contrib);
01186       x++;
01187     }
01188 
01189     // Get the final (partial) xel
01190     PN_stdfloat x_contrib = x1 - (PN_stdfloat)x_last;
01191     if (x_contrib > 0.0001) {
01192       box_filter_point(result, coverage, x, y, x_contrib, y_contrib);
01193     }
01194   }
01195 }
01196 
01197 ////////////////////////////////////////////////////////////////////
01198 //     Function: PfmFile::box_filter_point
01199 //       Access: Private
01200 //  Description: 
01201 ////////////////////////////////////////////////////////////////////
01202 void PfmFile::
01203 box_filter_point(LPoint3 &result, PN_stdfloat &coverage,
01204                  int x, int y, PN_stdfloat x_contrib, PN_stdfloat y_contrib) const {
01205   const LPoint3 &point = get_point(x, y);
01206   if (_zero_special && point == LPoint3::zero()) {
01207     return;
01208   }
01209 
01210   PN_stdfloat contrib = x_contrib * y_contrib;
01211   result += point * contrib;
01212   coverage += contrib;
01213 }
01214 
01215 ////////////////////////////////////////////////////////////////////
01216 //     Function: PfmFile::fill_mini_grid
01217 //       Access: Private
01218 //  Description: A support function for calc_average_point(), this
01219 //               recursively fills in the holes in the mini_grid data
01220 //               with the index to the nearest value.
01221 ////////////////////////////////////////////////////////////////////
01222 void PfmFile::
01223 fill_mini_grid(MiniGridCell *mini_grid, int x_size, int y_size, 
01224                int xi, int yi, int dist, int ti) const {
01225   if (xi < 0 || xi >= x_size || yi < 0 || yi >= y_size) {
01226     // Out of bounds.
01227     return;
01228   }
01229 
01230   int gi = yi * x_size + xi;
01231   if (mini_grid[gi]._dist == -1 || mini_grid[gi]._dist > dist) {
01232     // Here's an undefined value that we need to populate.
01233     mini_grid[gi]._dist = dist;
01234     mini_grid[gi]._ti = ti;
01235     fill_mini_grid(mini_grid, x_size, y_size, xi + 1, yi, dist + 1, ti);
01236     fill_mini_grid(mini_grid, x_size, y_size, xi - 1, yi, dist + 1, ti);
01237     fill_mini_grid(mini_grid, x_size, y_size, xi, yi + 1, dist + 1, ti);
01238     fill_mini_grid(mini_grid, x_size, y_size, xi, yi - 1, dist + 1, ti);
01239   }
01240 }
 All Classes Functions Variables Enumerations