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