00001 // Filename: eggTextureCollection.cxx 00002 // Created by: drose (15Feb00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved 00008 // 00009 // All use of this software is subject to the terms of the Panda 3d 00010 // Software license. You should have received a copy of this license 00011 // along with this source code; you will also find a current copy of 00012 // the license at http://www.panda3d.org/license.txt . 00013 // 00014 // To contact the maintainers of this program write to 00015 // panda3d@yahoogroups.com . 00016 // 00017 //////////////////////////////////////////////////////////////////// 00018 00019 #include "eggTextureCollection.h" 00020 #include "eggGroupNode.h" 00021 #include "eggPrimitive.h" 00022 #include "eggTexture.h" 00023 00024 #include <nameUniquifier.h> 00025 00026 #include <algorithm> 00027 00028 //////////////////////////////////////////////////////////////////// 00029 // Function: EggTextureCollection::Constructor 00030 // Access: Public 00031 // Description: 00032 //////////////////////////////////////////////////////////////////// 00033 EggTextureCollection:: 00034 EggTextureCollection() { 00035 } 00036 00037 //////////////////////////////////////////////////////////////////// 00038 // Function: EggTextureCollection::Copy Constructor 00039 // Access: Public 00040 // Description: 00041 //////////////////////////////////////////////////////////////////// 00042 EggTextureCollection:: 00043 EggTextureCollection(const EggTextureCollection ©) : 00044 _textures(copy._textures), 00045 _ordered_textures(copy._ordered_textures) 00046 { 00047 } 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: EggTextureCollection::Copy Assignment Operator 00051 // Access: Public 00052 // Description: 00053 //////////////////////////////////////////////////////////////////// 00054 EggTextureCollection &EggTextureCollection:: 00055 operator = (const EggTextureCollection ©) { 00056 _textures = copy._textures; 00057 _ordered_textures = copy._ordered_textures; 00058 return *this; 00059 } 00060 00061 //////////////////////////////////////////////////////////////////// 00062 // Function: EggTextureCollection::Destructor 00063 // Access: Public 00064 // Description: 00065 //////////////////////////////////////////////////////////////////// 00066 EggTextureCollection:: 00067 ~EggTextureCollection() { 00068 } 00069 00070 //////////////////////////////////////////////////////////////////// 00071 // Function: EggTextureCollection::clear 00072 // Access: Public 00073 // Description: Removes all textures from the collection. 00074 //////////////////////////////////////////////////////////////////// 00075 void EggTextureCollection:: 00076 clear() { 00077 _textures.clear(); 00078 _ordered_textures.clear(); 00079 } 00080 00081 //////////////////////////////////////////////////////////////////// 00082 // Function: EggTextureCollection::extract_textures 00083 // Access: Public 00084 // Description: Walks the egg hierarchy beginning at the indicated 00085 // node, and removes any EggTextures encountered in the 00086 // hierarchy, adding them to the collection. Returns 00087 // the number of EggTextures encountered. 00088 //////////////////////////////////////////////////////////////////// 00089 int EggTextureCollection:: 00090 extract_textures(EggGroupNode *node) { 00091 // Since this traversal is destructive, we'll handle it within the 00092 // EggGroupNode code. 00093 return node->find_textures(this); 00094 } 00095 00096 //////////////////////////////////////////////////////////////////// 00097 // Function: EggTextureCollection::insert_textures 00098 // Access: Public 00099 // Description: Adds a series of EggTexture nodes to the beginning of 00100 // the indicated node to reflect each of the textures in 00101 // the collection. Returns an iterator representing the 00102 // first position after the newly inserted textures. 00103 //////////////////////////////////////////////////////////////////// 00104 EggGroupNode::iterator EggTextureCollection:: 00105 insert_textures(EggGroupNode *node) { 00106 return insert_textures(node, node->begin()); 00107 } 00108 00109 //////////////////////////////////////////////////////////////////// 00110 // Function: EggTextureCollection::insert_textures 00111 // Access: Public 00112 // Description: Adds a series of EggTexture nodes to the beginning of 00113 // the indicated node to reflect each of the textures in 00114 // the collection. Returns an iterator representing the 00115 // first position after the newly inserted textures. 00116 //////////////////////////////////////////////////////////////////// 00117 EggGroupNode::iterator EggTextureCollection:: 00118 insert_textures(EggGroupNode *node, EggGroupNode::iterator position) { 00119 OrderedTextures::iterator oti; 00120 for (oti = _ordered_textures.begin(); 00121 oti != _ordered_textures.end(); 00122 ++oti) { 00123 EggTexture *texture = (*oti); 00124 position = node->insert(position, texture); 00125 } 00126 00127 return position; 00128 } 00129 00130 //////////////////////////////////////////////////////////////////// 00131 // Function: EggTextureCollection::find_used_textures 00132 // Access: Public 00133 // Description: Walks the egg hierarchy beginning at the indicated 00134 // node, looking for textures that are referenced by 00135 // primitives but are not already members of the 00136 // collection, adding them to the collection. 00137 // 00138 // If this is called following extract_textures(), it 00139 // can be used to pick up any additional texture 00140 // references that appeared in the egg hierarchy (but 00141 // whose EggTexture node was not actually part of the 00142 // hierarchy). 00143 // 00144 // If this is called in lieu of extract_textures(), it 00145 // will fill up the collection with all of the 00146 // referenced textures (and only the referenced 00147 // textures), without destructively removing the 00148 // EggTextures from the hierarchy. 00149 // 00150 // This also has the side effect of incrementing the 00151 // internal usage count for a texture in the collection 00152 // each time a texture reference is encountered. This 00153 // side effect is taken advantage of by 00154 // remove_unused_textures(). 00155 //////////////////////////////////////////////////////////////////// 00156 int EggTextureCollection:: 00157 find_used_textures(EggNode *node) { 00158 int num_found = 0; 00159 00160 if (node->is_of_type(EggPrimitive::get_class_type())) { 00161 EggPrimitive *primitive = DCAST(EggPrimitive, node); 00162 if (primitive->has_texture()) { 00163 EggTexture *tex = primitive->get_texture(); 00164 Textures::iterator ti = _textures.find(tex); 00165 if (ti == _textures.end()) { 00166 // Here's a new texture! 00167 num_found++; 00168 _textures.insert(Textures::value_type(tex, 1)); 00169 _ordered_textures.push_back(tex); 00170 } else { 00171 // Here's a texture we'd already known about. Increment its 00172 // usage count. 00173 (*ti).second++; 00174 } 00175 } 00176 00177 } else if (node->is_of_type(EggGroupNode::get_class_type())) { 00178 EggGroupNode *group = DCAST(EggGroupNode, node); 00179 00180 EggGroupNode::iterator ci; 00181 for (ci = group->begin(); ci != group->end(); ++ci) { 00182 EggNode *child = *ci; 00183 00184 num_found += find_used_textures(child); 00185 } 00186 } 00187 00188 return num_found; 00189 } 00190 00191 //////////////////////////////////////////////////////////////////// 00192 // Function: EggTextureCollection::remove_unused_textures 00193 // Access: Public 00194 // Description: Removes any textures from the collection that aren't 00195 // referenced by any primitives in the indicated egg 00196 // hierarchy. This also, incidentally, adds textures to 00197 // the collection that had been referenced by primitives 00198 // but had not previously appeared in the collection. 00199 //////////////////////////////////////////////////////////////////// 00200 void EggTextureCollection:: 00201 remove_unused_textures(EggNode *node) { 00202 // We'll do this the easy way: First, we'll remove *all* the 00203 // textures from the collection, and then we'll add back only those 00204 // that appear in the hierarchy. 00205 clear(); 00206 find_used_textures(node); 00207 } 00208 00209 //////////////////////////////////////////////////////////////////// 00210 // Function: EggTextureCollection::collapse_equivalent_textures 00211 // Access: Public 00212 // Description: Walks through the collection and collapses together 00213 // any separate textures that are equivalent according 00214 // to the indicated equivalence factor, eq (see 00215 // EggTexture::is_equivalent_to()). The return value is 00216 // the number of textures removed. 00217 // 00218 // This flavor of collapse_equivalent_textures() 00219 // automatically adjusts all the primitives in the egg 00220 // hierarchy to refer to the new texture pointers. 00221 //////////////////////////////////////////////////////////////////// 00222 int EggTextureCollection:: 00223 collapse_equivalent_textures(int eq, EggGroupNode *node) { 00224 TextureReplacement removed; 00225 int num_collapsed = collapse_equivalent_textures(eq, removed); 00226 00227 // And now walk the egg hierarchy and replace any references to a 00228 // removed texture with its replacement. 00229 replace_textures(node, removed); 00230 00231 return num_collapsed; 00232 } 00233 00234 //////////////////////////////////////////////////////////////////// 00235 // Function: EggTextureCollection::collapse_equivalent_textures 00236 // Access: Public 00237 // Description: Walks through the collection and collapses together 00238 // any separate textures that are equivalent according 00239 // to the indicated equivalence factor, eq (see 00240 // EggTexture::is_equivalent_to()). The return value is 00241 // the number of textures removed. 00242 // 00243 // This flavor of collapse_equivalent_textures() does 00244 // not adjust any primitives in the egg hierarchy; 00245 // instead, it fills up the 'removed' map with an entry 00246 // for each removed texture, mapping it back to the 00247 // equivalent retained texture. It's up to the user to 00248 // then call replace_textures() with this map, if 00249 // desired, to apply these changes to the egg hierarchy. 00250 //////////////////////////////////////////////////////////////////// 00251 int EggTextureCollection:: 00252 collapse_equivalent_textures(int eq, EggTextureCollection::TextureReplacement &removed) { 00253 int num_collapsed = 0; 00254 00255 typedef pset<PT(EggTexture), UniqueEggTextures> Collapser; 00256 UniqueEggTextures uet(eq); 00257 Collapser collapser(uet); 00258 00259 // First, put all of the textures into the Collapser structure, to 00260 // find out the unique textures. 00261 OrderedTextures::const_iterator oti; 00262 for (oti = _ordered_textures.begin(); 00263 oti != _ordered_textures.end(); 00264 ++oti) { 00265 EggTexture *tex = (*oti); 00266 00267 pair<Collapser::const_iterator, bool> result = collapser.insert(tex); 00268 if (!result.second) { 00269 // This texture is non-unique; another one was already there. 00270 EggTexture *first = *(result.first); 00271 removed.insert(TextureReplacement::value_type(tex, first)); 00272 num_collapsed++; 00273 } 00274 } 00275 00276 // Now record all of the unique textures only. 00277 clear(); 00278 Collapser::const_iterator ci; 00279 for (ci = collapser.begin(); ci != collapser.end(); ++ci) { 00280 add_texture(*ci); 00281 } 00282 00283 return num_collapsed; 00284 } 00285 00286 //////////////////////////////////////////////////////////////////// 00287 // Function: EggTextureCollection::replace_textures 00288 // Access: Public, Static 00289 // Description: Walks the egg hierarchy, changing out any reference 00290 // to a texture appearing on the left side of the map 00291 // with its corresponding texture on the right side. 00292 // This is most often done following a call to 00293 // collapse_equivalent_textures(). It does not directly 00294 // affect the Collection. 00295 //////////////////////////////////////////////////////////////////// 00296 void EggTextureCollection:: 00297 replace_textures(EggGroupNode *node, 00298 const EggTextureCollection::TextureReplacement &replace) { 00299 EggGroupNode::iterator ci; 00300 for (ci = node->begin(); 00301 ci != node->end(); 00302 ++ci) { 00303 EggNode *child = *ci; 00304 if (child->is_of_type(EggPrimitive::get_class_type())) { 00305 EggPrimitive *primitive = DCAST(EggPrimitive, child); 00306 if (primitive->has_texture()) { 00307 PT(EggTexture) tex = primitive->get_texture(); 00308 TextureReplacement::const_iterator ri; 00309 ri = replace.find(tex); 00310 if (ri != replace.end()) { 00311 // Here's a texture we want to replace. 00312 primitive->set_texture((*ri).second); 00313 } 00314 } 00315 00316 } else if (child->is_of_type(EggGroupNode::get_class_type())) { 00317 EggGroupNode *group_child = DCAST(EggGroupNode, child); 00318 replace_textures(group_child, replace); 00319 } 00320 } 00321 } 00322 00323 //////////////////////////////////////////////////////////////////// 00324 // Function: EggTextureCollection::uniquify_trefs 00325 // Access: Public 00326 // Description: Guarantees that each texture in the collection has a 00327 // unique TRef name. This is essential before writing 00328 // an egg file. 00329 //////////////////////////////////////////////////////////////////// 00330 void EggTextureCollection:: 00331 uniquify_trefs() { 00332 NameUniquifier nu(".tref", "tref"); 00333 00334 OrderedTextures::const_iterator oti; 00335 for (oti = _ordered_textures.begin(); 00336 oti != _ordered_textures.end(); 00337 ++oti) { 00338 EggTexture *tex = (*oti); 00339 00340 tex->set_name(nu.add_name(tex->get_name())); 00341 } 00342 } 00343 00344 //////////////////////////////////////////////////////////////////// 00345 // Function: EggTextureCollection::sort_by_tref 00346 // Access: Public 00347 // Description: Sorts all the textures into alphabetical order by 00348 // TRef name. Subsequent operations using begin()/end() 00349 // will traverse in this sorted order. 00350 //////////////////////////////////////////////////////////////////// 00351 void EggTextureCollection:: 00352 sort_by_tref() { 00353 sort(_ordered_textures.begin(), _ordered_textures.end(), 00354 NamableOrderByName()); 00355 } 00356 00357 //////////////////////////////////////////////////////////////////// 00358 // Function: EggTextureCollection::add_texture 00359 // Access: Public 00360 // Description: Explicitly adds a new texture to the collection. 00361 // Returns true if the texture was added, false if it 00362 // was already there or if there was some error. 00363 //////////////////////////////////////////////////////////////////// 00364 bool EggTextureCollection:: 00365 add_texture(EggTexture *texture) { 00366 nassertr(_textures.size() == _ordered_textures.size(), false); 00367 00368 PT(EggTexture) new_tex = texture; 00369 00370 Textures::const_iterator ti; 00371 ti = _textures.find(new_tex); 00372 if (ti != _textures.end()) { 00373 // This texture is already a member of the collection. 00374 return false; 00375 } 00376 00377 _textures.insert(Textures::value_type(new_tex, 0)); 00378 _ordered_textures.push_back(new_tex); 00379 00380 nassertr(_textures.size() == _ordered_textures.size(), false); 00381 return true; 00382 } 00383 00384 //////////////////////////////////////////////////////////////////// 00385 // Function: EggTextureCollection::remove_texture 00386 // Access: Public 00387 // Description: Explicitly removes a texture from the collection. 00388 // Returns true if the texture was removed, false if it 00389 // wasn't there or if there was some error. 00390 //////////////////////////////////////////////////////////////////// 00391 bool EggTextureCollection:: 00392 remove_texture(EggTexture *texture) { 00393 nassertr(_textures.size() == _ordered_textures.size(), false); 00394 00395 Textures::iterator ti; 00396 ti = _textures.find(texture); 00397 if (ti == _textures.end()) { 00398 // This texture is not a member of the collection. 00399 return false; 00400 } 00401 00402 _textures.erase(ti); 00403 00404 OrderedTextures::iterator oti; 00405 PT(EggTexture) ptex = texture; 00406 oti = find(_ordered_textures.begin(), _ordered_textures.end(), ptex); 00407 nassertr(oti != _ordered_textures.end(), false); 00408 00409 _ordered_textures.erase(oti); 00410 00411 nassertr(_textures.size() == _ordered_textures.size(), false); 00412 return true; 00413 } 00414 00415 //////////////////////////////////////////////////////////////////// 00416 // Function: EggTextureCollection::create_unique_texture 00417 // Access: Public 00418 // Description: Creates a new texture if there is not already one 00419 // equivalent (according to eq, see 00420 // EggTexture::is_equivalent_to()) to the indicated 00421 // texture, or returns the existing one if there is. 00422 //////////////////////////////////////////////////////////////////// 00423 EggTexture *EggTextureCollection:: 00424 create_unique_texture(const EggTexture ©, int eq) { 00425 // This requires a complete linear traversal, not terribly 00426 // efficient. 00427 OrderedTextures::const_iterator oti; 00428 for (oti = _ordered_textures.begin(); 00429 oti != _ordered_textures.end(); 00430 ++oti) { 00431 EggTexture *tex = (*oti); 00432 if (copy.is_equivalent_to(*tex, eq)) { 00433 return tex; 00434 } 00435 } 00436 00437 EggTexture *new_texture = new EggTexture(copy); 00438 add_texture(new_texture); 00439 return new_texture; 00440 } 00441 00442 //////////////////////////////////////////////////////////////////// 00443 // Function: EggTextureCollection::find_tref 00444 // Access: Public 00445 // Description: Returns the texture with the indicated TRef name, or 00446 // NULL if no texture matches. 00447 //////////////////////////////////////////////////////////////////// 00448 EggTexture *EggTextureCollection:: 00449 find_tref(const string &tref_name) const { 00450 // This requires a complete linear traversal, not terribly 00451 // efficient. 00452 OrderedTextures::const_iterator oti; 00453 for (oti = _ordered_textures.begin(); 00454 oti != _ordered_textures.end(); 00455 ++oti) { 00456 EggTexture *tex = (*oti); 00457 if (tex->get_name() == tref_name) { 00458 return tex; 00459 } 00460 } 00461 00462 return (EggTexture *)NULL; 00463 } 00464 00465 //////////////////////////////////////////////////////////////////// 00466 // Function: EggTextureCollection::find_filename 00467 // Access: Public 00468 // Description: Returns the texture with the indicated filename, or 00469 // NULL if no texture matches. 00470 //////////////////////////////////////////////////////////////////// 00471 EggTexture *EggTextureCollection:: 00472 find_filename(const Filename &filename) const { 00473 // This requires a complete linear traversal, not terribly 00474 // efficient. 00475 OrderedTextures::const_iterator oti; 00476 for (oti = _ordered_textures.begin(); 00477 oti != _ordered_textures.end(); 00478 ++oti) { 00479 EggTexture *tex = (*oti); 00480 if (tex->get_filename() == filename) { 00481 return tex; 00482 } 00483 } 00484 00485 return (EggTexture *)NULL; 00486 }