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