Panda3D
eggBinMaker.h
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 eggBinMaker.h
10  * @author drose
11  * @date 1999-01-21
12  */
13 
14 #ifndef EGGBINMAKER_H
15 #define EGGBINMAKER_H
16 
17 /*
18  * EggBinMaker This is a handy class for collecting related nodes together.
19  * Its purpose is to make it easier to process egg files for converting to
20  * another scene graph format. Egg is very general and allows nodes to be
21  * parented willy-nilly anywhere you like, while many other scene graph
22  * formats have requirements that certain kinds of nodes be grouped together.
23  * Although EggBinMaker can be used to group any kinds of nodes together, one
24  * of the most common examples is grouping polygons into polysets. Egg allows
25  * individual polygons to be parented directly to any group node, while most
26  * scene graph formats prefer to have polygons with similar attributes grouped
27  * into some kind of a polyset node. Therefore, the following usage
28  * discussion will use grouping polygons into polysets as an example.
29  * EggBinMaker is actually an abstract class; it cannot be used directly. To
30  * use it, you must create a subclass and redefine some or all of its virtual
31  * functions to specify the precise behavior you require. You must define at
32  * least the following function: virtual int get_bin_number(const EggNode
33  * *node); This function identifies the kinds of nodes in the graph, for
34  * instance EggPolygons, that are to be put into bins. It will be called once
35  * for each node encountered, and it should return nonzero if the node is to
36  * be binned, and zero otherwise. To group polygons into polysets, this
37  * function might look like: virtual int get_bin_number(const EggNode *node) {
38  * if (node->is_of_type(EggPolygon::get_class_type())) { return 1; } else {
39  * return 0; } } This function may also return the bin number that a given
40  * node should be dropped into. The bin number is completely arbitrary, and
41  * it just serves to differentiate different bins. By default, all sibling
42  * nodes will be dropped into the same bin; you can redefine this to sort
43  * nodes further into categories. For instance, if you wanted to put textured
44  * polygons into a different polyset than untextured polygons, you might
45  * define this function as follows: virtual int get_bin_number(const EggNode
46  * *node) { if (node->is_of_type(EggPolygon::get_class_type())) { EggPolygon
47  * *poly = DCAST(EggPolygon, node); return (poly->has_texture()) ? 1 : 2; }
48  * else { return 0; } } Of course, unrelated nodes--nodes that belong to
49  * different parents--will never be placed into the same bin together,
50  * regardless of the bin number. It is important to note that it is not
51  * necessarily true that there is only one bin for each bin number. If you
52  * redefine sorts_less(), below, you provide a finer-grained control that may
53  * create multiple bins for a given bin number. This function may be called
54  * several times for a given node, and it should return the same number each
55  * time. You may also redefine any or all of the following functions: virtual
56  * void prepare_node(EggNode *node); This method is called, once, on each node
57  * in the egg hierarchy as it is visited the first time. It allows the
58  * subclass a chance to analyze the node or do any other initial processing.
59  * This is a fine opportunity to tag an EggUserData onto the node, for
60  * instance. virtual bool sorts_less(int bin_number, const EggNode *a, const
61  * EggNode *b); Sometimes a simple bin number alone is not enough. For
62  * instance, suppose you needed to group together not just all textured
63  * polygons, but all polygons that shared a particular texture map. Two
64  * polygons that are each textured with a different texture map should go into
65  * different polysets. To do this with bin numbers, you'd have to know ahead
66  * of time all the texture maps that are in use, and assign a unique number to
67  * each one. sorts_less() can make this unnecessary. It's a finer-grained
68  * sorting than by bin numbers. Once two nodes have been grouped together
69  * into the same bin number, sorts_less is called on them. If it returns
70  * true, then node a should be placed into an earlier bin than node b, even
71  * though they share the same bin number. If sorts_less(a, b) and
72  * sorts_less(b, a) both return false, then nodes a and b are placed into the
73  * same bin. To continue the example, and sort polygons into different bins
74  * based on the texture map: virtual bool sorts_less(int bin_number, const
75  * EggNode *a, const EggNode *b) { if (bin_number == 2) { bin 2, textured
76  * geometry return (a->get_texture() < b->get_texture()); } else { bin 1,
77  * untextured geometry return false; } } The actual comparison can be
78  * arbitrary, as long as it is consistent. Its only purpose is to assign some
79  * ordering among bins. In the example, for instance, the comparison is based
80  * on the pointer to the texture maps--it doesn't matter which comes before
81  * the other, as long as it's consistent. In particular, it should never be
82  * true that sorts_less(a, b) and sorts_less(b, a) both return true--that is a
83  * clear contradiction. Of course, if you're using sorts_less() anyway, you
84  * could put *all* of the logic for binning into this function; there's no
85  * need to use both get_bin_number() and sorts_less(), necessarily. In the
86  * current example, here's another version of sorts_less() that accomplishes
87  * the same thing as the combined effects of the above get_bin_number() and
88  * sorts_less() working together: virtual bool sorts_less(int bin_number,
89  * const EggNode *a, const EggNode *b) { if (a->has_texture() !=
90  * b->has_texture()) { return ((int)a->has_texture() < (int)b->has_texture());
91  * } if (a->has_texture()) { return (a->get_texture() < b->get_texture()); }
92  * return false; } virtual bool collapse_group(const EggGroup *group, int
93  * bin_number); After all the nodes have been assigned to bins and the
94  * individual bins (polysets) have been created, it might turn out that some
95  * groups have had all their children placed into the same bin. In this case,
96  * the group node is now redundant, since it contains just the one child, the
97  * new EggBin (polyset) node. It might be advantageous to remove the group
98  * and collapse its properties into the new node. In this case (and this case
99  * only), collapse_group() will be called, given the node and the bin number.
100  * If it returns true, the node will indeed be collapsed into its bin;
101  * otherwise, they will be left separate. The point is that there might be
102  * some attributes in the group node (for instance, a matrix transform) that
103  * cannot be represented in a polyset node in the new scene graph format, so
104  * there may be some cases in which the group cannot be safely collapsed.
105  * Since the egg library cannot know about which such cases cause problems, it
106  * leaves it up to you. The default behavior is never to collapse nodes.
107  * virtual string get_bin_name(int bin_number, EggNode *child); This function
108  * is called as each new bin is created, to optionally define a name for the
109  * new node. If it returns the empty string, the node name will be empty,
110  * unless it was collapsed with its parent group, in which case it will
111  * inherit its former parent's name. Once you have subclassed EggBinMaker and
112  * defined the functions as you require, you use it by simply calling
113  * make_bins() one or more times, passing it the pointer to the root of the
114  * scene graph or of some subgraph. It will traverse the subgraph and create
115  * a series of EggBin objects, as required, moving all the binned geometry
116  * under the EggBin objects. The return value is the number of EggBins
117  * created. Each EggBin stores its bin number, which may be retrieved via
118  * get_bin_number().
119  */
120 
121 
122 #include "pandabase.h"
123 
124 #include "eggObject.h"
125 
126 #include "pointerTo.h"
127 #include "pnotify.h"
128 
129 #include "pset.h"
130 #include "pmap.h"
131 
132 class EggNode;
133 class EggGroup;
134 class EggGroupNode;
135 class EggBin;
136 class EggBinMaker;
137 
138 /**
139  * This is just an STL function object, used to sort nodes within EggBinMaker.
140  * It's part of the private interface; ignore it.
141  */
142 class EXPCL_PANDA_EGG EggBinMakerCompareNodes {
143 public:
145  // We need to have a default constructor to compile, but it should never
146  // be called.
147  nassertv(false);
148  }
149  EggBinMakerCompareNodes(EggBinMaker *ebm) : _ebm(ebm) { }
150  bool operator ()(const EggNode *a, const EggNode *b) const;
151 
152  EggBinMaker *_ebm;
153 };
154 
155 
156 /**
157  * This is a handy class for collecting related nodes together. It is an
158  * abstract class; to use it you must subclass off of it. See the somewhat
159  * lengthy comment above.
160  */
161 class EXPCL_PANDA_EGG EggBinMaker : public EggObject {
162 PUBLISHED:
163  EggBinMaker();
164  ~EggBinMaker();
165 
166  int make_bins(EggGroupNode *root_group);
167 
168  virtual void
169  prepare_node(EggNode *node);
170 
171  virtual int
172  get_bin_number(const EggNode *node)=0;
173 
174  virtual bool
175  sorts_less(int bin_number, const EggNode *a, const EggNode *b);
176 
177  virtual bool
178  collapse_group(const EggGroup *group, int bin_number);
179 
180  virtual std::string
181  get_bin_name(int bin_number, const EggNode *child);
182 
183  virtual PT(EggBin)
184  make_bin(int bin_number, const EggNode *child, EggGroup *collapse_from);
185 
186 private:
187  // The logic is two-pass. First, we make a scene graph traversal and store
188  // all the pointers into the GroupNodesSortedNodes structure, which groups
189  // nodes by their parent group, and then sorted into bin order.
192 
193  // Then we walk through that list and create a BinsNodes structure for each
194  // group, which separates out the nodes into the individual bins.
195  typedef pvector< PT(EggNode) > Nodes;
196  typedef pvector<Nodes> Bins;
197 
198  void collect_nodes(EggGroupNode *group);
199  int get_bins_for_group(GroupNodes::const_iterator gi);
200  void make_bins_for_group(EggGroupNode *group, const Bins &bins);
201  void setup_bin(EggBin *bin, const Nodes &nodes);
202 
203  GroupNodes _group_nodes;
204 
205 
206 public:
207 
208  static TypeHandle get_class_type() {
209  return _type_handle;
210  }
211  static void init_type() {
212  EggObject::init_type();
213  register_type(_type_handle, "EggBinMaker",
214  EggObject::get_class_type());
215  }
216  virtual TypeHandle get_type() const {
217  return get_class_type();
218  }
219  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
220 
221 private:
222  static TypeHandle _type_handle;
223 
224 };
225 
226 #endif
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
void register_type(TypeHandle &type_handle, const std::string &name)
This inline function is just a convenient way to call TypeRegistry::register_type(),...
Definition: register_type.I:22
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
This is a handy class for collecting related nodes together.
Definition: eggBinMaker.h:161
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is our own Panda specialization on the default STL multiset.
Definition: pset.h:108
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
The highest-level base class in the egg directory.
Definition: eggObject.h:29
This is just an STL function object, used to sort nodes within EggBinMaker.
Definition: eggBinMaker.h:142
A type of group node that holds related subnodes.
Definition: eggBin.h:26