Panda3D
|
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 ¶ms) { 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 }