diff --git a/panda/src/grutil/pfmVizzer.cxx b/panda/src/grutil/pfmVizzer.cxx index a9193614e6..c8d1ab8699 100644 --- a/panda/src/grutil/pfmVizzer.cxx +++ b/panda/src/grutil/pfmVizzer.cxx @@ -105,10 +105,10 @@ extrude(const Lens *lens) { // Create an implicit UV coordinate for each point. LPoint2 uv_scale(1.0, 1.0); if (_pfm.get_x_size() > 1) { - uv_scale[0] = 1.0 / PN_stdfloat(_pfm.get_x_size() - 1); + uv_scale[0] = 1.0 / PN_stdfloat(_pfm.get_x_size()); } if (_pfm.get_y_size() > 1) { - uv_scale[1] = 1.0 / PN_stdfloat(_pfm.get_y_size() - 1); + uv_scale[1] = 1.0 / PN_stdfloat(_pfm.get_y_size()); } for (int yi = 0; yi < _pfm.get_y_size(); ++yi) { for (int xi = 0; xi < _pfm.get_x_size(); ++xi) { @@ -116,8 +116,8 @@ extrude(const Lens *lens) { continue; } LPoint3 p; - p.set((PN_stdfloat)xi * uv_scale[0], - (PN_stdfloat)yi * uv_scale[1], + p.set(((PN_stdfloat)xi + 0.5) * uv_scale[0], + ((PN_stdfloat)yi + 0.5) * uv_scale[1], (PN_stdfloat)_pfm.get_point1(xi, yi)); from_uv.xform_point_in_place(p); @@ -221,10 +221,10 @@ generate_vis_points() const { LPoint2f uv_scale(1.0, 1.0); if (_pfm.get_x_size() > 1) { - uv_scale[0] = 1.0f / PN_float32(_pfm.get_x_size() - 1); + uv_scale[0] = 1.0f / PN_float32(_pfm.get_x_size()); } if (_pfm.get_y_size() > 1) { - uv_scale[1] = 1.0f / PN_float32(_pfm.get_y_size() - 1); + uv_scale[1] = 1.0f / PN_float32(_pfm.get_y_size()); } int num_points = 0; @@ -235,8 +235,8 @@ generate_vis_points() const { } const LPoint3f &point = _pfm.get_point(xi, yi); - LPoint2f uv(PN_float32(xi) * uv_scale[0], - PN_float32(yi) * uv_scale[1]); + LPoint2f uv((PN_float32(xi) + 0.5) * uv_scale[0], + (PN_float32(yi) + 0.5) * uv_scale[1]); if (_vis_inverse) { vertex.add_data2f(uv); texcoord.add_data3f(point); @@ -306,7 +306,7 @@ generate_vis_mesh(MeshFace face) const { //////////////////////////////////////////////////////////////////// // Function: PfmVizzer::calc_max_u_displacement -// Access: Private +// Access: Published // Description: Computes the maximum amount of shift, in pixels // either left or right, of any pixel in the distortion // map. This can be passed to make_displacement(); see @@ -326,7 +326,7 @@ calc_max_u_displacement() const { } const LPoint3f &point = _pfm.get_point(xi, yi); - double nxi = point[0] * (double)(x_size - 1); + double nxi = point[0] * (double)x_size - 0.5; max_u = max(max_u, cabs(nxi - (double)xi)); } @@ -337,7 +337,7 @@ calc_max_u_displacement() const { //////////////////////////////////////////////////////////////////// // Function: PfmVizzer::calc_max_v_displacement -// Access: Private +// Access: Published // Description: Computes the maximum amount of shift, in pixels // either up or down, of any pixel in the distortion // map. This can be passed to make_displacement(); see @@ -357,7 +357,7 @@ calc_max_v_displacement() const { } const LPoint3f &point = _pfm.get_point(xi, yi); - double nyi = point[1] * (double)(y_size - 1); + double nyi = point[1] * (double)y_size - 0.5; max_v = max(max_v, cabs(nyi - (double)yi)); } @@ -368,7 +368,7 @@ calc_max_v_displacement() const { //////////////////////////////////////////////////////////////////// // Function: PfmVizzer::make_displacement -// Access: Private +// Access: Published // Description: Assuming the underlying PfmFile is a 2-d distortion // mesh, with the U and V in the first two components // and the third component unused, this computes an @@ -389,7 +389,17 @@ make_displacement(PNMImage &result, double max_u, double max_v) const { int x_size = _pfm.get_x_size(); int y_size = _pfm.get_y_size(); result.clear(x_size, y_size, 3, PNM_MAXMAXVAL); - result.fill(0.5); + result.fill_val(0, 0, PNM_MAXMAXVAL); + + // After Effects defines this as the zero (no-change) value. It's + // not exactly 0.5, because they round up. + static const int midval = (PNM_MAXMAXVAL + 1) / 2; + + // After Effects seems to always undershift by exactly this amount. + static const double scale_factor = 256.0 / 255.0; + + double u_scale = scale_factor * 0.5 * (PNM_MAXMAXVAL - 1) / max_u; + double v_scale = scale_factor * 0.5 * (PNM_MAXMAXVAL - 1) / max_v; for (int yi = 0; yi < y_size; ++yi) { for (int xi = 0; xi < x_size; ++xi) { @@ -398,16 +408,86 @@ make_displacement(PNMImage &result, double max_u, double max_v) const { } const LPoint3f &point = _pfm.get_point(xi, yi); - double nxi = point[0] * (double)(x_size - 1); - double nyi = point[1] * (double)(y_size - 1); + double nxi = point[0] * (double)x_size - 0.5; + double nyi = point[1] * (double)y_size - 0.5; - double u_shift = (nxi - (double)xi) / max_u; - double v_shift = (nyi - (double)yi) / max_v; + double x_shift = (nxi - (double)xi); + double y_shift = (nyi - (double)yi); - result.set_red(xi, yi, u_shift * 0.5 + 0.5); - result.set_green(xi, yi, v_shift * 0.5 + 0.5); + int u_val = midval + (int)cfloor(x_shift * u_scale + 0.5); + int v_val = midval + (int)cfloor(y_shift * v_scale + 0.5); + + // We use the blue channel to mark holes, so we can fill them in + // later. + result.set_xel_val(xi, yi, u_val, v_val, 0); } } + + // Now fill in holes. + for (int yi = 0; yi < y_size; ++yi) { + for (int xi = 0; xi < x_size; ++xi) { + if (!_pfm.has_point(xi, yi)) { + continue; + } + + const LPoint3f &point = _pfm.get_point(xi, yi); + double nxi = point[0] * (double)x_size - 0.5; + double nyi = point[1] * (double)y_size - 0.5; + + r_fill_displacement(result, xi - 1, yi, nxi, nyi, u_scale, v_scale, 1); + r_fill_displacement(result, xi + 1, yi, nxi, nyi, u_scale, v_scale, 1); + r_fill_displacement(result, xi, yi - 1, nxi, nyi, u_scale, v_scale, 1); + r_fill_displacement(result, xi, yi + 1, nxi, nyi, u_scale, v_scale, 1); + } + } + + // Finally, reset the blue channel for cleanliness. + for (int yi = 0; yi < y_size; ++yi) { + for (int xi = 0; xi < x_size; ++xi) { + result.set_blue_val(xi, yi, midval); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmVizzer::r_fill_displacement +// Access: Private +// Description: Recursively fills in holes with the color of their +// nearest neighbor after processing the image. This +// avoids sudden discontinuities in the displacement map +// at the edge of the screen geometry. +//////////////////////////////////////////////////////////////////// +void PfmVizzer:: +r_fill_displacement(PNMImage &result, int xi, int yi, + double nxi, double nyi, double u_scale, double v_scale, + int distance) const { + if (xi < 0 || yi < 0 || + xi >= result.get_x_size() || yi >= result.get_y_size()) { + // Stop at the edge. + return; + } + + if (distance > 1000) { + // Avoid runaway recursion. + return; + } + + int val = result.get_blue_val(xi, yi); + if (val > distance) { + // We've found a point that's closer. + static const int midval = (PNM_MAXMAXVAL + 1) / 2; + + double x_shift = (nxi - (double)xi); + double y_shift = (nyi - (double)yi); + int u_val = midval + (int)cfloor(x_shift * u_scale + 0.5); + int v_val = midval + (int)cfloor(y_shift * v_scale + 0.5); + result.set_xel_val(xi, yi, u_val, v_val, distance); + + r_fill_displacement(result, xi - 1, yi, nxi, nyi, u_scale, v_scale, distance + 1); + r_fill_displacement(result, xi + 1, yi, nxi, nyi, u_scale, v_scale, distance + 1); + r_fill_displacement(result, xi, yi - 1, nxi, nyi, u_scale, v_scale, distance + 1); + r_fill_displacement(result, xi, yi + 1, nxi, nyi, u_scale, v_scale, distance + 1); + } } //////////////////////////////////////////////////////////////////// @@ -710,8 +790,8 @@ add_data(const PfmVizzer &vizzer, GeomVertexWriter &vwriter, int xi, int yi, boo switch (_source) { case CT_texcoord2: { - LPoint2f uv(PN_float32(xi) / PN_float32(pfm.get_x_size() - 1), - PN_float32(yi) / PN_float32(pfm.get_y_size() - 1)); + LPoint2f uv((PN_float32(xi) + 0.5) / PN_float32(pfm.get_x_size()), + (PN_float32(yi) + 0.5) / PN_float32(pfm.get_y_size())); transform_point(uv); vwriter.set_data2f(uv); } @@ -719,8 +799,8 @@ add_data(const PfmVizzer &vizzer, GeomVertexWriter &vwriter, int xi, int yi, boo case CT_texcoord3: { - LPoint3f uv(PN_float32(xi) / PN_float32(pfm.get_x_size() - 1), - PN_float32(yi) / PN_float32(pfm.get_y_size() - 1), + LPoint3f uv((PN_float32(xi) + 0.5) / PN_float32(pfm.get_x_size()), + (PN_float32(yi) + 0.5) / PN_float32(pfm.get_y_size()), 0.0f); transform_point(uv); vwriter.set_data3f(uv); diff --git a/panda/src/grutil/pfmVizzer.h b/panda/src/grutil/pfmVizzer.h index e33af79c5b..6156b4a5d9 100644 --- a/panda/src/grutil/pfmVizzer.h +++ b/panda/src/grutil/pfmVizzer.h @@ -79,6 +79,10 @@ PUBLISHED: BLOCKING void make_displacement(PNMImage &result, double max_u, double max_v) const; private: + void r_fill_displacement(PNMImage &result, int xi, int yi, + double nxi, double nyi, double u_scale, double v_scale, + int distance) const; + void make_vis_mesh_geom(GeomNode *gnode, bool inverted) const; diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index b88d707b58..7db4eda518 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/panda/src/pnmimage/pfmFile.cxx @@ -748,10 +748,10 @@ resize(int new_x_size, int new_y_size) { PN_float32 y_scale = 1.0; if (new_x_size > 1) { - x_scale = (PN_float32)(_x_size - 1) / (PN_float32)(new_x_size - 1); + x_scale = (PN_float32)_x_size / (PN_float32)new_x_size; } if (new_y_size > 1) { - y_scale = (PN_float32)(_y_size - 1) / (PN_float32)(new_y_size - 1); + y_scale = (PN_float32)_y_size / (PN_float32)new_y_size; } switch (_num_channels) { @@ -759,12 +759,12 @@ resize(int new_x_size, int new_y_size) { { from_y0 = 0.0; for (int to_y = 0; to_y < new_y_size; ++to_y) { - from_y1 = (to_y + 0.5) * y_scale; + from_y1 = (to_y + 1.0) * y_scale; from_y1 = min(from_y1, (PN_float32) _y_size); from_x0 = 0.0; for (int to_x = 0; to_x < new_x_size; ++to_x) { - from_x1 = (to_x + 0.5) * x_scale; + from_x1 = (to_x + 1.0) * x_scale; from_x1 = min(from_x1, (PN_float32) _x_size); // Now the box from (from_x0, from_y0) - (from_x1, from_y1) @@ -784,12 +784,12 @@ resize(int new_x_size, int new_y_size) { { from_y0 = 0.0; for (int to_y = 0; to_y < new_y_size; ++to_y) { - from_y1 = (to_y + 0.5) * y_scale; + from_y1 = (to_y + 1.0) * y_scale; from_y1 = min(from_y1, (PN_float32) _y_size); from_x0 = 0.0; for (int to_x = 0; to_x < new_x_size; ++to_x) { - from_x1 = (to_x + 0.5) * x_scale; + from_x1 = (to_x + 1.0) * x_scale; from_x1 = min(from_x1, (PN_float32) _x_size); // Now the box from (from_x0, from_y0) - (from_x1, from_y1) @@ -811,12 +811,12 @@ resize(int new_x_size, int new_y_size) { { from_y0 = 0.0; for (int to_y = 0; to_y < new_y_size; ++to_y) { - from_y1 = (to_y + 0.5) * y_scale; + from_y1 = (to_y + 1.0) * y_scale; from_y1 = min(from_y1, (PN_float32) _y_size); from_x0 = 0.0; for (int to_x = 0; to_x < new_x_size; ++to_x) { - from_x1 = (to_x + 0.5) * x_scale; + from_x1 = (to_x + 1.0) * x_scale; from_x1 = min(from_x1, (PN_float32) _x_size); // Now the box from (from_x0, from_y0) - (from_x1, from_y1) @@ -956,7 +956,7 @@ xform(const LMatrix4f &transform) { //////////////////////////////////////////////////////////////////// // Function: PfmFile::forward_distort // Access: Published -// Description: Applys the distortion indicated in the supplied dist +// Description: Applies the distortion indicated in the supplied dist // map to the current map. The dist map is understood // to be a mapping of points in the range 0..1 in the // first two dimensions. Each (u,v) point in the @@ -994,8 +994,8 @@ forward_distort(const PfmFile &dist) { continue; } LPoint2f uv = dist_p->get_point2(xi, yi); - int dist_xi = int(uv[0] * (_x_size - 1)); - int dist_yi = int(uv[1] * (_y_size - 1)); + int dist_xi = (int)cfloor(uv[0] * (double)_x_size); + int dist_yi = (int)cfloor(uv[0] * (double)_y_size); if (dist_xi < 0 || dist_xi >= _x_size || dist_yi < 0 || dist_yi >= _y_size) { continue; @@ -1015,7 +1015,7 @@ forward_distort(const PfmFile &dist) { //////////////////////////////////////////////////////////////////// // Function: PfmFile::reverse_distort // Access: Published -// Description: Applys the distortion indicated in the supplied dist +// Description: Applies the distortion indicated in the supplied dist // map to the current map. The dist map is understood // to be a mapping of points in the range 0..1 in the // first two dimensions. Each (x,y) point in the @@ -1055,8 +1055,8 @@ reverse_distort(const PfmFile &dist) { continue; } LPoint2f uv = dist_p->get_point2(xi, yi); - int dist_xi = int(uv[0] * (_x_size - 1)); - int dist_yi = int(uv[1] * (_y_size - 1)); + int dist_xi = (int)cfloor(uv[0] * (double)_x_size); + int dist_yi = (int)cfloor(uv[0] * (double)_y_size); if (dist_xi < 0 || dist_xi >= _x_size || dist_yi < 0 || dist_yi >= _y_size) { continue; @@ -1163,17 +1163,18 @@ clear_to_texcoords(int x_size, int y_size) { clear(x_size, y_size, 3); LPoint2f uv_scale(1.0, 1.0); - if (_x_size > 1) { - uv_scale[0] = 1.0f / PN_float32(_x_size - 1); + if (_x_size > 0) { + uv_scale[0] = 1.0f / PN_float32(_x_size); } - if (_y_size > 1) { - uv_scale[1] = 1.0f / PN_float32(_y_size - 1); + if (_y_size > 0) { + uv_scale[1] = 1.0f / PN_float32(_y_size); } for (int yi = 0; yi < _y_size; ++yi) { for (int xi = 0; xi < _x_size; ++xi) { - LPoint3f uv(PN_float32(xi) * uv_scale[0], - PN_float32(yi) * uv_scale[1], 0.0f); + LPoint3f uv(PN_float32(xi) * uv_scale[0] + 0.5, + PN_float32(yi) * uv_scale[1] + 0.5, + 0.0f); set_point(xi, yi, uv); } }