David Rose 25 лет назад
Родитель
Сommit
c0d69295be

+ 19 - 20
pandatool/src/egg-palettize-old/Sources.pp

@@ -1,23 +1,22 @@
-#begin bin_target
-  #define TARGET egg-palettize-new
-  #define LOCAL_LIBS \
-    eggbase progbase
-  #define OTHER_LIBS \
-    egg:c linmath:c putil:c express:c pnmimage:c pnmimagetypes:c \
-    pandaegg:m panda:m pandaexpress:m \
-    dtoolutil:c dconfig:c dtool:m pystub
+//  #begin bin_target
+//    #define TARGET egg-palettize-new
+//    #define LOCAL_LIBS \
+//      eggbase progbase
+//    #define OTHER_LIBS \
+//      egg:c linmath:c putil:c express:c pnmimage:c pnmimagetypes:c \
+//      pandaegg:m panda:m pandaexpress:m \
+//      dtoolutil:c dconfig:c dtool:m pystub
 
-  #define SOURCES \
-    attribFile.cxx attribFile.h config_egg_palettize.cxx \
-    eggPalettize.cxx eggPalettize.h \
-    palette.cxx palette.h paletteGroup.cxx \
-    paletteGroup.h pTexture.cxx pTexture.h sourceEgg.cxx \
-    sourceEgg.h string_utils.cxx string_utils.h \
-    textureEggRef.cxx textureEggRef.h textureOmitReason.h \
-    texturePacking.cxx \
-    texturePacking.h userAttribLine.cxx userAttribLine.h
+//    #define SOURCES \
+//      attribFile.cxx attribFile.h config_egg_palettize.cxx \
+//      eggPalettize.cxx eggPalettize.h \
+//      palette.cxx palette.h paletteGroup.cxx \
+//      paletteGroup.h pTexture.cxx pTexture.h sourceEgg.cxx \
+//      sourceEgg.h string_utils.cxx string_utils.h \
+//      textureEggRef.cxx textureEggRef.h textureOmitReason.h \
+//      texturePacking.cxx \
+//      texturePacking.h userAttribLine.cxx userAttribLine.h
 
-  #define INSTALL_HEADERS
-
-#end bin_target
+//    #define INSTALL_HEADERS
 
+//  #end bin_target

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

@@ -0,0 +1,28 @@
+#begin bin_target
+  #define TARGET egg-palettize
+  #define LOCAL_LIBS \
+    eggbase progbase
+  #define OTHER_LIBS \
+    egg:c loader:c linmath:c putil:c express:c pnmimage:c pnmimagetypes:c \
+    pandaegg:m panda:m pandaexpress:m \
+    dtoolutil:c dconfig:c dtool:m pystub
+
+  #define SOURCES \
+    config_egg_palettize.cxx config_egg_palettize.h \
+    eggFile.cxx eggFile.h eggPalettize.cxx eggPalettize.h \
+    imageFile.cxx imageFile.h omitReason.cxx omitReason.h \
+    paletteGroup.h paletteGroup.cxx \
+    paletteGroups.h paletteGroups.cxx paletteImage.h paletteImage.cxx \
+    palettePage.cxx palettePage.h \
+    palettizer.cxx palettizer.h \
+    sourceTextureImage.cxx sourceTextureImage.h string_utils.cxx \
+    string_utils.h textureImage.cxx textureImage.h \
+    texturePlacement.cxx texturePlacement.h \
+    texturePosition.cxx texturePosition.h \
+    textureProperties.cxx textureProperties.h textureReference.cxx \
+    textureReference.h textureRequest.h textureRequest.cxx \
+    txaFile.cxx txaFile.h \
+    txaLine.cxx txaLine.h
+
+#end bin_target
+

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

@@ -0,0 +1,54 @@
+// Filename: config_egg_palettize.cxx
+// Created by:  drose (01Dec00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "config_egg_palettize.h"
+#include "palettizer.h"
+#include "eggFile.h"
+#include "paletteGroup.h"
+#include "paletteGroups.h"
+#include "textureReference.h"
+#include "textureProperties.h"
+#include "imageFile.h"
+#include "sourceTextureImage.h"
+#include "textureImage.h"
+#include "paletteImage.h"
+#include "texturePlacement.h"
+#include "texturePosition.h"
+#include "palettePage.h"
+
+#include <dconfig.h>
+
+Configure(config_egg_palettize);
+
+ConfigureFn(config_egg_palettize) {
+  Palettizer::init_type();
+  EggFile::init_type();
+  PaletteGroup::init_type();
+  PaletteGroups::init_type();
+  TextureReference::init_type();
+  TextureProperties::init_type();
+  ImageFile::init_type();
+  SourceTextureImage::init_type();
+  TextureImage::init_type();
+  PaletteImage::init_type();
+  TexturePlacement::init_type();
+  TexturePosition::init_type();
+  PalettePage::init_type();
+
+  // Registration of writeable object's creation functions with
+  // BamReader's factory
+  Palettizer::register_with_read_factory();
+  EggFile::register_with_read_factory();
+  PaletteGroup::register_with_read_factory();
+  PaletteGroups::register_with_read_factory();
+  TextureReference::register_with_read_factory();
+  TextureProperties::register_with_read_factory();
+  SourceTextureImage::register_with_read_factory();
+  TextureImage::register_with_read_factory();
+  PaletteImage::register_with_read_factory();
+  TexturePlacement::register_with_read_factory();
+  TexturePosition::register_with_read_factory();
+  PalettePage::register_with_read_factory();
+}

