Panda3D
Loading...
Searching...
No Matches
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
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 */
28EggTextureCollection::
29EggTextureCollection() {
30}
31
32/**
33 *
34 */
35EggTextureCollection::
36EggTextureCollection(const EggTextureCollection &copy) :
37 _textures(copy._textures),
38 _ordered_textures(copy._ordered_textures)
39{
40}
41
42/**
43 *
44 */
45EggTextureCollection &EggTextureCollection::
46operator = (const EggTextureCollection &copy) {
47 _textures = copy._textures;
48 _ordered_textures = copy._ordered_textures;
49 return *this;
50}
51
52/**
53 *
54 */
55EggTextureCollection::
56~EggTextureCollection() {
57}
58
59/**
60 * Removes all textures from the collection.
61 */
63clear() {
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 */
85is_empty() const {
86 return _ordered_textures.empty();
87}
88
89/**
90 * Returns the number of EggTextures in the collection.
91 */
93get_num_textures() const {
94 return _ordered_textures.size();
95}
96
97/**
98 * Returns the nth EggTexture in the collection.
99 */
101get_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 */
112EggGroupNode::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 */
122EggGroupNode::iterator EggTextureCollection::
123insert_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
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 */
357sort_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 */
379add_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 */
403remove_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 */
432create_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 */
456find_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 */
476find_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.
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,...
get_num_textures
Returns the number of textures applied to the primitive.
get_texture
Returns the first texture on the primitive, if any, or NULL if there are no textures on the primitive...
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),...
bool multitexture_over(EggTexture *other)
Indicates that this texture should be layered on top of the other texture.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
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.