Panda3D
|
00001 // Filename: lens.cxx 00002 // Created by: drose (18Feb99) 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 "lens.h" 00016 #include "throw_event.h" 00017 #include "compose_matrix.h" 00018 #include "look_at.h" 00019 #include "geom.h" 00020 #include "geomLinestrips.h" 00021 #include "geomVertexWriter.h" 00022 #include "boundingHexahedron.h" 00023 #include "indent.h" 00024 #include "config_gobj.h" 00025 #include "plane.h" 00026 00027 TypeHandle Lens::_type_handle; 00028 00029 //////////////////////////////////////////////////////////////////// 00030 // Function: Lens::Constructor 00031 // Access: Public 00032 // Description: 00033 //////////////////////////////////////////////////////////////////// 00034 Lens:: 00035 Lens() { 00036 clear(); 00037 } 00038 00039 //////////////////////////////////////////////////////////////////// 00040 // Function: Lens::Copy Constructor 00041 // Access: Public 00042 // Description: 00043 //////////////////////////////////////////////////////////////////// 00044 Lens:: 00045 Lens(const Lens ©) { 00046 (*this) = copy; 00047 } 00048 00049 //////////////////////////////////////////////////////////////////// 00050 // Function: Lens::Copy Assignment Operator 00051 // Access: Public 00052 // Description: 00053 //////////////////////////////////////////////////////////////////// 00054 void Lens:: 00055 operator = (const Lens ©) { 00056 _change_event = copy._change_event; 00057 _cs = copy._cs; 00058 _film_size = copy._film_size; 00059 _film_offset = copy._film_offset; 00060 _focal_length = copy._focal_length; 00061 _fov = copy._fov; 00062 _aspect_ratio = copy._aspect_ratio; 00063 _near_distance = copy._near_distance; 00064 _far_distance = copy._far_distance; 00065 00066 _view_hpr = copy._view_hpr; 00067 _view_vector = copy._view_vector; 00068 _interocular_distance = copy._interocular_distance; 00069 _convergence_distance = copy._convergence_distance; 00070 _keystone = copy._keystone; 00071 00072 _user_flags = copy._user_flags; 00073 _comp_flags = 0; 00074 00075 _focal_length_seq = copy._focal_length_seq; 00076 _fov_seq = copy._fov_seq; 00077 _film_size_seq = copy._film_size_seq; 00078 00079 // We don't copy the _geom_data. That's unique to each Lens. 00080 } 00081 00082 //////////////////////////////////////////////////////////////////// 00083 // Function: Lens::set_coordinate_system 00084 // Access: Published 00085 // Description: Specifies the coordinate system that all 3-d 00086 // computations are performed within for this 00087 // Lens. Normally, this is CS_default. 00088 //////////////////////////////////////////////////////////////////// 00089 void Lens:: 00090 set_coordinate_system(CoordinateSystem cs) { 00091 _cs = cs; 00092 adjust_comp_flags(CF_mat | CF_view_hpr | CF_view_vector, 0); 00093 } 00094 00095 //////////////////////////////////////////////////////////////////// 00096 // Function: Lens::clear 00097 // Access: Published 00098 // Description: Resets all lens parameters to their initial default 00099 // settings. 00100 //////////////////////////////////////////////////////////////////// 00101 void Lens:: 00102 clear() { 00103 _change_event = ""; 00104 _cs = CS_default; 00105 _film_size.set(1.0f, 1.0f); 00106 _film_offset.set(0.0f, 0.0f); 00107 _focal_length = 1.0f; 00108 _fov.set(default_fov, default_fov); 00109 _aspect_ratio = 1.0f; 00110 _near_distance = default_near; 00111 _far_distance = default_far; 00112 _view_hpr.set(0.0f, 0.0f, 0.0f); 00113 _view_vector.set(0.0f, 1.0f, 0.0f); 00114 _up_vector.set(0.0f, 0.0f, 1.0f); 00115 _keystone.set(0.0f, 0.0f); 00116 00117 _user_flags = 0; 00118 _comp_flags = CF_fov; 00119 00120 set_interocular_distance(default_iod); 00121 set_convergence_distance(default_converge); 00122 00123 if (default_keystone.has_value()) { 00124 _keystone.set(default_keystone[0], default_keystone[1]); 00125 _user_flags |= UF_keystone; 00126 } 00127 00128 // Assign an initial arbitrary sequence to these three. 00129 _film_size_seq = 0; 00130 _focal_length_seq = 1; 00131 _fov_seq = 2; 00132 } 00133 00134 //////////////////////////////////////////////////////////////////// 00135 // Function: Lens::set_film_size 00136 // Access: Published 00137 // Description: Sets the horizontal size of the film without changing 00138 // its shape. The aspect ratio remains unchanged; this 00139 // computes the vertical size of the film to 00140 // automatically maintain the aspect ratio. 00141 //////////////////////////////////////////////////////////////////// 00142 void Lens:: 00143 set_film_size(float width) { 00144 nassertv(!cnan(width)); 00145 _film_size.set(width, width / get_aspect_ratio()); 00146 00147 // We can't specify all three of focal length, fov, and film size. 00148 // Throw out the oldest one. 00149 resequence_fov_triad(_film_size_seq, _focal_length_seq, _fov_seq); 00150 00151 if (_fov_seq == 0) { 00152 // Throw out fov if it's oldest. 00153 adjust_user_flags(UF_hfov | UF_vfov | UF_min_fov | UF_film_height, 00154 UF_film_width); 00155 } else { 00156 // Otherwise, throw out focal length. 00157 nassertv(_focal_length_seq == 0); 00158 adjust_user_flags(UF_focal_length | UF_film_height, 00159 UF_film_width); 00160 } 00161 adjust_comp_flags(CF_mat | CF_focal_length | CF_fov, 00162 CF_film_size); 00163 throw_change_event(); 00164 } 00165 00166 //////////////////////////////////////////////////////////////////// 00167 // Function: Lens::set_film_size 00168 // Access: Published 00169 // Description: Sets the size and shape of the "film" within the 00170 // lens. This both establishes the units used by 00171 // calls like set_focal_length(), and establishes the 00172 // aspect ratio of the frame. 00173 // 00174 // In a physical camera, the field of view of a lens is 00175 // determined by the lens' focal length and by the size 00176 // of the film area exposed by the lens. For instance, 00177 // a 35mm camera exposes a rectangle on the film about 00178 // 24mm x 36mm, which means a 50mm lens gives about a 00179 // 40-degree horizontal field of view. 00180 // 00181 // In the virtual camera, you may set the film size to 00182 // any units here, and specify a focal length in the 00183 // same units to simulate the same effect. Or, you may 00184 // ignore this parameter, and specify the field of view 00185 // and aspect ratio of the lens directly. 00186 //////////////////////////////////////////////////////////////////// 00187 void Lens:: 00188 set_film_size(const LVecBase2f &film_size) { 00189 nassertv(!film_size.is_nan()); 00190 _film_size = film_size; 00191 00192 // We can't specify all three of focal length, fov, and film size. 00193 // Throw out the oldest one. 00194 resequence_fov_triad(_film_size_seq, _focal_length_seq, _fov_seq); 00195 00196 if (_fov_seq == 0) { 00197 // Throw out fov if it's oldest. 00198 adjust_user_flags(UF_hfov | UF_vfov | UF_min_fov | UF_aspect_ratio, 00199 UF_film_width | UF_film_height); 00200 } else { 00201 // Otherwise, throw out focal length. 00202 nassertv(_focal_length_seq == 0); 00203 adjust_user_flags(UF_focal_length | UF_vfov | UF_aspect_ratio, 00204 UF_film_width | UF_film_height); 00205 } 00206 adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_aspect_ratio, 00207 CF_film_size); 00208 00209 // Also, the user has implicitly specified an aspect ratio. Make it 00210 // stick until the user tells us something different. 00211 compute_aspect_ratio(); 00212 adjust_user_flags(0, UF_aspect_ratio); 00213 00214 throw_change_event(); 00215 } 00216 00217 //////////////////////////////////////////////////////////////////// 00218 // Function: Lens::get_film_size 00219 // Access: Published 00220 // Description: Returns the horizontal and vertical film size of 00221 // the virtual film. See set_film_size(). 00222 //////////////////////////////////////////////////////////////////// 00223 const LVecBase2f &Lens:: 00224 get_film_size() const { 00225 if ((_comp_flags & CF_film_size) == 0) { 00226 // We pretend this is a const method, even though it may call a 00227 // non-const method to recompute the internal values. We can do 00228 // this because this is just compute-on-demand. 00229 ((Lens *)this)->compute_film_size(); 00230 } 00231 return _film_size; 00232 } 00233 00234 //////////////////////////////////////////////////////////////////// 00235 // Function: Lens::set_focal_length 00236 // Access: Published 00237 // Description: Sets the focal length of the lens. This may adjust 00238 // the field-of-view correspondingly, and is an 00239 // alternate way to specify field of view. 00240 // 00241 // For certain kinds of lenses (e.g. OrthographicLens), 00242 // the focal length has no meaning. 00243 //////////////////////////////////////////////////////////////////// 00244 void Lens:: 00245 set_focal_length(float focal_length) { 00246 nassertv(!cnan(focal_length)); 00247 _focal_length = focal_length; 00248 00249 // We can't specify all three of focal length, fov, and film size. 00250 // Throw out the oldest one. 00251 resequence_fov_triad(_focal_length_seq, _film_size_seq, _fov_seq); 00252 00253 if (_film_size_seq == 0) { 00254 // Throw out film size if it's oldest. 00255 adjust_user_flags(UF_film_width | UF_film_height, 00256 UF_focal_length); 00257 } else { 00258 // Otherwise, throw out the fov. 00259 nassertv(_fov_seq == 0); 00260 adjust_user_flags(UF_hfov | UF_vfov | UF_min_fov, 00261 UF_focal_length); 00262 } 00263 00264 adjust_comp_flags(CF_mat | CF_fov | CF_film_size, 00265 CF_focal_length); 00266 throw_change_event(); 00267 } 00268 00269 //////////////////////////////////////////////////////////////////// 00270 // Function: Lens::get_focal_length 00271 // Access: Published 00272 // Description: Returns the focal length of the lens. This may have 00273 // been set explicitly by a previous call to 00274 // set_focal_length(), or it may be computed based on 00275 // the lens' fov and film_size. For certain kinds of 00276 // lenses, the focal length has no meaning. 00277 //////////////////////////////////////////////////////////////////// 00278 float Lens:: 00279 get_focal_length() const { 00280 if ((_comp_flags & CF_focal_length) == 0) { 00281 ((Lens *)this)->compute_focal_length(); 00282 } 00283 return _focal_length; 00284 } 00285 00286 //////////////////////////////////////////////////////////////////// 00287 // Function: Lens::set_min_fov 00288 // Access: Published 00289 // Description: Sets the field of view of the smallest dimension of 00290 // the window. If the window is wider than it is tall, 00291 // this specifies the vertical field of view; if it is 00292 // taller than it is wide, this specifies the horizontal 00293 // field of view. 00294 // 00295 // In many cases, this is preferable to setting either 00296 // the horizontal or vertical field of view explicitly. 00297 // Setting this parameter means that pulling the window 00298 // wider will widen the field of view, which is usually 00299 // what you expect to happen. 00300 //////////////////////////////////////////////////////////////////// 00301 void Lens:: 00302 set_min_fov(float min_fov) { 00303 nassertv(!cnan(min_fov)); 00304 _min_fov = min_fov; 00305 00306 // We can't specify all three of focal length, fov, and film size. 00307 // Throw out the oldest one. 00308 resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq); 00309 00310 if (_focal_length_seq == 0) { 00311 // Throw out focal length if it's oldest. 00312 adjust_user_flags(UF_focal_length | UF_vfov | UF_hfov, 00313 UF_min_fov); 00314 } else { 00315 // Otherwise, throw out film size. 00316 nassertv(_film_size_seq == 0); 00317 00318 // Make sure we save the aspect ratio first. 00319 compute_aspect_ratio(); 00320 adjust_user_flags(UF_film_width | UF_film_height | UF_vfov | UF_hfov, 00321 UF_min_fov); 00322 } 00323 adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_film_size, 00324 0); 00325 // We leave CF_fov off of comp_flags, because we will still need to 00326 // recompute the vertical fov. It's not exactly the same as hfov * 00327 // get_aspect_ratio(). 00328 throw_change_event(); 00329 } 00330 00331 //////////////////////////////////////////////////////////////////// 00332 // Function: Lens::set_fov 00333 // Access: Published 00334 // Description: Sets the horizontal field of view of the lens without 00335 // changing the aspect ratio. The vertical field of 00336 // view is adjusted to maintain the same aspect ratio. 00337 //////////////////////////////////////////////////////////////////// 00338 void Lens:: 00339 set_fov(float hfov) { 00340 nassertv(!cnan(hfov)); 00341 _fov[0] = hfov; 00342 00343 // We can't specify all three of focal length, fov, and film size. 00344 // Throw out the oldest one. 00345 resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq); 00346 00347 if (_focal_length_seq == 0) { 00348 // Throw out focal length if it's oldest. 00349 adjust_user_flags(UF_focal_length | UF_vfov | UF_min_fov, 00350 UF_hfov); 00351 } else { 00352 // Otherwise, throw out film size. 00353 nassertv(_film_size_seq == 0); 00354 00355 // Make sure we save the aspect ratio first. 00356 compute_aspect_ratio(); 00357 adjust_user_flags(UF_film_width | UF_film_height | UF_vfov | UF_min_fov, 00358 UF_hfov); 00359 } 00360 adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_film_size, 00361 0); 00362 // We leave CF_fov off of comp_flags, because we will still need to 00363 // recompute the vertical fov. It's not exactly the same as hfov * 00364 // get_aspect_ratio(). 00365 throw_change_event(); 00366 } 00367 00368 //////////////////////////////////////////////////////////////////// 00369 // Function: Lens::set_fov 00370 // Access: Published 00371 // Description: Sets the field of view of the lens in both 00372 // dimensions. This establishes both the field of view 00373 // and the aspect ratio of the lens. This is one way to 00374 // specify the field of view of a lens; 00375 // set_focal_length() is another way. 00376 // 00377 // For certain kinds of lenses (like OrthographicLens), 00378 // the field of view has no meaning. 00379 //////////////////////////////////////////////////////////////////// 00380 void Lens:: 00381 set_fov(const LVecBase2f &fov) { 00382 nassertv(!fov.is_nan()); 00383 _fov = fov; 00384 00385 // We can't specify all three of focal length, fov, and film size. 00386 // Throw out the oldest one. 00387 resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq); 00388 00389 if (_focal_length_seq == 0) { 00390 // Throw out focal length if it's oldest. 00391 adjust_user_flags(UF_focal_length | UF_film_height | UF_min_fov | UF_aspect_ratio, 00392 UF_hfov | UF_vfov); 00393 } else { 00394 // Otherwise, throw out film size. 00395 nassertv(_film_size_seq == 0); 00396 adjust_user_flags(UF_film_width | UF_film_height | UF_min_fov | UF_aspect_ratio, 00397 UF_hfov | UF_vfov); 00398 } 00399 adjust_comp_flags(CF_mat | CF_focal_length | CF_film_size | CF_aspect_ratio, 00400 CF_fov); 00401 00402 // Also, the user has implicitly specified an aspect ratio. Make it 00403 // stick until the user tells us something different. 00404 compute_aspect_ratio(); 00405 adjust_user_flags(0, UF_aspect_ratio); 00406 00407 throw_change_event(); 00408 } 00409 00410 //////////////////////////////////////////////////////////////////// 00411 // Function: Lens::get_fov 00412 // Access: Published 00413 // Description: Returns the horizontal and vertical film size of 00414 // the virtual film. See set_fov(). 00415 //////////////////////////////////////////////////////////////////// 00416 const LVecBase2f &Lens:: 00417 get_fov() const { 00418 if ((_comp_flags & CF_fov) == 0) { 00419 ((Lens *)this)->compute_fov(); 00420 } 00421 return _fov; 00422 } 00423 00424 //////////////////////////////////////////////////////////////////// 00425 // Function: Lens::get_min_fov 00426 // Access: Published 00427 // Description: Returns the field of view of the narrowest dimension 00428 // of the window. See set_min_fov(). 00429 //////////////////////////////////////////////////////////////////// 00430 float Lens:: 00431 get_min_fov() const { 00432 if ((_comp_flags & CF_fov) == 0) { 00433 ((Lens *)this)->compute_fov(); 00434 } 00435 return _min_fov; 00436 } 00437 00438 00439 //////////////////////////////////////////////////////////////////// 00440 // Function: Lens::set_aspect_ratio 00441 // Access: Published 00442 // Description: Sets the aspect ratio of the lens. This is the ratio 00443 // of the height to the width of the generated image. 00444 // Setting this overrides the two-parameter fov or film 00445 // size setting. 00446 //////////////////////////////////////////////////////////////////// 00447 void Lens:: 00448 set_aspect_ratio(float aspect_ratio) { 00449 nassertv(!cnan(aspect_ratio)); 00450 _aspect_ratio = aspect_ratio; 00451 adjust_user_flags(UF_film_height | UF_vfov, 00452 UF_aspect_ratio); 00453 adjust_comp_flags(CF_mat | CF_film_size | CF_fov | CF_focal_length, 00454 CF_aspect_ratio); 00455 throw_change_event(); 00456 } 00457 00458 //////////////////////////////////////////////////////////////////// 00459 // Function: Lens::get_aspect_ratio 00460 // Access: Published 00461 // Description: Returns the aspect ratio of the Lens. This is 00462 // determined based on the indicated film size; see 00463 // set_film_size(). 00464 //////////////////////////////////////////////////////////////////// 00465 float Lens:: 00466 get_aspect_ratio() const { 00467 if ((_comp_flags & CF_aspect_ratio) == 0) { 00468 ((Lens *)this)->compute_aspect_ratio(); 00469 } 00470 return _aspect_ratio; 00471 } 00472 00473 //////////////////////////////////////////////////////////////////// 00474 // Function: Lens::get_default_near 00475 // Access: Published, Static 00476 // Description: Returns the default near plane distance that will be 00477 // assigned to each newly-created lens. This is read 00478 // from the Configrc file. 00479 //////////////////////////////////////////////////////////////////// 00480 float Lens:: 00481 get_default_near() { 00482 return default_near; 00483 } 00484 00485 //////////////////////////////////////////////////////////////////// 00486 // Function: Lens::get_default_far 00487 // Access: Published, Static 00488 // Description: Returns the default far plane distance that will be 00489 // assigned to each newly-created lens. This is read 00490 // from the Configrc file. 00491 //////////////////////////////////////////////////////////////////// 00492 float Lens:: 00493 get_default_far() { 00494 return default_far; 00495 } 00496 00497 //////////////////////////////////////////////////////////////////// 00498 // Function: Lens::set_view_hpr 00499 // Access: Published 00500 // Description: Sets the direction in which the lens is facing. 00501 // Normally, this is down the forward axis (usually the 00502 // Y axis), but it may be rotated. This is only one way 00503 // of specifying the rotation; you may also specify an 00504 // explicit vector in which to look, or you may give a 00505 // complete transformation matrix. 00506 //////////////////////////////////////////////////////////////////// 00507 void Lens:: 00508 set_view_hpr(const LVecBase3f &view_hpr) { 00509 nassertv(!view_hpr.is_nan()); 00510 _view_hpr = view_hpr; 00511 adjust_user_flags(UF_view_vector | UF_view_mat, 00512 UF_view_hpr); 00513 adjust_comp_flags(CF_mat | CF_view_vector, 00514 CF_view_hpr); 00515 throw_change_event(); 00516 } 00517 00518 //////////////////////////////////////////////////////////////////// 00519 // Function: Lens::get_view_hpr 00520 // Access: Published 00521 // Description: Returns the direction in which the lens is facing. 00522 //////////////////////////////////////////////////////////////////// 00523 const LVecBase3f &Lens:: 00524 get_view_hpr() const { 00525 if ((_comp_flags & CF_view_hpr) == 0) { 00526 ((Lens *)this)->compute_view_hpr(); 00527 } 00528 return _view_hpr; 00529 } 00530 00531 //////////////////////////////////////////////////////////////////// 00532 // Function: Lens::set_view_vector 00533 // Access: Published 00534 // Description: Specifies the direction in which the lens is facing 00535 // by giving an axis to look along, and a perpendicular 00536 // (or at least non-parallel) up axis. 00537 // 00538 // See also set_view_hpr(). 00539 //////////////////////////////////////////////////////////////////// 00540 void Lens:: 00541 set_view_vector(const LVector3f &view_vector, const LVector3f &up_vector) { 00542 nassertv(!view_vector.is_nan()); 00543 _view_vector = view_vector; 00544 _up_vector = up_vector; 00545 adjust_user_flags(UF_view_hpr | UF_view_mat, 00546 UF_view_vector); 00547 adjust_comp_flags(CF_mat | CF_view_hpr, 00548 CF_view_vector); 00549 throw_change_event(); 00550 } 00551 00552 //////////////////////////////////////////////////////////////////// 00553 // Function: Lens::get_view_vector 00554 // Access: Published 00555 // Description: Returns the axis along which the lens is facing. 00556 //////////////////////////////////////////////////////////////////// 00557 const LVector3f &Lens:: 00558 get_view_vector() const { 00559 if ((_comp_flags & CF_view_vector) == 0) { 00560 ((Lens *)this)->compute_view_vector(); 00561 } 00562 return _view_vector; 00563 } 00564 00565 //////////////////////////////////////////////////////////////////// 00566 // Function: Lens::get_up_vector 00567 // Access: Published 00568 // Description: Returns the axis perpendicular to the camera's view 00569 // vector that indicates the "up" direction. 00570 //////////////////////////////////////////////////////////////////// 00571 const LVector3f &Lens:: 00572 get_up_vector() const { 00573 if ((_comp_flags & CF_view_vector) == 0) { 00574 ((Lens *)this)->compute_view_vector(); 00575 } 00576 return _up_vector; 00577 } 00578 00579 //////////////////////////////////////////////////////////////////// 00580 // Function: Lens::get_nodal_point 00581 // Access: Published 00582 // Description: Returns the center point of the lens: the point from 00583 // which the lens is viewing. 00584 //////////////////////////////////////////////////////////////////// 00585 LPoint3f Lens:: 00586 get_nodal_point() const { 00587 return get_view_mat().get_row3(3); 00588 } 00589 00590 //////////////////////////////////////////////////////////////////// 00591 // Function: Lens::set_interocular_distance 00592 // Access: Published 00593 // Description: Sets the distance between the left and right eyes of 00594 // a stereo camera. This distance is used to apply a 00595 // stereo effect when the lens is rendered on a stereo 00596 // display region. It only has an effect on a 00597 // PerspectiveLens. 00598 // 00599 // The left eye and the right eye are each offset along 00600 // the X axis by half of this distance, so that this 00601 // parameter specifies the total distance between them. 00602 // 00603 // Also see set_convergence_distance(), which relates. 00604 //////////////////////////////////////////////////////////////////// 00605 void Lens:: 00606 set_interocular_distance(float interocular_distance) { 00607 nassertv(!cnan(interocular_distance)); 00608 _interocular_distance = interocular_distance; 00609 if (_interocular_distance == 0.0f) { 00610 adjust_user_flags(UF_interocular_distance, 0); 00611 } else { 00612 adjust_user_flags(0, UF_interocular_distance); 00613 } 00614 00615 adjust_comp_flags(CF_mat, 0); 00616 throw_change_event(); 00617 } 00618 00619 //////////////////////////////////////////////////////////////////// 00620 // Function: Lens::get_interocular_distance 00621 // Access: Published 00622 // Description: See set_interocular_distance(). 00623 //////////////////////////////////////////////////////////////////// 00624 float Lens:: 00625 get_interocular_distance() const { 00626 return _interocular_distance; 00627 } 00628 00629 //////////////////////////////////////////////////////////////////// 00630 // Function: Lens::set_convergence_distance 00631 // Access: Published 00632 // Description: Sets the distance between between the camera plane 00633 // and the point in the distance that the left and right 00634 // eyes are both looking at. This distance is used to 00635 // apply a stereo effect when the lens is rendered on a 00636 // stereo display region. It only has an effect on a 00637 // PerspectiveLens. 00638 // 00639 // This parameter must be greater than 0, but may be as 00640 // large as you like. It controls the amount to which 00641 // the two eyes are directed inwards towards each other, 00642 // which is a normal property of stereo vision. It is a 00643 // distance, not an angle; normally this should be set 00644 // to the distance from the camera to the area of 00645 // interest in your scene. If you want to simulate 00646 // parallel stereo, set this value to a very large 00647 // number. 00648 // 00649 // Also see set_interocular_distance(), which relates. 00650 //////////////////////////////////////////////////////////////////// 00651 void Lens:: 00652 set_convergence_distance(float convergence_distance) { 00653 nassertv(!cnan(convergence_distance)); 00654 _convergence_distance = convergence_distance; 00655 if (_convergence_distance == 0.0f) { 00656 adjust_user_flags(UF_convergence_distance, 0); 00657 } else { 00658 adjust_user_flags(0, UF_convergence_distance); 00659 } 00660 00661 adjust_comp_flags(CF_mat, 0); 00662 throw_change_event(); 00663 } 00664 00665 //////////////////////////////////////////////////////////////////// 00666 // Function: Lens::get_convergence_distance 00667 // Access: Published 00668 // Description: See set_convergence_distance(). 00669 //////////////////////////////////////////////////////////////////// 00670 float Lens:: 00671 get_convergence_distance() const { 00672 return _convergence_distance; 00673 } 00674 00675 //////////////////////////////////////////////////////////////////// 00676 // Function: Lens::set_view_mat 00677 // Access: Published 00678 // Description: Sets an arbitrary transformation on the lens. This 00679 // replaces the individual transformation components 00680 // like set_view_hpr(). 00681 // 00682 // Setting a transformation here will have a slightly 00683 // different effect than putting one on the LensNode 00684 // that contains this lens. In particular, lighting and 00685 // other effects computations will still be performed on 00686 // the lens in its untransformed (facing forward) 00687 // position, but the actual projection matrix will be 00688 // transformed by this matrix. 00689 //////////////////////////////////////////////////////////////////// 00690 void Lens:: 00691 set_view_mat(const LMatrix4f &view_mat) { 00692 nassertv(!view_mat.is_nan()); 00693 _lens_mat = view_mat; 00694 adjust_user_flags(UF_view_vector | UF_view_hpr, 00695 UF_view_mat); 00696 adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | 00697 CF_projection_mat_left_inv | CF_projection_mat_right_inv | 00698 CF_lens_mat_inv | CF_view_hpr | CF_view_vector, 00699 CF_lens_mat); 00700 throw_change_event(); 00701 } 00702 00703 //////////////////////////////////////////////////////////////////// 00704 // Function: Lens::get_view_mat 00705 // Access: Published 00706 // Description: Returns the direction in which the lens is facing. 00707 //////////////////////////////////////////////////////////////////// 00708 const LMatrix4f &Lens:: 00709 get_view_mat() const { 00710 if ((_comp_flags & CF_lens_mat) == 0) { 00711 ((Lens *)this)->compute_lens_mat(); 00712 } 00713 return _lens_mat; 00714 } 00715 00716 //////////////////////////////////////////////////////////////////// 00717 // Function: Lens::clear_view_mat 00718 // Access: Published 00719 // Description: Resets the lens transform to identity. 00720 //////////////////////////////////////////////////////////////////// 00721 void Lens:: 00722 clear_view_mat() { 00723 _lens_mat = LMatrix4f::ident_mat(); 00724 adjust_user_flags(0, UF_view_vector | UF_view_hpr | UF_view_mat); 00725 adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | 00726 CF_projection_mat_left_inv | CF_projection_mat_right_inv | 00727 CF_lens_mat_inv | CF_view_hpr | CF_view_vector, 00728 CF_lens_mat); 00729 throw_change_event(); 00730 } 00731 00732 //////////////////////////////////////////////////////////////////// 00733 // Function: Lens::set_keystone 00734 // Access: Published 00735 // Description: Indicates the ratio of keystone correction to perform 00736 // on the lens, in each of three axes. This will build 00737 // a special non-affine scale factor into the projection 00738 // matrix that will compensate for keystoning of a 00739 // projected image; this can be used to compensate for a 00740 // projector that for physical reasons cannot be aimed 00741 // directly at its screen. 00742 // 00743 // The default value is taken from the default-keystone 00744 // Config variable. 0, 0 indicates no keystone 00745 // correction; specify a small value (usually in the 00746 // range -1 .. 1) in either the x or y position to 00747 // generate a keystone correction in that axis. 00748 //////////////////////////////////////////////////////////////////// 00749 void Lens:: 00750 set_keystone(const LVecBase2f &keystone) { 00751 nassertv(!keystone.is_nan()); 00752 _keystone = keystone; 00753 adjust_user_flags(0, UF_keystone); 00754 adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | 00755 CF_projection_mat_left_inv | CF_projection_mat_right_inv | 00756 CF_film_mat | CF_film_mat_inv, 0); 00757 throw_change_event(); 00758 } 00759 00760 //////////////////////////////////////////////////////////////////// 00761 // Function: Lens::clear_keystone 00762 // Access: Published 00763 // Description: Disables the lens keystone correction. 00764 //////////////////////////////////////////////////////////////////// 00765 void Lens:: 00766 clear_keystone() { 00767 _keystone.set(0.0f, 0.0f); 00768 adjust_user_flags(UF_keystone, 0); 00769 adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv | 00770 CF_projection_mat_left_inv | CF_projection_mat_right_inv | 00771 CF_film_mat | CF_film_mat_inv, 0); 00772 throw_change_event(); 00773 } 00774 00775 //////////////////////////////////////////////////////////////////// 00776 // Function: Lens::set_frustum_from_corners 00777 // Access: Published 00778 // Description: Sets up the lens to use the frustum defined by the 00779 // four indicated points. This is most useful for a 00780 // PerspectiveLens, but it may be called for other kinds 00781 // of lenses as well. 00782 // 00783 // The frustum will be rooted at the origin (or by 00784 // whatever translation might have been specified in a 00785 // previous call to set_view_mat). 00786 // 00787 // It is legal for the four points not to be arranged in 00788 // a rectangle; if this is the case, the frustum will be 00789 // fitted as tightly as possible to cover all four 00790 // points. 00791 // 00792 // The flags parameter contains the union of one or more 00793 // of the following bits to control the behavior of this 00794 // function: 00795 // 00796 // FC_roll - If this is included, the camera may be 00797 // rotated so that its up vector is perpendicular to the 00798 // top line. Otherwise, the standard up vector is used. 00799 // 00800 // FC_camera_plane - This allows the camera plane to be 00801 // adjusted to be as nearly perpendicular to the center 00802 // of the frustum as possible. Without this bit, the 00803 // orientation camera plane is defined by position of 00804 // the four points (which should all be coplanar). With 00805 // this bit, the camera plane is arbitarary, and may be 00806 // chosen so that the four points do not themselves lie 00807 // in the camera plane (but the points will still be 00808 // within the frustum). 00809 // 00810 // FC_off_axis - This allows the resulting frustum to be 00811 // off-axis to get the tightest possible fit. Without 00812 // this bit, the viewing axis will be centered within 00813 // the frustum, but there may be more wasted space along 00814 // the edges. 00815 // 00816 // FC_aspect_ratio - This allows the frustum to be 00817 // scaled non-proportionately in the vertical and 00818 // horizontal dimensions, if necessary, to get a tighter 00819 // fit. Without this bit, the current aspect ratio will 00820 // be preserved. 00821 // 00822 // FC_shear - This allows the frustum to be sheared, if 00823 // necessary, to get the tightest possible fit. This 00824 // may result in a parallelogram-based frustum, which 00825 // will give a slanted appearance to the rendered image. 00826 // Without this bit, the frustum will be 00827 // rectangle-based. 00828 // 00829 // In general, if 0 is passed in as the value for flags, 00830 // the generated frustum will be a loose fit but sane; 00831 // if -1 is passed in, it will be a tighter fit and 00832 // possibly screwy. 00833 //////////////////////////////////////////////////////////////////// 00834 void Lens:: 00835 set_frustum_from_corners(const LVecBase3f &ul, const LVecBase3f &ur, 00836 const LVecBase3f &ll, const LVecBase3f &lr, 00837 int flags) { 00838 nassertv(!ul.is_nan() && !ur.is_nan() && !ll.is_nan() && !lr.is_nan()); 00839 // We'll need to know the pre-existing eyepoint translation from the 00840 // center, so we can preserve it in the new frustum. This is 00841 // usually (0, 0, 0), but it could be an arbitrary vector. 00842 const LMatrix4f &lens_mat_inv = get_lens_mat_inv(); 00843 LVector3f eye_offset; 00844 lens_mat_inv.get_row3(eye_offset, 3); 00845 00846 // Now choose the viewing axis. If FC_camera_plane is specified, 00847 // we'll pass it through the centroid for the best camera plane; 00848 // otherwise, it's perpendicular to the plane in which the points 00849 // lie. 00850 LVector3f view_vector; 00851 if ((flags & FC_camera_plane) != 0) { 00852 view_vector = (ul + ur + ll + lr) * 0.25f; 00853 } else { 00854 Planef plane(ll, ul, ur); 00855 view_vector = plane.get_normal(); 00856 nassertv(!view_vector.is_nan() && view_vector.length_squared() != 0.0f); 00857 } 00858 00859 // Now determine the up axis. If FC_roll is specified, or if our 00860 // view vector is straight up, it is the vector perpendicular to 00861 // both the viewing axis and the top line. Otherwise, it is the 00862 // standard up axis. 00863 LVector3f up_vector = LVector3f::up(_cs); 00864 if (view_vector == up_vector || ((flags & FC_roll) != 0)) { 00865 LVector3f top = ul - ur; 00866 up_vector = view_vector.cross(top); 00867 nassertv(!up_vector.is_nan() && up_vector.length_squared() != 0.0f); 00868 } 00869 00870 // Now compute the matrix that applies this rotation. 00871 LMatrix4f rot_mat; 00872 look_at(rot_mat, view_vector, up_vector, CS_zup_right); 00873 00874 // And invert it. 00875 LMatrix4f inv_rot_mat; 00876 inv_rot_mat.invert_affine_from(rot_mat); 00877 00878 // Use that inverse matrix to convert the four corners to a local 00879 // coordinate system, looking down the Y axis. 00880 LPoint3f cul = inv_rot_mat.xform_point(ul); 00881 LPoint3f cur = inv_rot_mat.xform_point(ur); 00882 LPoint3f cll = inv_rot_mat.xform_point(ll); 00883 LPoint3f clr = inv_rot_mat.xform_point(lr); 00884 00885 // Project all points into the Y == 1 plane, so we can do 2-d 00886 // manipulation on them. 00887 nassertv(cul[1] != 0.0f && cur[1] != 0.0f && cll[1] != 0.0f && clr[1] != 0.0f); 00888 cul /= cul[1]; 00889 cur /= cur[1]; 00890 cll /= cll[1]; 00891 clr /= clr[1]; 00892 00893 LMatrix4f shear_mat = LMatrix4f::ident_mat(); 00894 LMatrix4f inv_shear_mat = LMatrix4f::ident_mat(); 00895 00896 // Now, if we're allowed to shear the frustum, do so. 00897 if ((flags & FC_shear) != 0) { 00898 build_shear_mat(shear_mat, cul, cur, cll, clr); 00899 inv_shear_mat.invert_from(shear_mat); 00900 } 00901 00902 // Now build the complete view matrix. 00903 LMatrix4f inv_view_mat = 00904 inv_rot_mat * 00905 inv_shear_mat; 00906 00907 // And reapply the eye offset to this matrix. 00908 inv_view_mat.set_row(3, eye_offset); 00909 00910 LMatrix4f view_mat; 00911 view_mat.invert_from(inv_view_mat); 00912 set_view_mat(view_mat); 00913 00914 LPoint3f ful = inv_view_mat.xform_point(ul); 00915 LPoint3f fur = inv_view_mat.xform_point(ur); 00916 LPoint3f fll = inv_view_mat.xform_point(ll); 00917 LPoint3f flr = inv_view_mat.xform_point(lr); 00918 00919 // Normalize *these* points into the y == 1 plane. 00920 nassertv(ful[1] != 0.0f && fur[1] != 0.0f && fll[1] != 0.0f && flr[1] != 0.0f); 00921 ful /= ful[1]; 00922 fur /= fur[1]; 00923 fll /= fll[1]; 00924 flr /= flr[1]; 00925 00926 // Determine the minimum field of view necesary to cover all four 00927 // transformed points. 00928 float min_x = min(min(ful[0], fur[0]), min(fll[0], flr[0])); 00929 float max_x = max(max(ful[0], fur[0]), max(fll[0], flr[0])); 00930 float min_z = min(min(ful[2], fur[2]), min(fll[2], flr[2])); 00931 float max_z = max(max(ful[2], fur[2]), max(fll[2], flr[2])); 00932 00933 float x_spread, x_center, z_spread, z_center; 00934 00935 if ((flags & FC_off_axis) != 0) { 00936 // If we're allowed to make an off-axis projection, then pick the 00937 // best center. 00938 x_center = (max_x + min_x) * 0.5f; 00939 z_center = (max_z + min_z) * 0.5f; 00940 x_spread = x_center - min_x; 00941 z_spread = z_center - min_z; 00942 } else { 00943 // Otherwise, the center must be (0, 0). 00944 x_center = 0.0f; 00945 z_center = 0.0f; 00946 x_spread = max(cabs(max_x), cabs(min_x)); 00947 z_spread = max(cabs(max_z), cabs(min_z)); 00948 } 00949 00950 float aspect_ratio = get_aspect_ratio(); 00951 nassertv(aspect_ratio != 0.0f); 00952 if ((flags & FC_aspect_ratio) == 0) { 00953 // If we must preserve the aspect ratio, then the x and z spreads 00954 // must be adjusted to match. 00955 if (x_spread < z_spread * aspect_ratio) { 00956 // x_spread is too small. 00957 x_spread = z_spread * aspect_ratio; 00958 } else if (z_spread < x_spread / aspect_ratio) { 00959 // z_spread is too small. 00960 z_spread = x_spread / aspect_ratio; 00961 } 00962 } 00963 00964 float hfov = rad_2_deg(catan(x_spread)) * 2.0f; 00965 float vfov = rad_2_deg(catan(z_spread)) * 2.0f; 00966 00967 set_fov(hfov, vfov); 00968 00969 if ((flags & FC_aspect_ratio) == 0) { 00970 // If we must preserve the aspect ratio, store it one more time. 00971 // This is mainly in case we have a non-perspective lens with a 00972 // funny relationship between fov and aspect ratio. 00973 set_aspect_ratio(aspect_ratio); 00974 } 00975 00976 const LVecBase2f &film_size = get_film_size(); 00977 nassertv(x_spread != 0.0f && z_spread != 0.0f); 00978 set_film_offset(film_size[0] * x_center / (x_spread * 2.0f), 00979 film_size[1] * z_center / (z_spread * 2.0f)); 00980 } 00981 00982 00983 //////////////////////////////////////////////////////////////////// 00984 // Function: Lens::recompute_all 00985 // Access: Published 00986 // Description: Forces all internal parameters of the Lens to be 00987 // recomputed. Normally, this should never need to be 00988 // called; it is provided only to assist in debugging. 00989 //////////////////////////////////////////////////////////////////// 00990 void Lens:: 00991 recompute_all() { 00992 _comp_flags = 0; 00993 } 00994 00995 //////////////////////////////////////////////////////////////////// 00996 // Function: Lens::is_linear 00997 // Access: Published, Virtual 00998 // Description: Returns true if the lens represents a linear 00999 // projection (e.g. PerspectiveLens, OrthographicLens), 01000 // and therefore there is a valid matrix returned by 01001 // get_projection_mat(), or false otherwise. 01002 //////////////////////////////////////////////////////////////////// 01003 bool Lens:: 01004 is_linear() const { 01005 return false; 01006 } 01007 01008 //////////////////////////////////////////////////////////////////// 01009 // Function: Lens::is_perspective 01010 // Access: Published, Virtual 01011 // Description: Returns true if the lens represents a perspective 01012 // projection (i.e. it is a PerspectiveLens), false 01013 // otherwise. 01014 //////////////////////////////////////////////////////////////////// 01015 bool Lens:: 01016 is_perspective() const { 01017 return false; 01018 } 01019 01020 //////////////////////////////////////////////////////////////////// 01021 // Function: Lens::is_orthographic 01022 // Access: Published, Virtual 01023 // Description: Returns true if the lens represents a orthographic 01024 // projection (i.e. it is a OrthographicLens), false 01025 // otherwise. 01026 //////////////////////////////////////////////////////////////////// 01027 bool Lens:: 01028 is_orthographic() const { 01029 return false; 01030 } 01031 01032 //////////////////////////////////////////////////////////////////// 01033 // Function: Lens::make_geometry 01034 // Access: Published, Virtual 01035 // Description: Allocates and returns a new Geom that can be rendered 01036 // to show a visible representation of the frustum used 01037 // for this kind of lens, if it makes sense to do 01038 // so. If a visible representation cannot be created, 01039 // returns NULL. 01040 //////////////////////////////////////////////////////////////////// 01041 PT(Geom) Lens:: 01042 make_geometry() { 01043 // The default behavior for make_geometry() will be to draw a 01044 // hexahedron around the eight vertices of the frustum. If the lens 01045 // is non-linear, the hexahedron will be curved; in that case, we'll 01046 // subdivide the lines into several segments to get an approximation 01047 // of the curve. 01048 01049 // First, define all the points we'll use in this Geom. That's one 01050 // point at each corner of the near and far planes (and possibly 01051 // more points along the edges). 01052 int num_segments = define_geom_data(); 01053 if (num_segments == 0) { 01054 // Can't do a frustum. 01055 _geom_data.clear(); 01056 return (Geom *)NULL; 01057 } 01058 01059 // Now string together the line segments. 01060 PT(GeomLinestrips) line = new GeomLinestrips(Geom::UH_static); 01061 01062 // Draw a frame around the near plane. 01063 int i, si; 01064 for (i = 0; i < 4; ++i) { 01065 for (si = 0; si < num_segments; ++si) { 01066 line->add_vertex(i * 2 + si * (4 * 2) + 0); 01067 } 01068 } 01069 line->add_vertex(0); 01070 line->close_primitive(); 01071 01072 // Draw a frame around the far plane. 01073 for (i = 0; i < 4; ++i) { 01074 for (si = 0; si < num_segments; ++si) { 01075 line->add_vertex(i * 2 + si * (4 * 2) + 1); 01076 } 01077 } 01078 line->add_vertex(1); 01079 line->close_primitive(); 01080 01081 // Draw connecting lines at the corners. 01082 line->add_vertex(0 * 2 + 0); 01083 line->add_vertex(0 * 2 + 1); 01084 line->close_primitive(); 01085 01086 line->add_vertex(1 * 2 + 0); 01087 line->add_vertex(1 * 2 + 1); 01088 line->close_primitive(); 01089 01090 line->add_vertex(2 * 2 + 0); 01091 line->add_vertex(2 * 2 + 1); 01092 line->close_primitive(); 01093 01094 line->add_vertex(3 * 2 + 0); 01095 line->add_vertex(3 * 2 + 1); 01096 line->close_primitive(); 01097 01098 // And one more line for the viewing axis. 01099 line->add_vertex(num_segments * (4 * 2) + 0); 01100 line->add_vertex(num_segments * (4 * 2) + 1); 01101 line->close_primitive(); 01102 01103 PT(Geom) geom = new Geom(_geom_data); 01104 geom->add_primitive(line); 01105 01106 return geom.p(); 01107 } 01108 01109 //////////////////////////////////////////////////////////////////// 01110 // Function: Lens::make_bounds 01111 // Access: Published, Virtual 01112 // Description: Allocates and returns a new BoundingVolume that 01113 // encloses the frustum used for this kind of 01114 // lens, if possible. If a suitable bounding 01115 // volume cannot be created, returns NULL. 01116 //////////////////////////////////////////////////////////////////// 01117 PT(BoundingVolume) Lens:: 01118 make_bounds() const { 01119 // The default bounding volume is a hexahedron based on the eight 01120 // corners of the frustum. 01121 LPoint3f fll, flr, ful, fur; 01122 LPoint3f nll, nlr, nul, nur; 01123 LPoint2f corner; 01124 01125 corner[0] = -1.0f; corner[1] = 1.0f; 01126 01127 // Upper left. 01128 if (!extrude(corner, nul, ful)) { 01129 return (BoundingVolume *)NULL; 01130 } 01131 01132 corner[0] = 1.0f; corner[1] = 1.0f; 01133 01134 // Upper right. 01135 if (!extrude(corner, nur, fur)) { 01136 return (BoundingVolume *)NULL; 01137 } 01138 01139 corner[0] = 1.0f; corner[1] = -1.0f; 01140 01141 // Lower right. 01142 if (!extrude(corner, nlr, flr)) { 01143 return (BoundingVolume *)NULL; 01144 } 01145 01146 corner[0] = -1.0f; corner[1] = -1.0f; 01147 01148 // Lower left. 01149 if (!extrude(corner, nll, fll)) { 01150 return (BoundingVolume *)NULL; 01151 } 01152 01153 return new BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul); 01154 } 01155 01156 //////////////////////////////////////////////////////////////////// 01157 // Function: Lens::get_projection_mat 01158 // Access: Published 01159 // Description: Returns the complete transformation matrix from a 3-d 01160 // point in space to a point on the film, if such a 01161 // matrix exists, or the identity matrix if the lens is 01162 // nonlinear. 01163 //////////////////////////////////////////////////////////////////// 01164 const LMatrix4f &Lens:: 01165 get_projection_mat(StereoChannel channel) const { 01166 if ((_comp_flags & CF_projection_mat) == 0) { 01167 ((Lens *)this)->compute_projection_mat(); 01168 } 01169 01170 switch (channel) { 01171 case SC_left: 01172 return _projection_mat_left; 01173 case SC_right: 01174 return _projection_mat_right; 01175 case SC_mono: 01176 case SC_stereo: 01177 return _projection_mat; 01178 } 01179 01180 return _projection_mat; 01181 } 01182 01183 //////////////////////////////////////////////////////////////////// 01184 // Function: Lens::get_projection_mat_inv 01185 // Access: Published 01186 // Description: Returns the matrix that transforms from a 2-d point 01187 // on the film to a 3-d vector in space, if such a 01188 // matrix exists. 01189 //////////////////////////////////////////////////////////////////// 01190 const LMatrix4f &Lens:: 01191 get_projection_mat_inv(StereoChannel stereo_channel) const { 01192 switch (stereo_channel) { 01193 case SC_left: 01194 { 01195 if ((_comp_flags & CF_projection_mat_left_inv) == 0) { 01196 Lens *non_const = (Lens *)this; 01197 const LMatrix4f &projection_mat_left = get_projection_mat(SC_left); 01198 non_const->_projection_mat_left_inv.invert_from(projection_mat_left); 01199 non_const->adjust_comp_flags(0, CF_projection_mat_left_inv); 01200 } 01201 } 01202 return _projection_mat_left_inv; 01203 01204 case SC_right: 01205 { 01206 if ((_comp_flags & CF_projection_mat_right_inv) == 0) { 01207 Lens *non_const = (Lens *)this; 01208 const LMatrix4f &projection_mat_right = get_projection_mat(SC_right); 01209 non_const->_projection_mat_right_inv.invert_from(projection_mat_right); 01210 non_const->adjust_comp_flags(0, CF_projection_mat_right_inv); 01211 } 01212 } 01213 return _projection_mat_right_inv; 01214 01215 case SC_mono: 01216 case SC_stereo: 01217 break; 01218 } 01219 01220 if ((_comp_flags & CF_projection_mat_inv) == 0) { 01221 Lens *non_const = (Lens *)this; 01222 const LMatrix4f &projection_mat = get_projection_mat(); 01223 non_const->_projection_mat_inv.invert_from(projection_mat); 01224 non_const->adjust_comp_flags(0, CF_projection_mat_inv); 01225 } 01226 return _projection_mat_inv; 01227 } 01228 01229 //////////////////////////////////////////////////////////////////// 01230 // Function: Lens::get_film_mat 01231 // Access: Published 01232 // Description: Returns the matrix that transforms from a point 01233 // behind the lens to a point on the film. 01234 //////////////////////////////////////////////////////////////////// 01235 const LMatrix4f &Lens:: 01236 get_film_mat() const { 01237 if ((_comp_flags & CF_film_mat) == 0) { 01238 ((Lens *)this)->compute_film_mat(); 01239 } 01240 return _film_mat; 01241 } 01242 01243 //////////////////////////////////////////////////////////////////// 01244 // Function: Lens::get_film_mat_inv 01245 // Access: Published 01246 // Description: Returns the matrix that transforms from a point on 01247 // the film to a point behind the lens. 01248 //////////////////////////////////////////////////////////////////// 01249 const LMatrix4f &Lens:: 01250 get_film_mat_inv() const { 01251 if ((_comp_flags & CF_film_mat_inv) == 0) { 01252 Lens *non_const = (Lens *)this; 01253 const LMatrix4f &film_mat = get_film_mat(); 01254 non_const->_film_mat_inv.invert_from(film_mat); 01255 non_const->adjust_comp_flags(0, CF_film_mat_inv); 01256 } 01257 return _film_mat_inv; 01258 } 01259 01260 //////////////////////////////////////////////////////////////////// 01261 // Function: Lens::get_lens_mat 01262 // Access: Published 01263 // Description: Returns the matrix that transforms from a point 01264 // in front of the lens to a point in space. 01265 //////////////////////////////////////////////////////////////////// 01266 const LMatrix4f &Lens:: 01267 get_lens_mat() const { 01268 if ((_comp_flags & CF_lens_mat) == 0) { 01269 ((Lens *)this)->compute_lens_mat(); 01270 } 01271 return _lens_mat; 01272 } 01273 01274 //////////////////////////////////////////////////////////////////// 01275 // Function: Lens::get_lens_mat_inv 01276 // Access: Published 01277 // Description: Returns the matrix that transforms from a point in 01278 // space to a point in front of the lens. 01279 //////////////////////////////////////////////////////////////////// 01280 const LMatrix4f &Lens:: 01281 get_lens_mat_inv() const { 01282 if ((_comp_flags & CF_lens_mat_inv) == 0) { 01283 Lens *non_const = (Lens *)this; 01284 const LMatrix4f &lens_mat = get_lens_mat(); 01285 non_const->_lens_mat_inv.invert_from(lens_mat); 01286 non_const->adjust_comp_flags(0, CF_lens_mat_inv); 01287 } 01288 return _lens_mat_inv; 01289 } 01290 01291 //////////////////////////////////////////////////////////////////// 01292 // Function: Lens::output 01293 // Access: Published, Virtual 01294 // Description: 01295 //////////////////////////////////////////////////////////////////// 01296 void Lens:: 01297 output(ostream &out) const { 01298 out << get_type(); 01299 } 01300 01301 //////////////////////////////////////////////////////////////////// 01302 // Function: Lens::write 01303 // Access: Published, Virtual 01304 // Description: 01305 //////////////////////////////////////////////////////////////////// 01306 void Lens:: 01307 write(ostream &out, int indent_level) const { 01308 indent(out, indent_level) << get_type() << " fov = " << get_fov() << "\n"; 01309 } 01310 01311 //////////////////////////////////////////////////////////////////// 01312 // Function: Lens::throw_change_event 01313 // Access: Protected 01314 // Description: Throws the event associated with changing properties 01315 // on this Lens, if any. 01316 //////////////////////////////////////////////////////////////////// 01317 void Lens:: 01318 throw_change_event() { 01319 ++_last_change; 01320 01321 if (!_change_event.empty()) { 01322 throw_event(_change_event, this); 01323 } 01324 01325 if (!_geom_data.is_null()) { 01326 if (_geom_data->get_ref_count() == 1) { 01327 // No one's using the data any more (there are no references to 01328 // it other than this one), so don't bother to recompute it; 01329 // just release it. 01330 _geom_data.clear(); 01331 } else { 01332 // Someone still has a handle to the data, so recompute it for 01333 // them. 01334 define_geom_data(); 01335 } 01336 } 01337 } 01338 01339 //////////////////////////////////////////////////////////////////// 01340 // Function: Lens::extrude_impl 01341 // Access: Protected, Virtual 01342 // Description: Given a 2-d point in the range (-1,1) in both 01343 // dimensions, where (0,0) is the center of the 01344 // lens and (-1,-1) is the lower-left corner, 01345 // compute the corresponding vector in space that maps 01346 // to this point, if such a vector can be determined. 01347 // The vector is returned by indicating the points on 01348 // the near plane and far plane that both map to the 01349 // indicated 2-d point. 01350 // 01351 // The z coordinate of the 2-d point is ignored. 01352 // 01353 // Returns true if the vector is defined, or false 01354 // otherwise. 01355 //////////////////////////////////////////////////////////////////// 01356 bool Lens:: 01357 extrude_impl(const LPoint3f &point2d, LPoint3f &near_point, LPoint3f &far_point) const { 01358 const LMatrix4f &projection_mat_inv = get_projection_mat_inv(); 01359 { 01360 LVecBase4f full(point2d[0], point2d[1], -1.0f, 1.0f); 01361 full = projection_mat_inv.xform(full); 01362 01363 float recip_full3 = 1.0 / max((double)full[3], (double)lens_far_limit); 01364 near_point.set(full[0] * recip_full3, 01365 full[1] * recip_full3, 01366 full[2] * recip_full3); 01367 } 01368 { 01369 LVecBase4f full(point2d[0], point2d[1], 1.0f, 1.0f); 01370 full = projection_mat_inv.xform(full); 01371 01372 // We can truncate the weight factor at near 0. If it goes too 01373 // close to zero, or becomes negative, the far plane moves out 01374 // past infinity and comes back in behind the lens, which is just 01375 // crazy. Truncating it to zero keeps the far plane from moving 01376 // too far out. 01377 float recip_full3 = 1.0 / max((double)full[3], (double)lens_far_limit); 01378 far_point.set(full[0] * recip_full3, 01379 full[1] * recip_full3, 01380 full[2] * recip_full3); 01381 } 01382 return true; 01383 } 01384 01385 //////////////////////////////////////////////////////////////////// 01386 // Function: Lens::extrude_vec_impl 01387 // Access: Protected, Virtual 01388 // Description: Given a 2-d point in the range (-1,1) in both 01389 // dimensions, where (0,0) is the center of the 01390 // lens and (-1,-1) is the lower-left corner, 01391 // compute the vector that corresponds to the view 01392 // direction. This will be parallel to the normal on 01393 // the surface (the far plane) corresponding to the lens 01394 // shape at this point. 01395 // 01396 // Generally, for all rational lenses, the center of the 01397 // film at (0,0) computes a vector that is in the same 01398 // direction as the vector specified by 01399 // set_view_vector(). 01400 // 01401 // For all linear lenses, including perspective and 01402 // orthographic lenses, all points on the film compute 01403 // this same vector (the far plane is a flat plane, so 01404 // the normal is the same everywhere). For curved 01405 // lenses like fisheye and cylindrical lenses, different 01406 // points may compute different vectors (the far "plane" 01407 // on these lenses is a curved surface). 01408 // 01409 // The z coordinate of the 2-d point is ignored. 01410 // 01411 // Returns true if the vector is defined, or false 01412 // otherwise. 01413 //////////////////////////////////////////////////////////////////// 01414 bool Lens:: 01415 extrude_vec_impl(const LPoint3f &point2d, LVector3f &vec) const { 01416 vec = LVector3f::forward(_cs) * get_lens_mat(); 01417 return true; 01418 } 01419 01420 //////////////////////////////////////////////////////////////////// 01421 // Function: Lens::project_impl 01422 // Access: Protected, Virtual 01423 // Description: Given a 3-d point in space, determine the 2-d point 01424 // this maps to, in the range (-1,1) in both dimensions, 01425 // where (0,0) is the center of the lens and 01426 // (-1,-1) is the lower-left corner. 01427 // 01428 // The z coordinate will also be set to a value in the 01429 // range (-1, 1), where 1 represents a point on the near 01430 // plane, and -1 represents a point on the far plane. 01431 // 01432 // Returns true if the 3-d point is in front of the lens 01433 // and within the viewing frustum (in which case point2d 01434 // is filled in), or false otherwise (in which case 01435 // point2d will be filled in with something, which may 01436 // or may not be meaningful). 01437 //////////////////////////////////////////////////////////////////// 01438 bool Lens:: 01439 project_impl(const LPoint3f &point3d, LPoint3f &point2d) const { 01440 const LMatrix4f &projection_mat = get_projection_mat(); 01441 LVecBase4f full(point3d[0], point3d[1], point3d[2], 1.0f); 01442 full = projection_mat.xform(full); 01443 if (full[3] == 0.0f) { 01444 point2d.set(0.0f, 0.0f, 0.0f); 01445 return false; 01446 } 01447 float recip_full3 = 1.0f/full[3]; 01448 point2d.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3); 01449 return 01450 (full[3] > 0.0f) && 01451 (point2d[0] >= -1.0f) && (point2d[0] <= 1.0f) && 01452 (point2d[1] >= -1.0f) && (point2d[1] <= 1.0f); 01453 } 01454 01455 //////////////////////////////////////////////////////////////////// 01456 // Function: Lens::compute_film_size 01457 // Access: Protected, Virtual 01458 // Description: Computes the size and shape of the film behind the 01459 // camera, based on the aspect ratio and fov. 01460 //////////////////////////////////////////////////////////////////// 01461 void Lens:: 01462 compute_film_size() { 01463 if ((_user_flags & (UF_min_fov | UF_focal_length)) == (UF_min_fov | UF_focal_length)) { 01464 // If we just have a min FOV and a focal length, that determines 01465 // the smaller of the two film_sizes, and the larger is simply 01466 // chosen according to the aspect ratio. 01467 float fs = fov_to_film(_min_fov, _focal_length, true); 01468 nassertv((_user_flags & UF_aspect_ratio) != 0 || 01469 (_comp_flags & CF_aspect_ratio) != 0); 01470 01471 if (_aspect_ratio < 1.0f) { 01472 _film_size[1] = fs / _aspect_ratio; 01473 _film_size[0] = fs; 01474 01475 } else { 01476 _film_size[0] = fs * _aspect_ratio; 01477 _film_size[1] = fs; 01478 } 01479 01480 } else { 01481 if ((_user_flags & UF_film_width) == 0) { 01482 if ((_user_flags & (UF_hfov | UF_focal_length)) == (UF_hfov | UF_focal_length)) { 01483 _film_size[0] = fov_to_film(_fov[0], _focal_length, true); 01484 } else { 01485 _film_size[0] = 1.0f; 01486 } 01487 } 01488 01489 if ((_user_flags & UF_film_height) == 0) { 01490 if ((_user_flags & (UF_vfov | UF_focal_length)) == (UF_vfov | UF_focal_length)) { 01491 _film_size[1] = fov_to_film(_fov[1], _focal_length, false); 01492 01493 } else if ((_user_flags & (UF_hfov | UF_vfov)) == (UF_hfov | UF_vfov)) { 01494 // If we don't have a focal length, but we have an explicit vfov 01495 // and hfov, we can infer the focal length is whatever makes the 01496 // film width, above, be what it is. 01497 if ((_comp_flags & CF_focal_length) == 0) { 01498 _focal_length = fov_to_focal_length(_fov[0], _film_size[0], true); 01499 adjust_comp_flags(0, CF_focal_length); 01500 } 01501 _film_size[1] = fov_to_film(_fov[1], _focal_length, false); 01502 01503 } else if ((_user_flags & UF_aspect_ratio) != 0 || 01504 (_comp_flags & CF_aspect_ratio) != 0) { 01505 _film_size[1] = _film_size[0] / _aspect_ratio; 01506 01507 } else { 01508 // Default is an aspect ratio of 1. 01509 _film_size[1] = _film_size[0]; 01510 } 01511 } 01512 } 01513 01514 adjust_comp_flags(0, CF_film_size); 01515 } 01516 01517 //////////////////////////////////////////////////////////////////// 01518 // Function: Lens::compute_focal_length 01519 // Access: Protected, Virtual 01520 // Description: Computes the focal length of the lens, based on the 01521 // fov and film size. This is based on the horizontal 01522 // dimension. 01523 //////////////////////////////////////////////////////////////////// 01524 void Lens:: 01525 compute_focal_length() { 01526 if ((_user_flags & UF_focal_length) == 0) { 01527 const LVecBase2f &film_size = get_film_size(); 01528 const LVecBase2f &fov = get_fov(); 01529 _focal_length = fov_to_focal_length(fov[0], film_size[0], true); 01530 } 01531 01532 adjust_comp_flags(0, CF_focal_length); 01533 } 01534 01535 //////////////////////////////////////////////////////////////////// 01536 // Function: Lens::compute_fov 01537 // Access: Protected, Virtual 01538 // Description: Computes the field of view of the lens, based on the 01539 // film size and focal length. 01540 //////////////////////////////////////////////////////////////////// 01541 void Lens:: 01542 compute_fov() { 01543 const LVecBase2f &film_size = get_film_size(); 01544 01545 bool got_hfov = ((_user_flags & UF_hfov) != 0); 01546 bool got_vfov = ((_user_flags & UF_vfov) != 0); 01547 bool got_min_fov = ((_user_flags & UF_min_fov) != 0); 01548 01549 if (!got_hfov && !got_vfov && !got_min_fov) { 01550 // If the user hasn't specified any FOV, we have to compute it. 01551 if ((_user_flags & UF_focal_length) != 0) { 01552 // The FOV is determined from the film size and focal length. 01553 _fov[0] = film_to_fov(film_size[0], _focal_length, true); 01554 _fov[1] = film_to_fov(film_size[1], _focal_length, true); 01555 got_hfov = true; 01556 got_vfov = true; 01557 01558 } else { 01559 // We can't compute the FOV; take the default. 01560 _min_fov = default_fov; 01561 got_min_fov = true; 01562 } 01563 } 01564 01565 if (got_min_fov) { 01566 // If we have just a min_fov, use it to derive whichever fov is 01567 // smaller. 01568 if (film_size[0] < film_size[1]) { 01569 _fov[0] = _min_fov; 01570 got_hfov = true; 01571 } else { 01572 _fov[1] = _min_fov; 01573 got_vfov = true; 01574 } 01575 } 01576 01577 // Now compute whichever fov is remaining. 01578 if (!got_hfov) { 01579 if ((_user_flags & UF_focal_length) == 0 && 01580 (_comp_flags & CF_focal_length) == 0) { 01581 // If we don't have an explicit focal length, we can infer it 01582 // from the above. 01583 nassertv(got_vfov); 01584 _focal_length = fov_to_focal_length(_fov[1], film_size[1], true); 01585 adjust_comp_flags(0, CF_focal_length); 01586 } 01587 _fov[0] = film_to_fov(film_size[0], _focal_length, false); 01588 got_hfov = true; 01589 } 01590 01591 if (!got_vfov) { 01592 if ((_user_flags & UF_focal_length) == 0 && 01593 (_comp_flags & CF_focal_length) == 0) { 01594 // If we don't have an explicit focal length, we can infer it 01595 // from the above. 01596 nassertv(got_hfov); 01597 _focal_length = fov_to_focal_length(_fov[0], film_size[0], true); 01598 adjust_comp_flags(0, CF_focal_length); 01599 } 01600 _fov[1] = film_to_fov(film_size[1], _focal_length, false); 01601 got_vfov = true; 01602 } 01603 01604 if (!got_min_fov) { 01605 _min_fov = film_size[0] < film_size[1] ? _fov[0] : _fov[1]; 01606 got_min_fov = true; 01607 } 01608 01609 nassertv(got_hfov && got_vfov && got_min_fov); 01610 adjust_comp_flags(0, CF_fov); 01611 } 01612 01613 //////////////////////////////////////////////////////////////////// 01614 // Function: Lens::compute_aspect_ratio 01615 // Access: Protected, Virtual 01616 // Description: Computes the aspect ratio of the film rectangle, as a 01617 // ratio of width to height. 01618 //////////////////////////////////////////////////////////////////// 01619 void Lens:: 01620 compute_aspect_ratio() { 01621 if ((_user_flags & UF_aspect_ratio) == 0) { 01622 const LVecBase2f &film_size = get_film_size(); 01623 if (film_size[1] == 0.0f) { 01624 _aspect_ratio = 1.0f; 01625 } else { 01626 _aspect_ratio = film_size[0] / film_size[1]; 01627 } 01628 } 01629 adjust_comp_flags(0, CF_aspect_ratio); 01630 } 01631 01632 //////////////////////////////////////////////////////////////////// 01633 // Function: Lens::compute_view_hpr 01634 // Access: Protected, Virtual 01635 // Description: Computes the Euler angles representing the lens' 01636 // rotation. 01637 //////////////////////////////////////////////////////////////////// 01638 void Lens:: 01639 compute_view_hpr() { 01640 if ((_user_flags & UF_view_hpr) == 0) { 01641 const LMatrix4f &view_mat = get_view_mat(); 01642 LVecBase3f scale, shear, translate; 01643 decompose_matrix(view_mat, scale, shear, _view_hpr, translate, _cs); 01644 } 01645 adjust_comp_flags(0, CF_view_hpr); 01646 } 01647 01648 //////////////////////////////////////////////////////////////////// 01649 // Function: Lens::compute_view_vector 01650 // Access: Protected, Virtual 01651 // Description: Computes the view vector and up vector for the lens. 01652 //////////////////////////////////////////////////////////////////// 01653 void Lens:: 01654 compute_view_vector() { 01655 if ((_user_flags & UF_view_vector) == 0) { 01656 const LMatrix4f &view_mat = get_view_mat(); 01657 _view_vector = LVector3f::forward(_cs) * view_mat; 01658 _up_vector = LVector3f::up(_cs) * view_mat; 01659 } 01660 adjust_comp_flags(0, CF_view_vector); 01661 } 01662 01663 //////////////////////////////////////////////////////////////////// 01664 // Function: Lens::compute_projection_mat 01665 // Access: Protected, Virtual 01666 // Description: Computes the complete transformation matrix from 3-d 01667 // point to 2-d point, if the lens is linear. 01668 //////////////////////////////////////////////////////////////////// 01669 void Lens:: 01670 compute_projection_mat() { 01671 _projection_mat = 01672 _projection_mat_left = 01673 _projection_mat_right = 01674 _projection_mat_inv = 01675 _projection_mat_left_inv = 01676 _projection_mat_right_inv = 01677 LMatrix4f::ident_mat(); 01678 adjust_comp_flags(0, CF_projection_mat | CF_projection_mat_inv | 01679 CF_projection_mat_left_inv |CF_projection_mat_right_inv); 01680 } 01681 01682 //////////////////////////////////////////////////////////////////// 01683 // Function: Lens::compute_film_mat 01684 // Access: Protected, Virtual 01685 // Description: Computes the matrix that transforms from a point 01686 // behind the lens to a point on the film. 01687 //////////////////////////////////////////////////////////////////// 01688 void Lens:: 01689 compute_film_mat() { 01690 // The lens will return a point in the range [-film_size/2, 01691 // film_size/2] in each dimension. Convert this to [-1, 1], and 01692 // also apply the offset. 01693 01694 // We declare these two as local variables, instead of references, 01695 // to work around a VC7 compiler bug. 01696 LVecBase2f film_size = get_film_size(); 01697 LVector2f film_offset = get_film_offset(); 01698 01699 float scale_x = 2.0f / film_size[0]; 01700 float scale_y = 2.0f / film_size[1]; 01701 _film_mat.set(scale_x, 0.0f, 0.0f, 0.0f, 01702 0.0f, scale_y, 0.0f, 0.0f, 01703 0.0f, 0.0f, 1.0f, 0.0f, 01704 -film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f, 1.0f); 01705 01706 if ((_user_flags & UF_keystone) != 0) { 01707 _film_mat = LMatrix4f(1.0f, 0.0f, _keystone[0], _keystone[0], 01708 0.0f, 1.0f, _keystone[1], _keystone[1], 01709 0.0f, 0.0f, 1.0f, 0.0f, 01710 0.0f, 0.0f, 0.0f, 1.0f) * _film_mat; 01711 } 01712 01713 adjust_comp_flags(CF_film_mat_inv, 01714 CF_film_mat); 01715 } 01716 01717 //////////////////////////////////////////////////////////////////// 01718 // Function: Lens::compute_lens_mat 01719 // Access: Protected, Virtual 01720 // Description: Computes the matrix that transforms from a point 01721 // in front of the lens to a point in space. 01722 //////////////////////////////////////////////////////////////////// 01723 void Lens:: 01724 compute_lens_mat() { 01725 if ((_user_flags & UF_view_mat) == 0) { 01726 if ((_user_flags & UF_view_hpr) != 0) { 01727 compose_matrix(_lens_mat, 01728 LVecBase3f(1.0f, 1.0f, 1.0f), 01729 LVecBase3f(0.0f, 0.0f, 0.0f), 01730 _view_hpr, 01731 LVecBase3f(0.0f, 0.0f, 0.0f), _cs); 01732 01733 } else if ((_user_flags & UF_view_vector) != 0) { 01734 look_at(_lens_mat, _view_vector, _up_vector, _cs); 01735 01736 } else { 01737 _lens_mat = LMatrix4f::ident_mat(); 01738 } 01739 } 01740 adjust_comp_flags(CF_lens_mat_inv, 01741 CF_lens_mat); 01742 } 01743 01744 //////////////////////////////////////////////////////////////////// 01745 // Function: Lens::fov_to_film 01746 // Access: Protected, Virtual 01747 // Description: Given a field of view in degrees and a focal length, 01748 // compute the corresponding width (or height) on the 01749 // film. If horiz is true, this is in the horizontal 01750 // direction; otherwise, it is in the vertical direction 01751 // (some lenses behave differently in each direction). 01752 //////////////////////////////////////////////////////////////////// 01753 float Lens:: 01754 fov_to_film(float, float, bool) const { 01755 return 1.0f; 01756 } 01757 01758 //////////////////////////////////////////////////////////////////// 01759 // Function: Lens::fov_to_focal_length 01760 // Access: Protected, Virtual 01761 // Description: Given a field of view in degrees and a width (or 01762 // height) on the film, compute the focal length of the 01763 // lens. If horiz is true, this is in the horizontal 01764 // direction; otherwise, it is in the vertical direction 01765 // (some lenses behave differently in each direction). 01766 //////////////////////////////////////////////////////////////////// 01767 float Lens:: 01768 fov_to_focal_length(float, float, bool) const { 01769 return 1.0f; 01770 } 01771 01772 //////////////////////////////////////////////////////////////////// 01773 // Function: Lens::film_to_fov 01774 // Access: Protected, Virtual 01775 // Description: Given a width (or height) on the film and a focal 01776 // length, compute the field of view in degrees. If 01777 // horiz is true, this is in the horizontal direction; 01778 // otherwise, it is in the vertical direction (some 01779 // lenses behave differently in each direction). 01780 //////////////////////////////////////////////////////////////////// 01781 float Lens:: 01782 film_to_fov(float, float, bool) const { 01783 return default_fov; 01784 } 01785 01786 //////////////////////////////////////////////////////////////////// 01787 // Function: Lens::resequence_fov_triad 01788 // Access: Private, Static 01789 // Description: Called whenever the user changes one of the three FOV 01790 // parameters: fov, focal length, or film size. This 01791 // rearranges the three sequence numbers so the newest 01792 // parameter has value 2, and the older parameters are 01793 // kept in sequence order. 01794 // 01795 // This is used to determine which two parameters of the 01796 // three are the most recently changed, and conversely, 01797 // which one the user has *not* changed recently. It is 01798 // this third value which should be discarded. 01799 //////////////////////////////////////////////////////////////////// 01800 void Lens:: 01801 resequence_fov_triad(char &newest, char &older_a, char &older_b) { 01802 nassertv(newest + older_a + older_b == 3); 01803 switch (newest) { 01804 case 0: 01805 newest = 2; 01806 older_a--; 01807 older_b--; 01808 nassertv(older_a + older_b == 1); 01809 break; 01810 01811 case 1: 01812 newest = 2; 01813 if (older_a == 2) { 01814 nassertv(older_b == 0); 01815 older_a = 1; 01816 } else { 01817 nassertv(older_a == 0 && older_b == 2); 01818 older_b = 1; 01819 } 01820 break; 01821 01822 case 2: 01823 nassertv(older_a + older_b == 1); 01824 break; 01825 01826 default: 01827 gobj_cat.error() 01828 << "Invalid fov sequence numbers in lens: " 01829 << (int)newest << ", " << (int)older_a << ", " << (int)older_b << "\n"; 01830 nassertv(false); 01831 return; 01832 } 01833 } 01834 01835 //////////////////////////////////////////////////////////////////// 01836 // Function: Lens::define_geom_data 01837 // Access: Private 01838 // Description: Adjusts (or defines for the first time) all the 01839 // vertices in the _geom_data to match the properties of 01840 // the lens. This will update the visual representation 01841 // of the lens's frustum to match the changing 01842 // parameters. Returns the number of line segments per 01843 // edge. 01844 //////////////////////////////////////////////////////////////////// 01845 int Lens:: 01846 define_geom_data() { 01847 int num_segments = 1; 01848 if (!is_linear()) { 01849 num_segments = 10; 01850 } 01851 01852 if (_geom_data == (GeomVertexData *)NULL) { 01853 _geom_data = new GeomVertexData 01854 ("lens", GeomVertexFormat::get_v3(), 01855 Geom::UH_dynamic); 01856 } 01857 01858 GeomVertexWriter vertex(_geom_data, InternalName::get_vertex()); 01859 LPoint3f near_point, far_point; 01860 for (int si = 0; si < num_segments; si++) { 01861 float t = 2.0f * (float)si / (float)num_segments; 01862 01863 // Upper left, top edge. 01864 LPoint2f p1(-1.0f + t, 1.0f); 01865 if (!extrude(p1, near_point, far_point)) { 01866 // Hey, this point is off the lens! Can't do a frustum. 01867 return 0; 01868 } 01869 vertex.add_data3f(near_point); 01870 vertex.add_data3f(far_point); 01871 01872 // Upper right, right edge. 01873 LPoint2f p2(1.0f, 1.0f - t); 01874 if (!extrude(p2, near_point, far_point)) { 01875 // Hey, this point is off the lens! Can't do a frustum. 01876 return 0; 01877 } 01878 vertex.add_data3f(near_point); 01879 vertex.add_data3f(far_point); 01880 01881 // Lower right, bottom edge. 01882 LPoint2f p3(1.0f - t, -1.0f); 01883 if (!extrude(p3, near_point, far_point)) { 01884 // Hey, this point is off the lens! Can't do a frustum. 01885 return 0; 01886 } 01887 vertex.add_data3f(near_point); 01888 vertex.add_data3f(far_point); 01889 01890 // Lower left, left edge. 01891 LPoint2f p4(-1.0f, -1.0f + t); 01892 if (!extrude(p4, near_point, far_point)) { 01893 // Hey, this point is off the lens! Can't do a frustum. 01894 return 0; 01895 } 01896 vertex.add_data3f(near_point); 01897 vertex.add_data3f(far_point); 01898 } 01899 01900 // Finally, add one more pair for the viewing axis. 01901 LPoint3f near_axis = LPoint3f::origin(_cs) + LVector3f::forward(_cs) * _near_distance; 01902 LPoint3f far_axis = LPoint3f::origin(_cs) + LVector3f::forward(_cs) * _far_distance; 01903 const LMatrix4f &lens_mat = get_lens_mat(); 01904 near_axis = near_axis * lens_mat; 01905 far_axis = far_axis * lens_mat; 01906 vertex.add_data3f(near_axis); 01907 vertex.add_data3f(far_axis); 01908 01909 return num_segments; 01910 } 01911 01912 //////////////////////////////////////////////////////////////////// 01913 // Function: Lens::build_shear_mat 01914 // Access: Private, Static 01915 // Description: A support function for set_frustum_from_corners(), 01916 // this computes a matrix that will shear the four 01917 // indicated points to the most nearly rectangular. 01918 //////////////////////////////////////////////////////////////////// 01919 void Lens:: 01920 build_shear_mat(LMatrix4f &shear_mat, 01921 const LPoint3f &cul, const LPoint3f &cur, 01922 const LPoint3f &cll, const LPoint3f &clr) { 01923 // Fit a parallelogram around these four points. 01924 01925 // Put the points in an array so we can rotate it around to find 01926 // the longest edge. 01927 LPoint3f points[4] = { 01928 cul, cur, clr, cll 01929 }; 01930 01931 float max_edge_length = -1.0f; 01932 int base_edge = -1; 01933 for (int i = 0; i < 4; i++) { 01934 LVector3f edge = points[(i + 1) % 4] - points[i]; 01935 float length = edge.length_squared(); 01936 if (length > max_edge_length) { 01937 base_edge = i; 01938 max_edge_length = length; 01939 } 01940 } 01941 01942 const LPoint3f &base_origin = points[base_edge]; 01943 LVector3f base_vec = points[(base_edge + 1) % 4] - base_origin; 01944 01945 float base_edge_length = csqrt(max_edge_length); 01946 01947 // The longest edge is the base of our parallelogram. The parallel 01948 // edge must pass through the point furthest from this edge. 01949 01950 int a = (base_edge + 2) % 4; 01951 int b = (base_edge + 3) % 4; 01952 01953 float a_dist = sqr_dist_to_line(points[a], base_origin, base_vec); 01954 float b_dist = sqr_dist_to_line(points[b], base_origin, base_vec); 01955 01956 int far_point; 01957 float dist; 01958 if (a_dist > b_dist) { 01959 far_point = a; 01960 dist = csqrt(a_dist); 01961 } else { 01962 far_point = b; 01963 dist = csqrt(b_dist); 01964 } 01965 01966 // Try to make the parallelogram as nearly rectangular as possible. 01967 // How suitable is a true rectangle? 01968 LVector3f perpendic = base_vec.cross(LVector3f(0.0f, -1.0f, 0.0f)); 01969 perpendic.normalize(); 01970 perpendic *= dist; 01971 LPoint3f parallel_origin = points[base_edge] + perpendic; 01972 01973 // It follows that far_point is on the line passing through the 01974 // parallel edge. Is it within the endpoints? 01975 LVector3f base_norm_vec = base_vec / base_edge_length; 01976 01977 LVector3f far_point_delta = points[far_point] - parallel_origin; 01978 float far_point_pos = far_point_delta.dot(base_norm_vec); 01979 01980 if (far_point_pos < 0.0f) { 01981 // We have to slide the parallel_origin back to include far_point. 01982 parallel_origin += base_norm_vec * far_point_pos; 01983 01984 } else if (far_point_pos > base_edge_length) { 01985 // We have to slide the parallel_origin forward to include 01986 // far_point. 01987 parallel_origin += base_norm_vec * (far_point_pos - base_edge_length); 01988 } 01989 01990 // Finally, is the other point within the parallelogram? 01991 float t; 01992 float Ox = parallel_origin[0]; 01993 float Oy = parallel_origin[2]; 01994 float Vx = base_vec[0]; 01995 float Vy = base_vec[2]; 01996 float Ax, Ay, Bx, By; 01997 01998 if (far_point == a) { 01999 // near point is b 02000 LVector3f v = points[b] - base_origin; 02001 Ax = points[b][0]; 02002 Ay = points[b][2]; 02003 Bx = v[0]; 02004 By = v[2]; 02005 } else { 02006 // near point is a 02007 LVector3f v = points[a] - (base_origin + base_vec); 02008 Ax = points[a][0]; 02009 Ay = points[a][2]; 02010 Bx = v[0]; 02011 By = v[2]; 02012 } 02013 t = ((Ox - Ax) * By + (Ay - Oy) * Bx) / (Bx * Vy - By * Vx); 02014 02015 if (t < 0.0f) { 02016 // We need to slide the parallel_origin back to include 02017 // the near point. 02018 parallel_origin += base_vec * t; 02019 } else if (t > 1.0f) { 02020 // We need to slide the parallel_origin forward to include the far 02021 // point. 02022 parallel_origin += base_vec * (1.0f - t); 02023 } 02024 02025 LVector3f adjacent_norm_vec = parallel_origin - base_origin; 02026 adjacent_norm_vec.normalize(); 02027 02028 // Now we've defined a parallelogram that includes all four points, 02029 // and we're ready to build a shear transform. 02030 shear_mat = LMatrix4f::ident_mat(); 02031 02032 // The edges of the parallelogram become the axes. 02033 switch (base_edge) { 02034 case 0: 02035 // The base_origin is the upper-left corner. X axis is base_norm_vec, 02036 // Z axis is -adjacent_norm_vec. 02037 shear_mat.set_row(0, base_norm_vec); 02038 shear_mat.set_row(2, -adjacent_norm_vec); 02039 break; 02040 02041 case 1: 02042 // The base_origin is the upper-right corner. X axis is 02043 // -adjacent_norm_vec, Z axis is -base_norm_vec. 02044 shear_mat.set_row(0, -adjacent_norm_vec); 02045 shear_mat.set_row(2, -base_norm_vec); 02046 break; 02047 02048 case 2: 02049 // The base_origin is the lower-right corner. X axis is 02050 // -base_norm_vec, Z axis is adjacent_norm_vec. 02051 shear_mat.set_row(0, -base_norm_vec); 02052 shear_mat.set_row(2, adjacent_norm_vec); 02053 break; 02054 02055 case 3: 02056 // The base_origin is the lower-left corner. X axis is 02057 // adjacent_norm_vec, Z axis is base_norm_vec. 02058 shear_mat.set_row(0, adjacent_norm_vec); 02059 shear_mat.set_row(2, base_norm_vec); 02060 break; 02061 02062 default: 02063 nassertv(false); 02064 } 02065 } 02066 02067 //////////////////////////////////////////////////////////////////// 02068 // Function: Lens::sqr_dist_to_line 02069 // Access: Private, Static 02070 // Description: A support function for build_shear_mat(), this 02071 // computes the minimum distance from a point to a line, 02072 // and returns the distance squared. 02073 //////////////////////////////////////////////////////////////////// 02074 float Lens:: 02075 sqr_dist_to_line(const LPoint3f &point, const LPoint3f &origin, 02076 const LVector3f &vec) { 02077 LVector3f norm = vec; 02078 norm.normalize(); 02079 LVector3f d = point - origin; 02080 float hyp_2 = d.length_squared(); 02081 float leg = d.dot(norm); 02082 return hyp_2 - leg * leg; 02083 } 02084 02085 //////////////////////////////////////////////////////////////////// 02086 // Function: Lens::write_datagram 02087 // Access: Public, Virtual 02088 // Description: Writes the contents of this object to the datagram 02089 // for shipping out to a Bam file. 02090 //////////////////////////////////////////////////////////////////// 02091 void Lens:: 02092 write_datagram(BamWriter *manager, Datagram &dg) { 02093 TypedWritable::write_datagram(manager, dg); 02094 02095 dg.add_string(_change_event); 02096 dg.add_uint8((int)_cs); 02097 _film_size.write_datagram(dg); 02098 _film_offset.write_datagram(dg); 02099 dg.add_float32(_focal_length); 02100 _fov.write_datagram(dg); 02101 dg.add_float32(_aspect_ratio); 02102 dg.add_float32(_near_distance); 02103 dg.add_float32(_far_distance); 02104 dg.add_uint16(_user_flags); 02105 } 02106 02107 //////////////////////////////////////////////////////////////////// 02108 // Function: Lens::fillin 02109 // Access: Protected 02110 // Description: This internal function is called by make_from_bam to 02111 // read in all of the relevant data from the BamFile for 02112 // the new Lens. 02113 //////////////////////////////////////////////////////////////////// 02114 void Lens:: 02115 fillin(DatagramIterator &scan, BamReader *manager) { 02116 TypedWritable::fillin(scan, manager); 02117 02118 _change_event = scan.get_string(); 02119 _cs = (CoordinateSystem)scan.get_uint8(); 02120 _film_size.read_datagram(scan); 02121 _film_offset.read_datagram(scan); 02122 _focal_length = scan.get_float32(); 02123 _fov.read_datagram(scan); 02124 _aspect_ratio = scan.get_float32(); 02125 _near_distance = scan.get_float32(); 02126 _far_distance = scan.get_float32(); 02127 _user_flags = scan.get_uint16(); 02128 02129 _comp_flags = 0; 02130 }