diff --git a/pandatool/src/egg-palettize/eggFile.cxx b/pandatool/src/egg-palettize/eggFile.cxx index 28745c3e14..7ea240b300 100644 --- a/pandatool/src/egg-palettize/eggFile.cxx +++ b/pandatool/src/egg-palettize/eggFile.cxx @@ -7,6 +7,8 @@ #include "textureImage.h" #include "paletteGroup.h" #include "texturePlacement.h" +#include "textureReference.h" +#include "sourceTextureImage.h" #include "palettizer.h" #include "filenameUnifier.h" @@ -242,8 +244,9 @@ is_stale() const { //////////////////////////////////////////////////////////////////// // Function: EggFile::build_cross_links // Access: Public -// Description: Calls TextureImage::note_egg_file() for each texture -// the egg file references, and +// Description: Calls TextureImage::note_egg_file() and +// SourceTextureImage::increment_egg_count() for each +// texture the egg file references, and // PaletteGroup::increment_egg_count() for each palette // group it wants. This sets up some of the back // references to support determining an ideal texture @@ -265,6 +268,12 @@ build_cross_links() { Textures::const_iterator ti; for (ti = _textures.begin(); ti != _textures.end(); ++ti) { (*ti)->get_texture()->note_egg_file(this); + + // Actually, this may count the same egg file multiple times for a + // particular SourceTextureImage, since a given texture may be + // reference multiples times within an egg file. No harm done, + // however. + (*ti)->get_source()->increment_egg_count(); } PaletteGroups::const_iterator gi; diff --git a/pandatool/src/egg-palettize/eggPalettize.cxx b/pandatool/src/egg-palettize/eggPalettize.cxx index b5215430fd..ef70872973 100644 --- a/pandatool/src/egg-palettize/eggPalettize.cxx +++ b/pandatool/src/egg-palettize/eggPalettize.cxx @@ -358,14 +358,30 @@ run() { okflag = false; } - if (!state_file.open_write(state_filename) || + // Make up a temporary filename to write the state file to, then + // move the state file into place. We do this in case the user + // interrupts us (or we core dump) before we're done; that way we + // won't leave the state file incompletely written. + string dirname = state_filename.get_dirname(); + if (dirname.empty()) { + dirname = "."; + } + char *name = tempnam(dirname.c_str(), "pi"); + Filename temp_filename(name); + + if (!state_file.open_write(temp_filename) || !state_file.write_object(pal)) { - nout << "Unable to write palettization information to " << state_filename + nout << "Unable to write palettization information to " << temp_filename << "\n"; exit(1); } state_file.close(); + if (!temp_filename.rename_to(state_filename)) { + nout << "Unable to rename temporary file " << temp_filename << " to " + << state_filename << "\n"; + exit(1); + } if (!okflag) { exit(1); diff --git a/pandatool/src/egg-palettize/palettizer.cxx b/pandatool/src/egg-palettize/palettizer.cxx index 7f7d4e35e8..2d85e48d0d 100644 --- a/pandatool/src/egg-palettize/palettizer.cxx +++ b/pandatool/src/egg-palettize/palettizer.cxx @@ -218,6 +218,13 @@ process_command_line_eggs(bool force_texture_read) { egg_file->post_txa_file(); } + // Now that all of our egg files are read in, build in all the cross + // links and back pointers and stuff. + EggFiles::const_iterator efi; + for (efi = _egg_files.begin(); efi != _egg_files.end(); ++efi) { + (*efi).second->build_cross_links(); + } + // Now match each of the textures mentioned in those egg files // against a line in the .txa file. CommandLineTextures::iterator ti; @@ -235,13 +242,6 @@ process_command_line_eggs(bool force_texture_read) { texture->post_txa_file(); } - // Now that all of our data is read in, build in all the cross links - // and back pointers and stuff. - EggFiles::const_iterator efi; - for (efi = _egg_files.begin(); efi != _egg_files.end(); ++efi) { - (*efi).second->build_cross_links(); - } - // And now, assign each of the current set of textures to an // appropriate group or groups. for (ti = _command_line_textures.begin(); @@ -310,6 +310,12 @@ process_all(bool force_texture_read) { egg_file->post_txa_file(); } + // Now that all of our egg files are 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(); + } + // Now match each of the textures in the world against a line in the // .txa file. Textures::iterator ti; @@ -325,12 +331,6 @@ process_all(bool force_texture_read) { 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; diff --git a/pandatool/src/egg-palettize/sourceTextureImage.cxx b/pandatool/src/egg-palettize/sourceTextureImage.cxx index b1bb4b44af..529803fa86 100644 --- a/pandatool/src/egg-palettize/sourceTextureImage.cxx +++ b/pandatool/src/egg-palettize/sourceTextureImage.cxx @@ -5,6 +5,7 @@ #include "sourceTextureImage.h" #include "textureImage.h" +#include "filenameUnifier.h" #include #include @@ -24,6 +25,7 @@ SourceTextureImage:: SourceTextureImage() { _texture = (TextureImage *)NULL; + _egg_count = 0; _read_header = false; _successfully_read_header = false; } @@ -40,6 +42,7 @@ SourceTextureImage(TextureImage *texture, const Filename &filename, { _filename = filename; _alpha_filename = alpha_filename; + _egg_count = 0; _read_header = false; _successfully_read_header = false; } @@ -55,6 +58,28 @@ get_texture() const { return _texture; } +//////////////////////////////////////////////////////////////////// +// Function: SourceTextureImage::increment_egg_count +// Access: Public +// Description: Increments by one the number of egg files that are +// known to reference this SourceTextureImage. +//////////////////////////////////////////////////////////////////// +void SourceTextureImage:: +increment_egg_count() { + _egg_count++; +} + +//////////////////////////////////////////////////////////////////// +// Function: SourceTextureImage::get_egg_count +// Access: Public +// Description: Returns the number of egg files that share this +// SourceTextureImage. +//////////////////////////////////////////////////////////////////// +int SourceTextureImage:: +get_egg_count() const { + return _egg_count; +} + //////////////////////////////////////////////////////////////////// // Function: SourceTextureImage::get_size // Access: Public @@ -105,7 +130,7 @@ read_header() { _properties._got_num_channels = true; _properties._num_channels = header.get_num_channels(); - if (!_alpha_filename.empty()) { + if (!_alpha_filename.empty() && _alpha_filename.exists()) { // Assume if we have an alpha filename, that we have an additional // alpha channel. if (_properties._num_channels == 1 || _properties._num_channels == 3) { @@ -144,6 +169,9 @@ write_datagram(BamWriter *writer, Datagram &datagram) { ImageFile::write_datagram(writer, datagram); writer->write_pointer(datagram, _texture); + // We don't store _egg_count; instead, we count these up again each + // session. + // We don't store _read_header or _successfully_read_header in the // Bam file; these are transitory and we need to reread the image // header for each session (in case the image files change between @@ -175,7 +203,7 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) { // allocate and return a new object with all the data // read. //////////////////////////////////////////////////////////////////// -TypedWriteable* SourceTextureImage:: +TypedWriteable *SourceTextureImage:: make_SourceTextureImage(const FactoryParams ¶ms) { SourceTextureImage *me = new SourceTextureImage; BamReader *manager; diff --git a/pandatool/src/egg-palettize/sourceTextureImage.h b/pandatool/src/egg-palettize/sourceTextureImage.h index c26b5d7a45..f8ad767ccf 100644 --- a/pandatool/src/egg-palettize/sourceTextureImage.h +++ b/pandatool/src/egg-palettize/sourceTextureImage.h @@ -26,14 +26,18 @@ public: const Filename &alpha_filename); TextureImage *get_texture() const; + + void increment_egg_count(); + int get_egg_count() const; bool get_size(); bool read_header(); private: + TextureImage *_texture; + int _egg_count; bool _read_header; bool _successfully_read_header; - TextureImage *_texture; // The TypedWriteable interface follows. public: diff --git a/pandatool/src/egg-palettize/textureImage.cxx b/pandatool/src/egg-palettize/textureImage.cxx index 2b34a51d8c..80db846736 100644 --- a/pandatool/src/egg-palettize/textureImage.cxx +++ b/pandatool/src/egg-palettize/textureImage.cxx @@ -9,6 +9,7 @@ #include "eggFile.h" #include "paletteGroup.h" #include "texturePlacement.h" +#include "filenameUnifier.h" #include #include @@ -377,7 +378,6 @@ is_surprise() const { } - //////////////////////////////////////////////////////////////////// // Function: TextureImage::get_source // Access: Public @@ -389,7 +389,8 @@ is_surprise() const { //////////////////////////////////////////////////////////////////// SourceTextureImage *TextureImage:: get_source(const Filename &filename, const Filename &alpha_filename) { - string key = filename.get_fullpath() + ":" + alpha_filename.get_fullpath(); + string key = get_source_key(filename, alpha_filename); + Sources::iterator si; si = _sources.find(key); if (si != _sources.end()) { @@ -423,13 +424,65 @@ get_preferred_source() { } // Now examine all of the various source images available to us and - // pick the most suitable. + // pick the most suitable. We base this on the following criteria: - // **** For now, we arbitrarily pick the first one. - if (!_sources.empty()) { - _preferred_source = (*_sources.begin()).second; + // (1) A suitable source image must be referenced by at least one + // egg file, unless no source images are referenced by any egg file. + + // (2) A larger source image is preferable to a smaller one. + + // (3) Given two source images of the same size, the more recent one + // is preferable. + + // Are any source images referenced by an egg file? + + bool any_referenced = false; + Sources::iterator si; + for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) { + SourceTextureImage *source = (*si).second; + if (source->get_egg_count() > 0) { + any_referenced = true; + } } + SourceTextureImage *best = (SourceTextureImage *)NULL; + int best_size; + + for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) { + SourceTextureImage *source = (*si).second; + + if (source->get_egg_count() > 0 || !any_referenced) { + // Rule (1) passes. + + if (source->exists() && source->get_size()) { + int source_size = source->get_x_size() * source->get_y_size(); + if (best == (SourceTextureImage *)NULL) { + best = source; + best_size = source_size; + + } else if (source_size > best_size) { + // Rule (2) passes. + best = source; + best_size = source_size; + + } else if (source_size == best_size && + source->get_filename().compare_timestamps(best->get_filename()) > 0) { + // Rule (3) passes. + best = source; + best_size = source_size; + } + } + } + } + + if (best == (SourceTextureImage *)NULL && !_sources.empty()) { + // If we didn't pick any that pass, it must be that all of them + // are unreadable. In this case, it really doesn't matter which + // one we pick. + best = (*_sources.begin()).second; + } + + _preferred_source = best; return _preferred_source; } @@ -880,6 +933,20 @@ copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) { } } +//////////////////////////////////////////////////////////////////// +// Function: TextureImage::get_source_key +// Access: Private +// Description: Returns the key that a SourceTextureImage should be +// stored in, given its one or two filenames. +//////////////////////////////////////////////////////////////////// +string TextureImage:: +get_source_key(const Filename &filename, const Filename &alpha_filename) { + Filename f = FilenameUnifier::make_bam_filename(filename); + Filename a = FilenameUnifier::make_bam_filename(alpha_filename); + + return f.get_fullpath() + ":" + a.get_fullpath(); +} + //////////////////////////////////////////////////////////////////// // Function: TextureImage::register_with_read_factory // Access: Public, Static @@ -976,18 +1043,20 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) { for (i = 0; i < _num_sources; i++) { SourceTextureImage *source; DCAST_INTO_R(source, plist[index], index); - string key = source->get_filename().get_fullpath() + ":" + - source->get_alpha_filename().get_fullpath(); + string key = get_source_key(source->get_filename(), + source->get_alpha_filename()); - _sources.insert(Sources::value_type(key, source)); + bool inserted = _sources.insert(Sources::value_type(key, source)).second; index++; + nassertr(inserted, 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)); + bool inserted = _dests.insert(Dests::value_type(dest->get_filename(), dest)).second; index++; + nassertr(inserted, index); } return index; @@ -1001,7 +1070,7 @@ complete_pointers(vector_typedWriteable &plist, BamReader *manager) { // allocate and return a new object with all the data // read. //////////////////////////////////////////////////////////////////// -TypedWriteable* TextureImage:: +TypedWriteable *TextureImage:: make_TextureImage(const FactoryParams ¶ms) { TextureImage *me = new TextureImage; BamReader *manager; diff --git a/pandatool/src/egg-palettize/textureImage.h b/pandatool/src/egg-palettize/textureImage.h index d7a0509379..2101198cde 100644 --- a/pandatool/src/egg-palettize/textureImage.h +++ b/pandatool/src/egg-palettize/textureImage.h @@ -87,6 +87,9 @@ private: void remove_old_dests(const Dests &a, const Dests &b); void copy_new_dests(const Dests &a, const Dests &b); + string get_source_key(const Filename &filename, + const Filename &alpha_filename); + private: TextureRequest _request; TextureProperties _pre_txa_properties; diff --git a/pandatool/src/egg-palettize/txaLine.cxx b/pandatool/src/egg-palettize/txaLine.cxx index cd60cc695c..cd2e0a2bc2 100644 --- a/pandatool/src/egg-palettize/txaLine.cxx +++ b/pandatool/src/egg-palettize/txaLine.cxx @@ -322,7 +322,7 @@ match_texture(TextureImage *texture) const { break; case ST_scale: - if (source->get_size()) { + if (source != (SourceTextureImage *)NULL && source->get_size()) { request._got_size = true; request._x_size = (int)(source->get_x_size() * _scale / 100.0); request._y_size = (int)(source->get_y_size() * _scale / 100.0);