From 59b4efd18f294b9ceb5f02a9c44ca6e6eceef97c Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 15 May 2002 15:40:04 +0000 Subject: [PATCH] add M_dual and M_binary auto-detect to egg-palettize --- pandatool/src/egg-palettize/eggPalettize.cxx | 15 +- pandatool/src/egg-palettize/eggPalettize.h | 4 +- pandatool/src/egg-palettize/paletteImage.cxx | 2 +- pandatool/src/egg-palettize/palettizer.cxx | 16 +- pandatool/src/egg-palettize/palettizer.h | 4 +- pandatool/src/egg-palettize/textureImage.cxx | 165 ++++++++++++++---- pandatool/src/egg-palettize/textureImage.h | 26 ++- .../src/egg-palettize/textureReference.cxx | 10 ++ .../src/egg-palettize/textureRequest.cxx | 1 + pandatool/src/egg-palettize/textureRequest.h | 6 +- pandatool/src/egg-palettize/txaLine.cxx | 44 +++-- pandatool/src/egg-palettize/txaLine.h | 8 +- 12 files changed, 225 insertions(+), 76 deletions(-) diff --git a/pandatool/src/egg-palettize/eggPalettize.cxx b/pandatool/src/egg-palettize/eggPalettize.cxx index 52f41f6e5d..8bfda6275d 100644 --- a/pandatool/src/egg-palettize/eggPalettize.cxx +++ b/pandatool/src/egg-palettize/eggPalettize.cxx @@ -284,6 +284,17 @@ describe_input_file() { "formats cannot be placed together on the same palette " "images.\n\n"); + show_text(" force-rgba", 10, + "This specifies a particular format, as above, that should be " + "in effect for this texture, but it will not be downgraded to " + "match the number of channels. As above, any valid egg texture " + "format may be used, e.g. force-rgba12, force-rgb5, etc.\n\n"); + + show_text(" (alpha mode)", 10, + "A particular alpha mode may be applied to a texture by naming " + "the alpha mode. This may be any valid egg alpha mode, e.g. " + "blend, binary, ms, or dual.\n\n"); + show_text(" (image type)", 10, "A texture may be converted to a particular image type, for " "instance jpg or rgb, by naming the type. If present, this " @@ -637,9 +648,9 @@ run() { } if (_all_textures) { - pal->process_all(_redo_all); + pal->process_all(_redo_all, state_filename); } else { - pal->process_command_line_eggs(_redo_all); + pal->process_command_line_eggs(_redo_all, state_filename); } if (_optimal) { diff --git a/pandatool/src/egg-palettize/eggPalettize.h b/pandatool/src/egg-palettize/eggPalettize.h index 97d2f9a13a..dcf43302b1 100644 --- a/pandatool/src/egg-palettize/eggPalettize.h +++ b/pandatool/src/egg-palettize/eggPalettize.h @@ -19,9 +19,9 @@ #ifndef EGGPALETTIZE_H #define EGGPALETTIZE_H -#include +#include "pandatoolbase.h" -#include +#include "eggMultiFilter.h" //////////////////////////////////////////////////////////////////// // Class : EggPalettize diff --git a/pandatool/src/egg-palettize/paletteImage.cxx b/pandatool/src/egg-palettize/paletteImage.cxx index f83f2378f2..3d1ce05a8f 100644 --- a/pandatool/src/egg-palettize/paletteImage.cxx +++ b/pandatool/src/egg-palettize/paletteImage.cxx @@ -361,7 +361,7 @@ check_solitary() { /* if (!(placement->get_omit_reason() == OR_none || placement->get_omit_reason() == OR_solitary)) { - cerr << "texture " << *placement->get_texture() << " is omitted for " + nout << "texture " << *placement->get_texture() << " is omitted for " << placement->get_omit_reason() << "\n"; } */ diff --git a/pandatool/src/egg-palettize/palettizer.cxx b/pandatool/src/egg-palettize/palettizer.cxx index 91ffd51a33..23533fa67c 100644 --- a/pandatool/src/egg-palettize/palettizer.cxx +++ b/pandatool/src/egg-palettize/palettizer.cxx @@ -41,12 +41,13 @@ Palettizer *pal = (Palettizer *)NULL; // allows us to easily update egg-palettize to write out additional // information to its pi file, without having it increment the bam // version number for all bam and boo files anywhere in the world. -int Palettizer::_pi_version = 5; +int Palettizer::_pi_version = 6; // Updated to version 1 on 12/11/00 to add _remap_char_uv. // Updated to version 2 on 12/19/00 to add TexturePlacement::_dest. // Updated to version 3 on 12/19/00 to add PaletteGroup::_dependency_order. // Updated to version 4 on 5/3/01 to add PaletteGroup::_dirname_order. // Updated to version 5 on 10/31/01 to add TextureProperties::_force_format. +// Updated to version 6 on 3/14/02 to add TextureImage::_alpha_mode. int Palettizer::_read_pi_version = 0; @@ -377,7 +378,7 @@ all_params_set() { // for grayscaleness etc.) before placing. //////////////////////////////////////////////////////////////////// void Palettizer:: -process_command_line_eggs(bool force_texture_read) { +process_command_line_eggs(bool force_texture_read, const Filename &state_filename) { _command_line_textures.clear(); // Start by scanning all the egg files we read up on the command @@ -411,8 +412,9 @@ process_command_line_eggs(bool force_texture_read) { ++ti) { TextureImage *texture = *ti; - if (force_texture_read) { - // If we're forcing a redo, re-read the complete image. + if (force_texture_read || texture->is_newer_than(state_filename)) { + // If we're forcing a redo, or the texture image has changed, + // re-read the complete image. texture->read_source_image(); } else { // Otherwise, just the header is sufficient. @@ -470,7 +472,7 @@ process_command_line_eggs(bool force_texture_read) { // for grayscaleness etc.) before placing. //////////////////////////////////////////////////////////////////// void Palettizer:: -process_all(bool force_texture_read) { +process_all(bool force_texture_read, const Filename &state_filename) { // If there *were* any egg files on the command line, deal with // them. CommandLineEggs::const_iterator ei; @@ -505,7 +507,7 @@ process_all(bool force_texture_read) { for (ti = _textures.begin(); ti != _textures.end(); ++ti) { TextureImage *texture = (*ti).second; - if (force_texture_read) { + if (force_texture_read || texture->is_newer_than(state_filename)) { texture->read_source_image(); } @@ -637,7 +639,7 @@ read_stale_eggs(bool redo_all) { for (ii = invalid_eggs.begin(); ii != invalid_eggs.end(); ++ii) { EggFiles::iterator ei = (*ii); EggFile *egg_file = (*ei).second; - cerr << "Removing " << (*ei).first << "\n"; + nout << "Removing " << (*ei).first << "\n"; egg_file->remove_egg(); _egg_files.erase(ei); } diff --git a/pandatool/src/egg-palettize/palettizer.h b/pandatool/src/egg-palettize/palettizer.h index 6171ac5e79..3a9c3051c8 100644 --- a/pandatool/src/egg-palettize/palettizer.h +++ b/pandatool/src/egg-palettize/palettizer.h @@ -53,8 +53,8 @@ public: void read_txa_file(const Filename &txa_filename); void all_params_set(); - void process_command_line_eggs(bool force_texture_read); - void process_all(bool force_texture_read); + void process_command_line_eggs(bool force_texture_read, const Filename &state_filename); + void process_all(bool force_texture_read, const Filename &state_filename); void optimal_resize(); void reset_images(); void generate_images(bool redo_all); diff --git a/pandatool/src/egg-palettize/textureImage.cxx b/pandatool/src/egg-palettize/textureImage.cxx index 410cfab5ff..4f3ba3d10b 100644 --- a/pandatool/src/egg-palettize/textureImage.cxx +++ b/pandatool/src/egg-palettize/textureImage.cxx @@ -46,7 +46,8 @@ TextureImage() { _is_surprise = true; _ever_read_image = false; _forced_grayscale = false; - _forced_unalpha = false; + _alpha_bits = 0; + _alpha_mode = EggRenderMode::AM_unspecified; _got_txa_file = false; } @@ -256,13 +257,16 @@ pre_txa_file() { // Save our current properties, so we can note if they change. _pre_txa_properties = _properties; - // Update our properties from the egg files that reference this - // texture. It's possible the .txa file will update them further. + // Get our properties from the actual image for this texture. It's + // possible the .txa file will update them further. SourceTextureImage *source = get_preferred_source(); if (source != (SourceTextureImage *)NULL) { _properties = source->get_properties(); } + _pre_txa_alpha_mode = _alpha_mode; + _alpha_mode = EggRenderMode::AM_unspecified; + _request.pre_txa_file(); _is_surprise = true; } @@ -306,10 +310,11 @@ post_txa_file() { consider_grayscale(); } - // Also consider downgrading from alpha to non-alpha. + // Also consider the alpha properties, and whether we should + // downgrade from alpha to non-alpha. if (_properties._got_num_channels && (_properties._num_channels == 2 || _properties._num_channels == 4)) { - consider_unalpha(); + consider_alpha(); } // However, if we got an explicit request for channels, honor that. @@ -351,6 +356,25 @@ post_txa_file() { mark_eggs_stale(); } } + + // The alpha mode isn't stored in the properties, because it doesn't + // affect which textures may be associated into a common palette. + if (_request._alpha_mode != EggRenderMode::AM_unspecified) { + _alpha_mode = _request._alpha_mode; + } + + // On the other hand, if we don't use alpha, we shouldn't have an + // alpha mode. + if (_properties._got_num_channels && + (_properties._num_channels == 1 || _properties._num_channels == 3)) { + _alpha_mode = EggRenderMode::AM_unspecified; + } + + // If we've changed the alpha mode, we should also mark the eggs + // stale. + if (_pre_txa_alpha_mode != _alpha_mode) { + mark_eggs_stale(); + } } //////////////////////////////////////////////////////////////////// @@ -451,6 +475,19 @@ is_used() const { return !_placement.empty(); } +//////////////////////////////////////////////////////////////////// +// Function: TextureImage::get_alpha_mode +// Access: Public +// Description: Returns the alpha mode that should be used to render +// objects with this texture, as specified by the user +// or as determined from examining the texture's alpha +// channel. +//////////////////////////////////////////////////////////////////// +EggRenderMode::AlphaMode TextureImage:: +get_alpha_mode() const { + return _alpha_mode; +} + //////////////////////////////////////////////////////////////////// // Function: TextureImage::get_source @@ -677,6 +714,26 @@ read_header() { } } +//////////////////////////////////////////////////////////////////// +// Function: TextureImage::is_newer_than +// Access: Public +// Description: Returns true if the source image is newer than the +// indicated file, false otherwise. If the image has +// already been read, this always returns false. +//////////////////////////////////////////////////////////////////// +bool TextureImage:: +is_newer_than(const Filename &reference_filename) { + if (!_read_source_image) { + SourceTextureImage *source = get_preferred_source(); + if (source != (SourceTextureImage *)NULL) { + const Filename &source_filename = source->get_filename(); + return source_filename.compare_timestamps(reference_filename) >= 0; + } + } + + return false; +} + //////////////////////////////////////////////////////////////////// // Function: TextureImage::write_source_pathnames // Access: Public @@ -960,46 +1017,76 @@ consider_grayscale() { } //////////////////////////////////////////////////////////////////// -// Function: TextureImage::consider_unalpha +// Function: TextureImage::consider_alpha // Access: Private // Description: Examines the actual contents of the image to -// determine if its alpha channel should be eliminated -// (e.g. it's completely white, and therefore -// pointless). +// determine what alpha properties it has. //////////////////////////////////////////////////////////////////// void TextureImage:: -consider_unalpha() { +consider_alpha() { // As above, we don't bother doing this if we've already done this // in a previous session. - if (!_read_source_image && _ever_read_image) { - if (_forced_unalpha) { - _properties._num_channels--; - } - return; - } - const PNMImage &source = read_source_image(); - if (!source.is_valid()) { - return; - } + // _alpha_bits == -1 indicates we have read an older textures.boo + // file that didn't define these bits. + if (_read_source_image || !_ever_read_image || _alpha_bits == -1) { + _alpha_bits = 0; - if (!source.has_alpha()) { - return; - } - - for (int y = 0; y < source.get_y_size(); y++) { - for (int x = 0; x < source.get_x_size(); x++) { - if (source.get_alpha_val(x, y) != source.get_maxval()) { - // Here's a non-white pixel; the alpha channel is meaningful. - _forced_unalpha = false; - return; + const PNMImage &source = read_source_image(); + if (source.is_valid() && source.has_alpha()) { + xelval maxval = source.get_maxval(); + for (int y = 0; y < source.get_y_size(); y++) { + for (int x = 0; x < source.get_x_size(); x++) { + xelval alpha_val = source.get_alpha_val(x, y); + if (alpha_val == 0) { + _alpha_bits |= AB_zero; + } else if (alpha_val == maxval) { + _alpha_bits |= AB_one; + } else { + _alpha_bits |= AB_mid; + } + if (_alpha_bits == AB_all) { + // Once we've found a sample of everything, we can stop + // searching. + break; + } + } } } } - // All alpha pixels in the image were white! - _properties._num_channels--; - _forced_unalpha = true; + if (_alpha_bits != 0) { + if (_alpha_bits == AB_one) { + // All alpha pixels are white; drop the alpha channel. + nassertv(_properties._num_channels == 2 || _properties._num_channels == 4); + _properties._num_channels--; + + } else if (_alpha_bits == AB_zero) { + // All alpha pixels are invisible; this is probably a mistake. + // Drop the alpha channel and complain. + nassertv(_properties._num_channels == 2 || _properties._num_channels == 4); + _properties._num_channels--; + if (_read_source_image) { + nout << *this << " has an all-zero alpha channel; dropping alpha.\n"; + } + + } else if (_alpha_mode == EggRenderMode::AM_unspecified) { + // Consider fiddling with the alpha mode, if the user hasn't + // specified a particular alpha mode in the txa file. + if ((_alpha_bits & AB_mid) == 0) { + // No middle range bits: a binary alpha image. + _alpha_mode = EggRenderMode::AM_binary; + + } else if ((_alpha_bits & AB_one) != 0) { + // At least some opaque bits: a dual alpha image. + _alpha_mode = EggRenderMode::AM_dual; + + } else { + // No opaque bits; just use regular alpha blending. + _alpha_mode = EggRenderMode::AM_blend; + } + } + } } //////////////////////////////////////////////////////////////////// @@ -1133,7 +1220,8 @@ write_datagram(BamWriter *writer, Datagram &datagram) { datagram.add_bool(_is_surprise); datagram.add_bool(_ever_read_image); datagram.add_bool(_forced_grayscale); - datagram.add_bool(_forced_unalpha); + datagram.add_uint8(_alpha_bits); + datagram.add_int16((int)_alpha_mode); // We don't write out _explicitly_assigned_groups; this is re-read // from the .txa file each time. @@ -1240,7 +1328,14 @@ fillin(DatagramIterator &scan, BamReader *manager) { _is_surprise = scan.get_bool(); _ever_read_image = scan.get_bool(); _forced_grayscale = scan.get_bool(); - _forced_unalpha = scan.get_bool(); + if (pal->_read_pi_version >= 6) { + _alpha_bits = scan.get_uint8(); + _alpha_mode = (EggRenderMode::AlphaMode)scan.get_int16(); + } else { + scan.get_uint8(); + _alpha_bits = -1; + _alpha_mode = EggRenderMode::AM_unspecified; + } _actual_assigned_groups.fillin(scan, manager); diff --git a/pandatool/src/egg-palettize/textureImage.h b/pandatool/src/egg-palettize/textureImage.h index af06863836..e531f5c7aa 100644 --- a/pandatool/src/egg-palettize/textureImage.h +++ b/pandatool/src/egg-palettize/textureImage.h @@ -19,15 +19,16 @@ #ifndef TEXTUREIMAGE_H #define TEXTUREIMAGE_H -#include +#include "pandatoolbase.h" #include "imageFile.h" #include "paletteGroups.h" #include "textureRequest.h" -#include -#include -#include +#include "namable.h" +#include "filename.h" +#include "pnmImage.h" +#include "eggRenderMode.h" #include "pmap.h" #include "pset.h" @@ -73,6 +74,7 @@ public: int get_margin() const; bool is_surprise() const; bool is_used() const; + EggRenderMode::AlphaMode get_alpha_mode() const; SourceTextureImage *get_source(const Filename &filename, const Filename &alpha_filename); @@ -83,6 +85,7 @@ public: const PNMImage &read_source_image(); void read_header(); + bool is_newer_than(const Filename &reference_filename); void write_source_pathnames(ostream &out, int indent_level = 0) const; void write_scale_info(ostream &out, int indent_level = 0); @@ -98,7 +101,7 @@ private: void assign_to_groups(const PaletteGroups &groups); void consider_grayscale(); - void consider_unalpha(); + void consider_alpha(); void remove_old_dests(const Dests &a, const Dests &b); void copy_new_dests(const Dests &a, const Dests &b); @@ -109,12 +112,23 @@ private: private: TextureRequest _request; TextureProperties _pre_txa_properties; + EggRenderMode::AlphaMode _pre_txa_alpha_mode; SourceTextureImage *_preferred_source; bool _is_surprise; bool _ever_read_image; bool _forced_grayscale; - bool _forced_unalpha; + + enum AlphaBits { + // consider_alpha() sets alpha_bits to the union of all of these + // pixel values that might be found in the alpha channel. + AB_one = 0x01, + AB_mid = 0x02, + AB_zero = 0x04, + AB_all = 0x07 // == AB_zero | AB_mid | AB_one + }; + int _alpha_bits; + EggRenderMode::AlphaMode _alpha_mode; PaletteGroups _explicitly_assigned_groups; PaletteGroups _actual_assigned_groups; diff --git a/pandatool/src/egg-palettize/textureReference.cxx b/pandatool/src/egg-palettize/textureReference.cxx index 36fe0af909..0bdb8a0c0f 100644 --- a/pandatool/src/egg-palettize/textureReference.cxx +++ b/pandatool/src/egg-palettize/textureReference.cxx @@ -297,6 +297,16 @@ update_egg() { nassertv(_placement != (TexturePlacement *)NULL); + // Make sure the alpha mode is set according to what the texture + // image wants. + TextureImage *texture = get_texture(); + if (texture != (TextureImage *)NULL) { + EggRenderMode::AlphaMode am = texture->get_alpha_mode(); + if (am != EggRenderMode::AM_unspecified) { + _egg_tex->set_alpha_mode(am); + } + } + // We check for an OmitReason of OR_none, rather than asking // is_placed(), because in this case we don't want to consider an // OR_solitary texture as having been placed. diff --git a/pandatool/src/egg-palettize/textureRequest.cxx b/pandatool/src/egg-palettize/textureRequest.cxx index 127777ae04..08bbab6dc7 100644 --- a/pandatool/src/egg-palettize/textureRequest.cxx +++ b/pandatool/src/egg-palettize/textureRequest.cxx @@ -35,6 +35,7 @@ TextureRequest() { _force_format = false; _minfilter = EggTexture::FT_unspecified; _magfilter = EggTexture::FT_unspecified; + _alpha_mode = EggRenderMode::AM_unspecified; _omit = false; _margin = 0; _coverage_threshold = 0.0; diff --git a/pandatool/src/egg-palettize/textureRequest.h b/pandatool/src/egg-palettize/textureRequest.h index 3801c93574..1347640dc8 100644 --- a/pandatool/src/egg-palettize/textureRequest.h +++ b/pandatool/src/egg-palettize/textureRequest.h @@ -19,11 +19,12 @@ #ifndef TEXTUREREQUEST_H #define TEXTUREREQUEST_H -#include +#include "pandatoolbase.h" #include "textureProperties.h" -#include +#include "eggTexture.h" +#include "eggRenderMode.h" //////////////////////////////////////////////////////////////////// // Class : TextureRequest @@ -47,6 +48,7 @@ public: bool _force_format; EggTexture::FilterType _minfilter; EggTexture::FilterType _magfilter; + EggRenderMode::AlphaMode _alpha_mode; bool _omit; int _margin; double _coverage_threshold; diff --git a/pandatool/src/egg-palettize/txaLine.cxx b/pandatool/src/egg-palettize/txaLine.cxx index c6c36949db..e76412f2aa 100644 --- a/pandatool/src/egg-palettize/txaLine.cxx +++ b/pandatool/src/egg-palettize/txaLine.cxx @@ -41,6 +41,7 @@ TxaLine() { _num_channels = 0; _format = EggTexture::F_unspecified; _force_format = false; + _alpha_mode = EggRenderMode::AM_unspecified; _got_margin = false; _margin = 0; _got_coverage_threshold = false; @@ -220,24 +221,31 @@ parse(const string &line) { } } else { - // Maybe it's a format name. This suggests an image format, - // but may be overridden to reflect the number of channels in - // the image. - EggTexture::Format format = EggTexture::string_format(word); - if (format != EggTexture::F_unspecified) { - if (!_force_format) { - _format = format; - } + // Maybe it's a group name. + PaletteGroup *group = pal->test_palette_group(word); + if (group != (PaletteGroup *)NULL) { + _palette_groups.insert(group); + } else { - // Maybe it's a group name. - PaletteGroup *group = pal->test_palette_group(word); - if (group != (PaletteGroup *)NULL) { - _palette_groups.insert(group); - + // Maybe it's a format name. This suggests an image format, + // but may be overridden to reflect the number of channels in + // the image. + EggTexture::Format format = EggTexture::string_format(word); + if (format != EggTexture::F_unspecified) { + if (!_force_format) { + _format = format; + } } else { - // Maybe it's an image file request. - if (!parse_image_type_request(word, _color_type, _alpha_type)) { - return false; + // Maybe it's an alpha mode. + EggRenderMode::AlphaMode am = EggRenderMode::string_alpha_mode(word); + if (am != EggRenderMode::AM_unspecified) { + _alpha_mode = am; + + } else { + // Maybe it's an image file request. + if (!parse_image_type_request(word, _color_type, _alpha_type)) { + return false; + } } } } @@ -386,6 +394,10 @@ match_texture(TextureImage *texture) const { request._force_format = _force_format; } + if (_alpha_mode != EggRenderMode::AM_unspecified) { + request._alpha_mode = _alpha_mode; + } + bool got_cont = false; Keywords::const_iterator ki; for (ki = _keywords.begin(); ki != _keywords.end(); ++ki) { diff --git a/pandatool/src/egg-palettize/txaLine.h b/pandatool/src/egg-palettize/txaLine.h index d415575ab6..9df4f88077 100644 --- a/pandatool/src/egg-palettize/txaLine.h +++ b/pandatool/src/egg-palettize/txaLine.h @@ -19,12 +19,13 @@ #ifndef TXALINE_H #define TXALINE_H -#include +#include "pandatoolbase.h" #include "paletteGroups.h" -#include -#include +#include "globPattern.h" +#include "eggTexture.h" +#include "eggRenderMode.h" #include "pvector.h" @@ -69,6 +70,7 @@ private: int _num_channels; EggTexture::Format _format; bool _force_format; + EggRenderMode::AlphaMode _alpha_mode; bool _got_margin; int _margin;