Selaa lähdekoodia

*** empty log message ***

David Rose 25 vuotta sitten
vanhempi
sitoutus
07a1bca3a0
30 muutettua tiedostoa jossa 1483 lisäystä ja 178 poistoa
  1. 5 3
      panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx
  2. 2 0
      pandatool/src/egg-palettize/Sources.pp
  3. 3 0
      pandatool/src/egg-palettize/config_egg_palettize.cxx
  4. 126 0
      pandatool/src/egg-palettize/destTextureImage.cxx
  5. 65 0
      pandatool/src/egg-palettize/destTextureImage.h
  6. 250 21
      pandatool/src/egg-palettize/eggFile.cxx
  7. 21 4
      pandatool/src/egg-palettize/eggFile.h
  8. 66 30
      pandatool/src/egg-palettize/eggPalettize.cxx
  9. 2 1
      pandatool/src/egg-palettize/eggPalettize.h
  10. 109 0
      pandatool/src/egg-palettize/filenameUnifier.cxx
  11. 40 0
      pandatool/src/egg-palettize/filenameUnifier.h
  12. 50 16
      pandatool/src/egg-palettize/imageFile.cxx
  13. 2 1
      pandatool/src/egg-palettize/imageFile.h
  14. 88 0
      pandatool/src/egg-palettize/paletteGroup.cxx
  15. 7 0
      pandatool/src/egg-palettize/paletteGroup.h
  16. 10 0
      pandatool/src/egg-palettize/paletteGroups.cxx
  17. 1 0
      pandatool/src/egg-palettize/paletteGroups.h
  18. 5 3
      pandatool/src/egg-palettize/paletteImage.cxx
  19. 225 35
      pandatool/src/egg-palettize/palettizer.cxx
  20. 18 5
      pandatool/src/egg-palettize/palettizer.h
  21. 0 13
      pandatool/src/egg-palettize/sourceTextureImage.cxx
  22. 1 3
      pandatool/src/egg-palettize/sourceTextureImage.h
  23. 225 15
      pandatool/src/egg-palettize/textureImage.cxx
  24. 16 3
      pandatool/src/egg-palettize/textureImage.h
  25. 67 8
      pandatool/src/egg-palettize/texturePlacement.cxx
  26. 6 1
      pandatool/src/egg-palettize/texturePlacement.h
  27. 39 5
      pandatool/src/egg-palettize/textureReference.cxx
  28. 6 1
      pandatool/src/egg-palettize/textureReference.h
  29. 26 9
      pandatool/src/egg-palettize/txaLine.cxx
  30. 2 1
      pandatool/src/egg-palettize/txaLine.h

+ 5 - 3
panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx

