open_toontown_panda3d/panda/src/egg/eggBinMaker.cxx

338 lines
11 KiB
C++

// Filename: eggBinMaker.cxx
// Created by: drose (21Jan99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "eggBinMaker.h"
#include "eggGroupNode.h"
#include "eggGroup.h"
#include "eggBin.h"
#include "dcast.h"
#include "pnotify.h"
TypeHandle EggBinMaker::_type_handle;
////////////////////////////////////////////////////////////////////
// Function: EggBinMakerCompareNodes::Function operator
// Access: Public
// Description: Called by the SortedNodes set to put nodes into bin
// order. Returns true if the first node falls into an
// earlier bin than the second node, false otherwise.
////////////////////////////////////////////////////////////////////
bool EggBinMakerCompareNodes::
operator ()(const EggNode *a, const EggNode *b) const {
int bin_number_a = _ebm->get_bin_number(a);
int bin_number_b = _ebm->get_bin_number(b);
if (bin_number_a != bin_number_b) {
// If the two nodes return different bin numbers, then they
// sort based on those numbers.
return bin_number_a < bin_number_b;
}
// The two nodes fell into the same bin number, so fall back on the
// comparison function to see if they should be differentiated.
return _ebm->sorts_less(bin_number_a, a, b);
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggBinMaker::
EggBinMaker() {
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
EggBinMaker::
~EggBinMaker() {
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::make_bins
// Access: Public
// Description: The main entry point to EggBinMaker. Walks the egg
// scene graph beginning at the indicated root node, and
// moves all binnable nodes into EggBin objects.
// Returns the number of EggBins created.
////////////////////////////////////////////////////////////////////
int EggBinMaker::
make_bins(EggGroupNode *root_group) {
_group_nodes.clear();
collect_nodes(root_group);
int num_bins = 0;
GroupNodes::const_iterator gi;
for (gi = _group_nodes.begin(); gi != _group_nodes.end(); ++gi) {
num_bins += get_bins_for_group(gi);
}
return num_bins;
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::prepare_node
// Access: Public, Virtual
// Description: May be overridden in derived classes to perform some
// setup work as each node is encountered. This will be
// called once for each node in the egg hierarchy.
////////////////////////////////////////////////////////////////////
void EggBinMaker::
prepare_node(EggNode *) {
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::sorts_less
// Access: Public, Virtual
// Description: May be overridden in derived classes to create
// additional bins within a particular bin number, based
// on some arbitrary property of nodes. This function
// establishes an arbitrary but fixed ordering between
// nodes; if two nodes do not sort to the same position,
// different bins are created for each one (with the
// same bin number on each bin).
////////////////////////////////////////////////////////////////////
bool EggBinMaker::
sorts_less(int, const EggNode *, const EggNode *) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::collapse_group
// Access: Public, Virtual
// Description: May be overridden in derived classes to specify
// whether a particular group node, apparently
// redundant, may be safely collapsed out.
////////////////////////////////////////////////////////////////////
bool EggBinMaker::
collapse_group(const EggGroup *, int) {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::get_bin_name
// Access: Public, Virtual
// Description: May be overridden in derived classes to define a name
// for each new bin, based on its bin number, and a
// sample child.
////////////////////////////////////////////////////////////////////
string EggBinMaker::
get_bin_name(int, const EggNode *) {
return string();
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::make_bin
// Access: Public, Virtual
// Description: May be overridden in derived classes to construct a
// new EggBin object (or some derived class, if needed),
// and preload some initial data into as required.
//
// child is an arbitrary child of the bin, and
// collapse_from is the group the bin is being collapsed
// with, if any (implying collapse_group() returned
// true), or NULL if not.
////////////////////////////////////////////////////////////////////
PT(EggBin) EggBinMaker::
make_bin(int, const EggNode *, EggGroup *collapse_from) {
if (collapse_from == (EggGroup *)NULL) {
return new EggBin;
} else {
return new EggBin(*collapse_from);
}
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::collect_nodes
// Access: Private
// Description: Walks the egg scene graph, identifying nodes to be
// binned and moving them from the scene graph into the
// internal bin structure.
////////////////////////////////////////////////////////////////////
void EggBinMaker::
collect_nodes(EggGroupNode *group) {
// We have to play games with this next iterator, because we might
// be destructively operating on the child list as we traverse it.
EggGroupNode::iterator i, next;
bool first_in_group = true;
GroupNodes::iterator gni = _group_nodes.end();
i = group->begin();
next = i;
while (i != group->end()) {
EggNode *node = (*i);
++next;
prepare_node(node);
if (get_bin_number(node) != 0) {
// Ok, here's a node to be binned. Add it to the appropriate
// bin.
if (first_in_group) {
// If this is the first time this group has been encountered,
// we need to create a new entry in _group_nodes for it.
pair<GroupNodes::iterator, bool> result;
result = _group_nodes.insert
(GroupNodes::value_type
(group, SortedNodes(EggBinMakerCompareNodes(this))));
nassertv(result.second);
gni = result.first;
first_in_group = false;
}
// Add this node to the set of all nodes being binned for the
// group. This also puts the nodes into bin order.
nassertv(gni != _group_nodes.end());
(*gni).second.insert(node);
// And remove it from the scene graph.
group->erase(i);
}
if (node->is_of_type(EggGroupNode::get_class_type())) {
// Recursively traverse.
collect_nodes(DCAST(EggGroupNode, node));
}
i = next;
}
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::get_bins_for_group
// Access: Private
// Description: Breaks the set of nodes for a given group up into
// individual bins.
////////////////////////////////////////////////////////////////////
int EggBinMaker::
get_bins_for_group(GroupNodes::const_iterator gi) {
EggGroupNode *group = (*gi).first;
const SortedNodes &nodes = (*gi).second;
// It shouldn't be possible for this to be empty.
nassertr(!nodes.empty(), 0);
Bins bins;
EggBinMakerCompareNodes cn(this);
SortedNodes::const_iterator sni, last;
sni = nodes.begin();
last = sni;
bins.push_back(Nodes());
bins.back().push_back(*sni);
++sni;
while (sni != nodes.end()) {
if (cn(*last, *sni)) {
// Begin a new bin.
bins.push_back(Nodes());
}
bins.back().push_back(*sni);
last = sni;
++sni;
}
make_bins_for_group(group, bins);
return bins.size();
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::make_bins_for_group
// Access: Private
// Description: Creates the EggBin nodes indicated by the internal
// bin structure for each group.
////////////////////////////////////////////////////////////////////
void EggBinMaker::
make_bins_for_group(EggGroupNode *group, const Bins &bins) {
// We shouldn't be able to get here if we have no bins!
nassertv(!bins.empty());
// If the group will have only one bin, and no other children, and
// the group is not the root node (and it is not some funny
// group-like node like a <Table>), maybe we should collapse the
// group and its bin together.
bool collapse = false;
if (group->empty() &&
bins.size() == 1 &&
group->get_parent() != NULL &&
group->is_of_type(EggGroup::get_class_type())) {
const Nodes &nodes = bins.front();
nassertv(!nodes.empty());
int bin_number = get_bin_number(nodes.front());
collapse = collapse_group(DCAST(EggGroup, group), bin_number);
}
if (collapse) {
const Nodes &nodes = bins.front();
nassertv(!nodes.empty());
int bin_number = get_bin_number(nodes.front());
PT(EggBin) bin = make_bin(bin_number, nodes.front(), DCAST(EggGroup, group));
setup_bin(bin, nodes);
EggGroupNode *parent = group->get_parent();
parent->remove_child(group);
parent->add_child(bin);
} else {
Bins::const_iterator bi;
for (bi = bins.begin(); bi != bins.end(); ++bi) {
const Nodes &nodes = (*bi);
nassertv(!nodes.empty());
int bin_number = get_bin_number(nodes.front());
PT(EggBin) bin = make_bin(bin_number, nodes.front(), NULL);
setup_bin(bin, nodes);
group->add_child(bin);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: EggBinMaker::setup_bin
// Access: Private
// Description: Sets up a recently-created EggBin structure with all
// of its children.
////////////////////////////////////////////////////////////////////
void EggBinMaker::
setup_bin(EggBin *bin, const Nodes &nodes) {
nassertv(!nodes.empty());
int bin_number = get_bin_number(nodes.front());
bin->set_bin_number(bin_number);
string bin_name = get_bin_name(bin_number, nodes.front());
if (!bin_name.empty()) {
bin->set_name(bin_name);
}
Nodes::const_iterator ni;
for (ni = nodes.begin(); ni != nodes.end(); ++ni) {
bin->add_child(*ni);
}
}