+ 427 - 0
pandatool/src/egg-palettize/eggPalettize.cxx

@@ -0,0 +1,427 @@
+// Filename: eggPalettize.cxx
+// Created by:  drose (28Nov00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "eggPalettize.h"
+#include "palettizer.h"
+#include "eggFile.h"
+#include "string_utils.h"
+
+#include <pnmFileTypeRegistry.h>
+#include <pnmFileType.h>
+#include <eggData.h>
+#include <bamFile.h>
+
+#include <stdio.h>
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPalettize::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+EggPalettize::
+EggPalettize() : EggMultiFilter(true) {
+  set_program_description
+    ("egg-palettize attempts to pack several texture maps from various models "
+     "together into one or more palette images, for improved rendering performance "
+     "and ease of texture management.  It can also resize textures on the fly, "
+     "whether or not they are actually placed on a palette.\n\n"
+     
+     "egg-palettize reads and writes an AttributesFile, which contains instructions "
+     "from the user about resizing particular textures, as well as the complete "
+     "information necessary to reconstruct the palettization from past runs, "
+     "including references to other egg files that may share this palette.  This "
+     "is designed to allow multiple egg files to use the same palette, without "
+     "having to process them all at once.\n\n"
+     
+     "Note that it is not even necessary to specify any egg files at all on the "
+     "command line; egg-palettize can be run on an existing AttributesFiles by "
+     "itself to freshen up a palette when necessary.");
+
+
+  clear_runlines();
+  add_runline("[opts] attribfile.txa file.egg [file.egg ...]");
+
+  // We always have EggMultiBase's -f on: force complete load.  In
+  // fact, we use -f for our own purposes, below.
+  remove_option("f");
+  _force_complete = true;
+
+  add_option
+    ("a", "filename", 0, 
+     "Read the indicated file as the .txa file.  The default is textures.txa.",
+     &EggPalettize::dispatch_filename, NULL, &_txa_filename);
+
+  add_option
+    ("pi", "", 0, 
+     "Do not process anything, but instead report the detailed palettization "
+     "information.",
+     &EggPalettize::dispatch_none, &_report_pi);
+
+  add_option
+    ("s", "", 0, 
+     "Do not process anything, but report statistics on palette "
+     "and texture itilization.",
+     &EggPalettize::dispatch_none, &_statistics_only);
+
+  // We redefine -d using add_option() instead of redescribe_option()
+  // so it gets listed along with these other options that relate.
+  add_option
+    ("d", "dirname", 0, 
+     "The directory in which to write the palettized egg files.  This is "
+     "only necessary if more than one egg file is processed at the same "
+     "time; if it is included, each egg file will be processed and written "
+     "into the indicated directory.",
+     &EggPalettize::dispatch_filename, &_got_output_dirname, &_output_dirname);
+  add_option
+    ("dm", "dirname", 0, 
+     "The directory in which to place all maps: generated palettes, "
+     "as well as images which were not placed on palettes "
+     "(but may have been resized).  If this contains the string %s, "
+     "this will be replaced with the \"dir\" string associated with a "
+     "palette group.",
+     &EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname);
+  add_option
+    ("dr", "dirname", 0, 
+     "The directory to make map filenames relative to when writing egg "
+     "files.  If specified, this should be an initial substring of -dm.",
+     &EggPalettize::dispatch_filename, &_got_rel_dirname, &_rel_dirname);
+  add_option
+    ("g", "group", 0, 
+     "The default palette group that egg files will be assigned to if they "
+     "are not explicitly assigned to any other group.",
+     &EggPalettize::dispatch_string, &_got_default_groupname, &_default_groupname);
+  add_option
+    ("gdir", "name", 0, 
+     "The \"dir\" string to associate with the default palette group "
+     "specified with -g, if no other dir name is given in the .txa file.",
+     &EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir);
+  
+  add_option
+    ("f", "", 0, 
+     "Force an optimal packing.  By default, textures are added to "
+     "existing palettes without disturbing them, which can lead to "
+     "suboptimal packing.  Including this switch forces the palettes "
+     "to be rebuilt if necessary to optimize the packing, but this "
+     "may invalidate other egg files which share this palette.",
+     &EggPalettize::dispatch_none, &_force_optimal);
+  add_option
+    ("F", "", 0, 
+     "Force a redo of everything.  This is useful in case something "
+     "has gotten out of sync and the old palettes are just bad.",
+     &EggPalettize::dispatch_none, &_force_redo_all);
+  add_option
+    ("R", "", 0, 
+     "Resize mostly-empty palettes to their minimal size.",
+     &EggPalettize::dispatch_none, &_optimal_resize);
+  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);
+  add_option
+    ("C", "", 0, 
+     "Aggressively keep the map directory clean by deleting unused "
+     "textures from previous passes.  This will remain in effect across "
+     "future sessions until -offC is specified.",
+     &EggPalettize::dispatch_none, &_aggressively_clean_mapdir);
+  add_option
+    ("offC", "", 0, 
+     "Turn off the aggressive cleaning specified by a past -C.",
+     &EggPalettize::dispatch_none, &_off_aggressively_clean_mapdir);
+  add_option
+    ("2", "", 0, 
+     "Force textures that have been left out of the palette to a size "
+     "which is an integer power of 2.  They will be scaled down to "
+     "achieve this.  This will remain in effect across future sessions "
+     "until -off2 is specified.",
+     &EggPalettize::dispatch_none, &_force_power_2);
+  add_option
+    ("off2", "", 0, 
+     "Turn off the power-of-2 scaling specified by a past -2.",
+     &EggPalettize::dispatch_none, &_off_force_power_2);
+  add_option
+    ("type", "imagetype[,alphatype]", 0, 
+     "Specify the type of image file to output.  All image files, whether "
+     "palettes or unplaced textures, will be converted to files of this "
+     "type.  If the optional alpha type is specified, then an alpha channel, "
+     "if present, will be written as a separate file of the indicated "
+     "type--useful if the primary image type does not support alpha.  "
+     "Use '-type list' to show the available image types.",
+     &EggPalettize::dispatch_string, &_got_image_type, &_image_type);
+  add_option
+    ("m", "margin", 0, 
+     "Specify the default margin size.",
+     &EggPalettize::dispatch_int, &_got_margin, &_margin);
+  add_option
+    ("r", "percent", 0, 
+     "A repeating texture may still be palettized if it does not repeat "
+     "very many times, by adding multiple adjacent copies of the "
+     "texture to the palette.  This parameter specifies the cutoff "
+     "threshold for this.  This is the maximum "
+     "percentage a texture will be expanded to palettize a repeating "
+     "texture.  If this is set to 100, no repeating textures will be "
+     "palettized; if this is set to 200, a texture that repeats twice "
+     "will be palettized by adding it to the palette twice.",
+     &EggPalettize::dispatch_double, &_got_repeat_threshold, &_repeat_threshold);
+  add_option
+    ("P", "x,y", 0, 
+     "Specify the default palette size.",
+     &EggPalettize::dispatch_int_pair, &_got_palette_size, _pal_size);
+  add_option
+    ("nolock", "", 0, 
+     "Don't attempt to lock the .pi file before rewriting it.  Use "
+     "with extreme caution, as multiple processes running on the same "
+     ".pi file may overwrite each other.  Use this only if the lock "
+     "cannot be achieved for some reason.",
+     &EggPalettize::dispatch_none, &_dont_lock_pi);
+  add_option
+    ("H", "", 0, 
+     "Describe the syntax of the attributes file.",
+     &EggPalettize::dispatch_none, &_describe_input_file);
+
+  _txa_filename = "textures.txa";
+  _color_type = (PNMFileType *)NULL;
+  _alpha_type = (PNMFileType *)NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPalettize::handle_args
+//       Access: Protected, Virtual
+//  Description: Does something with the additional arguments on the
+//               command line (after all the -options have been
+//               parsed).  Returns true if the arguments are good,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool EggPalettize::
+handle_args(ProgramBase::Args &args) {
+  if (_describe_input_file) {
+    describe_input_file();
+    exit(1);
+  }
+
+  if (_got_image_type) {
+    if (_image_type == "list" ||
+	!parse_image_type_request(_image_type, _color_type, _alpha_type)) {
+      nout << "\nKnown image types are:\n";
+      PNMFileTypeRegistry::get_ptr()->write_types(nout, 2);
+      nout << "\n";
+      exit(1);
+    }
+  }
+
+  return EggMultiFilter::handle_args(args);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPalettize::describe_input_file
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void EggPalettize::
+describe_input_file() {
+  nout <<
+    "An attributes file consists mostly of lines describing desired sizes of "
+    "texture maps.  The format resembles, but is not identical to, that of "
+    "the qtess input file.  Examples:\n\n"
+
+    "  texturename.rgb : 64 64\n"
+    "  texture-a.rgb texture-b.rgb : 32 16 2\n"
+    "  *.rgb : 50%\n"
+    "  eyelids.rgb : 16 16 omit\n\n"
+
+    "In general, each line consists of one or more filenames (and can "
+    "contain shell globbing characters like '*' or '?'), and a colon "
+    "followed by a size request.  For each texture appearing in an egg "
+    "file, the input list is scanned from the beginning and the first "
+    "line that matches the filename defines the size of the texture.\n\n"
+
+    "A size request may be either a pair of numbers, giving a specific x y "
+    "size of the texture, or it may be a scale factor in the form of a "
+    "percentage.  It may also include an additional number, giving a margin "
+    "for this particular texture (otherwise the default margin is "
+    "applied).  Finally, the keyword 'omit' may be included along with the "
+    "size to specify that the texture should not be placed in a palette.\n\n"
+
+    "The attributes file may also assign certain egg files into various "
+    "named palette groups.  The syntax is similar to the above:\n\n"
+
+    "  car-blue.egg : main\n"
+    "  road.egg house.egg : main\n"
+    "  plane.egg : phase2 main\n"
+    "  car*.egg : phase2\n\n"
+
+    "Any number of egg files may be named on one line, and the group of "
+    "egg files may be simultaneously assigned to one or more groups.  Each "
+    "named group represents a semi-independent collection of textures; a "
+    "different set of palette images will be created for each group.  Each "
+    "texture that is referenced by a given egg file will be palettized "
+    "in one of the groups assigned to the egg file.  Also see the "
+    ":group command, below, which defines relationships between the "
+    "different groups.\n\n"
+
+    "There are some other special lines that may appear in this second, "
+    "along with the resize commands.  They begin with a colon to "
+    "distinguish them from the resize commands.  They are:\n\n"
+
+    "  :palette xsize ysize\n\n"
+
+    "This specifies the size of the palette file(s) to be created.  It "
+    "overrides the -s command-line option.\n\n"
+
+    "  :margin msize\n\n"
+
+    "This specifies the size of the default margin for all subsequent "
+    "resize commands.  This may appear several times in a given file.\n\n"
+
+    "  :group groupname1 with groupname2 [groupname3 ...]\n\n"
+
+    "This indicates that the palette group named by groupname1 should "
+    "be allowed to shared textures with those on groupname2 or groupname3, "
+    "etc.  In other words, that whenever palette group groupname1 is in "
+    "texture memory, we can assume that palette groups groupname2 and "
+    "groupname3 will also be in memory.  Textures that already exist on "
+    "groupname2 and other dependent groups will not be added to groupname1; "
+    "instead, egg files will reference the textures directly from the "
+    "other palettes.\n\n"
+
+    "Comments may appear freely throughout the file, and are set off by a "
+    "hash mark (#).\n";
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggPalettize::run
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void EggPalettize::
+run() {
+  if (!_txa_filename.exists()) {
+    nout << _txa_filename << " does not exist; cannot run.\n";
+    exit(1);
+  }
+
+  Filename pi_filename = _txa_filename;
+  pi_filename.set_extension("boo");
+
+  BamFile pi_file;
+
+  if (!pi_filename.exists()) {
+    nout << pi_filename << " does not exist; starting palettization from scratch.\n";
+    pal = new Palettizer;
+
+  } else {
+    // Read the Palettizer object from the Bam file written
+    // previously.  This will recover all of the state saved from the
+    // 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";
+      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";
+      exit(1);
+    }
+
+    if (!obj->is_of_type(Palettizer::get_class_type())) {
+      nout << pi_filename << " exists, but does not appear to be "
+	   << "an egg-palettize output file.  Perhaps you "
+	   << "should remove it so a new one can be created.\n";
+      exit(1);
+    }
+
+    pi_file.close();
+
+    pal = DCAST(Palettizer, obj);
+  }
+
+  if (_report_pi) {
+    pal->report_pi();
+    exit(0);
+  }
+
+  if (_got_image_type) {
+    pal->_color_type = _color_type;
+    pal->_alpha_type = _alpha_type;
+  }
+
+  if (_got_margin) {
+    pal->_margin = _margin;
+  }
+
+  if (_got_repeat_threshold) {
+    pal->_repeat_threshold = _repeat_threshold;
+  }
+
+  if (_got_palette_size) {
+    pal->_pal_x_size = _pal_size[0];
+    pal->_pal_y_size = _pal_size[1];
+  }
+
+  if (_force_power_2) {
+    pal->_force_power_2 = true;
+  }
+  if (_off_force_power_2) {
+    pal->_force_power_2 = false;
+  }
+  if (_aggressively_clean_mapdir) {
+    pal->_aggressively_clean_mapdir = true;
+  }
+  if (_off_aggressively_clean_mapdir) {
+    pal->_aggressively_clean_mapdir = false;
+  }
+
+  if (_got_default_groupname) {
+    pal->_default_groupname = _default_groupname;
+  } else {
+    pal->_default_groupname = _txa_filename.get_basename_wo_extension();
+  }
+
+  TxaFile txa_file;
+  if (!txa_file.read(_txa_filename)) {
+    exit(1);
+  }
+
+  Eggs::const_iterator ei;
+  for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
+    EggData *egg_data = (*ei);
+    Filename source_filename = egg_data->get_egg_filename();
+    Filename dest_filename = get_output_filename(source_filename);
+    string name = source_filename.get_basename();
+
+    EggFile *egg_file = pal->get_egg_file(name);
+    egg_file->from_command_line(egg_data, source_filename, dest_filename);
+
+    pal->_command_line_eggs.push_back(egg_file);
+  }
+
+  pal->run(txa_file);
+
+  if (!pi_file.open_write(pi_filename) ||
+      !pi_file.write_object(pal)) {
+    nout << "Unable to write palettization information to " << pi_filename
+	 << "\n";
+    exit(1);
+  }
+
+  pi_file.close();
+
+  write_eggs();
+}
+
+int 
+main(int argc, char *argv[]) {
+  EggPalettize prog;
+  prog.parse_command_line(argc, argv);
+  prog.run();
+  return 0;
+}
+
+