@@ -102,11 +102,13 @@ write_data(xel *array, xelval *) {
    */
    */
   cinfo.image_width = _x_size;      /* image width and height, in pixels */
   cinfo.image_width = _x_size;      /* image width and height, in pixels */
   cinfo.image_height = _y_size;
   cinfo.image_height = _y_size;
-  if (is_grayscale())
+  if (is_grayscale()) {
     cinfo.input_components = 1;
     cinfo.input_components = 1;
-  else
+    cinfo.in_color_space = JCS_GRAYSCALE;
+  } else {
     cinfo.input_components = 3;
     cinfo.input_components = 3;
-  cinfo.in_color_space = JCS_RGB;       /* colorspace of input image */
+    cinfo.in_color_space = JCS_RGB;
+  }
   /* Now use the library's routine to set default compression parameters.
   /* Now use the library's routine to set default compression parameters.
    * (You must set at least cinfo.in_color_space before calling this,
    * (You must set at least cinfo.in_color_space before calling this,
    * since the defaults depend on the source color space.)
    * since the defaults depend on the source color space.)

+ 2 - 0
pandatool/src/egg-palettize/Sources.pp

@@ -9,7 +9,9 @@
 
 
   #define SOURCES \
   #define SOURCES \
     config_egg_palettize.cxx config_egg_palettize.h \
     config_egg_palettize.cxx config_egg_palettize.h \
+    destTextureImage.cxx destTextureImage.h \
     eggFile.cxx eggFile.h eggPalettize.cxx eggPalettize.h \
     eggFile.cxx eggFile.h eggPalettize.cxx eggPalettize.h \
+    filenameUnifier.cxx filenameUnifier.h \
     imageFile.cxx imageFile.h omitReason.cxx omitReason.h \
     imageFile.cxx imageFile.h omitReason.cxx omitReason.h \
     paletteGroup.h paletteGroup.cxx \
     paletteGroup.h paletteGroup.cxx \
     paletteGroups.h paletteGroups.cxx paletteImage.h paletteImage.cxx \
     paletteGroups.h paletteGroups.cxx paletteImage.h paletteImage.cxx \

+ 3 - 0
pandatool/src/egg-palettize/config_egg_palettize.cxx

@@ -12,6 +12,7 @@
 #include "textureProperties.h"
 #include "textureProperties.h"
 #include "imageFile.h"
 #include "imageFile.h"
 #include "sourceTextureImage.h"
 #include "sourceTextureImage.h"
+#include "destTextureImage.h"
 #include "textureImage.h"
 #include "textureImage.h"
 #include "paletteImage.h"
 #include "paletteImage.h"
 #include "texturePlacement.h"
 #include "texturePlacement.h"
@@ -31,6 +32,7 @@ ConfigureFn(config_egg_palettize) {
   TextureProperties::init_type();
   TextureProperties::init_type();
   ImageFile::init_type();
   ImageFile::init_type();
   SourceTextureImage::init_type();
   SourceTextureImage::init_type();
+  DestTextureImage::init_type();
   TextureImage::init_type();
   TextureImage::init_type();
   PaletteImage::init_type();
   PaletteImage::init_type();
   TexturePlacement::init_type();
   TexturePlacement::init_type();
@@ -46,6 +48,7 @@ ConfigureFn(config_egg_palettize) {
   TextureReference::register_with_read_factory();
   TextureReference::register_with_read_factory();
   TextureProperties::register_with_read_factory();
   TextureProperties::register_with_read_factory();
   SourceTextureImage::register_with_read_factory();
   SourceTextureImage::register_with_read_factory();
+  DestTextureImage::register_with_read_factory();
   TextureImage::register_with_read_factory();
   TextureImage::register_with_read_factory();
   PaletteImage::register_with_read_factory();
   PaletteImage::register_with_read_factory();
   TexturePlacement::register_with_read_factory();
   TexturePlacement::register_with_read_factory();

+ 126 - 0
pandatool/src/egg-palettize/destTextureImage.cxx

@@ -0,0 +1,126 @@
+// Filename: destTextureImage.cxx
+// Created by:  drose (05Dec00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "destTextureImage.h"
+#include "texturePlacement.h"
+#include "textureImage.h"
+
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamReader.h>
+#include <bamWriter.h>
+
+TypeHandle DestTextureImage::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::Default Constructor
+//       Access: Private
+//  Description: The default constructor is only for the convenience
+//               of the Bam reader.
+////////////////////////////////////////////////////////////////////
+DestTextureImage::
+DestTextureImage() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+DestTextureImage::
+DestTextureImage(TexturePlacement *placement) {
+  TextureImage *texture = placement->get_texture();
+  _properties = texture->get_properties();
+  _size_known = true;
+  _x_size = texture->get_x_size();
+  _y_size = texture->get_y_size();
+  set_filename(placement->get_group(), texture->get_name());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::copy
+//       Access: Public
+//  Description: Unconditionally copies the source texture into the
+//               appropriate filename.
+////////////////////////////////////////////////////////////////////
+void DestTextureImage::
+copy(TextureImage *texture) {
+  PNMImage image = texture->get_dest_image();
+  write(image);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::copy_if_stale
+//       Access: Public
+//  Description: Copies the source texture into the appropriate
+//               filename only if the indicated old reference, which
+//               represents the way it was last copied, is now
+//               out-of-date.
+////////////////////////////////////////////////////////////////////
+void DestTextureImage::
+copy_if_stale(const DestTextureImage *other, TextureImage *texture) {
+  if (other->get_x_size() != get_x_size() ||
+      other->get_y_size() != get_y_size() ||
+      other->get_num_channels() != get_num_channels()) {
+    copy(texture);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::register_with_read_factory
+//       Access: Public, Static
+//  Description: Registers the current object as something that can be
+//               read from a Bam file.
+////////////////////////////////////////////////////////////////////
+void DestTextureImage::
+register_with_read_factory() {
+  BamReader::get_factory()->
+    register_factory(get_class_type(), make_DestTextureImage);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::write_datagram
+//       Access: Public, Virtual
+//  Description: Fills the indicated datagram up with a binary
+//               representation of the current object, in preparation
+//               for writing to a Bam file.
+////////////////////////////////////////////////////////////////////
+void DestTextureImage::
+write_datagram(BamWriter *writer, Datagram &datagram) {
+  ImageFile::write_datagram(writer, datagram);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::make_DestTextureImage
+//       Access: Protected
+//  Description: This method is called by the BamReader when an object
+//               of this type is encountered in a Bam file; it should
+//               allocate and return a new object with all the data
+//               read.
+////////////////////////////////////////////////////////////////////
+TypedWriteable* DestTextureImage::
+make_DestTextureImage(const FactoryParams &params) {
+  DestTextureImage *me = new DestTextureImage;
+  BamReader *manager;
+  Datagram packet;
+
+  parse_params(params, manager, packet);
+  DatagramIterator scan(packet);
+
+  me->fillin(scan, manager);
+  return me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DestTextureImage::fillin
+//       Access: Protected
+//  Description: Reads the binary data from the given datagram
+//               iterator, which was written by a previous call to
+//               write_datagram().
+////////////////////////////////////////////////////////////////////
+void DestTextureImage::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  ImageFile::fillin(scan, manager);
+}

+ 65 - 0
pandatool/src/egg-palettize/destTextureImage.h

@@ -0,0 +1,65 @@
+// Filename: destTextureImage.h
+// Created by:  drose (05Dec00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef DESTTEXTUREIMAGE_H
+#define DESTTEXTUREIMAGE_H
+
+#include <pandatoolbase.h>
+
+#include "imageFile.h"
+
+class TexturePlacement;
+class TextureImage;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : DestTextureImage
+// Description : This represents a texture filename as it has been
+//               resized and copied to the map directory (e.g. for an
+//               unplaced texture).
+////////////////////////////////////////////////////////////////////
+class DestTextureImage : public ImageFile {
+private:
+  DestTextureImage();
+
+public:
+  DestTextureImage(TexturePlacement *placement);
+
+  void copy(TextureImage *texture);
+  void copy_if_stale(const DestTextureImage *other, TextureImage *texture);
+
+  // The TypedWriteable interface follows.
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *writer, Datagram &datagram); 
+
+protected:
+  static TypedWriteable *make_DestTextureImage(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    ImageFile::init_type();
+    register_type(_type_handle, "DestTextureImage",
+		  ImageFile::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+INLINE ostream &
+operator << (ostream &out, const DestTextureImage &dest) {
+  dest.output_filename(out);
+  return out;
+}
+
+#endif
+

+ 250 - 21
pandatool/src/egg-palettize/eggFile.cxx

@@ -8,6 +8,7 @@
 #include "paletteGroup.h"
 #include "paletteGroup.h"
 #include "texturePlacement.h"
 #include "texturePlacement.h"
 #include "palettizer.h"
 #include "palettizer.h"
+#include "filenameUnifier.h"
 
 
 #include <eggData.h>
 #include <eggData.h>
 #include <eggTextureCollection.h>
 #include <eggTextureCollection.h>
@@ -26,6 +27,10 @@ TypeHandle EggFile::_type_handle;
 EggFile::
 EggFile::
 EggFile() {
 EggFile() {
   _data = (EggData *)NULL;
   _data = (EggData *)NULL;
+  _default_group = (PaletteGroup *)NULL;
+  _is_surprise = true;
+  _is_stale = true;
+  _first_txa_match = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -41,6 +46,11 @@ from_command_line(EggData *data,
   _data = data;
   _data = data;
   _source_filename = source_filename;
   _source_filename = source_filename;
   _dest_filename = dest_filename;
   _dest_filename = dest_filename;
+
+  // We save the default PaletteGroup at this point, because the egg
+  // file inherits the default group that was in effect when it was
+  // specified on the command line.
+  _default_group = pal->get_default_group();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -70,7 +80,7 @@ scan_textures() {
     EggTexture *egg_tex = (*eti);
     EggTexture *egg_tex = (*eti);
 
 
     TextureReference *ref = new TextureReference;
     TextureReference *ref = new TextureReference;
-    ref->from_egg(_data, egg_tex);
+    ref->from_egg(this, _data, egg_tex);
 
 
     _textures.push_back(ref);
     _textures.push_back(ref);
   }
   }
@@ -93,6 +103,39 @@ get_textures(set<TextureImage *> &result) const {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::pre_txa_file
+//       Access: Public
+//  Description: Does some processing prior to scanning the .txa file.
+////////////////////////////////////////////////////////////////////
+void EggFile::
+pre_txa_file() {
+  _is_surprise = true;
+  _first_txa_match = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::match_txa_groups
+//       Access: Public
+//  Description: Adds the indicated set of groups, read from the .txa
+//               file, to the set of groups to which the egg file is
+//               assigned.
+////////////////////////////////////////////////////////////////////
+void EggFile::
+match_txa_groups(const PaletteGroups &groups) {
+  if (_first_txa_match) {
+    // If this is the first line we matched in the .txa file, clear
+    // the set of groups we'd matched from before.  We don't clear
+    // until we match a line in the .txa file, because if we don't
+    // match any lines we still want to remember what groups we used
+    // to be assigned to.
+    _explicitly_assigned_groups.clear();
+    _first_txa_match = false;
+  }
+
+  _explicitly_assigned_groups.make_union(_explicitly_assigned_groups, groups);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggFile::post_txa_file
 //     Function: EggFile::post_txa_file
 //       Access: Public
 //       Access: Public
@@ -102,22 +145,90 @@ get_textures(set<TextureImage *> &result) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void EggFile::
 void EggFile::
 post_txa_file() {
 post_txa_file() {
-  if (_assigned_groups.empty()) {
-    // If the egg file has been assigned to no groups, we have to
-    // assign it to something.
-    _assigned_groups.insert(pal->get_default_group());
-  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: EggFile::get_groups
+//     Function: EggFile::get_explicit_groups
 //       Access: Public
 //       Access: Public
 //  Description: Returns the set of PaletteGroups that the egg file
 //  Description: Returns the set of PaletteGroups that the egg file
 //               has been explicitly assigned to in the .txa file.
 //               has been explicitly assigned to in the .txa file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 const PaletteGroups &EggFile::
 const PaletteGroups &EggFile::
-get_groups() const {
-  return _assigned_groups;
+get_explicit_groups() const {
+  return _explicitly_assigned_groups;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::get_default_group
+//       Access: Public
+//  Description: Returns the PaletteGroup that was specified as the
+//               default group on the command line at the time the egg
+//               file last appeared on the command line.
+////////////////////////////////////////////////////////////////////
+PaletteGroup *EggFile::
+get_default_group() const {
+  return _default_group;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::get_complete_groups
+//       Access: Public
+//  Description: Returns the complete set of PaletteGroups that the
+//               egg file is assigned to.  This is the set of all the
+//               groups it is explicitly assigned to, plus all the
+//               groups that these groups depend on.
+////////////////////////////////////////////////////////////////////
+const PaletteGroups &EggFile::
+get_complete_groups() const {
+  return _complete_groups;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::clear_surprise
+//       Access: Public
+//  Description: Removes the 'surprise' flag; this file has been
+//               successfully matched against a line in the .txa file.
+////////////////////////////////////////////////////////////////////
+void EggFile::
+clear_surprise() {
+  _is_surprise = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::is_surprise
+//       Access: Public
+//  Description: Returns true if this particular egg file is a
+//               'surprise', i.e. it wasn't matched by a line in the
+//               .txa file that didn't include the keyword 'cont'.
+////////////////////////////////////////////////////////////////////
+bool EggFile::
+is_surprise() const {
+  return _is_surprise;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::mark_stale
+//       Access: Public
+//  Description: Marks this particular egg file as stale, meaning that
+//               something has changed, such as the location of a
+//               texture within its palette, which causes the egg file
+//               to need to be regenerated.
+////////////////////////////////////////////////////////////////////
+void EggFile::
+mark_stale() {
+  _is_stale = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::is_stale
+//       Access: Public
+//  Description: Returns true if the egg file needs to be updated,
+//               i.e. some palettizations have changed affecting it,
+//               or false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggFile::
+is_stale() const {
+  return _is_stale;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -132,16 +243,25 @@ get_groups() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void EggFile::
 void EggFile::
 build_cross_links() {
 build_cross_links() {
+  if (_explicitly_assigned_groups.empty()) {
+    // If the egg file has been assigned to no groups, we have to
+    // assign it to something.
+    _complete_groups.clear();
+    _complete_groups.insert(_default_group);
+    _complete_groups.make_complete(_complete_groups);
+
+  } else {
+    _complete_groups.make_complete(_explicitly_assigned_groups);
+  }
+
   Textures::const_iterator ti;
   Textures::const_iterator ti;
   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
     (*ti)->get_texture()->note_egg_file(this);
     (*ti)->get_texture()->note_egg_file(this);
   }
   }
 
 
-  _assigned_groups.make_complete(_assigned_groups);
-
   PaletteGroups::const_iterator gi;
   PaletteGroups::const_iterator gi;
-  for (gi = _assigned_groups.begin();
-       gi != _assigned_groups.end();
+  for (gi = _complete_groups.begin();
+       gi != _complete_groups.end();
        ++gi) {
        ++gi) {
     (*gi)->increment_egg_count();
     (*gi)->increment_egg_count();
   }
   }
@@ -174,7 +294,7 @@ choose_placements() {
     } else {
     } else {
       // We need to select a new TexturePlacement.
       // We need to select a new TexturePlacement.
       PaletteGroups groups;
       PaletteGroups groups;
-      groups.make_intersection(get_groups(), texture->get_groups());
+      groups.make_intersection(get_complete_groups(), texture->get_groups());
       
       
       // Now groups is the set of groups that the egg file requires,
       // Now groups is the set of groups that the egg file requires,
       // which also happen to include the texture.  It better not be
       // which also happen to include the texture.  It better not be
@@ -195,6 +315,17 @@ choose_placements() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::has_data
+//       Access: Public
+//  Description: Returns true if the EggData for this EggFile has ever
+//               been loaded.
+////////////////////////////////////////////////////////////////////
+bool EggFile::
+has_data() const {
+  return (_data != (EggData *)NULL);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggFile::update_egg
 //     Function: EggFile::update_egg
 //       Access: Public
 //       Access: Public
@@ -211,6 +342,88 @@ update_egg() {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::read_egg
+//       Access: Public
+//  Description: Reads in the egg file from its _source_filename.  It
+//               is only valid to call this if it has not already been
+//               read in, e.g. from the command line.  Returns true if
+//               successful, false if there is an error.
+////////////////////////////////////////////////////////////////////
+bool EggFile::
+read_egg() {
+  nassertr(_data == (EggData *)NULL, false);
+  nassertr(!_source_filename.empty(), false);
+
+  if (!_source_filename.exists()) {
+    nout << _source_filename << " does not exist.\n";
+    return false;
+  }
+
+  EggData *data = new EggData;
+  if (!data->read(_source_filename)) {
+    // Failure reading.
+    delete data;
+    return false;
+  }
+
+  if (!data->resolve_externals()) {
+    // Failure reading an external.
+    delete data;
+    return false;
+  }
+
+  _data = data;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::write_egg
+//       Access: Public
+//  Description: Writes out the egg file to its _dest_filename.
+//               Returns true if successful, false if there is an
+//               error.
+////////////////////////////////////////////////////////////////////
+bool EggFile::
+write_egg() {
+  nassertr(_data != (EggData *)NULL, false);
+  nassertr(!_dest_filename.empty(), false);
+
+  _dest_filename.make_dir();
+  nout << "Writing " << _dest_filename << "\n";
+  if (!_data->write_egg(_dest_filename)) {
+    // Some error while writing.  Most unusual.
+    _is_stale = true;
+    return false;
+  }
+
+  _is_stale = false;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggFile::write_description
+//       Access: Public
+//  Description: Writes a one-line description of the egg file and its
+//               group assignments to the indicated output stream.
+////////////////////////////////////////////////////////////////////
+void EggFile::
+write_description(ostream &out, int indent_level) const {
+  indent(out, indent_level) << get_name() << ": ";
+  if (_explicitly_assigned_groups.empty()) {
+    if (_default_group != (PaletteGroup *)NULL) {
+      out << _default_group->get_name();
+    }
+  } else {
+    out << _explicitly_assigned_groups;
+  }
+
+  if (is_stale()) {
+    out << " (needs update)";
+  }
+  out << "\n";
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: EggFile::write_texture_refs
 //     Function: EggFile::write_texture_refs
 //       Access: Public
 //       Access: Public
@@ -251,8 +464,8 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
 
 
   // We don't write out _data; that needs to be reread each session.
   // We don't write out _data; that needs to be reread each session.
 
 
-  datagram.add_string(_source_filename);
-  datagram.add_string(_dest_filename);
+  datagram.add_string(FilenameUnifier::make_bam_filename(_source_filename));
+  datagram.add_string(FilenameUnifier::make_bam_filename(_dest_filename));
 
 
   datagram.add_uint32(_textures.size());
   datagram.add_uint32(_textures.size());
   Textures::iterator ti;
   Textures::iterator ti;
@@ -260,7 +473,14 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
     writer->write_pointer(datagram, (*ti));
     writer->write_pointer(datagram, (*ti));
   }
   }
 
 
-  _assigned_groups.write_datagram(writer, datagram);
+  _explicitly_assigned_groups.write_datagram(writer, datagram);
+  writer->write_pointer(datagram, _default_group);
+
+  // We don't write out _complete_groups; that is recomputed each
+  // session.
+
+  datagram.add_bool(_is_surprise);
+  datagram.add_bool(_is_stale);
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -274,7 +494,7 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int EggFile::
 int EggFile::
 complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
 complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
-  nassertr((int)plist.size() >= _num_textures, 0);
+  nassertr((int)plist.size() >= _num_textures + 1, 0);
   int index = 0;
   int index = 0;
 
 
   int i;
   int i;
@@ -286,6 +506,11 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
     index++;
     index++;
   }
   }
 
 
+  if (plist[index] != (TypedWriteable *)NULL) {
+    DCAST_INTO_R(_default_group, plist[index], index);
+  }
+  index++;
+
   return index;
   return index;
 }
 }
 
 
@@ -320,11 +545,15 @@ make_EggFile(const FactoryParams &params) {
 void EggFile::
 void EggFile::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   set_name(scan.get_string());
   set_name(scan.get_string());
-  _source_filename = scan.get_string();
-  _dest_filename = scan.get_string();
+  _source_filename = FilenameUnifier::get_bam_filename(scan.get_string());
+  _dest_filename = FilenameUnifier::get_bam_filename(scan.get_string());
 
 
   _num_textures = scan.get_uint32();
   _num_textures = scan.get_uint32();
   manager->read_pointers(scan, this, _num_textures);
   manager->read_pointers(scan, this, _num_textures);
 
 
-  _assigned_groups.fillin(scan, manager);
+  _explicitly_assigned_groups.fillin(scan, manager);
+  manager->read_pointer(scan, this);  // _default_group
+
+  _is_surprise = scan.get_bool();
+  _is_stale = scan.get_bool();
 }
 }

+ 21 - 4
pandatool/src/egg-palettize/eggFile.h

@@ -40,15 +40,29 @@ public:
   void scan_textures();
   void scan_textures();
   void get_textures(set<TextureImage *> &result) const;
   void get_textures(set<TextureImage *> &result) const;
 
 
+  void pre_txa_file();
+  void match_txa_groups(const PaletteGroups &groups);
   void post_txa_file();
   void post_txa_file();
 
 
-  const PaletteGroups &get_groups() const;
+  const PaletteGroups &get_explicit_groups() const;
+  PaletteGroup *get_default_group() const;
+  const PaletteGroups &get_complete_groups() const;
+  void clear_surprise();
+  bool is_surprise() const;
+
+  void mark_stale();
+  bool is_stale() const;
 
 
   void build_cross_links();
   void build_cross_links();
   void choose_placements();
   void choose_placements();
 
 
+  bool has_data() const;
+  
   void update_egg();
   void update_egg();
+  bool read_egg();
+  bool write_egg();
 
 
+  void write_description(ostream &out, int indent_level = 0) const;
   void write_texture_refs(ostream &out, int indent_level = 0) const;
   void write_texture_refs(ostream &out, int indent_level = 0) const;
 
 
 private:
 private:
@@ -59,7 +73,12 @@ private:
   typedef vector<TextureReference *> Textures;
   typedef vector<TextureReference *> Textures;
   Textures _textures;
   Textures _textures;
 
 
-  PaletteGroups _assigned_groups;
+  bool _first_txa_match;
+  PaletteGroups _explicitly_assigned_groups;
+  PaletteGroup *_default_group;
+  PaletteGroups _complete_groups;
+  bool _is_surprise;
+  bool _is_stale;
 
 
 
 
   // The TypedWriteable interface follows.
   // The TypedWriteable interface follows.
@@ -95,8 +114,6 @@ public:
 
 
 private:
 private:
   static TypeHandle _type_handle;
   static TypeHandle _type_handle;
-
-  friend class TxaLine;
 };
 };
 
 
 #endif
 #endif

+ 66 - 30
pandatool/src/egg-palettize/eggPalettize.cxx

@@ -7,6 +7,7 @@
 #include "palettizer.h"
 #include "palettizer.h"
 #include "eggFile.h"
 #include "eggFile.h"
 #include "string_utils.h"
 #include "string_utils.h"
+#include "filenameUnifier.h"
 
 
 #include <eggData.h>
 #include <eggData.h>
 #include <bamFile.h>
 #include <bamFile.h>
@@ -76,7 +77,7 @@ EggPalettize() : EggMultiFilter(true) {
     ("dm", "dirname", 0, 
     ("dm", "dirname", 0, 
      "The directory in which to place all maps: generated palettes, "
      "The directory in which to place all maps: generated palettes, "
      "as well as images which were not placed on palettes "
      "as well as images which were not placed on palettes "
-     "(but may have been resized).  If this contains the string %s, "
+     "(but may have been resized).  If this contains the string %g, "
      "this will be replaced with the \"dir\" string associated with a "
      "this will be replaced with the \"dir\" string associated with a "
      "palette group.",
      "palette group.",
      &EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname);
      &EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname);
@@ -97,7 +98,13 @@ EggPalettize() : EggMultiFilter(true) {
      &EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir);
      &EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir);
   
   
   add_option
   add_option
-    ("f", "", 0, 
+    ("all", "", 0, 
+     "Consider all the textures referenced in all egg files that have "
+     "ever been palettized, not just the egg files that appear on "
+     "the command line.",
+     &EggPalettize::dispatch_none, &_all_textures);
+  add_option
+    ("opt", "", 0, 
      "Force an optimal packing.  By default, textures are added to "
      "Force an optimal packing.  By default, textures are added to "
      "existing palettes without disturbing them, which can lead to "
      "existing palettes without disturbing them, which can lead to "
      "suboptimal packing.  Including this switch forces the palettes "
      "suboptimal packing.  Including this switch forces the palettes "
@@ -105,7 +112,7 @@ EggPalettize() : EggMultiFilter(true) {
      "may invalidate other egg files which share this palette.",
      "may invalidate other egg files which share this palette.",
      &EggPalettize::dispatch_none, &_force_optimal);
      &EggPalettize::dispatch_none, &_force_optimal);
   add_option
   add_option
-    ("F", "", 0, 
+    ("redo", "", 0, 
      "Force a redo of everything.  This is useful in case something "
      "Force a redo of everything.  This is useful in case something "
      "has gotten out of sync and the old palettes are just bad.",
      "has gotten out of sync and the old palettes are just bad.",
      &EggPalettize::dispatch_none, &_force_redo_all);
      &EggPalettize::dispatch_none, &_force_redo_all);
@@ -114,12 +121,10 @@ EggPalettize() : EggMultiFilter(true) {
      "Resize mostly-empty palettes to their minimal size.",
      "Resize mostly-empty palettes to their minimal size.",
      &EggPalettize::dispatch_none, &_optimal_resize);
      &EggPalettize::dispatch_none, &_optimal_resize);
   add_option
   add_option
-    ("t", "", 0, 
-     "Touch any additional egg files that share this palette and will "
-     "need to be refreshed, but were not included on the command "
-     "line.  Presumably a future make pass will cause them to be run "
-     "through egg-palettize again.",
-     &EggPalettize::dispatch_none, &_touch_eggs);
+    ("egg", "", 0, 
+     "Regenerate all egg files that need modification, even those that "
+     "aren't named on the command line.",
+     &EggPalettize::dispatch_none, &_redo_eggs);
 
 
   add_option
   add_option
     ("nolock", "", 0, 
     ("nolock", "", 0, 
@@ -244,38 +249,40 @@ run() {
     exit(1);
     exit(1);
   }
   }
 
 
-  Filename pi_filename = _txa_filename;
-  pi_filename.set_extension("boo");
+  FilenameUnifier::set_txa_filename(_txa_filename);
+
+  Filename state_filename = _txa_filename;
+  state_filename.set_extension("boo");
 
 
-  BamFile pi_file;
+  BamFile state_file;
 
 
-  if (!pi_filename.exists()) {
-    nout << pi_filename << " does not exist; starting palettization from scratch.\n";
+  if (!state_filename.exists()) {
+    nout << state_filename << " does not exist; starting palettization from scratch.\n";
     pal = new Palettizer;
     pal = new Palettizer;
 
 
   } else {
   } else {
     // Read the Palettizer object from the Bam file written
     // Read the Palettizer object from the Bam file written
     // previously.  This will recover all of the state saved from the
     // previously.  This will recover all of the state saved from the
     // past session.
     // past session.
-    if (!pi_file.open_read(pi_filename)) {
-      nout << pi_filename << " exists, but cannot be read.  Perhaps you should remove it so a new one can be created.\n";
+    if (!state_file.open_read(state_filename)) {
+      nout << state_filename << " exists, but cannot be read.  Perhaps you should remove it so a new one can be created.\n";
       exit(1);
       exit(1);
     }
     }
 
 
-    TypedWriteable *obj = pi_file.read_object();
-    if (obj == (TypedWriteable *)NULL || !pi_file.resolve()) {
-      nout << pi_filename << " exists, but appears to be corrupt.  Perhaps you should remove it so a new one can be created.\n";
+    TypedWriteable *obj = state_file.read_object();
+    if (obj == (TypedWriteable *)NULL || !state_file.resolve()) {
+      nout << state_filename << " exists, but appears to be corrupt.  Perhaps you should remove it so a new one can be created.\n";
       exit(1);
       exit(1);
     }
     }
 
 
     if (!obj->is_of_type(Palettizer::get_class_type())) {
     if (!obj->is_of_type(Palettizer::get_class_type())) {
-      nout << pi_filename << " exists, but does not appear to be "
+      nout << state_filename << " exists, but does not appear to be "
 	   << "an egg-palettize output file.  Perhaps you "
 	   << "an egg-palettize output file.  Perhaps you "
 	   << "should remove it so a new one can be created.\n";
 	   << "should remove it so a new one can be created.\n";
       exit(1);
       exit(1);
     }
     }
 
 
-    pi_file.close();
+    state_file.close();
 
 
     pal = DCAST(Palettizer, obj);
     pal = DCAST(Palettizer, obj);
   }
   }
@@ -285,15 +292,26 @@ run() {
     exit(0);
     exit(0);
   }
   }
 
 
+  bool okflag = true;
+
+  pal->read_txa_file(_txa_filename);
+
   if (_got_default_groupname) {
   if (_got_default_groupname) {
     pal->_default_groupname = _default_groupname;
     pal->_default_groupname = _default_groupname;
   } else {
   } else {
     pal->_default_groupname = _txa_filename.get_basename_wo_extension();
     pal->_default_groupname = _txa_filename.get_basename_wo_extension();
   }
   }
 
 
-  TxaFile txa_file;
-  if (!txa_file.read(_txa_filename)) {
-    exit(1);
+  if (_got_default_groupdir) {
+    pal->_default_groupdir = _default_groupdir;
+  }
+
+  if (_got_map_dirname) {
+    pal->_map_dirname = _map_dirname;
+  }
+  if (_got_rel_dirname) {
+    pal->_rel_dirname = _rel_dirname;
+    FilenameUnifier::set_rel_dirname(_rel_dirname);
   }
   }
 
 
   Eggs::const_iterator ei;
   Eggs::const_iterator ei;
@@ -309,18 +327,36 @@ run() {
     pal->_command_line_eggs.push_back(egg_file);
     pal->_command_line_eggs.push_back(egg_file);
   }
   }
 
 
-  pal->run(txa_file);
+  if (_all_textures) {
+    pal->process_all();
+  } else {
+    pal->process_command_line_eggs();
+  }
+
+  if (_redo_eggs) {
+    if (!pal->read_stale_eggs()) {
+      okflag = false;
+    }
+  }
+
+  pal->generate_images();
 
 
-  if (!pi_file.open_write(pi_filename) ||
-      !pi_file.write_object(pal)) {
-    nout << "Unable to write palettization information to " << pi_filename
+  if (!pal->write_eggs()) {
+    okflag = false;
+  }
+
+  if (!state_file.open_write(state_filename) ||
+      !state_file.write_object(pal)) {
+    nout << "Unable to write palettization information to " << state_filename
 	 << "\n";
 	 << "\n";
     exit(1);
     exit(1);
   }
   }
 
 
-  pi_file.close();
+  state_file.close();
 
 
-  write_eggs();
+  if (!okflag) {
+    exit(1);
+  }
 }
 }
 
 
 int 
 int 

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

@@ -43,10 +43,11 @@ private:
   // They're not saved for future sessions.
   // They're not saved for future sessions.
   bool _report_pi;
   bool _report_pi;
   bool _statistics_only;
   bool _statistics_only;
+  bool _all_textures; 
   bool _force_optimal;
   bool _force_optimal;
   bool _force_redo_all;
   bool _force_redo_all;
   bool _optimal_resize;
   bool _optimal_resize;
-  bool _touch_eggs;
+  bool _redo_eggs;
   bool _dont_lock_pi;
   bool _dont_lock_pi;
 
 
   bool _describe_input_file;
   bool _describe_input_file;

+ 109 - 0
pandatool/src/egg-palettize/filenameUnifier.cxx

@@ -0,0 +1,109 @@
+// Filename: filenameUnifier.cxx
+// Created by:  drose (05Dec00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "filenameUnifier.h"
+
+#include <executionEnvironment.h>
+
+Filename FilenameUnifier::_txa_filename;
+Filename FilenameUnifier::_txa_dir;
+Filename FilenameUnifier::_rel_dirname;
+
+////////////////////////////////////////////////////////////////////
+//     Function: FilenameUnifier::set_txa_filename
+//       Access: Public, Static
+//  Description: Notes the filename the .txa file was found in.  This
+//               may have come from the command line, or it may have
+//               been implicitly located.  This has other implications
+//               for the FilenameUnifier, particularly in locating the bam
+//               file that saves the filenameUnifier state from last
+//               session.
+////////////////////////////////////////////////////////////////////
+void FilenameUnifier::
+set_txa_filename(const Filename &txa_filename) {
+  _txa_filename = txa_filename;
+  _txa_dir = txa_filename.get_dirname();
+  _txa_dir.make_canonical();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FilenameUnifier::set_rel_dirname
+//       Access: Public, Static
+//  Description: Sets the name of the directory that texture filenames
+//               will be written relative to, when generating egg
+//               files.  This is not the directory the textures are
+//               actually written to (see set_map_dirname()), but
+//               rather is the name of some directory above that,
+//               which will be the starting point for the pathnames
+//               written to the egg files.  If this is empty, the full
+//               pathnames will be written to the egg files.
+////////////////////////////////////////////////////////////////////
+void FilenameUnifier::
+set_rel_dirname(const Filename &rel_dirname) {
+  _rel_dirname = rel_dirname;
+  if (!_rel_dirname.empty()) {
+    _rel_dirname.make_canonical();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FilenameUnifier::make_bam_filename
+//       Access: Public, Static
+//  Description: Returns a new filename that's made relative to the
+//               bam file itself, suitable for writing to the bam file.
+////////////////////////////////////////////////////////////////////
+Filename FilenameUnifier::
+make_bam_filename(Filename filename) {
+  filename.make_canonical();
+  filename.make_relative_to(_txa_dir);
+  return filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FilenameUnifier::get_bam_filename
+//       Access: Public, Static
+//  Description: Returns an absolute pathname based on the given
+//               relative pathname, presumably read from the bam file
+//               and relative to the bam file.
+////////////////////////////////////////////////////////////////////
+Filename FilenameUnifier::
+get_bam_filename(Filename filename) {
+  if (!filename.empty()) {
+    filename.make_absolute(_txa_dir);
+  }
+  return filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FilenameUnifier::make_egg_filename
+//       Access: Public, Static
+//  Description: Returns a new filename that's made relative to the
+//               rel_directory, suitable for writing out within egg
+//               files.
+////////////////////////////////////////////////////////////////////
+Filename FilenameUnifier::
+make_egg_filename(Filename filename) {
+  if (!filename.empty()) {
+    filename.make_canonical();
+    filename.make_relative_to(_rel_dirname);
+  }
+  return filename;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FilenameUnifier::make_user_filename
+//       Access: Public, Static
+//  Description: Returns a new filename that's made relative to the
+//               current directory, suitable for reporting to the
+//               user.
+////////////////////////////////////////////////////////////////////
+Filename FilenameUnifier::
+make_user_filename(Filename filename) {
+  if (!filename.empty()) {
+    filename.make_canonical();
+    filename.make_relative_to(ExecutionEnvironment::get_cwd());
+  }
+  return filename;
+}

+ 40 - 0
pandatool/src/egg-palettize/filenameUnifier.h

@@ -0,0 +1,40 @@
+// Filename: filenameUnifier.h
+// Created by:  drose (05Dec00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef FILENAMEUNIFIER_H
+#define FILENAMEUNIFIER_H
+
+#include <pandatoolbase.h>
+
+#include <filename.h>
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : FilenameUnifier
+// Description : This static class does the job of converting
+//               filenames from relative to absolute to canonical or
+//               whatever is appropriate.  Its main purpose is to
+//               allow us to write relative pathnames to the bam file
+//               and turn them back into absolute pathnames on read,
+//               so that a given bam file does not get tied to
+//               absolute pathnames.
+////////////////////////////////////////////////////////////////////
+class FilenameUnifier {
+public:
+  static void set_txa_filename(const Filename &txa_filename);
+  static void set_rel_dirname(const Filename &rel_dirname);
+
+  static Filename make_bam_filename(Filename filename);
+  static Filename get_bam_filename(Filename filename);
+  static Filename make_egg_filename(Filename filename);
+  static Filename make_user_filename(Filename filename);
+
+private:
+  static Filename _txa_filename;
+  static Filename _txa_dir;
+  static Filename _rel_dirname;
+};
+
+#endif
+

+ 50 - 16
pandatool/src/egg-palettize/imageFile.cxx

@@ -4,6 +4,9 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 
 
 #include "imageFile.h"
 #include "imageFile.h"
+#include "palettizer.h"
+#include "filenameUnifier.h"
+#include "paletteGroup.h"
 
 
 #include <pnmImage.h>
 #include <pnmImage.h>
 #include <pnmFileType.h>
 #include <pnmFileType.h>
@@ -118,8 +121,33 @@ update_properties(const TextureProperties &properties) {
 //               automatically applied.
 //               automatically applied.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ImageFile::
 void ImageFile::
-set_filename(const string &basename) {
-  _filename = basename;
+set_filename(PaletteGroup *group, const string &basename) {
+  // Synthesize the directory name based on the map_dirname set to the
+  // palettizer, and the group's dirname.
+  string dirname;
+  string::iterator pi;
+  pi = pal->_map_dirname.begin();
+  while (pi != pal->_map_dirname.end()) {
+    if (*pi == '%') {
+      ++pi;
+      switch (*pi) {
+      case '%':
+	dirname += '%';
+	break;
+
+      case 'g':
+	if (group != (PaletteGroup *)NULL) {
+	  dirname += group->get_dirname();
+	}
+	break;
+      }
+    } else {
+      dirname += *pi;
+    }
+    ++pi;
+  }
+
+  _filename = Filename(dirname, basename);
 
 
   if (_properties._color_type != (PNMFileType *)NULL) {
   if (_properties._color_type != (PNMFileType *)NULL) {
     _filename.set_extension
     _filename.set_extension
@@ -189,17 +217,17 @@ read(PNMImage &image) const {
   nassertr(!_filename.empty(), false);
   nassertr(!_filename.empty(), false);
 
 
   image.set_type(_properties._color_type);
   image.set_type(_properties._color_type);
-  nout << "Reading " << _filename << "\n";
+  nout << "Reading " << FilenameUnifier::make_user_filename(_filename) << "\n";
   if (!image.read(_filename)) {
   if (!image.read(_filename)) {
     nout << "Unable to read.\n";
     nout << "Unable to read.\n";
     return false;
     return false;
   }
   }
 
 
-  if (!_alpha_filename.empty()) {
+  if (!_alpha_filename.empty() && _alpha_filename.exists()) {
     // Read in a separate color image and an alpha channel image.
     // Read in a separate color image and an alpha channel image.
     PNMImage alpha_image;
     PNMImage alpha_image;
     alpha_image.set_type(_properties._alpha_type);
     alpha_image.set_type(_properties._alpha_type);
-    nout << "Reading " << _alpha_filename << "\n";
+    nout << "Reading " << FilenameUnifier::make_user_filename(_alpha_filename) << "\n";
     if (!alpha_image.read(_alpha_filename)) {
     if (!alpha_image.read(_alpha_filename)) {
       nout << "Unable to read.\n";
       nout << "Unable to read.\n";
       return false;
       return false;
@@ -236,9 +264,11 @@ write(PNMImage &image) const {
   if (!image.has_alpha() || 
   if (!image.has_alpha() || 
       _properties._alpha_type == (PNMFileType *)NULL) {
       _properties._alpha_type == (PNMFileType *)NULL) {
     if (!_alpha_filename.empty() && _alpha_filename.exists()) {
     if (!_alpha_filename.empty() && _alpha_filename.exists()) {
+      nout << "Deleting " << FilenameUnifier::make_user_filename(_alpha_filename) << "\n";
       _alpha_filename.unlink();
       _alpha_filename.unlink();
     }
     }
-    nout << "Writing " << _filename << "\n";
+    nout << "Writing " << FilenameUnifier::make_user_filename(_filename) << "\n";
+    _filename.make_dir();
     if (!image.write(_filename, _properties._color_type)) {
     if (!image.write(_filename, _properties._color_type)) {
       nout << "Unable to write.\n";
       nout << "Unable to write.\n";
       return false;
       return false;
@@ -256,12 +286,14 @@ write(PNMImage &image) const {
   }
   }
 
 
   image.remove_alpha();
   image.remove_alpha();
-  nout << "Writing " << _filename << "\n";
+  nout << "Writing " << FilenameUnifier::make_user_filename(_filename) << "\n";
+  _filename.make_dir();
   if (!image.write(_filename, _properties._color_type)) {
   if (!image.write(_filename, _properties._color_type)) {
     nout << "Unable to write.\n";
     nout << "Unable to write.\n";
     return false;
     return false;
   }
   }
-  nout << "Writing " << _alpha_filename << "\n";
+  nout << "Writing " << FilenameUnifier::make_user_filename(_alpha_filename) << "\n";
+  _alpha_filename.make_dir();
   if (!alpha_image.write(_alpha_filename, _properties._alpha_type)) {
   if (!alpha_image.write(_alpha_filename, _properties._alpha_type)) {
     nout << "Unable to write.\n";
     nout << "Unable to write.\n";
     return false;
     return false;
@@ -277,11 +309,11 @@ write(PNMImage &image) const {
 void ImageFile::
 void ImageFile::
 unlink() {
 unlink() {
   if (!_filename.empty() && _filename.exists()) {
   if (!_filename.empty() && _filename.exists()) {
-    nout << "Deleting " << _filename << "\n";
+    nout << "Deleting " << FilenameUnifier::make_user_filename(_filename) << "\n";
     _filename.unlink();
     _filename.unlink();
   }
   }
   if (!_alpha_filename.empty() && _alpha_filename.exists()) {
   if (!_alpha_filename.empty() && _alpha_filename.exists()) {
-    nout << "Deleting " << _alpha_filename << "\n";
+    nout << "Deleting " << FilenameUnifier::make_user_filename(_alpha_filename) << "\n";
     _alpha_filename.unlink();
     _alpha_filename.unlink();
   }
   }
 }
 }
@@ -298,6 +330,7 @@ update_egg_tex(EggTexture *egg_tex) const {
   egg_tex->set_filename(_filename);
   egg_tex->set_filename(_filename);
 
 
   if (_properties._alpha_type != (PNMFileType *)NULL &&
   if (_properties._alpha_type != (PNMFileType *)NULL &&
+      _properties.uses_alpha() &&
       !_alpha_filename.empty()) {
       !_alpha_filename.empty()) {
     egg_tex->set_alpha_file(_alpha_filename);
     egg_tex->set_alpha_file(_alpha_filename);
   } else {
   } else {
@@ -315,10 +348,11 @@ update_egg_tex(EggTexture *egg_tex) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void ImageFile::
 void ImageFile::
 output_filename(ostream &out) const {
 output_filename(ostream &out) const {
-  out << _filename;
+  out << FilenameUnifier::make_user_filename(_filename);
   if (_properties._alpha_type != (PNMFileType *)NULL &&
   if (_properties._alpha_type != (PNMFileType *)NULL &&
+      _properties.uses_alpha() &&
       !_alpha_filename.empty()) {
       !_alpha_filename.empty()) {
-    out << " " << _alpha_filename;
+    out << " " << FilenameUnifier::make_user_filename(_alpha_filename);
   }
   }
 }
 }
 
 
@@ -332,8 +366,8 @@ output_filename(ostream &out) const {
 void ImageFile::
 void ImageFile::
 write_datagram(BamWriter *writer, Datagram &datagram) {
 write_datagram(BamWriter *writer, Datagram &datagram) {
   _properties.write_datagram(writer, datagram);
   _properties.write_datagram(writer, datagram);
-  datagram.add_string(_filename);
-  datagram.add_string(_alpha_filename);
+  datagram.add_string(FilenameUnifier::make_bam_filename(_filename));
+  datagram.add_string(FilenameUnifier::make_bam_filename(_alpha_filename));
   datagram.add_bool(_size_known);
   datagram.add_bool(_size_known);
   datagram.add_int32(_x_size);
   datagram.add_int32(_x_size);
   datagram.add_int32(_y_size);
   datagram.add_int32(_y_size);
@@ -349,8 +383,8 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
 void ImageFile::
 void ImageFile::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   _properties.fillin(scan, manager);
   _properties.fillin(scan, manager);
-  _filename = scan.get_string();
-  _alpha_filename = scan.get_string();
+  _filename = FilenameUnifier::get_bam_filename(scan.get_string());
+  _alpha_filename = FilenameUnifier::get_bam_filename(scan.get_string());
   _size_known = scan.get_bool();
   _size_known = scan.get_bool();
   _x_size = scan.get_int32();
   _x_size = scan.get_int32();
   _y_size = scan.get_int32();
   _y_size = scan.get_int32();

+ 2 - 1
pandatool/src/egg-palettize/imageFile.h

@@ -15,6 +15,7 @@
 
 
 class PNMImage;
 class PNMImage;
 class EggTexture;
 class EggTexture;
+class PaletteGroup;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 // 	 Class : ImageFile
 // 	 Class : ImageFile
@@ -36,7 +37,7 @@ public:
   const TextureProperties &get_properties() const;
   const TextureProperties &get_properties() const;
   void update_properties(const TextureProperties &properties);
   void update_properties(const TextureProperties &properties);
 
 
-  void set_filename(const string &basename);
+  void set_filename(PaletteGroup *group, const string &basename);
   const Filename &get_filename() const;
   const Filename &get_filename() const;
   const Filename &get_alpha_filename() const;
   const Filename &get_alpha_filename() const;
   bool exists() const;
   bool exists() const;

+ 88 - 0
pandatool/src/egg-palettize/paletteGroup.cxx

@@ -24,6 +24,7 @@ TypeHandle PaletteGroup::_type_handle;
 PaletteGroup::
 PaletteGroup::
 PaletteGroup() {
 PaletteGroup() {
   _egg_count = 0;
   _egg_count = 0;
+  _dependency_level = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -39,6 +40,41 @@ set_dirname(const string &dirname) {
   _dirname = dirname;
   _dirname = dirname;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::has_dirname
+//       Access: Public
+//  Description: Returns true if the directory name has been
+//               explicitly set for this group.  If it has not,
+//               get_dirname() returns an empty string.
+////////////////////////////////////////////////////////////////////
+bool PaletteGroup::
+has_dirname() const {
+  return !_dirname.empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::get_dirname
+//       Access: Public
+//  Description: Returns the directory name associated with the
+//               palette group.  See set_dirname().
+////////////////////////////////////////////////////////////////////
+const string &PaletteGroup::
+get_dirname() const {
+  return _dirname;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::clear_depends
+//       Access: Public
+//  Description: Eliminates all the dependency information for this
+//               group.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+clear_depends() {
+  _dependent.clear();
+  _dependency_level = 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PaletteGroup::group_with
 //     Function: PaletteGroup::group_with
 //       Access: Public
 //       Access: Public
@@ -66,6 +102,55 @@ get_groups() const {
   return _dependent;
   return _dependent;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::set_dependency_level
+//       Access: Public
+//  Description: Sets the dependency level of this group to the
+//               indicated level, provided that level is not lower
+//               than the level that was set previously.  Also
+//               cascades to all dependent groups.  See
+//               get_dependency_level().
+//
+//               This call recurses to correctly set the dependency
+//               level of all PaletteGroups in the hierarchy.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+set_dependency_level(int level) {
+  if (level > _dependency_level) {
+    _dependency_level = level;
+    PaletteGroups::iterator gi;
+    for (gi = _dependent.begin(); gi != _dependent.end(); ++gi) {
+      (*gi)->set_dependency_level(level + 1);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::get_dependency_level
+//       Access: Public
+//  Description: Returns the dependency level of this group.  This is
+//               a measure of how specific the group is; the lower the
+//               dependency level, the more specific the group.
+//
+//               Groups depend on other groups in a hierarchical
+//               relationship.  In general, if group a depends on
+//               group b, then b->get_dependency_level() >
+//               a->get_dependency_level().
+//
+//               Thus, groups that lots of other groups depend on have
+//               a higher dependency level; groups that no one else
+//               depends on have a low dependency level.  This is
+//               important when deciding which groups are best suited
+//               for assigning a texture to; in general, the texture
+//               should be assigned to the most specific suitable
+//               group (i.e. the one with the lowest dependency
+//               level).
+////////////////////////////////////////////////////////////////////
+int PaletteGroup::
+get_dependency_level() const {
+  return _dependency_level;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PaletteGroup::increment_egg_count
 //     Function: PaletteGroup::increment_egg_count
 //       Access: Public
 //       Access: Public
@@ -261,6 +346,9 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
   datagram.add_string(_dirname);
   datagram.add_string(_dirname);
   _dependent.write_datagram(writer, datagram);
   _dependent.write_datagram(writer, datagram);
 
 
+  // We don't write out _dependency_level.  It's best to recompute
+  // that each time.
+
   datagram.add_uint32(_placements.size());
   datagram.add_uint32(_placements.size());
   Placements::const_iterator pli;
   Placements::const_iterator pli;
   for (pli = _placements.begin(); pli != _placements.end(); ++pli) {
   for (pli = _placements.begin(); pli != _placements.end(); ++pli) {

+ 7 - 0
pandatool/src/egg-palettize/paletteGroup.h

@@ -37,10 +37,16 @@ public:
   PaletteGroup();
   PaletteGroup();
 
 
   void set_dirname(const string &dirname);
   void set_dirname(const string &dirname);
+  bool has_dirname() const;
+  const string &get_dirname() const;
 
 
+  void clear_depends();
   void group_with(PaletteGroup *other);
   void group_with(PaletteGroup *other);
   const PaletteGroups &get_groups() const;
   const PaletteGroups &get_groups() const;
 
 
+  void set_dependency_level(int level);
+  int get_dependency_level() const;
+
   void increment_egg_count();
   void increment_egg_count();
   int get_egg_count() const;
   int get_egg_count() const;
 
 
@@ -59,6 +65,7 @@ private:
   string _dirname;
   string _dirname;
   int _egg_count;
   int _egg_count;
   PaletteGroups _dependent;
   PaletteGroups _dependent;
+  int _dependency_level;
 
 
   typedef set<TexturePlacement *> Placements;
   typedef set<TexturePlacement *> Placements;
   Placements _placements;
   Placements _placements;

+ 10 - 0
pandatool/src/egg-palettize/paletteGroups.cxx

@@ -143,6 +143,16 @@ make_intersection(const PaletteGroups &a, const PaletteGroups &b) {
   _groups.swap(i);
   _groups.swap(i);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroups::clear
+//       Access: Public
+//  Description: Empties the set.
+////////////////////////////////////////////////////////////////////
+void PaletteGroups::
+clear() {
+  _groups.clear();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PaletteGroups::empty
 //     Function: PaletteGroups::empty
 //       Access: Public
 //       Access: Public

+ 1 - 0
pandatool/src/egg-palettize/paletteGroups.h

@@ -43,6 +43,7 @@ public:
   void make_complete(const PaletteGroups &a);
   void make_complete(const PaletteGroups &a);
   void make_union(const PaletteGroups &a, const PaletteGroups &b);
   void make_union(const PaletteGroups &a, const PaletteGroups &b);
   void make_intersection(const PaletteGroups &a, const PaletteGroups &b);
   void make_intersection(const PaletteGroups &a, const PaletteGroups &b);
+  void clear();
 
 
   bool empty() const;
   bool empty() const;
   size_type size() const;
   size_type size() const;

+ 5 - 3
pandatool/src/egg-palettize/paletteImage.cxx

@@ -163,7 +163,7 @@ PaletteImage(PalettePage *page, int index) :
   name << page->get_group()->get_name() << "_palette_" 
   name << page->get_group()->get_name() << "_palette_" 
        << page->get_name() << "_" << index + 1;
        << page->get_name() << "_" << index + 1;
 
 
-  set_filename(name.str());
+  set_filename(page->get_group(), name.str());
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -465,9 +465,10 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
 
 
   writer->write_pointer(datagram, _page);
   writer->write_pointer(datagram, _page);
   datagram.add_uint32(_index);
   datagram.add_uint32(_index);
+  datagram.add_bool(_new_image);
 
 
-  // We don't write _new_image, _got_image, or _image.  These are all
-  // loaded per-session.
+  // We don't write _got_image or _image.  These are loaded
+  // per-session.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -546,4 +547,5 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   manager->read_pointer(scan, this);  // _page
   manager->read_pointer(scan, this);  // _page
 
 
   _index = scan.get_uint32();
   _index = scan.get_uint32();
+  _new_image = scan.get_bool();
 }
 }

+ 225 - 35
pandatool/src/egg-palettize/palettizer.cxx

@@ -8,6 +8,7 @@
 #include "textureImage.h"
 #include "textureImage.h"
 #include "string_utils.h"
 #include "string_utils.h"
 #include "paletteGroup.h"
 #include "paletteGroup.h"
+#include "filenameUnifier.h"
 
 
 #include <pnmImage.h>
 #include <pnmImage.h>
 #include <pnmFileTypeRegistry.h>
 #include <pnmFileTypeRegistry.h>
@@ -36,6 +37,7 @@ Palettizer() {
   // version number for all bam and boo files anywhere in the world.
   // version number for all bam and boo files anywhere in the world.
   _pi_version = 0;
   _pi_version = 0;
 
 
+  _map_dirname = "%g";
   _margin = 2;
   _margin = 2;
   _repeat_threshold = 250.0;
   _repeat_threshold = 250.0;
   _aggressively_clean_mapdir = true;
   _aggressively_clean_mapdir = true;
@@ -61,6 +63,9 @@ void Palettizer::
 report_pi() const {
 report_pi() const {
   cout 
   cout 
     << "\nparams\n"
     << "\nparams\n"
+    << "  map directory: " << _map_dirname << "\n"
+    << "  egg relative directory: " 
+    << FilenameUnifier::make_user_filename(_rel_dirname) << "\n"
     << "  palettize size: " << _pal_x_size << " by " << _pal_y_size << "\n"
     << "  palettize size: " << _pal_x_size << " by " << _pal_y_size << "\n"
     << "  margin: " << _margin << "\n"
     << "  margin: " << _margin << "\n"
     << "  repeat threshold: " << _repeat_threshold << "%\n"
     << "  repeat threshold: " << _repeat_threshold << "%\n"
@@ -108,8 +113,7 @@ report_pi() const {
   EggFiles::const_iterator ei;
   EggFiles::const_iterator ei;
   for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
   for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
     EggFile *egg_file = (*ei).second;
     EggFile *egg_file = (*ei).second;
-    cout << "  " << egg_file->get_name() << ": " 
-	 << egg_file->get_groups() << "\n";
+    egg_file->write_description(cout, 2);
     egg_file->write_texture_refs(cout, 4);
     egg_file->write_texture_refs(cout, 4);
   }
   }
 
 
@@ -117,35 +121,82 @@ report_pi() const {
   Groups::const_iterator gi;
   Groups::const_iterator gi;
   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
     PaletteGroup *group = (*gi).second;
     PaletteGroup *group = (*gi).second;
+    if (gi != _groups.begin()) {
+      cout << "\n";
+    }
     cout << "  " << group->get_name() << ": "
     cout << "  " << group->get_name() << ": "
 	 << group->get_groups() << "\n";
 	 << group->get_groups() << "\n";
     group->write_image_info(cout, 4);
     group->write_image_info(cout, 4);
-    cout << "\n";
   }
   }
 
 
-  cout << "textures\n";
+  cout << "\ntextures\n";
   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
     TextureImage *texture = (*ti).second;
     TextureImage *texture = (*ti).second;
     texture->write_scale_info(cout, 2);
     texture->write_scale_info(cout, 2);
   }
   }
 
 
+  cout << "\nsurprises\n";
+  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
+    TextureImage *texture = (*ti).second;
+    if (texture->is_surprise()) {
+      cout << "  " << texture->get_name() << "\n";
+    }
+  }
+  for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
+    EggFile *egg_file = (*ei).second;
+    if (egg_file->is_surprise()) {
+      cout << "  " << egg_file->get_name() << "\n";
+    }
+  }
+
   cout << "\n";
   cout << "\n";
 }
 }
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: Palettizer::run
+//     Function: Palettizer::read_txa_file
 //       Access: Public
 //       Access: Public
-//  Description:
+//  Description: Reads in the .txa file and keeps it ready for
+//               matching textures and egg files.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void Palettizer::
 void Palettizer::
-run(const TxaFile &txa_file) {
+read_txa_file(const Filename &txa_filename) {
+  // Clear out the group dependencies, in preparation for reading them
+  // again from the .txa file.
+  Groups::iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    PaletteGroup *group = (*gi).second;
+    group->clear_depends();
+  }
+
+  if (!_txa_file.read(txa_filename)) {
+    exit(1);
+  }
+
   if (_color_type == (PNMFileType *)NULL) {
   if (_color_type == (PNMFileType *)NULL) {
-    nout << "No valid output image file type available, cannot run.\n"
+    nout << "No valid output image file type available; cannot run.\n"
 	 << "Use :imagetype command in .txa file.\n";
 	 << "Use :imagetype command in .txa file.\n";
     exit(1);
     exit(1);
   }
   }
 
 
-  set<TextureImage *> command_line_textures;
+  // Compute the correct dependency level for each group.  This will
+  // help us when we assign the textures to their groups.
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    PaletteGroup *group = (*gi).second;
+    group->set_dependency_level(1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Palettizer::process_command_line_eggs
+//       Access: Public
+//  Description: Processes all the textures named in the
+//               _command_line_eggs, placing them on the appropriate
+//               palettes or whatever needs to be done with them.
+////////////////////////////////////////////////////////////////////
+void Palettizer::
+process_command_line_eggs() {
+  _command_line_textures.clear();
 
 
   // Start by scanning all the egg files we read up on the command
   // Start by scanning all the egg files we read up on the command
   // line.
   // line.
@@ -156,23 +207,23 @@ run(const TxaFile &txa_file) {
     EggFile *egg_file = (*ei);
     EggFile *egg_file = (*ei);
 
 
     egg_file->scan_textures();
     egg_file->scan_textures();
-    egg_file->get_textures(command_line_textures);
-
-    txa_file.match_egg(egg_file);
+    egg_file->get_textures(_command_line_textures);
 
 
+    egg_file->pre_txa_file();
+    _txa_file.match_egg(egg_file);
     egg_file->post_txa_file();
     egg_file->post_txa_file();
   }
   }
 
 
   // Now match each of the textures mentioned in those egg files
   // Now match each of the textures mentioned in those egg files
   // against a line in the .txa file.
   // against a line in the .txa file.
-  set<TextureImage *>::iterator ti;
-  for (ti = command_line_textures.begin(); 
-       ti != command_line_textures.end();
+  CommandLineTextures::iterator ti;
+  for (ti = _command_line_textures.begin(); 
+       ti != _command_line_textures.end();
        ++ti) {
        ++ti) {
     TextureImage *texture = *ti;
     TextureImage *texture = *ti;
 
 
     texture->pre_txa_file();
     texture->pre_txa_file();
-    txa_file.match_texture(texture);
+    _txa_file.match_texture(texture);
     texture->post_txa_file();
     texture->post_txa_file();
   }
   }
 
 
@@ -185,8 +236,8 @@ run(const TxaFile &txa_file) {
 
 
   // And now, assign each of the current set of textures to an
   // And now, assign each of the current set of textures to an
   // appropriate group or groups.
   // appropriate group or groups.
-  for (ti = command_line_textures.begin(); 
-       ti != command_line_textures.end();
+  for (ti = _command_line_textures.begin(); 
+       ti != _command_line_textures.end();
        ++ti) {
        ++ti) {
     TextureImage *texture = *ti;
     TextureImage *texture = *ti;
     texture->assign_groups();
     texture->assign_groups();
@@ -202,8 +253,8 @@ run(const TxaFile &txa_file) {
 
 
   // Now that *that's* done, we need to make sure the various
   // Now that *that's* done, we need to make sure the various
   // TexturePlacements require the right size for their textures.
   // TexturePlacements require the right size for their textures.
-  for (ti = command_line_textures.begin(); 
-       ti != command_line_textures.end();
+  for (ti = _command_line_textures.begin(); 
+       ti != _command_line_textures.end();
        ++ti) {
        ++ti) {
     TextureImage *texture = *ti;
     TextureImage *texture = *ti;
     texture->determine_placement_size();
     texture->determine_placement_size();
@@ -216,22 +267,160 @@ run(const TxaFile &txa_file) {
     PaletteGroup *group = (*gi).second;
     PaletteGroup *group = (*gi).second;
     group->place_all();
     group->place_all();
   }
   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Palettizer::process_all
+//       Access: Public
+//  Description: Reprocesses all textures known.
+////////////////////////////////////////////////////////////////////
+void Palettizer::
+process_all() {
+  // If there *were* any egg files on the command line, deal with
+  // them.
+  CommandLineEggs::const_iterator ei;
+  for (ei = _command_line_eggs.begin();
+       ei != _command_line_eggs.end();
+       ++ei) {
+    EggFile *egg_file = (*ei);
+
+    egg_file->scan_textures();
+    egg_file->get_textures(_command_line_textures);
+  }
+
+  // Then match up all the egg files we know about with the .txa file.
+  EggFiles::const_iterator efi;
+  for (efi = _egg_files.begin(); efi != _egg_files.end(); ++efi) {
+    EggFile *egg_file = (*efi).second;
+
+    egg_file->pre_txa_file();
+    _txa_file.match_egg(egg_file);
+    egg_file->post_txa_file();
+  }
+
+  // Now match each of the textures in the world against a line in the
+  // .txa file.
+  Textures::iterator ti;
+  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
+    TextureImage *texture = (*ti).second;
+
+    texture->pre_txa_file();
+    _txa_file.match_texture(texture);
+    texture->post_txa_file();
+  }
+
+  // Now that all of our data is read in, build in all the cross links
+  // and back pointers and stuff.
+  for (efi = _egg_files.begin(); efi != _egg_files.end(); ++efi) {
+    (*efi).second->build_cross_links();
+  }
+
+  // And now, assign each texture to an appropriate group or groups.
+  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
+    TextureImage *texture = (*ti).second;
+    texture->assign_groups();
+  }
+
+  // And then the egg files need to sign up for a particular
+  // TexturePlacement, so we can determine some more properties about
+  // how the textures are placed (for instance, how big the UV range
+  // is for a particular TexturePlacement).
+  for (efi = _egg_files.begin(); efi != _egg_files.end(); ++efi) {
+    (*efi).second->choose_placements();
+  }
+
+  // Now that *that's* done, we need to make sure the various
+  // TexturePlacements require the right size for their textures.
+  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
+    TextureImage *texture = (*ti).second;
+    texture->determine_placement_size();
+  }
+
+  // Now that each texture has been assigned to a suitable group,
+  // make sure the textures are placed on specific PaletteImages.
+  Groups::iterator gi;
+  for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
+    PaletteGroup *group = (*gi).second;
+    group->place_all();
+  }
+}
 
 
-  // Finally, generate all the images.
+////////////////////////////////////////////////////////////////////
+//     Function: Palettizer::generate_images
+//       Access: Public
+//  Description: Actually generates the appropriate palette and
+//               unplaced texture images into the map directories.
+////////////////////////////////////////////////////////////////////
+void Palettizer::
+generate_images() {
+  Groups::iterator gi;
   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
   for (gi = _groups.begin(); gi != _groups.end(); ++gi) {
     PaletteGroup *group = (*gi).second;
     PaletteGroup *group = (*gi).second;
     group->update_images();
     group->update_images();
   }
   }
 
 
-  // And now we can adjust all the egg files.
-  for (ei = _command_line_eggs.begin();
-       ei != _command_line_eggs.end();
-       ++ei) {
-    EggFile *egg_file = (*ei);
-    egg_file->update_egg();
+  Textures::iterator ti;
+  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
+    TextureImage *texture = (*ti).second;
+    texture->copy_unplaced();
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: Palettizer::read_stale_eggs
+//       Access: Public
+//  Description: Reads in any egg file that is known to be stale, even
+//               if it was not listed on the command line, so that it
+//               may be updated and written out when write_eggs() is
+//               called.  Returns true if successful, or false if
+//               there was some error.
+////////////////////////////////////////////////////////////////////
+bool Palettizer::
+read_stale_eggs() {
+  bool okflag = true;
+
+  EggFiles::iterator ei;
+  for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
+    EggFile *egg_file = (*ei).second;
+    if (!egg_file->has_data() && egg_file->is_stale()) {
+      if (!egg_file->read_egg()) {
+	okflag = false;
+
+      } else {
+	egg_file->scan_textures();
+	egg_file->choose_placements();
+      }
+    }
+  }
+
+  return okflag;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Palettizer::write_eggs
+//       Access: Public
+//  Description: Adjusts the egg files to reference the newly
+//               generated textures, and writes them out.  Returns
+//               true if successful, or false if there was some error.
+////////////////////////////////////////////////////////////////////
+bool Palettizer::
+write_eggs() {
+  bool okflag = true;
+
+  EggFiles::iterator ei;
+  for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
+    EggFile *egg_file = (*ei).second;
+    if (egg_file->has_data()) {
+      egg_file->update_egg();
+      if (!egg_file->write_egg()) {
+	okflag = false;
+      }
+    }
+  }
+
+  return okflag;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Palettizer::get_egg_file
 //     Function: Palettizer::get_egg_file
 //       Access: Public
 //       Access: Public
@@ -298,7 +487,11 @@ test_palette_group(const string &name) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PaletteGroup *Palettizer::
 PaletteGroup *Palettizer::
 get_default_group() {
 get_default_group() {
-  return get_palette_group(_default_groupname);
+  PaletteGroup *default_group = get_palette_group(_default_groupname);
+  if (!_default_groupdir.empty() && !default_group->has_dirname()) {
+    default_group->set_dirname(_default_groupdir);
+  }
+  return default_group;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -319,7 +512,7 @@ get_texture(const string &name) {
 
 
   TextureImage *image = new TextureImage;
   TextureImage *image = new TextureImage;
   image->set_name(name);
   image->set_name(name);
-  image->set_filename(name);
+  //  image->set_filename(name);
   _textures.insert(Textures::value_type(name, image));
   _textures.insert(Textures::value_type(name, image));
   return image;
   return image;
 }
 }
@@ -358,9 +551,7 @@ void Palettizer::
 write_datagram(BamWriter *writer, Datagram &datagram) {
 write_datagram(BamWriter *writer, Datagram &datagram) {
   datagram.add_int32(_pi_version);
   datagram.add_int32(_pi_version);
   datagram.add_string(_map_dirname);
   datagram.add_string(_map_dirname);
-  datagram.add_string(_rel_dirname);
-  datagram.add_string(_default_groupname);
-  datagram.add_string(_default_groupdir);
+  datagram.add_string(FilenameUnifier::make_bam_filename(_rel_dirname));
   datagram.add_int32(_pal_x_size);
   datagram.add_int32(_pal_x_size);
   datagram.add_int32(_pal_y_size);
   datagram.add_int32(_pal_y_size);
   datagram.add_int32(_margin);
   datagram.add_int32(_margin);
@@ -477,9 +668,8 @@ void Palettizer::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
   _pi_version = scan.get_int32();
   _pi_version = scan.get_int32();
   _map_dirname = scan.get_string();
   _map_dirname = scan.get_string();
-  _rel_dirname = scan.get_string();
-  _default_groupname = scan.get_string();
-  _default_groupdir = scan.get_string();
+  _rel_dirname = FilenameUnifier::get_bam_filename(scan.get_string());
+  FilenameUnifier::set_rel_dirname(_rel_dirname);
   _pal_x_size = scan.get_int32();
   _pal_x_size = scan.get_int32();
   _pal_y_size = scan.get_int32();
   _pal_y_size = scan.get_int32();
   _margin = scan.get_int32();
   _margin = scan.get_int32();

+ 18 - 5
pandatool/src/egg-palettize/palettizer.h

@@ -13,6 +13,7 @@
 #include <typedWriteable.h>
 #include <typedWriteable.h>
 
 
 #include <vector>
 #include <vector>
+#include <set>
 #include <map>
 #include <map>
 
 
 class PNMFileType;
 class PNMFileType;
@@ -33,7 +34,12 @@ public:
   Palettizer();
   Palettizer();
 
 
   void report_pi() const;
   void report_pi() const;
-  void run(const TxaFile &txa_file);
+  void read_txa_file(const Filename &txa_filename);
+  void process_command_line_eggs();
+  void process_all();
+  void generate_images();
+  bool read_stale_eggs();
+  bool write_eggs();
 
 
   EggFile *get_egg_file(const string &name);
   EggFile *get_egg_file(const string &name);
   PaletteGroup *get_palette_group(const string &name);
   PaletteGroup *get_palette_group(const string &name);
@@ -53,13 +59,17 @@ public:
     RU_poly
     RU_poly
   };
   };
 
 
+  // These values are not stored in the bam file, but are specific to
+  // each session.
+  TxaFile _txa_file;
+  string _default_groupname;
+  string _default_groupdir;
+
   // The following parameter values specifically relate to textures
   // The following parameter values specifically relate to textures
-  // and palettes.  These values are stored in the .pi file for future
+  // and palettes.  These values are stored in the bam file for future
   // reference.
   // reference.
-  Filename _map_dirname;
+  string _map_dirname;
   Filename _rel_dirname;
   Filename _rel_dirname;
-  string _default_groupname;
-  string _default_groupdir;
   int _pal_x_size, _pal_y_size;
   int _pal_x_size, _pal_y_size;
   int _margin;
   int _margin;
   double _repeat_threshold;
   double _repeat_threshold;
@@ -79,6 +89,9 @@ private:
   typedef vector<EggFile *> CommandLineEggs;
   typedef vector<EggFile *> CommandLineEggs;
   CommandLineEggs _command_line_eggs;
   CommandLineEggs _command_line_eggs;
 
 
+  typedef set<TextureImage *> CommandLineTextures;
+  CommandLineTextures _command_line_textures;
+  
   typedef map<string, PaletteGroup *> Groups;
   typedef map<string, PaletteGroup *> Groups;
   Groups _groups;
   Groups _groups;
 
 

+ 0 - 13
pandatool/src/egg-palettize/sourceTextureImage.cxx

@@ -120,19 +120,6 @@ read_header() {
 }
 }
 
 
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: SourceTextureImage::output
-//       Access: Public
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void SourceTextureImage::
-output(ostream &out) const {
-  out << _filename;
-  if (!_alpha_filename.empty()) {
-    out << " (" << _alpha_filename << ")";
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SourceTextureImage::register_with_read_factory
 //     Function: SourceTextureImage::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static

+ 1 - 3
pandatool/src/egg-palettize/sourceTextureImage.h

@@ -30,8 +30,6 @@ public:
   bool get_size();
   bool get_size();
   bool read_header();
   bool read_header();
 
 
-  void output(ostream &out) const;
-
 private:
 private:
   bool _read_header;
   bool _read_header;
   bool _successfully_read_header;
   bool _successfully_read_header;
@@ -67,7 +65,7 @@ private:
 
 
 INLINE ostream &
 INLINE ostream &
 operator << (ostream &out, const SourceTextureImage &source) {
 operator << (ostream &out, const SourceTextureImage &source) {
-  source.output(out);
+  source.output_filename(out);
   return out;
   return out;
 }
 }
 
 

+ 225 - 15
pandatool/src/egg-palettize/textureImage.cxx

@@ -5,6 +5,7 @@
 
 
 #include "textureImage.h"
 #include "textureImage.h"
 #include "sourceTextureImage.h"
 #include "sourceTextureImage.h"
+#include "destTextureImage.h"
 #include "eggFile.h"
 #include "eggFile.h"
 #include "paletteGroup.h"
 #include "paletteGroup.h"
 #include "texturePlacement.h"
 #include "texturePlacement.h"
@@ -26,6 +27,8 @@ TextureImage::
 TextureImage() {
 TextureImage() {
   _preferred_source = (SourceTextureImage *)NULL;
   _preferred_source = (SourceTextureImage *)NULL;
   _read_source_image = false;
   _read_source_image = false;
+  _got_dest_image = false;
+  _is_surprise = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -38,7 +41,7 @@ TextureImage() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextureImage::
 void TextureImage::
 note_egg_file(EggFile *egg_file) {
 note_egg_file(EggFile *egg_file) {
-  nassertv(!egg_file->get_groups().empty());
+  nassertv(!egg_file->get_complete_groups().empty());
   _egg_files.insert(egg_file);
   _egg_files.insert(egg_file);
 }
 }
 
 
@@ -73,7 +76,7 @@ assign_groups() {
     EggFiles::const_iterator ei;
     EggFiles::const_iterator ei;
     for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
     for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
       PaletteGroups intersect;
       PaletteGroups intersect;
-      intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_groups());
+      intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups());
       if (!intersect.empty()) {
       if (!intersect.empty()) {
 	// This egg file is satisfied by one of the texture's explicit
 	// This egg file is satisfied by one of the texture's explicit
 	// assignments.
 	// assignments.
@@ -100,12 +103,13 @@ assign_groups() {
     PaletteGroups total;
     PaletteGroups total;
     WorkingEggs::const_iterator ei;
     WorkingEggs::const_iterator ei;
     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
-      total.make_union(total, (*ei)->get_groups());
+      total.make_union(total, (*ei)->get_complete_groups());
     }
     }
     
     
-    // Now, find the group that will satisfy the most egg files.  If two
-    // groups satisfy the same number of egg files, choose the one that
-    // has the fewest egg files sharing it.
+    // Now, find the group that will satisfy the most egg files.  If
+    // two groups satisfy the same number of egg files, choose (a) the
+    // most specific one, i.e. with the lowest dependency_level value,
+    // and (b) the one that has the fewest egg files sharing it.
     nassertv(!total.empty());
     nassertv(!total.empty());
     PaletteGroups::iterator gi = total.begin();
     PaletteGroups::iterator gi = total.begin();
     PaletteGroup *best = (*gi);
     PaletteGroup *best = (*gi);
@@ -113,10 +117,22 @@ assign_groups() {
     ++gi;
     ++gi;
     while (gi != total.end()) {
     while (gi != total.end()) {
       PaletteGroup *group = (*gi);
       PaletteGroup *group = (*gi);
+
+      // Do we prefer this group to our current 'best'?
+      bool prefer_group = false;
       int group_egg_count = compute_egg_count(group, needed_eggs);
       int group_egg_count = compute_egg_count(group, needed_eggs);
-      if (group_egg_count > best_egg_count ||
-	  (group_egg_count == best_egg_count &&
-	   group->get_egg_count() < best->get_egg_count())) {
+      if (group_egg_count != best_egg_count) {
+	prefer_group = (group_egg_count > best_egg_count);
+
+      } else if (group->get_dependency_level() != best->get_dependency_level()){ 
+	prefer_group = 
+	  (group->get_dependency_level() < best->get_dependency_level());
+
+      } else {
+	prefer_group = (group->get_egg_count() < best->get_egg_count());
+      }
+
+      if (prefer_group) {
 	best = group;
 	best = group;
 	best_egg_count = group_egg_count;
 	best_egg_count = group_egg_count;
       }
       }
@@ -129,7 +145,7 @@ assign_groups() {
     
     
     WorkingEggs next_needed_eggs;
     WorkingEggs next_needed_eggs;
     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
-      if ((*ei)->get_groups().count(best) == 0) {
+      if ((*ei)->get_complete_groups().count(best) == 0) {
 	// This one wasn't eliminated.
 	// This one wasn't eliminated.
 	next_needed_eggs.push_back(*ei);
 	next_needed_eggs.push_back(*ei);
       }
       }
@@ -209,6 +225,7 @@ pre_txa_file() {
   }
   }
 
 
   _request.pre_txa_file();
   _request.pre_txa_file();
+  _is_surprise = true;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -332,6 +349,19 @@ get_margin() const {
   return _request._margin;
   return _request._margin;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureImage::is_surprise
+//       Access: Public
+//  Description: Returns true if this particular texture is a
+//               'surprise', i.e. it wasn't matched by a line in the
+//               .txa file that didn't include the keyword 'cont'.
+////////////////////////////////////////////////////////////////////
+bool TextureImage::
+is_surprise() const {
+  return _is_surprise;
+}
+
+
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureImage::get_source
 //     Function: TextureImage::get_source
@@ -359,6 +389,7 @@ get_source(const Filename &filename, const Filename &alpha_filename) {
   // next time someone asks.
   // next time someone asks.
   _preferred_source = (SourceTextureImage *)NULL;
   _preferred_source = (SourceTextureImage *)NULL;
   _read_source_image = false;
   _read_source_image = false;
+  _got_dest_image = false;
 
 
   return source;
   return source;
 }
 }
@@ -387,6 +418,62 @@ get_preferred_source() {
   return _preferred_source;
   return _preferred_source;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureImage::copy_unplaced
+//       Access: Public
+//  Description: Copies the texture to whichever destination
+//               directories are appropriate for the groups in which
+//               it has been unplaced.  Also removes the old filenames
+//               for previous sessions where it was unplaced, but is
+//               no longer.
+////////////////////////////////////////////////////////////////////
+void TextureImage::
+copy_unplaced() {
+  // First, we need to build up the set of DestTextureImages that
+  // represents the files we need to generate.
+  Dests generate;
+
+  // Go through all the TexturePlacements and note the ones for which
+  // we're unplaced.  We check get_omit_reason() and not is_placed(),
+  // because we want to consider solitary images to be unplaced in
+  // this case.
+  Placement::iterator pi;
+  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
+    TexturePlacement *placement = (*pi).second;
+    if (placement->get_omit_reason() != OR_none) {
+      DestTextureImage *dest = new DestTextureImage(placement);
+      Filename filename = dest->get_filename();
+      filename.make_canonical();
+
+      pair<Dests::iterator, bool> insert_result = generate.insert
+	(Dests::value_type(filename, dest));
+      if (!insert_result.second) {
+	// At least two DestTextureImages map to the same filename, no
+	// sweat.
+	delete dest;
+	dest = (*insert_result.first).second;
+      }
+
+      placement->set_dest(dest);
+    }
+  }
+
+  // Now remove the old files that we previously generated, but we
+  // don't need any more.
+  remove_old_dests(generate, _dests);
+
+  // And then copy in the new ones.
+  copy_new_dests(generate, _dests);
+
+  // Clean up the old set.
+  Dests::iterator di;
+  for (di = _dests.begin(); di != _dests.end(); ++di) {
+    delete (*di).second;
+  }
+
+  _dests.swap(generate);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureImage::read_source_image
 //     Function: TextureImage::read_source_image
 //       Access: Public
 //       Access: Public
@@ -406,6 +493,27 @@ read_source_image() {
   return _source_image;
   return _source_image;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureImage::get_dest_image
+//       Access: Public
+//  Description: Returns the image appropriate for writing to the
+//               destination directory, having been resized and
+//               everything.
+////////////////////////////////////////////////////////////////////
+const PNMImage &TextureImage::
+get_dest_image() {
+  if (!_got_dest_image) {
+    const PNMImage &source_image = read_source_image();
+    _dest_image.clear(get_x_size(), get_y_size(), get_num_channels(),
+		      source_image.get_maxval());
+    _dest_image.quick_filter_from(source_image);
+
+    _got_dest_image = true;
+  }
+
+  return _dest_image;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureImage::write_source_pathnames
 //     Function: TextureImage::write_source_pathnames
 //       Access: Public
 //       Access: Public
@@ -464,7 +572,7 @@ write_scale_info(ostream &out, int indent_level) {
     double scale = 
     double scale = 
       100.0 * (((double)get_x_size() / (double)source->get_x_size()) +
       100.0 * (((double)get_x_size() / (double)source->get_x_size()) +
 	       ((double)get_y_size() / (double)source->get_y_size())) / 2.0;
 	       ((double)get_y_size() / (double)source->get_y_size())) / 2.0;
-    out << " " << scale << "%";
+    out << " scale " << scale << "%";
   }
   }
   out << "\n";
   out << "\n";
 }
 }
@@ -483,7 +591,7 @@ compute_egg_count(PaletteGroup *group,
 
 
   WorkingEggs::const_iterator ei;
   WorkingEggs::const_iterator ei;
   for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) {
   for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) {
-    if ((*ei)->get_groups().count(group) != 0) {
+    if ((*ei)->get_complete_groups().count(group) != 0) {
       count++;
       count++;
     }
     }
   }
   }
@@ -589,6 +697,88 @@ consider_grayscale() {
   _properties._num_channels -= 2;
   _properties._num_channels -= 2;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureImage::remove_old_dests
+//       Access: Private
+//  Description: Removes all of the filenames named in b that are not
+//               also named in a.
+////////////////////////////////////////////////////////////////////
+void TextureImage::
+remove_old_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
+  Dests::const_iterator ai = a.begin();
+  Dests::const_iterator bi = b.begin();
+
+  while (ai != a.end() && bi != b.end()) {
+    const string &astr = (*ai).first;
+    const string &bstr = (*bi).first;
+
+    if (astr < bstr) {
+      // Here's a filename in a, not in b.
+      ++ai;
+
+    } else if (bstr < astr) {
+      // Here's a filename in b, not in a.
+      (*bi).second->unlink();
+      ++bi;
+
+    } else { // bstr == astr
+      // Here's a filename in both a and b.
+      ++ai;
+      ++bi;
+    }
+  }
+
+  while (bi != b.end()) {
+    // Here's a filename in b, not in a.
+    (*bi).second->unlink();
+    ++bi;
+  }
+
+  while (ai != a.end()) {
+    ++ai;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextureImage::copy_new_dests
+//       Access: Private
+//  Description: Copies a resized texture into each filename named in
+//               a that is not also listed in b, or whose
+//               corresponding listing in b is out of date.
+////////////////////////////////////////////////////////////////////
+void TextureImage::
+copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
+  Dests::const_iterator ai = a.begin();
+  Dests::const_iterator bi = b.begin();
+
+  while (ai != a.end() && bi != b.end()) {
+    const string &astr = (*ai).first;
+    const string &bstr = (*bi).first;
+
+    if (astr < bstr) {
+      // Here's a filename in a, not in b.
+      (*ai).second->copy(this);
+      ++ai;
+
+    } else if (bstr < astr) {
+      // Here's a filename in b, not in a.
+      ++bi;
+
+    } else { // bstr == astr
+      // Here's a filename in both a and b.
+      (*ai).second->copy_if_stale((*bi).second, this);
+      ++ai;
+      ++bi;
+    }
+  }
+
+  while (ai != a.end()) {
+    // Here's a filename in a, not in b.
+    (*ai).second->copy(this);
+    ++ai;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureImage::register_with_read_factory
 //     Function: TextureImage::register_with_read_factory
 //       Access: Public, Static
 //       Access: Public, Static
@@ -621,6 +811,8 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
   // We don't write out _preferred_source; this is redetermined each
   // We don't write out _preferred_source; this is redetermined each
   // session.
   // session.
 
 
+  datagram.add_bool(_is_surprise);
+
   // We don't write out _explicitly_assigned_groups; this is re-read
   // We don't write out _explicitly_assigned_groups; this is re-read
   // from the .txa file each time.
   // from the .txa file each time.
 
 
@@ -641,8 +833,15 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
     writer->write_pointer(datagram, (*si).second);
     writer->write_pointer(datagram, (*si).second);
   }
   }
 
 
-  // We don't write out _read_source_image or _source_image; this must
-  // be reread each session.
+  datagram.add_uint32(_dests.size());
+  Dests::const_iterator di;
+  for (di = _dests.begin(); di != _dests.end(); ++di) {
+    writer->write_pointer(datagram, (*di).second);
+  }
+
+  // We don't write out _read_source_image, _source_image,
+  // _got_dest_image, or _dest_image; these must be reread each
+  // session.
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -656,7 +855,7 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int TextureImage::
 int TextureImage::
 complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
 complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
-  nassertr((int)plist.size() >= _num_placement * 2 + _num_sources, 0);
+  nassertr((int)plist.size() >= _num_placement * 2 + _num_sources + _num_dests, 0);
   int index = 0;
   int index = 0;
 
 
   int i;
   int i;
@@ -680,6 +879,13 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
     index++;
     index++;
   }
   }
 
 
+  for (i = 0; i < _num_dests; i++) {
+    DestTextureImage *dest;
+    DCAST_INTO_R(dest, plist[index], index);
+    _dests.insert(Dests::value_type(dest->get_filename(), dest));
+    index++;
+  }
+
   return index;
   return index;
 }
 }
 
 
@@ -716,6 +922,8 @@ fillin(DatagramIterator &scan, BamReader *manager) {
   ImageFile::fillin(scan, manager);
   ImageFile::fillin(scan, manager);
   set_name(scan.get_string());
   set_name(scan.get_string());
 
 
+  _is_surprise = scan.get_bool();
+
   _actual_assigned_groups.fillin(scan, manager);
   _actual_assigned_groups.fillin(scan, manager);
 
 
   _num_placement = scan.get_uint32();
   _num_placement = scan.get_uint32();
@@ -723,4 +931,6 @@ fillin(DatagramIterator &scan, BamReader *manager) {
 
 
   _num_sources = scan.get_uint32();
   _num_sources = scan.get_uint32();
   manager->read_pointers(scan, this, _num_sources);
   manager->read_pointers(scan, this, _num_sources);
+  _num_dests = scan.get_uint32();
+  manager->read_pointers(scan, this, _num_dests);
 }
 }

+ 16 - 3
pandatool/src/egg-palettize/textureImage.h

@@ -20,6 +20,7 @@
 #include <set>
 #include <set>
 
 
 class SourceTextureImage;
 class SourceTextureImage;
+class DestTextureImage;
 class TexturePlacement;
 class TexturePlacement;
 class EggFile;
 class EggFile;
 
 
@@ -55,15 +56,17 @@ public:
   bool get_omit() const;
   bool get_omit() const;
   double get_repeat_threshold() const;
   double get_repeat_threshold() const;
   int get_margin() const;
   int get_margin() const;
-
+  bool is_surprise() const;
 
 
   SourceTextureImage *get_source(const Filename &filename, 
   SourceTextureImage *get_source(const Filename &filename, 
 				 const Filename &alpha_filename);
 				 const Filename &alpha_filename);
 
 
   SourceTextureImage *get_preferred_source();
   SourceTextureImage *get_preferred_source();
-  bool get_size();
+
+  void copy_unplaced();
 
 
   const PNMImage &read_source_image();
   const PNMImage &read_source_image();
+  const PNMImage &get_dest_image();
 
 
   void write_source_pathnames(ostream &out, int indent_level = 0) const;
   void write_source_pathnames(ostream &out, int indent_level = 0) const;
   void write_scale_info(ostream &out, int indent_level = 0);
   void write_scale_info(ostream &out, int indent_level = 0);
@@ -71,6 +74,8 @@ public:
 private:
 private:
   typedef set<EggFile *> EggFiles;
   typedef set<EggFile *> EggFiles;
   typedef vector<EggFile *> WorkingEggs;
   typedef vector<EggFile *> WorkingEggs;
+  typedef map<string, SourceTextureImage *> Sources;
+  typedef map<string, DestTextureImage *> Dests;
 
 
   static int compute_egg_count(PaletteGroup *group, 
   static int compute_egg_count(PaletteGroup *group, 
 			       const WorkingEggs &egg_files);
 			       const WorkingEggs &egg_files);
@@ -78,9 +83,14 @@ private:
   void assign_to_groups(const PaletteGroups &groups);
   void assign_to_groups(const PaletteGroups &groups);
   void consider_grayscale();
   void consider_grayscale();
 
 
+  void remove_old_dests(const Dests &a, const Dests &b);
+  void copy_new_dests(const Dests &a, const Dests &b);
+
+private:
   TextureRequest _request;
   TextureRequest _request;
   TextureProperties _pre_txa_properties;
   TextureProperties _pre_txa_properties;
   SourceTextureImage *_preferred_source;
   SourceTextureImage *_preferred_source;
+  bool _is_surprise;
 
 
   PaletteGroups _explicitly_assigned_groups;
   PaletteGroups _explicitly_assigned_groups;
   PaletteGroups _actual_assigned_groups;
   PaletteGroups _actual_assigned_groups;
@@ -90,11 +100,13 @@ private:
   typedef map<PaletteGroup *, TexturePlacement *> Placement;
   typedef map<PaletteGroup *, TexturePlacement *> Placement;
   Placement _placement;
   Placement _placement;
 
 
-  typedef map<string, SourceTextureImage *> Sources;
   Sources _sources;
   Sources _sources;
+  Dests _dests;
 
 
   bool _read_source_image;
   bool _read_source_image;
   PNMImage _source_image;
   PNMImage _source_image;
+  bool _got_dest_image;
+  PNMImage _dest_image;
 
 
 
 
   // The TypedWriteable interface follows.
   // The TypedWriteable interface follows.
@@ -113,6 +125,7 @@ private:
   // don't use them otherwise.
   // don't use them otherwise.
   int _num_placement;
   int _num_placement;
   int _num_sources;
   int _num_sources;
+  int _num_dests;
 
 
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {

+ 67 - 8
pandatool/src/egg-palettize/texturePlacement.cxx

@@ -9,6 +9,7 @@
 #include "paletteGroup.h"
 #include "paletteGroup.h"
 #include "paletteImage.h"
 #include "paletteImage.h"
 #include "palettizer.h"
 #include "palettizer.h"
+#include "eggFile.h"
 
 
 #include <indent.h>
 #include <indent.h>
 #include <datagram.h>
 #include <datagram.h>
@@ -30,6 +31,7 @@ TexturePlacement() {
   _texture = (TextureImage *)NULL;
   _texture = (TextureImage *)NULL;
   _group = (PaletteGroup *)NULL;
   _group = (PaletteGroup *)NULL;
   _image = (PaletteImage *)NULL;
   _image = (PaletteImage *)NULL;
+  _dest = (DestTextureImage *)NULL;
   _has_uvs = false;
   _has_uvs = false;
   _size_known = false;
   _size_known = false;
   _is_filled = true;
   _is_filled = true;
@@ -55,6 +57,7 @@ TexturePlacement(TextureImage *texture, PaletteGroup *group) :
   }
   }
 
 
   _image = (PaletteImage *)NULL;
   _image = (PaletteImage *)NULL;
+  _dest = (DestTextureImage *)NULL;
   _has_uvs = false;
   _has_uvs = false;
   _size_known = false;
   _size_known = false;
   _is_filled = false;
   _is_filled = false;
@@ -119,8 +122,11 @@ get_group() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TexturePlacement::
 void TexturePlacement::
 add_egg(TextureReference *reference) {
 add_egg(TextureReference *reference) {
+  reference->mark_egg_stale();
+  /*
   _has_uvs = false;
   _has_uvs = false;
   _size_known = false;
   _size_known = false;
+  */
   _references.insert(reference);
   _references.insert(reference);
 }
 }
 
 
