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