+ 78 - 0
pandatool/src/egg-palettize/eggPalettize.h

@@ -0,0 +1,78 @@
+// Filename: eggPalettize.h
+// Created by:  drose (28Nov00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef EGGPALETTIZE_H
+#define EGGPALETTIZE_H
+
+#include <pandatoolbase.h>
+
+#include "txaFile.h"
+
+#include <eggMultiFilter.h>
+
+#include <vector>
+
+class PNMFileType;
+class EggFile;
+class PaletteGroup;
+class TextureImage;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : EggPalettize
+// Description : This is the program wrapper for egg-palettize, but it
+//               mainly serves to read in all the command-line
+//               parameters and then invoke the Palettizer.
+////////////////////////////////////////////////////////////////////
+class EggPalettize : public EggMultiFilter {
+public:
+  EggPalettize();
+
+  virtual bool handle_args(Args &args);
+
+  void describe_input_file();
+
+  void run();
+
+  // The following parameter values specifically relate to textures
+  // and palettes.  These values are copied to the Palettizer.
+  Filename _txa_filename;
+  string _map_dirname;
+  bool _got_map_dirname;
+  Filename _rel_dirname;
+  bool _got_rel_dirname;
+  string _default_groupname;
+  bool _got_default_groupname;
+  string _default_groupdir;
+  bool _got_default_groupdir;
+  int _pal_size[2];
+  bool _got_palette_size;
+  int _margin;
+  bool _got_margin;
+  double _repeat_threshold;
+  bool _got_repeat_threshold;
+  bool _force_power_2;
+  bool _off_force_power_2;
+  bool _aggressively_clean_mapdir;
+  bool _off_aggressively_clean_mapdir;
+  string _image_type;
+  bool _got_image_type;
+  PNMFileType *_color_type;
+  PNMFileType *_alpha_type;
+
+private:
+  // The following values control behavior specific to this session.
+  // They're not saved for future sessions.
+  bool _report_pi;
+  bool _statistics_only;
+  bool _force_optimal;
+  bool _force_redo_all;
+  bool _optimal_resize;
+  bool _touch_eggs;
+  bool _dont_lock_pi;
+
+  bool _describe_input_file;
+};
+
+#endif

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

