Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

pandatool/src/egg-palettize/textureImage.cxx

Go to the documentation of this file.
00001 // Filename: textureImage.cxx
00002 // Created by:  drose (29Nov00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "textureImage.h"
00020 #include "sourceTextureImage.h"
00021 #include "destTextureImage.h"
00022 #include "eggFile.h"
00023 #include "paletteGroup.h"
00024 #include "paletteImage.h"
00025 #include "texturePlacement.h"
00026 #include "filenameUnifier.h"
00027 
00028 #include "indent.h"
00029 #include "datagram.h"
00030 #include "datagramIterator.h"
00031 #include "bamReader.h"
00032 #include "bamWriter.h"
00033 #include "pnmFileType.h"
00034 #include "indirectCompareNames.h"
00035 #include "pvector.h"
00036 
00037 TypeHandle TextureImage::_type_handle;
00038 
00039 ////////////////////////////////////////////////////////////////////
00040 //     Function: TextureImage::Constructor
00041 //       Access: Public
00042 //  Description:
00043 ////////////////////////////////////////////////////////////////////
00044 TextureImage::
00045 TextureImage() {
00046   _preferred_source = (SourceTextureImage *)NULL;
00047   _read_source_image = false;
00048   _is_surprise = true;
00049   _ever_read_image = false;
00050   _forced_grayscale = false;
00051   _alpha_bits = 0;
00052   _alpha_mode = EggRenderMode::AM_unspecified;
00053   _got_txa_file = false;
00054 }
00055 
00056 ////////////////////////////////////////////////////////////////////
00057 //     Function: TextureImage::note_egg_file
00058 //       Access: Public
00059 //  Description: Records that a particular egg file references this
00060 //               texture.  This is essential to know when deciding how
00061 //               to assign the TextureImage to the various
00062 //               PaletteGroups.
00063 ////////////////////////////////////////////////////////////////////
00064 void TextureImage::
00065 note_egg_file(EggFile *egg_file) {
00066   nassertv(!egg_file->get_complete_groups().empty());
00067   _egg_files.insert(egg_file);
00068 }
00069 
00070 ////////////////////////////////////////////////////////////////////
00071 //     Function: TextureImage::assign_groups
00072 //       Access: Public
00073 //  Description: Assigns the texture to all of the PaletteGroups the
00074 //               various egg files that use it need.  Attempts to
00075 //               choose the minimum set of PaletteGroups that
00076 //               satisfies all of the egg files.
00077 ////////////////////////////////////////////////////////////////////
00078 void TextureImage::
00079 assign_groups() {
00080   if (_egg_files.empty()) {
00081     // If we're not referenced by any egg files any more, assign us to
00082     // no groups.
00083     PaletteGroups empty;
00084     assign_to_groups(empty);
00085     return;
00086   }
00087 
00088   PaletteGroups definitely_in;
00089 
00090   // First, we need to eliminate from consideration all the egg files
00091   // that are already taken care of by the user's explicit group
00092   // assignments for this texture.
00093   WorkingEggs needed_eggs;
00094 
00095   if (_explicitly_assigned_groups.empty()) {
00096     // If we have no explicit group assignments, we must consider all
00097     // the egg files.
00098     copy(_egg_files.begin(), _egg_files.end(), back_inserter(needed_eggs));
00099 
00100   } else {
00101     // Otherwise, we only need to consider the egg files that don't
00102     // have any groups in common with our explicit assignments.
00103 
00104     EggFiles::const_iterator ei;
00105     for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
00106       PaletteGroups intersect;
00107       intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups());
00108       if (!intersect.empty()) {
00109         // This egg file is satisfied by one of the texture's explicit
00110         // assignments.
00111 
00112         // We must use at least one of the explicitly-assigned groups
00113         // that satisfied the egg file.  We don't need to use all of
00114         // them, however, and we choose the first one arbitrarily.
00115         definitely_in.insert(*intersect.begin());
00116 
00117       } else {
00118         // This egg file was not satisfied by any of the texture's
00119         // explicit assignments.  Therefore, we'll need to choose some
00120         // additional group to assign the texture to, to make the egg
00121         // file happy.  Defer this a bit.
00122         needed_eggs.push_back(*ei);
00123       }
00124     }
00125   }
00126 
00127   while (!needed_eggs.empty()) {
00128     // We need to know the complete set of groups that we need to
00129     // consider adding the texture to.  This is the union of all the egg
00130     // files' requested groups.
00131     PaletteGroups total;
00132     WorkingEggs::const_iterator ei;
00133     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
00134       total.make_union(total, (*ei)->get_complete_groups());
00135     }
00136 
00137     // Now, find the group that will satisfy the most egg files.  If
00138     // two groups satisfy the same number of egg files, choose (a) the
00139     // most specific one, i.e. with the lowest dirname_level, or the
00140     // lowest dependency_level if the dirname_levels are equal, and
00141     // (b) the one that has the fewest egg files sharing it.
00142     nassertv(!total.empty());
00143     PaletteGroups::iterator gi = total.begin();
00144     PaletteGroup *best = (*gi);
00145     int best_egg_count = compute_egg_count(best, needed_eggs);
00146     ++gi;
00147     while (gi != total.end()) {
00148       PaletteGroup *group = (*gi);
00149 
00150       // Do we prefer this group to our current 'best'?
00151       bool prefer_group = false;
00152       int group_egg_count = compute_egg_count(group, needed_eggs);
00153       if (group_egg_count != best_egg_count) {
00154         prefer_group = (group_egg_count > best_egg_count);
00155 
00156       } else {
00157         prefer_group = group->is_preferred_over(*best);
00158       }
00159 
00160       if (prefer_group) {
00161         best = group;
00162         best_egg_count = group_egg_count;
00163       }
00164       ++gi;
00165     }
00166 
00167     // Okay, now we've picked the best group.  Eliminate all the eggs
00168     // from consideration that are satisfied by this group, and repeat.
00169     definitely_in.insert(best);
00170 
00171     WorkingEggs next_needed_eggs;
00172     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
00173       if ((*ei)->get_complete_groups().count(best) == 0) {
00174         // This one wasn't eliminated.
00175         next_needed_eggs.push_back(*ei);
00176       }
00177     }
00178     needed_eggs.swap(next_needed_eggs);
00179   }
00180 
00181   // Finally, now that we've computed the set of groups we need to
00182   // assign the texture to, we need to reconcile this with the set of
00183   // groups we've assigned the texture to previously.
00184   assign_to_groups(definitely_in);
00185 }
00186 
00187 ////////////////////////////////////////////////////////////////////
00188 //     Function: TextureImage::get_groups
00189 //       Access: Public
00190 //  Description: Once assign_groups() has been called, this returns
00191 //               the actual set of groups the TextureImage has been
00192 //               assigned to.
00193 ////////////////////////////////////////////////////////////////////
00194 const PaletteGroups &TextureImage::
00195 get_groups() const {
00196   return _actual_assigned_groups;
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: TextureImage::get_placement
00201 //       Access: Public
00202 //  Description: Gets the TexturePlacement object which represents the
00203 //               assignment of this texture to the indicated group.
00204 //               If the texture has not been assigned to the indicated
00205 //               group, returns NULL.
00206 ////////////////////////////////////////////////////////////////////
00207 TexturePlacement *TextureImage::
00208 get_placement(PaletteGroup *group) const {
00209   Placement::const_iterator pi;
00210   pi = _placement.find(group);
00211   if (pi == _placement.end()) {
00212     return (TexturePlacement *)NULL;
00213   }
00214 
00215   return (*pi).second;
00216 }
00217 
00218 ////////////////////////////////////////////////////////////////////
00219 //     Function: TextureImage::force_replace
00220 //       Access: Public
00221 //  Description: Removes the texture from any PaletteImages it is
00222 //               assigned to, but does not remove it from the groups.
00223 //               It will be re-placed within each group when
00224 //               PaletteGroup::place_all() is called.
00225 ////////////////////////////////////////////////////////////////////
00226 void TextureImage::
00227 force_replace() {
00228   Placement::iterator pi;
00229   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00230     (*pi).second->force_replace();
00231   }
00232 }
00233 
00234 ////////////////////////////////////////////////////////////////////
00235 //     Function: TextureImage::mark_eggs_stale
00236 //       Access: Public
00237 //  Description: Marks all the egg files that reference this texture
00238 //               stale.  Should be called only when the texture
00239 //               properties change in some catastrophic way that will
00240 //               require every egg file referencing it to be
00241 //               regenerated, even if it is not palettized.
00242 ////////////////////////////////////////////////////////////////////
00243 void TextureImage::
00244 mark_eggs_stale() {
00245   Placement::iterator pi;
00246   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00247     (*pi).second->mark_eggs_stale();
00248   }
00249 }
00250 
00251 ////////////////////////////////////////////////////////////////////
00252 //     Function: TextureImage::pre_txa_file
00253 //       Access: Public
00254 //  Description: Updates any internal state prior to reading the .txa
00255 //               file.
00256 ////////////////////////////////////////////////////////////////////
00257 void TextureImage::
00258 pre_txa_file() {
00259   // Save our current properties, so we can note if they change.
00260   _pre_txa_properties = _properties;
00261 
00262   // Get our properties from the actual image for this texture.  It's
00263   // possible the .txa file will update them further.
00264   SourceTextureImage *source = get_preferred_source();
00265   if (source != (SourceTextureImage *)NULL) {
00266     _properties = source->get_properties();
00267   }
00268 
00269   _pre_txa_alpha_mode = _alpha_mode;
00270   _alpha_mode = EggRenderMode::AM_unspecified;
00271 
00272   _request.pre_txa_file();
00273   _is_surprise = true;
00274 }
00275 
00276 ////////////////////////////////////////////////////////////////////
00277 //     Function: TextureImage::post_txa_file
00278 //       Access: Public
00279 //  Description: Once the .txa file has been read and the TextureImage
00280 //               matched against it, considers applying the requested
00281 //               size change.  Updates the TextureImage's size with
00282 //               the size the texture ought to be, if this can be
00283 //               determined.
00284 ////////////////////////////////////////////////////////////////////
00285 void TextureImage::
00286 post_txa_file() {
00287   _got_txa_file = true;
00288 
00289   // First, get the actual size of the texture.
00290   SourceTextureImage *source = get_preferred_source();
00291   if (source != (SourceTextureImage *)NULL) {
00292     if (source->get_size()) {
00293       _size_known = true;
00294       _x_size = source->get_x_size();
00295       _y_size = source->get_y_size();
00296       _properties.set_num_channels(source->get_num_channels());
00297     }
00298   }
00299 
00300   // Now update this with a particularly requested size.
00301   if (_request._got_size) {
00302     _size_known = true;
00303     _x_size = _request._x_size;
00304     _y_size = _request._y_size;
00305   }
00306 
00307   if (_properties.has_num_channels()) {
00308     int num_channels = _properties.get_num_channels();
00309     // Examine the image to determine if we can downgrade the number
00310     // of channels, for instance from color to grayscale.
00311     if (num_channels == 3 || num_channels == 4) {
00312       consider_grayscale();
00313     }
00314     
00315     // Also consider the alpha properties, and whether we should
00316     // downgrade from alpha to non-alpha.
00317     if (num_channels == 2 || num_channels == 4) {
00318       consider_alpha();
00319     }
00320   }
00321 
00322   // However, if we got an explicit request for channels, honor that.
00323   if (_request._got_num_channels) {
00324     _properties.set_num_channels(_request._num_channels);
00325   }
00326 
00327   _properties._generic_format = _request._generic_format;
00328 
00329   if (_request._format != EggTexture::F_unspecified) {
00330     _properties._format = _request._format;
00331     _properties._force_format = _request._force_format;
00332   }
00333   if (_request._minfilter != EggTexture::FT_unspecified) {
00334     _properties._minfilter = _request._minfilter;
00335   }
00336   if (_request._magfilter != EggTexture::FT_unspecified) {
00337     _properties._magfilter = _request._magfilter;
00338   }
00339 
00340   _properties._anisotropic_degree = _request._anisotropic_degree;
00341 
00342   if (_properties._color_type == (PNMFileType *)NULL) {
00343     _properties._color_type = _request._properties._color_type;
00344     _properties._alpha_type = _request._properties._alpha_type;
00345   }
00346 
00347   // Finally, make sure our properties are fully defined.
00348   _properties.fully_define();
00349 
00350   // Now, if our properties have changed in all that from our previous
00351   // session, we need to re-place ourself in all palette groups.
00352   if (_properties != _pre_txa_properties) {
00353     force_replace();
00354 
00355     // The above will mark the egg files stale when the texture is
00356     // palettized (since the UV's will certainly need to be
00357     // recomputed), but sometimes we need to mark the egg files stale
00358     // even when the texture is not palettized (if a critical property
00359     // has changed).  The following accomplishes this:
00360     if (!_properties.egg_properties_match(_pre_txa_properties)) {
00361       mark_eggs_stale();
00362     }
00363   }
00364 
00365   // The alpha mode isn't stored in the properties, because it doesn't
00366   // affect which textures may be associated into a common palette.
00367   if (_request._alpha_mode != EggRenderMode::AM_unspecified) {
00368     _alpha_mode = _request._alpha_mode;
00369   }
00370 
00371   // On the other hand, if we don't have an alpha channel, we
00372   // shouldn't have an alpha mode.
00373   if (_properties.has_num_channels()) {
00374     int num_channels = _properties.get_num_channels();
00375     if (num_channels == 1 || num_channels == 3) {
00376       _alpha_mode = EggRenderMode::AM_unspecified;
00377     }
00378   }
00379 
00380   // If we've changed the alpha mode, we should also mark the eggs
00381   // stale.
00382   if (_pre_txa_alpha_mode != _alpha_mode) {
00383     mark_eggs_stale();
00384   }
00385 }
00386 
00387 ////////////////////////////////////////////////////////////////////
00388 //     Function: TextureImage::got_txa_file
00389 //       Access: Public
00390 //  Description: Returns true if this TextureImage has been looked up
00391 //               in the .txa file this session, false otherwise.
00392 ////////////////////////////////////////////////////////////////////
00393 bool TextureImage::
00394 got_txa_file() const {
00395   return _got_txa_file;
00396 }
00397 
00398 ////////////////////////////////////////////////////////////////////
00399 //     Function: TextureImage::determine_placement_size
00400 //       Access: Public
00401 //  Description: Calls determine_size() on each TexturePlacement for
00402 //               the texture, to ensure that each TexturePlacement is
00403 //               still requesting the best possible size for the
00404 //               texture.
00405 ////////////////////////////////////////////////////////////////////
00406 void TextureImage::
00407 determine_placement_size() {
00408   Placement::iterator pi;
00409   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00410     TexturePlacement *placement = (*pi).second;
00411     placement->determine_size();
00412   }
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: TextureImage::get_omit
00417 //       Access: Public
00418 //  Description: Returns true if the user specifically requested to
00419 //               omit this texture via the "omit" keyword in the .txa
00420 //               file, or false otherwise.
00421 ////////////////////////////////////////////////////////////////////
00422 bool TextureImage::
00423 get_omit() const {
00424   return _request._omit;
00425 }
00426 
00427 ////////////////////////////////////////////////////////////////////
00428 //     Function: TextureImage::get_coverage_threshold
00429 //       Access: Public
00430 //  Description: Returns the appropriate coverage threshold for this
00431 //               texture.  This is either the
00432 //               Palettizer::_coverage_threshold parameter, given
00433 //               globally via -r, or a particular value for this
00434 //               texture as supplied by the "coverage" keyword in the
00435 //               .txa file.
00436 ////////////////////////////////////////////////////////////////////
00437 double TextureImage::
00438 get_coverage_threshold() const {
00439   return _request._coverage_threshold;
00440 }
00441 
00442 ////////////////////////////////////////////////////////////////////
00443 //     Function: TextureImage::get_margin
00444 //       Access: Public
00445 //  Description: Returns the appropriate margin for this texture.
00446 //               This is either the Palettizer::_margin parameter, or
00447 //               a particular value for this texture as supplied by
00448 //               the "margin" keyword in the .txa file.
00449 ////////////////////////////////////////////////////////////////////
00450 int TextureImage::
00451 get_margin() const {
00452   return _request._margin;
00453 }
00454 
00455 ////////////////////////////////////////////////////////////////////
00456 //     Function: TextureImage::is_surprise
00457 //       Access: Public
00458 //  Description: Returns true if this particular texture is a
00459 //               'surprise', i.e. it wasn't matched by a line in the
00460 //               .txa file that didn't include the keyword 'cont'.
00461 ////////////////////////////////////////////////////////////////////
00462 bool TextureImage::
00463 is_surprise() const {
00464   if (_placement.empty()) {
00465     // A texture that is not actually placed anywhere is not
00466     // considered a surprise.
00467     return false;
00468   }
00469 
00470   return _is_surprise;
00471 }
00472 
00473 ////////////////////////////////////////////////////////////////////
00474 //     Function: TextureImage::is_used
00475 //       Access: Public
00476 //  Description: Returns true if this particular texture has been
00477 //               placed somewhere, anywhere, or false if it is not
00478 //               used.
00479 ////////////////////////////////////////////////////////////////////
00480 bool TextureImage::
00481 is_used() const {
00482   return !_placement.empty();
00483 }
00484 
00485 ////////////////////////////////////////////////////////////////////
00486 //     Function: TextureImage::get_alpha_mode
00487 //       Access: Public
00488 //  Description: Returns the alpha mode that should be used to render
00489 //               objects with this texture, as specified by the user
00490 //               or as determined from examining the texture's alpha
00491 //               channel.
00492 ////////////////////////////////////////////////////////////////////
00493 EggRenderMode::AlphaMode TextureImage::
00494 get_alpha_mode() const {
00495   return _alpha_mode;
00496 }
00497 
00498 
00499 ////////////////////////////////////////////////////////////////////
00500 //     Function: TextureImage::get_source
00501 //       Access: Public
00502 //  Description: Returns the SourceTextureImage corresponding to the
00503 //               given filename(s).  If the given filename has never
00504 //               been used as a SourceTexture for this particular
00505 //               texture, creates a new SourceTextureImage and returns
00506 //               that.
00507 ////////////////////////////////////////////////////////////////////
00508 SourceTextureImage *TextureImage::
00509 get_source(const Filename &filename, const Filename &alpha_filename,
00510            int alpha_file_channel) {
00511   string key = get_source_key(filename, alpha_filename, alpha_file_channel);
00512 
00513   Sources::iterator si;
00514   si = _sources.find(key);
00515   if (si != _sources.end()) {
00516     return (*si).second;
00517   }
00518 
00519   SourceTextureImage *source =
00520     new SourceTextureImage(this, filename, alpha_filename, alpha_file_channel);
00521   _sources.insert(Sources::value_type(key, source));
00522 
00523   // Clear out the preferred source image to force us to rederive this
00524   // next time someone asks.
00525   _preferred_source = (SourceTextureImage *)NULL;
00526   _read_source_image = false;
00527 
00528   return source;
00529 }
00530 
00531 ////////////////////////////////////////////////////////////////////
00532 //     Function: TextureImage::get_preferred_source
00533 //       Access: Public
00534 //  Description: Determines the preferred source image for examining
00535 //               size and reading pixels, etc.  This is the largest
00536 //               and most recent of all the available source images.
00537 ////////////////////////////////////////////////////////////////////
00538 SourceTextureImage *TextureImage::
00539 get_preferred_source() {
00540   if (_preferred_source != (SourceTextureImage *)NULL) {
00541     return _preferred_source;
00542   }
00543 
00544   // Now examine all of the various source images available to us and
00545   // pick the most suitable.  We base this on the following criteria:
00546 
00547   // (1) A suitable source image must be referenced by at least one
00548   // egg file, unless no source images are referenced by any egg file.
00549 
00550   // (2) A larger source image is preferable to a smaller one.
00551 
00552   // (3) Given two source images of the same size, the more recent one
00553   // is preferable.
00554 
00555   // Are any source images referenced by an egg file?
00556 
00557   bool any_referenced = false;
00558   Sources::iterator si;
00559   for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) {
00560     SourceTextureImage *source = (*si).second;
00561     if (source->get_egg_count() > 0) {
00562       any_referenced = true;
00563     }
00564   }
00565 
00566   SourceTextureImage *best = (SourceTextureImage *)NULL;
00567   int best_size = 0;
00568 
00569   for (si = _sources.begin(); si != _sources.end(); ++si) {
00570     SourceTextureImage *source = (*si).second;
00571 
00572     if (source->get_egg_count() > 0 || !any_referenced) {
00573       // Rule (1) passes.
00574 
00575       if (source->exists() && source->get_size()) {
00576         int source_size = source->get_x_size() * source->get_y_size();
00577         if (best == (SourceTextureImage *)NULL) {
00578           best = source;
00579           best_size = source_size;
00580 
00581         } else if (source_size > best_size) {
00582           // Rule (2) passes.
00583           best = source;
00584           best_size = source_size;
00585 
00586         } else if (source_size == best_size &&
00587                    source->get_filename().compare_timestamps(best->get_filename()) > 0) {
00588           // Rule (3) passes.
00589           best = source;
00590           best_size = source_size;
00591         }
00592       }
00593     }
00594   }
00595 
00596   if (best == (SourceTextureImage *)NULL && !_sources.empty()) {
00597     // If we didn't pick any that pass, it must be that all of them
00598     // are unreadable.  In this case, it really doesn't matter which
00599     // one we pick, but we should at least pick one that has an egg
00600     // reference, if any of them do.
00601     if (any_referenced) {
00602       for (si = _sources.begin();
00603            si != _sources.end() && best == (SourceTextureImage *)NULL;
00604            ++si) {
00605         SourceTextureImage *source = (*si).second;
00606         if (source->get_egg_count() > 0) {
00607           best = source;
00608         }
00609       }
00610     } else {
00611       best = (*_sources.begin()).second;
00612     }
00613   }
00614 
00615   _preferred_source = best;
00616   return _preferred_source;
00617 }
00618 
00619 ////////////////////////////////////////////////////////////////////
00620 //     Function: TextureImage::clear_source_basic_properties
00621 //       Access: Public
00622 //  Description: Calls clear_basic_properties() on each source texture
00623 //               image used by this texture, to reset the properties
00624 //               in preparation for re-applying them from the set of
00625 //               all known egg files.
00626 ////////////////////////////////////////////////////////////////////
00627 void TextureImage::
00628 clear_source_basic_properties() {
00629   Sources::iterator si;
00630   for (si = _sources.begin(); si != _sources.end(); ++si) {
00631     SourceTextureImage *source = (*si).second;
00632     source->clear_basic_properties();
00633   }
00634 }
00635 
00636 ////////////////////////////////////////////////////////////////////
00637 //     Function: TextureImage::copy_unplaced
00638 //       Access: Public
00639 //  Description: Copies the texture to whichever destination
00640 //               directories are appropriate for the groups in which
00641 //               it has been unplaced.  Also removes the old filenames
00642 //               for previous sessions where it was unplaced, but is
00643 //               no longer.
00644 //
00645 //               If redo_all is true, this recopies the texture
00646 //               whether it needed to or not.
00647 ////////////////////////////////////////////////////////////////////
00648 void TextureImage::
00649 copy_unplaced(bool redo_all) {
00650   // First, we need to build up the set of DestTextureImages that
00651   // represents the files we need to generate.
00652   Dests generate;
00653 
00654   // Go through all the TexturePlacements and note the ones for which
00655   // we're unplaced.  We check get_omit_reason() and not is_placed(),
00656   // because we want to consider solitary images to be unplaced in
00657   // this case.
00658   Placement::iterator pi;
00659   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00660     TexturePlacement *placement = (*pi).second;
00661     if (placement->get_omit_reason() != OR_none &&
00662         placement->get_omit_reason() != OR_unknown) {
00663       DestTextureImage *dest = new DestTextureImage(placement);
00664       Filename filename = dest->get_filename();
00665       filename.make_canonical();
00666 
00667       pair<Dests::iterator, bool> insert_result = generate.insert
00668         (Dests::value_type(filename, dest));
00669       if (!insert_result.second) {
00670         // At least two DestTextureImages map to the same filename, no
00671         // sweat.
00672         delete dest;
00673         dest = (*insert_result.first).second;
00674       }
00675 
00676       placement->set_dest(dest);
00677 
00678     } else {
00679       placement->set_dest((DestTextureImage *)NULL);
00680     }
00681   }
00682 
00683   if (redo_all) {
00684     // If we're redoing everything, we remove everything first and
00685     // then recopy it again.
00686     Dests empty;
00687     remove_old_dests(empty, _dests);
00688     copy_new_dests(generate, empty);
00689 
00690   } else {
00691     // Otherwise, we only remove and recopy the things that changed
00692     // between this time and last time.
00693     remove_old_dests(generate, _dests);
00694     copy_new_dests(generate, _dests);
00695   }
00696 
00697   // Clean up the old set.
00698   Dests::iterator di;
00699   for (di = _dests.begin(); di != _dests.end(); ++di) {
00700     delete (*di).second;
00701   }
00702 
00703   _dests.swap(generate);
00704 }
00705 
00706 ////////////////////////////////////////////////////////////////////
00707 //     Function: TextureImage::read_source_image
00708 //       Access: Public
00709 //  Description: Reads in the original image, if it has not already
00710 //               been read, and returns it.
00711 ////////////////////////////////////////////////////////////////////
00712 const PNMImage &TextureImage::
00713 read_source_image() {
00714   if (!_read_source_image) {
00715     SourceTextureImage *source = get_preferred_source();
00716     if (source != (SourceTextureImage *)NULL) {
00717       source->read(_source_image);
00718     }
00719     _read_source_image = true;
00720     _ever_read_image = true;
00721   }
00722 
00723   return _source_image;
00724 }
00725 
00726 ////////////////////////////////////////////////////////////////////
00727 //     Function: TextureImage::read_header
00728 //       Access: Public
00729 //  Description: Causes the header part of the image to be reread,
00730 //               usually to confirm that its image properties (size,
00731 //               number of channels, etc.) haven't changed.
00732 ////////////////////////////////////////////////////////////////////
00733 void TextureImage::
00734 read_header() {
00735   if (!_read_source_image) {
00736     SourceTextureImage *source = get_preferred_source();
00737     if (source != (SourceTextureImage *)NULL) {
00738       source->read_header();
00739     }
00740   }
00741 }
00742 
00743 ////////////////////////////////////////////////////////////////////
00744 //     Function: TextureImage::is_newer_than
00745 //       Access: Public
00746 //  Description: Returns true if the source image is newer than the
00747 //               indicated file, false otherwise.  If the image has
00748 //               already been read, this always returns false.
00749 ////////////////////////////////////////////////////////////////////
00750 bool TextureImage::
00751 is_newer_than(const Filename &reference_filename) {
00752   if (!_read_source_image) {
00753     SourceTextureImage *source = get_preferred_source();
00754     if (source != (SourceTextureImage *)NULL) {
00755       const Filename &source_filename = source->get_filename();
00756       return source_filename.compare_timestamps(reference_filename) >= 0;
00757     }
00758   }
00759 
00760   return false;
00761 }
00762 
00763 ////////////////////////////////////////////////////////////////////
00764 //     Function: TextureImage::write_source_pathnames
00765 //       Access: Public
00766 //  Description: Writes the list of source pathnames that might
00767 //               contribute to this texture to the indicated output
00768 //               stream, one per line.
00769 ////////////////////////////////////////////////////////////////////
00770 void TextureImage::
00771 write_source_pathnames(ostream &out, int indent_level) const {
00772   Sources::const_iterator si;
00773   for (si = _sources.begin(); si != _sources.end(); ++si) {
00774     SourceTextureImage *source = (*si).second;
00775 
00776     if (source->get_egg_count() > 0) {
00777       indent(out, indent_level);
00778       source->output_filename(out);
00779       if (!source->is_size_known()) {
00780         out << " (unknown size)";
00781 
00782       } else {
00783         out << " " << source->get_x_size() << " "
00784             << source->get_y_size();
00785 
00786         if (source->get_properties().has_num_channels()) {
00787           out << " " << source->get_properties().get_num_channels();
00788         }
00789       }
00790       out << "\n";
00791     }
00792   }
00793 
00794   // Now write out the group assignments.
00795   if (!_egg_files.empty()) {
00796     // Sort the egg files into order by name for output.
00797     pvector<EggFile *> egg_vector;
00798     egg_vector.reserve(_egg_files.size());
00799     EggFiles::const_iterator ei;
00800     for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
00801       egg_vector.push_back(*ei);
00802     }
00803     sort(egg_vector.begin(), egg_vector.end(),
00804          IndirectCompareNames<EggFile>());
00805 
00806     indent(out, indent_level)
00807       << "Used by:\n";
00808     pvector<EggFile *>::const_iterator evi;
00809     for (evi = egg_vector.begin(); evi != egg_vector.end(); ++evi) {
00810       EggFile *egg = (*evi);
00811       indent(out, indent_level + 2)
00812         << egg->get_name() << " (";
00813       if (egg->get_explicit_groups().empty()) {
00814         out << *egg->get_default_group();
00815       } else {
00816         out << egg->get_explicit_groups();
00817       }
00818       out << ")\n";
00819     }
00820   }
00821   if (!_explicitly_assigned_groups.empty()) {
00822     indent(out, indent_level)
00823       << "Explicitly assigned to " << _explicitly_assigned_groups << " in .txa\n";
00824   }
00825 
00826   if (_placement.empty()) {
00827     indent(out, indent_level)
00828       << "Not used.\n";
00829   } else {
00830     indent(out, indent_level)
00831       << "Assigned to " << _actual_assigned_groups << "\n";
00832   }
00833 }
00834 
00835 ////////////////////////////////////////////////////////////////////
00836 //     Function: TextureImage::write_scale_info
00837 //       Access: Public
00838 //  Description: Writes the list of source pathnames that might
00839 //               contribute to this texture to the indicated output
00840 //               stream, one per line.
00841 ////////////////////////////////////////////////////////////////////
00842 void TextureImage::
00843 write_scale_info(ostream &out, int indent_level) {
00844   SourceTextureImage *source = get_preferred_source();
00845   indent(out, indent_level) << get_name();
00846 
00847   // Write the list of groups we're placed in.
00848   if (_placement.empty()) {
00849     out << " (not used)";
00850   } else {
00851     Placement::const_iterator pi;
00852     pi = _placement.begin();
00853     out << " (" << (*pi).second->get_group()->get_name();
00854     ++pi;
00855     while (pi != _placement.end()) {
00856       out << " " << (*pi).second->get_group()->get_name();
00857       ++pi;
00858     }
00859     out << ")";
00860   }
00861 
00862   out << " orig ";
00863 
00864   if (source == (SourceTextureImage *)NULL ||
00865       !source->is_size_known()) {
00866     out << "unknown";
00867   } else {
00868     out << source->get_x_size() << " " << source->get_y_size()
00869         << " " << source->get_num_channels();
00870   }
00871 
00872   if (!_placement.empty() && is_size_known()) {
00873     out << " new " << get_x_size() << " " << get_y_size()
00874         << " " << get_num_channels();
00875 
00876     if (source != (SourceTextureImage *)NULL &&
00877         source->is_size_known()) {
00878       double scale =
00879         100.0 * (((double)get_x_size() / (double)source->get_x_size()) +
00880                  ((double)get_y_size() / (double)source->get_y_size())) / 2.0;
00881       out << " scale " << scale << "%";
00882     }
00883   }
00884   out << "\n";
00885 
00886   // Also cross-reference the placed and unplaced information.
00887   Placement::iterator pi;
00888   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00889     TexturePlacement *placement = (*pi).second;
00890     if (placement->get_omit_reason() == OR_none) {
00891       PaletteImage *image = placement->get_image();
00892       nassertv(image != (PaletteImage *)NULL);
00893       indent(out, indent_level + 2)
00894         << "placed on "
00895         << FilenameUnifier::make_user_filename(image->get_filename())
00896         << "\n";
00897 
00898     } else if (placement->get_omit_reason() == OR_unknown) {
00899       indent(out, indent_level + 2)
00900         << "not placed because unknown.\n";
00901 
00902     } else {
00903       DestTextureImage *image = placement->get_dest();
00904       nassertv(image != (DestTextureImage *)NULL);
00905       indent(out, indent_level + 2)
00906         << "copied to "
00907         << FilenameUnifier::make_user_filename(image->get_filename());
00908       if (image->is_size_known() && is_size_known() &&
00909           (image->get_x_size() != get_x_size() ||
00910            image->get_y_size() != get_y_size())) {
00911         out << " at size " << image->get_x_size() << " "
00912             << image->get_y_size();
00913         if (source != (SourceTextureImage *)NULL &&
00914             source->is_size_known()) {
00915           double scale =
00916             100.0 * (((double)image->get_x_size() / (double)source->get_x_size()) +
00917                      ((double)image->get_y_size() / (double)source->get_y_size())) / 2.0;
00918           out << " scale " << scale << "%";
00919         }
00920       }
00921       out << "\n";
00922     }
00923   }
00924 }
00925 
00926 ////////////////////////////////////////////////////////////////////
00927 //     Function: TextureImage::compute_egg_count
00928 //       Access: Private
00929 //  Description: Counts the number of egg files in the indicated set
00930 //               that will be satisfied if a texture is assigned to
00931 //               the indicated group.
00932 ////////////////////////////////////////////////////////////////////
00933 int TextureImage::
00934 compute_egg_count(PaletteGroup *group,
00935                   const TextureImage::WorkingEggs &egg_files) {
00936   int count = 0;
00937 
00938   WorkingEggs::const_iterator ei;
00939   for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) {
00940     if ((*ei)->get_complete_groups().count(group) != 0) {
00941       count++;
00942     }
00943   }
00944 
00945   return count;
00946 }
00947 
00948 ////////////////////////////////////////////////////////////////////
00949 //     Function: TextureImage::assign_to_groups
00950 //       Access: Private
00951 //  Description: Assigns the texture to the indicated set of groups.
00952 //               If the texture was previously assigned to any of
00953 //               these groups, keeps the same TexturePlacement object
00954 //               for the assignment; at the same time, deletes any
00955 //               TexturePlacement objects that represent groups we are
00956 //               no longer assigned to.
00957 ////////////////////////////////////////////////////////////////////
00958 void TextureImage::
00959 assign_to_groups(const PaletteGroups &groups) {
00960   PaletteGroups::const_iterator gi;
00961   Placement::const_iterator pi;
00962 
00963   Placement new_placement;
00964 
00965   gi = groups.begin();
00966   pi = _placement.begin();
00967 
00968   while (gi != groups.end() && pi != _placement.end()) {
00969     PaletteGroup *a = (*gi);
00970     PaletteGroup *b = (*pi).first;
00971 
00972     if (a < b) {
00973       // Here's a group we're now assigned to that we weren't assigned
00974       // to previously.
00975       TexturePlacement *place = a->prepare(this);
00976       new_placement.insert
00977         (new_placement.end(), Placement::value_type(a, place));
00978       ++gi;
00979 
00980     } else if (b < a) {
00981       // Here's a group we're no longer assigned to.
00982       TexturePlacement *place = (*pi).second;
00983       delete place;
00984       ++pi;
00985 
00986     } else { // b == a
00987       // Here's a group we're still assigned to.
00988       TexturePlacement *place = (*pi).second;
00989       new_placement.insert
00990         (new_placement.end(), Placement::value_type(a, place));
00991       ++gi;
00992       ++pi;
00993     }
00994   }
00995 
00996   while (gi != groups.end()) {
00997     // Here's a group we're now assigned to that we weren't assigned
00998     // to previously.
00999     PaletteGroup *a = (*gi);
01000     TexturePlacement *place = a->prepare(this);
01001     new_placement.insert
01002       (new_placement.end(), Placement::value_type(a, place));
01003     ++gi;
01004   }
01005 
01006   while (pi != _placement.end()) {
01007     // Here's a group we're no longer assigned to.
01008     TexturePlacement *place = (*pi).second;
01009     delete place;
01010     ++pi;
01011   }
01012 
01013   _placement.swap(new_placement);
01014   _actual_assigned_groups = groups;
01015 }
01016 
01017 ////////////////////////////////////////////////////////////////////
01018 //     Function: TextureImage::consider_grayscale
01019 //       Access: Private
01020 //  Description: Examines the actual contents of the image to
01021 //               determine if it should maybe be considered a
01022 //               grayscale image (even though it has separate rgb
01023 //               components).
01024 ////////////////////////////////////////////////////////////////////
01025 void TextureImage::
01026 consider_grayscale() {
01027   // Since this isn't likely to change for a particular texture after
01028   // its creation, we save a bit of time by not performing this check
01029   // unless this is the first time we've ever seen this texture.  This
01030   // will save us from having to load the texture images each time we
01031   // look at them.  On the other hand, if we've already loaded up the
01032   // image, then go ahead.
01033   if (!_read_source_image && _ever_read_image) {
01034     if (_forced_grayscale) {
01035       _properties.force_grayscale();
01036     }
01037     return;
01038   }
01039 
01040   const PNMImage &source = read_source_image();
01041   if (!source.is_valid()) {
01042     return;
01043   }
01044 
01045   for (int y = 0; y < source.get_y_size(); y++) {
01046     for (int x = 0; x < source.get_x_size(); x++) {
01047       const xel &v = source.get_xel_val(x, y);
01048       if (PPM_GETR(v) != PPM_GETG(v) || PPM_GETR(v) != PPM_GETB(v)) {
01049         // Here's a colored pixel.  We can't go grayscale.
01050         _forced_grayscale = false;
01051         return;
01052       }
01053     }
01054   }
01055 
01056   // All pixels in the image were grayscale!
01057   _properties.force_grayscale();
01058   _forced_grayscale = true;
01059 }
01060 
01061 ////////////////////////////////////////////////////////////////////
01062 //     Function: TextureImage::consider_alpha
01063 //       Access: Private
01064 //  Description: Examines the actual contents of the image to
01065 //               determine what alpha properties it has.
01066 ////////////////////////////////////////////////////////////////////
01067 void TextureImage::
01068 consider_alpha() {
01069   // As above, we don't bother doing this if we've already done this
01070   // in a previous session.
01071 
01072   // _alpha_bits == -1 indicates we have read an older textures.boo
01073   // file that didn't define these bits.
01074   if (_read_source_image || !_ever_read_image || _alpha_bits == -1) {
01075     _alpha_bits = 0;
01076 
01077     const PNMImage &source = read_source_image();
01078     if (source.is_valid() && source.has_alpha()) {
01079       xelval maxval = source.get_maxval();
01080       for (int y = 0; y < source.get_y_size(); y++) {
01081         for (int x = 0; x < source.get_x_size(); x++) {
01082           xelval alpha_val = source.get_alpha_val(x, y);
01083           if (alpha_val == 0) {
01084             _alpha_bits |= AB_zero;
01085           } else if (alpha_val == maxval) {
01086             _alpha_bits |= AB_one;
01087           } else {
01088             _alpha_bits |= AB_mid;
01089           }
01090           if (_alpha_bits == AB_all) {
01091             // Once we've found a sample of everything, we can stop
01092             // searching.
01093             break;
01094           }
01095         }
01096       }
01097     }
01098   }
01099 
01100   if (_alpha_bits != 0) {
01101     if (_alpha_bits == AB_one) {
01102       // All alpha pixels are white; drop the alpha channel.
01103       _properties.force_nonalpha();
01104 
01105     } else if (_alpha_bits == AB_zero) {
01106       // All alpha pixels are invisible; this is probably a mistake.
01107       // Drop the alpha channel and complain.
01108       _properties.force_nonalpha();
01109       if (_read_source_image) {
01110         nout << *this << " has an all-zero alpha channel; dropping alpha.\n";
01111       }
01112 
01113     } else if (_alpha_mode == EggRenderMode::AM_unspecified) {
01114       // Consider fiddling with the alpha mode, if the user hasn't
01115       // specified a particular alpha mode in the txa file.
01116       if ((_alpha_bits & AB_mid) == 0) {
01117         // No middle range bits: a binary alpha image.
01118         _alpha_mode = EggRenderMode::AM_binary;
01119 
01120       } else if ((_alpha_bits & AB_one) != 0) {
01121         // At least some opaque bits: a dual alpha image.
01122         _alpha_mode = EggRenderMode::AM_dual;
01123 
01124       } else {
01125         // No opaque bits; just use regular alpha blending.
01126         _alpha_mode = EggRenderMode::AM_blend;
01127       }
01128     }
01129   }
01130 }
01131 
01132 ////////////////////////////////////////////////////////////////////
01133 //     Function: TextureImage::remove_old_dests
01134 //       Access: Private
01135 //  Description: Removes all of the filenames named in b that are not
01136 //               also named in a.
01137 ////////////////////////////////////////////////////////////////////
01138 void TextureImage::
01139 remove_old_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
01140   Dests::const_iterator ai = a.begin();
01141   Dests::const_iterator bi = b.begin();
01142 
01143   while (ai != a.end() && bi != b.end()) {
01144     const string &astr = (*ai).first;
01145     const string &bstr = (*bi).first;
01146 
01147     if (astr < bstr) {
01148       // Here's a filename in a, not in b.
01149       ++ai;
01150 
01151     } else if (bstr < astr) {
01152       // Here's a filename in b, not in a.
01153       (*bi).second->unlink();
01154       ++bi;
01155 
01156     } else { // bstr == astr
01157       // Here's a filename in both a and b.
01158       ++ai;
01159       ++bi;
01160     }
01161   }
01162 
01163   while (bi != b.end()) {
01164     // Here's a filename in b, not in a.
01165     (*bi).second->unlink();
01166     ++bi;
01167   }
01168 
01169   while (ai != a.end()) {
01170     ++ai;
01171   }
01172 }
01173 
01174 ////////////////////////////////////////////////////////////////////
01175 //     Function: TextureImage::copy_new_dests
01176 //       Access: Private
01177 //  Description: Copies a resized texture into each filename named in
01178 //               a that is not also listed in b, or whose
01179 //               corresponding listing in b is out of date.
01180 ////////////////////////////////////////////////////////////////////
01181 void TextureImage::
01182 copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
01183   Dests::const_iterator ai = a.begin();
01184   Dests::const_iterator bi = b.begin();
01185 
01186   while (ai != a.end() && bi != b.end()) {
01187     const string &astr = (*ai).first;
01188     const string &bstr = (*bi).first;
01189 
01190     if (astr < bstr) {
01191       // Here's a filename in a, not in b.
01192       (*ai).second->copy(this);
01193       ++ai;
01194 
01195     } else if (bstr < astr) {
01196       // Here's a filename in b, not in a.
01197       ++bi;
01198 
01199     } else { // bstr == astr
01200       // Here's a filename in both a and b.
01201       (*ai).second->copy_if_stale((*bi).second, this);
01202       ++ai;
01203       ++bi;
01204     }
01205   }
01206 
01207   while (ai != a.end()) {
01208     // Here's a filename in a, not in b.
01209     (*ai).second->copy(this);
01210     ++ai;
01211   }
01212 }
01213 
01214 ////////////////////////////////////////////////////////////////////
01215 //     Function: TextureImage::get_source_key
01216 //       Access: Private
01217 //  Description: Returns the key that a SourceTextureImage should be
01218 //               stored in, given its one or two filenames.
01219 ////////////////////////////////////////////////////////////////////
01220 string TextureImage::
01221 get_source_key(const Filename &filename, const Filename &alpha_filename,
01222                int alpha_file_channel) {
01223   Filename f = FilenameUnifier::make_bam_filename(filename);
01224   Filename a = FilenameUnifier::make_bam_filename(alpha_filename);
01225 
01226   return f.get_fullpath() + ":" + a.get_fullpath() + ":" +
01227     format_string(alpha_file_channel);
01228 }
01229 
01230 ////////////////////////////////////////////////////////////////////
01231 //     Function: TextureImage::register_with_read_factory
01232 //       Access: Public, Static
01233 //  Description: Registers the current object as something that can be
01234 //               read from a Bam file.
01235 ////////////////////////////////////////////////////////////////////
01236 void TextureImage::
01237 register_with_read_factory() {
01238   BamReader::get_factory()->
01239     register_factory(get_class_type(), make_TextureImage);
01240 }
01241 
01242 ////////////////////////////////////////////////////////////////////
01243 //     Function: TextureImage::write_datagram
01244 //       Access: Public, Virtual
01245 //  Description: Fills the indicated datagram up with a binary
01246 //               representation of the current object, in preparation
01247 //               for writing to a Bam file.
01248 ////////////////////////////////////////////////////////////////////
01249 void TextureImage::
01250 write_datagram(BamWriter *writer, Datagram &datagram) {
01251   ImageFile::write_datagram(writer, datagram);
01252   datagram.add_string(get_name());
01253 
01254   // We don't write out _request; this is re-read from the .txa file
01255   // each time.
01256 
01257   // We don't write out _pre_txa_properties; this is transitional.
01258 
01259   // We don't write out _preferred_source; this is redetermined each
01260   // session.
01261 
01262   datagram.add_bool(_is_surprise);
01263   datagram.add_bool(_ever_read_image);
01264   datagram.add_bool(_forced_grayscale);
01265   datagram.add_uint8(_alpha_bits);
01266   datagram.add_int16((int)_alpha_mode);
01267 
01268   // We don't write out _explicitly_assigned_groups; this is re-read
01269   // from the .txa file each time.
01270 
01271   _actual_assigned_groups.write_datagram(writer, datagram);
01272 
01273   // We don't write out _egg_files; this is redetermined each session.
01274 
01275   datagram.add_uint32(_placement.size());
01276   Placement::const_iterator pi;
01277   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
01278     writer->write_pointer(datagram, (*pi).first);
01279     writer->write_pointer(datagram, (*pi).second);
01280   }
01281 
01282   datagram.add_uint32(_sources.size());
01283   Sources::const_iterator si;
01284   for (si = _sources.begin(); si != _sources.end(); ++si) {
01285     writer->write_pointer(datagram, (*si).second);
01286   }
01287 
01288   datagram.add_uint32(_dests.size());
01289   Dests::const_iterator di;
01290   for (di = _dests.begin(); di != _dests.end(); ++di) {
01291     writer->write_pointer(datagram, (*di).second);
01292   }
01293 }
01294 
01295 ////////////////////////////////////////////////////////////////////
01296 //     Function: TextureImage::complete_pointers
01297 //       Access: Public, Virtual
01298 //  Description: Called after the object is otherwise completely read
01299 //               from a Bam file, this function's job is to store the
01300 //               pointers that were retrieved from the Bam file for
01301 //               each pointer object written.  The return value is the
01302 //               number of pointers processed from the list.
01303 ////////////////////////////////////////////////////////////////////
01304 int TextureImage::
01305 complete_pointers(TypedWritable **p_list, BamReader *manager) {
01306   int pi = ImageFile::complete_pointers(p_list, manager);
01307 
01308   pi += _actual_assigned_groups.complete_pointers(p_list + pi, manager);
01309 
01310   int i;
01311   for (i = 0; i < _num_placement; i++) {
01312     PaletteGroup *group;
01313     TexturePlacement *placement;
01314     DCAST_INTO_R(group, p_list[pi++], pi);
01315     DCAST_INTO_R(placement, p_list[pi++], pi);
01316     _placement.insert(Placement::value_type(group, placement));
01317   }
01318 
01319   for (i = 0; i < _num_sources; i++) {
01320     SourceTextureImage *source;
01321     DCAST_INTO_R(source, p_list[pi++], pi);
01322     string key = get_source_key(source->get_filename(),
01323                                 source->get_alpha_filename(),
01324                                 source->get_alpha_file_channel());
01325 
01326     bool inserted = _sources.insert(Sources::value_type(key, source)).second;
01327     if (!inserted) {
01328       nout << "Warning: texture key " << key
01329            << " is nonunique; texture lost.\n";
01330     }
01331   }
01332 
01333   for (i = 0; i < _num_dests; i++) {
01334     DestTextureImage *dest;
01335     DCAST_INTO_R(dest, p_list[pi++], pi);
01336     bool inserted = _dests.insert(Dests::value_type(dest->get_filename(), dest)).second;
01337     if (!inserted) {
01338       nout << "Warning: dest filename " << dest->get_filename()
01339            << " is nonunique; texture lost.\n";
01340     }
01341   }
01342 
01343   return pi;
01344 }
01345 
01346 ////////////////////////////////////////////////////////////////////
01347 //     Function: TextureImage::make_TextureImage
01348 //       Access: Protected
01349 //  Description: This method is called by the BamReader when an object
01350 //               of this type is encountered in a Bam file; it should
01351 //               allocate and return a new object with all the data
01352 //               read.
01353 ////////////////////////////////////////////////////////////////////
01354 TypedWritable *TextureImage::
01355 make_TextureImage(const FactoryParams &params) {
01356   TextureImage *me = new TextureImage;
01357   DatagramIterator scan;
01358   BamReader *manager;
01359 
01360   parse_params(params, scan, manager);
01361   me->fillin(scan, manager);
01362   return me;
01363 }
01364 
01365 ////////////////////////////////////////////////////////////////////
01366 //     Function: TextureImage::fillin
01367 //       Access: Protected
01368 //  Description: Reads the binary data from the given datagram
01369 //               iterator, which was written by a previous call to
01370 //               write_datagram().
01371 ////////////////////////////////////////////////////////////////////
01372 void TextureImage::
01373 fillin(DatagramIterator &scan, BamReader *manager) {
01374   ImageFile::fillin(scan, manager);
01375   set_name(scan.get_string());
01376 
01377   _is_surprise = scan.get_bool();
01378   _ever_read_image = scan.get_bool();
01379   _forced_grayscale = scan.get_bool();
01380   _alpha_bits = scan.get_uint8();
01381   _alpha_mode = (EggRenderMode::AlphaMode)scan.get_int16();
01382 
01383   _actual_assigned_groups.fillin(scan, manager);
01384 
01385   _num_placement = scan.get_uint32();
01386   manager->read_pointers(scan, _num_placement * 2);
01387 
01388   _num_sources = scan.get_uint32();
01389   manager->read_pointers(scan, _num_sources);
01390   _num_dests = scan.get_uint32();
01391   manager->read_pointers(scan, _num_dests);
01392 }

Generated on Fri May 2 03:18:10 2003 for Panda-Tool by doxygen1.3