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
27CPT(RenderAttrib) LightAttrib::_empty_attrib;
28int LightAttrib::_attrib_slot;
29CPT(RenderAttrib) LightAttrib::_all_off_attrib;
30TypeHandle 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.
35class CompareLightPriorities {
36public:
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 */
54LightAttrib::
55LightAttrib(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 */
94CPT(RenderAttrib) LightAttrib::
95make(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 */
128CPT(RenderAttrib) LightAttrib::
129make(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 */
165CPT(RenderAttrib) LightAttrib::
166make(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 */
206CPT(RenderAttrib) LightAttrib::
207make(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 */
248CPT(RenderAttrib) LightAttrib::
249make_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 */
264LightAttrib::Operation LightAttrib::
265get_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 */
288get_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 */
307get_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 */
327has_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 */
344CPT(RenderAttrib) LightAttrib::
345add_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 */
362CPT(RenderAttrib) LightAttrib::
363remove_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 */
377CPT(RenderAttrib) LightAttrib::
378make() {
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 */
392CPT(RenderAttrib) LightAttrib::
393make_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 */
409CPT(RenderAttrib) LightAttrib::
410add_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 */
433CPT(RenderAttrib) LightAttrib::
434remove_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 */
450CPT(RenderAttrib) LightAttrib::
451replace_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 attrib->_on_lights.sort();
473 }
474 return return_new(attrib);
475}
476
477/**
478 * Returns a new LightAttrib, just like this one, but with the indicated light
479 * added to the list of lights turned off by this attrib.
480 */
481CPT(RenderAttrib) LightAttrib::
482add_off_light(const NodePath &light) const {
483 nassertr(!light.is_empty(), this);
484 Light *lobj = light.node()->as_light();
485 nassertr(lobj != nullptr, this);
486
487 LightAttrib *attrib = new LightAttrib(*this);
488 if (!_off_all_lights) {
489 attrib->_off_lights.insert(light);
490 }
491 if (attrib->_on_lights.erase(light)) {
492 lobj->attrib_unref();
493 }
494 return return_new(attrib);
495}
496
497/**
498 * Returns a new LightAttrib, just like this one, but with the indicated light
499 * removed from the list of lights turned off by this attrib.
500 */
501CPT(RenderAttrib) LightAttrib::
502remove_off_light(const NodePath &light) const {
503 nassertr(!light.is_empty() && light.node()->as_light() != nullptr, this);
504 LightAttrib *attrib = new LightAttrib(*this);
505 attrib->_off_lights.erase(light);
506 return return_new(attrib);
507}
508
509/**
510 * Returns a new LightAttrib, just like this one, but with the indicated light
511 * replaced with the given other light.
512 */
513CPT(RenderAttrib) LightAttrib::
514replace_off_light(const NodePath &source, const NodePath &dest) const {
515 if (source == dest) {
516 return this;
517 }
518
519 nassertr(!source.is_empty(), this);
520 Light *slobj = source.node()->as_light();
521 nassertr(slobj != nullptr, this);
522
523 nassertr(!dest.is_empty(), this);
524 Light *dlobj = dest.node()->as_light();
525 nassertr(dlobj != nullptr, this);
526
527 LightAttrib *attrib = new LightAttrib(*this);
528
529 auto it = attrib->_off_lights.find(source);
530 if (it != attrib->_off_lights.end()) {
531 dlobj->attrib_ref();
532 slobj->attrib_unref();
533
534 *it = dest;
535 attrib->_off_lights.sort();
536 }
537 return return_new(attrib);
538}
539
540/**
541 * Returns the most important light (that is, the light with the highest
542 * priority) in the LightAttrib, excluding any ambient lights. Returns an
543 * empty NodePath if no non-ambient lights are found.
544 */
545NodePath LightAttrib::
546get_most_important_light() const {
547 check_sorted();
548
549 if (_num_non_ambient_lights > 0) {
550 return _sorted_on_lights[0];
551 } else {
552 return NodePath();
553 }
554}
555
556/**
557 * Returns the total contribution of all the ambient lights.
558 */
559LColor LightAttrib::
560get_ambient_contribution() const {
561 check_sorted();
562
563 LVecBase4 total(0);
564
565 Lights::const_iterator li;
566 li = _sorted_on_lights.begin() + _num_non_ambient_lights;
567 for (; li != _sorted_on_lights.end(); ++li) {
568 const NodePath &np = (*li);
569 Light *light = np.node()->as_light();
570 nassertd(light != nullptr && light->is_ambient_light()) continue;
571
572 total += light->get_color();
573 }
574
575 return total;
576}
577
578/**
579 *
580 */
581void LightAttrib::
582output(std::ostream &out) const {
583 out << get_type() << ":";
584 if (_off_lights.empty()) {
585 if (_on_lights.empty()) {
586 if (_off_all_lights) {
587 out << "all off";
588 } else {
589 out << "identity";
590 }
591 } else {
592 if (_off_all_lights) {
593 out << "set";
594 } else {
595 out << "on";
596 }
597 }
598
599 } else {
600 out << "off";
601 Lights::const_iterator fi;
602 for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
603 NodePath light = (*fi);
604 if (light.is_empty()) {
605 out << " " << light;
606 } else {
607 out << " " << light.get_name();
608 }
609 }
610
611 if (!_on_lights.empty()) {
612 out << " on";
613 }
614 }
615
616 Lights::const_iterator li;
617 for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
618 NodePath light = (*li);
619 if (light.is_empty()) {
620 out << " " << light;
621 } else {
622 out << " " << light.get_name();
623 }
624 }
625}
626
627/**
628 *
629 */
630void LightAttrib::
631write(std::ostream &out, int indent_level) const {
632 indent(out, indent_level) << get_type() << ":";
633 if (_off_lights.empty()) {
634 if (_on_lights.empty()) {
635 if (_off_all_lights) {
636 out << "all off\n";
637 } else {
638 out << "identity\n";
639 }
640 } else {
641 if (_off_all_lights) {
642 out << "set\n";
643 } else {
644 out << "on\n";
645 }
646 }
647
648 } else {
649 out << "off\n";
650 Lights::const_iterator fi;
651 for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
652 NodePath light = (*fi);
653 indent(out, indent_level + 2) << light << "\n";
654 }
655
656 if (!_on_lights.empty()) {
657 indent(out, indent_level) << "on\n";
658 }
659 }
660
661 Lights::const_iterator li;
662 for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
663 NodePath light = (*li);
664 indent(out, indent_level + 2) << light << "\n";
665 }
666}
667
668/**
669 * Intended to be overridden by derived LightAttrib types to return a unique
670 * number indicating whether this LightAttrib is equivalent to the other one.
671 *
672 * This should return 0 if the two LightAttrib objects are equivalent, a
673 * number less than zero if this one should be sorted before the other one,
674 * and a number greater than zero otherwise.
675 *
676 * This will only be called with two LightAttrib objects whose get_type()
677 * functions return the same.
678 */
679int LightAttrib::
680compare_to_impl(const RenderAttrib *other) const {
681 const LightAttrib *ta = (const LightAttrib *)other;
682
683 if (_off_all_lights != ta->_off_all_lights) {
684 return (int)_off_all_lights - (int)ta->_off_all_lights;
685 }
686
687 Lights::const_iterator li = _on_lights.begin();
688 Lights::const_iterator oli = ta->_on_lights.begin();
689
690 while (li != _on_lights.end() && oli != ta->_on_lights.end()) {
691 NodePath light = (*li);
692 NodePath other_light = (*oli);
693
694 int compare = light.compare_to(other_light);
695 if (compare != 0) {
696 return compare;
697 }
698
699 ++li;
700 ++oli;
701 }
702
703 if (li != _on_lights.end()) {
704 return 1;
705 }
706 if (oli != ta->_on_lights.end()) {
707 return -1;
708 }
709
710 Lights::const_iterator fi = _off_lights.begin();
711 Lights::const_iterator ofi = ta->_off_lights.begin();
712
713 while (fi != _off_lights.end() && ofi != ta->_off_lights.end()) {
714 NodePath light = (*fi);
715 NodePath other_light = (*ofi);
716
717 int compare = light.compare_to(other_light);
718 if (compare != 0) {
719 return compare;
720 }
721
722 ++fi;
723 ++ofi;
724 }
725
726 if (fi != _off_lights.end()) {
727 return 1;
728 }
729 if (ofi != ta->_off_lights.end()) {
730 return -1;
731 }
732
733 return 0;
734}
735
736/**
737 * Intended to be overridden by derived RenderAttrib types to return a unique
738 * hash for these particular properties. RenderAttribs that compare the same
739 * with compare_to_impl(), above, should return the same hash; RenderAttribs
740 * that compare differently should return a different hash.
741 */
742size_t LightAttrib::
743get_hash_impl() const {
744 size_t hash = 0;
745
746 Lights::const_iterator li;
747 for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
748 NodePath light = (*li);
749 hash = light.add_hash(hash);
750 }
751
752 // This bool value goes here, between the two lists, to differentiate
753 // between the two.
754 hash = int_hash::add_hash(hash, (int)_off_all_lights);
755
756 for (li = _off_lights.begin(); li != _off_lights.end(); ++li) {
757 NodePath light = (*li);
758 hash = light.add_hash(hash);
759 }
760
761 return hash;
762}
763
764/**
765 * Intended to be overridden by derived RenderAttrib types to specify how two
766 * consecutive RenderAttrib objects of the same type interact.
767 *
768 * This should return the result of applying the other RenderAttrib to a node
769 * in the scene graph below this RenderAttrib, which was already applied. In
770 * most cases, the result is the same as the other RenderAttrib (that is, a
771 * subsequent RenderAttrib completely replaces the preceding one). On the
772 * other hand, some kinds of RenderAttrib (for instance, ColorTransformAttrib)
773 * might combine in meaningful ways.
774 */
775CPT(RenderAttrib) LightAttrib::
776compose_impl(const RenderAttrib *other) const {
777 const LightAttrib *ta = (const LightAttrib *)other;
778
779 if (ta->_off_all_lights) {
780 // If the other type turns off all lights, it doesn't matter what we are.
781 return ta;
782 }
783
784 // This is a three-way merge between ai, bi, and ci, except that bi and ci
785 // should have no intersection and therefore needn't be compared to each
786 // other.
787 Lights::const_iterator ai = _on_lights.begin();
788 Lights::const_iterator bi = ta->_on_lights.begin();
789 Lights::const_iterator ci = ta->_off_lights.begin();
790
791 // Create a new LightAttrib that will hold the result.
792 LightAttrib *new_attrib = new LightAttrib;
793 std::back_insert_iterator<Lights> result =
794 std::back_inserter(new_attrib->_on_lights);
795
796 while (ai != _on_lights.end() &&
797 bi != ta->_on_lights.end() &&
798 ci != ta->_off_lights.end()) {
799 if ((*ai) < (*bi)) {
800 if ((*ai) < (*ci)) {
801 // Here is a light that we have in the original, which is not present
802 // in the secondary.
803 *result = *ai;
804 ++ai;
805 ++result;
806
807 } else if ((*ci) < (*ai)) {
808 // Here is a light that is turned off in the secondary, but was not
809 // present in the original.
810 ++ci;
811
812 } else { // (*ci) == (*ai)
813 // Here is a light that is turned off in the secondary, and was
814 // present in the original.
815 ++ai;
816 ++ci;
817 }
818
819 } else if ((*bi) < (*ai)) {
820 // Here is a new light we have in the secondary, that was not present in
821 // the original.
822 *result = *bi;
823 ++bi;
824 ++result;
825
826 } else { // (*bi) == (*ai)
827 // Here is a light we have in both.
828 *result = *bi;
829 ++ai;
830 ++bi;
831 ++result;
832 }
833 }
834
835 while (ai != _on_lights.end() && bi != ta->_on_lights.end()) {
836 if ((*ai) < (*bi)) {
837 // Here is a light that we have in the original, which is not present in
838 // the secondary.
839 *result = *ai;
840 ++ai;
841 ++result;
842
843 } else if ((*bi) < (*ai)) {
844 // Here is a new light we have in the secondary, that was not present in
845 // the original.
846 *result = *bi;
847 ++bi;
848 ++result;
849
850 } else {
851 // Here is a light we have in both.
852 *result = *bi;
853 ++ai;
854 ++bi;
855 ++result;
856 }
857 }
858
859 while (ai != _on_lights.end() && ci != ta->_off_lights.end()) {
860 if ((*ai) < (*ci)) {
861 // Here is a light that we have in the original, which is not present in
862 // the secondary.
863 *result = *ai;
864 ++ai;
865 ++result;
866
867 } else if ((*ci) < (*ai)) {
868 // Here is a light that is turned off in the secondary, but was not
869 // present in the original.
870 ++ci;
871
872 } else { // (*ci) == (*ai)
873 // Here is a light that is turned off in the secondary, and was present
874 // in the original.
875 ++ai;
876 ++ci;
877 }
878 }
879
880 while (ai != _on_lights.end()) {
881 *result = *ai;
882 ++ai;
883 ++result;
884 }
885
886 while (bi != ta->_on_lights.end()) {
887 *result = *bi;
888 ++bi;
889 ++result;
890 }
891
892 // Increase the attrib_ref of all the lights in this new attribute.
893 Lights::const_iterator it;
894 for (it = new_attrib->_on_lights.begin(); it != new_attrib->_on_lights.end(); ++it) {
895 Light *lobj = (*it).node()->as_light();
896 nassertd(lobj != nullptr) continue;
897 lobj->attrib_ref();
898 }
899
900 // This is needed since _sorted_on_lights is not yet populated.
901 new_attrib->_sort_seq = UpdateSeq::old();
902
903 return return_new(new_attrib);
904}
905
906/**
907 * Intended to be overridden by derived RenderAttrib types to specify how two
908 * consecutive RenderAttrib objects of the same type interact.
909 *
910 * See invert_compose() and compose_impl().
911 */
913invert_compose_impl(const RenderAttrib *other) const {
914 // I think in this case the other attrib always wins. Maybe this needs a
915 // bit more thought. It's hard to imagine that it's even important to
916 // compute this properly.
917 return other;
918}
919
920/**
921 * Makes sure the lights are sorted in order of priority. Also counts the
922 * number of non-ambient lights.
923 */
924void LightAttrib::
925sort_on_lights() {
926 _sort_seq = Light::get_sort_seq();
927
928 // Separate the list of lights into ambient lights and other lights.
929 _sorted_on_lights.clear();
930 OrderedLights ambient_lights;
931
932 Lights::const_iterator li;
933 for (li = _on_lights.begin(); li != _on_lights.end(); ++li) {
934 const NodePath &np = (*li);
935 nassertd(!np.is_empty() && np.node()->as_light() != nullptr) continue;
936
937 if (!np.node()->is_ambient_light()) {
938 _sorted_on_lights.push_back(np);
939 } else {
940 ambient_lights.push_back(np);
941 }
942 }
943
944 // Remember how many lights were non-ambient lights, which makes it easier
945 // to traverse through the list of non-ambient lights.
946 _num_non_ambient_lights = _sorted_on_lights.size();
947
948 // This sort function uses the STL function object defined above.
949 sort(_sorted_on_lights.begin(), _sorted_on_lights.end(),
950 CompareLightPriorities());
951
952 // Now insert the ambient lights back at the end. We don't really care
953 // about their relative priorities, because their contribution will simply
954 // be summed up in the end anyway.
955 _sorted_on_lights.insert(_sorted_on_lights.end(),
956 ambient_lights.begin(), ambient_lights.end());
957}
958
959/**
960 * Tells the BamReader how to create objects of type LightAttrib.
961 */
964 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
965}
966
967/**
968 * Writes the contents of this object to the datagram for shipping out to a
969 * Bam file.
970 */
972write_datagram(BamWriter *manager, Datagram &dg) {
973 RenderAttrib::write_datagram(manager, dg);
974
975 dg.add_bool(_off_all_lights);
976
977 // write the number of off_lights
979
980 // write the off lights pointers if any
981 Lights::const_iterator fi;
982 if (manager->get_file_minor_ver() < 40) {
983 for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
984 manager->write_pointer(dg, fi->node());
985 }
986 } else {
987 for (fi = _off_lights.begin(); fi != _off_lights.end(); ++fi) {
988 (*fi).write_datagram(manager, dg);
989 }
990 }
991
992 // write the number of on lights
994 // write the on lights pointers if any
995 Lights::const_iterator nti;
996 if (manager->get_file_minor_ver() < 40) {
997 for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
998 manager->write_pointer(dg, nti->node());
999 }
1000 } else {
1001 for (nti = _on_lights.begin(); nti != _on_lights.end(); ++nti) {
1002 (*nti).write_datagram(manager, dg);
1003 }
1004 }
1005}
1006
1007/**
1008 * Receives an array of pointers, one for each time manager->read_pointer()
1009 * was called in fillin(). Returns the number of pointers processed.
1010 */
1012complete_pointers(TypedWritable **p_list, BamReader *manager) {
1013 int pi = RenderAttrib::complete_pointers(p_list, manager);
1014
1015 if (manager->get_file_minor_ver() >= 40) {
1016 for (size_t i = 0; i < _off_lights.size(); ++i) {
1017 pi += _off_lights[i].complete_pointers(p_list + pi, manager);
1018 }
1019
1020 for (size_t i = 0; i < _on_lights.size(); ++i) {
1021 pi += _on_lights[i].complete_pointers(p_list + pi, manager);
1022 }
1023
1024 } else {
1025 BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
1026 nassertr(aux != nullptr, pi);
1027
1028 int i;
1029 aux->_off_list.reserve(aux->_num_off_lights);
1030 for (i = 0; i < aux->_num_off_lights; ++i) {
1031 PandaNode *node;
1032 DCAST_INTO_R(node, p_list[pi++], pi);
1033 aux->_off_list.push_back(node);
1034 }
1035
1036 aux->_on_list.reserve(aux->_num_on_lights);
1037 for (i = 0; i < aux->_num_on_lights; ++i) {
1038 PandaNode *node;
1039 DCAST_INTO_R(node, p_list[pi++], pi);
1040 aux->_on_list.push_back(node);
1041 }
1042 }
1043
1044 return pi;
1045}
1046
1047/**
1048 * Called by the BamReader to perform any final actions needed for setting up
1049 * the object after all objects have been read and all pointers have been
1050 * completed.
1051 */
1053finalize(BamReader *manager) {
1054 if (manager->get_file_minor_ver() >= 40) {
1055 AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
1056
1057 // Check if any of the nodes we loaded are mentioned in the
1058 // AttribNodeRegistry. If so, replace them.
1059 for (size_t i = 0; i < _off_lights.size(); ++i) {
1060 int n = areg->find_node(_off_lights[i]);
1061 if (n != -1) {
1062 // If it's in the registry, replace it.
1063 _off_lights[i] = areg->get_node(n);
1064 }
1065 }
1066
1067 for (size_t i = 0; i < _on_lights.size(); ++i) {
1068 int n = areg->find_node(_on_lights[i]);
1069 if (n != -1) {
1070 // If it's in the registry, replace it.
1071 _on_lights[i] = areg->get_node(n);
1072 }
1073
1074 Light *lobj = _on_lights[i].node()->as_light();
1075 nassertd(lobj != nullptr) continue;
1076 lobj->attrib_ref();
1077 }
1078
1079 } else {
1080 // Now it's safe to convert our saved PandaNodes into NodePaths.
1081 BamAuxData *aux = (BamAuxData *)manager->get_aux_data(this, "lights");
1082 nassertv(aux != nullptr);
1083 nassertv(aux->_num_off_lights == (int)aux->_off_list.size());
1084 nassertv(aux->_num_on_lights == (int)aux->_on_list.size());
1085
1086 AttribNodeRegistry *areg = AttribNodeRegistry::get_global_ptr();
1087
1088 _off_lights.reserve(aux->_off_list.size());
1089 NodeList::iterator ni;
1090 for (ni = aux->_off_list.begin(); ni != aux->_off_list.end(); ++ni) {
1091 PandaNode *node = (*ni);
1092 int n = areg->find_node(node->get_type(), node->get_name());
1093 if (n != -1) {
1094 // If it's in the registry, add that NodePath.
1095 _off_lights.push_back(areg->get_node(n));
1096 } else {
1097 // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
1098 _off_lights.push_back(NodePath(node));
1099 }
1100 }
1101
1102 _on_lights.reserve(aux->_on_list.size());
1103 for (ni = aux->_on_list.begin(); ni != aux->_on_list.end(); ++ni) {
1104 PandaNode *node = (*ni);
1105 int n = areg->find_node(node->get_type(), node->get_name());
1106 if (n != -1) {
1107 // If it's in the registry, add that NodePath.
1108 _on_lights.push_back(areg->get_node(n));
1109 node = _on_lights.back().node();
1110 } else {
1111 // Otherwise, add any arbitrary NodePath. Complain if it's ambiguous.
1112 _on_lights.push_back(NodePath(node));
1113 }
1114
1115 Light *lobj = node->as_light();
1116 nassertd(lobj != nullptr) continue;
1117 lobj->attrib_ref();
1118 }
1119 }
1120
1121 // Now that the NodePaths have been filled in, we can sort the list.
1122 _off_lights.sort();
1123 _on_lights.sort();
1124}
1125
1126/**
1127 * This function is called by the BamReader's factory when a new object of
1128 * type LightAttrib is encountered in the Bam file. It should create the
1129 * LightAttrib and extract its information from the file.
1130 */
1131TypedWritable *LightAttrib::
1132make_from_bam(const FactoryParams &params) {
1133 LightAttrib *attrib = new LightAttrib;
1134 DatagramIterator scan;
1135 BamReader *manager;
1136
1137 parse_params(params, scan, manager);
1138 attrib->fillin(scan, manager);
1139
1140 manager->register_finalize(attrib);
1141
1142 return attrib;
1143}
1144
1145/**
1146 * This internal function is called by make_from_bam to read in all of the
1147 * relevant data from the BamFile for the new LightAttrib.
1148 */
1149void LightAttrib::
1150fillin(DatagramIterator &scan, BamReader *manager) {
1151 RenderAttrib::fillin(scan, manager);
1152
1153 _off_all_lights = scan.get_bool();
1154
1155 if (manager->get_file_minor_ver() >= 40) {
1156 _off_lights.resize(scan.get_uint16());
1157 for (size_t i = 0; i < _off_lights.size(); ++i) {
1158 _off_lights[i].fillin(scan, manager);
1159 }
1160
1161 _on_lights.resize(scan.get_uint16());
1162 for (size_t i = 0; i < _on_lights.size(); ++i) {
1163 _on_lights[i].fillin(scan, manager);
1164 }
1165 } else {
1166 BamAuxData *aux = new BamAuxData;
1167 manager->set_aux_data(this, "lights", aux);
1168
1169 aux->_num_off_lights = scan.get_uint16();
1170 manager->read_pointers(scan, aux->_num_off_lights);
1171
1172 aux->_num_on_lights = scan.get_uint16();
1173 manager->read_pointers(scan, aux->_num_on_lights);
1174 }
1175
1176 _sorted_on_lights.clear();
1177 _sort_seq = UpdateSeq::old();
1178}
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.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: dcindent.cxx:22
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
STL namespace.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.