00001 // Filename: eggMaterialCollection.cxx 00002 // Created by: drose (30Apr01) 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 "eggMaterialCollection.h" 00020 #include "eggGroupNode.h" 00021 #include "eggPrimitive.h" 00022 #include "eggMaterial.h" 00023 00024 #include <nameUniquifier.h> 00025 00026 #include <algorithm> 00027 00028 //////////////////////////////////////////////////////////////////// 00029 // Function: EggMaterialCollection::Constructor 00030 // Access: Public 00031 // Description: 00032 //////////////////////////////////////////////////////////////////// 00033 EggMaterialCollection:: 00034 EggMaterialCollection() { 00035 } 00036 00037 //////////////////////////////////////////////////////////////////// 00038 // Function: EggMaterialCollection::Copy Constructor 00039 // Access: Public 00040 // Description: 00041 //////////////////////////////////////////////////////////////////// 00042 EggMaterialCollection:: 00043 EggMaterialCollection(const EggMaterialCollection ©) : 00044 _materials(copy._materials), 00045 _ordered_materials(copy._ordered_materials) 00046 { 00047 } 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: EggMaterialCollection::Copy Assignment Operator 00051 // Access: Public 00052 // Description: 00053 //////////////////////////////////////////////////////////////////// 00054 EggMaterialCollection &EggMaterialCollection:: 00055 operator = (const EggMaterialCollection ©) { 00056 _materials = copy._materials; 00057 _ordered_materials = copy._ordered_materials; 00058 return *this; 00059 } 00060 00061 //////////////////////////////////////////////////////////////////// 00062 // Function: EggMaterialCollection::Destructor 00063 // Access: Public 00064 // Description: 00065 //////////////////////////////////////////////////////////////////// 00066 EggMaterialCollection:: 00067 ~EggMaterialCollection() { 00068 } 00069 00070 //////////////////////////////////////////////////////////////////// 00071 // Function: EggMaterialCollection::clear 00072 // Access: Public 00073 // Description: Removes all materials from the collection. 00074 //////////////////////////////////////////////////////////////////// 00075 void EggMaterialCollection:: 00076 clear() { 00077 _materials.clear(); 00078 _ordered_materials.clear(); 00079 } 00080 00081 //////////////////////////////////////////////////////////////////// 00082 // Function: EggMaterialCollection::extract_materials 00083 // Access: Public 00084 // Description: Walks the egg hierarchy beginning at the indicated 00085 // node, and removes any EggMaterials encountered in the 00086 // hierarchy, adding them to the collection. Returns 00087 // the number of EggMaterials encountered. 00088 //////////////////////////////////////////////////////////////////// 00089 int EggMaterialCollection:: 00090 extract_materials(EggGroupNode *node) { 00091 // Since this traversal is destructive, we'll handle it within the 00092 // EggGroupNode code. 00093 return node->find_materials(this); 00094 } 00095 00096 //////////////////////////////////////////////////////////////////// 00097 // Function: EggMaterialCollection::insert_materials 00098 // Access: Public 00099 // Description: Adds a series of EggMaterial nodes to the beginning of 00100 // the indicated node to reflect each of the materials in 00101 // the collection. Returns an iterator representing the 00102 // first position after the newly inserted materials. 00103 //////////////////////////////////////////////////////////////////// 00104 EggGroupNode::iterator EggMaterialCollection:: 00105 insert_materials(EggGroupNode *node) { 00106 return insert_materials(node, node->begin()); 00107 } 00108 00109 //////////////////////////////////////////////////////////////////// 00110 // Function: EggMaterialCollection::insert_materials 00111 // Access: Public 00112 // Description: Adds a series of EggMaterial nodes to the beginning of 00113 // the indicated node to reflect each of the materials in 00114 // the collection. Returns an iterator representing the 00115 // first position after the newly inserted materials. 00116 //////////////////////////////////////////////////////////////////// 00117 EggGroupNode::iterator EggMaterialCollection:: 00118 insert_materials(EggGroupNode *node, EggGroupNode::iterator position) { 00119 OrderedMaterials::iterator oti; 00120 for (oti = _ordered_materials.begin(); 00121 oti != _ordered_materials.end(); 00122 ++oti) { 00123 EggMaterial *material = (*oti); 00124 position = node->insert(position, material); 00125 } 00126 00127 return position; 00128 } 00129 00130 //////////////////////////////////////////////////////////////////// 00131 // Function: EggMaterialCollection::find_used_materials 00132 // Access: Public 00133 // Description: Walks the egg hierarchy beginning at the indicated 00134 // node, looking for materials 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_materials(), it 00139 // can be used to pick up any additional material 00140 // references that appeared in the egg hierarchy (but 00141 // whose EggMaterial node was not actually part of the 00142 // hierarchy). 00143 // 00144 // If this is called in lieu of extract_materials(), it 00145 // will fill up the collection with all of the 00146 // referenced materials (and only the referenced 00147 // materials), without destructively removing the 00148 // EggMaterials from the hierarchy. 00149 // 00150 // This also has the side effect of incrementing the 00151 // internal usage count for a material in the collection 00152 // each time a material reference is encountered. This 00153 // side effect is taken advantage of by 00154 // remove_unused_materials(). 00155 //////////////////////////////////////////////////////////////////// 00156 int EggMaterialCollection:: 00157 find_used_materials(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_material()) { 00163 EggMaterial *tex = primitive->get_material(); 00164 Materials::iterator ti = _materials.find(tex); 00165 if (ti == _materials.end()) { 00166 // Here's a new material! 00167 num_found++; 00168 _materials.insert(Materials::value_type(tex, 1)); 00169 _ordered_materials.push_back(tex); 00170 } else { 00171 // Here's a material 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_materials(child); 00185 } 00186 } 00187 00188 return num_found; 00189 } 00190 00191 //////////////////////////////////////////////////////////////////// 00192 // Function: EggMaterialCollection::remove_unused_materials 00193 // Access: Public 00194 // Description: Removes any materials from the collection that aren't 00195 // referenced by any primitives in the indicated egg 00196 // hierarchy. This also, incidentally, adds materials to 00197 // the collection that had been referenced by primitives 00198 // but had not previously appeared in the collection. 00199 //////////////////////////////////////////////////////////////////// 00200 void EggMaterialCollection:: 00201 remove_unused_materials(EggNode *node) { 00202 // We'll do this the easy way: First, we'll remove *all* the 00203 // materials from the collection, and then we'll add back only those 00204 // that appear in the hierarchy. 00205 clear(); 00206 find_used_materials(node); 00207 } 00208 00209 //////////////////////////////////////////////////////////////////// 00210 // Function: EggMaterialCollection::collapse_equivalent_materials 00211 // Access: Public 00212 // Description: Walks through the collection and collapses together 00213 // any separate materials that are equivalent according 00214 // to the indicated equivalence factor, eq (see 00215 // EggMaterial::is_equivalent_to()). The return value is 00216 // the number of materials removed. 00217 // 00218 // This flavor of collapse_equivalent_materials() 00219 // automatically adjusts all the primitives in the egg 00220 // hierarchy to refer to the new material pointers. 00221 //////////////////////////////////////////////////////////////////// 00222 int EggMaterialCollection:: 00223 collapse_equivalent_materials(int eq, EggGroupNode *node) { 00224 MaterialReplacement removed; 00225 int num_collapsed = collapse_equivalent_materials(eq, removed); 00226 00227 // And now walk the egg hierarchy and replace any references to a 00228 // removed material with its replacement. 00229 replace_materials(node, removed); 00230 00231 return num_collapsed; 00232 } 00233 00234 //////////////////////////////////////////////////////////////////// 00235 // Function: EggMaterialCollection::collapse_equivalent_materials 00236 // Access: Public 00237 // Description: Walks through the collection and collapses together 00238 // any separate materials that are equivalent according 00239 // to the indicated equivalence factor, eq (see 00240 // EggMaterial::is_equivalent_to()). The return value is 00241 // the number of materials removed. 00242 // 00243 // This flavor of collapse_equivalent_materials() 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 material, mapping it back to the 00247 // equivalent retained material. It's up to the user to 00248 // then call replace_materials() with this map, if 00249 // desired, to apply these changes to the egg hierarchy. 00250 //////////////////////////////////////////////////////////////////// 00251 int EggMaterialCollection:: 00252 collapse_equivalent_materials(int eq, EggMaterialCollection::MaterialReplacement &removed) { 00253 int num_collapsed = 0; 00254 00255 typedef pset<PT(EggMaterial), UniqueEggMaterials> Collapser; 00256 UniqueEggMaterials uet(eq); 00257 Collapser collapser(uet); 00258 00259 // First, put all of the materials into the Collapser structure, to 00260 // find out the unique materials. 00261 OrderedMaterials::const_iterator oti; 00262 for (oti = _ordered_materials.begin(); 00263 oti != _ordered_materials.end(); 00264 ++oti) { 00265 EggMaterial *tex = (*oti); 00266 00267 pair<Collapser::const_iterator, bool> result = collapser.insert(tex); 00268 if (!result.second) { 00269 // This material is non-unique; another one was already there. 00270 EggMaterial *first = *(result.first); 00271 removed.insert(MaterialReplacement::value_type(tex, first)); 00272 num_collapsed++; 00273 } 00274 } 00275 00276 // Now record all of the unique materials only. 00277 clear(); 00278 Collapser::const_iterator ci; 00279 for (ci = collapser.begin(); ci != collapser.end(); ++ci) { 00280 add_material(*ci); 00281 } 00282 00283 return num_collapsed; 00284 } 00285 00286 //////////////////////////////////////////////////////////////////// 00287 // Function: EggMaterialCollection::replace_materials 00288 // Access: Public, Static 00289 // Description: Walks the egg hierarchy, changing out any reference 00290 // to a material appearing on the left side of the map 00291 // with its corresponding material on the right side. 00292 // This is most often done following a call to 00293 // collapse_equivalent_materials(). It does not directly 00294 // affect the Collection. 00295 //////////////////////////////////////////////////////////////////// 00296 void EggMaterialCollection:: 00297 replace_materials(EggGroupNode *node, 00298 const EggMaterialCollection::MaterialReplacement &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_material()) { 00307 PT(EggMaterial) tex = primitive->get_material(); 00308 MaterialReplacement::const_iterator ri; 00309 ri = replace.find(tex); 00310 if (ri != replace.end()) { 00311 // Here's a material we want to replace. 00312 primitive->set_material((*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_materials(group_child, replace); 00319 } 00320 } 00321 } 00322 00323 //////////////////////////////////////////////////////////////////// 00324 // Function: EggMaterialCollection::uniquify_mrefs 00325 // Access: Public 00326 // Description: Guarantees that each material in the collection has a 00327 // unique MRef name. This is essential before writing 00328 // an egg file. 00329 //////////////////////////////////////////////////////////////////// 00330 void EggMaterialCollection:: 00331 uniquify_mrefs() { 00332 NameUniquifier nu(".mref", "mref"); 00333 00334 OrderedMaterials::const_iterator oti; 00335 for (oti = _ordered_materials.begin(); 00336 oti != _ordered_materials.end(); 00337 ++oti) { 00338 EggMaterial *tex = (*oti); 00339 00340 tex->set_name(nu.add_name(tex->get_name())); 00341 } 00342 } 00343 00344 //////////////////////////////////////////////////////////////////// 00345 // Function: EggMaterialCollection::sort_by_mref 00346 // Access: Public 00347 // Description: Sorts all the materials into alphabetical order by 00348 // MRef name. Subsequent operations using begin()/end() 00349 // will traverse in this sorted order. 00350 //////////////////////////////////////////////////////////////////// 00351 void EggMaterialCollection:: 00352 sort_by_mref() { 00353 sort(_ordered_materials.begin(), _ordered_materials.end(), 00354 NamableOrderByName()); 00355 } 00356 00357 //////////////////////////////////////////////////////////////////// 00358 // Function: EggMaterialCollection::add_material 00359 // Access: Public 00360 // Description: Explicitly adds a new material to the collection. 00361 // Returns true if the material was added, false if it 00362 // was already there or if there was some error. 00363 //////////////////////////////////////////////////////////////////// 00364 bool EggMaterialCollection:: 00365 add_material(EggMaterial *material) { 00366 nassertr(_materials.size() == _ordered_materials.size(), false); 00367 00368 PT(EggMaterial) new_tex = material; 00369 00370 Materials::const_iterator ti; 00371 ti = _materials.find(new_tex); 00372 if (ti != _materials.end()) { 00373 // This material is already a member of the collection. 00374 return false; 00375 } 00376 00377 _materials.insert(Materials::value_type(new_tex, 0)); 00378 _ordered_materials.push_back(new_tex); 00379 00380 nassertr(_materials.size() == _ordered_materials.size(), false); 00381 return true; 00382 } 00383 00384 //////////////////////////////////////////////////////////////////// 00385 // Function: EggMaterialCollection::remove_material 00386 // Access: Public 00387 // Description: Explicitly removes a material from the collection. 00388 // Returns true if the material was removed, false if it 00389 // wasn't there or if there was some error. 00390 //////////////////////////////////////////////////////////////////// 00391 bool EggMaterialCollection:: 00392 remove_material(EggMaterial *material) { 00393 nassertr(_materials.size() == _ordered_materials.size(), false); 00394 00395 Materials::iterator ti; 00396 ti = _materials.find(material); 00397 if (ti == _materials.end()) { 00398 // This material is not a member of the collection. 00399 return false; 00400 } 00401 00402 _materials.erase(ti); 00403 00404 OrderedMaterials::iterator oti; 00405 PT(EggMaterial) ptex = material; 00406 oti = find(_ordered_materials.begin(), _ordered_materials.end(), ptex); 00407 nassertr(oti != _ordered_materials.end(), false); 00408 00409 _ordered_materials.erase(oti); 00410 00411 nassertr(_materials.size() == _ordered_materials.size(), false); 00412 return true; 00413 } 00414 00415 //////////////////////////////////////////////////////////////////// 00416 // Function: EggMaterialCollection::create_unique_material 00417 // Access: Public 00418 // Description: Creates a new material if there is not already one 00419 // equivalent (according to eq, see 00420 // EggMaterial::is_equivalent_to()) to the indicated 00421 // material, or returns the existing one if there is. 00422 //////////////////////////////////////////////////////////////////// 00423 EggMaterial *EggMaterialCollection:: 00424 create_unique_material(const EggMaterial ©, int eq) { 00425 // This requires a complete linear traversal, not terribly 00426 // efficient. 00427 OrderedMaterials::const_iterator oti; 00428 for (oti = _ordered_materials.begin(); 00429 oti != _ordered_materials.end(); 00430 ++oti) { 00431 EggMaterial *tex = (*oti); 00432 if (copy.is_equivalent_to(*tex, eq)) { 00433 return tex; 00434 } 00435 } 00436 00437 EggMaterial *new_material = new EggMaterial(copy); 00438 add_material(new_material); 00439 return new_material; 00440 } 00441 00442 //////////////////////////////////////////////////////////////////// 00443 // Function: EggMaterialCollection::find_mref 00444 // Access: Public 00445 // Description: Returns the material with the indicated MRef name, or 00446 // NULL if no material matches. 00447 //////////////////////////////////////////////////////////////////// 00448 EggMaterial *EggMaterialCollection:: 00449 find_mref(const string &mref_name) const { 00450 // This requires a complete linear traversal, not terribly 00451 // efficient. 00452 OrderedMaterials::const_iterator oti; 00453 for (oti = _ordered_materials.begin(); 00454 oti != _ordered_materials.end(); 00455 ++oti) { 00456 EggMaterial *tex = (*oti); 00457 if (tex->get_name() == mref_name) { 00458 return tex; 00459 } 00460 } 00461 00462 return (EggMaterial *)NULL; 00463 }