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