Panda3D
 All Classes Functions Variables Enumerations
eggFile.cxx
00001 // Filename: eggFile.cxx
00002 // Created by:  drose (29Nov00)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "eggFile.h"
00016 #include "textureImage.h"
00017 #include "paletteGroup.h"
00018 #include "texturePlacement.h"
00019 #include "textureReference.h"
00020 #include "sourceTextureImage.h"
00021 #include "palettizer.h"
00022 #include "filenameUnifier.h"
00023 
00024 #include "eggData.h"
00025 #include "eggGroup.h"
00026 #include "eggTextureCollection.h"
00027 #include "eggComment.h"
00028 #include "datagram.h"
00029 #include "datagramIterator.h"
00030 #include "bamReader.h"
00031 #include "bamWriter.h"
00032 #include "executionEnvironment.h"
00033 #include "dSearchPath.h"
00034 #include "indirectLess.h"
00035 
00036 #include <algorithm>
00037 
00038 TypeHandle EggFile::_type_handle;
00039 
00040 ////////////////////////////////////////////////////////////////////
00041 //     Function: EggFile::Constructor
00042 //       Access: Public
00043 //  Description:
00044 ////////////////////////////////////////////////////////////////////
00045 EggFile::
00046 EggFile() {
00047   _data = (EggData *)NULL;
00048   _first_txa_match = false;
00049   _default_group = (PaletteGroup *)NULL;
00050   _is_surprise = true;
00051   _is_stale = true;
00052   _had_data = false;
00053 }
00054 
00055 ////////////////////////////////////////////////////////////////////
00056 //     Function: EggFile::from_command_line
00057 //       Access: Public
00058 //  Description: Accepts the information about the egg file as
00059 //               supplied from the command line.  Returns true if the
00060 //               egg file is valid, false otherwise.
00061 ////////////////////////////////////////////////////////////////////
00062 bool EggFile::
00063 from_command_line(EggData *data,
00064                   const Filename &source_filename,
00065                   const Filename &dest_filename,
00066                   const string &egg_comment) {
00067   _data = data;
00068   _had_data = true;
00069   remove_backstage(_data);
00070 
00071   // We save the current directory at the time the egg file appeared
00072   // on the command line, so that we'll later be able to properly
00073   // resolve external references (like textures) that might be
00074   // relative to this directory.
00075   _current_directory = ExecutionEnvironment::get_cwd();
00076   _source_filename = source_filename;
00077   _source_filename.make_absolute();
00078   _dest_filename = dest_filename;
00079   _dest_filename.make_absolute();
00080 
00081   // We also save the command line that loaded this egg file, so we
00082   // can continue to write it as a comment to the beginning of the egg
00083   // file, should we need to rewrite it later.
00084   _egg_comment = egg_comment;
00085 
00086   // We save the default PaletteGroup at this point, because the egg
00087   // file inherits the default group that was in effect when it was
00088   // specified on the command line.
00089   _default_group = pal->get_default_group();
00090 
00091   return true;
00092 }
00093 
00094 ////////////////////////////////////////////////////////////////////
00095 //     Function: EggFile::get_source_filename
00096 //       Access: Public
00097 //  Description: Returns the filename this egg file was read from.
00098 ////////////////////////////////////////////////////////////////////
00099 const Filename &EggFile::
00100 get_source_filename() const {
00101   return _source_filename;
00102 }
00103 
00104 
00105 ////////////////////////////////////////////////////////////////////
00106 //     Function: EggFile::scan_textures
00107 //       Access: Public
00108 //  Description: Scans the egg file for texture references and updates
00109 //               the _textures list appropriately.  This assumes the
00110 //               egg file was supplied on the command line and thus
00111 //               the _data member is available.
00112 ////////////////////////////////////////////////////////////////////
00113 void EggFile::
00114 scan_textures() {
00115   nassertv(_data != (EggData *)NULL);
00116 
00117   // Extract the set of textures referenced by this egg file.
00118   EggTextureCollection tc;
00119   tc.find_used_textures(_data);
00120   
00121   // Make sure each tref name is unique within a given file.
00122   tc.uniquify_trefs();
00123 
00124   // Now build up a list of new TextureReference objects that
00125   // represent the textures actually used and their uv range, etc.
00126   Textures new_textures;
00127 
00128   EggTextureCollection::iterator eti;
00129   for (eti = tc.begin(); eti != tc.end(); ++eti) {
00130     EggTexture *egg_tex = (*eti);
00131 
00132     TextureReference *ref = new TextureReference;
00133     ref->from_egg(this, _data, egg_tex);
00134 
00135     if (!ref->has_uvs()) {
00136       // This texture isn't *really* referenced.  (Usually this
00137       // happens if the texture is only referenced by "backstage"
00138       // geometry, which we don't care about.)
00139       delete ref;
00140 
00141     } else {
00142       new_textures.push_back(ref);
00143     }
00144   }
00145 
00146   // Sort the new references into order so we can compare them with
00147   // the original references.
00148   sort(new_textures.begin(), new_textures.end(), 
00149        IndirectLess<TextureReference>());
00150   
00151   // Sort the original references too.  This should already be sorted
00152   // from the previous run, but we might as well be neurotic about it.
00153   sort(_textures.begin(), _textures.end(), 
00154        IndirectLess<TextureReference>());
00155 
00156   // Now go through and merge the lists.
00157   Textures combined_textures;
00158   Textures::const_iterator ai = _textures.begin();
00159   Textures::const_iterator bi = new_textures.begin();
00160 
00161   while (ai != _textures.end() && bi != new_textures.end()) {
00162     TextureReference *aref = (*ai);
00163     TextureReference *bref = (*bi);
00164 
00165     if ((*aref) < (*bref)) {
00166       // Here's a texture reference in the original list, but not in
00167       // the new list.  Remove it.
00168       delete aref;
00169       ++ai;
00170 
00171     } else if ((*bref) < (*aref)) {
00172       // Here's a texture reference in the new list, but not in the
00173       // original list.  Add it.
00174       combined_textures.push_back(bref);
00175       ++bi;
00176 
00177     } else { // (*bref) == (*aref)
00178       // Here's a texture reference that was in both lists.  Compare it.
00179       if (aref->is_equivalent(*bref)) {
00180         // It hasn't changed substantially, so keep the original
00181         // (which still has the placement references from a previous
00182         // pass).
00183         aref->from_egg_quick(*bref);
00184         combined_textures.push_back(aref);
00185         delete bref;
00186 
00187       } else {
00188         // It has changed, so drop the original and keep the new one.
00189         combined_textures.push_back(bref);
00190         delete aref;
00191       }
00192       ++ai;
00193       ++bi;
00194     }
00195   }
00196 
00197   while (bi != new_textures.end()) {
00198     TextureReference *bref = (*bi);
00199     // Here's a texture reference in the new list, but not in the
00200     // original list.  Add it.
00201     combined_textures.push_back(bref);
00202     ++bi;
00203   }
00204 
00205   while (ai != _textures.end()) {
00206     TextureReference *aref = (*ai);
00207     // Here's a texture reference in the original list, but not in
00208     // the new list.  Remove it.
00209     delete aref;
00210     ++ai;
00211   }
00212 
00213   _textures.swap(combined_textures);
00214 }
00215 
00216 ////////////////////////////////////////////////////////////////////
00217 //     Function: EggFile::get_textures
00218 //       Access: Public
00219 //  Description: Fills up the indicated set with the set of textures
00220 //               referenced by this egg file.  It is the user's
00221 //               responsibility to ensure the set is empty before
00222 //               making this call; otherwise, the new textures will be
00223 //               appended to the existing set.
00224 ////////////////////////////////////////////////////////////////////
00225 void EggFile::
00226 get_textures(pset<TextureImage *> &result) const {
00227   Textures::const_iterator ti;
00228   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00229     result.insert((*ti)->get_texture());
00230   }
00231 }
00232 
00233 ////////////////////////////////////////////////////////////////////
00234 //     Function: EggFile::pre_txa_file
00235 //       Access: Public
00236 //  Description: Does some processing prior to scanning the .txa file.
00237 ////////////////////////////////////////////////////////////////////
00238 void EggFile::
00239 pre_txa_file() {
00240   _is_surprise = true;
00241   _first_txa_match = true;
00242 }
00243 
00244 ////////////////////////////////////////////////////////////////////
00245 //     Function: EggFile::match_txa_groups
00246 //       Access: Public
00247 //  Description: Adds the indicated set of groups, read from the .txa
00248 //               file, to the set of groups to which the egg file is
00249 //               assigned.
00250 ////////////////////////////////////////////////////////////////////
00251 void EggFile::
00252 match_txa_groups(const PaletteGroups &groups) {
00253   if (_first_txa_match) {
00254     // If this is the first line we matched in the .txa file, clear
00255     // the set of groups we'd matched from before.  We don't clear
00256     // until we match a line in the .txa file, because if we don't
00257     // match any lines we still want to remember what groups we used
00258     // to be assigned to.
00259     _explicitly_assigned_groups.clear();
00260     _first_txa_match = false;
00261   }
00262 
00263   _explicitly_assigned_groups.make_union(_explicitly_assigned_groups, groups);
00264 }
00265 
00266 ////////////////////////////////////////////////////////////////////
00267 //     Function: EggFile::post_txa_file
00268 //       Access: Public
00269 //  Description: Once the egg file has been matched against all of the
00270 //               matching lines the .txa file, do whatever adjustment
00271 //               is necessary.
00272 ////////////////////////////////////////////////////////////////////
00273 void EggFile::
00274 post_txa_file() {
00275 }
00276 
00277 ////////////////////////////////////////////////////////////////////
00278 //     Function: EggFile::get_explicit_groups
00279 //       Access: Public
00280 //  Description: Returns the set of PaletteGroups that the egg file
00281 //               has been explicitly assigned to in the .txa file.
00282 ////////////////////////////////////////////////////////////////////
00283 const PaletteGroups &EggFile::
00284 get_explicit_groups() const {
00285   return _explicitly_assigned_groups;
00286 }
00287 
00288 ////////////////////////////////////////////////////////////////////
00289 //     Function: EggFile::get_default_group
00290 //       Access: Public
00291 //  Description: Returns the PaletteGroup that was specified as the
00292 //               default group on the command line at the time the egg
00293 //               file last appeared on the command line.
00294 ////////////////////////////////////////////////////////////////////
00295 PaletteGroup *EggFile::
00296 get_default_group() const {
00297   return _default_group;
00298 }
00299 
00300 ////////////////////////////////////////////////////////////////////
00301 //     Function: EggFile::get_complete_groups
00302 //       Access: Public
00303 //  Description: Returns the complete set of PaletteGroups that the
00304 //               egg file is assigned to.  This is the set of all the
00305 //               groups it is explicitly assigned to, plus all the
00306 //               groups that these groups depend on.
00307 ////////////////////////////////////////////////////////////////////
00308 const PaletteGroups &EggFile::
00309 get_complete_groups() const {
00310   return _complete_groups;
00311 }
00312 
00313 ////////////////////////////////////////////////////////////////////
00314 //     Function: EggFile::clear_surprise
00315 //       Access: Public
00316 //  Description: Removes the 'surprise' flag; this file has been
00317 //               successfully matched against a line in the .txa file.
00318 ////////////////////////////////////////////////////////////////////
00319 void EggFile::
00320 clear_surprise() {
00321   _is_surprise = false;
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: EggFile::is_surprise
00326 //       Access: Public
00327 //  Description: Returns true if this particular egg file is a
00328 //               'surprise', i.e. it wasn't matched by a line in the
00329 //               .txa file that didn't include the keyword 'cont'.
00330 ////////////////////////////////////////////////////////////////////
00331 bool EggFile::
00332 is_surprise() const {
00333   return _is_surprise;
00334 }
00335 
00336 ////////////////////////////////////////////////////////////////////
00337 //     Function: EggFile::mark_stale
00338 //       Access: Public
00339 //  Description: Marks this particular egg file as stale, meaning that
00340 //               something has changed, such as the location of a
00341 //               texture within its palette, which causes the egg file
00342 //               to need to be regenerated.
00343 ////////////////////////////////////////////////////////////////////
00344 void EggFile::
00345 mark_stale() {
00346   _is_stale = true;
00347 }
00348 
00349 ////////////////////////////////////////////////////////////////////
00350 //     Function: EggFile::is_stale
00351 //       Access: Public
00352 //  Description: Returns true if the egg file needs to be updated,
00353 //               i.e. some palettizations have changed affecting it,
00354 //               or false otherwise.
00355 ////////////////////////////////////////////////////////////////////
00356 bool EggFile::
00357 is_stale() const {
00358   return _is_stale;
00359 }
00360 
00361 ////////////////////////////////////////////////////////////////////
00362 //     Function: EggFile::build_cross_links
00363 //       Access: Public
00364 //  Description: Calls TextureImage::note_egg_file() and
00365 //               SourceTextureImage::increment_egg_count() for each
00366 //               texture the egg file references, and
00367 //               PaletteGroup::increment_egg_count() for each palette
00368 //               group it wants.  This sets up some of the back
00369 //               references to support determining an ideal texture
00370 //               assignment.
00371 ////////////////////////////////////////////////////////////////////
00372 void EggFile::
00373 build_cross_links() {
00374   if (_explicitly_assigned_groups.empty()) {
00375     // If the egg file has been assigned to no groups, we have to
00376     // assign it to something.
00377     _complete_groups.clear();
00378     _complete_groups.insert(_default_group);
00379     _complete_groups.make_complete(_complete_groups);
00380 
00381   } else {
00382     _complete_groups.make_complete(_explicitly_assigned_groups);
00383   }
00384 
00385   Textures::const_iterator ti;
00386   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00387     TextureReference *reference = (*ti);
00388     TextureImage *texture = reference->get_texture();
00389     nassertv(texture != (TextureImage *)NULL);
00390     texture->note_egg_file(this);
00391 
00392     // Actually, this may count the same egg file multiple times for a
00393     // particular SourceTextureImage, since a given texture may be
00394     // referenced multiples times within an egg file.  No harm done,
00395     // however.
00396     reference->get_source()->increment_egg_count();
00397   }
00398 
00399   PaletteGroups::const_iterator gi;
00400   for (gi = _complete_groups.begin();
00401        gi != _complete_groups.end();
00402        ++gi) {
00403     (*gi)->increment_egg_count();
00404   }
00405 }
00406 
00407 ////////////////////////////////////////////////////////////////////
00408 //     Function: EggFile::apply_properties_to_source
00409 //       Access: Public
00410 //  Description: Calls apply_properties_to_source() for each texture
00411 //               reference, updating all the referenced source
00412 //               textures with the complete set of property
00413 //               information from this egg file.
00414 ////////////////////////////////////////////////////////////////////
00415 void EggFile::
00416 apply_properties_to_source() {
00417   Textures::const_iterator ti;
00418   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00419     TextureReference *reference = (*ti);
00420     reference->apply_properties_to_source();
00421   }
00422 }
00423 
00424 ////////////////////////////////////////////////////////////////////
00425 //     Function: EggFile::choose_placements
00426 //       Access: Public
00427 //  Description: Once all the textures have been assigned to groups
00428 //               (but before they may actually be placed), chooses a
00429 //               suitable TexturePlacement for each texture that
00430 //               appears in the egg file.  This will be necessary to
00431 //               do at some point before writing out the egg file
00432 //               anyway, and doing it before the textures are placed
00433 //               allows us to decide what the necessary UV range is
00434 //               for each to-be-placed texture.
00435 ////////////////////////////////////////////////////////////////////
00436 void EggFile::
00437 choose_placements() {
00438   Textures::const_iterator ti;
00439   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00440     TextureReference *reference = (*ti);
00441     TextureImage *texture = reference->get_texture();
00442 
00443     if (reference->get_placement() != (TexturePlacement *)NULL &&
00444         texture->get_groups().count(reference->get_placement()->get_group()) != 0) {
00445       // The egg file is already using a TexturePlacement that is
00446       // suitable.  Don't bother changing it.
00447 
00448     } else {
00449       // We need to select a new TexturePlacement.
00450       PaletteGroups groups;
00451       groups.make_intersection(get_complete_groups(), texture->get_groups());
00452 
00453       // Now groups is the set of groups that the egg file requires,
00454       // which also happen to include the texture.
00455 
00456       if (groups.empty()) {
00457         // It might be empty if the egg file was assigned only to the
00458         // "null" group (since this group is not propagated to the
00459         // textures).  In this case, choose from the wider set of
00460         // groups available to the texture.
00461         groups = texture->get_groups();
00462       }
00463 
00464       if (!groups.empty()) {
00465         // It doesn't really matter which group in the set we choose, so
00466         // we arbitrarily choose the first one.
00467         PaletteGroup *group = (*groups.begin());
00468 
00469         // Now get the TexturePlacement object that corresponds to the
00470         // placement of this texture into this group.
00471         TexturePlacement *placement = texture->get_placement(group);
00472         nassertv(placement != (TexturePlacement *)NULL);
00473 
00474         reference->set_placement(placement);
00475       }
00476     }
00477   }
00478 }
00479 
00480 ////////////////////////////////////////////////////////////////////
00481 //     Function: EggFile::has_data
00482 //       Access: Public
00483 //  Description: Returns true if the EggData for this EggFile has 
00484 //               been loaded, and not yet released.
00485 ////////////////////////////////////////////////////////////////////
00486 bool EggFile::
00487 has_data() const {
00488   return (_data != (EggData *)NULL);
00489 }
00490 
00491 ////////////////////////////////////////////////////////////////////
00492 //     Function: EggFile::had_data
00493 //       Access: Public
00494 //  Description: Returns true if the EggData for this EggFile has ever
00495 //               been loaded in this session.
00496 ////////////////////////////////////////////////////////////////////
00497 bool EggFile::
00498 had_data() const {
00499   return _had_data;
00500 }
00501 
00502 ////////////////////////////////////////////////////////////////////
00503 //     Function: EggFile::update_egg
00504 //       Access: Public
00505 //  Description: Once all textures have been placed appropriately,
00506 //               updates the egg file with all the information to
00507 //               reference the new textures.
00508 ////////////////////////////////////////////////////////////////////
00509 void EggFile::
00510 update_egg() {
00511   nassertv(_data != (EggData *)NULL);
00512 
00513   Textures::iterator ti;
00514   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00515     TextureReference *reference = (*ti);
00516     reference->update_egg();
00517   }
00518 }
00519 
00520 ////////////////////////////////////////////////////////////////////
00521 //     Function: EggFile::remove_egg
00522 //       Access: Public
00523 //  Description: Removes this egg file from all things that reference
00524 //               it, in preparation for removing it from the database.
00525 ////////////////////////////////////////////////////////////////////
00526 void EggFile::
00527 remove_egg() {
00528   Textures::iterator ti;
00529   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00530     TextureReference *reference = (*ti);
00531     TexturePlacement *placement = reference->get_placement();
00532     placement->remove_egg(reference);
00533   }
00534 }
00535 
00536 ////////////////////////////////////////////////////////////////////
00537 //     Function: EggFile::read_egg
00538 //       Access: Public
00539 //  Description: Reads in the egg file from its _source_filename.  It
00540 //               is only valid to call this if it has not already been
00541 //               read in, e.g. from the command line.  Returns true if
00542 //               successful, false if there is an error.
00543 //
00544 //               This may also be called after a previous call to
00545 //               release_egg_data(), in order to re-read the same egg
00546 //               file.
00547 ////////////////////////////////////////////////////////////////////
00548 bool EggFile::
00549 read_egg(bool noabs) {
00550   nassertr(_data == (EggData *)NULL, false);
00551   nassertr(!_source_filename.empty(), false);
00552 
00553   Filename user_source_filename = 
00554     FilenameUnifier::make_user_filename(_source_filename);
00555 
00556   if (!_source_filename.exists()) {
00557     nout << user_source_filename << " does not exist.\n";
00558     return false;
00559   }
00560 
00561   PT(EggData) data = new EggData;
00562   if (!data->read(_source_filename, user_source_filename)) {
00563     // Failure reading.
00564     return false;
00565   }
00566 
00567   if (noabs && data->original_had_absolute_pathnames()) {
00568     nout << _source_filename.get_basename()
00569          << " references textures using absolute pathnames!\n";
00570     return false;
00571   }
00572 
00573   // Extract the set of textures referenced by this egg file.
00574   EggTextureCollection tc;
00575   tc.find_used_textures(data);
00576   
00577   // Make sure each tref name is unique within a given file.
00578   tc.uniquify_trefs();
00579 
00580   // Now build up a list of new TextureReference objects that
00581   // represent the textures actually used and their uv range, etc.
00582   Textures new_textures;
00583 
00584   // We want to search for filenames based on the egg directory, and
00585   // also on our current directory from which we originally loaded the
00586   // egg file.  This is important because it's possible the egg file
00587   // referenced some textures or something relative to that directory.
00588   DSearchPath dir;
00589   dir.append_directory(_source_filename.get_dirname());
00590   dir.append_directory(_current_directory);
00591   data->resolve_filenames(dir);
00592 
00593   // If any relative filenames remain, they are relative to the source
00594   // directory, by convention.
00595   data->force_filenames(_current_directory);
00596 
00597   if (!data->load_externals()) {
00598     // Failure reading an external.
00599     return false;
00600   }
00601 
00602   _data = data;
00603   _had_data = true;
00604   remove_backstage(_data);
00605 
00606   // Insert a comment that shows how we first generated the egg file.
00607   PT(EggNode) comment = new EggComment("", _egg_comment);
00608   _data->insert(_data->begin(), comment);
00609 
00610   if (!_textures.empty()) {
00611     // If we already have textures, assume we're re-reading the file.
00612     rescan_textures();
00613   }
00614 
00615   return true;
00616 }
00617 
00618 ////////////////////////////////////////////////////////////////////
00619 //     Function: EggFile::release_egg_data
00620 //       Access: Public
00621 //  Description: Releases the memory that was loaded by a previous
00622 //               call to read_egg().
00623 ////////////////////////////////////////////////////////////////////
00624 void EggFile::
00625 release_egg_data() {
00626   if (_data != (EggData *)NULL) {
00627     _data = NULL;
00628   }
00629   Textures::iterator ti;
00630   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00631     TextureReference *reference = (*ti);
00632     reference->release_egg_data();
00633   }
00634 }
00635 
00636 ////////////////////////////////////////////////////////////////////
00637 //     Function: EggFile::write_egg
00638 //       Access: Public
00639 //  Description: Writes out the egg file to its _dest_filename.
00640 //               Returns true if successful, false if there is an
00641 //               error.
00642 ////////////////////////////////////////////////////////////////////
00643 bool EggFile::
00644 write_egg() {
00645   nassertr(_data != (EggData *)NULL, false);
00646   nassertr(!_dest_filename.empty(), false);
00647 
00648   _dest_filename.make_dir();
00649   nout << "Writing " << FilenameUnifier::make_user_filename(_dest_filename)
00650        << "\n";
00651   if (!_data->write_egg(_dest_filename)) {
00652     // Some error while writing.  Most unusual.
00653     _is_stale = true;
00654     return false;
00655   }
00656 
00657   _is_stale = false;
00658   return true;
00659 }
00660 
00661 ////////////////////////////////////////////////////////////////////
00662 //     Function: EggFile::write_description
00663 //       Access: Public
00664 //  Description: Writes a one-line description of the egg file and its
00665 //               group assignments to the indicated output stream.
00666 ////////////////////////////////////////////////////////////////////
00667 void EggFile::
00668 write_description(ostream &out, int indent_level) const {
00669   indent(out, indent_level) << get_name() << ": ";
00670   if (_explicitly_assigned_groups.empty()) {
00671     if (_default_group != (PaletteGroup *)NULL) {
00672       out << _default_group->get_name();
00673     }
00674   } else {
00675     out << _explicitly_assigned_groups;
00676   }
00677 
00678   if (is_stale()) {
00679     out << " (needs update)";
00680   }
00681   out << "\n";
00682 }
00683 
00684 ////////////////////////////////////////////////////////////////////
00685 //     Function: EggFile::write_texture_refs
00686 //       Access: Public
00687 //  Description: Writes the list of texture references to the
00688 //               indicated output stream, one per line.
00689 ////////////////////////////////////////////////////////////////////
00690 void EggFile::
00691 write_texture_refs(ostream &out, int indent_level) const {
00692   Textures::const_iterator ti;
00693   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00694     TextureReference *reference = (*ti);
00695     reference->write(out, indent_level);
00696   }
00697 }
00698 
00699 ////////////////////////////////////////////////////////////////////
00700 //     Function: EggFile::remove_backstage
00701 //       Access: Private
00702 //  Description: Recursively walks the egg hierarchy and removes any
00703 //               "backstage" nodes found from the scene graph
00704 //               completely.  These aren't part of the egg scene
00705 //               anyway, and removing them early helps reduce
00706 //               confusion.
00707 ////////////////////////////////////////////////////////////////////
00708 void EggFile::
00709 remove_backstage(EggGroupNode *node) {
00710   EggGroupNode::iterator ci;
00711   ci = node->begin();
00712   while (ci != node->end()) {
00713     EggNode *child = (*ci);
00714     bool remove_child = false;
00715 
00716     if (child->is_of_type(EggGroup::get_class_type())) {
00717       EggGroup *egg_group;
00718       DCAST_INTO_V(egg_group, child);
00719       remove_child = egg_group->has_object_type("backstage");
00720     }
00721 
00722     if (remove_child) {
00723       ci = node->erase(ci);
00724     } else {
00725       if (child->is_of_type(EggGroupNode::get_class_type())) {
00726         // Recurse on children.
00727         remove_backstage(DCAST(EggGroupNode, child));
00728       }
00729       ++ci;
00730     }
00731   }
00732 }
00733 
00734 ////////////////////////////////////////////////////////////////////
00735 //     Function: EggFile::rescan_textures
00736 //       Access: Private
00737 //  Description: After reloading the egg file for the second time in a
00738 //               given session, rematches the texture pointers with
00739 //               the TextureReference objects.
00740 ////////////////////////////////////////////////////////////////////
00741 void EggFile::
00742 rescan_textures() {
00743   nassertv(_data != (EggData *)NULL);
00744 
00745   // Extract the set of textures referenced by this egg file.
00746   EggTextureCollection tc;
00747   tc.find_used_textures(_data);
00748   
00749   // Make sure each tref name is unique within a given file.
00750   tc.uniquify_trefs();
00751 
00752   typedef pmap<string, TextureReference *> ByTRefName;
00753   ByTRefName by_tref_name;
00754   for (Textures::const_iterator ti = _textures.begin();
00755        ti != _textures.end();
00756        ++ti) {
00757     TextureReference *ref = (*ti);
00758     by_tref_name[ref->get_tref_name()] = ref;
00759   }
00760 
00761   EggTextureCollection::iterator eti;
00762   for (eti = tc.begin(); eti != tc.end(); ++eti) {
00763     EggTexture *egg_tex = (*eti);
00764 
00765     ByTRefName::const_iterator tni = by_tref_name.find(egg_tex->get_name());
00766     if (tni == by_tref_name.end()) {
00767       // We didn't find this TRef name last time around!
00768       nout << _source_filename.get_basename()
00769            << " modified during session--TRef " << egg_tex->get_name() 
00770            << " is new!\n";
00771 
00772     } else {
00773       TextureReference *ref = (*tni).second;
00774       ref->rebind_egg_data(_data, egg_tex);
00775     }
00776   }
00777 }
00778 
00779 ////////////////////////////////////////////////////////////////////
00780 //     Function: EggFile::register_with_read_factory
00781 //       Access: Public, Static
00782 //  Description: Registers the current object as something that can be
00783 //               read from a Bam file.
00784 ////////////////////////////////////////////////////////////////////
00785 void EggFile::
00786 register_with_read_factory() {
00787   BamReader::get_factory()->
00788     register_factory(get_class_type(), make_EggFile);
00789 }
00790 
00791 ////////////////////////////////////////////////////////////////////
00792 //     Function: EggFile::write_datagram
00793 //       Access: Public, Virtual
00794 //  Description: Fills the indicated datagram up with a binary
00795 //               representation of the current object, in preparation
00796 //               for writing to a Bam file.
00797 ////////////////////////////////////////////////////////////////////
00798 void EggFile::
00799 write_datagram(BamWriter *writer, Datagram &datagram) {
00800   TypedWritable::write_datagram(writer, datagram);
00801   datagram.add_string(get_name());
00802 
00803   // We don't write out _data; that needs to be reread each session.
00804 
00805   datagram.add_string(FilenameUnifier::make_bam_filename(_current_directory));
00806   datagram.add_string(FilenameUnifier::make_bam_filename(_source_filename));
00807   datagram.add_string(FilenameUnifier::make_bam_filename(_dest_filename));
00808   datagram.add_string(_egg_comment);
00809 
00810   datagram.add_uint32(_textures.size());
00811   Textures::iterator ti;
00812   for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00813     writer->write_pointer(datagram, (*ti));
00814   }
00815 
00816   _explicitly_assigned_groups.write_datagram(writer, datagram);
00817   writer->write_pointer(datagram, _default_group);
00818 
00819   // We don't write out _complete_groups; that is recomputed each
00820   // session.
00821 
00822   datagram.add_bool(_is_surprise);
00823   datagram.add_bool(_is_stale);
00824 }
00825 
00826 ////////////////////////////////////////////////////////////////////
00827 //     Function: EggFile::complete_pointers
00828 //       Access: Public, Virtual
00829 //  Description: Called after the object is otherwise completely read
00830 //               from a Bam file, this function's job is to store the
00831 //               pointers that were retrieved from the Bam file for
00832 //               each pointer object written.  The return value is the
00833 //               number of pointers processed from the list.
00834 ////////////////////////////////////////////////////////////////////
00835 int EggFile::
00836 complete_pointers(TypedWritable **p_list, BamReader *manager) {
00837   int pi = TypedWritable::complete_pointers(p_list, manager);
00838 
00839   int i;
00840   _textures.reserve(_num_textures);
00841   for (i = 0; i < _num_textures; i++) {
00842     TextureReference *texture;
00843     DCAST_INTO_R(texture, p_list[pi], pi);
00844     _textures.push_back(texture);
00845     pi++;
00846   }
00847 
00848   pi += _explicitly_assigned_groups.complete_pointers(p_list + pi, manager);
00849 
00850   if (p_list[pi] != (TypedWritable *)NULL) {
00851     DCAST_INTO_R(_default_group, p_list[pi], pi);
00852   }
00853   pi++;
00854 
00855   return pi;
00856 }
00857 
00858 ////////////////////////////////////////////////////////////////////
00859 //     Function: EggFile::make_EggFile
00860 //       Access: Protected
00861 //  Description: This method is called by the BamReader when an object
00862 //               of this type is encountered in a Bam file; it should
00863 //               allocate and return a new object with all the data
00864 //               read.
00865 ////////////////////////////////////////////////////////////////////
00866 TypedWritable* EggFile::
00867 make_EggFile(const FactoryParams &params) {
00868   EggFile *me = new EggFile();
00869   DatagramIterator scan;
00870   BamReader *manager;
00871 
00872   parse_params(params, scan, manager);
00873   me->fillin(scan, manager);
00874   return me;
00875 }
00876 
00877 ////////////////////////////////////////////////////////////////////
00878 //     Function: EggFile::fillin
00879 //       Access: Protected
00880 //  Description: Reads the binary data from the given datagram
00881 //               iterator, which was written by a previous call to
00882 //               write_datagram().
00883 ////////////////////////////////////////////////////////////////////
00884 void EggFile::
00885 fillin(DatagramIterator &scan, BamReader *manager) {
00886   TypedWritable::fillin(scan, manager);
00887   set_name(scan.get_string());
00888   _current_directory = FilenameUnifier::get_bam_filename(scan.get_string());
00889   _source_filename = FilenameUnifier::get_bam_filename(scan.get_string());
00890   _dest_filename = FilenameUnifier::get_bam_filename(scan.get_string());
00891   if (Palettizer::_read_pi_version >= 9) {
00892     _egg_comment = scan.get_string();
00893   }
00894 
00895   _num_textures = scan.get_uint32();
00896   manager->read_pointers(scan, _num_textures);
00897 
00898   _explicitly_assigned_groups.fillin(scan, manager);
00899   manager->read_pointer(scan);  // _default_group
00900 
00901   _is_surprise = scan.get_bool();
00902   _is_stale = scan.get_bool();
00903 
00904   if (Palettizer::_read_pi_version < 11) {
00905     // If this file was written by a version of egg-palettize prior to
00906     // 11, we didn't store the tref names on the texture references.
00907     // Since we need that information now, it follows that every egg
00908     // file is stale.
00909     _is_stale = true;
00910   }
00911 }
 All Classes Functions Variables Enumerations