Panda3D

lens.cxx

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 &copy) {
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 &copy) {
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_aspect_ratio,
00392                       UF_hfov | UF_vfov | UF_min_fov);
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_aspect_ratio,
00397                       UF_hfov | UF_vfov | UF_min_fov);
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     return;
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     return;
01821 
01822   case 2:
01823     nassertv(older_a + older_b == 1);
01824     return;
01825 
01826   default:
01827     gobj_cat.error()
01828       << "Invalid fov sequence numbers in lens: " << newest << ", " << older_a
01829       << ", " << 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 }
 All Classes Functions Variables Enumerations