From ab0645ab7922cfb81db8faf6088a5b0ac93ca79f Mon Sep 17 00:00:00 2001 From: David Rose Date: Thu, 23 Dec 2010 21:05:32 +0000 Subject: [PATCH] pfmprogs --- pandatool/src/pfmprogs/Sources.pp | 21 + pandatool/src/pfmprogs/config_pfm.cxx | 58 +++ pandatool/src/pfmprogs/config_pfm.h | 32 ++ pandatool/src/pfmprogs/pfmFile.I | 125 ++++++ pandatool/src/pfmprogs/pfmFile.cxx | 592 ++++++++++++++++++++++++++ pandatool/src/pfmprogs/pfmFile.h | 87 ++++ pandatool/src/pfmprogs/pfmTrans.cxx | 444 +++++++++++++++++++ pandatool/src/pfmprogs/pfmTrans.h | 75 ++++ 8 files changed, 1434 insertions(+) create mode 100755 pandatool/src/pfmprogs/Sources.pp create mode 100755 pandatool/src/pfmprogs/config_pfm.cxx create mode 100755 pandatool/src/pfmprogs/config_pfm.h create mode 100755 pandatool/src/pfmprogs/pfmFile.I create mode 100755 pandatool/src/pfmprogs/pfmFile.cxx create mode 100755 pandatool/src/pfmprogs/pfmFile.h create mode 100755 pandatool/src/pfmprogs/pfmTrans.cxx create mode 100755 pandatool/src/pfmprogs/pfmTrans.h diff --git a/pandatool/src/pfmprogs/Sources.pp b/pandatool/src/pfmprogs/Sources.pp new file mode 100755 index 0000000000..14045127c5 --- /dev/null +++ b/pandatool/src/pfmprogs/Sources.pp @@ -0,0 +1,21 @@ +#define OTHER_LIBS \ + egg:c pandaegg:m \ + pipeline:c event:c pstatclient:c panda:m \ + pandabase:c pnmimage:c mathutil:c linmath:c putil:c express:c \ + pandaexpress:m \ + interrogatedb:c prc:c dconfig:c dtoolconfig:m \ + dtoolutil:c dtoolbase:c dtool:m \ + $[if $[WANT_NATIVE_NET],nativenet:c] \ + $[if $[and $[HAVE_NET],$[WANT_NATIVE_NET]],net:c downloader:c] \ + pystub + +#begin bin_target + #define TARGET pfm-trans + #define LOCAL_LIBS progbase + + #define SOURCES \ + config_pfm.cxx config_pfm.h \ + pfmFile.cxx pfmFile.h \ + pfmTrans.cxx pfmTrans.h + +#end bin_target diff --git a/pandatool/src/pfmprogs/config_pfm.cxx b/pandatool/src/pfmprogs/config_pfm.cxx new file mode 100755 index 0000000000..460fff0751 --- /dev/null +++ b/pandatool/src/pfmprogs/config_pfm.cxx @@ -0,0 +1,58 @@ +// Filename: config_pfm.cxx +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "config_pfm.h" + +#include "dconfig.h" + +Configure(config_pfm); +NotifyCategoryDef(pfm, ""); + +ConfigVariableBool pfm_force_littleendian +("pfm-force-littleendian", false, + PRC_DESC("This forces a pfm file to be read as a sequence of little-endian " + "floats, even if its scale factor is given as a positive number.")); + +ConfigVariableBool pfm_reverse_dimensions +("pfm-reverse-dimensions", false, + PRC_DESC("Understands that the width and height of a pfm file are given " + "backwards, in the form height width instead of width height, " + "on input. Does not affect output, which is always written width height.")); + +ConfigVariableDouble pfm_bba_dist +("pfm-bba-dist", "0.2 0.05", + PRC_DESC("Specifies the point_dist and sample_radius, in UV space, for " + "compute bba files with pfm_trans.")); + +ConfigureFn(config_pfm) { + init_libpfm(); +} + +//////////////////////////////////////////////////////////////////// +// Function: init_libpfm +// Description: Initializes the library. This must be called at +// least once before any of the functions or classes in +// this library can be used. Normally it will be +// called by the static initializers and need not be +// called explicitly, but special cases exist. +//////////////////////////////////////////////////////////////////// +void +init_libpfm() { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; +} + diff --git a/pandatool/src/pfmprogs/config_pfm.h b/pandatool/src/pfmprogs/config_pfm.h new file mode 100755 index 0000000000..55d5d07b38 --- /dev/null +++ b/pandatool/src/pfmprogs/config_pfm.h @@ -0,0 +1,32 @@ +// Filename: config_pfm.h +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef CONFIG_PFM_H +#define CONFIG_PFM_H + +#include "pandatoolbase.h" + +#include "notifyCategoryProxy.h" +#include "configVariableBool.h" +#include "configVariableDouble.h" + +NotifyCategoryDeclNoExport(pfm); + +extern ConfigVariableBool pfm_force_littleendian; +extern ConfigVariableBool pfm_reverse_dimensions; +extern ConfigVariableDouble pfm_bba_dist; + +extern void init_libpfm(); + +#endif diff --git a/pandatool/src/pfmprogs/pfmFile.I b/pandatool/src/pfmprogs/pfmFile.I new file mode 100755 index 0000000000..f6bde5f408 --- /dev/null +++ b/pandatool/src/pfmprogs/pfmFile.I @@ -0,0 +1,125 @@ +// Filename: pfmFile.I +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::is_valid +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE bool PfmFile:: +is_valid() const { + return _num_channels != 0 && (_x_size * _y_size == (int)_table.size()); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_x_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PfmFile:: +get_x_size() const { + return _x_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_y_size +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE int PfmFile:: +get_y_size() const { + return _y_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_scale +// Access: Public +// Description: The "scale" is reported in the pfm header and is +// probably meaningless. +//////////////////////////////////////////////////////////////////// +INLINE float PfmFile:: +get_scale() const { + return _scale; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_num_channels +// Access: Public +// Description: A pfm file can be either 1-channel +// (get_num_channels() == 1) or 3-channel +// (get_num_channels() == 3). In the case of a +// 1-channel file, the values can be found in the x +// component of each point, and the y and z components +// are unused. +//////////////////////////////////////////////////////////////////// +INLINE int PfmFile:: +get_num_channels() const { + return _num_channels; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_point +// Access: Public +// Description: Returns the 3-component point value at the indicated +// point. In a 1-channel image, the channel value is in +// the x component. +//////////////////////////////////////////////////////////////////// +INLINE const LPoint3f &PfmFile:: +get_point(int x, int y) const { + nassertr(x >= 0 && x < _x_size, LPoint3f::zero()); + nassertr(y >= 0 && y < _y_size, LPoint3f::zero()); + return _table[y * _x_size + x]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::modify_point +// Access: Public +// Description: Returns a modifiable 3-component point value at the +// indicated point. +//////////////////////////////////////////////////////////////////// +INLINE LPoint3f &PfmFile:: +modify_point(int x, int y) { +#ifndef NDEBUG + static LPoint3f dummy_value = LPoint3f::zero(); + nassertr(x >= 0 && x < _x_size, dummy_value); + nassertr(y >= 0 && y < _y_size, dummy_value); +#endif + + return _table[y * _x_size + x]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::set_zero_special +// Access: Public +// Description: Sets the zero_special flag. When this flag is true, +// values of (0, 0, 0) in the pfm file are treated as a +// special case, and are not processed. +//////////////////////////////////////////////////////////////////// +INLINE void PfmFile:: +set_zero_special(bool zero_special) { + _zero_special = zero_special; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_zero_special +// Access: Public +// Description: Returns the zero_special flag. When this flag is true, +// values of (0, 0, 0) in the pfm file are treated as a +// special case, and are not processed. +//////////////////////////////////////////////////////////////////// +INLINE bool PfmFile:: +get_zero_special() const { + return _zero_special; +} diff --git a/pandatool/src/pfmprogs/pfmFile.cxx b/pandatool/src/pfmprogs/pfmFile.cxx new file mode 100755 index 0000000000..454d8c6165 --- /dev/null +++ b/pandatool/src/pfmprogs/pfmFile.cxx @@ -0,0 +1,592 @@ +// Filename: pfmFile.cxx +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "config_pfm.h" +#include "pfmFile.h" +#include "virtualFileSystem.h" +#include "pandaFileStream.h" +#include "littleEndian.h" +#include "bigEndian.h" +#include "cmath.h" +#include "geomNode.h" +#include "geom.h" +#include "geomVertexData.h" +#include "geomVertexFormat.h" +#include "geomPoints.h" +#include "geomVertexWriter.h" +#include "look_at.h" + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PfmFile:: +PfmFile() { + _zero_special = false; + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PfmFile:: +PfmFile(const PfmFile ©) : + _table(copy._table), + _x_size(copy._x_size), + _y_size(copy._y_size), + _scale(copy._scale), + _num_channels(copy._num_channels), + _zero_special(copy._zero_special) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::Copy Assignment +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +operator = (const PfmFile ©) { + _table = copy._table; + _x_size = copy._x_size; + _y_size = copy._y_size; + _scale = copy._scale; + _num_channels = copy._num_channels; + _zero_special = copy._zero_special; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::clear +// Access: Public +// Description: Eliminates all data in the file. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +clear() { + _x_size = 0; + _y_size = 0; + _num_channels = 0; + _table.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::read +// Access: Public +// Description: Reads the PFM data from the indicated file, returning +// true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool PfmFile:: +read(const Filename &fullpath) { + VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); + + Filename filename = Filename::binary_filename(fullpath); + PT(VirtualFile) file = vfs->get_file(filename); + if (file == (VirtualFile *)NULL) { + // No such file. + pfm_cat.error() + << "Could not find " << fullpath << "\n"; + return false; + } + + if (pfm_cat.is_debug()) { + pfm_cat.debug() + << "Reading PFM file " << filename << "\n"; + } + + istream *in = file->open_read_file(true); + bool success = read(*in); + vfs->close_read_file(in); + + return success; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::read +// Access: Public +// Description: Reads the PFM data from the indicated stream, +// returning true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool PfmFile:: +read(istream &in) { + clear(); + + string identifier; + in >> identifier; + + if (identifier == "PF") { + _num_channels = 3; + } else if (identifier == "Pf") { + _num_channels = 1; + } else { + pfm_cat.error() + << "Not a pfm file.\n"; + return false; + } + + int width, height; + float scale; + in >> width >> height >> scale; + if (!in) { + pfm_cat.error() + << "Error parsing pfm header.\n"; + return false; + } + + // Skip the last newline/whitespace character before the raw data + // begins. + in.get(); + + bool little_endian = false; + if (scale < 0) { + scale = -scale; + little_endian = true; + } + if (pfm_force_littleendian) { + little_endian = true; + } + if (pfm_reverse_dimensions) { + int t = width; + width = height; + height = t; + } + + _x_size = width; + _y_size = height; + _scale = scale; + + // So far, so good. Now read the data. + int size = _x_size * _y_size; + _table.reserve(size); + + if (little_endian) { + for (int i = 0; i < size; ++i) { + LPoint3f point = LPoint3f::zero(); + for (int ci = 0; ci < _num_channels; ++ci) { + float data; + in.read((char *)&data, sizeof(data)); + LittleEndian value(&data, sizeof(data)); + value.store_value(&(point[ci]), sizeof(point[ci])); + } + _table.push_back(point); + } + } else { + for (int i = 0; i < size; ++i) { + LPoint3f point = LPoint3f::zero(); + for (int ci = 0; ci < _num_channels; ++ci) { + float data; + in.read((char *)&data, sizeof(data)); + BigEndian value(&data, sizeof(data)); + value.store_value(&(point[ci]), sizeof(point[ci])); + } + _table.push_back(point); + } + } + + if (in.fail() && !in.eof()) { + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::write +// Access: Public +// Description: Writes the PFM data to the indicated file, returning +// true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool PfmFile:: +write(const Filename &fullpath) { + Filename filename = Filename::binary_filename(fullpath); + pofstream out; + if (!filename.open_write(out)) { + pfm_cat.error() + << "Unable to open " << filename << "\n"; + return false; + } + + if (pfm_cat.is_debug()) { + pfm_cat.debug() + << "Writing PFM file " << filename << "\n"; + } + + return write(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::write +// Access: Public +// Description: Writes the PFM data to the indicated stream, +// returning true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool PfmFile:: +write(ostream &out) { + nassertr(is_valid(), false); + + if (_num_channels == 1) { + out << "Pf\n"; + } else { + out << "PF\n"; + } + out << _x_size << " " << _y_size << "\n"; + + float scale = cabs(_scale); + if (scale == 0.0f) { + scale = 1.0f; + } +#ifndef WORDS_BIGENDIAN + // Little-endian must write negative values for scale. + scale = -scale; +#endif + out << scale << "\n"; + + int size = _x_size * _y_size; + for (int i = 0; i < size; ++i) { + const LPoint3f &point = _table[i]; + for (int ci = 0; ci < _num_channels; ++ci) { + float data = point[ci]; + out.write((const char *)&data, sizeof(data)); + } + } + + if (out.fail()) { + return false; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::resize +// Access: Public +// Description: Applies a simple filter to resample the pfm file +// in-place to the indicated size. Don't confuse this +// with applying a scale to all of the points via +// xform(). +//////////////////////////////////////////////////////////////////// +void PfmFile:: +resize(int new_x_size, int new_y_size) { + Table new_data; + new_data.reserve(new_x_size * new_y_size); + + double from_x0, from_x1, from_y0, from_y1; + + double x_scale = (double)_x_size / (double)new_x_size; + double y_scale = (double)_y_size / (double)new_y_size; + + from_y0 = 0; + for (int to_y = 0; to_y < new_y_size; ++to_y) { + from_y1 = (to_y+1) * y_scale; + + from_x0 = 0; + for (int to_x = 0; to_x < new_x_size; ++to_x) { + from_x1 = (to_x+1) * x_scale; + + // Now the box from (from_x0, from_y0) - (from_x1, from_y1) + // but not including (from_x1, from_y1) maps to the pixel (to_x, to_y). + LPoint3f result; + box_filter_region(result, from_x0, from_y0, from_x1, from_y1); + new_data.push_back(result); + + from_x0 = from_x1; + } + from_y0 = from_y1; + } + + _table.swap(new_data); + _x_size = new_x_size; + _y_size = new_y_size; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::reverse_rows +// Access: Public +// Description: Performs an in-place reversal of the row (y) data. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +reverse_rows() { + nassertv(is_valid()); + + Table reversed; + reversed.reserve(_table.size()); + for (int yi = 0; yi < _y_size; ++yi) { + int source_yi = _y_size - 1 - yi; + int start = source_yi * _x_size; + reversed.insert(reversed.end(), + _table.begin() + start, _table.begin() + start + _x_size); + } + + nassertv(reversed.size() == _table.size()); + _table.swap(reversed); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::xform +// Access: Public +// Description: Applies the indicated transform matrix to all points +// in-place. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +xform(const LMatrix4f &transform) { + nassertv(is_valid()); + + Table::iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + if (_zero_special && (*ti) == LPoint3f::zero()) { + continue; + } + + (*ti) = (*ti) * transform; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::compute_planar_bounds +// Access: Public +// Description: Computes the minmax bounding volume of the points in +// 3-D space, assuming the points represent a +// mostly-planar surface. +// +// This algorithm works by sampling the (square) +// sample_radius pixels at three of the four point_dist +// corners around the center (cx - pd, cx + pd) and so +// on, to determine the plane of the surface. Then all +// of the points are projected into that plane and the +// bounding volume within that plane is determined. +// +// point_dist and sample_radius are in UV space, i.e. in +// the range 0..1. +//////////////////////////////////////////////////////////////////// +PT(BoundingHexahedron) PfmFile:: +compute_planar_bounds(double point_dist, double sample_radius) const { + LPoint3f p0, p1, p2; + compute_sample_point(p0, 0.5 + point_dist, 0.5 - point_dist, sample_radius); + compute_sample_point(p1, 0.5 + point_dist, 0.5 + point_dist, sample_radius); + compute_sample_point(p2, 0.5 - point_dist, 0.5 + point_dist, sample_radius); + + LPoint3f normal; + + normal[0] = p0[1] * p1[2] - p0[2] * p1[1]; + normal[1] = p0[2] * p1[0] - p0[0] * p1[2]; + normal[2] = p0[0] * p1[1] - p0[1] * p1[0]; + + normal[0] += p1[1] * p2[2] - p1[2] * p2[1]; + normal[1] += p1[2] * p2[0] - p1[0] * p2[2]; + normal[2] += p1[0] * p2[1] - p1[1] * p2[0]; + + normal[0] += p2[1] * p0[2] - p2[2] * p0[1]; + normal[1] += p2[2] * p0[0] - p2[0] * p0[2]; + normal[2] += p2[0] * p0[1] - p2[1] * p0[0]; + + normal.normalize(); + + cerr << "p0 = " << p0 << "\np1 = " << p1 << "\np2 = " << p2 + << "\nnormal = " << normal << "\n"; + + // Compute the transform necessary to rotate all of the points into + // the Y = 0 plane. + LMatrix4f rotate; + look_at(rotate, normal, p1 - p0); + + LMatrix4f rinv; + rinv.invert_from(rotate); + + LPoint3f trans = p0 * rinv; + rinv.set_row(3, -trans); + rotate.invert_from(rinv); + + // Now determine the minmax in the XZ plane. + float min_x, min_z, max_x, max_z; + bool got_point = false; + Table::const_iterator ti; + for (ti = _table.begin(); ti != _table.end(); ++ti) { + if (_zero_special && (*ti) == LPoint3f::zero()) { + continue; + } + LPoint3f point = (*ti) * rinv; + if (!got_point) { + min_x = point[0]; + min_z = point[2]; + max_x = point[0]; + max_z = point[2]; + got_point = true; + } else { + min_x = min(min_x, point[0]); + min_z = min(min_z, point[2]); + max_x = max(max_x, point[0]); + max_z = max(max_z, point[2]); + } + } + + PT(BoundingHexahedron) bounds = new BoundingHexahedron + (LPoint3f(min_x, 0, min_z), LPoint3f(max_x, 0, min_z), + LPoint3f(min_x, 0, max_z), LPoint3f(max_x, 0, max_z), + LPoint3f(min_x, 0, min_z), LPoint3f(max_x, 0, min_z), + LPoint3f(min_x, 0, max_z), LPoint3f(max_x, 0, max_z)); + + // Rotate the bounding volume back into the original space of the + // screen. + bounds->xform(rotate); + + return bounds; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::generate_vis_points +// Access: Public +// Description: Creates a point cloud with the points of the pfm as +// 3-d coordinates in space, and texture coordinates +// ranging from 0 .. 1 based on the position within the +// pfm grid. +//////////////////////////////////////////////////////////////////// +NodePath PfmFile:: +generate_vis_points() const { + nassertr(is_valid(), NodePath()); + + PT(GeomVertexData) vdata = new GeomVertexData + ("points", GeomVertexFormat::get_v3t2(), + Geom::UH_static); + vdata->set_num_rows(_x_size * _y_size); + GeomVertexWriter vertex(vdata, InternalName::get_vertex()); + GeomVertexWriter texcoord(vdata, InternalName::get_texcoord()); + + for (int yi = 0; yi < _y_size; ++yi) { + for (int xi = 0; xi < _x_size; ++xi) { + vertex.add_data3f(get_point(xi, yi)); + texcoord.add_data2f(float(xi) / float(_x_size - 1), + float(yi) / float(_y_size - 1)); + } + } + + PT(Geom) geom = new Geom(vdata); + PT(GeomPoints) points = new GeomPoints(Geom::UH_static); + points->add_next_vertices(_x_size * _y_size); + geom->add_primitive(points); + + PT(GeomNode) gnode = new GeomNode(""); + gnode->add_geom(geom); + return NodePath(gnode); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::compute_sample_point +// Access: Private +// Description: Computes the average of all the point within +// sample_radius (manhattan distance) and the indicated +// point. +// +// Unlike box_filter_*(), these point values are given +// in UV space, in the range 0..1. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +compute_sample_point(LPoint3f &result, + double x, double y, double sample_radius) const { + x *= _x_size; + y *= _y_size; + double xr = sample_radius * _x_size; + double yr = sample_radius * _y_size; + box_filter_region(result, x - xr, y - yr, x + xr, y + yr); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_region +// Access: Private +// Description: Averages all the points in the rectangle from x0 +// .. y0 to x1 .. y1 into result. The region may be +// defined by floating-point boundaries; the result will +// be weighted by the degree of coverage of each +// included point. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_region(LPoint3f &result, + double x0, double y0, double x1, double y1) const { + result = LPoint3f::zero(); + double coverage = 0.0; + + assert(y0 >= 0.0 && y1 >= 0.0); + + int y = (int)y0; + // Get the first (partial) row + box_filter_line(result, coverage, x0, y, x1, (double)(y+1)-y0); + + int y_last = (int)y1; + if (y < y_last) { + y++; + while (y < y_last) { + // Get each consecutive (complete) row + box_filter_line(result, coverage, x0, y, x1, 1.0); + y++; + } + + // Get the final (partial) row + double y_contrib = y1 - (double)y_last; + if (y_contrib > 0.0001) { + box_filter_line(result, coverage, x0, y, x1, y_contrib); + } + } + + if (coverage != 0.0) { + result /= coverage; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_line +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_line(LPoint3f &result, double &coverage, + double x0, int y, double x1, double y_contrib) const { + int x = (int)x0; + // Get the first (partial) xel + box_filter_point(result, coverage, x, y, (double)(x+1)-x0, y_contrib); + + int x_last = (int)x1; + if (x < x_last) { + x++; + while (x < x_last) { + // Get each consecutive (complete) xel + box_filter_point(result, coverage, x, y, 1.0, y_contrib); + x++; + } + + // Get the final (partial) xel + double x_contrib = x1 - (double)x_last; + if (x_contrib > 0.0001) { + box_filter_point(result, coverage, x, y, x_contrib, y_contrib); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::box_filter_point +// Access: Private +// Description: +//////////////////////////////////////////////////////////////////// +void PfmFile:: +box_filter_point(LPoint3f &result, double &coverage, + int x, int y, double x_contrib, double y_contrib) const { + const LPoint3f &point = get_point(x, y); + if (_zero_special && point == LPoint3f::zero()) { + return; + } + + double contrib = x_contrib * y_contrib; + result += point * contrib; + coverage += contrib; +} diff --git a/pandatool/src/pfmprogs/pfmFile.h b/pandatool/src/pfmprogs/pfmFile.h new file mode 100755 index 0000000000..bf0639bc69 --- /dev/null +++ b/pandatool/src/pfmprogs/pfmFile.h @@ -0,0 +1,87 @@ +// Filename: pfmFile.h +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PFMFILE_H +#define PFMFILE_H + +#include "pandatoolbase.h" +#include "luse.h" +#include "nodePath.h" +#include "boundingHexahedron.h" + +//////////////////////////////////////////////////////////////////// +// Class : PfmFile +// Description : Defines a pfm file, a 2-d table of floating-point +// numbers, either 3-component or 1-component. +//////////////////////////////////////////////////////////////////// +class PfmFile { +public: + PfmFile(); + PfmFile(const PfmFile ©); + void operator = (const PfmFile ©); + + void clear(); + + bool read(const Filename &fullpath); + bool read(istream &in); + bool write(const Filename &fullpath); + bool write(ostream &out); + + INLINE bool is_valid() const; + + INLINE int get_x_size() const; + INLINE int get_y_size() const; + INLINE float get_scale() const; + INLINE int get_num_channels() const; + + INLINE const LPoint3f &get_point(int x, int y) const; + INLINE LPoint3f &modify_point(int x, int y); + + INLINE void set_zero_special(bool zero_special); + INLINE bool get_zero_special() const; + + void resize(int new_x_size, int new_y_size); + void reverse_rows(); + void xform(const LMatrix4f &transform); + + PT(BoundingHexahedron) compute_planar_bounds(double point_dist, double sample_radius) const; + + NodePath generate_vis_points() const; + +private: + void compute_sample_point(LPoint3f &result, + double x, double y, double sample_radius) const; + void box_filter_region(LPoint3f &result, + double x0, double y0, double x1, double y1) const; + void box_filter_line(LPoint3f &result, double &coverage, + double x0, int y, double x1, double y_contrib) const; + void box_filter_point(LPoint3f &result, double &coverage, + int x, int y, double x_contrib, double y_contrib) const; + + +private: + typedef pvector Table; + Table _table; + + int _x_size; + int _y_size; + float _scale; + int _num_channels; + + bool _zero_special; +}; + +#include "pfmFile.I" + +#endif diff --git a/pandatool/src/pfmprogs/pfmTrans.cxx b/pandatool/src/pfmprogs/pfmTrans.cxx new file mode 100755 index 0000000000..096c70dfe0 --- /dev/null +++ b/pandatool/src/pfmprogs/pfmTrans.cxx @@ -0,0 +1,444 @@ +// Filename: pfmTrans.cxx +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#include "pfmTrans.h" +#include "config_pfm.h" +#include "pfmFile.h" +#include "pystub.h" +#include "texture.h" +#include "texturePool.h" +#include "pointerTo.h" +#include "string_utils.h" +#include "pandaFileStream.h" + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PfmTrans:: +PfmTrans() { + _got_transform = false; + _transform = LMatrix4f::ident_mat(); + + add_transform_options(); + + set_program_description + ("pfm-trans reads an pfm file and transforms it, filters it, " + "operates on it, writing the output to another pfm file. A pfm " + "file contains a 2-d table of floating-point values."); + + add_option + ("r", "", 0, + "Reverses the rows of the pfm data vertically.", + &PfmTrans::dispatch_none, &_got_reverse); + + add_option + ("z", "", 0, + "Treats (0,0,0) in the pfm file as a special don't-touch value.", + &PfmTrans::dispatch_none, &_got_zero_special); + + add_option + ("resize", "width,height", 0, + "Resamples the pfm file to scale it to the indicated grid size. " + "A simple box filter is applied during the scale. Don't confuse this " + "with -TS, which scales the individual point values, but doesn't " + "change the number of points.", + &PfmTrans::dispatch_int_pair, &_got_resize, &_resize); + + add_option + ("o", "filename", 50, + "Specify the filename to which the resulting pfm file will be written. " + "This is only valid when there is only one input pfm file on the command " + "line. If you want to process multiple files simultaneously, you must " + "use -d.", + &PfmTrans::dispatch_filename, &_got_output_filename, &_output_filename); + + add_option + ("d", "dirname", 50, + "Specify the name of the directory in which to write the processed pfm " + "files. If you are processing only one pfm file, this may be omitted " + "in lieu of the -o option.", + &PfmTrans::dispatch_filename, &_got_output_dirname, &_output_dirname); + + add_option + ("bba", "", 50, + "Generates a .bba file alongside each output file that lists the " + "planar bounding volume of each pfm file's data.", + &PfmTrans::dispatch_none, &_got_bba); + + add_option + ("vis", "filename.bam", 60, + "Generates a bam file that represents a visualization of the pfm file " + "as a 3-D geometric mesh. If -vistex is specified, the mesh is " + "textured.", + &PfmTrans::dispatch_filename, &_got_vis_filename, &_vis_filename); + + add_option + ("vistex", "texture.jpg", 60, + "Specifies the name of the texture to apply to the visualization.", + &PfmTrans::dispatch_filename, &_got_vistex_filename, &_vistex_filename); +} + + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::run +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +void PfmTrans:: +run() { + if (_got_vis_filename) { + _mesh_root = NodePath("mesh_root"); + } + + Filenames::const_iterator fi; + for (fi = _input_filenames.begin(); fi != _input_filenames.end(); ++fi) { + PfmFile file; + if (!file.read(*fi)) { + nout << "Cannot read " << *fi << "\n"; + exit(1); + } + if (!process_pfm(*fi, file)) { + exit(1); + } + } + + if (_got_vis_filename) { + _mesh_root.write_bam_file(_vis_filename); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::process_pfm +// Access: Public +// Description: Handles a single pfm file. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +process_pfm(const Filename &input_filename, PfmFile &file) { + file.set_zero_special(_got_zero_special); + + if (_got_resize) { + file.resize(_resize[0], _resize[1]); + } + + if (_got_reverse) { + file.reverse_rows(); + } + + if (_got_transform) { + file.xform(_transform); + } + + if (_got_vis_filename) { + NodePath mesh = file.generate_vis_points(); + if (_got_vistex_filename) { + PT(Texture) tex = TexturePool::load_texture(_vistex_filename); + if (tex == NULL) { + nout << "Couldn't find " << _vistex_filename << "\n"; + } else { + mesh.set_texture(tex); + } + } + mesh.set_name(input_filename.get_basename_wo_extension()); + mesh.reparent_to(_mesh_root); + } + + Filename output_filename; + if (_got_output_filename) { + output_filename = _output_filename; + } else if (_got_output_dirname) { + output_filename = Filename(_output_dirname, input_filename.get_basename()); + } + + if (!output_filename.empty()) { + if (_got_bba) { + Filename bba_filename = output_filename; + bba_filename.set_text(); + bba_filename.set_extension("bba"); + PT(BoundingHexahedron) bounds = file.compute_planar_bounds(pfm_bba_dist[0], pfm_bba_dist[1]); + nassertr(bounds != (BoundingHexahedron *)NULL, false); + + pofstream out; + if (!bba_filename.open_write(out)) { + pfm_cat.error() + << "Unable to open " << bba_filename << "\n"; + return false; + } + + for (int i = 0; i < bounds->get_num_points(); ++i) { + LPoint3f p = bounds->get_point(i); + out << p[0] << "," << p[1] << "," << p[2] << "\n"; + } + } + return file.write(output_filename); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::add_transform_options +// Access: Public +// Description: Adds -TS, -TT, etc. as valid options for this +// program. If the user specifies one of the options on +// the command line, the data will be transformed when +// the egg file is written out. +//////////////////////////////////////////////////////////////////// +void PfmTrans:: +add_transform_options() { + add_option + ("TS", "sx[,sy,sz]", 49, + "Scale the model uniformly by the given factor (if only one number " + "is given) or in each axis by sx, sy, sz (if three numbers are given).", + &PfmTrans::dispatch_scale, &_got_transform, &_transform); + + add_option + ("TR", "x,y,z", 49, + "Rotate the model x degrees about the x axis, then y degrees about the " + "y axis, and then z degrees about the z axis.", + &PfmTrans::dispatch_rotate_xyz, &_got_transform, &_transform); + + add_option + ("TA", "angle,x,y,z", 49, + "Rotate the model angle degrees counterclockwise about the given " + "axis.", + &PfmTrans::dispatch_rotate_axis, &_got_transform, &_transform); + + add_option + ("TT", "x,y,z", 49, + "Translate the model by the indicated amount.\n\n" + "All transformation options (-TS, -TR, -TA, -TT) are cumulative and are " + "applied in the order they are encountered on the command line.", + &PfmTrans::dispatch_translate, &_got_transform, &_transform); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::handle_args +// Access: Protected, Virtual +// Description: Does something with the additional arguments on the +// command line (after all the -options have been +// parsed). Returns true if the arguments are good, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +handle_args(ProgramBase::Args &args) { + if (args.empty()) { + nout << "You must specify the pfm file(s) to read on the command line.\n"; + return false; + } + + if (_got_output_filename && args.size() == 1) { + if (_got_output_dirname) { + nout << "Cannot specify both -o and -d.\n"; + return false; + } + + } else { + if (_got_output_filename) { + nout << "Cannot use -o when multiple pfm files are specified.\n"; + return false; + } + } + + Args::const_iterator ai; + for (ai = args.begin(); ai != args.end(); ++ai) { + _input_filenames.push_back(Filename::from_os_specific(*ai)); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::dispatch_scale +// Access: Protected, Static +// Description: Handles -TS, which specifies a scale transform. Var +// is an LMatrix4f. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +dispatch_scale(const string &opt, const string &arg, void *var) { + LMatrix4f *transform = (LMatrix4f *)var; + + vector_string words; + tokenize(arg, words, ","); + + float sx, sy, sz; + + bool okflag = false; + if (words.size() == 3) { + okflag = + string_to_float(words[0], sx) && + string_to_float(words[1], sy) && + string_to_float(words[2], sz); + + } else if (words.size() == 1) { + okflag = + string_to_float(words[0], sx); + sy = sz = sx; + } + + if (!okflag) { + nout << "-" << opt + << " requires one or three numbers separated by commas.\n"; + return false; + } + + *transform = (*transform) * LMatrix4f::scale_mat(sx, sy, sz); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::dispatch_rotate_xyz +// Access: Protected, Static +// Description: Handles -TR, which specifies a rotate transform about +// the three cardinal axes. Var is an LMatrix4f. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +dispatch_rotate_xyz(ProgramBase *self, const string &opt, const string &arg, void *var) { + PfmTrans *base = (PfmTrans *)self; + return base->ns_dispatch_rotate_xyz(opt, arg, var); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::ns_dispatch_rotate_xyz +// Access: Protected +// Description: Handles -TR, which specifies a rotate transform about +// the three cardinal axes. Var is an LMatrix4f. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +ns_dispatch_rotate_xyz(const string &opt, const string &arg, void *var) { + LMatrix4f *transform = (LMatrix4f *)var; + + vector_string words; + tokenize(arg, words, ","); + + LVecBase3f xyz; + + bool okflag = false; + if (words.size() == 3) { + okflag = + string_to_float(words[0], xyz[0]) && + string_to_float(words[1], xyz[1]) && + string_to_float(words[2], xyz[2]); + } + + if (!okflag) { + nout << "-" << opt + << " requires three numbers separated by commas.\n"; + return false; + } + + LMatrix4f mat = + LMatrix4f::rotate_mat(xyz[0], LVector3f(1.0, 0.0, 0.0)) * + LMatrix4f::rotate_mat(xyz[1], LVector3f(0.0, 1.0, 0.0)) * + LMatrix4f::rotate_mat(xyz[2], LVector3f(0.0, 0.0, 1.0)); + + *transform = (*transform) * mat; + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::dispatch_rotate_axis +// Access: Protected, Static +// Description: Handles -TA, which specifies a rotate transform about +// an arbitrary axis. Var is an LMatrix4f. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +dispatch_rotate_axis(ProgramBase *self, const string &opt, const string &arg, void *var) { + PfmTrans *base = (PfmTrans *)self; + return base->ns_dispatch_rotate_axis(opt, arg, var); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::ns_dispatch_rotate_axis +// Access: Protected +// Description: Handles -TA, which specifies a rotate transform about +// an arbitrary axis. Var is an LMatrix4f. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +ns_dispatch_rotate_axis(const string &opt, const string &arg, void *var) { + LMatrix4f *transform = (LMatrix4f *)var; + + vector_string words; + tokenize(arg, words, ","); + + float angle; + LVecBase3f axis; + + bool okflag = false; + if (words.size() == 4) { + okflag = + string_to_float(words[0], angle) && + string_to_float(words[1], axis[0]) && + string_to_float(words[2], axis[1]) && + string_to_float(words[3], axis[2]); + } + + if (!okflag) { + nout << "-" << opt + << " requires four numbers separated by commas.\n"; + return false; + } + + *transform = (*transform) * LMatrix4f::rotate_mat(angle, axis); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmTrans::dispatch_translate +// Access: Protected, Static +// Description: Handles -TT, which specifies a translate transform. +// Var is an LMatrix4f. +//////////////////////////////////////////////////////////////////// +bool PfmTrans:: +dispatch_translate(const string &opt, const string &arg, void *var) { + LMatrix4f *transform = (LMatrix4f *)var; + + vector_string words; + tokenize(arg, words, ","); + + LVector3f trans; + + bool okflag = false; + if (words.size() == 3) { + okflag = + string_to_float(words[0], trans[0]) && + string_to_float(words[1], trans[1]) && + string_to_float(words[2], trans[2]); + } + + if (!okflag) { + nout << "-" << opt + << " requires three numbers separated by commas.\n"; + return false; + } + + *transform = (*transform) * LMatrix4f::translate_mat(trans); + + return true; +} + + +int main(int argc, char *argv[]) { + // A call to pystub() to force libpystub.so to be linked in. + pystub(); + + PfmTrans prog; + prog.parse_command_line(argc, argv); + prog.run(); + return 0; +} diff --git a/pandatool/src/pfmprogs/pfmTrans.h b/pandatool/src/pfmprogs/pfmTrans.h new file mode 100755 index 0000000000..9db1b3c389 --- /dev/null +++ b/pandatool/src/pfmprogs/pfmTrans.h @@ -0,0 +1,75 @@ +// Filename: pfmTrans.h +// Created by: drose (23Dec10) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) Carnegie Mellon University. All rights reserved. +// +// All use of this software is subject to the terms of the revised BSD +// license. You should have received a copy of this license along +// with this source code in a file named "LICENSE." +// +//////////////////////////////////////////////////////////////////// + +#ifndef PFMTRANS_H +#define PFMTRANS_H + +#include "pandatoolbase.h" +#include "programBase.h" +#include "filename.h" +#include "pvector.h" +#include "nodePath.h" +#include "luse.h" + +class PfmFile; + +//////////////////////////////////////////////////////////////////// +// Class : PfmTrans +// Description : Operates on a pfm file. +//////////////////////////////////////////////////////////////////// +class PfmTrans : public ProgramBase { +public: + PfmTrans(); + + void run(); + bool process_pfm(const Filename &input_filename, PfmFile &file); + + void add_transform_options(); + +protected: + virtual bool handle_args(Args &args); + + static bool dispatch_scale(const string &opt, const string &arg, void *var); + static bool dispatch_rotate_xyz(ProgramBase *self, const string &opt, const string &arg, void *var); + bool ns_dispatch_rotate_xyz(const string &opt, const string &arg, void *var); + static bool dispatch_rotate_axis(ProgramBase *self, const string &opt, const string &arg, void *var); + bool ns_dispatch_rotate_axis(const string &opt, const string &arg, void *var); + static bool dispatch_translate(const string &opt, const string &arg, void *var); + +private: + typedef pvector Filenames; + Filenames _input_filenames; + + bool _got_reverse; + bool _got_zero_special; + bool _got_resize; + int _resize[2]; + bool _got_bba; + + bool _got_output_filename; + Filename _output_filename; + bool _got_output_dirname; + Filename _output_dirname; + bool _got_vis_filename; + Filename _vis_filename; + bool _got_vistex_filename; + Filename _vistex_filename; + + bool _got_transform; + LMatrix4f _transform; + + NodePath _mesh_root; +}; + +#endif