Panda3D
clipPlaneAttrib.cxx
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 clipPlaneAttrib.cxx
10  * @author drose
11  * @date 2002-07-11
12  */
13 
14 #include "clipPlaneAttrib.h"
15 #include "pandaNode.h"
17 #include "bamReader.h"
18 #include "bamWriter.h"
19 #include "datagram.h"
20 #include "datagramIterator.h"
21 #include "config_pgraph.h"
22 #include "attribNodeRegistry.h"
23 #include <iterator>
24 
25 CPT(RenderAttrib) ClipPlaneAttrib::_empty_attrib;
26 CPT(RenderAttrib) ClipPlaneAttrib::_all_off_attrib;
27 TypeHandle ClipPlaneAttrib::_type_handle;
28 int ClipPlaneAttrib::_attrib_slot;
29 
30 // This STL Function object is used in filter_to_max(), below, to sort a list
31 // of PlaneNodes in reverse order by priority.
32 class ComparePlaneNodePriorities {
33 public:
34  bool operator ()(const NodePath &a, const NodePath &b) const {
35  nassertr(!a.is_empty() && !b.is_empty(), a < b);
36  PlaneNode *pa = DCAST(PlaneNode, a.node());
37  PlaneNode *pb = DCAST(PlaneNode, b.node());
38  nassertr(pa != nullptr && pb != nullptr, a < b);
39 
40  return pa->get_priority() > pb->get_priority();
41  }
42 };
43 
44 /**
45  * Constructs a new ClipPlaneAttrib object that enables (or disables,
46  * according to op) the indicated plane(s).
47  *
48  * This method is now deprecated. Use add_on_plane() or add_off_plane()
49  * instead.
50  */
51 CPT(RenderAttrib) ClipPlaneAttrib::
52 make(ClipPlaneAttrib::Operation op, PlaneNode *plane) {
53  pgraph_cat.warning()
54  << "Using deprecated ClipPlaneAttrib interface.\n";
55 
56  CPT(RenderAttrib) attrib;
57 
58  switch (op) {
59  case O_set:
60  attrib = make_all_off();
61  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane));
62  return attrib;
63 
64  case O_add:
65  attrib = make();
66  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane));
67  return attrib;
68 
69  case O_remove:
70  attrib = make();
71  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane));
72  return attrib;
73  }
74 
75  nassert_raise("invalid operation");
76  return make();
77 }
78 
79 /**
80  * Constructs a new ClipPlaneAttrib object that turns on (or off, according to
81  * op) the indicate plane(s).
82  *
83  * This method is now deprecated. Use add_on_plane() or add_off_plane()
84  * instead.
85  */
86 CPT(RenderAttrib) ClipPlaneAttrib::
87 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2) {
88  pgraph_cat.warning()
89  << "Using deprecated ClipPlaneAttrib interface.\n";
90 
91  CPT(RenderAttrib) attrib;
92 
93  switch (op) {
94  case O_set:
95  attrib = make_all_off();
96  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
97  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
98  return attrib;
99 
100  case O_add:
101  attrib = make();
102  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
103  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
104  return attrib;
105 
106  case O_remove:
107  attrib = make();
108  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane1));
109  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane2));
110  return attrib;
111  }
112 
113  nassert_raise("invalid operation");
114  return make();
115 }
116 
117 /**
118  * Constructs a new ClipPlaneAttrib object that turns on (or off, according to
119  * op) the indicate plane(s).
120  *
121  * This method is now deprecated. Use add_on_plane() or add_off_plane()
122  * instead.
123  */
124 CPT(RenderAttrib) ClipPlaneAttrib::
125 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
126  PlaneNode *plane3) {
127  pgraph_cat.warning()
128  << "Using deprecated ClipPlaneAttrib interface.\n";
129 
130  CPT(RenderAttrib) attrib;
131 
132  switch (op) {
133  case O_set:
134  attrib = make_all_off();
135  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
136  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
137  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
138  return attrib;
139 
140  case O_add:
141  attrib = make();
142  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
143  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
144  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
145  return attrib;
146 
147  case O_remove:
148  attrib = make();
149  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane1));
150  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane2));
151  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane3));
152  return attrib;
153  }
154 
155  nassert_raise("invalid operation");
156  return make();
157 }
158 
159 /**
160  * Constructs a new ClipPlaneAttrib object that turns on (or off, according to
161  * op) the indicate plane(s).
162  *
163  * This method is now deprecated. Use add_on_plane() or add_off_plane()
164  * instead.
165  */
166 CPT(RenderAttrib) ClipPlaneAttrib::
167 make(ClipPlaneAttrib::Operation op, PlaneNode *plane1, PlaneNode *plane2,
168  PlaneNode *plane3, PlaneNode *plane4) {
169  pgraph_cat.warning()
170  << "Using deprecated ClipPlaneAttrib interface.\n";
171 
172  CPT(RenderAttrib) attrib;
173 
174  switch (op) {
175  case O_set:
176  attrib = make_all_off();
177  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
178  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
179  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
180  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane4));
181  return attrib;
182 
183  case O_add:
184  attrib = make();
185  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane1));
186  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane2));
187  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane3));
188  attrib = DCAST(ClipPlaneAttrib, attrib)->add_on_plane(NodePath(plane4));
189  return attrib;
190 
191  case O_remove:
192  attrib = make();
193  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane1));
194  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane2));
195  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane3));
196  attrib = DCAST(ClipPlaneAttrib, attrib)->add_off_plane(NodePath(plane4));
197  return attrib;
198  }
199 
200  nassert_raise("invalid operation");
201  return make();
202 }
203 
204 /**
205  * Returns a RenderAttrib that corresponds to whatever the standard default
206  * properties for render attributes of this type ought to be.
207  */
208 CPT(RenderAttrib) ClipPlaneAttrib::
209 make_default() {
210  return return_new(new ClipPlaneAttrib);
211 }
212 
213 /**
214  * Returns the basic operation type of the ClipPlaneAttrib. If this is O_set,
215  * the planes listed here completely replace any planes that were already on.
216  * If this is O_add, the planes here are added to the set of planes that
217  * were already on, and if O_remove, the planes here are removed from the set
218  * of planes that were on.
219  *
220  * This method is now deprecated. ClipPlaneAttribs nowadays have a separate
221  * list of on_planes and off_planes, so this method doesn't make sense. Query
222  * the lists independently.
223  */
224 ClipPlaneAttrib::Operation ClipPlaneAttrib::
225 get_operation() const {
226  pgraph_cat.warning()
227  << "Using deprecated ClipPlaneAttrib interface.\n";
228 
229  if (has_all_off()) {
230  return O_set;
231 
232  } else if (get_num_off_planes() == 0) {
233  return O_add;
234 
235  } else {
236  return O_remove;
237  }
238 }
239 
240 /**
241  * Returns the number of planes listed in the attribute.
242  *
243  * This method is now deprecated. ClipPlaneAttribs nowadays have a separate
244  * list of on_planes and off_planes, so this method doesn't make sense. Query
245  * the lists independently.
246  */
248 get_num_planes() const {
249  pgraph_cat.warning()
250  << "Using deprecated ClipPlaneAttrib interface.\n";
251 
252  if (get_num_off_planes() == 0) {
253  return get_num_on_planes();
254  } else {
255  return get_num_off_planes();
256  }
257 }
258 
259 /**
260  * Returns the nth plane listed in the attribute.
261  *
262  * This method is now deprecated. ClipPlaneAttribs nowadays have a separate
263  * list of on_planes and off_planes, so this method doesn't make sense. Query
264  * the lists independently.
265  */
267 get_plane(int n) const {
268  pgraph_cat.warning()
269  << "Using deprecated ClipPlaneAttrib interface.\n";
270 
271  if (get_num_off_planes() == 0) {
272  return DCAST(PlaneNode, get_on_plane(n).node());
273  } else {
274  return DCAST(PlaneNode, get_off_plane(n).node());
275  }
276 }
277 
278 /**
279  * Returns true if the indicated plane is listed in the attrib, false
280  * otherwise.
281  *
282  * This method is now deprecated. ClipPlaneAttribs nowadays have a separate
283  * list of on_planes and off_planes, so this method doesn't make sense. Query
284  * the lists independently.
285  */
287 has_plane(PlaneNode *plane) const {
288  pgraph_cat.warning()
289  << "Using deprecated ClipPlaneAttrib interface.\n";
290 
291  if (get_num_off_planes() == 0) {
292  return has_on_plane(NodePath(plane));
293  } else {
294  return has_off_plane(NodePath(plane));
295  }
296 }
297 
298 /**
299  * Returns a new ClipPlaneAttrib, just like this one, but with the indicated
300  * plane added to the list of planes.
301  *
302  * This method is now deprecated. Use add_on_plane() or add_off_plane()
303  * instead.
304  */
305 CPT(RenderAttrib) ClipPlaneAttrib::
306 add_plane(PlaneNode *plane) const {
307  pgraph_cat.warning()
308  << "Using deprecated ClipPlaneAttrib interface.\n";
309 
310  if (get_num_off_planes() == 0) {
311  return add_on_plane(NodePath(plane));
312  } else {
313  return add_off_plane(NodePath(plane));
314  }
315 }
316 
317 /**
318  * Returns a new ClipPlaneAttrib, just like this one, but with the indicated
319  * plane removed from the list of planes.
320  *
321  * This method is now deprecated. Use remove_on_plane() or remove_off_plane()
322  * instead.
323  */
324 CPT(RenderAttrib) ClipPlaneAttrib::
325 remove_plane(PlaneNode *plane) const {
326  pgraph_cat.warning()
327  << "Using deprecated ClipPlaneAttrib interface.\n";
328 
329  if (get_num_off_planes() == 0) {
330  return remove_on_plane(NodePath(plane));
331  } else {
332  return remove_off_plane(NodePath(plane));
333  }
334 }
335 
336 /**
337  * Constructs a new ClipPlaneAttrib object that does nothing.
338  */
339 CPT(RenderAttrib) ClipPlaneAttrib::
340 make() {
341  // We make it a special case and store a pointer to the empty attrib forever
342  // once we find it the first time, as an optimization.
343  if (_empty_attrib == nullptr) {
344  _empty_attrib = return_new(new ClipPlaneAttrib);
345  }
346 
347  return _empty_attrib;
348 }
349 
350 /**
351  * Constructs a new ClipPlaneAttrib object that disables all planes (and hence
352  * disables clipping).
353  */
354 CPT(RenderAttrib) ClipPlaneAttrib::
355 make_all_off() {
356  // We make it a special case and store a pointer to the off attrib forever
357  // once we find it the first time, as an optimization.
358  if (_all_off_attrib == nullptr) {
359  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
360  attrib->_off_all_planes = true;
361  _all_off_attrib = return_new(attrib);
362  }
363 
364  return _all_off_attrib;
365 }
366 
367 /**
368  * Returns a new ClipPlaneAttrib, just like this one, but with the indicated
369  * plane added to the list of planes enabled by this attrib.
370  */
371 CPT(RenderAttrib) ClipPlaneAttrib::
372 add_on_plane(const NodePath &plane) const {
373  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
374  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
375  attrib->_on_planes.insert(plane);
376  attrib->_off_planes.erase(plane);
377 
378  std::pair<Planes::iterator, bool> insert_result =
379  attrib->_on_planes.insert(Planes::value_type(plane));
380  if (insert_result.second) {
381  // Also ensure it is removed from the off_planes list.
382  attrib->_off_planes.erase(plane);
383  }
384 
385  return return_new(attrib);
386 }
387 
388 /**
389  * Returns a new ClipPlaneAttrib, just like this one, but with the indicated
390  * plane removed from the list of planes enabled by this attrib.
391  */
392 CPT(RenderAttrib) ClipPlaneAttrib::
393 remove_on_plane(const NodePath &plane) const {
394  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
395  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
396  attrib->_on_planes.erase(plane);
397  return return_new(attrib);
398 }
399 
400 /**
401  * Returns a new ClipPlaneAttrib, just like this one, but with the indicated
402  * plane added to the list of planes disabled by this attrib.
403  */
404 CPT(RenderAttrib) ClipPlaneAttrib::
405 add_off_plane(const NodePath &plane) const {
406  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
407  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
408  if (!_off_all_planes) {
409  attrib->_off_planes.insert(plane);
410  }
411  attrib->_on_planes.erase(plane);
412  return return_new(attrib);
413 }
414 
415 /**
416  * Returns a new ClipPlaneAttrib, just like this one, but with the indicated
417  * plane removed from the list of planes disabled by this attrib.
418  */
419 CPT(RenderAttrib) ClipPlaneAttrib::
420 remove_off_plane(const NodePath &plane) const {
421  nassertr(!plane.is_empty() && plane.node()->is_of_type(PlaneNode::get_class_type()), this);
422  ClipPlaneAttrib *attrib = new ClipPlaneAttrib(*this);
423  attrib->_off_planes.erase(plane);
424  return return_new(attrib);
425 }
426 
427 /**
428  * Returns a new ClipPlaneAttrib, very much like this one, but with the number
429  * of on_planes reduced to be no more than max_clip_planes. The number of
430  * off_planes in the new ClipPlaneAttrib is undefined.
431  */
432 CPT(ClipPlaneAttrib) ClipPlaneAttrib::
433 filter_to_max(int max_clip_planes) const {
434  if (max_clip_planes < 0 || (int)_on_planes.size() <= max_clip_planes) {
435  // Trivial case: this ClipPlaneAttrib qualifies.
436  return this;
437  }
438 
439  // Since check_filtered() will clear the _filtered list if we are out of
440  // date, we should call it first.
441  check_filtered();
442 
443  Filtered::const_iterator fi;
444  fi = _filtered.find(max_clip_planes);
445  if (fi != _filtered.end()) {
446  // Easy case: we have already computed this for this particular
447  // ClipPlaneAttrib.
448  return (*fi).second;
449  }
450 
451  // Harder case: we have to compute it now. We must choose the n planeNodes
452  // with the highest priority in our list of planeNodes.
453  Planes priority_planes = _on_planes;
454 
455  // This sort function uses the STL function object defined above.
456  sort(priority_planes.begin(), priority_planes.end(),
457  ComparePlaneNodePriorities());
458 
459  // Now lop off all of the planeNodes after the first max_clip_planes.
460  priority_planes.erase(priority_planes.begin() + max_clip_planes,
461  priority_planes.end());
462 
463  // And re-sort the ov_set into its proper order.
464  priority_planes.sort();
465 
466  // Now create a new attrib reflecting these planeNodes.
467  PT(ClipPlaneAttrib) attrib = new ClipPlaneAttrib;
468  attrib->_on_planes.swap(priority_planes);
469 
470  CPT(RenderAttrib) new_attrib = return_new(attrib);
471 
472  // Finally, record this newly-created attrib in the map for next time.
473  CPT(ClipPlaneAttrib) planeNode_attrib = (const ClipPlaneAttrib *)new_attrib.p();
474  ((ClipPlaneAttrib *)this)->_filtered[max_clip_planes] = planeNode_attrib;
475  return planeNode_attrib;
476 }
477 
478 /**
479  * This is a special method which composes two ClipPlaneAttribs with regard
480  * only to their set of "off" clip planes, for the purposes of deriving
481  * PandaNode::get_off_clip_planes().
482  *
483  * The result will be a ClipPlaneAttrib that represents the union of all of
484  * the clip planes turned off in either attrib. The set of on planes in the
485  * result is undefined and should be ignored.
486  */
487 CPT(RenderAttrib) ClipPlaneAttrib::
488 compose_off(const RenderAttrib *other) const {
489  const ClipPlaneAttrib *ta;
490  DCAST_INTO_R(ta, other, nullptr);
491 
492  if (_off_all_planes || (!ta->_off_all_planes && ta->_off_planes.empty())) {
493  // If we turn off all planes, or the other turns none off, the result is
494  // the same as this one.
495  return this;
496  }
497 
498  if (ta->_off_all_planes || _off_planes.empty()) {
499  // And contrariwise.
500  return ta;
501  }
502 
503  Planes::const_iterator ai = _off_planes.begin();
504  Planes::const_iterator bi = ta->_off_planes.begin();
505 
506  // Create a new ClipPlaneAttrib that will hold the result.
507  ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
508  std::back_insert_iterator<Planes> result =
509  std::back_inserter(new_attrib->_on_planes);
510 
511  while (ai != _off_planes.end() &&
512  bi != ta->_off_planes.end()) {
513  if ((*ai) < (*bi)) {
514  // Here is a plane that we have in the original, which is not present in
515  // the secondary.
516  *result = *ai;
517  ++ai;
518  ++result;
519 
520  } else if ((*bi) < (*ai)) {
521  // Here is a new plane we have in the secondary, that was not present in
522  // the original.
523  *result = *bi;
524  ++bi;
525  ++result;
526 
527  } else { // (*bi) == (*ai)
528  // Here is a plane we have in both.
529  *result = *bi;
530  ++ai;
531  ++bi;
532  ++result;
533  }
534  }
535 
536  while (ai != _off_planes.end()) {
537  *result = *ai;
538  ++ai;
539  ++result;
540  }
541 
542  while (bi != ta->_off_planes.end()) {
543  *result = *bi;
544  ++bi;
545  ++result;
546  }
547 
548  return return_new(new_attrib);
549 }
550 
551 /**
552  *
553  */
554 void ClipPlaneAttrib::
555 output(std::ostream &out) const {
556  out << get_type() << ":";
557  if (_off_planes.empty()) {
558  if (_on_planes.empty()) {
559  if (_off_all_planes) {
560  out << "all off";
561  } else {
562  out << "identity";
563  }
564  } else {
565  if (_off_all_planes) {
566  out << "set";
567  } else {
568  out << "on";
569  }
570  }
571 
572  } else {
573  out << "off";
574  Planes::const_iterator fi;
575  for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
576  NodePath plane = (*fi);
577  out << " " << plane;
578  }
579 
580  if (!_on_planes.empty()) {
581  out << " on";
582  }
583  }
584 
585  Planes::const_iterator li;
586  for (li = _on_planes.begin(); li != _on_planes.end(); ++li) {
587  NodePath plane = (*li);
588  out << " " << plane;
589  }
590 }
591 
592 /**
593  * Intended to be overridden by derived ClipPlaneAttrib types to return a
594  * unique number indicating whether this ClipPlaneAttrib is equivalent to the
595  * other one.
596  *
597  * This should return 0 if the two ClipPlaneAttrib objects are equivalent, a
598  * number less than zero if this one should be sorted before the other one,
599  * and a number greater than zero otherwise.
600  *
601  * This will only be called with two ClipPlaneAttrib objects whose get_type()
602  * functions return the same.
603  */
604 int ClipPlaneAttrib::
605 compare_to_impl(const RenderAttrib *other) const {
606  const ClipPlaneAttrib *ta;
607  DCAST_INTO_R(ta, other, 0);
608 
609  if (_off_all_planes != ta->_off_all_planes) {
610  return (int)_off_all_planes - (int)ta->_off_all_planes;
611  }
612 
613  Planes::const_iterator li = _on_planes.begin();
614  Planes::const_iterator oli = ta->_on_planes.begin();
615 
616  while (li != _on_planes.end() && oli != ta->_on_planes.end()) {
617  NodePath plane = (*li);
618  NodePath other_plane = (*oli);
619 
620  int compare = plane.compare_to(other_plane);
621  if (compare != 0) {
622  return compare;
623  }
624 
625  ++li;
626  ++oli;
627  }
628 
629  if (li != _on_planes.end()) {
630  return 1;
631  }
632  if (oli != ta->_on_planes.end()) {
633  return -1;
634  }
635 
636  Planes::const_iterator fi = _off_planes.begin();
637  Planes::const_iterator ofi = ta->_off_planes.begin();
638 
639  while (fi != _off_planes.end() && ofi != ta->_off_planes.end()) {
640  NodePath plane = (*fi);
641  NodePath other_plane = (*ofi);
642 
643  int compare = plane.compare_to(other_plane);
644  if (compare != 0) {
645  return compare;
646  }
647 
648  ++fi;
649  ++ofi;
650  }
651 
652  if (fi != _off_planes.end()) {
653  return 1;
654  }
655  if (ofi != ta->_off_planes.end()) {
656  return -1;
657  }
658 
659  return 0;
660 }
661 
662 /**
663  * Intended to be overridden by derived RenderAttrib types to return a unique
664  * hash for these particular properties. RenderAttribs that compare the same
665  * with compare_to_impl(), above, should return the same hash; RenderAttribs
666  * that compare differently should return a different hash.
667  */
668 size_t ClipPlaneAttrib::
669 get_hash_impl() const {
670  size_t hash = 0;
671 
672  Planes::const_iterator li;
673  for (li = _on_planes.begin(); li != _on_planes.end(); ++li) {
674  NodePath plane = (*li);
675  hash = plane.add_hash(hash);
676  }
677 
678  // This bool value goes here, between the two lists, to differentiate
679  // between the two.
680  hash = int_hash::add_hash(hash, (int)_off_all_planes);
681 
682  for (li = _off_planes.begin(); li != _off_planes.end(); ++li) {
683  NodePath plane = (*li);
684  hash = plane.add_hash(hash);
685  }
686 
687  return hash;
688 }
689 
690 /**
691  * Intended to be overridden by derived RenderAttrib types to specify how two
692  * consecutive RenderAttrib objects of the same type interact.
693  *
694  * This should return the result of applying the other RenderAttrib to a node
695  * in the scene graph below this RenderAttrib, which was already applied. In
696  * most cases, the result is the same as the other RenderAttrib (that is, a
697  * subsequent RenderAttrib completely replaces the preceding one). On the
698  * other hand, some kinds of RenderAttrib (for instance, ColorTransformAttrib)
699  * might combine in meaningful ways.
700  */
701 CPT(RenderAttrib) ClipPlaneAttrib::
702 compose_impl(const RenderAttrib *other) const {
703  const ClipPlaneAttrib *ta;
704  DCAST_INTO_R(ta, other, nullptr);
705 
706  if (ta->_off_all_planes) {
707  // If the other type turns off all planes, it doesn't matter what we are.
708  return ta;
709  }
710 
711  // This is a three-way merge between ai, bi, and ci, except that bi and ci
712  // should have no intersection and therefore needn't be compared to each
713  // other.
714  Planes::const_iterator ai = _on_planes.begin();
715  Planes::const_iterator bi = ta->_on_planes.begin();
716  Planes::const_iterator ci = ta->_off_planes.begin();
717 
718  // Create a new ClipPlaneAttrib that will hold the result.
719  ClipPlaneAttrib *new_attrib = new ClipPlaneAttrib;
720  std::back_insert_iterator<Planes> result =
721  std::back_inserter(new_attrib->_on_planes);
722 
723  while (ai != _on_planes.end() &&
724  bi != ta->_on_planes.end() &&
725  ci != ta->_off_planes.end()) {
726  if ((*ai) < (*bi)) {
727  if ((*ai) < (*ci)) {
728  // Here is a plane that we have in the original, which is not present
729  // in the secondary.
730  *result = *ai;
731  ++ai;
732  ++result;
733 
734  } else if ((*ci) < (*ai)) {
735  // Here is a plane that is disabled in the secondary, but was not
736  // present in the original.
737  ++ci;
738 
739  } else { // (*ci) == (*ai)
740  // Here is a plane that is disabled in the secondary, and was present
741  // in the original.
742  ++ai;
743  ++ci;
744  }
745 
746  } else if ((*bi) < (*ai)) {
747  // Here is a new plane we have in the secondary, that was not present in
748  // the original.
749  *result = *bi;
750  ++bi;
751  ++result;
752 
753  } else { // (*bi) == (*ai)
754  // Here is a plane we have in both.
755  *result = *bi;
756  ++ai;
757  ++bi;
758  ++result;
759  }
760  }
761 
762  while (ai != _on_planes.end() && bi != ta->_on_planes.end()) {
763  if ((*ai) < (*bi)) {
764  // Here is a plane that we have in the original, which is not present in
765  // the secondary.
766  *result = *ai;
767  ++ai;
768  ++result;
769 
770  } else if ((*bi) < (*ai)) {
771  // Here is a new plane we have in the secondary, that was not present in
772  // the original.
773  *result = *bi;
774  ++bi;
775  ++result;
776 
777  } else {
778  // Here is a plane we have in both.
779  *result = *bi;
780  ++ai;
781  ++bi;
782  ++result;
783  }
784  }
785 
786  while (ai != _on_planes.end() && ci != ta->_off_planes.end()) {
787  if ((*ai) < (*ci)) {
788  // Here is a plane that we have in the original, which is not present in
789  // the secondary.
790  *result = *ai;
791  ++ai;
792  ++result;
793 
794  } else if ((*ci) < (*ai)) {
795  // Here is a plane that is disabled in the secondary, but was not
796  // present in the original.
797  ++ci;
798 
799  } else { // (*ci) == (*ai)
800  // Here is a plane that is disabled in the secondary, and was present in
801  // the original.
802  ++ai;
803  ++ci;
804  }
805  }
806 
807  while (ai != _on_planes.end()) {
808  *result = *ai;
809  ++ai;
810  ++result;
811  }
812 
813  while (bi != ta->_on_planes.end()) {
814  *result = *bi;
815  ++bi;
816  ++result;
817  }
818 
819  return return_new(new_attrib);
820 }
821 
822 /**
823  * Intended to be overridden by derived RenderAttrib types to specify how two
824  * consecutive RenderAttrib objects of the same type interact.
825  *
826  * See invert_compose() and compose_impl().
827  */
828 CPT(RenderAttrib) ClipPlaneAttrib::
829 invert_compose_impl(const RenderAttrib *other) const {
830  // I think in this case the other attrib always wins. Maybe this needs a
831  // bit more thought. It's hard to imagine that it's even important to
832  // compute this properly.
833  return other;
834 }
835 
836 /**
837  * This is patterned after TextureAttrib::sort_on_stages(), but since
838  * planeNodes don't actually require sorting, this only empties the _filtered
839  * map.
840  */
841 void ClipPlaneAttrib::
842 sort_on_planes() {
843  _sort_seq = PlaneNode::get_sort_seq();
844  _filtered.clear();
845 }
846 
847 /**
848  * Tells the BamReader how to create objects of type ClipPlaneAttrib.
849  */
852  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
853 }
854 
855 /**
856  * Writes the contents of this object to the datagram for shipping out to a
857  * Bam file.
858  */
861  RenderAttrib::write_datagram(manager, dg);
862 
863  dg.add_bool(_off_all_planes);
864 
865  // write the number of off_planes
867 
868  // write the off planes pointers if any
869  Planes::const_iterator fi;
870  if (manager->get_file_minor_ver() < 40) {
871  for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
872  manager->write_pointer(dg, fi->node());
873  }
874  } else {
875  for (fi = _off_planes.begin(); fi != _off_planes.end(); ++fi) {
876  (*fi).write_datagram(manager, dg);
877  }
878  }
879 
880  // write the number of on planes
882 
883  // write the on planes pointers if any
884  Planes::const_iterator nti;
885  if (manager->get_file_minor_ver() < 40) {
886  for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
887  manager->write_pointer(dg, nti->node());
888  }
889  } else {
890  for (nti = _on_planes.begin(); nti != _on_planes.end(); ++nti) {
891  (*nti).write_datagram(manager, dg);
892  }
893  }
894 }
895 
896 /**
897  * Receives an array of pointers, one for each time manager->read_pointer()
898  * was called in fillin(). Returns the number of pointers processed.
899  */
902  int pi = RenderAttrib::complete_pointers(p_list, manager);
903 
904  if (manager->get_file_minor_ver() >= 40) {
905  for (size_t i = 0; i < _off_planes.size(); ++i) {
906  pi += _off_planes[i].complete_pointers(p_list + pi, manager);
907  }
908 
909  for (size_t i = 0; i < _on_planes.size(); ++i) {
910  pi += _on_planes[i].complete_pointers(p_list + pi, manager);
911  }
912 
913  } else {
914  BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "planes");
915  nassertr(aux != nullptr, pi);
916 
917  int i;
918  aux->_off_list.reserve(aux->_num_off_planes);
919  for (i = 0; i < aux->_num_off_planes; ++i) {
920  PandaNode *node;
921  DCAST_INTO_R(node, p_list[pi++], pi);
922  aux->_off_list.push_back(node);
923  }
924 
925  aux->_on_list.reserve(aux->_num_on_planes);
926  for (i = 0; i < aux->_num_on_planes; ++i) {
927  PandaNode *node;
928  DCAST_INTO_R(node, p_list[pi++], pi);
929  aux->_on_list.push_back(node);
930  }
931  }
932 
933  return pi;
934 }
935 
936 /**
937  * Called by the BamReader to perform any final actions needed for setting up
938  * the object after all objects have been read and all pointers have been
939  * completed.
940  */
942 finalize(BamReader *manager) {
943  if (manager->get_file_minor_ver() >= 40) {
944  AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
945 
946  // Check if any of the nodes we loaded are mentioned in the
947  // AttribNodeRegistry. If so, replace them.
948  for (size_t i = 0; i < _off_planes.size(); ++i) {
949  int n = areg->find_node(_off_planes[i]);
950  if (n != -1) {
951  // If it's in the registry, replace it.
952  _off_planes[i] = areg->get_node(n);
953  }
954  }
955 
956  for (size_t i = 0; i < _on_planes.size(); ++i) {
957  int n = areg->find_node(_on_planes[i]);
958  if (n != -1) {
959  // If it's in the registry, replace it.
960  _on_planes[i] = areg->get_node(n);
961  }
962  }
963 
964  } else {
965  // Now it's safe to convert our saved PandaNodes into NodePaths.
966  BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "planes");
967  nassertv(aux != nullptr);
968  nassertv(aux->_num_off_planes == (int)aux->_off_list.size());
969  nassertv(aux->_num_on_planes == (int)aux->_on_list.size());
970 
971  AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
972 
973  _off_planes.reserve(aux->_off_list.size());
974  NodeList::iterator ni;
975  for (ni = aux->_off_list.begin(); ni != aux->_off_list.end(); ++ni) {
976  PandaNode *node = (*ni);
977  int n = areg->find_node(node->get_type(), node->get_name());
978  if (n != -1) {
979  // If it's in the registry, add that NodePath.
980  _off_planes.push_back(areg->get_node(n));
981  } else {
982  // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
983  _off_planes.push_back(NodePath(node));
984  }
985  }
986 
987  _on_planes.reserve(aux->_on_list.size());
988  for (ni = aux->_on_list.begin(); ni != aux->_on_list.end(); ++ni) {
989  PandaNode *node = (*ni);
990  int n = areg->find_node(node->get_type(), node->get_name());
991  if (n != -1) {
992  // If it's in the registry, add that NodePath.
993  _on_planes.push_back(areg->get_node(n));
994  node = _on_planes.back().node();
995  } else {
996  // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
997  _on_planes.push_back(NodePath(node));
998  }
999  }
1000  }
1001 
1002  // Now that the NodePaths have been filled in, we can sort the list.
1003  _off_planes.sort();
1004  _on_planes.sort();
1005 }
1006 
1007 /**
1008  * This function is called by the BamReader's factory when a new object of
1009  * type ClipPlaneAttrib is encountered in the Bam file. It should create the
1010  * ClipPlaneAttrib and extract its information from the file.
1011  */
1012 TypedWritable *ClipPlaneAttrib::
1013 make_from_bam(const FactoryParams &params) {
1014  ClipPlaneAttrib *attrib = new ClipPlaneAttrib;
1015  DatagramIterator scan;
1016  BamReader *manager;
1017 
1018  parse_params(params, scan, manager);
1019  attrib->fillin(scan, manager);
1020 
1021  manager->register_finalize(attrib);
1022 
1023  return attrib;
1024 }
1025 
1026 /**
1027  * This internal function is called by make_from_bam to read in all of the
1028  * relevant data from the BamFile for the new ClipPlaneAttrib.
1029  */
1030 void ClipPlaneAttrib::
1031 fillin(DatagramIterator &scan, BamReader *manager) {
1032  RenderAttrib::fillin(scan, manager);
1033 
1034  _off_all_planes = scan.get_bool();
1035 
1036  if (manager->get_file_minor_ver() >= 40) {
1037  _off_planes.resize(scan.get_uint16());
1038  for (size_t i = 0; i < _off_planes.size(); ++i) {
1039  _off_planes[i].fillin(scan, manager);
1040  }
1041 
1042  _on_planes.resize(scan.get_uint16());
1043  for (size_t i = 0; i < _on_planes.size(); ++i) {
1044  _on_planes[i].fillin(scan, manager);
1045  }
1046  } else {
1047  BamAuxData *aux = new BamAuxData;
1048  manager->set_aux_data(this, "planes", aux);
1049 
1050  aux->_num_off_planes = scan.get_uint16();
1051  manager->read_pointers(scan, aux->_num_off_planes);
1052 
1053  aux->_num_on_planes = scan.get_uint16();
1054  manager->read_pointers(scan, aux->_num_on_planes);
1055  }
1056 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PlaneNode * get_plane(int n) const
Returns the nth plane listed in the attribute.
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
bool has_off_plane(const NodePath &plane) const
Returns true if the indicated plane is disabled by the attrib, false otherwise.
AuxData * get_aux_data(TypedWritable *obj, const std::string &name) const
Returns the pointer previously associated with the bam reader by a previous call to set_aux_data(),...
Definition: bamReader.cxx:189
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool get_bool()
Extracts a boolean value.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
size_type_0 size() const
Returns the number of elements in the ordered vector.
int get_num_planes() const
Returns the number of planes listed in the attribute.
This global object records NodePaths that are referenced by scene graph attribs, such as ClipPlaneAtt...
bool has_all_off() const
Returns true if this attrib disables all planes (although it may also enable some).
int compare_to(const NodePath &other) const
Returns a number less than zero if this NodePath sorts before the other one, greater than zero if it ...
Definition: nodePath.I:1955
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
iterator_0 begin()
Returns the iterator that marks the first element in the ordered vector.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This functions similarly to a LightAttrib.
reference back()
Returns a reference to the first element.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
iterator_0 end()
Returns the iterator that marks the end of the ordered vector.
void reserve(size_type_0 n)
Informs the vector of a planned change in size; ensures that the capacity of the vector is greater th...
bool empty() const
Returns true if the ordered vector is empty, false otherwise.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
void swap(ordered_vector< Key, Compare, Vector > &other)
Exchanges the contents of this vector and the other vector, in constant time (e.g....
static size_t add_hash(size_t start, const Key &key)
Adds the indicated key into a running hash.
Definition: stl_compares.I:101
get_off_plane
Returns the nth plane disabled by the attribute, sorted in arbitrary (pointer) order.
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
int find_node(const NodePath &attrib_node) const
Returns the index number of the indicated NodePath in the registry (assuming its name hasn't changed ...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
static UpdateSeq get_sort_seq()
Returns a global sequence number that is incremented any time any PlaneNode in the world changes sort...
Definition: planeNode.I:138
CPT(RenderAttrib) ClipPlaneAttrib
Constructs a new ClipPlaneAttrib object that enables (or disables, according to op) the indicated pla...
Operation get_operation() const
Returns the basic operation type of the ClipPlaneAttrib.
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().
get_num_off_planes
Returns the number of planes that are disabled by the attribute.
get_num_on_planes
Returns the number of planes that are enabled by the attribute.
void read_pointers(DatagramIterator &scan, int count)
A convenience function to read a contiguous list of pointers.
Definition: bamReader.cxx:653
void sort()
Maps to sort_unique().
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
bool has_on_plane(const NodePath &plane) const
Returns true if the indicated plane is enabled by the attrib, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void register_factory(TypeHandle handle, CreateFunc *func, void *user_data=nullptr)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:73
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:808
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void push_back(const value_type_0 &key)
Adds the new element to the end of the vector without regard for proper sorting.
void set_aux_data(TypedWritable *obj, const std::string &name, AuxData *data)
Associates an arbitrary block of data with the indicated object (or NULL), and the indicated name.
Definition: bamReader.cxx:167
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
get_on_plane
Returns the nth plane enabled by the attribute, sorted in render order.
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
get_node
Returns the nth NodePath recorded in the registry.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
static void register_with_read_factory()
Tells the BamReader how to create objects of type ClipPlaneAttrib.
A class to retrieve the individual data elements previously stored in a Datagram.
size_t add_hash(size_t hash) const
Adds the NodePath into the running hash.
Definition: nodePath.I:264
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
bool has_plane(PlaneNode *plane) const
Returns true if the indicated plane is listed in the attrib, false otherwise.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A node that contains a plane.
Definition: planeNode.h:36
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317