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  */
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  */
339 uniquify_trefs() {
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  */
357 sort_by_tref() {
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  */
379 add_texture(EggTexture *texture) {
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  */
403 remove_texture(EggTexture *texture) {
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 }
const Filename & get_filename() const
Returns a nonmodifiable reference to the filename.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:49
get_num_textures
Returns the number of textures applied to the primitive.
Definition: eggPrimitive.h:100
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
This is a collection of textures by TRef name.
bool add_texture(EggTexture *texture)
Explicitly adds a new texture to the collection.
bool is_empty() const
Returns true if there are no EggTexures in the collection, false otherwise.
EggTexture * find_tref(const std::string &tref_name) const
Returns the texture with the indicated TRef name, or NULL if no texture matches.
void sort_by_tref()
Sorts all the textures into alphabetical order by TRef name.
get_texture
Returns the nth EggTexture in the collection.
int find_used_textures(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for textures that are referenced by ...
EggTexture * find_filename(const Filename &filename) const
Returns the texture with the indicated filename, or NULL if no texture matches.
int collapse_equivalent_textures(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate textures that are equivalent accordi...
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...
void clear()
Removes all textures from the collection.
int extract_textures(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggTextures encountered in t...
void uniquify_trefs()
Guarantees that each texture in the collection has a unique TRef name.
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...
void remove_unused_textures(EggNode *node)
Removes any textures from the collection that aren't referenced by any primitives in the indicated eg...
void sort_by_basename()
Sorts all the textures into alphabetical order by the basename part (including extension) of the file...
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...
get_num_textures
Returns the number of EggTextures in the collection.
bool remove_texture(EggTexture *texture)
Explicitly removes a texture from the collection.
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
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
bool multitexture_over(EggTexture *other)
Indicates that this texture should be layered on top of the other texture.
Definition: eggTexture.cxx:637
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
An STL function object for sorting an array of pointers to Namables into order by name.
Definition: namable.h:62
A handy class for converting a list of arbitrary names (strings) so that each name is guaranteed to b...
std::string add_name(const std::string &name)
If name is nonempty and so far unique, returns it unchanged.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
An STL function object for sorting textures into order by properties.
Definition: eggTexture.h:465
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.