Panda3D
 All Classes Functions Variables Enumerations
eggTextureCollection.cxx
00001 // Filename: eggTextureCollection.cxx
00002 // Created by:  drose (15Feb00)
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 "eggTextureCollection.h"
00016 #include "eggGroupNode.h"
00017 #include "eggPrimitive.h"
00018 #include "eggTexture.h"
00019 #include "pt_EggTexture.h"
00020 #include "dcast.h"
00021 
00022 #include "nameUniquifier.h"
00023 
00024 #include <algorithm>
00025 
00026 ////////////////////////////////////////////////////////////////////
00027 //     Function: EggTextureCollection::Constructor
00028 //       Access: Public
00029 //  Description:
00030 ////////////////////////////////////////////////////////////////////
00031 EggTextureCollection::
00032 EggTextureCollection() {
00033 }
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: EggTextureCollection::Copy Constructor
00037 //       Access: Public
00038 //  Description:
00039 ////////////////////////////////////////////////////////////////////
00040 EggTextureCollection::
00041 EggTextureCollection(const EggTextureCollection &copy) :
00042   _textures(copy._textures),
00043   _ordered_textures(copy._ordered_textures)
00044 {
00045 }
00046 
00047 ////////////////////////////////////////////////////////////////////
00048 //     Function: EggTextureCollection::Copy Assignment Operator
00049 //       Access: Public
00050 //  Description:
00051 ////////////////////////////////////////////////////////////////////
00052 EggTextureCollection &EggTextureCollection::
00053 operator = (const EggTextureCollection &copy) {
00054   _textures = copy._textures;
00055   _ordered_textures = copy._ordered_textures;
00056   return *this;
00057 }
00058 
00059 ////////////////////////////////////////////////////////////////////
00060 //     Function: EggTextureCollection::Destructor
00061 //       Access: Public
00062 //  Description:
00063 ////////////////////////////////////////////////////////////////////
00064 EggTextureCollection::
00065 ~EggTextureCollection() {
00066 }
00067 
00068 ////////////////////////////////////////////////////////////////////
00069 //     Function: EggTextureCollection::clear
00070 //       Access: Public
00071 //  Description: Removes all textures from the collection.
00072 ////////////////////////////////////////////////////////////////////
00073 void EggTextureCollection::
00074 clear() {
00075   _textures.clear();
00076   _ordered_textures.clear();
00077 }
00078 
00079 ////////////////////////////////////////////////////////////////////
00080 //     Function: EggTextureCollection::extract_textures
00081 //       Access: Public
00082 //  Description: Walks the egg hierarchy beginning at the indicated
00083 //               node, and removes any EggTextures encountered in the
00084 //               hierarchy, adding them to the collection.  Returns
00085 //               the number of EggTextures encountered.
00086 ////////////////////////////////////////////////////////////////////
00087 int EggTextureCollection::
00088 extract_textures(EggGroupNode *node) {
00089   // Since this traversal is destructive, we'll handle it within the
00090   // EggGroupNode code.
00091   return node->find_textures(this);
00092 }
00093 
00094 
00095 ////////////////////////////////////////////////////////////////////
00096 //     Function: EggTextureCollection::is_empty
00097 //       Access: Published
00098 //  Description: Returns true if there are no EggTexures in the
00099 //               collection, false otherwise.
00100 ////////////////////////////////////////////////////////////////////
00101 bool EggTextureCollection::
00102 is_empty() const {
00103   return _ordered_textures.empty();
00104 }
00105 
00106 ////////////////////////////////////////////////////////////////////
00107 //     Function: EggTextureCollection::get_num_textures
00108 //       Access: Published
00109 //  Description: Returns the number of EggTextures in the collection.
00110 ////////////////////////////////////////////////////////////////////
00111 int EggTextureCollection::
00112 get_num_textures() const {
00113   return _ordered_textures.size();
00114 }
00115 
00116 ////////////////////////////////////////////////////////////////////
00117 //     Function: EggTextureCollection::get_texture
00118 //       Access: Published
00119 //  Description: Returns the nth EggTexture in the collection.
00120 ////////////////////////////////////////////////////////////////////
00121 EggTexture *EggTextureCollection::
00122 get_texture(int index) const {
00123   nassertr(index >= 0 && index < (int)_ordered_textures.size(), NULL);
00124 
00125   return _ordered_textures[index];
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: EggTextureCollection::insert_textures
00130 //       Access: Public
00131 //  Description: Adds a series of EggTexture nodes to the beginning of
00132 //               the indicated node to reflect each of the textures in
00133 //               the collection.  Returns an iterator representing the
00134 //               first position after the newly inserted textures.
00135 ////////////////////////////////////////////////////////////////////
00136 EggGroupNode::iterator EggTextureCollection::
00137 insert_textures(EggGroupNode *node) {
00138   return insert_textures(node, node->begin());
00139 }
00140 
00141 ////////////////////////////////////////////////////////////////////
00142 //     Function: EggTextureCollection::insert_textures
00143 //       Access: Public
00144 //  Description: Adds a series of EggTexture nodes to the beginning of
00145 //               the indicated node to reflect each of the textures in
00146 //               the collection.  Returns an iterator representing the
00147 //               first position after the newly inserted textures.
00148 ////////////////////////////////////////////////////////////////////
00149 EggGroupNode::iterator EggTextureCollection::
00150 insert_textures(EggGroupNode *node, EggGroupNode::iterator position) {
00151   OrderedTextures::iterator oti;
00152   for (oti = _ordered_textures.begin();
00153        oti != _ordered_textures.end();
00154        ++oti) {
00155     EggTexture *texture = (*oti);
00156     position = node->insert(position, texture);
00157   }
00158 
00159   return position;
00160 }
00161 
00162 ////////////////////////////////////////////////////////////////////
00163 //     Function: EggTextureCollection::find_used_textures
00164 //       Access: Public
00165 //  Description: Walks the egg hierarchy beginning at the indicated
00166 //               node, looking for textures that are referenced by
00167 //               primitives but are not already members of the
00168 //               collection, adding them to the collection.
00169 //
00170 //               If this is called following extract_textures(), it
00171 //               can be used to pick up any additional texture
00172 //               references that appeared in the egg hierarchy (but
00173 //               whose EggTexture node was not actually part of the
00174 //               hierarchy).
00175 //
00176 //               If this is called in lieu of extract_textures(), it
00177 //               will fill up the collection with all of the
00178 //               referenced textures (and only the referenced
00179 //               textures), without destructively removing the
00180 //               EggTextures from the hierarchy.
00181 //
00182 //               This also has the side effect of incrementing the
00183 //               internal usage count for a texture in the collection
00184 //               each time a texture reference is encountered.  This
00185 //               side effect is taken advantage of by
00186 //               remove_unused_textures().
00187 //
00188 //               And one more side effect: this function identifies
00189 //               the presence of multitexturing in the egg file, and
00190 //               calls multitexture_over() on each texture
00191 //               appropriately so that, after this call, you may
00192 //               expect get_multitexture_sort() to return a reasonable
00193 //               value for each texture.
00194 ////////////////////////////////////////////////////////////////////
00195 int EggTextureCollection::
00196 find_used_textures(EggNode *node) {
00197   int num_found = 0;
00198 
00199   if (node->is_of_type(EggPrimitive::get_class_type())) {
00200     EggPrimitive *primitive = DCAST(EggPrimitive, node);
00201 
00202     int num_textures = primitive->get_num_textures();
00203     for (int i = 0; i < num_textures; i++) {
00204       EggTexture *tex = primitive->get_texture(i);
00205 
00206       Textures::iterator ti = _textures.find(tex);
00207       if (ti == _textures.end()) {
00208         // Here's a new texture!
00209         num_found++;
00210         _textures.insert(Textures::value_type(tex, 1));
00211         _ordered_textures.push_back(tex);
00212       } else {
00213         // Here's a texture we'd already known about.  Increment its
00214         // usage count.
00215         (*ti).second++;
00216       }
00217 
00218       // Get the multitexture ordering right.
00219       for (int j = 0; j < i; j++) {
00220         // The return value of this function will be false if there is
00221         // some cycle in the texture layout order; e.g. A layers over
00222         // B on one primitive, but B layers over A on another
00223         // primitive.  In that case the Egg Loader won't be able to
00224         // assign a unique ordering between A and B, so it's probably
00225         // an error worth reporting to the user--but we don't report
00226         // it here, because this is a much lower-level function that
00227         // gets called in other contexts too.  That means it doesn't
00228         // get reported at all, but too bad.
00229         tex->multitexture_over(primitive->get_texture(j));
00230       }
00231     }
00232 
00233   } else if (node->is_of_type(EggGroupNode::get_class_type())) {
00234     EggGroupNode *group = DCAST(EggGroupNode, node);
00235 
00236     EggGroupNode::iterator ci;
00237     for (ci = group->begin(); ci != group->end(); ++ci) {
00238       EggNode *child = *ci;
00239 
00240       num_found += find_used_textures(child);
00241     }
00242   }
00243 
00244   return num_found;
00245 }
00246 
00247 ////////////////////////////////////////////////////////////////////
00248 //     Function: EggTextureCollection::remove_unused_textures
00249 //       Access: Public
00250 //  Description: Removes any textures from the collection that aren't
00251 //               referenced by any primitives in the indicated egg
00252 //               hierarchy.  This also, incidentally, adds textures to
00253 //               the collection that had been referenced by primitives
00254 //               but had not previously appeared in the collection.
00255 ////////////////////////////////////////////////////////////////////
00256 void EggTextureCollection::
00257 remove_unused_textures(EggNode *node) {
00258   // We'll do this the easy way: First, we'll remove *all* the
00259   // textures from the collection, and then we'll add back only those
00260   // that appear in the hierarchy.
00261   clear();
00262   find_used_textures(node);
00263 }
00264 
00265 ////////////////////////////////////////////////////////////////////
00266 //     Function: EggTextureCollection::collapse_equivalent_textures
00267 //       Access: Public
00268 //  Description: Walks through the collection and collapses together
00269 //               any separate textures that are equivalent according
00270 //               to the indicated equivalence factor, eq (see
00271 //               EggTexture::is_equivalent_to()).  The return value is
00272 //               the number of textures removed.
00273 //
00274 //               This flavor of collapse_equivalent_textures()
00275 //               automatically adjusts all the primitives in the egg
00276 //               hierarchy to refer to the new texture pointers.
00277 ////////////////////////////////////////////////////////////////////
00278 int EggTextureCollection::
00279 collapse_equivalent_textures(int eq, EggGroupNode *node) {
00280   TextureReplacement removed;
00281   int num_collapsed = collapse_equivalent_textures(eq, removed);
00282 
00283   // And now walk the egg hierarchy and replace any references to a
00284   // removed texture with its replacement.
00285   replace_textures(node, removed);
00286 
00287   return num_collapsed;
00288 }
00289 
00290 ////////////////////////////////////////////////////////////////////
00291 //     Function: EggTextureCollection::collapse_equivalent_textures
00292 //       Access: Public
00293 //  Description: Walks through the collection and collapses together
00294 //               any separate textures that are equivalent according
00295 //               to the indicated equivalence factor, eq (see
00296 //               EggTexture::is_equivalent_to()).  The return value is
00297 //               the number of textures removed.
00298 //
00299 //               This flavor of collapse_equivalent_textures() does
00300 //               not adjust any primitives in the egg hierarchy;
00301 //               instead, it fills up the 'removed' map with an entry
00302 //               for each removed texture, mapping it back to the
00303 //               equivalent retained texture.  It's up to the user to
00304 //               then call replace_textures() with this map, if
00305 //               desired, to apply these changes to the egg hierarchy.
00306 ////////////////////////////////////////////////////////////////////
00307 int EggTextureCollection::
00308 collapse_equivalent_textures(int eq, EggTextureCollection::TextureReplacement &removed) {
00309   int num_collapsed = 0;
00310 
00311   typedef pset<PT_EggTexture, UniqueEggTextures> Collapser;
00312   UniqueEggTextures uet(eq);
00313   Collapser collapser(uet);
00314 
00315   // First, put all of the textures into the Collapser structure, to
00316   // find out the unique textures.
00317   OrderedTextures::const_iterator oti;
00318   for (oti = _ordered_textures.begin();
00319        oti != _ordered_textures.end();
00320        ++oti) {
00321     EggTexture *tex = (*oti);
00322 
00323     pair<Collapser::const_iterator, bool> result = collapser.insert(tex);
00324     if (!result.second) {
00325       // This texture is non-unique; another one was already there.
00326       EggTexture *first = *(result.first);
00327       removed.insert(TextureReplacement::value_type(tex, first));
00328       num_collapsed++;
00329     }
00330   }
00331 
00332   // Now record all of the unique textures only.
00333   clear();
00334   Collapser::const_iterator ci;
00335   for (ci = collapser.begin(); ci != collapser.end(); ++ci) {
00336     add_texture(*ci);
00337   }
00338 
00339   return num_collapsed;
00340 }
00341 
00342 ////////////////////////////////////////////////////////////////////
00343 //     Function: EggTextureCollection::replace_textures
00344 //       Access: Public, Static
00345 //  Description: Walks the egg hierarchy, changing out any reference
00346 //               to a texture appearing on the left side of the map
00347 //               with its corresponding texture on the right side.
00348 //               This is most often done following a call to
00349 //               collapse_equivalent_textures().  It does not directly
00350 //               affect the Collection.
00351 ////////////////////////////////////////////////////////////////////
00352 void EggTextureCollection::
00353 replace_textures(EggGroupNode *node,
00354                  const EggTextureCollection::TextureReplacement &replace) {
00355   EggGroupNode::iterator ci;
00356   for (ci = node->begin();
00357        ci != node->end();
00358        ++ci) {
00359     EggNode *child = *ci;
00360     if (child->is_of_type(EggPrimitive::get_class_type())) {
00361       EggPrimitive *primitive = DCAST(EggPrimitive, child);
00362       EggPrimitive::Textures new_textures;
00363       EggPrimitive::Textures::const_iterator ti;
00364       for (ti = primitive->_textures.begin(); 
00365            ti != primitive->_textures.end();
00366            ++ti) {
00367         PT_EggTexture tex = (*ti);
00368         TextureReplacement::const_iterator ri;
00369         ri = replace.find(tex);
00370         if (ri != replace.end()) {
00371           // Here's a texture we want to replace.
00372           new_textures.push_back((*ri).second);
00373         } else {
00374           new_textures.push_back(tex);
00375         }
00376       }
00377       primitive->_textures.swap(new_textures);
00378 
00379     } else if (child->is_of_type(EggGroupNode::get_class_type())) {
00380       EggGroupNode *group_child = DCAST(EggGroupNode, child);
00381       replace_textures(group_child, replace);
00382     }
00383   }
00384 }
00385 
00386 ////////////////////////////////////////////////////////////////////
00387 //     Function: EggTextureCollection::uniquify_trefs
00388 //       Access: Public
00389 //  Description: Guarantees that each texture in the collection has a
00390 //               unique TRef name.  This is essential before writing
00391 //               an egg file.
00392 ////////////////////////////////////////////////////////////////////
00393 void EggTextureCollection::
00394 uniquify_trefs() {
00395   NameUniquifier nu(".tref", "tref");
00396 
00397   OrderedTextures::const_iterator oti;
00398   for (oti = _ordered_textures.begin();
00399        oti != _ordered_textures.end();
00400        ++oti) {
00401     EggTexture *tex = (*oti);
00402 
00403     tex->set_name(nu.add_name(tex->get_name()));
00404   }
00405 }
00406 
00407 ////////////////////////////////////////////////////////////////////
00408 //     Function: EggTextureCollection::sort_by_tref
00409 //       Access: Public
00410 //  Description: Sorts all the textures into alphabetical order by
00411 //               TRef name.  Subsequent operations using begin()/end()
00412 //               will traverse in this sorted order.
00413 ////////////////////////////////////////////////////////////////////
00414 void EggTextureCollection::
00415 sort_by_tref() {
00416   sort(_ordered_textures.begin(), _ordered_textures.end(),
00417        NamableOrderByName());
00418 }
00419 
00420 ////////////////////////////////////////////////////////////////////
00421 //     Function: EggTextureCollection::sort_by_basename
00422 //       Access: Public
00423 //  Description: Sorts all the textures into alphabetical order by
00424 //               the basename part (including extension) of the
00425 //               filename.  Subsequent operations using begin()/end()
00426 //               will traverse in this sorted order.
00427 ////////////////////////////////////////////////////////////////////
00428 void EggTextureCollection::
00429 sort_by_basename() {
00430   sort(_ordered_textures.begin(), _ordered_textures.end(),
00431        EggFilenameNode::IndirectOrderByBasename());
00432 }
00433 
00434 ////////////////////////////////////////////////////////////////////
00435 //     Function: EggTextureCollection::add_texture
00436 //       Access: Public
00437 //  Description: Explicitly adds a new texture to the collection.
00438 //               Returns true if the texture was added, false if it
00439 //               was already there or if there was some error.
00440 ////////////////////////////////////////////////////////////////////
00441 bool EggTextureCollection::
00442 add_texture(EggTexture *texture) {
00443   nassertr(_textures.size() == _ordered_textures.size(), false);
00444 
00445   PT_EggTexture new_tex = texture;
00446 
00447   Textures::const_iterator ti;
00448   ti = _textures.find(new_tex);
00449   if (ti != _textures.end()) {
00450     // This texture is already a member of the collection.
00451     return false;
00452   }
00453 
00454   _textures.insert(Textures::value_type(new_tex, 0));
00455   _ordered_textures.push_back(new_tex);
00456 
00457   nassertr(_textures.size() == _ordered_textures.size(), false);
00458   return true;
00459 }
00460 
00461 ////////////////////////////////////////////////////////////////////
00462 //     Function: EggTextureCollection::remove_texture
00463 //       Access: Public
00464 //  Description: Explicitly removes a texture from the collection.
00465 //               Returns true if the texture was removed, false if it
00466 //               wasn't there or if there was some error.
00467 ////////////////////////////////////////////////////////////////////
00468 bool EggTextureCollection::
00469 remove_texture(EggTexture *texture) {
00470   nassertr(_textures.size() == _ordered_textures.size(), false);
00471 
00472   Textures::iterator ti;
00473   ti = _textures.find(texture);
00474   if (ti == _textures.end()) {
00475     // This texture is not a member of the collection.
00476     return false;
00477   }
00478 
00479   _textures.erase(ti);
00480 
00481   OrderedTextures::iterator oti;
00482   PT_EggTexture ptex = texture;
00483   oti = find(_ordered_textures.begin(), _ordered_textures.end(), ptex);
00484   nassertr(oti != _ordered_textures.end(), false);
00485 
00486   _ordered_textures.erase(oti);
00487 
00488   nassertr(_textures.size() == _ordered_textures.size(), false);
00489   return true;
00490 }
00491 
00492 ////////////////////////////////////////////////////////////////////
00493 //     Function: EggTextureCollection::create_unique_texture
00494 //       Access: Public
00495 //  Description: Creates a new texture if there is not already one
00496 //               equivalent (according to eq, see
00497 //               EggTexture::is_equivalent_to()) to the indicated
00498 //               texture, or returns the existing one if there is.
00499 ////////////////////////////////////////////////////////////////////
00500 EggTexture *EggTextureCollection::
00501 create_unique_texture(const EggTexture &copy, int eq) {
00502   // This requires a complete linear traversal, not terribly
00503   // efficient.
00504   OrderedTextures::const_iterator oti;
00505   for (oti = _ordered_textures.begin();
00506        oti != _ordered_textures.end();
00507        ++oti) {
00508     EggTexture *tex = (*oti);
00509     if (copy.is_equivalent_to(*tex, eq)) {
00510       //cout << "tex:" << tex->get_name() << "---copy:" << copy.get_name() << endl;
00511       return tex;
00512     }
00513   }
00514   //cout << "adding a texture to collection: " << copy.get_name() << endl;
00515   EggTexture *new_texture = new EggTexture(copy);
00516   add_texture(new_texture);
00517   return new_texture;
00518 }
00519 
00520 ////////////////////////////////////////////////////////////////////
00521 //     Function: EggTextureCollection::find_tref
00522 //       Access: Public
00523 //  Description: Returns the texture with the indicated TRef name, or
00524 //               NULL if no texture matches.
00525 ////////////////////////////////////////////////////////////////////
00526 EggTexture *EggTextureCollection::
00527 find_tref(const string &tref_name) const {
00528   // This requires a complete linear traversal, not terribly
00529   // efficient.
00530   OrderedTextures::const_iterator oti;
00531   for (oti = _ordered_textures.begin();
00532        oti != _ordered_textures.end();
00533        ++oti) {
00534     EggTexture *tex = (*oti);
00535     if (tex->get_name() == tref_name) {
00536       return tex;
00537     }
00538   }
00539 
00540   return (EggTexture *)NULL;
00541 }
00542 
00543 ////////////////////////////////////////////////////////////////////
00544 //     Function: EggTextureCollection::find_filename
00545 //       Access: Public
00546 //  Description: Returns the texture with the indicated filename, or
00547 //               NULL if no texture matches.
00548 ////////////////////////////////////////////////////////////////////
00549 EggTexture *EggTextureCollection::
00550 find_filename(const Filename &filename) const {
00551   // This requires a complete linear traversal, not terribly
00552   // efficient.
00553   OrderedTextures::const_iterator oti;
00554   for (oti = _ordered_textures.begin();
00555        oti != _ordered_textures.end();
00556        ++oti) {
00557     EggTexture *tex = (*oti);
00558     if (tex->get_filename() == filename) {
00559       return tex;
00560     }
00561   }
00562 
00563   return (EggTexture *)NULL;
00564 }
 All Classes Functions Variables Enumerations