Panda3D
Loading...
Searching...
No Matches
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
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 */
27EggMaterialCollection::
28EggMaterialCollection() {
29}
30
31/**
32 *
33 */
34EggMaterialCollection::
35EggMaterialCollection(const EggMaterialCollection &copy) :
36 _materials(copy._materials),
37 _ordered_materials(copy._ordered_materials)
38{
39}
40
41/**
42 *
43 */
44EggMaterialCollection &EggMaterialCollection::
45operator = (const EggMaterialCollection &copy) {
46 _materials = copy._materials;
47 _ordered_materials = copy._ordered_materials;
48 return *this;
49}
50
51/**
52 *
53 */
54EggMaterialCollection::
55~EggMaterialCollection() {
56}
57
58/**
59 * Removes all materials from the collection.
60 */
62clear() {
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 */
84EggGroupNode::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 */
94EggGroupNode::iterator EggMaterialCollection::
95insert_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();
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 */
300sort_by_mref() {
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 */
311add_material(EggMaterial *material) {
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 */
335remove_material(EggMaterial *material) {
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 */
364create_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 */
386find_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 nodes in the hierarchy that are not leaf nodes.
This is a collection of materials by MRef name.
bool add_material(EggMaterial *material)
Explicitly adds a new material to the collection.
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...
void uniquify_mrefs()
Guarantees that each material in the collection has a unique MRef name.
int find_used_materials(EggNode *node)
Walks the egg hierarchy beginning at the indicated node, looking for materials that are referenced by...
int collapse_equivalent_materials(int eq, EggGroupNode *node)
Walks through the collection and collapses together any separate materials that are equivalent accord...
int extract_materials(EggGroupNode *node)
Walks the egg hierarchy beginning at the indicated node, and removes any EggMaterials encountered in ...
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...
bool remove_material(EggMaterial *material)
Explicitly removes a material from the collection.
void sort_by_mref()
Sorts all the materials into alphabetical order by MRef name.
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...
void clear()
Removes all materials from the collection.
EggMaterial * find_mref(const std::string &mref_name) const
Returns the material with the indicated MRef name, or NULL if no material matches.
void remove_unused_materials(EggNode *node)
Removes any materials from the collection that aren't referenced by any primitives in the indicated e...
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 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_material
Returns a pointer to the applied material, or NULL if there is no material applied.
set_material
Applies the indicated material to the primitive.
has_material
Returns true if the primitive is materiald (and get_material() will return a real pointer),...
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 materials into order by properties.
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.