From 07a1bca3a0f53befd715a6f25ee19656f926bd2f Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 6 Dec 2000 04:51:13 +0000 Subject: [PATCH] *** empty log message *** --- .../pnmimagetypes/pnmFileTypeJPGWriter.cxx | 8 +- pandatool/src/egg-palettize/Sources.pp | 2 + .../egg-palettize/config_egg_palettize.cxx | 3 + .../src/egg-palettize/destTextureImage.cxx | 126 ++++++++ .../src/egg-palettize/destTextureImage.h | 65 +++++ pandatool/src/egg-palettize/eggFile.cxx | 271 ++++++++++++++++-- pandatool/src/egg-palettize/eggFile.h | 25 +- pandatool/src/egg-palettize/eggPalettize.cxx | 96 +++++-- pandatool/src/egg-palettize/eggPalettize.h | 3 +- .../src/egg-palettize/filenameUnifier.cxx | 109 +++++++ pandatool/src/egg-palettize/filenameUnifier.h | 40 +++ pandatool/src/egg-palettize/imageFile.cxx | 66 +++-- pandatool/src/egg-palettize/imageFile.h | 3 +- pandatool/src/egg-palettize/paletteGroup.cxx | 88 ++++++ pandatool/src/egg-palettize/paletteGroup.h | 7 + pandatool/src/egg-palettize/paletteGroups.cxx | 10 + pandatool/src/egg-palettize/paletteGroups.h | 1 + pandatool/src/egg-palettize/paletteImage.cxx | 8 +- pandatool/src/egg-palettize/palettizer.cxx | 260 ++++++++++++++--- pandatool/src/egg-palettize/palettizer.h | 25 +- .../src/egg-palettize/sourceTextureImage.cxx | 13 - .../src/egg-palettize/sourceTextureImage.h | 4 +- pandatool/src/egg-palettize/textureImage.cxx | 240 +++++++++++++++- pandatool/src/egg-palettize/textureImage.h | 19 +- .../src/egg-palettize/texturePlacement.cxx | 75 ++++- .../src/egg-palettize/texturePlacement.h | 7 +- .../src/egg-palettize/textureReference.cxx | 44 ++- .../src/egg-palettize/textureReference.h | 7 +- pandatool/src/egg-palettize/txaLine.cxx | 35 ++- pandatool/src/egg-palettize/txaLine.h | 3 +- 30 files changed, 1484 insertions(+), 179 deletions(-) create mode 100644 pandatool/src/egg-palettize/destTextureImage.cxx create mode 100644 pandatool/src/egg-palettize/destTextureImage.h create mode 100644 pandatool/src/egg-palettize/filenameUnifier.cxx create mode 100644 pandatool/src/egg-palettize/filenameUnifier.h diff --git a/panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx b/panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx index b681b92775..ccf34f1e67 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeJPGWriter.cxx +++ b/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_height = _y_size; - if (is_grayscale()) + if (is_grayscale()) { cinfo.input_components = 1; - else + cinfo.in_color_space = JCS_GRAYSCALE; + } else { 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. * (You must set at least cinfo.in_color_space before calling this, * since the defaults depend on the source color space.) diff --git a/pandatool/src/egg-palettize/Sources.pp b/pandatool/src/egg-palettize/Sources.pp index 2a80bfe315..4c59f103db 100644 --- a/pandatool/src/egg-palettize/Sources.pp +++ b/pandatool/src/egg-palettize/Sources.pp @@ -9,7 +9,9 @@ #define SOURCES \ config_egg_palettize.cxx config_egg_palettize.h \ + destTextureImage.cxx destTextureImage.h \ eggFile.cxx eggFile.h eggPalettize.cxx eggPalettize.h \ + filenameUnifier.cxx filenameUnifier.h \ imageFile.cxx imageFile.h omitReason.cxx omitReason.h \ paletteGroup.h paletteGroup.cxx \ paletteGroups.h paletteGroups.cxx paletteImage.h paletteImage.cxx \ diff --git a/pandatool/src/egg-palettize/config_egg_palettize.cxx b/pandatool/src/egg-palettize/config_egg_palettize.cxx index 88fe73eb32..1a58d1de4a 100644 --- a/pandatool/src/egg-palettize/config_egg_palettize.cxx +++ b/pandatool/src/egg-palettize/config_egg_palettize.cxx @@ -12,6 +12,7 @@ #include "textureProperties.h" #include "imageFile.h" #include "sourceTextureImage.h" +#include "destTextureImage.h" #include "textureImage.h" #include "paletteImage.h" #include "texturePlacement.h" @@ -31,6 +32,7 @@ ConfigureFn(config_egg_palettize) { TextureProperties::init_type(); ImageFile::init_type(); SourceTextureImage::init_type(); + DestTextureImage::init_type(); TextureImage::init_type(); PaletteImage::init_type(); TexturePlacement::init_type(); @@ -46,6 +48,7 @@ ConfigureFn(config_egg_palettize) { TextureReference::register_with_read_factory(); TextureProperties::register_with_read_factory(); SourceTextureImage::register_with_read_factory(); + DestTextureImage::register_with_read_factory(); TextureImage::register_with_read_factory(); PaletteImage::register_with_read_factory(); TexturePlacement::register_with_read_factory(); diff --git a/pandatool/src/egg-palettize/destTextureImage.cxx b/pandatool/src/egg-palettize/destTextureImage.cxx new file mode 100644 index 0000000000..dfb302ac17 --- /dev/null +++ b/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 +#include +#include +#include + +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 ¶ms) { + 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); +} diff --git a/pandatool/src/egg-palettize/destTextureImage.h b/pandatool/src/egg-palettize/destTextureImage.h new file mode 100644 index 0000000000..3bd3d8e87d --- /dev/null +++ b/pandatool/src/egg-palettize/destTextureImage.h @@ -0,0 +1,65 @@ +// Filename: destTextureImage.h +// Created by: drose (05Dec00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef DESTTEXTUREIMAGE_H +#define DESTTEXTUREIMAGE_H + +#include + +#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 ¶ms); + 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 + diff --git a/pandatool/src/egg-palettize/eggFile.cxx b/pandatool/src/egg-palettize/eggFile.cxx index e1415cca32..1f988b4a38 100644 --- a/pandatool/src/egg-palettize/eggFile.cxx +++ b/pandatool/src/egg-palettize/eggFile.cxx @@ -8,6 +8,7 @@ #include "paletteGroup.h" #include "texturePlacement.h" #include "palettizer.h" +#include "filenameUnifier.h" #include #include @@ -26,6 +27,10 @@ TypeHandle EggFile::_type_handle; EggFile:: EggFile() { _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; _source_filename = source_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); TextureReference *ref = new TextureReference; - ref->from_egg(_data, egg_tex); + ref->from_egg(this, _data, egg_tex); _textures.push_back(ref); } @@ -93,6 +103,39 @@ get_textures(set &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 // Access: Public @@ -102,22 +145,90 @@ get_textures(set &result) const { //////////////////////////////////////////////////////////////////// void EggFile:: 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 // Description: Returns the set of PaletteGroups that the egg file // has been explicitly assigned to in the .txa file. //////////////////////////////////////////////////////////////////// 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:: 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; for (ti = _textures.begin(); ti != _textures.end(); ++ti) { (*ti)->get_texture()->note_egg_file(this); } - _assigned_groups.make_complete(_assigned_groups); - PaletteGroups::const_iterator gi; - for (gi = _assigned_groups.begin(); - gi != _assigned_groups.end(); + for (gi = _complete_groups.begin(); + gi != _complete_groups.end(); ++gi) { (*gi)->increment_egg_count(); } @@ -174,7 +294,7 @@ choose_placements() { } else { // We need to select a new TexturePlacement. 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, // 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 // 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 // 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. - 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()); Textures::iterator ti; @@ -260,7 +473,14 @@ write_datagram(BamWriter *writer, Datagram &datagram) { 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:: 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 i; @@ -286,6 +506,11 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) { index++; } + if (plist[index] != (TypedWriteable *)NULL) { + DCAST_INTO_R(_default_group, plist[index], index); + } + index++; + return index; } @@ -320,11 +545,15 @@ make_EggFile(const FactoryParams ¶ms) { void EggFile:: fillin(DatagramIterator &scan, BamReader *manager) { 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(); 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(); } diff --git a/pandatool/src/egg-palettize/eggFile.h b/pandatool/src/egg-palettize/eggFile.h index d726adc7ab..3b516efca3 100644 --- a/pandatool/src/egg-palettize/eggFile.h +++ b/pandatool/src/egg-palettize/eggFile.h @@ -40,15 +40,29 @@ public: void scan_textures(); void get_textures(set &result) const; + void pre_txa_file(); + void match_txa_groups(const PaletteGroups &groups); 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 choose_placements(); + bool has_data() const; + 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; private: @@ -59,7 +73,12 @@ private: typedef vector 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. @@ -95,8 +114,6 @@ public: private: static TypeHandle _type_handle; - - friend class TxaLine; }; #endif diff --git a/pandatool/src/egg-palettize/eggPalettize.cxx b/pandatool/src/egg-palettize/eggPalettize.cxx index 8824b8cd79..0ffb52fb46 100644 --- a/pandatool/src/egg-palettize/eggPalettize.cxx +++ b/pandatool/src/egg-palettize/eggPalettize.cxx @@ -7,6 +7,7 @@ #include "palettizer.h" #include "eggFile.h" #include "string_utils.h" +#include "filenameUnifier.h" #include #include @@ -76,7 +77,7 @@ EggPalettize() : EggMultiFilter(true) { ("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, " + "(but may have been resized). If this contains the string %g, " "this will be replaced with the \"dir\" string associated with a " "palette group.", &EggPalettize::dispatch_string, &_got_map_dirname, &_map_dirname); @@ -97,7 +98,13 @@ EggPalettize() : EggMultiFilter(true) { &EggPalettize::dispatch_string, &_got_default_groupdir, &_default_groupdir); 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 " "existing palettes without disturbing them, which can lead to " "suboptimal packing. Including this switch forces the palettes " @@ -105,7 +112,7 @@ EggPalettize() : EggMultiFilter(true) { "may invalidate other egg files which share this palette.", &EggPalettize::dispatch_none, &_force_optimal); add_option - ("F", "", 0, + ("redo", "", 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); @@ -114,12 +121,10 @@ EggPalettize() : EggMultiFilter(true) { "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); + ("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 ("nolock", "", 0, @@ -244,38 +249,40 @@ run() { exit(1); } - Filename pi_filename = _txa_filename; - pi_filename.set_extension("boo"); + FilenameUnifier::set_txa_filename(_txa_filename); - BamFile pi_file; + Filename state_filename = _txa_filename; + state_filename.set_extension("boo"); - if (!pi_filename.exists()) { - nout << pi_filename << " does not exist; starting palettization from scratch.\n"; + BamFile state_file; + + if (!state_filename.exists()) { + nout << state_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"; + 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); } - 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); } 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 " << "should remove it so a new one can be created.\n"; exit(1); } - pi_file.close(); + state_file.close(); pal = DCAST(Palettizer, obj); } @@ -285,15 +292,26 @@ run() { exit(0); } + bool okflag = true; + + pal->read_txa_file(_txa_filename); + 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); + 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; @@ -309,18 +327,36 @@ run() { 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 (!pi_file.open_write(pi_filename) || - !pi_file.write_object(pal)) { - nout << "Unable to write palettization information to " << pi_filename + if (_redo_eggs) { + if (!pal->read_stale_eggs()) { + okflag = false; + } + } + + pal->generate_images(); + + 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"; exit(1); } - pi_file.close(); + state_file.close(); - write_eggs(); + if (!okflag) { + exit(1); + } } int diff --git a/pandatool/src/egg-palettize/eggPalettize.h b/pandatool/src/egg-palettize/eggPalettize.h index b7160d835f..1833c68576 100644 --- a/pandatool/src/egg-palettize/eggPalettize.h +++ b/pandatool/src/egg-palettize/eggPalettize.h @@ -43,10 +43,11 @@ private: // They're not saved for future sessions. bool _report_pi; bool _statistics_only; + bool _all_textures; bool _force_optimal; bool _force_redo_all; bool _optimal_resize; - bool _touch_eggs; + bool _redo_eggs; bool _dont_lock_pi; bool _describe_input_file; diff --git a/pandatool/src/egg-palettize/filenameUnifier.cxx b/pandatool/src/egg-palettize/filenameUnifier.cxx new file mode 100644 index 0000000000..8b046a28d5 --- /dev/null +++ b/pandatool/src/egg-palettize/filenameUnifier.cxx @@ -0,0 +1,109 @@ +// Filename: filenameUnifier.cxx +// Created by: drose (05Dec00) +// +//////////////////////////////////////////////////////////////////// + +#include "filenameUnifier.h" + +#include + +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; +} diff --git a/pandatool/src/egg-palettize/filenameUnifier.h b/pandatool/src/egg-palettize/filenameUnifier.h new file mode 100644 index 0000000000..8b8da5ad5a --- /dev/null +++ b/pandatool/src/egg-palettize/filenameUnifier.h @@ -0,0 +1,40 @@ +// Filename: filenameUnifier.h +// Created by: drose (05Dec00) +// +//////////////////////////////////////////////////////////////////// + +#ifndef FILENAMEUNIFIER_H +#define FILENAMEUNIFIER_H + +#include + +#include + +//////////////////////////////////////////////////////////////////// +// 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 + diff --git a/pandatool/src/egg-palettize/imageFile.cxx b/pandatool/src/egg-palettize/imageFile.cxx index e698b901dc..74f25e622b 100644 --- a/pandatool/src/egg-palettize/imageFile.cxx +++ b/pandatool/src/egg-palettize/imageFile.cxx @@ -4,6 +4,9 @@ //////////////////////////////////////////////////////////////////// #include "imageFile.h" +#include "palettizer.h" +#include "filenameUnifier.h" +#include "paletteGroup.h" #include #include @@ -118,8 +121,33 @@ update_properties(const TextureProperties &properties) { // automatically applied. //////////////////////////////////////////////////////////////////// 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) { _filename.set_extension @@ -189,17 +217,17 @@ read(PNMImage &image) const { nassertr(!_filename.empty(), false); image.set_type(_properties._color_type); - nout << "Reading " << _filename << "\n"; + nout << "Reading " << FilenameUnifier::make_user_filename(_filename) << "\n"; if (!image.read(_filename)) { nout << "Unable to read.\n"; return false; } - if (!_alpha_filename.empty()) { + if (!_alpha_filename.empty() && _alpha_filename.exists()) { // Read in a separate color image and an alpha channel image. PNMImage alpha_image; 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)) { nout << "Unable to read.\n"; return false; @@ -236,9 +264,11 @@ write(PNMImage &image) const { if (!image.has_alpha() || _properties._alpha_type == (PNMFileType *)NULL) { if (!_alpha_filename.empty() && _alpha_filename.exists()) { + nout << "Deleting " << FilenameUnifier::make_user_filename(_alpha_filename) << "\n"; _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)) { nout << "Unable to write.\n"; return false; @@ -256,12 +286,14 @@ write(PNMImage &image) const { } 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)) { nout << "Unable to write.\n"; 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)) { nout << "Unable to write.\n"; return false; @@ -277,11 +309,11 @@ write(PNMImage &image) const { void ImageFile:: unlink() { if (!_filename.empty() && _filename.exists()) { - nout << "Deleting " << _filename << "\n"; + nout << "Deleting " << FilenameUnifier::make_user_filename(_filename) << "\n"; _filename.unlink(); } if (!_alpha_filename.empty() && _alpha_filename.exists()) { - nout << "Deleting " << _alpha_filename << "\n"; + nout << "Deleting " << FilenameUnifier::make_user_filename(_alpha_filename) << "\n"; _alpha_filename.unlink(); } } @@ -298,6 +330,7 @@ update_egg_tex(EggTexture *egg_tex) const { egg_tex->set_filename(_filename); if (_properties._alpha_type != (PNMFileType *)NULL && + _properties.uses_alpha() && !_alpha_filename.empty()) { egg_tex->set_alpha_file(_alpha_filename); } else { @@ -315,10 +348,11 @@ update_egg_tex(EggTexture *egg_tex) const { //////////////////////////////////////////////////////////////////// void ImageFile:: output_filename(ostream &out) const { - out << _filename; + out << FilenameUnifier::make_user_filename(_filename); if (_properties._alpha_type != (PNMFileType *)NULL && + _properties.uses_alpha() && !_alpha_filename.empty()) { - out << " " << _alpha_filename; + out << " " << FilenameUnifier::make_user_filename(_alpha_filename); } } @@ -332,8 +366,8 @@ output_filename(ostream &out) const { void ImageFile:: write_datagram(BamWriter *writer, Datagram &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_int32(_x_size); datagram.add_int32(_y_size); @@ -349,8 +383,8 @@ write_datagram(BamWriter *writer, Datagram &datagram) { void ImageFile:: fillin(DatagramIterator &scan, BamReader *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(); _x_size = scan.get_int32(); _y_size = scan.get_int32(); diff --git a/pandatool/src/egg-palettize/imageFile.h b/pandatool/src/egg-palettize/imageFile.h index dd6a625200..80d73a800d 100644 --- a/pandatool/src/egg-palettize/imageFile.h +++ b/pandatool/src/egg-palettize/imageFile.h @@ -15,6 +15,7 @@ class PNMImage; class EggTexture; +class PaletteGroup; //////////////////////////////////////////////////////////////////// // Class : ImageFile @@ -36,7 +37,7 @@ public: const TextureProperties &get_properties() const; 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_alpha_filename() const; bool exists() const; diff --git a/pandatool/src/egg-palettize/paletteGroup.cxx b/pandatool/src/egg-palettize/paletteGroup.cxx index 481b093dd7..dcead03a62 100644 --- a/pandatool/src/egg-palettize/paletteGroup.cxx +++ b/pandatool/src/egg-palettize/paletteGroup.cxx @@ -24,6 +24,7 @@ TypeHandle PaletteGroup::_type_handle; PaletteGroup:: PaletteGroup() { _egg_count = 0; + _dependency_level = 0; } //////////////////////////////////////////////////////////////////// @@ -39,6 +40,41 @@ set_dirname(const string &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 // Access: Public @@ -66,6 +102,55 @@ get_groups() const { 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 // Access: Public @@ -261,6 +346,9 @@ write_datagram(BamWriter *writer, Datagram &datagram) { datagram.add_string(_dirname); _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()); Placements::const_iterator pli; for (pli = _placements.begin(); pli != _placements.end(); ++pli) { diff --git a/pandatool/src/egg-palettize/paletteGroup.h b/pandatool/src/egg-palettize/paletteGroup.h index 5f9181e778..930fe450af 100644 --- a/pandatool/src/egg-palettize/paletteGroup.h +++ b/pandatool/src/egg-palettize/paletteGroup.h @@ -37,10 +37,16 @@ public: PaletteGroup(); void set_dirname(const string &dirname); + bool has_dirname() const; + const string &get_dirname() const; + void clear_depends(); void group_with(PaletteGroup *other); const PaletteGroups &get_groups() const; + void set_dependency_level(int level); + int get_dependency_level() const; + void increment_egg_count(); int get_egg_count() const; @@ -59,6 +65,7 @@ private: string _dirname; int _egg_count; PaletteGroups _dependent; + int _dependency_level; typedef set Placements; Placements _placements; diff --git a/pandatool/src/egg-palettize/paletteGroups.cxx b/pandatool/src/egg-palettize/paletteGroups.cxx index 98500ec669..f4a8a4b274 100644 --- a/pandatool/src/egg-palettize/paletteGroups.cxx +++ b/pandatool/src/egg-palettize/paletteGroups.cxx @@ -143,6 +143,16 @@ make_intersection(const PaletteGroups &a, const PaletteGroups &b) { _groups.swap(i); } +//////////////////////////////////////////////////////////////////// +// Function: PaletteGroups::clear +// Access: Public +// Description: Empties the set. +//////////////////////////////////////////////////////////////////// +void PaletteGroups:: +clear() { + _groups.clear(); +} + //////////////////////////////////////////////////////////////////// // Function: PaletteGroups::empty // Access: Public diff --git a/pandatool/src/egg-palettize/paletteGroups.h b/pandatool/src/egg-palettize/paletteGroups.h index be9fe037da..1780446a2e 100644 --- a/pandatool/src/egg-palettize/paletteGroups.h +++ b/pandatool/src/egg-palettize/paletteGroups.h @@ -43,6 +43,7 @@ public: void make_complete(const PaletteGroups &a); void make_union(const PaletteGroups &a, const PaletteGroups &b); void make_intersection(const PaletteGroups &a, const PaletteGroups &b); + void clear(); bool empty() const; size_type size() const; diff --git a/pandatool/src/egg-palettize/paletteImage.cxx b/pandatool/src/egg-palettize/paletteImage.cxx index bbf16d057f..17c815894e 100644 --- a/pandatool/src/egg-palettize/paletteImage.cxx +++ b/pandatool/src/egg-palettize/paletteImage.cxx @@ -163,7 +163,7 @@ PaletteImage(PalettePage *page, int index) : name << page->get_group()->get_name() << "_palette_" << 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); 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 _index = scan.get_uint32(); + _new_image = scan.get_bool(); } diff --git a/pandatool/src/egg-palettize/palettizer.cxx b/pandatool/src/egg-palettize/palettizer.cxx index 84cb83dd30..df2fb3c81c 100644 --- a/pandatool/src/egg-palettize/palettizer.cxx +++ b/pandatool/src/egg-palettize/palettizer.cxx @@ -8,6 +8,7 @@ #include "textureImage.h" #include "string_utils.h" #include "paletteGroup.h" +#include "filenameUnifier.h" #include #include @@ -36,6 +37,7 @@ Palettizer() { // version number for all bam and boo files anywhere in the world. _pi_version = 0; + _map_dirname = "%g"; _margin = 2; _repeat_threshold = 250.0; _aggressively_clean_mapdir = true; @@ -61,6 +63,9 @@ void Palettizer:: report_pi() const { cout << "\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" << " margin: " << _margin << "\n" << " repeat threshold: " << _repeat_threshold << "%\n" @@ -108,8 +113,7 @@ report_pi() const { EggFiles::const_iterator ei; for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) { 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); } @@ -117,35 +121,82 @@ report_pi() const { Groups::const_iterator gi; for (gi = _groups.begin(); gi != _groups.end(); ++gi) { PaletteGroup *group = (*gi).second; + if (gi != _groups.begin()) { + cout << "\n"; + } cout << " " << group->get_name() << ": " << group->get_groups() << "\n"; group->write_image_info(cout, 4); - cout << "\n"; } - cout << "textures\n"; + cout << "\ntextures\n"; for (ti = _textures.begin(); ti != _textures.end(); ++ti) { TextureImage *texture = (*ti).second; 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"; } + //////////////////////////////////////////////////////////////////// -// Function: Palettizer::run +// Function: Palettizer::read_txa_file // Access: Public -// Description: +// Description: Reads in the .txa file and keeps it ready for +// matching textures and egg files. //////////////////////////////////////////////////////////////////// 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) { - 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"; exit(1); } - set 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 // line. @@ -156,23 +207,23 @@ run(const TxaFile &txa_file) { EggFile *egg_file = (*ei); 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(); } // Now match each of the textures mentioned in those egg files // against a line in the .txa file. - set::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) { TextureImage *texture = *ti; texture->pre_txa_file(); - txa_file.match_texture(texture); + _txa_file.match_texture(texture); 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 // 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) { TextureImage *texture = *ti; texture->assign_groups(); @@ -202,8 +253,8 @@ run(const TxaFile &txa_file) { // Now that *that's* done, we need to make sure the various // 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) { TextureImage *texture = *ti; texture->determine_placement_size(); @@ -216,22 +267,160 @@ run(const TxaFile &txa_file) { PaletteGroup *group = (*gi).second; group->place_all(); } +} - // Finally, generate all the images. +//////////////////////////////////////////////////////////////////// +// 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(); + } +} + +//////////////////////////////////////////////////////////////////// +// 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) { PaletteGroup *group = (*gi).second; 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 // Access: Public @@ -298,7 +487,11 @@ test_palette_group(const string &name) const { //////////////////////////////////////////////////////////////////// PaletteGroup *Palettizer:: 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; image->set_name(name); - image->set_filename(name); + // image->set_filename(name); _textures.insert(Textures::value_type(name, image)); return image; } @@ -358,9 +551,7 @@ void Palettizer:: write_datagram(BamWriter *writer, Datagram &datagram) { datagram.add_int32(_pi_version); 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_y_size); datagram.add_int32(_margin); @@ -477,9 +668,8 @@ void Palettizer:: fillin(DatagramIterator &scan, BamReader *manager) { _pi_version = scan.get_int32(); _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_y_size = scan.get_int32(); _margin = scan.get_int32(); diff --git a/pandatool/src/egg-palettize/palettizer.h b/pandatool/src/egg-palettize/palettizer.h index b22d1d54e1..2fcc5823e5 100644 --- a/pandatool/src/egg-palettize/palettizer.h +++ b/pandatool/src/egg-palettize/palettizer.h @@ -13,6 +13,7 @@ #include #include +#include #include class PNMFileType; @@ -33,7 +34,12 @@ public: Palettizer(); 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); PaletteGroup *get_palette_group(const string &name); @@ -53,13 +59,17 @@ public: RU_poly }; - // The following parameter values specifically relate to textures - // and palettes. These values are stored in the .pi file for future - // reference. - Filename _map_dirname; - Filename _rel_dirname; + // 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 + // and palettes. These values are stored in the bam file for future + // reference. + string _map_dirname; + Filename _rel_dirname; int _pal_x_size, _pal_y_size; int _margin; double _repeat_threshold; @@ -79,6 +89,9 @@ private: typedef vector CommandLineEggs; CommandLineEggs _command_line_eggs; + typedef set CommandLineTextures; + CommandLineTextures _command_line_textures; + typedef map Groups; Groups _groups; diff --git a/pandatool/src/egg-palettize/sourceTextureImage.cxx b/pandatool/src/egg-palettize/sourceTextureImage.cxx index 75ec78a153..b1bb4b44af 100644 --- a/pandatool/src/egg-palettize/sourceTextureImage.cxx +++ b/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 // Access: Public, Static diff --git a/pandatool/src/egg-palettize/sourceTextureImage.h b/pandatool/src/egg-palettize/sourceTextureImage.h index f14e46e487..c26b5d7a45 100644 --- a/pandatool/src/egg-palettize/sourceTextureImage.h +++ b/pandatool/src/egg-palettize/sourceTextureImage.h @@ -30,8 +30,6 @@ public: bool get_size(); bool read_header(); - void output(ostream &out) const; - private: bool _read_header; bool _successfully_read_header; @@ -67,7 +65,7 @@ private: INLINE ostream & operator << (ostream &out, const SourceTextureImage &source) { - source.output(out); + source.output_filename(out); return out; } diff --git a/pandatool/src/egg-palettize/textureImage.cxx b/pandatool/src/egg-palettize/textureImage.cxx index e3c2f727a8..4c9287b603 100644 --- a/pandatool/src/egg-palettize/textureImage.cxx +++ b/pandatool/src/egg-palettize/textureImage.cxx @@ -5,6 +5,7 @@ #include "textureImage.h" #include "sourceTextureImage.h" +#include "destTextureImage.h" #include "eggFile.h" #include "paletteGroup.h" #include "texturePlacement.h" @@ -26,6 +27,8 @@ TextureImage:: TextureImage() { _preferred_source = (SourceTextureImage *)NULL; _read_source_image = false; + _got_dest_image = false; + _is_surprise = true; } //////////////////////////////////////////////////////////////////// @@ -38,7 +41,7 @@ TextureImage() { //////////////////////////////////////////////////////////////////// void TextureImage:: note_egg_file(EggFile *egg_file) { - nassertv(!egg_file->get_groups().empty()); + nassertv(!egg_file->get_complete_groups().empty()); _egg_files.insert(egg_file); } @@ -73,7 +76,7 @@ assign_groups() { EggFiles::const_iterator ei; for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) { PaletteGroups intersect; - intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_groups()); + intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups()); if (!intersect.empty()) { // This egg file is satisfied by one of the texture's explicit // assignments. @@ -100,12 +103,13 @@ assign_groups() { PaletteGroups total; WorkingEggs::const_iterator 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()); PaletteGroups::iterator gi = total.begin(); PaletteGroup *best = (*gi); @@ -113,10 +117,22 @@ assign_groups() { ++gi; while (gi != total.end()) { 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); - 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_egg_count = group_egg_count; } @@ -129,7 +145,7 @@ assign_groups() { WorkingEggs next_needed_eggs; 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. next_needed_eggs.push_back(*ei); } @@ -209,6 +225,7 @@ pre_txa_file() { } _request.pre_txa_file(); + _is_surprise = true; } //////////////////////////////////////////////////////////////////// @@ -332,6 +349,19 @@ get_margin() const { 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 @@ -359,6 +389,7 @@ get_source(const Filename &filename, const Filename &alpha_filename) { // next time someone asks. _preferred_source = (SourceTextureImage *)NULL; _read_source_image = false; + _got_dest_image = false; return source; } @@ -387,6 +418,62 @@ get_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 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 // Access: Public @@ -406,6 +493,27 @@ read_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 // Access: Public @@ -464,7 +572,7 @@ write_scale_info(ostream &out, int indent_level) { double scale = 100.0 * (((double)get_x_size() / (double)source->get_x_size()) + ((double)get_y_size() / (double)source->get_y_size())) / 2.0; - out << " " << scale << "%"; + out << " scale " << scale << "%"; } out << "\n"; } @@ -483,7 +591,7 @@ compute_egg_count(PaletteGroup *group, WorkingEggs::const_iterator 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++; } } @@ -589,6 +697,88 @@ consider_grayscale() { _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 // Access: Public, Static @@ -621,6 +811,8 @@ write_datagram(BamWriter *writer, Datagram &datagram) { // We don't write out _preferred_source; this is redetermined each // session. + datagram.add_bool(_is_surprise); + // We don't write out _explicitly_assigned_groups; this is re-read // from the .txa file each time. @@ -641,8 +833,15 @@ write_datagram(BamWriter *writer, Datagram &datagram) { 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:: 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 i; @@ -680,6 +879,13 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) { 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; } @@ -716,6 +922,8 @@ fillin(DatagramIterator &scan, BamReader *manager) { ImageFile::fillin(scan, manager); set_name(scan.get_string()); + _is_surprise = scan.get_bool(); + _actual_assigned_groups.fillin(scan, manager); _num_placement = scan.get_uint32(); @@ -723,4 +931,6 @@ fillin(DatagramIterator &scan, BamReader *manager) { _num_sources = scan.get_uint32(); manager->read_pointers(scan, this, _num_sources); + _num_dests = scan.get_uint32(); + manager->read_pointers(scan, this, _num_dests); } diff --git a/pandatool/src/egg-palettize/textureImage.h b/pandatool/src/egg-palettize/textureImage.h index cf0df30cf4..248ed9e82d 100644 --- a/pandatool/src/egg-palettize/textureImage.h +++ b/pandatool/src/egg-palettize/textureImage.h @@ -20,6 +20,7 @@ #include class SourceTextureImage; +class DestTextureImage; class TexturePlacement; class EggFile; @@ -55,15 +56,17 @@ public: bool get_omit() const; double get_repeat_threshold() const; int get_margin() const; - + bool is_surprise() const; SourceTextureImage *get_source(const Filename &filename, const Filename &alpha_filename); SourceTextureImage *get_preferred_source(); - bool get_size(); + + void copy_unplaced(); const PNMImage &read_source_image(); + const PNMImage &get_dest_image(); void write_source_pathnames(ostream &out, int indent_level = 0) const; void write_scale_info(ostream &out, int indent_level = 0); @@ -71,6 +74,8 @@ public: private: typedef set EggFiles; typedef vector WorkingEggs; + typedef map Sources; + typedef map Dests; static int compute_egg_count(PaletteGroup *group, const WorkingEggs &egg_files); @@ -78,9 +83,14 @@ private: void assign_to_groups(const PaletteGroups &groups); 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; TextureProperties _pre_txa_properties; SourceTextureImage *_preferred_source; + bool _is_surprise; PaletteGroups _explicitly_assigned_groups; PaletteGroups _actual_assigned_groups; @@ -90,11 +100,13 @@ private: typedef map Placement; Placement _placement; - typedef map Sources; Sources _sources; + Dests _dests; bool _read_source_image; PNMImage _source_image; + bool _got_dest_image; + PNMImage _dest_image; // The TypedWriteable interface follows. @@ -113,6 +125,7 @@ private: // don't use them otherwise. int _num_placement; int _num_sources; + int _num_dests; public: static TypeHandle get_class_type() { diff --git a/pandatool/src/egg-palettize/texturePlacement.cxx b/pandatool/src/egg-palettize/texturePlacement.cxx index e156fa956a..65aa7ffb5d 100644 --- a/pandatool/src/egg-palettize/texturePlacement.cxx +++ b/pandatool/src/egg-palettize/texturePlacement.cxx @@ -9,6 +9,7 @@ #include "paletteGroup.h" #include "paletteImage.h" #include "palettizer.h" +#include "eggFile.h" #include #include @@ -30,6 +31,7 @@ TexturePlacement() { _texture = (TextureImage *)NULL; _group = (PaletteGroup *)NULL; _image = (PaletteImage *)NULL; + _dest = (DestTextureImage *)NULL; _has_uvs = false; _size_known = false; _is_filled = true; @@ -55,6 +57,7 @@ TexturePlacement(TextureImage *texture, PaletteGroup *group) : } _image = (PaletteImage *)NULL; + _dest = (DestTextureImage *)NULL; _has_uvs = false; _size_known = false; _is_filled = false; @@ -119,8 +122,11 @@ get_group() const { //////////////////////////////////////////////////////////////////// void TexturePlacement:: add_egg(TextureReference *reference) { + reference->mark_egg_stale(); + /* _has_uvs = false; _size_known = false; + */ _references.insert(reference); } @@ -132,11 +138,53 @@ add_egg(TextureReference *reference) { //////////////////////////////////////////////////////////////////// void TexturePlacement:: remove_egg(TextureReference *reference) { - _has_uvs = false; - _size_known = false; + reference->mark_egg_stale(); + /* + _has_uvs = false; + _size_known = false; + */ _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 // Access: Public @@ -358,7 +406,9 @@ get_y_size() const { //////////////////////////////////////////////////////////////////// double TexturePlacement:: get_uv_area() const { - nassertr(_has_uvs, 0.0); + if (!_has_uvs) { + return 0.0; + } TexCoordd range = _position._max_uv - _position._min_uv; return range[0] * range[1]; @@ -462,8 +512,7 @@ get_placed_y_size() const { //////////////////////////////////////////////////////////////////// double TexturePlacement:: get_placed_uv_area() const { - nassertr(_has_uvs, 0.0); - + nassertr(is_placed(), 0); TexCoordd range = _placed._max_uv - _placed._min_uv; return range[0] * range[1]; } @@ -502,8 +551,9 @@ force_replace() { if (_image != (PaletteImage *)NULL) { _image->unplace(this); _image = (PaletteImage *)NULL; - _omit_reason = OR_working; } + _omit_reason = OR_working; + mark_eggs_stale(); } //////////////////////////////////////////////////////////////////// @@ -519,7 +569,10 @@ force_replace() { void TexturePlacement:: omit_solitary() { 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:: not_solitary() { 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, _group); 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(_size_known); diff --git a/pandatool/src/egg-palettize/texturePlacement.h b/pandatool/src/egg-palettize/texturePlacement.h index 9ae41f0395..2eff5ac42d 100644 --- a/pandatool/src/egg-palettize/texturePlacement.h +++ b/pandatool/src/egg-palettize/texturePlacement.h @@ -16,8 +16,8 @@ #include - class TextureImage; +class DestTextureImage; class PaletteGroup; class PaletteImage; class PalettePage; @@ -47,6 +47,10 @@ public: void add_egg(TextureReference *reference); void remove_egg(TextureReference *reference); + void mark_eggs_stale(); + + void set_dest(DestTextureImage *dest); + DestTextureImage *get_dest() const; bool determine_size(); OmitReason get_omit_reason() const; @@ -83,6 +87,7 @@ private: TextureImage *_texture; PaletteGroup *_group; PaletteImage *_image; + DestTextureImage *_dest; bool _has_uvs; bool _size_known; diff --git a/pandatool/src/egg-palettize/textureReference.cxx b/pandatool/src/egg-palettize/textureReference.cxx index 11d1199891..9aaebe15ad 100644 --- a/pandatool/src/egg-palettize/textureReference.cxx +++ b/pandatool/src/egg-palettize/textureReference.cxx @@ -7,8 +7,10 @@ #include "textureImage.h" #include "paletteImage.h" #include "sourceTextureImage.h" +#include "destTextureImage.h" #include "texturePlacement.h" #include "palettizer.h" +#include "eggFile.h" #include #include @@ -32,6 +34,7 @@ TypeHandle TextureReference::_type_handle; //////////////////////////////////////////////////////////////////// TextureReference:: TextureReference() { + _egg_file = (EggFile *)NULL; _egg_tex = (EggTexture *)NULL; _tex_mat = LMatrix3d::ident_mat(); _inv_tex_mat = LMatrix3d::ident_mat(); @@ -62,7 +65,8 @@ TextureReference:: // extracted from an egg file. //////////////////////////////////////////////////////////////////// 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_data = data; @@ -112,6 +116,16 @@ from_egg(EggData *data, EggTexture *egg_tex) { _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 // Access: Public @@ -237,6 +251,19 @@ get_placement() const { 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 // Access: Public @@ -261,9 +288,9 @@ update_egg() { // The texture does not appear on a palette. This is the easy // case; we simply have to update the texture reference to the new // 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; } @@ -601,6 +628,7 @@ write_datagram(BamWriter *writer, Datagram &datagram) { // We don't write _egg_tex, _egg_data, or _tex_mat; that's specific // to the session. + writer->write_pointer(datagram, _egg_file); writer->write_pointer(datagram, _source_texture); writer->write_pointer(datagram, _placement); @@ -626,9 +654,14 @@ write_datagram(BamWriter *writer, Datagram &datagram) { //////////////////////////////////////////////////////////////////// int TextureReference:: complete_pointers(vector_typedWriteable &plist, BamReader *manager) { - nassertr((int)plist.size() >= 2, 0); + nassertr((int)plist.size() >= 3, 0); int index = 0; + if (plist[index] != (TypedWriteable *)NULL) { + DCAST_INTO_R(_egg_file, plist[index], index); + } + index++; + if (plist[index] != (TypedWriteable *)NULL) { DCAST_INTO_R(_source_texture, plist[index], index); } @@ -672,6 +705,7 @@ make_TextureReference(const FactoryParams ¶ms) { //////////////////////////////////////////////////////////////////// void TextureReference:: fillin(DatagramIterator &scan, BamReader *manager) { + manager->read_pointer(scan, this); // _egg_file manager->read_pointer(scan, this); // _source_texture manager->read_pointer(scan, this); // _placement diff --git a/pandatool/src/egg-palettize/textureReference.h b/pandatool/src/egg-palettize/textureReference.h index c9e8f5a529..8cb43542af 100644 --- a/pandatool/src/egg-palettize/textureReference.h +++ b/pandatool/src/egg-palettize/textureReference.h @@ -16,6 +16,7 @@ class TextureImage; class SourceTextureImage; class Filename; +class EggFile; class EggData; class EggTexture; class EggGroupNode; @@ -34,8 +35,9 @@ public: 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; TextureImage *get_texture() const; @@ -50,6 +52,7 @@ public: void clear_placement(); TexturePlacement *get_placement() const; + void mark_egg_stale(); void update_egg(); void output(ostream &out) const; @@ -68,8 +71,10 @@ private: static LVector2d translate_uv(const TexCoordd &min_uv, const TexCoordd &max_uv); + EggFile *_egg_file; EggTexture *_egg_tex; EggData *_egg_data; + LMatrix3d _tex_mat, _inv_tex_mat; SourceTextureImage *_source_texture; TexturePlacement *_placement; diff --git a/pandatool/src/egg-palettize/txaLine.cxx b/pandatool/src/egg-palettize/txaLine.cxx index 35af8f90bc..cd60cc695c 100644 --- a/pandatool/src/egg-palettize/txaLine.cxx +++ b/pandatool/src/egg-palettize/txaLine.cxx @@ -56,10 +56,21 @@ parse(const string &line) { vector_string::iterator 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"; return false; } @@ -229,8 +240,8 @@ match_egg(EggFile *egg_file) const { bool matched_any = false; 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) { 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 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 // matches. + egg_file->clear_surprise(); + return true; } @@ -290,8 +302,8 @@ match_texture(TextureImage *texture) const { bool matched_any = false; 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) { 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 // matches. + texture->_is_surprise = false; + return true; } @@ -398,7 +412,10 @@ match_texture(TextureImage *texture) const { void TxaLine:: output(ostream &out) const { 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 << ":"; diff --git a/pandatool/src/egg-palettize/txaLine.h b/pandatool/src/egg-palettize/txaLine.h index 1319bf165d..d82cf581b1 100644 --- a/pandatool/src/egg-palettize/txaLine.h +++ b/pandatool/src/egg-palettize/txaLine.h @@ -39,7 +39,8 @@ public: private: typedef vector Patterns; - Patterns _patterns; + Patterns _texture_patterns; + Patterns _egg_patterns; enum SizeType { ST_none,