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 ©) : 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 ©) { 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 ©, 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 }