open_toontown_panda3d/panda/src/egg/eggMaterialCollection.cxx

465 lines
17 KiB
C++

// Filename: eggMaterialCollection.cxx
// Created by: drose (30Apr01)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "eggMaterialCollection.h"
#include "eggGroupNode.h"
#include "eggPrimitive.h"
#include "eggMaterial.h"
#include "nameUniquifier.h"
#include "dcast.h"
#include <algorithm>
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggMaterialCollection::
EggMaterialCollection() {
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggMaterialCollection::
EggMaterialCollection(const EggMaterialCollection &copy) :
_materials(copy._materials),
_ordered_materials(copy._ordered_materials)
{
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggMaterialCollection &EggMaterialCollection::
operator = (const EggMaterialCollection &copy) {
_materials = copy._materials;
_ordered_materials = copy._ordered_materials;
return *this;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggMaterialCollection::
~EggMaterialCollection() {
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::clear
// Access: Public
// Description: Removes all materials from the collection.
////////////////////////////////////////////////////////////////////
void EggMaterialCollection::
clear() {
_materials.clear();
_ordered_materials.clear();
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::extract_materials
// Access: Public
// Description: Walks the egg hierarchy beginning at the indicated
// node, and removes any EggMaterials encountered in the
// hierarchy, adding them to the collection. Returns
// the number of EggMaterials encountered.
////////////////////////////////////////////////////////////////////
int EggMaterialCollection::
extract_materials(EggGroupNode *node) {
// Since this traversal is destructive, we'll handle it within the
// EggGroupNode code.
return node->find_materials(this);
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::insert_materials
// Access: Public
// Description: Adds a series of EggMaterial nodes to the beginning of
// the indicated node to reflect each of the materials in
// the collection. Returns an iterator representing the
// first position after the newly inserted materials.
////////////////////////////////////////////////////////////////////
EggGroupNode::iterator EggMaterialCollection::
insert_materials(EggGroupNode *node) {
return insert_materials(node, node->begin());
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::insert_materials
// Access: Public
// Description: Adds a series of EggMaterial nodes to the beginning of
// the indicated node to reflect each of the materials in
// the collection. Returns an iterator representing the
// first position after the newly inserted materials.
////////////////////////////////////////////////////////////////////
EggGroupNode::iterator EggMaterialCollection::
insert_materials(EggGroupNode *node, EggGroupNode::iterator position) {
OrderedMaterials::iterator oti;
for (oti = _ordered_materials.begin();
oti != _ordered_materials.end();
++oti) {
EggMaterial *material = (*oti);
position = node->insert(position, material);
}
return position;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::find_used_materials
// Access: Public
// Description: Walks the egg hierarchy beginning at the indicated
// node, looking for materials that are referenced by
// primitives but are not already members of the
// collection, adding them to the collection.
//
// If this is called following extract_materials(), it
// can be used to pick up any additional material
// references that appeared in the egg hierarchy (but
// whose EggMaterial node was not actually part of the
// hierarchy).
//
// If this is called in lieu of extract_materials(), it
// will fill up the collection with all of the
// referenced materials (and only the referenced
// materials), without destructively removing the
// EggMaterials from the hierarchy.
//
// This also has the side effect of incrementing the
// internal usage count for a material in the collection
// each time a material reference is encountered. This
// side effect is taken advantage of by
// remove_unused_materials().
////////////////////////////////////////////////////////////////////
int EggMaterialCollection::
find_used_materials(EggNode *node) {
int num_found = 0;
if (node->is_of_type(EggPrimitive::get_class_type())) {
EggPrimitive *primitive = DCAST(EggPrimitive, node);
if (primitive->has_material()) {
EggMaterial *tex = primitive->get_material();
Materials::iterator ti = _materials.find(tex);
if (ti == _materials.end()) {
// Here's a new material!
num_found++;
_materials.insert(Materials::value_type(tex, 1));
_ordered_materials.push_back(tex);
} else {
// Here's a material we'd already known about. Increment its
// usage count.
(*ti).second++;
}
}
} else if (node->is_of_type(EggGroupNode::get_class_type())) {
EggGroupNode *group = DCAST(EggGroupNode, node);
EggGroupNode::iterator ci;
for (ci = group->begin(); ci != group->end(); ++ci) {
EggNode *child = *ci;
num_found += find_used_materials(child);
}
}
return num_found;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::remove_unused_materials
// Access: Public
// Description: Removes any materials from the collection that aren't
// referenced by any primitives in the indicated egg
// hierarchy. This also, incidentally, adds materials to
// the collection that had been referenced by primitives
// but had not previously appeared in the collection.
////////////////////////////////////////////////////////////////////
void EggMaterialCollection::
remove_unused_materials(EggNode *node) {
// We'll do this the easy way: First, we'll remove *all* the
// materials from the collection, and then we'll add back only those
// that appear in the hierarchy.
clear();
find_used_materials(node);
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::collapse_equivalent_materials
// Access: Public
// Description: Walks through the collection and collapses together
// any separate materials that are equivalent according
// to the indicated equivalence factor, eq (see
// EggMaterial::is_equivalent_to()). The return value is
// the number of materials removed.
//
// This flavor of collapse_equivalent_materials()
// automatically adjusts all the primitives in the egg
// hierarchy to refer to the new material pointers.
////////////////////////////////////////////////////////////////////
int EggMaterialCollection::
collapse_equivalent_materials(int eq, EggGroupNode *node) {
MaterialReplacement removed;
int num_collapsed = collapse_equivalent_materials(eq, removed);
// And now walk the egg hierarchy and replace any references to a
// removed material with its replacement.
replace_materials(node, removed);
return num_collapsed;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::collapse_equivalent_materials
// Access: Public
// Description: Walks through the collection and collapses together
// any separate materials that are equivalent according
// to the indicated equivalence factor, eq (see
// EggMaterial::is_equivalent_to()). The return value is
// the number of materials removed.
//
// This flavor of collapse_equivalent_materials() does
// not adjust any primitives in the egg hierarchy;
// instead, it fills up the 'removed' map with an entry
// for each removed material, mapping it back to the
// equivalent retained material. It's up to the user to
// then call replace_materials() with this map, if
// desired, to apply these changes to the egg hierarchy.
////////////////////////////////////////////////////////////////////
int EggMaterialCollection::
collapse_equivalent_materials(int eq, EggMaterialCollection::MaterialReplacement &removed) {
int num_collapsed = 0;
typedef pset<PT(EggMaterial), UniqueEggMaterials> Collapser;
UniqueEggMaterials uet(eq);
Collapser collapser(uet);
// First, put all of the materials into the Collapser structure, to
// find out the unique materials.
OrderedMaterials::const_iterator oti;
for (oti = _ordered_materials.begin();
oti != _ordered_materials.end();
++oti) {
EggMaterial *tex = (*oti);
pair<Collapser::const_iterator, bool> result = collapser.insert(tex);
if (!result.second) {
// This material is non-unique; another one was already there.
EggMaterial *first = *(result.first);
removed.insert(MaterialReplacement::value_type(tex, first));
num_collapsed++;
}
}
// Now record all of the unique materials only.
clear();
Collapser::const_iterator ci;
for (ci = collapser.begin(); ci != collapser.end(); ++ci) {
add_material(*ci);
}
return num_collapsed;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::replace_materials
// Access: Public, Static
// Description: Walks the egg hierarchy, changing out any reference
// to a material appearing on the left side of the map
// with its corresponding material on the right side.
// This is most often done following a call to
// collapse_equivalent_materials(). It does not directly
// affect the Collection.
////////////////////////////////////////////////////////////////////
void EggMaterialCollection::
replace_materials(EggGroupNode *node,
const EggMaterialCollection::MaterialReplacement &replace) {
EggGroupNode::iterator ci;
for (ci = node->begin();
ci != node->end();
++ci) {
EggNode *child = *ci;
if (child->is_of_type(EggPrimitive::get_class_type())) {
EggPrimitive *primitive = DCAST(EggPrimitive, child);
if (primitive->has_material()) {
PT(EggMaterial) tex = primitive->get_material();
MaterialReplacement::const_iterator ri;
ri = replace.find(tex);
if (ri != replace.end()) {
// Here's a material we want to replace.
primitive->set_material((*ri).second);
}
}
} else if (child->is_of_type(EggGroupNode::get_class_type())) {
EggGroupNode *group_child = DCAST(EggGroupNode, child);
replace_materials(group_child, replace);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::uniquify_mrefs
// Access: Public
// Description: Guarantees that each material in the collection has a
// unique MRef name. This is essential before writing
// an egg file.
////////////////////////////////////////////////////////////////////
void EggMaterialCollection::
uniquify_mrefs() {
NameUniquifier nu(".mref", "mref");
OrderedMaterials::const_iterator oti;
for (oti = _ordered_materials.begin();
oti != _ordered_materials.end();
++oti) {
EggMaterial *tex = (*oti);
tex->set_name(nu.add_name(tex->get_name()));
}
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::sort_by_mref
// Access: Public
// Description: Sorts all the materials into alphabetical order by
// MRef name. Subsequent operations using begin()/end()
// will traverse in this sorted order.
////////////////////////////////////////////////////////////////////
void EggMaterialCollection::
sort_by_mref() {
sort(_ordered_materials.begin(), _ordered_materials.end(),
NamableOrderByName());
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::add_material
// Access: Public
// Description: Explicitly adds a new material to the collection.
// Returns true if the material was added, false if it
// was already there or if there was some error.
////////////////////////////////////////////////////////////////////
bool EggMaterialCollection::
add_material(EggMaterial *material) {
nassertr(_materials.size() == _ordered_materials.size(), false);
PT(EggMaterial) new_tex = material;
Materials::const_iterator ti;
ti = _materials.find(new_tex);
if (ti != _materials.end()) {
// This material is already a member of the collection.
return false;
}
_materials.insert(Materials::value_type(new_tex, 0));
_ordered_materials.push_back(new_tex);
nassertr(_materials.size() == _ordered_materials.size(), false);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::remove_material
// Access: Public
// Description: Explicitly removes a material from the collection.
// Returns true if the material was removed, false if it
// wasn't there or if there was some error.
////////////////////////////////////////////////////////////////////
bool EggMaterialCollection::
remove_material(EggMaterial *material) {
nassertr(_materials.size() == _ordered_materials.size(), false);
Materials::iterator ti;
ti = _materials.find(material);
if (ti == _materials.end()) {
// This material is not a member of the collection.
return false;
}
_materials.erase(ti);
OrderedMaterials::iterator oti;
PT(EggMaterial) ptex = material;
oti = find(_ordered_materials.begin(), _ordered_materials.end(), ptex);
nassertr(oti != _ordered_materials.end(), false);
_ordered_materials.erase(oti);
nassertr(_materials.size() == _ordered_materials.size(), false);
return true;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::create_unique_material
// Access: Public
// Description: Creates a new material if there is not already one
// equivalent (according to eq, see
// EggMaterial::is_equivalent_to()) to the indicated
// material, or returns the existing one if there is.
////////////////////////////////////////////////////////////////////
EggMaterial *EggMaterialCollection::
create_unique_material(const EggMaterial &copy, int eq) {
// This requires a complete linear traversal, not terribly
// efficient.
OrderedMaterials::const_iterator oti;
for (oti = _ordered_materials.begin();
oti != _ordered_materials.end();
++oti) {
EggMaterial *tex = (*oti);
if (copy.is_equivalent_to(*tex, eq)) {
return tex;
}
}
EggMaterial *new_material = new EggMaterial(copy);
add_material(new_material);
return new_material;
}
////////////////////////////////////////////////////////////////////
// Function: EggMaterialCollection::find_mref
// Access: Public
// Description: Returns the material with the indicated MRef name, or
// NULL if no material matches.
////////////////////////////////////////////////////////////////////
EggMaterial *EggMaterialCollection::
find_mref(const string &mref_name) const {
// This requires a complete linear traversal, not terribly
// efficient.
OrderedMaterials::const_iterator oti;
for (oti = _ordered_materials.begin();
oti != _ordered_materials.end();
++oti) {
EggMaterial *tex = (*oti);
if (tex->get_name() == mref_name) {
return tex;
}
}
return (EggMaterial *)NULL;
}