@@ -132,11 +138,53 @@ add_egg(TextureReference *reference) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TexturePlacement::
 void TexturePlacement::
 remove_egg(TextureReference *reference) {
 remove_egg(TextureReference *reference) {
-  _has_uvs = false;
-  _size_known = false;
+  reference->mark_egg_stale();
+  /*
+    _has_uvs = false;
+    _size_known = false;
+  */
   _references.erase(reference);
   _references.erase(reference);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePlacement::mark_eggs_stale
+//       Access: Public
+//  Description: Marks all the egg files that reference this placement
+//               stale.  Presumably this is called after moving the
+//               texture around in the palette or something.
+////////////////////////////////////////////////////////////////////
+void TexturePlacement::
+mark_eggs_stale() {
+  References::iterator ri;
+  for (ri = _references.begin(); ri != _references.end(); ++ri) {
+    TextureReference *reference = (*ri);
+
+    reference->mark_egg_stale();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePlacement::set_dest
+//       Access: Public
+//  Description: Sets the DestTextureImage that corresponds to this
+//               texture as it was copied to the install directory.
+////////////////////////////////////////////////////////////////////
+void TexturePlacement::
+set_dest(DestTextureImage *dest) {
+  _dest = dest;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePlacement::get_dest
+//       Access: Public
+//  Description: Returns the DestTextureImage that corresponds to this
+//               texture as it was copied to the install directory.
+////////////////////////////////////////////////////////////////////
+DestTextureImage *TexturePlacement::
+get_dest() const {
+  return _dest;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TexturePlacement::determine_size
 //     Function: TexturePlacement::determine_size
 //       Access: Public
 //       Access: Public
@@ -358,7 +406,9 @@ get_y_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 double TexturePlacement::
 double TexturePlacement::
 get_uv_area() const {
 get_uv_area() const {
-  nassertr(_has_uvs, 0.0);
+  if (!_has_uvs) {
+    return 0.0;
+  }
 
 
   TexCoordd range = _position._max_uv - _position._min_uv;
   TexCoordd range = _position._max_uv - _position._min_uv;
   return range[0] * range[1];
   return range[0] * range[1];
@@ -462,8 +512,7 @@ get_placed_y_size() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 double TexturePlacement::
 double TexturePlacement::
 get_placed_uv_area() const {
 get_placed_uv_area() const {
-  nassertr(_has_uvs, 0.0);
-
+  nassertr(is_placed(), 0);
   TexCoordd range = _placed._max_uv - _placed._min_uv;
   TexCoordd range = _placed._max_uv - _placed._min_uv;
   return range[0] * range[1];
   return range[0] * range[1];
 }
 }
@@ -502,8 +551,9 @@ force_replace() {
   if (_image != (PaletteImage *)NULL) {
   if (_image != (PaletteImage *)NULL) {
     _image->unplace(this);
     _image->unplace(this);
     _image = (PaletteImage *)NULL;
     _image = (PaletteImage *)NULL;
-    _omit_reason = OR_working;
   }
   }
+  _omit_reason = OR_working;
+  mark_eggs_stale();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -519,7 +569,10 @@ force_replace() {
 void TexturePlacement::
 void TexturePlacement::
 omit_solitary() {
 omit_solitary() {
   nassertv(is_placed());
   nassertv(is_placed());
-  _omit_reason = OR_solitary;
+  if (_omit_reason != OR_solitary) {
+    mark_eggs_stale();
+    _omit_reason = OR_solitary;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -531,7 +584,10 @@ omit_solitary() {
 void TexturePlacement::
 void TexturePlacement::
 not_solitary() {
 not_solitary() {
   nassertv(is_placed());
   nassertv(is_placed());
-  _omit_reason = OR_none;
+  if (_omit_reason != OR_none) {
+    mark_eggs_stale();
+    _omit_reason = OR_none;
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -807,6 +863,9 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
   writer->write_pointer(datagram, _texture);
   writer->write_pointer(datagram, _texture);
   writer->write_pointer(datagram, _group);
   writer->write_pointer(datagram, _group);
   writer->write_pointer(datagram, _image);
   writer->write_pointer(datagram, _image);
+
+  // We don't write _dest.  That can safely be redetermined each
+  // session.
   
   
   datagram.add_bool(_has_uvs);
   datagram.add_bool(_has_uvs);
   datagram.add_bool(_size_known);
   datagram.add_bool(_size_known);

+ 6 - 1
pandatool/src/egg-palettize/texturePlacement.h

@@ -16,8 +16,8 @@
 
 
 #include <set>
 #include <set>
 
 
-
 class TextureImage;
 class TextureImage;
+class DestTextureImage;
 class PaletteGroup;
 class PaletteGroup;
 class PaletteImage;
 class PaletteImage;
 class PalettePage;
 class PalettePage;
@@ -47,6 +47,10 @@ public:
 
 
   void add_egg(TextureReference *reference);
   void add_egg(TextureReference *reference);
   void remove_egg(TextureReference *reference);
   void remove_egg(TextureReference *reference);
+  void mark_eggs_stale();
+
+  void set_dest(DestTextureImage *dest);
+  DestTextureImage *get_dest() const;
 
 
   bool determine_size();
   bool determine_size();
   OmitReason get_omit_reason() const;
   OmitReason get_omit_reason() const;
@@ -83,6 +87,7 @@ private:
   TextureImage *_texture;
   TextureImage *_texture;
   PaletteGroup *_group;
   PaletteGroup *_group;
   PaletteImage *_image;
   PaletteImage *_image;
+  DestTextureImage *_dest;
 
 
   bool _has_uvs;
   bool _has_uvs;
   bool _size_known;
   bool _size_known;

+ 39 - 5
pandatool/src/egg-palettize/textureReference.cxx

@@ -7,8 +7,10 @@
 #include "textureImage.h"
 #include "textureImage.h"
 #include "paletteImage.h"
 #include "paletteImage.h"
 #include "sourceTextureImage.h"
 #include "sourceTextureImage.h"
+#include "destTextureImage.h"
 #include "texturePlacement.h"
 #include "texturePlacement.h"
 #include "palettizer.h"
 #include "palettizer.h"
+#include "eggFile.h"
 
 
 #include <indent.h>
 #include <indent.h>
 #include <eggTexture.h>
 #include <eggTexture.h>
@@ -32,6 +34,7 @@ TypeHandle TextureReference::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 TextureReference::
 TextureReference::
 TextureReference() {
 TextureReference() {
+  _egg_file = (EggFile *)NULL;
   _egg_tex = (EggTexture *)NULL;
   _egg_tex = (EggTexture *)NULL;
   _tex_mat = LMatrix3d::ident_mat();
   _tex_mat = LMatrix3d::ident_mat();
   _inv_tex_mat = LMatrix3d::ident_mat();
   _inv_tex_mat = LMatrix3d::ident_mat();
@@ -62,7 +65,8 @@ TextureReference::
 //               extracted from an egg file.
 //               extracted from an egg file.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextureReference::
 void TextureReference::
-from_egg(EggData *data, EggTexture *egg_tex) {
+from_egg(EggFile *egg_file, EggData *data, EggTexture *egg_tex) {
+  _egg_file = egg_file;
   _egg_tex = egg_tex;
   _egg_tex = egg_tex;
   _egg_data = data;
   _egg_data = data;
 
 
@@ -112,6 +116,16 @@ from_egg(EggData *data, EggTexture *egg_tex) {
   _wrap_v = egg_tex->determine_wrap_v();
   _wrap_v = egg_tex->determine_wrap_v();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureReference::get_egg_file
+//       Access: Public
+//  Description: Returns the EggFile that references this texture.
+////////////////////////////////////////////////////////////////////
+EggFile *TextureReference::
+get_egg_file() const {
+  return _egg_file;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureReference::get_source
 //     Function: TextureReference::get_source
 //       Access: Public
 //       Access: Public
@@ -237,6 +251,19 @@ get_placement() const {
   return _placement;
   return _placement;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureReference::mark_egg_stale
+//       Access: Public
+//  Description: Marks the egg file that shares this reference as
+//               stale.
+////////////////////////////////////////////////////////////////////
+void TextureReference::
+mark_egg_stale() {
+  if (_egg_file != (EggFile *)NULL) {
+    _egg_file->mark_stale();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextureReference::update_egg
 //     Function: TextureReference::update_egg
 //       Access: Public
 //       Access: Public
@@ -261,9 +288,9 @@ update_egg() {
     // The texture does not appear on a palette.  This is the easy
     // The texture does not appear on a palette.  This is the easy
     // case; we simply have to update the texture reference to the new
     // case; we simply have to update the texture reference to the new
     // texture location.
     // texture location.
-    TextureImage *texture = _placement->get_texture();
-    nassertv(texture != (TextureImage *)NULL);
-    texture->update_egg_tex(_egg_tex);
+    DestTextureImage *dest = _placement->get_dest();
+    nassertv(dest != (DestTextureImage *)NULL);
+    dest->update_egg_tex(_egg_tex);
     return;
     return;
   }
   }
 
 
@@ -601,6 +628,7 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
   // We don't write _egg_tex, _egg_data, or _tex_mat; that's specific
   // We don't write _egg_tex, _egg_data, or _tex_mat; that's specific
   // to the session.
   // to the session.
 
 
+  writer->write_pointer(datagram, _egg_file);
   writer->write_pointer(datagram, _source_texture);
   writer->write_pointer(datagram, _source_texture);
   writer->write_pointer(datagram, _placement);
   writer->write_pointer(datagram, _placement);
 
 
@@ -626,9 +654,14 @@ write_datagram(BamWriter *writer, Datagram &datagram) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 int TextureReference::
 int TextureReference::
 complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
 complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
-  nassertr((int)plist.size() >= 2, 0);
+  nassertr((int)plist.size() >= 3, 0);
   int index = 0;
   int index = 0;
 
 
+  if (plist[index] != (TypedWriteable *)NULL) {
+    DCAST_INTO_R(_egg_file, plist[index], index);
+  }
+  index++;
+
   if (plist[index] != (TypedWriteable *)NULL) {
   if (plist[index] != (TypedWriteable *)NULL) {
     DCAST_INTO_R(_source_texture, plist[index], index);
     DCAST_INTO_R(_source_texture, plist[index], index);
   }
   }
@@ -672,6 +705,7 @@ make_TextureReference(const FactoryParams &params) {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextureReference::
 void TextureReference::
 fillin(DatagramIterator &scan, BamReader *manager) {
 fillin(DatagramIterator &scan, BamReader *manager) {
+  manager->read_pointer(scan, this);  // _egg_file
   manager->read_pointer(scan, this);  // _source_texture
   manager->read_pointer(scan, this);  // _source_texture
   manager->read_pointer(scan, this);  // _placement
   manager->read_pointer(scan, this);  // _placement
 
 

+ 6 - 1
pandatool/src/egg-palettize/textureReference.h

@@ -16,6 +16,7 @@
 class TextureImage;
 class TextureImage;
 class SourceTextureImage;
 class SourceTextureImage;
 class Filename;
 class Filename;
+class EggFile;
 class EggData;
 class EggData;
 class EggTexture;
 class EggTexture;
 class EggGroupNode;
 class EggGroupNode;
@@ -34,8 +35,9 @@ public:
   TextureReference();
   TextureReference();
   ~TextureReference();
   ~TextureReference();
 
 
-  void from_egg(EggData *data, EggTexture *egg_tex);
+  void from_egg(EggFile *egg_file, EggData *data, EggTexture *egg_tex);
 
 
+  EggFile *get_egg_file() const;
   SourceTextureImage *get_source() const;
   SourceTextureImage *get_source() const;
   TextureImage *get_texture() const;
   TextureImage *get_texture() const;
 
 
@@ -50,6 +52,7 @@ public:
   void clear_placement();
   void clear_placement();
   TexturePlacement *get_placement() const;
   TexturePlacement *get_placement() const;
 
 
+  void mark_egg_stale();
   void update_egg();
   void update_egg();
 
 
   void output(ostream &out) const;
   void output(ostream &out) const;
@@ -68,8 +71,10 @@ private:
   static LVector2d translate_uv(const TexCoordd &min_uv,
   static LVector2d translate_uv(const TexCoordd &min_uv,
 				const TexCoordd &max_uv);
 				const TexCoordd &max_uv);
 
 
+  EggFile *_egg_file;
   EggTexture *_egg_tex;
   EggTexture *_egg_tex;
   EggData *_egg_data;
   EggData *_egg_data;
+
   LMatrix3d _tex_mat, _inv_tex_mat;
   LMatrix3d _tex_mat, _inv_tex_mat;
   SourceTextureImage *_source_texture;
   SourceTextureImage *_source_texture;
   TexturePlacement *_placement;
   TexturePlacement *_placement;

+ 26 - 9
pandatool/src/egg-palettize/txaLine.cxx

@@ -56,10 +56,21 @@ parse(const string &line) {
 
 
   vector_string::iterator wi;
   vector_string::iterator wi;
   for (wi = words.begin(); wi != words.end(); ++wi) {
   for (wi = words.begin(); wi != words.end(); ++wi) {
-    _patterns.push_back(GlobPattern(*wi));
+    const string &word = (*wi);
+
+    // If the pattern ends in the string ".egg", and only if it ends
+    // in this string, it is deemed an egg pattern and will only be
+    // tested against egg files.  If it ends in anything else, it is
+    // deemed a texture pattern and will only be tested against
+    // textures.
+    if (word.length() > 4 && word.substr(word.length() - 4) == ".egg") {
+      _egg_patterns.push_back(GlobPattern(word));
+    } else {
+      _texture_patterns.push_back(GlobPattern(*wi));
+    }
   }
   }
 
 
-  if (_patterns.empty()) {
+  if (_egg_patterns.empty() && _texture_patterns.empty()) {
     nout << "No texture or egg filenames given.\n";
     nout << "No texture or egg filenames given.\n";
     return false;
     return false;
   }
   }
@@ -229,8 +240,8 @@ match_egg(EggFile *egg_file) const {
 
 
   bool matched_any = false;
   bool matched_any = false;
   Patterns::const_iterator pi;
   Patterns::const_iterator pi;
-  for (pi = _patterns.begin(); 
-       pi != _patterns.end() && !matched_any;
+  for (pi = _egg_patterns.begin(); 
+       pi != _egg_patterns.end() && !matched_any;
        ++pi) {
        ++pi) {
     matched_any = (*pi).matches(name);
     matched_any = (*pi).matches(name);
   }
   }
@@ -259,8 +270,7 @@ match_egg(EggFile *egg_file) const {
     }
     }
   }
   }
 
 
-  egg_file->_assigned_groups.make_union
-    (egg_file->_assigned_groups, _palette_groups);
+  egg_file->match_txa_groups(_palette_groups);
 
 
   if (got_cont) {
   if (got_cont) {
     // If we have the "cont" keyword, we should keep scanning for
     // If we have the "cont" keyword, we should keep scanning for
@@ -270,6 +280,8 @@ match_egg(EggFile *egg_file) const {
 
 
   // Otherwise, in the normal case, a match ends the search for
   // Otherwise, in the normal case, a match ends the search for
   // matches.
   // matches.
+  egg_file->clear_surprise();
+
   return true;
   return true;
 }
 }
 
 
@@ -290,8 +302,8 @@ match_texture(TextureImage *texture) const {
 
 
   bool matched_any = false;
   bool matched_any = false;
   Patterns::const_iterator pi;
   Patterns::const_iterator pi;
-  for (pi = _patterns.begin(); 
-       pi != _patterns.end() && !matched_any;
+  for (pi = _texture_patterns.begin(); 
+       pi != _texture_patterns.end() && !matched_any;
        ++pi) {
        ++pi) {
     matched_any = (*pi).matches(name);
     matched_any = (*pi).matches(name);
   }
   }
@@ -387,6 +399,8 @@ match_texture(TextureImage *texture) const {
 
 
   // Otherwise, in the normal case, a match ends the search for
   // Otherwise, in the normal case, a match ends the search for
   // matches.
   // matches.
+  texture->_is_surprise = false;
+
   return true;
   return true;
 }
 }
 
 
@@ -398,7 +412,10 @@ match_texture(TextureImage *texture) const {
 void TxaLine::
 void TxaLine::
 output(ostream &out) const {
 output(ostream &out) const {
   Patterns::const_iterator pi;
   Patterns::const_iterator pi;
-  for (pi = _patterns.begin(); pi != _patterns.end(); ++pi) {
+  for (pi = _texture_patterns.begin(); pi != _texture_patterns.end(); ++pi) {
+    out << (*pi) << " ";
+  }
+  for (pi = _egg_patterns.begin(); pi != _egg_patterns.end(); ++pi) {
     out << (*pi) << " ";
     out << (*pi) << " ";
   }
   }
   out << ":";
   out << ":";

+ 2 - 1
pandatool/src/egg-palettize/txaLine.h

@@ -39,7 +39,8 @@ public:
 
 
 private:
 private:
   typedef vector<GlobPattern> Patterns;
   typedef vector<GlobPattern> Patterns;
-  Patterns _patterns;
+  Patterns _texture_patterns;
+  Patterns _egg_patterns;
 
 
   enum SizeType {
   enum SizeType {
     ST_none,
     ST_none,