Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/egg/eggMaterialCollection.cxx

Go to the documentation of this file.
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 &copy) :
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 &copy) {
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 &copy, 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 }

Generated on Fri May 2 00:37:42 2003 for Panda by doxygen1.3