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 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:47
void sort_by_mref()
Sorts all the materials into alphabetical order by MRef name.
get_material
Returns a pointer to the applied material, or NULL if there is no material applied.
Definition: eggPrimitive.h:115
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
void uniquify_mrefs()
Guarantees that each material in the collection has a unique MRef name.
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),...
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
This is a collection of materials by MRef name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool add_material(EggMaterial *material)
Explicitly adds a new material to the collection.
void clear()
Removes all materials from the collection.
int collapse_equivalent_materials(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate materials that are equivalent accord...
set_material
Applies the indicated material to the primitive.
Definition: eggPrimitive.h:115
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A handy class for converting a list of arbitrary names (strings) so that each name is guaranteed to b...
bool remove_material(EggMaterial *material)
Explicitly removes a material from the collection.
int extract_materials(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggMaterials encountered in ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An STL function object for sorting materials into order by properties.
Definition: eggMaterial.h:154
std::string add_name(const std::string &name)
If name is nonempty and so far unique, returns it unchanged.
void remove_unused_materials(EggNode *node)
Removes any materials from the collection that aren't referenced by any primitives in the indicated e...
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
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...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
EggMaterial * find_mref(const std::string &mref_name) const
Returns the material with the indicated MRef name, or NULL if no material matches.
int find_used_materials(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for materials that are referenced by...
An STL function object for sorting an array of pointers to Namables into order by name.
Definition: namable.h:62
has_material
Returns true if the primitive is materiald (and get_material() will return a real pointer),...
Definition: eggPrimitive.h:115