diff --git a/panda/src/gobj/texture.I b/panda/src/gobj/texture.I index 36e2b2e2dc..810c652f3b 100644 --- a/panda/src/gobj/texture.I +++ b/panda/src/gobj/texture.I @@ -315,6 +315,42 @@ load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: Texture::load +// Access: Published +// Description: Replaces the texture with the indicated image. +//////////////////////////////////////////////////////////////////// +INLINE bool Texture:: +load(const PfmFile &pfm, const LoaderOptions &options) { + CDWriter cdata(_cycler, true); + do_clear(cdata); + ++(cdata->_properties_modified); + ++(cdata->_image_modified); + if (do_load_one(cdata, pfm, get_name(), 0, 0, options)) { + bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0); + consider_auto_process_ram_image(generate_mipmaps || uses_mipmaps(), true); + return true; + } + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::load +// Access: Published +// Description: Stores the indicated image in the given page and +// mipmap level. See read(). +//////////////////////////////////////////////////////////////////// +INLINE bool Texture:: +load(const PfmFile &pfm, int z, int n, const LoaderOptions &options) { + CDWriter cdata(_cycler, true); + ++(cdata->_properties_modified); + ++(cdata->_image_modified); + if (do_load_one(cdata, pfm, get_name(), z, n, options)) { + return true; + } + return false; +} + //////////////////////////////////////////////////////////////////// // Function: Texture::store // Access: Published @@ -339,6 +375,30 @@ store(PNMImage &pnmimage, int z, int n) const { return ((Texture *)this)->do_store_one(cdata, pnmimage, z, n); } +//////////////////////////////////////////////////////////////////// +// Function: Texture::store +// Access: Published +// Description: Saves the texture to the indicated PfmFile, but does +// not write it to disk. +//////////////////////////////////////////////////////////////////// +INLINE bool Texture:: +store(PfmFile &pfm) const { + CDWriter cdata(((Texture *)this)->_cycler, false); + return ((Texture *)this)->do_store_one(cdata, pfm, 0, 0); +} + +//////////////////////////////////////////////////////////////////// +// Function: Texture::store +// Access: Published +// Description: Saves the indicated page and mipmap level of the +// texture to the PfmFile. +//////////////////////////////////////////////////////////////////// +INLINE bool Texture:: +store(PfmFile &pfm, int z, int n) const { + CDWriter cdata(((Texture *)this)->_cycler, false); + return ((Texture *)this)->do_store_one(cdata, pfm, z, n); +} + //////////////////////////////////////////////////////////////////// // Function: Texture::reload // Access: Published diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index a0f2bb6bd5..1c4099d5ae 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -2924,10 +2924,8 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa } image.copy_header_from(*image_reader); - // If it's a single-channel floating-point image file, read it by - // default into a floating-point texture. (Multi-channel image - // files are ready as integers by default, since current graphics - // API's don't support multi-channel float textures.) + // If it's a floating-point image file, read it by default into a + // floating-point texture. bool read_floating_point; int texture_load_type = (options.get_texture_flags() & (LoaderOptions::TF_integer | LoaderOptions::TF_float)); switch (texture_load_type) { @@ -2942,7 +2940,7 @@ do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpa default: // Neither TF_integer nor TF_float was specified; determine which // way the texture wants to be loaded. - read_floating_point = (image_reader->is_floating_point() && image_reader->get_num_channels() == 1); + read_floating_point = (image_reader->is_floating_point()); if (!alpha_fullpath.empty()) { read_floating_point = false; } @@ -3843,6 +3841,21 @@ do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n) { nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false); nassertr(cdata->_ram_image_compression == CM_off, false); + if (cdata->_component_type == T_float) { + // PNMImage by way of PfmFile. + PfmFile pfm; + bool success = convert_to_pfm(pfm, + do_get_expected_mipmap_x_size(cdata, n), + do_get_expected_mipmap_y_size(cdata, n), + cdata->_num_components, cdata->_component_width, + cdata->_ram_images[n]._image, + do_get_ram_mipmap_page_size(cdata, n), z); + if (!success) { + return false; + } + return pfm.store(pnmimage); + } + return convert_to_pnmimage(pnmimage, do_get_expected_mipmap_x_size(cdata, n), do_get_expected_mipmap_y_size(cdata, n), @@ -3869,6 +3882,21 @@ do_store_one(CData *cdata, PfmFile &pfm, int z, int n) { nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false); nassertr(cdata->_ram_image_compression == CM_off, false); + if (cdata->_component_type != T_float) { + // PfmFile by way of PNMImage. + PNMImage pnmimage; + bool success = convert_to_pnmimage(pnmimage, + do_get_expected_mipmap_x_size(cdata, n), + do_get_expected_mipmap_y_size(cdata, n), + cdata->_num_components, cdata->_component_width, + cdata->_ram_images[n]._image, + do_get_ram_mipmap_page_size(cdata, n), z); + if (!success) { + return false; + } + return pfm.load(pnmimage); + } + return convert_to_pfm(pfm, do_get_expected_mipmap_x_size(cdata, n), do_get_expected_mipmap_y_size(cdata, n), @@ -5901,7 +5929,11 @@ bool Texture:: convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, int num_components, int component_width, CPTA_uchar image, size_t page_size, int z) { - pnmimage.clear(x_size, y_size, num_components); + xelval maxval = 0xff; + if (component_width > 1) { + maxval = 0xffff; + } + pnmimage.clear(x_size, y_size, num_components, maxval); bool has_alpha = pnmimage.has_alpha(); bool is_grayscale = pnmimage.is_grayscale(); diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index 32c272ed25..ee03257126 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -260,8 +260,12 @@ PUBLISHED: BLOCKING INLINE bool load(const PNMImage &pnmimage, const LoaderOptions &options = LoaderOptions()); BLOCKING INLINE bool load(const PNMImage &pnmimage, int z, int n, const LoaderOptions &options = LoaderOptions()); + BLOCKING INLINE bool load(const PfmFile &pfm, const LoaderOptions &options = LoaderOptions()); + BLOCKING INLINE bool load(const PfmFile &pfm, int z, int n, const LoaderOptions &options = LoaderOptions()); BLOCKING INLINE bool store(PNMImage &pnmimage) const; BLOCKING INLINE bool store(PNMImage &pnmimage, int z, int n) const; + BLOCKING INLINE bool store(PfmFile &pfm) const; + BLOCKING INLINE bool store(PfmFile &pfm, int z, int n) const; BLOCKING INLINE bool reload(); BLOCKING Texture *load_related(const InternalName *suffix) const; diff --git a/panda/src/pnmimage/pfmFile.I b/panda/src/pnmimage/pfmFile.I index 2712f17870..93e531d5b5 100644 --- a/panda/src/pnmimage/pfmFile.I +++ b/panda/src/pnmimage/pfmFile.I @@ -113,6 +113,76 @@ set_point1(int x, int y, PN_float32 point) { _table[(y * _x_size + x) * _num_channels] = point; } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::get_point2 +// Access: Published +// Description: Returns the 2-component point value at the indicated +// point. In a 1-channel image, the channel value is in +// the x component. +//////////////////////////////////////////////////////////////////// +INLINE const LPoint2f &PfmFile:: +get_point2(int x, int y) const { + nassertr(x >= 0 && x < _x_size && + y >= 0 && y < _y_size, LPoint2f::zero()); + return *(LPoint2f *)&_table[(y * _x_size + x) * _num_channels]; +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::set_point2 +// Access: Published +// Description: Replaces the 2-component point value at the indicated +// point. In a 1-channel image, the channel value is in +// the x component. +//////////////////////////////////////////////////////////////////// +INLINE void PfmFile:: +set_point2(int x, int y, const LVecBase2f &point) { + nassertv(!point.is_nan()); + nassertv(x >= 0 && x < _x_size && + y >= 0 && y < _y_size); + switch (_num_channels) { + case 1: + _table[(y * _x_size + x)] = point[0]; + break; + + case 3: + (*(LPoint3f *)&_table[(y * _x_size + x) * _num_channels]).set(point[0], point[1], point[2]); + break; + + case 2: + *(LPoint2f *)&_table[(y * _x_size + x) * _num_channels] = point; + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::set_point2 +// Access: Published +// Description: Replaces the 2-component point value at the indicated +// point. In a 1-channel image, the channel value is in +// the x component. +//////////////////////////////////////////////////////////////////// +INLINE void PfmFile:: +set_point2(int x, int y, const LVecBase2d &point) { + set_point2(x, y, LCAST(PN_float32, point)); +} + +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::modify_point2 +// Access: Published +// Description: Returns a modifiable 2-component point value at the +// indicated point. +//////////////////////////////////////////////////////////////////// +INLINE LPoint2f &PfmFile:: +modify_point2(int x, int y) { +#ifndef NDEBUG + static LPoint2f dummy_value = LPoint2f::zero(); + nassertr(x >= 0 && x < _x_size && + y >= 0 && y < _y_size, dummy_value); +#endif + + return *(LPoint2f *)&_table[(y * _x_size + x) * _num_channels]; +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::get_point // Access: Published diff --git a/panda/src/pnmimage/pfmFile.cxx b/panda/src/pnmimage/pfmFile.cxx index 1cc768eb11..079fb8f046 100644 --- a/panda/src/pnmimage/pfmFile.cxx +++ b/panda/src/pnmimage/pfmFile.cxx @@ -993,6 +993,27 @@ merge(const PfmFile &other) { } } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::copy_channel +// Access: Published +// Description: Copies just the specified channel values from the +// indicated PfmFile (which could be same as this +// PfmFile) into the specified channel of this one. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +copy_channel(int to_channel, const PfmFile &other, int from_channel) { + nassertv(is_valid() && other.is_valid()); + nassertv(other._x_size == _x_size && other._y_size == _y_size); + nassertv(to_channel >= 0 && to_channel < get_num_channels() && + from_channel >= 0 && from_channel < other.get_num_channels()); + + for (int yi = 0; yi < _y_size; ++yi) { + for (int xi = 0; xi < _x_size; ++xi) { + set_component(xi, yi, to_channel, other.get_component(xi, yi, from_channel)); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::apply_crop // Access: Published @@ -1027,6 +1048,34 @@ apply_crop(int x_begin, int x_end, int y_begin, int y_end) { _y_size = new_y_size; } +//////////////////////////////////////////////////////////////////// +// Function: PfmFile::clear_to_texcoords +// Access: Published +// Description: Replaces this PfmFile with a new PfmFile of size +// x_size x y_size x 3, containing the x y 0 values in +// the range 0 .. 1 according to the x y index. +//////////////////////////////////////////////////////////////////// +void PfmFile:: +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 (_y_size > 1) { + uv_scale[1] = 1.0f / PN_float32(_y_size - 1); + } + + 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); + set_point(xi, yi, uv); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: PfmFile::compute_planar_bounds // Access: Published @@ -1366,6 +1415,16 @@ generate_vis_mesh(MeshFace face) const { nassertr(is_valid(), NodePath()); nassertr(face != 0, NodePath()); + if (_num_channels == 1 && _vis_columns.empty()) { + // If we're generating a default mesh from a one-channel pfm file, + // expand it to a three-channel pfm file to make the visualization + // useful. + PfmFile expanded; + expanded.clear_to_texcoords(_x_size, _y_size); + expanded.copy_channel(2, *this, 0); + return expanded.generate_vis_mesh(face); + } + if (_x_size == 1 || _y_size == 1) { // Can't generate a 1-d mesh, so generate points in this case. return generate_vis_points(); @@ -1864,7 +1923,7 @@ build_auto_vis_columns(VisColumns &vis_columns, bool for_points) const { if (_vis_2d) { // No normals needed if we're just generating a 2-d mesh. - add_vis_column(vis_columns, CT_vertex3, CT_vertex3, InternalName::get_vertex()); + add_vis_column(vis_columns, CT_vertex2, CT_vertex2, InternalName::get_vertex()); add_vis_column(vis_columns, CT_texcoord2, CT_texcoord2, InternalName::get_texcoord()); } else { @@ -1920,6 +1979,7 @@ make_array_format(const VisColumns &vis_columns) const { contents = GeomEnums::C_texcoord; break; + case CT_vertex1: case CT_vertex2: case CT_vertex3: num_components = 3; @@ -2069,10 +2129,18 @@ add_data(const PfmFile &file, GeomVertexWriter &vwriter, int xi, int yi, bool re } break; + case CT_vertex1: + { + PN_float32 p = file.get_point1(xi, yi); + LPoint2f point(p, 0.0); + transform_point(point); + vwriter.set_data2f(point); + } + break; + case CT_vertex2: { - const LPoint3f &point3 = file.get_point(xi, yi); - LPoint2f point(point3[0], point3[1]); + LPoint2f point = file.get_point2(xi, yi); transform_point(point); vwriter.set_data2f(point); } diff --git a/panda/src/pnmimage/pfmFile.h b/panda/src/pnmimage/pfmFile.h index dff4b46557..5b28f71f30 100644 --- a/panda/src/pnmimage/pfmFile.h +++ b/panda/src/pnmimage/pfmFile.h @@ -65,6 +65,10 @@ PUBLISHED: INLINE void set_component(int x, int y, int c, PN_float32 value); INLINE PN_float32 get_point1(int x, int y) const; INLINE void set_point1(int x, int y, PN_float32 point); + INLINE const LPoint2f &get_point2(int x, int y) const; + INLINE void set_point2(int x, int y, const LVecBase2f &point); + INLINE void set_point2(int x, int y, const LVecBase2d &point); + INLINE LPoint2f &modify_point2(int x, int y); INLINE const LPoint3f &get_point(int x, int y) const; INLINE void set_point(int x, int y, const LVecBase3f &point); INLINE void set_point(int x, int y, const LVecBase3d &point); @@ -99,7 +103,9 @@ PUBLISHED: INLINE BLOCKING void xform(const LMatrix4d &transform); BLOCKING void project(const Lens *lens); BLOCKING void merge(const PfmFile &other); + BLOCKING void copy_channel(int to_channel, const PfmFile &other, int from_channel); BLOCKING void apply_crop(int x_begin, int x_end, int y_begin, int y_end); + BLOCKING void clear_to_texcoords(int x_size, int y_size); BLOCKING PT(BoundingHexahedron) compute_planar_bounds(const LPoint2f ¢er, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const; INLINE BLOCKING PT(BoundingHexahedron) compute_planar_bounds(const LPoint2d ¢er, PN_float32 point_dist, PN_float32 sample_radius, bool points_only) const; @@ -117,6 +123,7 @@ PUBLISHED: enum ColumnType { CT_texcoord2, CT_texcoord3, + CT_vertex1, CT_vertex2, CT_vertex3, CT_normal3,