Panda3D
|
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 }