open_toontown_panda3d/panda/src/egg/eggBinMaker.h

324 lines
12 KiB
C++

// Filename: eggBinMaker.h
// 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."
//
////////////////////////////////////////////////////////////////////
#ifndef EGGBINMAKER_H
#define EGGBINMAKER_H
////////////////////////////////////////////////////////////////////
//
// EggBinMaker
//
// This is a handy class for collecting related nodes together. Its
// purpose is to make it easier to process egg files for converting to
// another scene graph format. Egg is very general and allows nodes
// to be parented willy-nilly anywhere you like, while many other
// scene graph formats have requirements that certain kinds of nodes
// be grouped together.
//
// Although EggBinMaker can be used to group any kinds of nodes
// together, one of the most common examples is grouping polygons into
// polysets. Egg allows individual polygons to be parented directly
// to any group node, while most scene graph formats prefer to have
// polygons with similar attributes grouped into some kind of a
// polyset node. Therefore, the following usage discussion will use
// grouping polygons into polysets as an example.
//
// EggBinMaker is actually an abstract class; it cannot be used
// directly. To use it, you must create a subclass and redefine some
// or all of its virtual functions to specify the precise behavior you
// require.
//
// You must define at least the following function:
//
// virtual int get_bin_number(const EggNode *node);
//
// This function identifies the kinds of nodes in the graph, for
// instance EggPolygons, that are to be put into bins. It will be
// called once for each node encountered, and it should return
// nonzero if the node is to be binned, and zero otherwise. To
// group polygons into polysets, this function might look like:
//
// virtual int get_bin_number(const EggNode *node) {
// if (node->is_of_type(EggPolygon::get_class_type())) {
// return 1;
// } else {
// return 0;
// }
// }
//
//
// This function may also return the bin number that a given node
// should be dropped into. The bin number is completely arbitrary,
// and it just serves to differentiate different bins.
//
// By default, all sibling nodes will be dropped into the same bin;
// you can redefine this to sort nodes further into categories.
// For instance, if you wanted to put textured polygons into a
// different polyset than untextured polygons, you might define
// this function as follows:
//
// virtual int get_bin_number(const EggNode *node) {
// if (node->is_of_type(EggPolygon::get_class_type())) {
// EggPolygon *poly = DCAST(EggPolygon, node);
// return (poly->has_texture()) ? 1 : 2;
// } else {
// return 0;
// }
// }
//
// Of course, unrelated nodes--nodes that belong to different
// parents--will never be placed into the same bin together,
// regardless of the bin number.
//
// It is important to note that it is not necessarily true that
// there is only one bin for each bin number. If you redefine
// sorts_less(), below, you provide a finer-grained control that
// may create multiple bins for a given bin number.
//
// This function may be called several times for a given node, and
// it should return the same number each time.
//
//
// You may also redefine any or all of the following functions:
//
// virtual void prepare_node(EggNode *node);
//
// This method is called, once, on each node in the egg hierarchy
// as it is visited the first time. It allows the subclass a
// chance to analyze the node or do any other initial processing.
// This is a fine opportunity to tag an EggUserData onto the node,
// for instance.
//
// virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b);
//
// Sometimes a simple bin number alone is not enough. For
// instance, suppose you needed to group together not just all
// textured polygons, but all polygons that shared a particular
// texture map. Two polygons that are each textured with a
// different texture map should go into different polysets. To do
// this with bin numbers, you'd have to know ahead of time all the
// texture maps that are in use, and assign a unique number to each
// one.
//
// sorts_less() can make this unnecessary. It's a finer-grained
// sorting than by bin numbers. Once two nodes have been grouped
// together into the same bin number, sorts_less is called on them.
// If it returns true, then node a should be placed into an earlier
// bin than node b, even though they share the same bin number. If
// sorts_less(a, b) and sorts_less(b, a) both return false, then
// nodes a and b are placed into the same bin.
//
// To continue the example, and sort polygons into different bins
// based on the texture map:
//
// virtual bool sorts_less(int bin_number,
// const EggNode *a, const EggNode *b) {
// if (bin_number == 2) {
// // bin 2, textured geometry
// return (a->get_texture() < b->get_texture());
// } else {
// // bin 1, untextured geometry
// return false;
// }
// }
//
// The actual comparison can be arbitrary, as long as it is
// consistent. Its only purpose is to assign some ordering among
// bins. In the example, for instance, the comparison is based on
// the pointer to the texture maps--it doesn't matter which comes
// before the other, as long as it's consistent.
//
// In particular, it should never be true that sorts_less(a, b) and
// sorts_less(b, a) both return true--that is a clear
// contradiction.
//
// Of course, if you're using sorts_less() anyway, you could put
// *all* of the logic for binning into this function; there's no
// need to use both get_bin_number() and sorts_less(), necessarily.
// In the current example, here's another version of sorts_less()
// that accomplishes the same thing as the combined effects of the
// above get_bin_number() and sorts_less() working together:
//
// virtual bool sorts_less(int bin_number,
// const EggNode *a, const EggNode *b) {
// if (a->has_texture() != b->has_texture()) {
// return ((int)a->has_texture() < (int)b->has_texture());
// }
// if (a->has_texture()) {
// return (a->get_texture() < b->get_texture());
// }
// return false;
// }
//
//
// virtual bool collapse_group(const EggGroup *group, int bin_number);
//
// After all the nodes have been assigned to bins and the
// individual bins (polysets) have been created, it might turn out
// that some groups have had all their children placed into the
// same bin. In this case, the group node is now redundant, since
// it contains just the one child, the new EggBin (polyset) node.
// It might be advantageous to remove the group and collapse its
// properties into the new node.
//
// In this case (and this case only), collapse_group() will be
// called, given the node and the bin number. If it returns true,
// the node will indeed be collapsed into its bin; otherwise, they
// will be left separate.
//
// The point is that there might be some attributes in the group
// node (for instance, a matrix transform) that cannot be
// represented in a polyset node in the new scene graph format, so
// there may be some cases in which the group cannot be safely
// collapsed. Since the egg library cannot know about which such
// cases cause problems, it leaves it up to you. The default
// behavior is never to collapse nodes.
//
//
// virtual string get_bin_name(int bin_number, EggNode *child);
//
// This function is called as each new bin is created, to
// optionally define a name for the new node. If it returns the
// empty string, the node name will be empty, unless it was
// collapsed with its parent group, in which case it will inherit
// its former parent's name.
//
//
//
// Once you have subclassed EggBinMaker and defined the functions as
// you require, you use it by simply calling make_bins() one or more
// times, passing it the pointer to the root of the scene graph or of
// some subgraph. It will traverse the subgraph and create a series
// of EggBin objects, as required, moving all the binned geometry
// under the EggBin objects. The return value is the number of
// EggBins created. Each EggBin stores its bin number, which may be
// retrieved via get_bin_number().
//
////////////////////////////////////////////////////////////////////
#include "pandabase.h"
#include "eggObject.h"
#include "pointerTo.h"
#include "pnotify.h"
#include "pset.h"
#include "pmap.h"
class EggNode;
class EggGroup;
class EggGroupNode;
class EggBin;
class EggBinMaker;
////////////////////////////////////////////////////////////////////
// Class : EggBinMakerCompareNodes
// Description : This is just an STL function object, used to sort
// nodes within EggBinMaker. It's part of the private
// interface; ignore it.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEGG EggBinMakerCompareNodes {
public:
EggBinMakerCompareNodes() {
// We need to have a default constructor to compile, but it should
// never be called.
nassertv(false);
}
EggBinMakerCompareNodes(EggBinMaker *ebm) : _ebm(ebm) { }
bool operator ()(const EggNode *a, const EggNode *b) const;
EggBinMaker *_ebm;
};
////////////////////////////////////////////////////////////////////
// Class : EggBinMaker
// Description : This is a handy class for collecting related nodes
// together. It is an abstract class; to use it you
// must subclass off of it. See the somewhat lengthy
// comment above.
////////////////////////////////////////////////////////////////////
class EXPCL_PANDAEGG EggBinMaker : public EggObject {
PUBLISHED:
EggBinMaker();
~EggBinMaker();
int make_bins(EggGroupNode *root_group);
virtual void
prepare_node(EggNode *node);
virtual int
get_bin_number(const EggNode *node)=0;
virtual bool
sorts_less(int bin_number, const EggNode *a, const EggNode *b);
virtual bool
collapse_group(const EggGroup *group, int bin_number);
virtual string
get_bin_name(int bin_number, const EggNode *child);
virtual PT(EggBin)
make_bin(int bin_number, const EggNode *child, EggGroup *collapse_from);
private:
// The logic is two-pass. First, we make a scene graph traversal
// and store all the pointers into the GroupNodes/SortedNodes
// structure, which groups nodes by their parent group, and then
// sorted into bin order.
typedef pmultiset<PT(EggNode), EggBinMakerCompareNodes> SortedNodes;
typedef pmap<EggGroupNode *, SortedNodes> GroupNodes;
// Then we walk through that list and create a Bins/Nodes structure
// for each group, which separates out the nodes into the individual
// bins.
typedef pvector< PT(EggNode) > Nodes;
typedef pvector<Nodes> Bins;
void collect_nodes(EggGroupNode *group);
int get_bins_for_group(GroupNodes::const_iterator gi);
void make_bins_for_group(EggGroupNode *group, const Bins &bins);
void setup_bin(EggBin *bin, const Nodes &nodes);
GroupNodes _group_nodes;
public:
static TypeHandle get_class_type() {
return _type_handle;
}
static void init_type() {
EggObject::init_type();
register_type(_type_handle, "EggBinMaker",
EggObject::get_class_type());
}
virtual TypeHandle get_type() const {
return get_class_type();
}
virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
private:
static TypeHandle _type_handle;
};
#endif