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