Panda3D
eggTextureCollection.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 eggTextureCollection.cxx
10  * @author drose
11  * @date 2000-02-15
12  */
13 
14 #include "eggTextureCollection.h"
15 #include "eggGroupNode.h"
16 #include "eggPrimitive.h"
17 #include "eggTexture.h"
18 #include "pt_EggTexture.h"
19 #include "dcast.h"
20 
21 #include "nameUniquifier.h"
22 
23 #include <algorithm>
24 
25 /**
26  *
27  */
28 EggTextureCollection::
29 EggTextureCollection() {
30 }
31 
32 /**
33  *
34  */
35 EggTextureCollection::
36 EggTextureCollection(const EggTextureCollection &copy) :
37  _textures(copy._textures),
38  _ordered_textures(copy._ordered_textures)
39 {
40 }
41 
42 /**
43  *
44  */
45 EggTextureCollection &EggTextureCollection::
46 operator = (const EggTextureCollection &copy) {
47  _textures = copy._textures;
48  _ordered_textures = copy._ordered_textures;
49  return *this;
50 }
51 
52 /**
53  *
54  */
55 EggTextureCollection::
56 ~EggTextureCollection() {
57 }
58 
59 /**
60  * Removes all textures from the collection.
61  */
63 clear() {
64  _textures.clear();
65  _ordered_textures.clear();
66 }
67 
68 /**
69  * Walks the egg hierarchy beginning at the indicated node, and removes any
70  * EggTextures encountered in the hierarchy, adding them to the collection.
71  * Returns the number of EggTextures encountered.
72  */
75  // Since this traversal is destructive, we'll handle it within the
76  // EggGroupNode code.
77  return node->find_textures(this);
78 }
79 
80 
81 /**
82  * Returns true if there are no EggTexures in the collection, false otherwise.
83  */
85 is_empty() const {
86  return _ordered_textures.empty();
87 }
88 
89 /**
90  * Returns the number of EggTextures in the collection.
91  */
92 int EggTextureCollection::
93 get_num_textures() const {
94  return _ordered_textures.size();
95 }
96 
97 /**
98  * Returns the nth EggTexture in the collection.
99  */
101 get_texture(int index) const {
102  nassertr(index >= 0 && index < (int)_ordered_textures.size(), nullptr);
103 
104  return _ordered_textures[index];
105 }
106 
107 /**
108  * Adds a series of EggTexture nodes to the beginning of the indicated node to
109  * reflect each of the textures in the collection. Returns an iterator
110  * representing the first position after the newly inserted textures.
111  */
112 EggGroupNode::iterator EggTextureCollection::
114  return insert_textures(node, node->begin());
115 }
116 
117 /**
118  * Adds a series of EggTexture nodes to the beginning of the indicated node to
119  * reflect each of the textures in the collection. Returns an iterator
120  * representing the first position after the newly inserted textures.
121  */
122 EggGroupNode::iterator EggTextureCollection::
123 insert_textures(EggGroupNode *node, EggGroupNode::iterator position) {
124  OrderedTextures::iterator oti;
125  for (oti = _ordered_textures.begin();
126  oti != _ordered_textures.end();
127  ++oti) {
128  EggTexture *texture = (*oti);
129  position = node->insert(position, texture);
130  }
131 
132  return position;
133 }
134 
135 /**
136  * Walks the egg hierarchy beginning at the indicated node, looking for
137  * textures that are referenced by primitives but are not already members of
138  * the collection, adding them to the collection.
139  *
140  * If this is called following extract_textures(), it can be used to pick up
141  * any additional texture references that appeared in the egg hierarchy (but
142  * whose EggTexture node was not actually part of the hierarchy).
143  *
144  * If this is called in lieu of extract_textures(), it will fill up the
145  * collection with all of the referenced textures (and only the referenced
146  * textures), without destructively removing the EggTextures from the
147  * hierarchy.
148  *
149  * This also has the side effect of incrementing the internal usage count for
150  * a texture in the collection each time a texture reference is encountered.
151  * This side effect is taken advantage of by remove_unused_textures().
152  *
153  * And one more side effect: this function identifies the presence of
154  * multitexturing in the egg file, and calls multitexture_over() on each
155  * texture appropriately so that, after this call, you may expect
156  * get_multitexture_sort() to return a reasonable value for each texture.
157  */
160  int num_found = 0;
161 
162  if (node->is_of_type(EggPrimitive::get_class_type())) {
163  EggPrimitive *primitive = DCAST(EggPrimitive, node);
164 
165  int num_textures = primitive->get_num_textures();
166  for (int i = 0; i < num_textures; i++) {
167  EggTexture *tex = primitive->get_texture(i);
168 
169  Textures::iterator ti = _textures.find(tex);
170  if (ti == _textures.end()) {
171  // Here's a new texture!
172  num_found++;
173  _textures.insert(Textures::value_type(tex, 1));
174  _ordered_textures.push_back(tex);
175  } else {
176  // Here's a texture we'd already known about. Increment its usage
177  // count.
178  (*ti).second++;
179  }
180 
181  // Get the multitexture ordering right.
182  for (int j = 0; j < i; j++) {
183 /*
184  * The return value of this function will be false if there is some cycle in
185  * the texture layout order; e.g. A layers over B on one primitive, but B
186  * layers over A on another primitive. In that case the Egg Loader won't be
187  * able to assign a unique ordering between A and B, so it's probably an error
188  * worth reporting to the user--but we don't report it here, because this is a
189  * much lower-level function that gets called in other contexts too. That
190  * means it doesn't get reported at all, but too bad.
191  */
192  tex->multitexture_over(primitive->get_texture(j));
193  }
194  }
195 
196  } else if (node->is_of_type(EggGroupNode::get_class_type())) {
197  EggGroupNode *group = DCAST(EggGroupNode, node);
198 
199  EggGroupNode::iterator ci;
200  for (ci = group->begin(); ci != group->end(); ++ci) {
201  EggNode *child = *ci;
202 
203  num_found += find_used_textures(child);
204  }
205  }
206 
207  return num_found;
208 }
209 
210 /**
211  * Removes any textures from the collection that aren't referenced by any
212  * primitives in the indicated egg hierarchy. This also, incidentally, adds
213  * textures to the collection that had been referenced by primitives but had
214  * not previously appeared in the collection.
215  */
218  // We'll do this the easy way: First, we'll remove *all* the textures from
219  // the collection, and then we'll add back only those that appear in the
220  // hierarchy.
221  clear();
222  find_used_textures(node);
223 }
224 
225 /**
226  * Walks through the collection and collapses together any separate textures
227  * that are equivalent according to the indicated equivalence factor, eq (see
228  * EggTexture::is_equivalent_to()). The return value is the number of
229  * textures removed.
230  *
231  * This flavor of collapse_equivalent_textures() automatically adjusts all the
232  * primitives in the egg hierarchy to refer to the new texture pointers.
233  */
236  TextureReplacement removed;
237  int num_collapsed = collapse_equivalent_textures(eq, removed);
238 
239  // And now walk the egg hierarchy and replace any references to a removed
240  // texture with its replacement.
241  replace_textures(node, removed);
242 
243  return num_collapsed;
244 }
245 
246 /**
247  * Walks through the collection and collapses together any separate textures
248  * that are equivalent according to the indicated equivalence factor, eq (see
249  * EggTexture::is_equivalent_to()). The return value is the number of
250  * textures removed.
251  *
252  * This flavor of collapse_equivalent_textures() does not adjust any
253  * primitives in the egg hierarchy; instead, it fills up the 'removed' map
254  * with an entry for each removed texture, mapping it back to the equivalent
255  * retained texture. It's up to the user to then call replace_textures() with
256  * this map, if desired, to apply these changes to the egg hierarchy.
257  */
260  int num_collapsed = 0;
261 
262  typedef pset<PT_EggTexture, UniqueEggTextures> Collapser;
263  UniqueEggTextures uet(eq);
264  Collapser collapser(uet);
265 
266  // First, put all of the textures into the Collapser structure, to find out
267  // the unique textures.
268  OrderedTextures::const_iterator oti;
269  for (oti = _ordered_textures.begin();
270  oti != _ordered_textures.end();
271  ++oti) {
272  EggTexture *tex = (*oti);
273 
274  std::pair<Collapser::const_iterator, bool> result = collapser.insert(tex);
275  if (!result.second) {
276  // This texture is non-unique; another one was already there.
277  EggTexture *first = *(result.first);
278  removed.insert(TextureReplacement::value_type(tex, first));
279  num_collapsed++;
280  }
281  }
282 
283  // Now record all of the unique textures only.
284  clear();
285  Collapser::const_iterator ci;
286  for (ci = collapser.begin(); ci != collapser.end(); ++ci) {
287  add_texture(*ci);
288  }
289 
290  return num_collapsed;
291 }
292 
293 /**
294  * Walks the egg hierarchy, changing out any reference to a texture appearing
295  * on the left side of the map with its corresponding texture on the right
296  * side. This is most often done following a call to
297  * collapse_equivalent_textures(). It does not directly affect the
298  * Collection.
299  */
303  EggGroupNode::iterator ci;
304  for (ci = node->begin();
305  ci != node->end();
306  ++ci) {
307  EggNode *child = *ci;
308  if (child->is_of_type(EggPrimitive::get_class_type())) {
309  EggPrimitive *primitive = DCAST(EggPrimitive, child);
310  EggPrimitive::Textures new_textures;
311  EggPrimitive::Textures::const_iterator ti;
312  for (ti = primitive->_textures.begin();
313  ti != primitive->_textures.end();
314  ++ti) {
315  PT_EggTexture tex = (*ti);
316  TextureReplacement::const_iterator ri;
317  ri = replace.find(tex);
318  if (ri != replace.end()) {
319  // Here's a texture we want to replace.
320  new_textures.push_back((*ri).second);
321  } else {
322  new_textures.push_back(tex);
323  }
324  }
325  primitive->_textures.swap(new_textures);
326 
327  } else if (child->is_of_type(EggGroupNode::get_class_type())) {
328  EggGroupNode *group_child = DCAST(EggGroupNode, child);
329  replace_textures(group_child, replace);
330  }
331  }
332 }
333 
334 /**
335  * Guarantees that each texture in the collection has a unique TRef name.
336  * This is essential before writing an egg file.
337  */
340  NameUniquifier nu(".tref", "tref");
341 
342  OrderedTextures::const_iterator oti;
343  for (oti = _ordered_textures.begin();
344  oti != _ordered_textures.end();
345  ++oti) {
346  EggTexture *tex = (*oti);
347 
348  tex->set_name(nu.add_name(tex->get_name()));
349  }
350 }
351 
352 /**
353  * Sorts all the textures into alphabetical order by TRef name. Subsequent
354  * operations using begin()/end() will traverse in this sorted order.
355  */
358  sort(_ordered_textures.begin(), _ordered_textures.end(),
360 }
361 
362 /**
363  * Sorts all the textures into alphabetical order by the basename part
364  * (including extension) of the filename. Subsequent operations using
365  * begin()/end() will traverse in this sorted order.
366  */
369  sort(_ordered_textures.begin(), _ordered_textures.end(),
371 }
372 
373 /**
374  * Explicitly adds a new texture to the collection. Returns true if the
375  * texture was added, false if it was already there or if there was some
376  * error.
377  */
380  nassertr(_textures.size() == _ordered_textures.size(), false);
381 
382  PT_EggTexture new_tex = texture;
383 
384  Textures::const_iterator ti;
385  ti = _textures.find(new_tex);
386  if (ti != _textures.end()) {
387  // This texture is already a member of the collection.
388  return false;
389  }
390 
391  _textures.insert(Textures::value_type(new_tex, 0));
392  _ordered_textures.push_back(new_tex);
393 
394  nassertr(_textures.size() == _ordered_textures.size(), false);
395  return true;
396 }
397 
398 /**
399  * Explicitly removes a texture from the collection. Returns true if the
400  * texture was removed, false if it wasn't there or if there was some error.
401  */
404  nassertr(_textures.size() == _ordered_textures.size(), false);
405 
406  Textures::iterator ti;
407  ti = _textures.find(texture);
408  if (ti == _textures.end()) {
409  // This texture is not a member of the collection.
410  return false;
411  }
412 
413  _textures.erase(ti);
414 
415  OrderedTextures::iterator oti;
416  PT_EggTexture ptex = texture;
417  oti = find(_ordered_textures.begin(), _ordered_textures.end(), ptex);
418  nassertr(oti != _ordered_textures.end(), false);
419 
420  _ordered_textures.erase(oti);
421 
422  nassertr(_textures.size() == _ordered_textures.size(), false);
423  return true;
424 }
425 
426 /**
427  * Creates a new texture if there is not already one equivalent (according to
428  * eq, see EggTexture::is_equivalent_to()) to the indicated texture, or
429  * returns the existing one if there is.
430  */
432 create_unique_texture(const EggTexture &copy, int eq) {
433  // This requires a complete linear traversal, not terribly efficient.
434  OrderedTextures::const_iterator oti;
435  for (oti = _ordered_textures.begin();
436  oti != _ordered_textures.end();
437  ++oti) {
438  EggTexture *tex = (*oti);
439  if (copy.is_equivalent_to(*tex, eq)) {
440  // cout << "tex:" << tex->get_name() << "---copy:" << copy.get_name() <<
441  // endl;
442  return tex;
443  }
444  }
445  // cout << "adding a texture to collection: " << copy.get_name() << endl;
446  EggTexture *new_texture = new EggTexture(copy);
447  add_texture(new_texture);
448  return new_texture;
449 }
450 
451 /**
452  * Returns the texture with the indicated TRef name, or NULL if no texture
453  * matches.
454  */
456 find_tref(const std::string &tref_name) const {
457  // This requires a complete linear traversal, not terribly efficient.
458  OrderedTextures::const_iterator oti;
459  for (oti = _ordered_textures.begin();
460  oti != _ordered_textures.end();
461  ++oti) {
462  EggTexture *tex = (*oti);
463  if (tex->get_name() == tref_name) {
464  return tex;
465  }
466  }
467 
468  return nullptr;
469 }
470 
471 /**
472  * Returns the texture with the indicated filename, or NULL if no texture
473  * matches.
474  */
476 find_filename(const Filename &filename) const {
477  // This requires a complete linear traversal, not terribly efficient.
478  OrderedTextures::const_iterator oti;
479  for (oti = _ordered_textures.begin();
480  oti != _ordered_textures.end();
481  ++oti) {
482  EggTexture *tex = (*oti);
483  if (tex->get_filename() == filename) {
484  return tex;
485  }
486  }
487 
488  return nullptr;
489 }
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:47
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
void sort_by_tref()
Sorts all the textures into alphabetical order by TRef name.
int collapse_equivalent_textures(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate textures that are equivalent accordi...
void uniquify_trefs()
Guarantees that each texture in the collection has a unique TRef name.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
EggTexture * create_unique_texture(const EggTexture &copy, int eq)
Creates a new texture if there is not already one equivalent (according to eq, see EggTexture::is_equ...
bool multitexture_over(EggTexture *other)
Indicates that this texture should be layered on top of the other texture.
Definition: eggTexture.cxx:634
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void remove_unused_textures(EggNode *node)
Removes any textures from the collection that aren't referenced by any primitives in the indicated eg...
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
bool is_empty() const
Returns true if there are no EggTexures in the collection, false otherwise.
void clear()
Removes all textures from the collection.
This is a collection of textures by TRef name.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool add_texture(EggTexture *texture)
Explicitly adds a new texture to the collection.
int extract_textures(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggTextures encountered in t...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool remove_texture(EggTexture *texture)
Explicitly removes a texture from the collection.
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...
static void replace_textures(EggGroupNode *node, const TextureReplacement &replace)
Walks the egg hierarchy, changing out any reference to a texture appearing on the left side of the ma...
EggGroupNode::iterator insert_textures(EggGroupNode *node)
Adds a series of EggTexture nodes to the beginning of the indicated node to reflect each of the textu...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
An STL function object for sorting textures into order by properties.
Definition: eggTexture.h:461
void sort_by_basename()
Sorts all the textures into alphabetical order by the basename part (including extension) of the file...
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string add_name(const std::string &name)
If name is nonempty and so far unique, returns it unchanged.
get_num_textures
Returns the number of textures applied to the primitive.
Definition: eggPrimitive.h:100
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
EggTexture * find_tref(const std::string &tref_name) const
Returns the texture with the indicated TRef name, or NULL if no texture matches.
get_texture
Returns the first texture on the primitive, if any, or NULL if there are no textures on the primitive...
Definition: eggPrimitive.h:100
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
get_texture
Returns the nth EggTexture in the collection.
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggTexture * find_filename(const Filename &filename) const
Returns the texture with the indicated filename, or NULL if no texture matches.
bool is_equivalent_to(const EggTexture &other, int eq) const
Returns true if the two textures are equivalent in all relevant properties (according to eq),...
Definition: eggTexture.cxx:329
An STL function object for sorting an array of pointers to Namables into order by name.
Definition: namable.h:62