Panda3D
lightAttrib.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 lightAttrib.cxx
10  * @author drose
11  * @date 2002-03-26
12  */
13 
14 #include "lightAttrib.h"
15 #include "pandaNode.h"
16 #include "nodePath.h"
18 #include "bamReader.h"
19 #include "bamWriter.h"
20 #include "datagram.h"
21 #include "datagramIterator.h"
22 #include "config_pgraph.h"
23 #include "attribNodeRegistry.h"
24 #include "indent.h"
25 #include <iterator>
26 
27 CPT(RenderAttrib) LightAttrib::_empty_attrib;
28 int LightAttrib::_attrib_slot;
29 CPT(RenderAttrib) LightAttrib::_all_off_attrib;
30 TypeHandle LightAttrib::_type_handle;
31 
32 // This STL Function object is used in sort_on_lights(), below, to sort a list
33 // of Lights in reverse order by priority. In the case of two lights with
34 // equal priority, the class priority is compared.
35 class CompareLightPriorities {
36 public:
37  bool operator ()(const NodePath &a, const NodePath &b) const {
38  nassertr(!a.is_empty() && !b.is_empty(), a < b);
39  Light *la = a.node()->as_light();
40  Light *lb = b.node()->as_light();
41  nassertr(la != nullptr && lb != nullptr, a < b);
42 
43  if (la->get_priority() != lb->get_priority()) {
44  return la->get_priority() > lb->get_priority();
45  }
46  return la->get_class_priority() > lb->get_class_priority();
47  }
48 };
49 
50 /**
51  * Use LightAttrib::make() to construct a new LightAttrib object. The copy
52  * constructor is only defined to facilitate methods like add_on_light().
53  */
54 LightAttrib::
55 LightAttrib(const LightAttrib &copy) :
56  _on_lights(copy._on_lights),
57  _off_lights(copy._off_lights),
58  _off_all_lights(copy._off_all_lights),
59  _sort_seq(UpdateSeq::old())
60 {
61  // Increase the attrib_ref of all the lights in this attribute.
62  Lights::const_iterator it;
63  for (it = _on_lights.begin(); it != _on_lights.end(); ++it) {
64  Light *lobj = (*it).node()->as_light();
65  nassertd(lobj != nullptr) continue;
66  lobj->attrib_ref();
67  }
68 }
69 
70 /**
71  * Destructor.
72  */
75  // Call attrib_unref() on all on lights.
76  Lights::const_iterator it;
77  for (it = _on_lights.begin(); it != _on_lights.end(); ++it) {
78  const NodePath &np = *it;
79  if (!np.is_empty()) {
80  Light *lobj = np.node()->as_light();
81  if (lobj != nullptr) {
82  lobj->attrib_unref();
83  }
84  }
85  }
86 }
87 
88 /**
89  * Constructs a new LightAttrib object that turns on (or off, according to op)
90  * the indicated light(s).
91  *
92  * This method is now deprecated. Use add_on_light() or add_off_light()
93  * instead.
94  */
95 CPT(RenderAttrib) LightAttrib::
96 make(LightAttrib::Operation op, Light *light) {
97  pgraph_cat.warning()
98  << "Using deprecated LightAttrib interface.\n";
99 
100  CPT(RenderAttrib) attrib;
101 
102  switch (op) {
103  case O_set:
104  attrib = make_all_off();
105  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light->as_node()));
106  return attrib;
107 
108  case O_add:
109  attrib = make();
110  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light->as_node()));
111  return attrib;
112 
113  case O_remove:
114  attrib = make();
115  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light->as_node()));
116  return attrib;
117  }
118 
119  nassert_raise("invalid operation");
120  return make();
121 }
122 
123 /**
124  * Constructs a new LightAttrib object that turns on (or off, according to op)
125  * the indicate light(s).
126  *
127  * This method is now deprecated. Use add_on_light() or add_off_light()
128  * instead.
129  */
130 CPT(RenderAttrib) LightAttrib::
131 make(LightAttrib::Operation op, Light *light1, Light *light2) {
132  pgraph_cat.warning()
133  << "Using deprecated LightAttrib interface.\n";
134 
135  CPT(RenderAttrib) attrib;
136 
137  switch (op) {
138  case O_set:
139  attrib = make_all_off();
140  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light1->as_node()));
141  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light2->as_node()));
142  return attrib;
143 
144  case O_add:
145  attrib = make();
146  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light1->as_node()));
147  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light2->as_node()));
148  return attrib;
149 
150  case O_remove:
151  attrib = make();
152  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light1->as_node()));
153  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light2->as_node()));
154  return attrib;
155  }
156 
157  nassert_raise("invalid operation");
158  return make();
159 }
160 
161 /**
162  * Constructs a new LightAttrib object that turns on (or off, according to op)
163  * the indicate light(s).
164  *
165  * This method is now deprecated. Use add_on_light() or add_off_light()
166  * instead.
167  */
168 CPT(RenderAttrib) LightAttrib::
169 make(LightAttrib::Operation op, Light *light1, Light *light2,
170  Light *light3) {
171  pgraph_cat.warning()
172  << "Using deprecated LightAttrib interface.\n";
173 
174  CPT(RenderAttrib) attrib;
175 
176  switch (op) {
177  case O_set:
178  attrib = make_all_off();
179  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light1->as_node()));
180  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light2->as_node()));
181  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light3->as_node()));
182  return attrib;
183 
184  case O_add:
185  attrib = make();
186  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light1->as_node()));
187  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light2->as_node()));
188  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light3->as_node()));
189  return attrib;
190 
191  case O_remove:
192  attrib = make();
193  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light1->as_node()));
194  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light2->as_node()));
195  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light3->as_node()));
196  return attrib;
197  }
198 
199  nassert_raise("invalid operation");
200  return make();
201 }
202 
203 /**
204  * Constructs a new LightAttrib object that turns on (or off, according to op)
205  * the indicate light(s).
206  *
207  * This method is now deprecated. Use add_on_light() or add_off_light()
208  * instead.
209  */
210 CPT(RenderAttrib) LightAttrib::
211 make(LightAttrib::Operation op, Light *light1, Light *light2,
212  Light *light3, Light *light4) {
213  pgraph_cat.warning()
214  << "Using deprecated LightAttrib interface.\n";
215 
216  CPT(RenderAttrib) attrib;
217 
218  switch (op) {
219  case O_set:
220  attrib = make_all_off();
221  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light1->as_node()));
222  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light2->as_node()));
223  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light3->as_node()));
224  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light4->as_node()));
225  return attrib;
226 
227  case O_add:
228  attrib = make();
229  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light1->as_node()));
230  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light2->as_node()));
231  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light3->as_node()));
232  attrib = DCAST(LightAttrib, attrib)->add_on_light(NodePath(light4->as_node()));
233  return attrib;
234 
235  case O_remove:
236  attrib = make();
237  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light1->as_node()));
238  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light2->as_node()));
239  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light3->as_node()));
240  attrib = DCAST(LightAttrib, attrib)->add_off_light(NodePath(light4->as_node()));
241  return attrib;
242  }
243 
244  nassert_raise("invalid operation");
245  return make();
246 }
247 
248 /**
249  * Returns a RenderAttrib that corresponds to whatever the standard default
250  * properties for render attributes of this type ought to be.
251  */
252 CPT(RenderAttrib) LightAttrib::
253 make_default() {
254  return return_new(new LightAttrib);
255 }
256 
257 /**
258  * Returns the basic operation type of the LightAttrib. If this is O_set, the
259  * lights listed here completely replace any lights that were already on. If
260  * this is O_add, the lights here are added to the set of lights that were
261  * already on, and if O_remove, the lights here are removed from the set of
262  * lights that were on.
263  *
264  * This method is now deprecated. LightAttribs nowadays have a separate list
265  * of on_lights and off_lights, so this method doesn't make sense. Query the
266  * lists independently.
267  */
268 LightAttrib::Operation LightAttrib::
269 get_operation() const {
270  pgraph_cat.warning()
271  << "Using deprecated LightAttrib interface.\n";
272 
273  if (has_all_off()) {
274  return O_set;
275 
276  } else if (get_num_off_lights() == 0) {
277  return O_add;
278 
279  } else {
280  return O_remove;
281  }
282 }
283 
284 /**
285  * Returns the number of lights listed in the attribute.
286  *
287  * This method is now deprecated. LightAttribs nowadays have a separate list
288  * of on_lights and off_lights, so this method doesn't make sense. Query the
289  * lists independently.
290  */
291 int LightAttrib::
292 get_num_lights() const {
293  pgraph_cat.warning()
294  << "Using deprecated LightAttrib interface.\n";
295 
296  if (get_num_off_lights() == 0) {
297  return get_num_on_lights();
298  } else {
299  return get_num_off_lights();
300  }
301 }
302 
303 /**
304  * Returns the nth light listed in the attribute.
305  *
306  * This method is now deprecated. LightAttribs nowadays have a separate list
307  * of on_lights and off_lights, so this method doesn't make sense. Query the
308  * lists independently.
309  */
311 get_light(int n) const {
312  pgraph_cat.warning()
313  << "Using deprecated LightAttrib interface.\n";
314 
315  if (get_num_off_lights() == 0) {
316  return get_on_light(n).node()->as_light();
317  } else {
318  return get_off_light(n).node()->as_light();
319  }
320 }
321 
322 /**
323  * Returns true if the indicated light is listed in the attrib, false
324  * otherwise.
325  *
326  * This method is now deprecated. LightAttribs nowadays have a separate list
327  * of on_lights and off_lights, so this method doesn't make sense. Query the
328  * lists independently.
329  */
330 bool LightAttrib::
331 has_light(Light *light) const {
332  pgraph_cat.warning()
333  << "Using deprecated LightAttrib interface.\n";
334 
335  if (get_num_off_lights() == 0) {
336  return has_on_light(NodePath(light->as_node()));
337  } else {
338  return has_off_light(NodePath(light->as_node()));
339  }
340 }
341 
342 /**
343  * Returns a new LightAttrib, just like this one, but with the indicated light
344  * added to the list of lights.
345  *
346  * This method is now deprecated. Use add_on_light() or add_off_light()
347  * instead.
348  */
349 CPT(RenderAttrib) LightAttrib::
350 add_light(Light *light) const {
351  pgraph_cat.warning()
352  << "Using deprecated LightAttrib interface.\n";
353 
354  if (get_num_off_lights() == 0) {
355  return add_on_light(NodePath(light->as_node()));
356  } else {
357  return add_off_light(NodePath(light->as_node()));
358  }
359 }
360 
361 /**
362  * Returns a new LightAttrib, just like this one, but with the indicated light
363  * removed from the list of lights.
364  *
365  * This method is now deprecated. Use remove_on_light() or remove_off_light()
366  * instead.
367  */
368 CPT(RenderAttrib) LightAttrib::
369 remove_light(Light *light) const {
370  pgraph_cat.warning()
371  << "Using deprecated LightAttrib interface.\n";
372 
373  if (get_num_off_lights() == 0) {
374  return remove_on_light(NodePath(light->as_node()));
375  } else {
376  return remove_off_light(NodePath(light->as_node()));
377  }
378 }
379 
380 /**
381  * Constructs a new LightAttrib object that does nothing.
382  */
383 CPT(RenderAttrib) LightAttrib::
384 make() {
385  // We make it a special case and store a pointer to the empty attrib forever
386  // once we find it the first time, as an optimization.
387  if (_empty_attrib == nullptr) {
388  _empty_attrib = return_new(new LightAttrib);
389  }
390 
391  return _empty_attrib;
392 }
393 
394 /**
395  * Constructs a new LightAttrib object that turns off all lights (and hence
396  * disables lighting).
397  */
398 CPT(RenderAttrib) LightAttrib::
399 make_all_off() {
400  // We make it a special case and store a pointer to the off attrib forever
401  // once we find it the first time, as an optimization.
402  if (_all_off_attrib == nullptr) {
403  LightAttrib *attrib = new LightAttrib;
404  attrib->_off_all_lights = true;
405  _all_off_attrib = return_new(attrib);
406  }
407 
408  return _all_off_attrib;
409 }
410 
411 /**
412  * Returns a new LightAttrib, just like this one, but with the indicated light
413  * added to the list of lights turned on by this attrib.
414  */
415 CPT(RenderAttrib) LightAttrib::
416 add_on_light(const NodePath &light) const {
417  nassertr(!light.is_empty(), this);
418  Light *lobj = light.node()->as_light();
419  nassertr(lobj != nullptr, this);
420 
421  LightAttrib *attrib = new LightAttrib(*this);
422 
423  std::pair<Lights::iterator, bool> insert_result =
424  attrib->_on_lights.insert(Lights::value_type(light));
425  if (insert_result.second) {
426  lobj->attrib_ref();
427 
428  // Also ensure it is removed from the off_lights list.
429  attrib->_off_lights.erase(light);
430  }
431 
432  return return_new(attrib);
433 }
434 
435 /**
436  * Returns a new LightAttrib, just like this one, but with the indicated light
437  * removed from the list of lights turned on by this attrib.
438  */
439 CPT(RenderAttrib) LightAttrib::
440 remove_on_light(const NodePath &light) const {
441  nassertr(!light.is_empty(), this);
442  Light *lobj = light.node()->as_light();
443  nassertr(lobj != nullptr, this);
444 
445  LightAttrib *attrib = new LightAttrib(*this);
446  if (attrib->_on_lights.erase(light)) {
447  lobj->attrib_unref();
448  }
449  return return_new(attrib);
450 }
451 
452 /**
453  * Returns a new LightAttrib, just like this one, but with the indicated light
454  * added to the list of lights turned off by this attrib.
455  */
456 CPT(RenderAttrib) LightAttrib::
457 add_off_light(const NodePath &light) const {
458  nassertr(!light.is_empty(), this);
459  Light *lobj = light.node()->as_light();
460  nassertr(lobj != nullptr, this);
461 
462  LightAttrib *attrib = new LightAttrib(*this);
463  if (!_off_all_lights) {
464  attrib->_off_lights.insert(light);
465  }
466  if (attrib->_on_lights.erase(light)) {
467  lobj->attrib_unref();
468  }
469  return return_new(attrib);
470 }
471 
472 /**
473  * Returns a new LightAttrib, just like this one, but with the indicated light
474  * removed from the list of lights turned off by this attrib.
475  */
476 CPT(RenderAttrib) LightAttrib::
477 remove_off_light(const NodePath &light) const {
478  nassertr(!light.is_empty() && light.node()->as_light() != nullptr, this);
479  LightAttrib *attrib = new LightAttrib(*this);
480  attrib->_off_lights.erase(light);
481  return return_new(attrib);
482 }
483 
484 /**
485  * Returns the most important light (that is, the light with the highest
486  * priority) in the LightAttrib, excluding any ambient lights. Returns an
487  * empty NodePath if no non-ambient lights are found.
488  */
489 NodePath LightAttrib::
490 get_most_important_light() const {
491  check_sorted();
492 
493  if (_num_non_ambient_lights > 0) {
494  return _sorted_on_lights[0];
495  } else {
496  return NodePath();
497  }
498 }
499 
500 /**
501  * Returns the total contribution of all the ambient lights.
502  */
503 LColor LightAttrib::
504 get_ambient_contribution() const {
505  check_sorted();
506 
507  LVecBase4 total(0);
508 
509  Lights::const_iterator li;
510  li = _sorted_on_lights.begin() + _num_non_ambient_lights;
511  for (; li != _sorted_on_lights.end(); ++li) {
512  const NodePath &np = (*li);
513  Light *light = np.node()->as_light();
514  nassertd(light != nullptr && light->is_ambient_light()) continue;
515 
516  total += light->get_color();
517  }
518 
519  return total;
520 }
521 
522 /**
523  *
524  */
525 void LightAttrib::
526 output(std::ostream &out) const {
527  out << get_type() << ":";
528  if (_off_lights.empty()) {
529  if (_on_lights.empty()) {
530  if (_off_all_lights) {
531  out << "all off";
532  } else {
533  out << "identity";
534  }
535  } else {
536  if (_off_all_lights) {
537  out << "set";
538  } else {
539  out << "on";
540  }
541  }
542 
543  } else {
544  out << "off";
545  Lights::const_iterator fi;
546  for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
547  NodePath light = (*fi);
548  if (light.is_empty()) {
549  out << " " << light;
550  } else {
551  out << " " << light.get_name();
552  }
553  }
554 
555  if (!_on_lights.empty()) {
556  out << " on";
557  }
558  }
559 
560  Lights::const_iterator li;
561  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
562  NodePath light = (*li);
563  if (light.is_empty()) {
564  out << " " << light;
565  } else {
566  out << " " << light.get_name();
567  }
568  }
569 }
570 
571 /**
572  *
573  */
574 void LightAttrib::
575 write(std::ostream &out, int indent_level) const {
576  indent(out, indent_level) << get_type() << ":";
577  if (_off_lights.empty()) {
578  if (_on_lights.empty()) {
579  if (_off_all_lights) {
580  out << "all off\n";
581  } else {
582  out << "identity\n";
583  }
584  } else {
585  if (_off_all_lights) {
586  out << "set\n";
587  } else {
588  out << "on\n";
589  }
590  }
591 
592  } else {
593  out << "off\n";
594  Lights::const_iterator fi;
595  for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
596  NodePath light = (*fi);
597  indent(out, indent_level + 2) << light << "\n";
598  }
599 
600  if (!_on_lights.empty()) {
601  indent(out, indent_level) << "on\n";
602  }
603  }
604 
605  Lights::const_iterator li;
606  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
607  NodePath light = (*li);
608  indent(out, indent_level + 2) << light << "\n";
609  }
610 }
611 
612 /**
613  * Intended to be overridden by derived LightAttrib types to return a unique
614  * number indicating whether this LightAttrib is equivalent to the other one.
615  *
616  * This should return 0 if the two LightAttrib objects are equivalent, a
617  * number less than zero if this one should be sorted before the other one,
618  * and a number greater than zero otherwise.
619  *
620  * This will only be called with two LightAttrib objects whose get_type()
621  * functions return the same.
622  */
623 int LightAttrib::
624 compare_to_impl(const RenderAttrib *other) const {
625  const LightAttrib *ta = (const LightAttrib *)other;
626 
627  if (_off_all_lights != ta->_off_all_lights) {
628  return (int)_off_all_lights - (int)ta->_off_all_lights;
629  }
630 
631  Lights::const_iterator li = _on_lights.begin();
632  Lights::const_iterator oli = ta->_on_lights.begin();
633 
634  while (li != _on_lights.end() && oli != ta->_on_lights.end()) {
635  NodePath light = (*li);
636  NodePath other_light = (*oli);
637 
638  int compare = light.compare_to(other_light);
639  if (compare != 0) {
640  return compare;
641  }
642 
643  ++li;
644  ++oli;
645  }
646 
647  if (li != _on_lights.end()) {
648  return 1;
649  }
650  if (oli != ta->_on_lights.end()) {
651  return -1;
652  }
653 
654  Lights::const_iterator fi = _off_lights.begin();
655  Lights::const_iterator ofi = ta->_off_lights.begin();
656 
657  while (fi != _off_lights.end() && ofi != ta->_off_lights.end()) {
658  NodePath light = (*fi);
659  NodePath other_light = (*ofi);
660 
661  int compare = light.compare_to(other_light);
662  if (compare != 0) {
663  return compare;
664  }
665 
666  ++fi;
667  ++ofi;
668  }
669 
670  if (fi != _off_lights.end()) {
671  return 1;
672  }
673  if (ofi != ta->_off_lights.end()) {
674  return -1;
675  }
676 
677  return 0;
678 }
679 
680 /**
681  * Intended to be overridden by derived RenderAttrib types to return a unique
682  * hash for these particular properties. RenderAttribs that compare the same
683  * with compare_to_impl(), above, should return the same hash; RenderAttribs
684  * that compare differently should return a different hash.
685  */
686 size_t LightAttrib::
687 get_hash_impl() const {
688  size_t hash = 0;
689 
690  Lights::const_iterator li;
691  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
692  NodePath light = (*li);
693  hash = light.add_hash(hash);
694  }
695 
696  // This bool value goes here, between the two lists, to differentiate
697  // between the two.
698  hash = int_hash::add_hash(hash, (int)_off_all_lights);
699 
700  for (li = _off_lights.begin(); li != _off_lights.end(); ++li) {
701  NodePath light = (*li);
702  hash = light.add_hash(hash);
703  }
704 
705  return hash;
706 }
707 
708 /**
709  * Intended to be overridden by derived RenderAttrib types to specify how two
710  * consecutive RenderAttrib objects of the same type interact.
711  *
712  * This should return the result of applying the other RenderAttrib to a node
713  * in the scene graph below this RenderAttrib, which was already applied. In
714  * most cases, the result is the same as the other RenderAttrib (that is, a
715  * subsequent RenderAttrib completely replaces the preceding one). On the
716  * other hand, some kinds of RenderAttrib (for instance, ColorTransformAttrib)
717  * might combine in meaningful ways.
718  */
719 CPT(RenderAttrib) LightAttrib::
720 compose_impl(const RenderAttrib *other) const {
721  const LightAttrib *ta = (const LightAttrib *)other;
722 
723  if (ta->_off_all_lights) {
724  // If the other type turns off all lights, it doesn't matter what we are.
725  return ta;
726  }
727 
728  // This is a three-way merge between ai, bi, and ci, except that bi and ci
729  // should have no intersection and therefore needn't be compared to each
730  // other.
731  Lights::const_iterator ai = _on_lights.begin();
732  Lights::const_iterator bi = ta->_on_lights.begin();
733  Lights::const_iterator ci = ta->_off_lights.begin();
734 
735  // Create a new LightAttrib that will hold the result.
736  LightAttrib *new_attrib = new LightAttrib;
737  std::back_insert_iterator<Lights> result =
738  std::back_inserter(new_attrib->_on_lights);
739 
740  while (ai != _on_lights.end() &&
741  bi != ta->_on_lights.end() &&
742  ci != ta->_off_lights.end()) {
743  if ((*ai) < (*bi)) {
744  if ((*ai) < (*ci)) {
745  // Here is a light that we have in the original, which is not present
746  // in the secondary.
747  *result = *ai;
748  ++ai;
749  ++result;
750 
751  } else if ((*ci) < (*ai)) {
752  // Here is a light that is turned off in the secondary, but was not
753  // present in the original.
754  ++ci;
755 
756  } else { // (*ci) == (*ai)
757  // Here is a light that is turned off in the secondary, and was
758  // present in the original.
759  ++ai;
760  ++ci;
761  }
762 
763  } else if ((*bi) < (*ai)) {
764  // Here is a new light we have in the secondary, that was not present in
765  // the original.
766  *result = *bi;
767  ++bi;
768  ++result;
769 
770  } else { // (*bi) == (*ai)
771  // Here is a light we have in both.
772  *result = *bi;
773  ++ai;
774  ++bi;
775  ++result;
776  }
777  }
778 
779  while (ai != _on_lights.end() && bi != ta->_on_lights.end()) {
780  if ((*ai) < (*bi)) {
781  // Here is a light that we have in the original, which is not present in
782  // the secondary.
783  *result = *ai;
784  ++ai;
785  ++result;
786 
787  } else if ((*bi) < (*ai)) {
788  // Here is a new light we have in the secondary, that was not present in
789  // the original.
790  *result = *bi;
791  ++bi;
792  ++result;
793 
794  } else {
795  // Here is a light we have in both.
796  *result = *bi;
797  ++ai;
798  ++bi;
799  ++result;
800  }
801  }
802 
803  while (ai != _on_lights.end() && ci != ta->_off_lights.end()) {
804  if ((*ai) < (*ci)) {
805  // Here is a light that we have in the original, which is not present in
806  // the secondary.
807  *result = *ai;
808  ++ai;
809  ++result;
810 
811  } else if ((*ci) < (*ai)) {
812  // Here is a light that is turned off in the secondary, but was not
813  // present in the original.
814  ++ci;
815 
816  } else { // (*ci) == (*ai)
817  // Here is a light that is turned off in the secondary, and was present
818  // in the original.
819  ++ai;
820  ++ci;
821  }
822  }
823 
824  while (ai != _on_lights.end()) {
825  *result = *ai;
826  ++ai;
827  ++result;
828  }
829 
830  while (bi != ta->_on_lights.end()) {
831  *result = *bi;
832  ++bi;
833  ++result;
834  }
835 
836  // Increase the attrib_ref of all the lights in this new attribute.
837  Lights::const_iterator it;
838  for (it = new_attrib->_on_lights.begin(); it != new_attrib->_on_lights.end(); ++it) {
839  Light *lobj = (*it).node()->as_light();
840  nassertd(lobj != nullptr) continue;
841  lobj->attrib_ref();
842  }
843 
844  // This is needed since _sorted_on_lights is not yet populated.
845  new_attrib->_sort_seq = UpdateSeq::old();
846 
847  return return_new(new_attrib);
848 }
849 
850 /**
851  * Intended to be overridden by derived RenderAttrib types to specify how two
852  * consecutive RenderAttrib objects of the same type interact.
853  *
854  * See invert_compose() and compose_impl().
855  */
857 invert_compose_impl(const RenderAttrib *other) const {
858  // I think in this case the other attrib always wins. Maybe this needs a
859  // bit more thought. It's hard to imagine that it's even important to
860  // compute this properly.
861  return other;
862 }
863 
864 /**
865  * Makes sure the lights are sorted in order of priority. Also counts the
866  * number of non-ambient lights.
867  */
868 void LightAttrib::
869 sort_on_lights() {
870  _sort_seq = Light::get_sort_seq();
871 
872  // Separate the list of lights into ambient lights and other lights.
873  _sorted_on_lights.clear();
874  OrderedLights ambient_lights;
875 
876  Lights::const_iterator li;
877  for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
878  const NodePath &np = (*li);
879  nassertd(!np.is_empty() && np.node()->as_light() != nullptr) continue;
880 
881  if (!np.node()->is_ambient_light()) {
882  _sorted_on_lights.push_back(np);
883  } else {
884  ambient_lights.push_back(np);
885  }
886  }
887 
888  // Remember how many lights were non-ambient lights, which makes it easier
889  // to traverse through the list of non-ambient lights.
890  _num_non_ambient_lights = _sorted_on_lights.size();
891 
892  // This sort function uses the STL function object defined above.
893  sort(_sorted_on_lights.begin(), _sorted_on_lights.end(),
894  CompareLightPriorities());
895 
896  // Now insert the ambient lights back at the end. We don't really care
897  // about their relative priorities, because their contribution will simply
898  // be summed up in the end anyway.
899  _sorted_on_lights.insert(_sorted_on_lights.end(),
900  ambient_lights.begin(), ambient_lights.end());
901 }
902 
903 /**
904  * Tells the BamReader how to create objects of type LightAttrib.
905  */
906 void LightAttrib::
908  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
909 }
910 
911 /**
912  * Writes the contents of this object to the datagram for shipping out to a
913  * Bam file.
914  */
915 void LightAttrib::
917  RenderAttrib::write_datagram(manager, dg);
918 
919  dg.add_bool(_off_all_lights);
920 
921  // write the number of off_lights
922  dg.add_uint16(get_num_off_lights());
923 
924  // write the off lights pointers if any
925  Lights::const_iterator fi;
926  if (manager->get_file_minor_ver() < 40) {
927  for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
928  manager->write_pointer(dg, fi->node());
929  }
930  } else {
931  for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
932  (*fi).write_datagram(manager, dg);
933  }
934  }
935 
936  // write the number of on lights
937  dg.add_uint16(get_num_on_lights());
938  // write the on lights pointers if any
939  Lights::const_iterator nti;
940  if (manager->get_file_minor_ver() < 40) {
941  for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
942  manager->write_pointer(dg, nti->node());
943  }
944  } else {
945  for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
946  (*nti).write_datagram(manager, dg);
947  }
948  }
949 }
950 
951 /**
952  * Receives an array of pointers, one for each time manager->read_pointer()
953  * was called in fillin(). Returns the number of pointers processed.
954  */
955 int LightAttrib::
957  int pi = RenderAttrib::complete_pointers(p_list, manager);
958 
959  if (manager->get_file_minor_ver() >= 40) {
960  for (size_t i = 0; i < _off_lights.size(); ++i) {
961  pi += _off_lights[i].complete_pointers(p_list + pi, manager);
962  }
963 
964  for (size_t i = 0; i < _on_lights.size(); ++i) {
965  pi += _on_lights[i].complete_pointers(p_list + pi, manager);
966  }
967 
968  } else {
969  BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
970  nassertr(aux != nullptr, pi);
971 
972  int i;
973  aux->_off_list.reserve(aux->_num_off_lights);
974  for (i = 0; i < aux->_num_off_lights; ++i) {
975  PandaNode *node;
976  DCAST_INTO_R(node, p_list[pi++], pi);
977  aux->_off_list.push_back(node);
978  }
979 
980  aux->_on_list.reserve(aux->_num_on_lights);
981  for (i = 0; i < aux->_num_on_lights; ++i) {
982  PandaNode *node;
983  DCAST_INTO_R(node, p_list[pi++], pi);
984  aux->_on_list.push_back(node);
985  }
986  }
987 
988  return pi;
989 }
990 
991 /**
992  * Called by the BamReader to perform any final actions needed for setting up
993  * the object after all objects have been read and all pointers have been
994  * completed.
995  */
996 void LightAttrib::
997 finalize(BamReader *manager) {
998  if (manager->get_file_minor_ver() >= 40) {
999  AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
1000 
1001  // Check if any of the nodes we loaded are mentioned in the
1002  // AttribNodeRegistry. If so, replace them.
1003  for (size_t i = 0; i < _off_lights.size(); ++i) {
1004  int n = areg->find_node(_off_lights[i]);
1005  if (n != -1) {
1006  // If it's in the registry, replace it.
1007  _off_lights[i] = areg->get_node(n);
1008  }
1009  }
1010 
1011  for (size_t i = 0; i < _on_lights.size(); ++i) {
1012  int n = areg->find_node(_on_lights[i]);
1013  if (n != -1) {
1014  // If it's in the registry, replace it.
1015  _on_lights[i] = areg->get_node(n);
1016  }
1017 
1018  Light *lobj = _on_lights[i].node()->as_light();
1019  nassertd(lobj != nullptr) continue;
1020  lobj->attrib_ref();
1021  }
1022 
1023  } else {
1024  // Now it's safe to convert our saved PandaNodes into NodePaths.
1025  BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
1026  nassertv(aux != nullptr);
1027  nassertv(aux->_num_off_lights == (int)aux->_off_list.size());
1028  nassertv(aux->_num_on_lights == (int)aux->_on_list.size());
1029 
1030  AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
1031 
1032  _off_lights.reserve(aux->_off_list.size());
1033  NodeList::iterator ni;
1034  for (ni = aux->_off_list.begin(); ni != aux->_off_list.end(); ++ni) {
1035  PandaNode *node = (*ni);
1036  int n = areg->find_node(node->get_type(), node->get_name());
1037  if (n != -1) {
1038  // If it's in the registry, add that NodePath.
1039  _off_lights.push_back(areg->get_node(n));
1040  } else {
1041  // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
1042  _off_lights.push_back(NodePath(node));
1043  }
1044  }
1045 
1046  _on_lights.reserve(aux->_on_list.size());
1047  for (ni = aux->_on_list.begin(); ni != aux->_on_list.end(); ++ni) {
1048  PandaNode *node = (*ni);
1049  int n = areg->find_node(node->get_type(), node->get_name());
1050  if (n != -1) {
1051  // If it's in the registry, add that NodePath.
1052  _on_lights.push_back(areg->get_node(n));
1053  node = _on_lights.back().node();
1054  } else {
1055  // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
1056  _on_lights.push_back(NodePath(node));
1057  }
1058 
1059  Light *lobj = node->as_light();
1060  nassertd(lobj != nullptr) continue;
1061  lobj->attrib_ref();
1062  }
1063  }
1064 
1065  // Now that the NodePaths have been filled in, we can sort the list.
1066  _off_lights.sort();
1067  _on_lights.sort();
1068 }
1069 
1070 /**
1071  * This function is called by the BamReader's factory when a new object of
1072  * type LightAttrib is encountered in the Bam file. It should create the
1073  * LightAttrib and extract its information from the file.
1074  */
1075 TypedWritable *LightAttrib::
1076 make_from_bam(const FactoryParams &params) {
1077  LightAttrib *attrib = new LightAttrib;
1078  DatagramIterator scan;
1079  BamReader *manager;
1080 
1081  parse_params(params, scan, manager);
1082  attrib->fillin(scan, manager);
1083 
1084  manager->register_finalize(attrib);
1085 
1086  return attrib;
1087 }
1088 
1089 /**
1090  * This internal function is called by make_from_bam to read in all of the
1091  * relevant data from the BamFile for the new LightAttrib.
1092  */
1093 void LightAttrib::
1094 fillin(DatagramIterator &scan, BamReader *manager) {
1095  RenderAttrib::fillin(scan, manager);
1096 
1097  _off_all_lights = scan.get_bool();
1098 
1099  if (manager->get_file_minor_ver() >= 40) {
1100  _off_lights.resize(scan.get_uint16());
1101  for (size_t i = 0; i < _off_lights.size(); ++i) {
1102  _off_lights[i].fillin(scan, manager);
1103  }
1104 
1105  _on_lights.resize(scan.get_uint16());
1106  for (size_t i = 0; i < _on_lights.size(); ++i) {
1107  _on_lights[i].fillin(scan, manager);
1108  }
1109  } else {
1110  BamAuxData *aux = new BamAuxData;
1111  manager->set_aux_data(this, "lights", aux);
1112 
1113  aux->_num_off_lights = scan.get_uint16();
1114  manager->read_pointers(scan, aux->_num_off_lights);
1115 
1116  aux->_num_on_lights = scan.get_uint16();
1117  manager->read_pointers(scan, aux->_num_on_lights);
1118  }
1119 
1120  _sorted_on_lights.clear();
1121  _sort_seq = UpdateSeq::old();
1122 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual Light * as_light()
Cross-casts the node to a Light pointer, if it is one of the four kinds of Light nodes,...
Definition: pandaNode.cxx:2101
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
The abstract interface to all kinds of lights.
Definition: light.h:38
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
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
This global object records NodePaths that are referenced by scene graph attribs, such as ClipPlaneAtt...
bool has_light(Light *light) const
Returns true if the indicated light is listed in the attrib, false otherwise.
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.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
virtual void attrib_unref()
This is called when the light is removed from a LightAttrib.
Definition: light.cxx:173
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.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
static size_t add_hash(size_t start, const Key &key)
Adds the indicated key into a running hash.
Definition: stl_compares.I:101
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.
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().
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
get_name
Returns the name of the referenced node.
Definition: nodePath.h:946
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
void read_pointers(DatagramIterator &scan, int count)
A convenience function to read a contiguous list of pointers.
Definition: bamReader.cxx:653
virtual bool is_ambient_light() const
Returns true if this is an AmbientLight, false if it is some other kind of light.
Definition: light.cxx:63
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
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.
static UpdateSeq get_sort_seq()
Returns a global sequence number that is incremented any time any Light in the world changes sort or ...
Definition: light.I:134
Operation get_operation() const
Returns the basic operation type of the LightAttrib.
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
virtual bool is_ambient_light() const
Returns true if this is an AmbientLight, false if it is not a light, or it is some other kind of ligh...
Definition: pandaNode.cxx:2110
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Light * get_light(int n) const
Returns the nth light listed in the attribute.
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
int get_num_lights() const
Returns the number of lights listed in the attribute.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
get_node
Returns the nth NodePath recorded in the registry.
get_color
Returns the basic color of the light.
Definition: light.h:49
virtual void attrib_ref()
This is called when the light is added to a LightAttrib.
Definition: light.cxx:166
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
This is a sequence number that increments monotonically.
Definition: updateSeq.h:37
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
static void register_with_read_factory()
Tells the BamReader how to create objects of type LightAttrib.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_priority
Returns the priority associated with this light.
Definition: light.h:64
Indicates which set of lights should be considered "on" to illuminate geometry at this level and belo...
Definition: lightAttrib.h:30
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
CPT(RenderAttrib) LightAttrib
Constructs a new LightAttrib object that turns on (or off, according to op) the indicated light(s).
Definition: lightAttrib.cxx:95
virtual int complete_pointers(TypedWritable **plist, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin().