Panda3D
Loading...
Searching...
No Matches
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
25using std::endl;
26
27TypeHandle PolylightEffect::_type_handle;
28
29/**
30 * Constructs a new PolylightEffect object.
31 */
32CPT(RenderEffect) PolylightEffect::
33make() {
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 */
44CPT(RenderEffect) PolylightEffect::
45make(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 */
56CPT(RenderEffect) PolylightEffect::
57make(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 */
73has_cull_callback() const {
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 */
107CPT(RenderAttrib) PolylightEffect::
108do_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 */
338void PolylightEffect::
339output(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 */
364int PolylightEffect::
365compare_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 */
389CPT(RenderEffect) PolylightEffect::
390add_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 */
400CPT(RenderEffect) PolylightEffect::
401remove_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 */
422CPT(RenderEffect) PolylightEffect::
423set_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 */
435CPT(RenderEffect) PolylightEffect::
436set_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 */
448CPT(RenderEffect) PolylightEffect::
449set_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 */
459bool PolylightEffect::
460has_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
466std::ostream &
467operator << (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}
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
SceneSetup * get_scene() const
Returns the SceneSetup object.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
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 ...
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...
A PolylightEffect can be used on a node to define a LightGroup for that node.
virtual bool has_cull_callback() const
Should be overridden by derived classes to return true if cull_callback() has been defined.
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...
A PolylightNode.
Attenuation_Type get_attenuation() const
Get "linear" or "quadratic" attenuation type.
LPoint3 get_pos() const
Returns position as a LPoint3.
bool is_enabled() const
Is this light is enabled/disabled?
PN_stdfloat get_radius() const
Get radius of the spherical light volume.
LColor flicker() const
If flickering is on, the do_poly_light function in PolylightNodeEffect will compute this light's colo...
LColor get_color() const
Returns the light's color as LColor.
bool is_flickering() const
Check is this light is flickering.
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This is the base class for a number of special render effects that may be set on scene graph nodes to...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
This object holds the camera position, etc., and other general setup information for rendering a part...
Definition sceneSetup.h:32
const NodePath & get_scene_root() const
Returns the root node of the scene.
Definition sceneSetup.I:83
const NodePath & get_cull_center() const
Returns the point from which the culling operations will be performed.
Definition sceneSetup.I:161
Indicates a coordinate-system transform on vertices.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.