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 }
nodePath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
polylightNode.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
cullTraverser.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SceneSetup::get_scene_root
const NodePath & get_scene_root() const
Returns the root node of the scene.
Definition: sceneSetup.I:83
PolylightNode::is_flickering
bool is_flickering() const
Check is this light is flickering.
Definition: polylightNode.I:202
RenderEffect
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
SceneSetup::get_cull_center
const NodePath & get_cull_center() const
Returns the point from which the culling operations will be performed.
Definition: sceneSetup.I:161
CullTraverser::get_scene
SceneSetup * get_scene() const
Returns the SceneSetup object.
Definition: cullTraverser.I:35
polylightEffect.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
CullTraverser
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
Definition: cullTraverser.h:45
RenderAttrib
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
RenderState
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
PolylightNode::get_pos
LPoint3 get_pos() const
Returns position as a LPoint3.
Definition: polylightNode.I:88
SceneSetup
This object holds the camera position, etc., and other general setup information for rendering a part...
Definition: sceneSetup.h:32
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
pmap.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
NodePath::get_relative_point
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
cullTraverserData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PolylightNode::get_color
LColor get_color() const
Returns the light's color as LColor.
Definition: polylightNode.I:311
PolylightNode::is_enabled
bool is_enabled() const
Is this light is enabled/disabled?
Definition: polylightNode.I:46
PolylightEffect::cull_callback
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...
Definition: polylightEffect.cxx:90
colorScaleAttrib.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PolylightEffect::has_cull_callback
virtual bool has_cull_callback() const
Should be overridden by derived classes to return true if cull_callback() has been defined.
Definition: polylightEffect.cxx:73
TransformState
Indicates a coordinate-system transform on vertices.
Definition: transformState.h:54
CPT
CPT(RenderEffect) PolylightEffect
Constructs a new PolylightEffect object.
Definition: polylightEffect.cxx:32
PolylightEffect
A PolylightEffect can be used on a node to define a LightGroup for that node.
Definition: polylightEffect.h:35
CullTraverserData
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Definition: cullTraverserData.h:40
PolylightNode::flicker
LColor flicker() const
If flickering is on, the do_poly_light function in PolylightNodeEffect will compute this light's colo...
Definition: polylightNode.cxx:88
PolylightNode::get_attenuation
Attenuation_Type get_attenuation() const
Get "linear" or "quadratic" attenuation type.
Definition: polylightNode.I:123
PolylightNode::get_radius
PN_stdfloat get_radius() const
Get radius of the spherical light volume.
Definition: polylightNode.I:104
NodePath
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
PolylightNode
A PolylightNode.
Definition: polylightNode.h:29
NodePath::get_color_scale
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
config_pgraph.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.