Panda3D
 All Classes Functions Variables Enumerations
eggMaterialCollection.cxx
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 &copy) :
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 &copy) {
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 &copy, 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 }
 All Classes Functions Variables Enumerations