Panda3D
 All Classes Functions Variables Enumerations
polylightEffect.cxx
1 // Filename: polylightEffect.cxx
2 // Created by: sshodhan (02Jun04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "polylightEffect.h"
16 #include "polylightNode.h"
17 #include "config_pgraph.h"
18 #include "nodePath.h"
19 #include "pmap.h"
20 #include "colorScaleAttrib.h"
21 #include "cullTraverserData.h"
22 #include "cullTraverser.h"
23 
24 #include <math.h>
25 
26 TypeHandle PolylightEffect::_type_handle;
27 
28 ////////////////////////////////////////////////////////////////////
29 // Function: PolylightEffect::make
30 // Access: Published, Static
31 // Description: Constructs a new PolylightEffect object.
32 ////////////////////////////////////////////////////////////////////
34 make() {
35  PolylightEffect *effect = new PolylightEffect;
36  effect->_contribution_type = CT_proximal;
37  effect->_weight = 1.0; // 0.9; // Asad: I don't think we should monkey with the weight.
38  effect->_effect_center = LPoint3(0.0,0.0,0.0);
39  return return_new(effect);
40 }
41 
42 ////////////////////////////////////////////////////////////////////
43 // Function: PolylightEffect::make
44 // Access: Published, Static
45 // Description: Constructs a new PolylightEffect object.
46 ////////////////////////////////////////////////////////////////////
48 make(PN_stdfloat weight, ContribType contrib, const LPoint3 &effect_center) {
49  PolylightEffect *effect = new PolylightEffect;
50  effect->_contribution_type = contrib;
51  effect->_weight = weight;
52  effect->_effect_center = effect_center;
53  return return_new(effect);
54 }
55 
56 ////////////////////////////////////////////////////////////////////
57 // Function: PolylightEffect::make
58 // Access: Published, Static
59 // Description: Constructs a new PolylightEffect object.
60 ////////////////////////////////////////////////////////////////////
62 make(PN_stdfloat weight, ContribType contrib, const LPoint3 &effect_center,
63  const LightGroup &lights) {
64  PolylightEffect *effect = new PolylightEffect;
65  effect->_contribution_type = contrib;
66  effect->_weight = weight;
67  effect->_effect_center = effect_center;
68  effect->_lightgroup = lights;
69  return return_new(effect);
70 }
71 
72 ////////////////////////////////////////////////////////////////////
73 // Function: PolylightEffect::has_cull_callback
74 // Access: Public, Virtual
75 // Description: Should be overridden by derived classes to return
76 // true if cull_callback() has been defined. Otherwise,
77 // returns false to indicate cull_callback() does not
78 // need to be called for this effect during the cull
79 // traversal.
80 ////////////////////////////////////////////////////////////////////
83  return !_lightgroup.empty();
84 }
85 
86 ////////////////////////////////////////////////////////////////////
87 // Function: PolylightEffect::cull_callback
88 // Access: Public, Virtual
89 // Description: If has_cull_callback() returns true, this function
90 // will be called during the cull traversal to perform
91 // any additional operations that should be performed at
92 // cull time. This may include additional manipulation
93 // of render state or additional visible/invisible
94 // decisions, or any other arbitrary operation.
95 //
96 // At the time this function is called, the current
97 // node's transform and state have not yet been applied
98 // to the net_transform and net_state. This callback
99 // may modify the node_transform and node_state to apply
100 // an effective change to the render state at this
101 // level.
102 ////////////////////////////////////////////////////////////////////
105  CPT(TransformState) &node_transform,
106  CPT(RenderState) &node_state) const {
107  //CPT(RenderAttrib) poly_light_attrib = do_poly_light(trav->get_scene()->get_scene_root(), &data, node_transform);
108  CPT(RenderAttrib) poly_light_attrib = do_poly_light(trav->get_scene(), &data, node_transform);
109  CPT(RenderState) poly_light_state = RenderState::make(poly_light_attrib);
110  node_state = node_state->compose(poly_light_state);
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: PolylightEffect::do_poly_light
115 // Access: Public
116 // Description: Gets the node's position and based on distance from
117 // lights in the lightgroup calculates the color to be
118 // modulated in. If the avatar is in the range of
119 // multiple lights, then determine, which light it is
120 // closer to, and get the weight of the scene_color
121 // in respect to that light's proximity.
122 ////////////////////////////////////////////////////////////////////
124 do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const TransformState *node_transform) const {
125  //static bool was_under_polylight = false;
126  PN_stdfloat dist; // To calculate the distance of each light from the node
127  PN_stdfloat r,g,b; // To hold the color calculation
128  PN_stdfloat min_dist; // hold the dist from light that avatar is closer to
129  int num_lights = 0; // Keep track of number of lights for division
130  PN_stdfloat light_scale; // Variable to calculate attenuation
131  PN_stdfloat weight_scale = 1.0f; // Variable to compensate snap of color when you walk inside the light volume
132  PN_stdfloat Rcollect, Gcollect, Bcollect;
133 
134  const NodePath &root = scene->get_scene_root();
135  //const NodePath &camera = scene->get_camera_path();
136  const NodePath &camera = scene->get_cull_center();
137 
138  // Initialize Color variables
139  r = g = b = 1.0;
140  Rcollect = Gcollect = Bcollect = 0.0;
141 
142  // get the avatar's base color scale
143  LColor scene_color = root.get_color_scale();
144  if (polylight_info) {
145  pgraph_cat.debug() << "scene color scale = " << scene_color << endl;
146  }
147  min_dist = 100000.0;
148  // Cycle through all the lights in this effect's lightgroup
149  LightGroup::const_iterator light_iter;
150  for (light_iter = _lightgroup.begin(); light_iter != _lightgroup.end(); light_iter++){
151  const PolylightNode *light = DCAST(PolylightNode, (*light_iter).node());
152 
153  // light holds the current PolylightNode
154  if (light->is_enabled()) { // if enabled get all the properties
155  PN_stdfloat light_radius = light->get_radius();
156  // Calculate the distance of the node from the light
157  //dist = light_iter->second->get_distance(data->_node_path.get_node_path());
158  const NodePath lightnp = *light_iter;
159  LPoint3 relative_point = data->_node_path.get_node_path().get_relative_point(lightnp, light->get_pos());
160 
161  if (_effect_center[2]) {
162  dist = (relative_point - _effect_center).length(); // this counts height difference
163  } else {
164  // get distance as if the light is at the same height of player
165  LVector2 xz(relative_point[0], relative_point[1]);
166  dist = xz.length(); // this does not count height difference
167  }
168 
169  if (dist <= light_radius) { // If node is in range of this light
170  // as to Schuyler's suggestion, lets do some vector processing relative to camera
171  LPoint3 light_position = light->get_pos();
172  //LPoint3 camera_position = camera.get_relative_point(lightnp, light->get_pos());
173  LPoint3 camera_position = lightnp.get_relative_point(camera, LPoint3(0,0,0));
174  LPoint3 avatar_position = lightnp.get_relative_point(data->_node_path.get_node_path(), LPoint3(0,0,0));
175  LVector3 light_camera = camera_position - light_position;
176  LVector3 light_avatar = avatar_position - light_position;
177  light_camera.normalize();
178  light_avatar.normalize();
179  PN_stdfloat intensity = light_camera.dot(light_avatar);
180 
181  if (polylight_info) {
182  pgraph_cat.debug() << "light position = " << light_position << endl;
183  pgraph_cat.debug() << "relative avatar position = " << avatar_position << endl;
184  pgraph_cat.debug() << "relative camera position = " << camera_position << endl;
185  pgraph_cat.debug() << "light->camera " << light_camera << endl;
186  pgraph_cat.debug() << "light->avatar " << light_avatar << endl;
187  pgraph_cat.debug() << "light->camera.light->avatar = " << intensity << endl;
188  pgraph_cat.debug() << "effect center = " << _effect_center << endl;
189  //pgraph_cat.debug() << "close to this light = " << light->get_name() << endl;
190  pgraph_cat.debug() << "dist = " << dist << ";radius = " << light_radius << endl;
191  }
192 
193  // map -1 to 1 into 0 to 1; and flip it
194  intensity = 1.0 - ((intensity + 1.0) * 0.5);
195  if (polylight_info) {
196  pgraph_cat.debug() << "remapped intensity = " << intensity << endl;
197  }
198 
199  PolylightNode::Attenuation_Type light_attenuation = light->get_attenuation();
200  LColor light_color;
201  if (light->is_flickering()) { // If flickering, modify color
202  light_color = light->flicker();
203  } else {
204  light_color = light->get_color();
205  //light_color = light->get_color_scenegraph();
206  }
207 
208  PN_stdfloat ratio = dist/light_radius;
209  if (light_attenuation == PolylightNode::ALINEAR) {
210  light_scale = 1.0 - ratio;
211  } else if (light_attenuation == PolylightNode::AQUADRATIC) {
212  /*
213  PN_stdfloat fd; // Variable for quadratic attenuation
214  PN_stdfloat light_a0 = light->get_a0();
215  PN_stdfloat light_a1 = light->get_a1();
216  PN_stdfloat light_a2 = light->get_a2();
217  if (light_a0 == 0 && light_a1 == 0 && light_a2 == 0) { // To prevent division by zero
218  light_a0 = 1.0;
219  }
220  fd = 1.0 / (light_a0 + light_a1 * dist + light_a2 * dist * dist);
221  if (fd < 1.0) {
222  light_scale = fd;
223  } else {
224  light_scale = 1.0;
225  }
226  */
227  //light_scale = 1.0 - ratio*ratio; // graph of 1-x^2
228  ratio = 1 - ratio;
229  if (ratio <= 0.8)
230  light_scale = (ratio*ratio)*(3-2*ratio); //graph of x^2(3-x)
231  else
232  light_scale = 1.0;
233  } else {
234  light_scale = 1.0;
235  }
236 
237  light_scale *= intensity;
238 
239  if (min_dist > dist) {
240  min_dist = dist;
241  // Keep accumulating each lights contribution...
242  // we have to prevent color snap, so factor in the weight.
243  // weight becomes negligent as you are closer to the light
244  // and opposite otherwise
245  weight_scale = _weight * (1.0 - light_scale);
246  }
247 
248  if (polylight_info) {
249  pgraph_cat.debug() << "weight_scale = " << weight_scale
250  << "; light_scale " << light_scale << endl;
251  }
252 
253  Rcollect += light_color[0] * light_scale;
254  Gcollect += light_color[1] * light_scale;
255  Bcollect += light_color[2] * light_scale;
256  /*
257  Rcollect += light_color[0] * light_scale + scene_color[0] * weight_scale;
258  Gcollect += light_color[1] * light_scale + scene_color[1] * weight_scale;
259  Bcollect += light_color[2] * light_scale + scene_color[2] * weight_scale;
260  */
261  /*
262  Rcollect += light_color[0] * light_scale + weight_scale;
263  Gcollect += light_color[1] * light_scale + weight_scale;
264  Bcollect += light_color[2] * light_scale + weight_scale;
265  */
266 
267 
268  num_lights++;
269  } // if dist< radius
270  } // if light is enabled
271  } // for all lights
272 
273  if ( _contribution_type == CT_all) {
274  // Sometimes to prevent snapping of color at light volume boundaries
275  // just divide total contribution by all the lights in the effect
276  // whether or not they contribute color
277  num_lights = _lightgroup.size();
278  }
279 
280  if (num_lights) {
281  //was_under_polylight = true;
282  //data->_node_path.get_node_path().set_color_scale_off();
283  if (polylight_info)
284  pgraph_cat.debug() << "num lights = " << num_lights << endl;
285 
286  // divide by number of lights to get average.
287  r = Rcollect;// / num_lights;
288  g = Gcollect;// / num_lights;
289  b = Bcollect;// / num_lights;
290 
291  if (polylight_info)
292  pgraph_cat.debug() << "avg: r=" << r << "; g=" << g << "; b=" << b << endl;
293 
294  // Now add the scene_color multiplied by weight_scale
295  r += scene_color[0] * weight_scale;
296  g += scene_color[1] * weight_scale;
297  b += scene_color[2] * weight_scale;
298  if (polylight_info)
299  pgraph_cat.debug() << "weighed: r=" << r << "; g=" << g << "; b=" << b << endl;
300 
301  /*
302  // normalize the color
303  LVector3 color_vector(r, g, b);
304  color_vector.normalize();
305  r = color_vector[0];
306  g = color_vector[1];
307  b = color_vector[2];
308 
309  if (polylight_info)
310  pgraph_cat.debug() << "unit: r=" << r << "; g=" << g << "; b=" << b << endl;
311  */
312 
313  // cap it
314  r = (r > 1.0)? 1.0 : r;
315  g = (g > 1.0)? 1.0 : g;
316  b = (b > 1.0)? 1.0 : b;
317 
318  if (polylight_info)
319  pgraph_cat.debug() << "capped: r=" << r << "; g=" << g << "; b=" << b << endl;
320 
321  // since this rgb will be scaled by scene_color by day night
322  // cycle, lets undo that effect by dividing this rgb by the
323  // scene_color. That way, the final render will contain this rgb
324  if (scene_color[0] >= 0.01)
325  r /= scene_color[0];
326  if (scene_color[1] >= 0.01)
327  g /= scene_color[1];
328  if (scene_color[2] >= 0.01)
329  b /= scene_color[2];
330 
331  if (polylight_info)
332  pgraph_cat.debug() << "final: r=" << r << "; g=" << g << "; b=" << b << endl;
333 
334  }
335  /*
336  else {
337  if (was_under_polylight) {
338  // under no polylight influence...so clear the color scale
339  //data->_node_path.get_node_path().clear_color_scale();
340  //data->_node_path.get_node_path().set_color_scale(scene_color);
341  was_under_polylight = false;
342  }
343  }
344  */
345 
346  return ColorScaleAttrib::make(LVecBase4(r, g, b, 1.0));
347 }
348 
349 ////////////////////////////////////////////////////////////////////
350 // Function: PolylightEffect::output
351 // Access: Public
352 // Description:
353 ////////////////////////////////////////////////////////////////////
354 void PolylightEffect::
355 output(ostream &out) const {
356  out << get_type() << ":";
357 
358  LightGroup::const_iterator li;
359  for (li = _lightgroup.begin(); li != _lightgroup.end(); ++li) {
360  NodePath light = (*li);
361  out << " " << light;
362  }
363  out << " weight " << _weight << " contrib " << _contribution_type
364  << " center " << _effect_center;
365 }
366 
367 
368 ////////////////////////////////////////////////////////////////////
369 // Function: PolylightEffect::compare_to_impl
370 // Access: Protected, Virtual
371 // Description: Intended to be overridden by derived PolylightEffect
372 // types to return a unique number indicating whether
373 // this PolylightEffect is equivalent to the other one.
374 //
375 // This should return 0 if the two PolylightEffect objects
376 // are equivalent, a number less than zero if this one
377 // should be sorted before the other one, and a number
378 // greater than zero otherwise.
379 //
380 // This will only be called with two PolylightEffect
381 // objects whose get_type() functions return the same.
382 ////////////////////////////////////////////////////////////////////
383 int PolylightEffect::
384 compare_to_impl(const RenderEffect *other) const {
385  const PolylightEffect *ta;
386  DCAST_INTO_R(ta, other, 0);
387 
388  if (_contribution_type != ta->_contribution_type) {
389  return _contribution_type < ta->_contribution_type ? -1 : 1;
390  }
391 
392  if (_weight != ta->_weight) {
393  return _weight < ta->_weight ? -1 :1;
394  }
395 
396  if (_lightgroup != ta->_lightgroup) {
397  return _lightgroup < ta->_lightgroup ? -1 : 1;
398  }
399 
400  return 0;
401 }
402 
403 
404 
405 ////////////////////////////////////////////////////////////////////
406 // Function: PolylightEffect::add_light
407 // Access: Published
408 // Description: Add a PolylightNode object to this effect and return
409 // a new effect
410 ////////////////////////////////////////////////////////////////////
412 add_light(const NodePath &newlight) const {
413  PolylightEffect *effect = new PolylightEffect(*this);
414  effect->_lightgroup.push_back(newlight);
415  return return_new(effect);
416 }
417 
418 
419 ////////////////////////////////////////////////////////////////////
420 // Function: PolylightEffect::remove_light
421 // Access: Published
422 // Description: Remove a light from this effect. Return the new updated
423 // effect
424 ////////////////////////////////////////////////////////////////////
426 remove_light(const NodePath &newlight) const {
427  PolylightEffect *effect = new PolylightEffect(*this);
428  LightGroup::iterator light_iter;
429  light_iter = find(effect->_lightgroup.begin(),effect->_lightgroup.end(), newlight);
430  if (light_iter == effect->_lightgroup.end()) {
431  pgraph_cat.debug()
432  << "Attempt to remove Polylight " << newlight << "; not found.\n";
433  } else {
434  // Remove light
435  effect->_lightgroup.erase(light_iter);
436  }
437  return return_new(effect);
438 
439 }
440 
441 ////////////////////////////////////////////////////////////////////
442 // Function: PolylightEffect::set_weight
443 // Access: Published
444 // Description: Set weight and return a new effect... the reason
445 // this couldnt be done through make was because
446 // that would return a new effect without the
447 // lightgroup which is static and cant be accessed
448 // Here, we just pass that to the make
449 ////////////////////////////////////////////////////////////////////
451 set_weight(PN_stdfloat w) const {
452  PolylightEffect *effect = new PolylightEffect(*this);
453  effect->_weight = w;
454  return return_new(effect);
455 }
456 
457 ////////////////////////////////////////////////////////////////////
458 // Function: PolylightEffect::set_contrib
459 // Access: Published
460 // Description: Set Contrib Type and return a new effect... the reason
461 // this couldnt be done through make was because
462 // that would return a new effect without the
463 // lightgroup which is static and cant be accessed
464 // Here, we just pass that to the make
465 ////////////////////////////////////////////////////////////////////
467 set_contrib(ContribType ct) const {
468  PolylightEffect *effect = new PolylightEffect(*this);
469  effect->_contribution_type = ct;
470  return return_new(effect);
471 }
472 
473 ////////////////////////////////////////////////////////////////////
474 // Function: PolylightEffect::set_effect_center
475 // Access: Published
476 // Description: Set weight and return a new effect... the reason
477 // this couldnt be done through make was because
478 // that would return a new effect without the
479 // lightgroup which is static and cant be accessed
480 // Here, we just pass that to the make
481 ////////////////////////////////////////////////////////////////////
483 set_effect_center(const LPoint3 &ec) const{
484  PolylightEffect *effect = new PolylightEffect(*this);
485  effect->_effect_center = ec;
486  return return_new(effect);
487 }
488 
489 ////////////////////////////////////////////////////////////////////
490 // Function: PolylightEffect::has_light
491 // Access: Published
492 // Description: Returns true if the indicated light is listed in the
493 // PolylightEffect, false otherwise.
494 ////////////////////////////////////////////////////////////////////
496 has_light(const NodePath &light) const {
497  LightGroup::const_iterator li;
498  li = find(_lightgroup.begin(), _lightgroup.end(), light);
499  return (li != _lightgroup.end());
500 }
501 
502 ostream &
503 operator << (ostream &out, PolylightEffect::ContribType ct) {
504  switch (ct) {
505  case PolylightEffect::CT_proximal:
506  return out << "proximal";
507 
508  case PolylightEffect::CT_all:
509  return out << "all";
510  }
511 
512  return out << "**Invalid ContribType(" << (int)ct << ")**";
513 }
bool is_flickering() const
Check is this light is flickering.
virtual bool has_cull_callback() const
Should be overridden by derived classes to return true if cull_callback() has been defined...
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
PN_stdfloat get_radius() const
Get radius of the spherical light volume.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the base class for a number of special render effects that may be set on scene graph nodes to...
Definition: renderEffect.h:56
Attenuation_Type get_attenuation() const
Get &quot;linear&quot; or &quot;quadratic&quot; attenuation type.
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:43
A PolylightEffect can be used on a node to define a LightGroup for that node.
LColor get_color() const
Returns the light&#39;s color as LColor.
virtual void cull_callback(CullTraverser *trav, CullTraverserData &data, CPT(TransformState)&node_transform, CPT(RenderState)&node_state) const
If has_cull_callback() returns true, this function will be called during the cull traversal to perfor...
const LVecBase4 & get_color_scale() const
Returns the complete color scale vector that has been applied to this node via a previous call to set...
Definition: nodePath.cxx:2536
bool has_light(const NodePath &light) const
Returns true if the indicated light is listed in the PolylightEffect, false otherwise.
A PolylightNode.
Definition: polylightNode.h:31
LColor flicker() const
If flickering is on, the do_poly_light function in PolylightNodeEffect will compute this light&#39;s colo...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
bool is_enabled() const
Is this light is enabled/disabled?
Definition: polylightNode.I:58
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This is a two-component vector offset.
Definition: lvector2.h:91
LPoint3 get_pos() const
Returns position as a LPoint3.
LPoint3 get_relative_point(const NodePath &other, const LVecBase3 &point) const
Given that the indicated point is in the coordinate system of the other node, returns the same point ...
Definition: nodePath.cxx:2198
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
This object holds the camera position, etc., and other general setup information for rendering a part...
Definition: sceneSetup.h:35
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:782
static int size()
Returns 4: the number of components of a LVecBase4.
Definition: lvecBase4.h:564
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48