Panda3D

polylightEffect.cxx

00001 // Filename: polylightEffect.cxx
00002 // Created by:  sshodhan (02Jun04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "polylightEffect.h"
00016 #include "polylightNode.h"
00017 #include "config_pgraph.h"
00018 #include "nodePath.h"
00019 #include "pmap.h"
00020 #include "colorScaleAttrib.h"
00021 #include "cullTraverserData.h"
00022 #include "cullTraverser.h"
00023 
00024 #include <math.h>
00025 
00026 TypeHandle PolylightEffect::_type_handle;
00027 
00028 ////////////////////////////////////////////////////////////////////
00029 //     Function: PolylightEffect::make
00030 //       Access: Published, Static
00031 //  Description: Constructs a new PolylightEffect object.
00032 ////////////////////////////////////////////////////////////////////
00033 CPT(RenderEffect) PolylightEffect::
00034 make() {
00035   PolylightEffect *effect = new PolylightEffect;
00036   effect->_contribution_type = CT_proximal;
00037   effect->_weight = 1.0; // 0.9; // Asad: I don't think we should monkey with the weight.
00038   effect->_effect_center = LPoint3(0.0,0.0,0.0);
00039   return return_new(effect);
00040 }
00041 
00042 ////////////////////////////////////////////////////////////////////
00043 //     Function: PolylightEffect::make
00044 //       Access: Published, Static
00045 //  Description: Constructs a new PolylightEffect object.
00046 ////////////////////////////////////////////////////////////////////
00047 CPT(RenderEffect) PolylightEffect::
00048 make(PN_stdfloat weight, ContribType contrib, const LPoint3 &effect_center) {
00049   PolylightEffect *effect = new PolylightEffect;
00050   effect->_contribution_type = contrib;
00051   effect->_weight = weight;
00052   effect->_effect_center = effect_center;
00053   return return_new(effect);
00054 }
00055 
00056 ////////////////////////////////////////////////////////////////////
00057 //     Function: PolylightEffect::make
00058 //       Access: Published, Static
00059 //  Description: Constructs a new PolylightEffect object.
00060 ////////////////////////////////////////////////////////////////////
00061 CPT(RenderEffect) PolylightEffect::
00062 make(PN_stdfloat weight, ContribType contrib, const LPoint3 &effect_center,
00063      const LightGroup &lights) {
00064   PolylightEffect *effect = new PolylightEffect;
00065   effect->_contribution_type = contrib;
00066   effect->_weight = weight;
00067   effect->_effect_center = effect_center;
00068   effect->_lightgroup = lights;
00069   return return_new(effect);
00070 }
00071 
00072 ////////////////////////////////////////////////////////////////////
00073 //     Function: PolylightEffect::has_cull_callback
00074 //       Access: Public, Virtual
00075 //  Description: Should be overridden by derived classes to return
00076 //               true if cull_callback() has been defined.  Otherwise,
00077 //               returns false to indicate cull_callback() does not
00078 //               need to be called for this effect during the cull
00079 //               traversal.
00080 ////////////////////////////////////////////////////////////////////
00081 bool PolylightEffect::
00082 has_cull_callback() const {
00083   return !_lightgroup.empty();
00084 }
00085 
00086 ////////////////////////////////////////////////////////////////////
00087 //     Function: PolylightEffect::cull_callback
00088 //       Access: Public, Virtual
00089 //  Description: If has_cull_callback() returns true, this function
00090 //               will be called during the cull traversal to perform
00091 //               any additional operations that should be performed at
00092 //               cull time.  This may include additional manipulation
00093 //               of render state or additional visible/invisible
00094 //               decisions, or any other arbitrary operation.
00095 //
00096 //               At the time this function is called, the current
00097 //               node's transform and state have not yet been applied
00098 //               to the net_transform and net_state.  This callback
00099 //               may modify the node_transform and node_state to apply
00100 //               an effective change to the render state at this
00101 //               level.
00102 ////////////////////////////////////////////////////////////////////
00103 void PolylightEffect::
00104 cull_callback(CullTraverser *trav, CullTraverserData &data,
00105               CPT(TransformState) &node_transform,
00106               CPT(RenderState) &node_state) const {
00107   //CPT(RenderAttrib) poly_light_attrib = do_poly_light(trav->get_scene()->get_scene_root(), &data, node_transform); 
00108   CPT(RenderAttrib) poly_light_attrib = do_poly_light(trav->get_scene(), &data, node_transform); 
00109   CPT(RenderState) poly_light_state = RenderState::make(poly_light_attrib);
00110   node_state = node_state->compose(poly_light_state); 
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //     Function: PolylightEffect::do_poly_light
00115 //       Access: Public
00116 //  Description: Gets the node's position and based on distance from 
00117 //               lights in the lightgroup calculates the color to be 
00118 //               modulated in. If the avatar is in the range of 
00119 //               multiple lights, then determine, which light it is
00120 //               closer to, and get the weight of the scene_color
00121 //               in respect to that light's proximity.
00122 ////////////////////////////////////////////////////////////////////
00123 CPT(RenderAttrib) PolylightEffect::
00124 do_poly_light(const SceneSetup *scene, const CullTraverserData *data, const TransformState *node_transform) const {
00125   //static bool was_under_polylight = false;
00126   PN_stdfloat dist; // To calculate the distance of each light from the node
00127   PN_stdfloat r,g,b; // To hold the color calculation
00128   PN_stdfloat min_dist; // hold the dist from light that avatar is closer to
00129   int num_lights = 0; // Keep track of number of lights for division
00130   PN_stdfloat light_scale; // Variable to calculate attenuation 
00131   PN_stdfloat weight_scale = 1.0f; // Variable to compensate snap of color when you walk inside the light volume
00132   PN_stdfloat Rcollect, Gcollect, Bcollect;
00133 
00134   const NodePath &root = scene->get_scene_root();
00135   //const NodePath &camera = scene->get_camera_path();
00136   const NodePath &camera = scene->get_cull_center();
00137 
00138   // Initialize Color variables
00139   r = g = b = 1.0;
00140   Rcollect = Gcollect = Bcollect = 0.0;
00141 
00142   // get the avatar's base color scale
00143   LColor scene_color = root.get_color_scale();
00144   if (polylight_info) {
00145     pgraph_cat.debug() << "scene color scale = " << scene_color << endl;
00146   }
00147   min_dist = 100000.0;
00148   // Cycle through all the lights in this effect's lightgroup
00149   LightGroup::const_iterator light_iter; 
00150   for (light_iter = _lightgroup.begin(); light_iter != _lightgroup.end(); light_iter++){
00151     const PolylightNode *light = DCAST(PolylightNode, (*light_iter).node()); 
00152 
00153     // light holds the current PolylightNode
00154     if (light->is_enabled()) { // if enabled get all the properties
00155       PN_stdfloat light_radius = light->get_radius();
00156       // Calculate the distance of the node from the light
00157       //dist = light_iter->second->get_distance(data->_node_path.get_node_path());
00158       const NodePath lightnp = *light_iter;
00159       LPoint3 relative_point = data->_node_path.get_node_path().get_relative_point(lightnp, light->get_pos());
00160 
00161       if (_effect_center[2]) {
00162         dist = (relative_point - _effect_center).length(); // this counts height difference
00163       } else {
00164         // get distance as if the light is at the same height of player
00165         LVector2 xz(relative_point[0], relative_point[1]);
00166         dist = xz.length(); // this does not count height difference
00167       }
00168 
00169       if (dist <= light_radius) { // If node is in range of this light
00170         // as to Schuyler's suggestion, lets do some vector processing relative to camera
00171         LPoint3 light_position = light->get_pos();
00172         //LPoint3 camera_position = camera.get_relative_point(lightnp, light->get_pos());
00173         LPoint3 camera_position = lightnp.get_relative_point(camera, LPoint3(0,0,0));
00174         LPoint3 avatar_position = lightnp.get_relative_point(data->_node_path.get_node_path(), LPoint3(0,0,0));
00175         LVector3 light_camera = camera_position  - light_position;
00176         LVector3 light_avatar = avatar_position - light_position;
00177         light_camera.normalize();
00178         light_avatar.normalize();
00179         PN_stdfloat intensity = light_camera.dot(light_avatar);
00180         
00181         if (polylight_info) {
00182           pgraph_cat.debug() << "light position = " << light_position << endl;
00183           pgraph_cat.debug() << "relative avatar position = " << avatar_position << endl;
00184           pgraph_cat.debug() << "relative camera position = " << camera_position << endl;
00185           pgraph_cat.debug() << "light->camera " << light_camera << endl;
00186           pgraph_cat.debug() << "light->avatar " << light_avatar << endl;
00187           pgraph_cat.debug() << "light->camera.light->avatar = " << intensity << endl;
00188           pgraph_cat.debug() << "effect center = " << _effect_center << endl;
00189           //pgraph_cat.debug() << "close to this light = " << light->get_name() << endl;
00190           pgraph_cat.debug() << "dist = " << dist << ";radius = " << light_radius << endl;
00191         }
00192 
00193         // map -1 to 1 into 0 to 1; and flip it
00194         intensity = 1.0 - ((intensity + 1.0) * 0.5);
00195         if (polylight_info) {
00196           pgraph_cat.debug() << "remapped intensity = " << intensity << endl;
00197         }
00198 
00199         PolylightNode::Attenuation_Type light_attenuation = light->get_attenuation();
00200         LColor light_color;
00201         if (light->is_flickering()) { // If flickering, modify color
00202           light_color = light->flicker();
00203         } else {
00204           light_color = light->get_color();
00205           //light_color = light->get_color_scenegraph();
00206         }
00207 
00208         PN_stdfloat ratio = dist/light_radius;
00209         if (light_attenuation == PolylightNode::ALINEAR) {
00210           light_scale = 1.0 - ratio;
00211         } else if (light_attenuation == PolylightNode::AQUADRATIC) {
00212           /*
00213           PN_stdfloat fd; // Variable for quadratic attenuation
00214           PN_stdfloat light_a0 = light->get_a0();
00215           PN_stdfloat light_a1 = light->get_a1();
00216           PN_stdfloat light_a2 = light->get_a2();
00217           if (light_a0 == 0 && light_a1 == 0 && light_a2 == 0) { // To prevent division by zero
00218             light_a0 = 1.0;
00219           }
00220           fd = 1.0 / (light_a0 + light_a1 * dist + light_a2 * dist * dist);
00221           if (fd < 1.0) {
00222             light_scale = fd;
00223           } else {
00224             light_scale = 1.0;
00225           }
00226           */
00227           //light_scale = 1.0 - ratio*ratio; // graph of 1-x^2
00228           ratio = 1 - ratio;
00229           if (ratio <= 0.8)
00230             light_scale = (ratio*ratio)*(3-2*ratio); //graph of x^2(3-x)
00231           else
00232             light_scale = 1.0;
00233         } else {
00234           light_scale = 1.0;
00235         }
00236 
00237         light_scale *= intensity;
00238 
00239         if (min_dist > dist) {
00240           min_dist = dist;
00241           // Keep accumulating each lights contribution... 
00242           // we have to prevent color snap, so factor in the weight.
00243           // weight becomes negligent as you are closer to the light
00244           // and opposite otherwise
00245           weight_scale = _weight * (1.0 - light_scale);
00246         }
00247 
00248         if (polylight_info) {
00249           pgraph_cat.debug() << "weight_scale = " << weight_scale
00250                              << "; light_scale " << light_scale << endl;
00251         }
00252 
00253         Rcollect += light_color[0] * light_scale;
00254         Gcollect += light_color[1] * light_scale;
00255         Bcollect += light_color[2] * light_scale;
00256         /*
00257         Rcollect += light_color[0] * light_scale + scene_color[0] * weight_scale;
00258         Gcollect += light_color[1] * light_scale + scene_color[1] * weight_scale;
00259         Bcollect += light_color[2] * light_scale + scene_color[2] * weight_scale;
00260         */
00261         /*
00262         Rcollect += light_color[0] * light_scale + weight_scale;
00263         Gcollect += light_color[1] * light_scale + weight_scale;
00264         Bcollect += light_color[2] * light_scale + weight_scale;
00265         */
00266 
00267 
00268         num_lights++;
00269       } // if dist< radius
00270     } // if light is enabled
00271   } // for all lights
00272 
00273   if ( _contribution_type == CT_all) {
00274     // Sometimes to prevent snapping of color at light volume boundaries
00275     // just divide total contribution by all the lights in the effect
00276     // whether or not they contribute color
00277     num_lights = _lightgroup.size();
00278   }
00279 
00280   if (num_lights) {
00281     //was_under_polylight = true;
00282     //data->_node_path.get_node_path().set_color_scale_off();
00283     if (polylight_info)
00284       pgraph_cat.debug() << "num lights = " << num_lights << endl;
00285 
00286     // divide by number of lights to get average.
00287     r = Rcollect;// / num_lights;
00288     g = Gcollect;// / num_lights;
00289     b = Bcollect;// / num_lights;
00290 
00291     if (polylight_info)
00292       pgraph_cat.debug() << "avg: r=" << r << "; g=" << g << "; b=" << b << endl;
00293 
00294     // Now add the scene_color multiplied by weight_scale
00295     r += scene_color[0] * weight_scale;
00296     g += scene_color[1] * weight_scale;
00297     b += scene_color[2] * weight_scale;
00298     if (polylight_info)
00299       pgraph_cat.debug() << "weighed: r=" << r << "; g=" << g << "; b=" << b << endl;
00300 
00301     /*
00302     // normalize the color
00303     LVector3 color_vector(r, g, b);
00304     color_vector.normalize();
00305     r = color_vector[0];
00306     g = color_vector[1];
00307     b = color_vector[2];
00308 
00309     if (polylight_info)
00310       pgraph_cat.debug() << "unit: r=" << r << "; g=" << g << "; b=" << b << endl;
00311     */
00312 
00313     // cap it
00314     r = (r > 1.0)? 1.0 : r;
00315     g = (g > 1.0)? 1.0 : g;
00316     b = (b > 1.0)? 1.0 : b;
00317 
00318     if (polylight_info)
00319       pgraph_cat.debug() << "capped: r=" << r << "; g=" << g << "; b=" << b << endl;
00320 
00321     // since this rgb will be scaled by scene_color by day night
00322     // cycle, lets undo that effect by dividing this rgb by the
00323     // scene_color. That way, the final render will contain this rgb
00324     if (scene_color[0] >= 0.01)
00325       r /= scene_color[0];
00326     if (scene_color[1] >= 0.01)
00327       g /= scene_color[1];
00328     if (scene_color[2] >= 0.01)
00329       b /= scene_color[2];
00330 
00331     if (polylight_info)
00332       pgraph_cat.debug() << "final: r=" << r << "; g=" << g << "; b=" << b << endl;
00333 
00334   }
00335   /*
00336   else {
00337     if (was_under_polylight) {
00338       // under no polylight influence...so clear the color scale
00339       //data->_node_path.get_node_path().clear_color_scale();
00340       //data->_node_path.get_node_path().set_color_scale(scene_color);
00341       was_under_polylight = false;
00342     }
00343   }
00344   */
00345 
00346   return ColorScaleAttrib::make(LVecBase4(r, g, b, 1.0));
00347 }
00348 
00349 ////////////////////////////////////////////////////////////////////
00350 //     Function: PolylightEffect::output
00351 //       Access: Public
00352 //  Description: 
00353 ////////////////////////////////////////////////////////////////////
00354 void PolylightEffect::
00355 output(ostream &out) const {
00356   out << get_type() << ":";
00357     
00358   LightGroup::const_iterator li;
00359   for (li = _lightgroup.begin(); li != _lightgroup.end(); ++li) {
00360     NodePath light = (*li);
00361     out << " " << light;
00362   }
00363   out << " weight " << _weight << " contrib " << _contribution_type
00364       << " center " << _effect_center;
00365 }
00366 
00367 
00368 ////////////////////////////////////////////////////////////////////
00369 //     Function: PolylightEffect::compare_to_impl
00370 //       Access: Protected, Virtual
00371 //  Description: Intended to be overridden by derived PolylightEffect
00372 //               types to return a unique number indicating whether
00373 //               this PolylightEffect is equivalent to the other one.
00374 //
00375 //               This should return 0 if the two PolylightEffect objects
00376 //               are equivalent, a number less than zero if this one
00377 //               should be sorted before the other one, and a number
00378 //               greater than zero otherwise.
00379 //
00380 //               This will only be called with two PolylightEffect
00381 //               objects whose get_type() functions return the same.
00382 ////////////////////////////////////////////////////////////////////
00383 int PolylightEffect::
00384 compare_to_impl(const RenderEffect *other) const {
00385   const PolylightEffect *ta;
00386   DCAST_INTO_R(ta, other, 0);
00387 
00388   if (_contribution_type != ta->_contribution_type) {
00389     return _contribution_type < ta->_contribution_type ? -1 : 1;
00390   }
00391  
00392   if (_weight != ta->_weight) {
00393     return _weight < ta->_weight ? -1 :1;
00394   }
00395 
00396   if (_lightgroup != ta->_lightgroup) {
00397     return _lightgroup < ta->_lightgroup ? -1 : 1;
00398   }
00399  
00400   return 0;
00401 }
00402 
00403 
00404 
00405 ////////////////////////////////////////////////////////////////////
00406 //     Function: PolylightEffect::add_light
00407 //       Access: Published
00408 //  Description: Add a PolylightNode object to this effect and return
00409 //               a new effect
00410 ////////////////////////////////////////////////////////////////////
00411 CPT(RenderEffect) PolylightEffect::
00412 add_light(const NodePath &newlight) const {
00413   PolylightEffect *effect = new PolylightEffect(*this);
00414   effect->_lightgroup.push_back(newlight);
00415   return return_new(effect);
00416 }
00417 
00418 
00419 ////////////////////////////////////////////////////////////////////
00420 //     Function: PolylightEffect::remove_light
00421 //       Access: Published
00422 //  Description: Remove a light from this effect. Return the new updated
00423 //               effect
00424 ////////////////////////////////////////////////////////////////////
00425 CPT(RenderEffect) PolylightEffect::
00426 remove_light(const NodePath &newlight) const {
00427   PolylightEffect *effect = new PolylightEffect(*this);
00428   LightGroup::iterator light_iter;
00429   light_iter = find(effect->_lightgroup.begin(),effect->_lightgroup.end(), newlight); 
00430   if (light_iter == effect->_lightgroup.end()) {
00431     pgraph_cat.debug()
00432       << "Attempt to remove Polylight " << newlight << "; not found.\n";
00433   } else {
00434     // Remove light
00435     effect->_lightgroup.erase(light_iter);
00436   }
00437   return return_new(effect);
00438  
00439 }
00440 
00441 ////////////////////////////////////////////////////////////////////
00442 //     Function: PolylightEffect::set_weight
00443 //       Access: Published
00444 //  Description: Set weight and return a new effect... the reason
00445 //               this couldnt be done through make was because
00446 //               that would return a new effect without the 
00447 //               lightgroup which is static and cant be accessed
00448 //               Here, we just pass that to the make
00449 ////////////////////////////////////////////////////////////////////
00450 CPT(RenderEffect) PolylightEffect::
00451 set_weight(PN_stdfloat w) const {
00452   PolylightEffect *effect = new PolylightEffect(*this);
00453   effect->_weight = w;
00454   return return_new(effect);
00455 }
00456 
00457 ////////////////////////////////////////////////////////////////////
00458 //     Function: PolylightEffect::set_contrib
00459 //       Access: Published
00460 //  Description: Set Contrib Type and return a new effect... the reason
00461 //               this couldnt be done through make was because
00462 //               that would return a new effect without the 
00463 //               lightgroup which is static and cant be accessed
00464 //               Here, we just pass that to the make
00465 ////////////////////////////////////////////////////////////////////
00466 CPT(RenderEffect) PolylightEffect::
00467 set_contrib(ContribType ct) const {
00468   PolylightEffect *effect = new PolylightEffect(*this);
00469   effect->_contribution_type = ct;
00470   return return_new(effect);
00471 }
00472 
00473 ////////////////////////////////////////////////////////////////////
00474 //     Function: PolylightEffect::set_effect_center
00475 //       Access: Published
00476 //  Description: Set weight and return a new effect... the reason
00477 //               this couldnt be done through make was because
00478 //               that would return a new effect without the 
00479 //               lightgroup which is static and cant be accessed
00480 //               Here, we just pass that to the make
00481 ////////////////////////////////////////////////////////////////////
00482 CPT(RenderEffect) PolylightEffect::
00483 set_effect_center(const LPoint3 &ec) const{
00484   PolylightEffect *effect = new PolylightEffect(*this);
00485   effect->_effect_center = ec;
00486   return return_new(effect);
00487 }
00488 
00489 ////////////////////////////////////////////////////////////////////
00490 //     Function: PolylightEffect::has_light
00491 //       Access: Published
00492 //  Description: Returns true if the indicated light is listed in the
00493 //               PolylightEffect, false otherwise.
00494 ////////////////////////////////////////////////////////////////////
00495 bool PolylightEffect::
00496 has_light(const NodePath &light) const {
00497   LightGroup::const_iterator li;
00498   li = find(_lightgroup.begin(), _lightgroup.end(), light); 
00499   return (li != _lightgroup.end());
00500 }
00501 
00502 ostream &
00503 operator << (ostream &out, PolylightEffect::ContribType ct) {
00504   switch (ct) {
00505   case PolylightEffect::CT_proximal:
00506     return out << "proximal";
00507 
00508   case PolylightEffect::CT_all:
00509     return out << "all";
00510   }
00511 
00512   return out << "**Invalid ContribType(" << (int)ct << ")**";
00513 }
 All Classes Functions Variables Enumerations