historical/toontown-classic.git/panda/include/eggBinMaker.h
2024-01-16 11:20:27 -06:00

226 lines
10 KiB
C++

/**
* 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."
*
* @file eggBinMaker.h
* @author drose
* @date 1999-01-21
*/
#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;
/**
* This is just an STL function object, used to sort nodes within EggBinMaker.
* It's part of the private interface; ignore it.
*/
class EXPCL_PANDA_EGG 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;
};
/**
* 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_PANDA_EGG 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 std::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 GroupNodesSortedNodes 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 BinsNodes 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