@@ -0,0 +1,378 @@
+// Filename: paletteGroup.cxx
+// Created by:  drose (30Nov00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "paletteGroup.h"
+#include "palettePage.h"
+#include "texturePlacement.h"
+#include "textureImage.h"
+
+#include <indent.h>
+#include <datagram.h>
+#include <datagramIterator.h>
+#include <bamReader.h>
+#include <bamWriter.h>
+
+TypeHandle PaletteGroup::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+PaletteGroup::
+PaletteGroup() {
+  _egg_count = 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::set_dirname
+//       Access: Public
+//  Description: Sets the directory name associated with the palette
+//               group.  This is an optional feature that can be used
+//               to place the maps for the different palette groups
+//               into different install directories.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+set_dirname(const string &dirname) {
+  _dirname = dirname;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::group_with
+//       Access: Public
+//  Description: Indicates a dependency of this group on some other
+//               group.  This means that the textures assigned to this
+//               group may be considered successfully assigned if they
+//               are actually placed in the other group.  In practice,
+//               this means that the textures associated with the
+//               other palette group will always be resident at
+//               runtime when textures from this palette group are
+//               required.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+group_with(PaletteGroup *other) {
+  _dependent.insert(other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::get_groups
+//       Access: Public
+//  Description: Returns the set of groups this group depends on.
+////////////////////////////////////////////////////////////////////
+const PaletteGroups &PaletteGroup::
+get_groups() const {
+  return _dependent;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::increment_egg_count
+//       Access: Public
+//  Description: Increments by one the number of egg files that are
+//               known to reference this PaletteGroup.  This is
+//               designed to aid the heuristics in texture placing;
+//               it's useful to know how many different egg files are
+//               sharing a particular PaletteGroup.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+increment_egg_count() {
+  _egg_count++;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::get_egg_count
+//       Access: Public
+//  Description: Returns the number of egg files that share this
+//               PaletteGroup.
+////////////////////////////////////////////////////////////////////
+int PaletteGroup::
+get_egg_count() const {
+  return _egg_count;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::get_page
+//       Access: Public
+//  Description: Returns the page associated with the indicated
+//               properties.  If no page object has yet been created,
+//               creates one.
+////////////////////////////////////////////////////////////////////
+PalettePage *PaletteGroup::
+get_page(const TextureProperties &properties) {
+  Pages::iterator pi = _pages.find(properties);
+  if (pi != _pages.end()) {
+    return (*pi).second;
+  }
+
+  PalettePage *page = new PalettePage(this, properties);
+  bool inserted = _pages.insert(Pages::value_type(properties, page)).second;
+  nassertr(inserted, page);
+  return page;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::prepare
+//       Access: Public
+//  Description: Marks the indicated Texture as ready for placing
+//               somewhere within this group, and returns a
+//               placeholder TexturePlacement object.  The texture is
+//               not placed immediately, but may be placed later when
+//               place_all() is called; at this time, the
+//               TexturePlacement fields will be filled in as
+//               appropriate.
+////////////////////////////////////////////////////////////////////
+TexturePlacement *PaletteGroup::
+prepare(TextureImage *texture) {
+  TexturePlacement *placement = new TexturePlacement(texture, this);
+  _placements.insert(placement);
+
+  return placement;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::unplace
+//       Access: Public
+//  Description: Removes the texture from its position on a
+//               PaletteImage, if it has been so placed.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+unplace(TexturePlacement *placement) {
+  nassertv(placement->get_group() == this);
+
+  Placements::iterator pi;
+  pi = _placements.find(placement);
+  if (pi != _placements.end()) {
+    _placements.erase(pi);
+
+    if (placement->is_placed()) {
+      placement->get_page()->unplace(placement);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::place_all
+//       Access: Public
+//  Description: Once all the textures have been assigned to this
+//               group, try to place them all onto suitable
+//               PaletteImages.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+place_all() {
+  // First, go through our prepared textures and assign each unplaced
+  // one to an appropriate page.
+  Placements::iterator pli;
+  for (pli = _placements.begin(); pli != _placements.end(); ++pli) {
+    TexturePlacement *placement = (*pli);
+    if (placement->get_omit_reason() == OR_working) {
+      PalettePage *page = get_page(placement->get_properties());
+      page->assign(placement);
+    }
+  }
+
+  // Then, go through the pages and actually do the placing.
+  Pages::iterator pai;
+  for (pai = _pages.begin(); pai != _pages.end(); ++pai) {
+    PalettePage *page = (*pai).second;
+    page->place_all();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::write_image_info
+//       Access: Public
+//  Description: Writes a list of the PaletteImages associated with
+//               this group, and all of their textures, to the
+//               indicated output stream.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+write_image_info(ostream &out, int indent_level) const {
+  Pages::const_iterator pai;
+  for (pai = _pages.begin(); pai != _pages.end(); ++pai) {
+    PalettePage *page = (*pai).second;
+    page->write_image_info(out, indent_level);
+  }
+
+  Placements::iterator pli;
+  for (pli = _placements.begin(); pli != _placements.end(); ++pli) {
+    TexturePlacement *placement = (*pli);
+    if (placement->get_omit_reason() != OR_none) {
+      indent(out, indent_level) 
+	<< placement->get_texture()->get_name()
+	<< " unplaced because ";
+      switch (placement->get_omit_reason()) {
+      case OR_repeats:
+	out << "repeats (" 
+	    << floor(placement->get_uv_area() * 10000.0 + 0.5) / 100.0
+	    << "%)";
+	break;
+
+      case OR_size:
+	out << "size (" << placement->get_x_size() << " " 
+	    << placement->get_y_size() << ")";
+	break;
+
+      default:
+	out << placement->get_omit_reason();
+      }
+      out << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::update_images
+//       Access: Public
+//  Description: Regenerates each PaletteImage on this group that needs
+//               it.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+update_images() {
+  Pages::iterator pai;
+  for (pai = _pages.begin(); pai != _pages.end(); ++pai) {
+    PalettePage *page = (*pai).second;
+    page->update_images();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::register_with_read_factory
+//       Access: Public, Static
+//  Description: Registers the current object as something that can be
+//               read from a Bam file.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+register_with_read_factory() {
+  BamReader::get_factory()->
+    register_factory(get_class_type(), make_PaletteGroup);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::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 PaletteGroup::
+write_datagram(BamWriter *writer, Datagram &datagram) {
+  datagram.add_string(get_name());
+  datagram.add_string(_dirname);
+  _dependent.write_datagram(writer, datagram);
+
+  datagram.add_uint32(_placements.size());
+  Placements::const_iterator pli;
+  for (pli = _placements.begin(); pli != _placements.end(); ++pli) {
+    writer->write_pointer(datagram, (*pli));
+  }
+
+  datagram.add_uint32(_pages.size());
+  Pages::const_iterator pai;
+  for (pai = _pages.begin(); pai != _pages.end(); ++pai) {
+    writer->write_pointer(datagram, (*pai).second);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::complete_pointers
+//       Access: Public, Virtual
+//  Description: Called after the object is otherwise completely read
+//               from a Bam file, this function's job is to store the
+//               pointers that were retrieved from the Bam file for
+//               each pointer object written.  The return value is the
+//               number of pointers processed from the list.
+////////////////////////////////////////////////////////////////////
+int PaletteGroup::
+complete_pointers(vector_typedWriteable &plist, BamReader *manager) {
+  nassertr((int)plist.size() >= _num_placements + _num_pages, 0);
+  int index = 0;
+
+  int i;
+  for (i = 0; i < _num_placements; i++) {
+    TexturePlacement *placement;
+    DCAST_INTO_R(placement, plist[index], index);
+    index++;
+    bool inserted = _placements.insert(placement).second;
+    nassertr(inserted, index);
+  }
+
+  // We must store the list of pages in a temporary vector first.  We
+  // can't put them directly into the map because the map requires
+  // that all the pointers in the page's get_properties() member have
+  // been filled in, which may not have happened yet.
+  _load_pages.reserve(_num_pages);
+  for (i = 0; i < _num_pages; i++) {
+    PalettePage *page;
+    DCAST_INTO_R(page, plist[index], index);
+    index++;
+    _load_pages.push_back(page);
+  }
+
+  return index;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::finalize
+//       Access: Public, Virtual
+//  Description: This method is called by the BamReader after all
+//               pointers everywhere in the world have been completely
+//               read in.  It's a hook at which the object can do
+//               whatever final setup it requires that depends on
+//               other pointers being valid.
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+finalize() {
+  // Now we can copy the pages into the actual map.
+  vector<PalettePage *>::const_iterator pi;
+  for (pi = _load_pages.begin(); pi != _load_pages.end(); ++pi) {
+    PalettePage *page = (*pi);
+    bool inserted = _pages.
+      insert(Pages::value_type(page->get_properties(), page)).second;
+    nassertv(inserted);
+  }
+
+  _load_pages.clear();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::make_PaletteGroup
+//       Access: Protected, Static
+//  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 *PaletteGroup::
+make_PaletteGroup(const FactoryParams &params) {
+  PaletteGroup *me = new PaletteGroup;
+  BamReader *manager;
+  Datagram packet;
+
+  parse_params(params, manager, packet);
+  DatagramIterator scan(packet);
+
+  me->fillin(scan, manager);
+  manager->register_finalize(me);
+  return me;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PaletteGroup::fillin
+//       Access: Protected
+//  Description: Reads the binary data from the given datagram
+//               iterator, which was written by a previous call to
+//               write_datagram().
+////////////////////////////////////////////////////////////////////
+void PaletteGroup::
+fillin(DatagramIterator &scan, BamReader *manager) {
+  set_name(scan.get_string());
+  _dirname = scan.get_string();
+  _dependent.fillin(scan, manager);
+
+  _num_placements = scan.get_uint32();
+  manager->read_pointers(scan, this, _num_placements);
+
+  _num_pages = scan.get_uint32();
+  manager->read_pointers(scan, this, _num_pages);
+}

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

@@ -0,0 +1,111 @@
+// Filename: paletteGroup.h
+// Created by:  drose (28Nov00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef PALETTEGROUP_H
+#define PALETTEGROUP_H
+
+#include <pandatoolbase.h>
+
+#include "paletteGroups.h"
+#include "textureProperties.h"
+
+#include <namable.h>
+#include <typedWriteable.h>
+
+#include <set>
+
+class EggFile;
+class TexturePlacement;
+class PalettePage;
+class TextureImage;
+
+////////////////////////////////////////////////////////////////////
+// 	 Class : PaletteGroup
+// Description : This is the highest level of grouping for
+//               TextureImages.  Textures are assigned to one or
+//               several PaletteGroups based on the information in the
+//               .txa file; each PaletteGroup is conceptually a
+//               collection of textures that are to be moved around
+//               (into texture memory, downloaded, etc.) in one big
+//               chunk.  It is the set of all textures that may be
+//               displayed together at any given time.
+////////////////////////////////////////////////////////////////////
+class PaletteGroup : public TypedWriteable, public Namable {
+public:
+  PaletteGroup();
+
+  void set_dirname(const string &dirname);
+
+  void group_with(PaletteGroup *other);
+  const PaletteGroups &get_groups() const;
+
+  void increment_egg_count();
+  int get_egg_count() const;
+
+  PalettePage *get_page(const TextureProperties &properties);
+
+  TexturePlacement *prepare(TextureImage *texture);
+
+  void unplace(TexturePlacement *placement);
+
+  void place_all();
+
+  void write_image_info(ostream &out, int indent_level = 0) const;
+  void update_images();
+
+private:
+  string _dirname;
+  int _egg_count;
+  PaletteGroups _dependent;
+
+  typedef set<TexturePlacement *> Placements;
+  Placements _placements;
+
+  typedef map<TextureProperties, PalettePage *> Pages;
+  Pages _pages;
+
+
+  // The TypedWriteable interface follows.
+public:
+  static void register_with_read_factory();
+  virtual void write_datagram(BamWriter *writer, Datagram &datagram); 
+  virtual int complete_pointers(vector_typedWriteable &plist, 
+				BamReader *manager);
+  virtual void finalize();
+
+protected:
+  static TypedWriteable *make_PaletteGroup(const FactoryParams &params);
+  void fillin(DatagramIterator &scan, BamReader *manager);
+
+private:
+  // These values are only filled in while reading from the bam file;
+  // don't use them otherwise.
+  int _num_placements;
+  int _num_pages;
+  vector<PalettePage *> _load_pages;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TypedWriteable::init_type();
+    Namable::init_type();
+    register_type(_type_handle, "PaletteGroup",
+		  TypedWriteable::get_class_type(),
+		  Namable::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class PaletteGroups;
+};
+
+#endif
+

+ 118 - 0
pandatool/src/egg-palettize/string_utils.cxx

@@ -0,0 +1,118 @@
+// Filename: string_utils.cxx
+// Created by:  drose (30Nov00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#include "string_utils.h"
+
+#include <pnmFileType.h>
+#include <pnmFileTypeRegistry.h>
+
+
+string 
+trim_left(const string &str) {
+  size_t begin = 0;
+  while (begin < str.size() && isspace(str[begin])) {
+    begin++;
+  }
+
+  return str.substr(begin);
+}
+
+string 
+trim_right(const string &str) {
+  size_t begin = 0;
+  size_t end = str.size();
+  while (end > begin && isspace(str[end - 1])) {
+    end--;
+  }
+
+  return str.substr(begin, end - begin);
+}
+
+void
+extract_words(const string &str, vector_string &words) {
+  size_t pos = 0;
+  while (pos < str.length() && isspace(str[pos])) {
+    pos++;
+  }
+  while (pos < str.length()) {
+    size_t word_start = pos;
+    while (pos < str.length() && !isspace(str[pos])) {
+      pos++;
+    }
+    words.push_back(str.substr(word_start, pos - word_start));
+
+    while (pos < str.length() && isspace(str[pos])) {
+      pos++;
+    }
+  }
+}
+
+// Extracts the first word of the string into param, and the remainder
+// of the line into value.
+void 
+extract_param_value(const string &str, string &param, string &value) {
+  size_t i = 0;
+
+  // First, skip all whitespace at the beginning.
+  while (i < str.length() && isspace(str[i])) {
+    i++;
+  }
+
+  size_t start = i;
+
+  // Now skip to the end of the whitespace.
+  while (i < str.length() && !isspace(str[i])) {
+    i++;
+  }
+
+  size_t end = i;
+
+  param = str.substr(start, end - start);
+
+  // Skip a little bit further to the start of the value.
+  while (i < str.length() && isspace(str[i])) {
+    i++;
+  }
+  value = trim_right(str.substr(i));
+}
+
+
+bool
+parse_image_type_request(const string &word, PNMFileType *&color_type,
+			 PNMFileType *&alpha_type) {
+  PNMFileTypeRegistry *registry = PNMFileTypeRegistry::get_ptr();
+  color_type = (PNMFileType *)NULL;
+  alpha_type = (PNMFileType *)NULL;
+
+  string color_name = word;
+  string alpha_name;
+  size_t comma = word.find(',');
+  if (comma != string::npos) {
+    // If we have a comma in the image_type, it's two types: a color
+    // type and an alpha type.
+    color_name = word.substr(0, comma);
+    alpha_name = word.substr(comma + 1);
+  }
+  
+  if (!color_name.empty()) {
+    color_type = registry->get_type_from_extension(color_name);
+    if (color_type == (PNMFileType *)NULL) {
+      nout << "Image file type '" << color_name << "' is unknown.\n";
+      return false;
+    }
+  }
+  
+  if (!alpha_name.empty()) {
+    alpha_type = registry->get_type_from_extension(alpha_name);
+    if (alpha_type == (PNMFileType *)NULL) {
+      nout << "Image file type '" << alpha_name << "' is unknown.\n";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+

+ 24 - 0
pandatool/src/egg-palettize/string_utils.h

@@ -0,0 +1,24 @@
+// Filename: string_utils.h
+// Created by:  drose (30Nov00)
+// 
+////////////////////////////////////////////////////////////////////
+
+#ifndef STRING_UTILS_H
+#define STRING_UTILS_H
+
+#include <pandatoolbase.h>
+#include <vector_string.h>
+
+class PNMFileType;
+
+string trim_left(const string &str);
+string trim_right(const string &str);
+
+void extract_words(const string &str, vector_string &words);
+void extract_param_value(const string &str, string &param, string &value);
+
+bool parse_image_type_request(const string &word, PNMFileType *&color_type,
+			      PNMFileType *&alpha_type);
+
+#endif
+