瀏覽代碼

add M_dual and M_binary auto-detect to egg-palettize

David Rose 23 年之前
父節點
當前提交
59b4efd18f

+ 13 - 2
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) {

+ 2 - 2
pandatool/src/egg-palettize/eggPalettize.h

@@ -19,9 +19,9 @@
 #ifndef EGGPALETTIZE_H
 #define EGGPALETTIZE_H
 
-#include <pandatoolbase.h>
+#include "pandatoolbase.h"
 
-#include <eggMultiFilter.h>
+#include "eggMultiFilter.h"
 
 ////////////////////////////////////////////////////////////////////
 //       Class : EggPalettize

+ 1 - 1
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";
       }
       */

+ 9 - 7
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);
   }

+ 2 - 2
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);

+ 128 - 33
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--;
+
+  // _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;
+
+    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;
+          }
+        }
+      }
     }
-    return;
   }
 
-  const PNMImage &source = read_source_image();
-  if (!source.is_valid()) {
-    return;
-  }
+  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--;
 
-  if (!source.has_alpha()) {
-    return;
-  }
+    } 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;
 
-  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;
+      } else {
+        // No opaque bits; just use regular alpha blending.
+        _alpha_mode = EggRenderMode::AM_blend;
       }
     }
   }
-
-  // All alpha pixels in the image were white!
-  _properties._num_channels--;
-  _forced_unalpha = true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -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);
 

+ 20 - 6
pandatool/src/egg-palettize/textureImage.h

@@ -19,15 +19,16 @@
 #ifndef TEXTUREIMAGE_H
 #define TEXTUREIMAGE_H
 
-#include <pandatoolbase.h>
+#include "pandatoolbase.h"
 
 #include "imageFile.h"
 #include "paletteGroups.h"
 #include "textureRequest.h"
 
-#include <namable.h>
-#include <filename.h>
-#include <pnmImage.h>
+#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;

+ 10 - 0
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.

+ 1 - 0
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;

+ 4 - 2
pandatool/src/egg-palettize/textureRequest.h

@@ -19,11 +19,12 @@
 #ifndef TEXTUREREQUEST_H
 #define TEXTUREREQUEST_H
 
-#include <pandatoolbase.h>
+#include "pandatoolbase.h"
 
 #include "textureProperties.h"
 
-#include <eggTexture.h>
+#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;

+ 28 - 16
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) {

+ 5 - 3
pandatool/src/egg-palettize/txaLine.h

@@ -19,12 +19,13 @@
 #ifndef TXALINE_H
 #define TXALINE_H
 
-#include <pandatoolbase.h>
+#include "pandatoolbase.h"
 
 #include "paletteGroups.h"
 
-#include <globPattern.h>
-#include <eggTexture.h>
+#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;