Panda3D
eggMaterialCollection.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file eggMaterialCollection.cxx
10  * @author drose
11  * @date 2001-04-30
12  */
13 
14 #include "eggMaterialCollection.h"
15 #include "eggGroupNode.h"
16 #include "eggPrimitive.h"
17 #include "eggMaterial.h"
18 
19 #include "nameUniquifier.h"
20 #include "dcast.h"
21 
22 #include <algorithm>
23 
24 /**
25  *
26  */
27 EggMaterialCollection::
28 EggMaterialCollection() {
29 }
30 
31 /**
32  *
33  */
34 EggMaterialCollection::
35 EggMaterialCollection(const EggMaterialCollection &copy) :
36  _materials(copy._materials),
37  _ordered_materials(copy._ordered_materials)
38 {
39 }
40 
41 /**
42  *
43  */
44 EggMaterialCollection &EggMaterialCollection::
45 operator = (const EggMaterialCollection &copy) {
46  _materials = copy._materials;
47  _ordered_materials = copy._ordered_materials;
48  return *this;
49 }
50 
51 /**
52  *
53  */
54 EggMaterialCollection::
55 ~EggMaterialCollection() {
56 }
57 
58 /**
59  * Removes all materials from the collection.
60  */
62 clear() {
63  _materials.clear();
64  _ordered_materials.clear();
65 }
66 
67 /**
68  * Walks the egg hierarchy beginning at the indicated node, and removes any
69  * EggMaterials encountered in the hierarchy, adding them to the collection.
70  * Returns the number of EggMaterials encountered.
71  */
74  // Since this traversal is destructive, we'll handle it within the
75  // EggGroupNode code.
76  return node->find_materials(this);
77 }
78 
79 /**
80  * Adds a series of EggMaterial nodes to the beginning of the indicated node
81  * to reflect each of the materials in the collection. Returns an iterator
82  * representing the first position after the newly inserted materials.
83  */
84 EggGroupNode::iterator EggMaterialCollection::
86  return insert_materials(node, node->begin());
87 }
88 
89 /**
90  * Adds a series of EggMaterial nodes to the beginning of the indicated node
91  * to reflect each of the materials in the collection. Returns an iterator
92  * representing the first position after the newly inserted materials.
93  */
94 EggGroupNode::iterator EggMaterialCollection::
95 insert_materials(EggGroupNode *node, EggGroupNode::iterator position) {
96  OrderedMaterials::iterator oti;
97  for (oti = _ordered_materials.begin();
98  oti != _ordered_materials.end();
99  ++oti) {
100  EggMaterial *material = (*oti);
101  position = node->insert(position, material);
102  }
103 
104  return position;
105 }
106 
107 /**
108  * Walks the egg hierarchy beginning at the indicated node, looking for
109  * materials that are referenced by primitives but are not already members of
110  * the collection, adding them to the collection.
111  *
112  * If this is called following extract_materials(), it can be used to pick up
113  * any additional material references that appeared in the egg hierarchy (but
114  * whose EggMaterial node was not actually part of the hierarchy).
115  *
116  * If this is called in lieu of extract_materials(), it will fill up the
117  * collection with all of the referenced materials (and only the referenced
118  * materials), without destructively removing the EggMaterials from the
119  * hierarchy.
120  *
121  * This also has the side effect of incrementing the internal usage count for
122  * a material in the collection each time a material reference is encountered.
123  * This side effect is taken advantage of by remove_unused_materials().
124  */
127  int num_found = 0;
128 
129  if (node->is_of_type(EggPrimitive::get_class_type())) {
130  EggPrimitive *primitive = DCAST(EggPrimitive, node);
131  if (primitive->has_material()) {
132  EggMaterial *tex = primitive->get_material();
133  Materials::iterator ti = _materials.find(tex);
134  if (ti == _materials.end()) {
135  // Here's a new material!
136  num_found++;
137  _materials.insert(Materials::value_type(tex, 1));
138  _ordered_materials.push_back(tex);
139  } else {
140  // Here's a material we'd already known about. Increment its usage
141  // count.
142  (*ti).second++;
143  }
144  }
145 
146  } else if (node->is_of_type(EggGroupNode::get_class_type())) {
147  EggGroupNode *group = DCAST(EggGroupNode, node);
148 
149  EggGroupNode::iterator ci;
150  for (ci = group->begin(); ci != group->end(); ++ci) {
151  EggNode *child = *ci;
152 
153  num_found += find_used_materials(child);
154  }
155  }
156 
157  return num_found;
158 }
159 
160 /**
161  * Removes any materials from the collection that aren't referenced by any
162  * primitives in the indicated egg hierarchy. This also, incidentally, adds
163  * materials to the collection that had been referenced by primitives but had
164  * not previously appeared in the collection.
165  */
168  // We'll do this the easy way: First, we'll remove *all* the materials from
169  // the collection, and then we'll add back only those that appear in the
170  // hierarchy.
171  clear();
172  find_used_materials(node);
173 }
174 
175 /**
176  * Walks through the collection and collapses together any separate materials
177  * that are equivalent according to the indicated equivalence factor, eq (see
178  * EggMaterial::is_equivalent_to()). The return value is the number of
179  * materials removed.
180  *
181  * This flavor of collapse_equivalent_materials() automatically adjusts all
182  * the primitives in the egg hierarchy to refer to the new material pointers.
183  */
186  MaterialReplacement removed;
187  int num_collapsed = collapse_equivalent_materials(eq, removed);
188 
189  // And now walk the egg hierarchy and replace any references to a removed
190  // material with its replacement.
191  replace_materials(node, removed);
192 
193  return num_collapsed;
194 }
195 
196 /**
197  * Walks through the collection and collapses together any separate materials
198  * that are equivalent according to the indicated equivalence factor, eq (see
199  * EggMaterial::is_equivalent_to()). The return value is the number of
200  * materials removed.
201  *
202  * This flavor of collapse_equivalent_materials() does not adjust any
203  * primitives in the egg hierarchy; instead, it fills up the 'removed' map
204  * with an entry for each removed material, mapping it back to the equivalent
205  * retained material. It's up to the user to then call replace_materials()
206  * with this map, if desired, to apply these changes to the egg hierarchy.
207  */
210  int num_collapsed = 0;
211 
212  typedef pset<PT(EggMaterial), UniqueEggMaterials> Collapser;
213  UniqueEggMaterials uet(eq);
214  Collapser collapser(uet);
215 
216  // First, put all of the materials into the Collapser structure, to find out
217  // the unique materials.
218  OrderedMaterials::const_iterator oti;
219  for (oti = _ordered_materials.begin();
220  oti != _ordered_materials.end();
221  ++oti) {
222  EggMaterial *tex = (*oti);
223 
224  std::pair<Collapser::const_iterator, bool> result = collapser.insert(tex);
225  if (!result.second) {
226  // This material is non-unique; another one was already there.
227  EggMaterial *first = *(result.first);
228  removed.insert(MaterialReplacement::value_type(tex, first));
229  num_collapsed++;
230  }
231  }
232 
233  // Now record all of the unique materials only.
234  clear();
235  Collapser::const_iterator ci;
236  for (ci = collapser.begin(); ci != collapser.end(); ++ci) {
237  add_material(*ci);
238  }
239 
240  return num_collapsed;
241 }
242 
243 /**
244  * Walks the egg hierarchy, changing out any reference to a material appearing
245  * on the left side of the map with its corresponding material on the right
246  * side. This is most often done following a call to
247  * collapse_equivalent_materials(). It does not directly affect the
248  * Collection.
249  */
253  EggGroupNode::iterator ci;
254  for (ci = node->begin();
255  ci != node->end();
256  ++ci) {
257  EggNode *child = *ci;
258  if (child->is_of_type(EggPrimitive::get_class_type())) {
259  EggPrimitive *primitive = DCAST(EggPrimitive, child);
260  if (primitive->has_material()) {
261  PT(EggMaterial) tex = primitive->get_material();
262  MaterialReplacement::const_iterator ri;
263  ri = replace.find(tex);
264  if (ri != replace.end()) {
265  // Here's a material we want to replace.
266  primitive->set_material((*ri).second);
267  }
268  }
269 
270  } else if (child->is_of_type(EggGroupNode::get_class_type())) {
271  EggGroupNode *group_child = DCAST(EggGroupNode, child);
272  replace_materials(group_child, replace);
273  }
274  }
275 }
276 
277 /**
278  * Guarantees that each material in the collection has a unique MRef name.
279  * This is essential before writing an egg file.
280  */
283  NameUniquifier nu(".mref", "mref");
284 
285  OrderedMaterials::const_iterator oti;
286  for (oti = _ordered_materials.begin();
287  oti != _ordered_materials.end();
288  ++oti) {
289  EggMaterial *tex = (*oti);
290 
291  tex->set_name(nu.add_name(tex->get_name()));
292  }
293 }
294 
295 /**
296  * Sorts all the materials into alphabetical order by MRef name. Subsequent
297  * operations using begin()/end() will traverse in this sorted order.
298  */
301  sort(_ordered_materials.begin(), _ordered_materials.end(),
303 }
304 
305 /**
306  * Explicitly adds a new material to the collection. Returns true if the
307  * material was added, false if it was already there or if there was some
308  * error.
309  */
312  nassertr(_materials.size() == _ordered_materials.size(), false);
313 
314  PT(EggMaterial) new_tex = material;
315 
316  Materials::const_iterator ti;
317  ti = _materials.find(new_tex);
318  if (ti != _materials.end()) {
319  // This material is already a member of the collection.
320  return false;
321  }
322 
323  _materials.insert(Materials::value_type(new_tex, 0));
324  _ordered_materials.push_back(new_tex);
325 
326  nassertr(_materials.size() == _ordered_materials.size(), false);
327  return true;
328 }
329 
330 /**
331  * Explicitly removes a material from the collection. Returns true if the
332  * material was removed, false if it wasn't there or if there was some error.
333  */
336  nassertr(_materials.size() == _ordered_materials.size(), false);
337 
338  Materials::iterator ti;
339  ti = _materials.find(material);
340  if (ti == _materials.end()) {
341  // This material is not a member of the collection.
342  return false;
343  }
344 
345  _materials.erase(ti);
346 
347  OrderedMaterials::iterator oti;
348  PT(EggMaterial) ptex = material;
349  oti = find(_ordered_materials.begin(), _ordered_materials.end(), ptex);
350  nassertr(oti != _ordered_materials.end(), false);
351 
352  _ordered_materials.erase(oti);
353 
354  nassertr(_materials.size() == _ordered_materials.size(), false);
355  return true;
356 }
357 
358 /**
359  * Creates a new material if there is not already one equivalent (according to
360  * eq, see EggMaterial::is_equivalent_to()) to the indicated material, or
361  * returns the existing one if there is.
362  */
364 create_unique_material(const EggMaterial &copy, int eq) {
365  // This requires a complete linear traversal, not terribly efficient.
366  OrderedMaterials::const_iterator oti;
367  for (oti = _ordered_materials.begin();
368  oti != _ordered_materials.end();
369  ++oti) {
370  EggMaterial *tex = (*oti);
371  if (copy.is_equivalent_to(*tex, eq)) {
372  return tex;
373  }
374  }
375 
376  EggMaterial *new_material = new EggMaterial(copy);
377  add_material(new_material);
378  return new_material;
379 }
380 
381 /**
382  * Returns the material with the indicated MRef name, or NULL if no material
383  * matches.
384  */
386 find_mref(const std::string &mref_name) const {
387  // This requires a complete linear traversal, not terribly efficient.
388  OrderedMaterials::const_iterator oti;
389  for (oti = _ordered_materials.begin();
390  oti != _ordered_materials.end();
391  ++oti) {
392  EggMaterial *tex = (*oti);
393  if (tex->get_name() == mref_name) {
394  return tex;
395  }
396  }
397 
398  return nullptr;
399 }
EggMaterialCollection::extract_materials
int extract_materials(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggMaterials encountered in ...
Definition: eggMaterialCollection.cxx:73
eggPrimitive.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NameUniquifier::add_name
std::string add_name(const std::string &name)
If name is nonempty and so far unique, returns it unchanged.
Definition: nameUniquifier.I:28
EggMaterialCollection
This is a collection of materials by MRef name.
Definition: eggMaterialCollection.h:30
EggMaterialCollection::sort_by_mref
void sort_by_mref()
Sorts all the materials into alphabetical order by MRef name.
Definition: eggMaterialCollection.cxx:300
EggMaterialCollection::clear
void clear()
Removes all materials from the collection.
Definition: eggMaterialCollection.cxx:62
pmap
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
EggMaterialCollection::collapse_equivalent_materials
int collapse_equivalent_materials(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate materials that are equivalent accord...
Definition: eggMaterialCollection.cxx:185
EggMaterial::is_equivalent_to
bool is_equivalent_to(const EggMaterial &other, int eq) const
Returns true if the two materials are equivalent in all relevant properties (according to eq),...
Definition: eggMaterial.cxx:166
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggMaterialCollection::create_unique_material
EggMaterial * create_unique_material(const EggMaterial &copy, int eq)
Creates a new material if there is not already one equivalent (according to eq, see EggMaterial::is_e...
Definition: eggMaterialCollection.cxx:364
EggMaterialCollection::remove_material
bool remove_material(EggMaterial *material)
Explicitly removes a material from the collection.
Definition: eggMaterialCollection.cxx:335
EggMaterial
Definition: eggMaterial.h:26
eggMaterial.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggGroupNode
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggPrimitive
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:47
nameUniquifier.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggPrimitive::has_material
has_material
Returns true if the primitive is materiald (and get_material() will return a real pointer),...
Definition: eggPrimitive.h:115
EggMaterialCollection::remove_unused_materials
void remove_unused_materials(EggNode *node)
Removes any materials from the collection that aren't referenced by any primitives in the indicated e...
Definition: eggMaterialCollection.cxx:167
EggPrimitive::set_material
set_material
Applies the indicated material to the primitive.
Definition: eggPrimitive.h:115
NamableOrderByName
An STL function object for sorting an array of pointers to Namables into order by name.
Definition: namable.h:62
EggMaterialCollection::replace_materials
static void replace_materials(EggGroupNode *node, const MaterialReplacement &replace)
Walks the egg hierarchy, changing out any reference to a material appearing on the left side of the m...
Definition: eggMaterialCollection.cxx:251
EggPrimitive::get_material
get_material
Returns a pointer to the applied material, or NULL if there is no material applied.
Definition: eggPrimitive.h:115
EggMaterialCollection::find_mref
EggMaterial * find_mref(const std::string &mref_name) const
Returns the material with the indicated MRef name, or NULL if no material matches.
Definition: eggMaterialCollection.cxx:386
EggMaterialCollection::find_used_materials
int find_used_materials(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for materials that are referenced by...
Definition: eggMaterialCollection.cxx:126
eggGroupNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggMaterialCollection::add_material
bool add_material(EggMaterial *material)
Explicitly adds a new material to the collection.
Definition: eggMaterialCollection.cxx:311
EggMaterialCollection::insert_materials
EggGroupNode::iterator insert_materials(EggGroupNode *node)
Adds a series of EggMaterial nodes to the beginning of the indicated node to reflect each of the mate...
Definition: eggMaterialCollection.cxx:85
UniqueEggMaterials
An STL function object for sorting materials into order by properties.
Definition: eggMaterial.h:154
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
eggMaterialCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggMaterialCollection::uniquify_mrefs
void uniquify_mrefs()
Guarantees that each material in the collection has a unique MRef name.
Definition: eggMaterialCollection.cxx:282
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
pset
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
NameUniquifier
A handy class for converting a list of arbitrary names (strings) so that each name is guaranteed to b...
Definition: nameUniquifier.h:28