Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/gobj/lens.cxx

Go to the documentation of this file.
00001 // Filename: lens.cxx
00002 // Created by:  drose (18Feb99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "lens.h"
00020 #include "throw_event.h"
00021 #include "compose_matrix.h"
00022 #include "look_at.h"
00023 #include "geomLinestrip.h"
00024 #include "boundingHexahedron.h"
00025 #include "indent.h"
00026 #include "config_gobj.h"
00027 #include "plane.h"
00028 
00029 TypeHandle Lens::_type_handle;
00030 
00031 const float Lens::_default_fov = 40.0f;
00032 
00033 ////////////////////////////////////////////////////////////////////
00034 //     Function: Lens::Constructor
00035 //       Access: Public
00036 //  Description: 
00037 ////////////////////////////////////////////////////////////////////
00038 Lens::
00039 Lens() {
00040   clear();
00041 }
00042 
00043 ////////////////////////////////////////////////////////////////////
00044 //     Function: Lens::Copy Constructor
00045 //       Access: Public
00046 //  Description: 
00047 ////////////////////////////////////////////////////////////////////
00048 Lens::
00049 Lens(const Lens &copy) {
00050   (*this) = copy;
00051 }
00052 
00053 ////////////////////////////////////////////////////////////////////
00054 //     Function: Lens::Copy Assignment Operator
00055 //       Access: Public
00056 //  Description: 
00057 ////////////////////////////////////////////////////////////////////
00058 void Lens::
00059 operator = (const Lens &copy) {
00060   _change_event = copy._change_event;
00061   _cs = copy._cs;
00062   _film_size = copy._film_size;
00063   _film_offset = copy._film_offset;
00064   _focal_length = copy._focal_length;
00065   _fov = copy._fov;
00066   _aspect_ratio = copy._aspect_ratio;
00067   _near_distance = copy._near_distance;
00068   _far_distance = copy._far_distance;
00069   _user_flags = copy._user_flags;
00070   _comp_flags = 0;
00071 
00072   // We don't copy the _geom_coords array.  That's unique to each
00073   // Lens.
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: Lens::set_coordinate_system
00078 //       Access: Published
00079 //  Description: Specifies the coordinate system that all 3-d
00080 //               computations are performed within for this
00081 //               Lens.  Normally, this is CS_default.
00082 ////////////////////////////////////////////////////////////////////
00083 void Lens::
00084 set_coordinate_system(CoordinateSystem cs) {
00085   _cs = cs;
00086   adjust_comp_flags(CF_mat | CF_view_hpr | CF_view_vector, 0);
00087 }
00088 
00089 ////////////////////////////////////////////////////////////////////
00090 //     Function: Lens::clear
00091 //       Access: Published
00092 //  Description: Resets all lens parameters to their initial default
00093 //               settings.
00094 ////////////////////////////////////////////////////////////////////
00095 void Lens::
00096 clear() {
00097   _change_event = "";
00098   _cs = CS_default;
00099   _film_size.set(1.0f, 1.0f);
00100   _film_offset.set(0.0f, 0.0f);
00101   _focal_length = 1.0f;
00102   _fov.set(_default_fov, _default_fov);
00103   _aspect_ratio = 1.0f;
00104   _near_distance = default_near;
00105   _far_distance = default_far;
00106   _view_hpr.set(0.0f, 0.0f, 0.0f);
00107   _view_vector.set(0.0f, 1.0f, 0.0f);
00108   _up_vector.set(0.0f, 0.0f, 1.0f);
00109   _iod_offset = 0.0f;
00110   _user_flags = 0;
00111   _comp_flags = CF_fov;
00112 
00113   // Assign an initial arbitrary sequence to these three.
00114   _film_size_seq = 0;
00115   _focal_length_seq = 1;
00116   _fov_seq = 2;
00117 }
00118 
00119 ////////////////////////////////////////////////////////////////////
00120 //     Function: Lens::set_film_size
00121 //       Access: Published
00122 //  Description: Sets the horizontal size of the film without changing
00123 //               its shape.  The aspect ratio remains unchanged; this
00124 //               computes the vertical size of the film to
00125 //               automatically maintain the aspect ratio.
00126 ////////////////////////////////////////////////////////////////////
00127 void Lens::
00128 set_film_size(float width) {
00129   _film_size.set(width, width / get_aspect_ratio());
00130 
00131   // We can't specify all three of focal length, fov, and film size.
00132   // Throw out the oldest one.
00133   resequence_fov_triad(_film_size_seq, _focal_length_seq, _fov_seq);
00134 
00135   if (_fov_seq == 0) {
00136     // Throw out fov if it's oldest.
00137     adjust_user_flags(UF_hfov | UF_vfov | UF_film_height,
00138                       UF_film_width);
00139   } else {
00140     // Otherwise, throw out focal length.
00141     nassertv(_focal_length_seq == 0);
00142     adjust_user_flags(UF_focal_length | UF_film_height,
00143                       UF_film_width);
00144   }
00145   adjust_comp_flags(CF_mat | CF_focal_length | CF_fov,
00146                     CF_film_size);
00147   throw_change_event();
00148 }
00149 
00150 ////////////////////////////////////////////////////////////////////
00151 //     Function: Lens::set_film_size
00152 //       Access: Published
00153 //  Description: Sets the size and shape of the "film" within the
00154 //               lens.  This both establishes the units used by
00155 //               calls like set_focal_length(), and establishes the
00156 //               aspect ratio of the frame.
00157 //
00158 //               In a physical camera, the field of view of a lens is
00159 //               determined by the lens' focal length and by the size
00160 //               of the film area exposed by the lens.  For instance,
00161 //               a 35mm camera exposes a rectangle on the film about
00162 //               24mm x 36mm, which means a 50mm lens gives about a
00163 //               40-degree horizontal field of view.
00164 //
00165 //               In the virtual camera, you may set the film size to
00166 //               any units here, and specify a focal length in the
00167 //               same units to simulate the same effect.  Or, you may
00168 //               ignore this parameter, and specify the field of view
00169 //               and aspect ratio of the lens directly.
00170 ////////////////////////////////////////////////////////////////////
00171 void Lens::
00172 set_film_size(const LVecBase2f &film_size) {
00173   _film_size = film_size;
00174 
00175   // We can't specify all three of focal length, fov, and film size.
00176   // Throw out the oldest one.
00177   resequence_fov_triad(_film_size_seq, _focal_length_seq, _fov_seq);
00178 
00179   if (_fov_seq == 0) {
00180     // Throw out fov if it's oldest.
00181     adjust_user_flags(UF_hfov | UF_vfov | UF_aspect_ratio,
00182                       UF_film_width | UF_film_height);
00183   } else {
00184     // Otherwise, throw out focal length.
00185     nassertv(_focal_length_seq == 0);
00186     adjust_user_flags(UF_focal_length | UF_vfov | UF_aspect_ratio,
00187                       UF_film_width | UF_film_height);
00188   }
00189   adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_aspect_ratio,
00190                     CF_film_size);
00191 
00192   // Also, the user has implicitly specified an aspect ratio.  Make it
00193   // stick until the user tells us something different.
00194   compute_aspect_ratio();
00195   adjust_user_flags(0, UF_aspect_ratio);
00196 
00197   throw_change_event();
00198 }
00199 
00200 ////////////////////////////////////////////////////////////////////
00201 //     Function: Lens::get_film_size
00202 //       Access: Published
00203 //  Description: Returns the horizontal and vertical film size of
00204 //               the virtual film.  See set_film_size().
00205 ////////////////////////////////////////////////////////////////////
00206 const LVecBase2f &Lens::
00207 get_film_size() const {
00208   if ((_comp_flags & CF_film_size) == 0) {
00209     // We pretend this is a const method, even though it may call a
00210     // non-const method to recompute the internal values.  We can do
00211     // this because this is just compute-on-demand.
00212     ((Lens *)this)->compute_film_size();
00213   }
00214   return _film_size;
00215 }
00216 
00217 ////////////////////////////////////////////////////////////////////
00218 //     Function: Lens::set_focal_length
00219 //       Access: Published
00220 //  Description: Sets the focal length of the lens.  This may adjust
00221 //               the field-of-view correspondingly, and is an
00222 //               alternate way to specify field of view.
00223 //
00224 //               For certain kinds of lenses (e.g. OrthographicLens),
00225 //               the focal length has no meaning.
00226 ////////////////////////////////////////////////////////////////////
00227 void Lens::
00228 set_focal_length(float focal_length) {
00229   _focal_length = focal_length;
00230 
00231   // We can't specify all three of focal length, fov, and film size.
00232   // Throw out the oldest one.
00233   resequence_fov_triad(_focal_length_seq, _film_size_seq, _fov_seq);
00234 
00235   if (_film_size_seq == 0) {
00236     // Throw out film size if it's oldest.
00237     adjust_user_flags(UF_film_width | UF_film_height,
00238                       UF_focal_length);
00239   } else {
00240     // Otherwise, throw out the fov.
00241     nassertv(_fov_seq == 0);
00242     adjust_user_flags(UF_hfov | UF_vfov,
00243                       UF_focal_length);
00244   }
00245 
00246   adjust_comp_flags(CF_mat | CF_fov | CF_film_size,
00247                     CF_focal_length);
00248   throw_change_event();
00249 }
00250 
00251 ////////////////////////////////////////////////////////////////////
00252 //     Function: Lens::get_focal_length
00253 //       Access: Published
00254 //  Description: Returns the focal length of the lens.  This may have
00255 //               been set explicitly by a previous call to
00256 //               set_focal_length(), or it may be computed based on
00257 //               the lens' fov and film_size.  For certain kinds of
00258 //               lenses, the focal length has no meaning.
00259 ////////////////////////////////////////////////////////////////////
00260 float Lens::
00261 get_focal_length() const {
00262   if ((_comp_flags & CF_focal_length) == 0) {
00263     ((Lens *)this)->compute_focal_length();
00264   }
00265   return _focal_length;
00266 }
00267 
00268 ////////////////////////////////////////////////////////////////////
00269 //     Function: Lens::set_fov
00270 //       Access: Published
00271 //  Description: Sets the horizontal field of view of the lens without
00272 //               changing the aspect ratio.  The vertical field of
00273 //               view is adjusted to maintain the same aspect ratio.
00274 ////////////////////////////////////////////////////////////////////
00275 void Lens::
00276 set_fov(float hfov) {
00277   _fov[0] = hfov;
00278 
00279   // We can't specify all three of focal length, fov, and film size.
00280   // Throw out the oldest one.
00281   resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq);
00282 
00283   if (_focal_length_seq == 0) {
00284     // Throw out focal length if it's oldest.
00285     adjust_user_flags(UF_focal_length | UF_vfov,
00286                       UF_hfov);
00287   } else {
00288     // Otherwise, throw out film size.
00289     nassertv(_film_size_seq == 0);
00290     adjust_user_flags(UF_film_width | UF_film_height | UF_vfov,
00291                       UF_hfov);
00292   }
00293   adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_film_size,
00294                     0);
00295   // We leave CF_fov off of comp_flags, because we will still need to
00296   // recompute the vertical fov.  It's not exactly the same as hfov *
00297   // get_aspect_ratio().
00298   throw_change_event();
00299 }
00300 
00301 ////////////////////////////////////////////////////////////////////
00302 //     Function: Lens::set_fov
00303 //       Access: Published
00304 //  Description: Sets the field of view of the lens in both
00305 //               dimensions.  This establishes both the field of view
00306 //               and the aspect ratio of the lens.  This is one way to
00307 //               specify the field of view of a lens;
00308 //               set_focal_length() is another way.
00309 //
00310 //               For certain kinds of lenses (like OrthographicLens),
00311 //               the field of view has no meaning.
00312 ////////////////////////////////////////////////////////////////////
00313 void Lens::
00314 set_fov(const LVecBase2f &fov) {
00315   _fov = fov;
00316 
00317   // We can't specify all three of focal length, fov, and film size.
00318   // Throw out the oldest one.
00319   resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq);
00320 
00321   if (_focal_length_seq == 0) {
00322     // Throw out focal length if it's oldest.
00323     adjust_user_flags(UF_focal_length | UF_film_height | UF_aspect_ratio,
00324                       UF_hfov | UF_vfov);
00325   } else {
00326     // Otherwise, throw out film size.
00327     nassertv(_film_size_seq == 0);
00328     adjust_user_flags(UF_film_width | UF_film_height | UF_aspect_ratio,
00329                       UF_hfov | UF_vfov);
00330   }
00331   adjust_comp_flags(CF_mat | CF_focal_length | CF_film_size | CF_aspect_ratio,
00332                     CF_fov);
00333 
00334   // Also, the user has implicitly specified an aspect ratio.  Make it
00335   // stick until the user tells us something different.
00336   compute_aspect_ratio();
00337   adjust_user_flags(0, UF_aspect_ratio);
00338 
00339   throw_change_event();
00340 }
00341 
00342 ////////////////////////////////////////////////////////////////////
00343 //     Function: Lens::get_fov
00344 //       Access: Published
00345 //  Description: Returns the horizontal and vertical film size of
00346 //               the virtual film.  See set_fov().
00347 ////////////////////////////////////////////////////////////////////
00348 const LVecBase2f &Lens::
00349 get_fov() const {
00350   if ((_comp_flags & CF_fov) == 0) {
00351     ((Lens *)this)->compute_fov();
00352   }
00353   return _fov;
00354 }
00355                 
00356 
00357 ////////////////////////////////////////////////////////////////////
00358 //     Function: Lens::set_aspect_ratio
00359 //       Access: Published
00360 //  Description: Sets the aspect ratio of the lens.  This is the ratio
00361 //               of the height to the width of the generated image.
00362 //               Setting this overrides the two-parameter fov or film
00363 //               size setting.
00364 ////////////////////////////////////////////////////////////////////
00365 void Lens::
00366 set_aspect_ratio(float aspect_ratio) {
00367   _aspect_ratio = aspect_ratio;
00368   adjust_user_flags(UF_film_height | UF_vfov,
00369                     UF_aspect_ratio);
00370   adjust_comp_flags(CF_mat | CF_film_size | CF_fov,
00371                     CF_aspect_ratio);
00372   throw_change_event();
00373 }
00374 
00375 ////////////////////////////////////////////////////////////////////
00376 //     Function: Lens::get_aspect_ratio
00377 //       Access: Published
00378 //  Description: Returns the aspect ratio of the Lens.  This is
00379 //               determined based on the indicated film size; see
00380 //               set_film_size().
00381 ////////////////////////////////////////////////////////////////////
00382 float Lens::
00383 get_aspect_ratio() const {
00384   if ((_comp_flags & CF_aspect_ratio) == 0) {
00385     ((Lens *)this)->compute_aspect_ratio();
00386   }
00387   return _aspect_ratio;
00388 }
00389 
00390 ////////////////////////////////////////////////////////////////////
00391 //     Function: Lens::get_default_near
00392 //       Access: Published, Static
00393 //  Description: Returns the default near plane distance that will be
00394 //               assigned to each newly-created lens.  This is read
00395 //               from the Configrc file.
00396 ////////////////////////////////////////////////////////////////////
00397 float Lens::
00398 get_default_near() {
00399   return default_near;
00400 }
00401 
00402 ////////////////////////////////////////////////////////////////////
00403 //     Function: Lens::get_default_far
00404 //       Access: Published, Static
00405 //  Description: Returns the default far plane distance that will be
00406 //               assigned to each newly-created lens.  This is read
00407 //               from the Configrc file.
00408 ////////////////////////////////////////////////////////////////////
00409 float Lens::
00410 get_default_far() {
00411   return default_far;
00412 }
00413 
00414 ////////////////////////////////////////////////////////////////////
00415 //     Function: Lens::set_view_hpr
00416 //       Access: Published
00417 //  Description: Sets the direction in which the lens is facing.
00418 //               Normally, this is down the forward axis (usually the
00419 //               Y axis), but it may be rotated.  This is only one way
00420 //               of specifying the rotation; you may also specify an
00421 //               explicit vector in which to look, or you may give a
00422 //               complete transformation matrix.
00423 ////////////////////////////////////////////////////////////////////
00424 void Lens::
00425 set_view_hpr(const LVecBase3f &view_hpr) {
00426   _view_hpr = view_hpr;
00427   adjust_user_flags(UF_view_vector | UF_view_mat,
00428                     UF_view_hpr);
00429   adjust_comp_flags(CF_mat | CF_view_vector | CF_iod_offset,
00430                     CF_view_hpr);
00431   throw_change_event();
00432 }
00433 
00434 ////////////////////////////////////////////////////////////////////
00435 //     Function: Lens::get_view_hpr
00436 //       Access: Published
00437 //  Description: Returns the direction in which the lens is facing.
00438 ////////////////////////////////////////////////////////////////////
00439 const LVecBase3f &Lens::
00440 get_view_hpr() const {
00441   if ((_comp_flags & CF_view_hpr) == 0) {
00442     ((Lens *)this)->compute_view_hpr();
00443   }
00444   return _view_hpr;
00445 }
00446 
00447 ////////////////////////////////////////////////////////////////////
00448 //     Function: Lens::set_view_vector
00449 //       Access: Published
00450 //  Description: Specifies the direction in which the lens is facing
00451 //               by giving an axis to look along, and a perpendicular
00452 //               (or at least non-parallel) up axis.
00453 //
00454 //               See also set_view_hpr().
00455 ////////////////////////////////////////////////////////////////////
00456 void Lens::
00457 set_view_vector(const LVector3f &view_vector, const LVector3f &up_vector) {
00458   _view_vector = view_vector;
00459   _up_vector = up_vector;
00460   adjust_user_flags(UF_view_hpr | UF_view_mat,
00461                     UF_view_vector);
00462   adjust_comp_flags(CF_mat | CF_view_hpr | CF_iod_offset,
00463                     CF_view_vector);
00464   throw_change_event();
00465 }
00466 
00467 ////////////////////////////////////////////////////////////////////
00468 //     Function: Lens::get_view_vector
00469 //       Access: Published
00470 //  Description: Returns the axis along which the lens is facing.
00471 ////////////////////////////////////////////////////////////////////
00472 const LVector3f &Lens::
00473 get_view_vector() const {
00474   if ((_comp_flags & CF_view_vector) == 0) {
00475     ((Lens *)this)->compute_view_vector();
00476   }
00477   return _view_vector;
00478 }
00479 
00480 ////////////////////////////////////////////////////////////////////
00481 //     Function: Lens::get_up_vector
00482 //       Access: Published
00483 //  Description: Returns the axis perpendicular to the camera's view
00484 //               vector that indicates the "up" direction.
00485 ////////////////////////////////////////////////////////////////////
00486 const LVector3f &Lens::
00487 get_up_vector() const {
00488   if ((_comp_flags & CF_view_vector) == 0) {
00489     ((Lens *)this)->compute_view_vector();
00490   }
00491   return _up_vector;
00492 }
00493 
00494 ////////////////////////////////////////////////////////////////////
00495 //     Function: Lens::get_nodal_point
00496 //       Access: Published
00497 //  Description: Returns the center point of the lens: the point from
00498 //               which the lens is viewing.
00499 ////////////////////////////////////////////////////////////////////
00500 LPoint3f Lens::
00501 get_nodal_point() const {
00502   return get_view_mat().get_row3(3);
00503 }
00504 
00505 ////////////////////////////////////////////////////////////////////
00506 //     Function: Lens::set_iod_offset
00507 //       Access: Published
00508 //  Description: Sets the amount by which the lens is shifted to the
00509 //               right, perpendicular to its view vector and up
00510 //               vector.  This is normally used to shift one or both
00511 //               lens of a stereo camera to generate parallax.  You
00512 //               can also simply set a complete transformation matrix
00513 //               (via set_view_mat()) that includes an arbitrary
00514 //               translation.
00515 ////////////////////////////////////////////////////////////////////
00516 void Lens::
00517 set_iod_offset(float iod_offset) {
00518   _iod_offset = iod_offset;
00519   adjust_user_flags(UF_view_mat,
00520                     UF_iod_offset);
00521   adjust_comp_flags(CF_mat | CF_view_hpr | CF_view_vector,
00522                     CF_iod_offset);
00523   throw_change_event();
00524 }
00525 
00526 ////////////////////////////////////////////////////////////////////
00527 //     Function: Lens::get_iod_offset
00528 //       Access: Published
00529 //  Description: Returns the aspect ratio of the Lens.  This is
00530 //               determined based on the indicated film size; see
00531 //               set_film_size().
00532 ////////////////////////////////////////////////////////////////////
00533 float Lens::
00534 get_iod_offset() const {
00535   if ((_comp_flags & CF_iod_offset) == 0) {
00536     ((Lens *)this)->compute_iod_offset();
00537   }
00538   return _iod_offset;
00539 }
00540 
00541 ////////////////////////////////////////////////////////////////////
00542 //     Function: Lens::set_view_mat
00543 //       Access: Published
00544 //  Description: Sets an arbitrary transformation on the lens.  This
00545 //               replaces the individual transformation components
00546 //               like set_view_hpr() or set_iod_offset().
00547 //
00548 //               Setting a transformation here will have a slightly
00549 //               different effect than putting one on the LensNode
00550 //               that contains this lens.  In particular, lighting and
00551 //               other effects computations will still be performed on
00552 //               the lens in its untransformed (facing forward)
00553 //               position, but the actual projection matrix will be
00554 //               transformed by this matrix.
00555 ////////////////////////////////////////////////////////////////////
00556 void Lens::
00557 set_view_mat(const LMatrix4f &view_mat) {
00558   _lens_mat = view_mat;
00559   adjust_user_flags(UF_view_vector | UF_view_hpr | UF_iod_offset,
00560                     UF_view_mat);
00561   adjust_comp_flags(CF_lens_mat_inv | CF_view_hpr | CF_view_vector | CF_iod_offset,
00562                     CF_lens_mat);
00563   throw_change_event();
00564 }
00565 
00566 ////////////////////////////////////////////////////////////////////
00567 //     Function: Lens::get_view_mat
00568 //       Access: Published
00569 //  Description: Returns the direction in which the lens is facing.
00570 ////////////////////////////////////////////////////////////////////
00571 const LMatrix4f &Lens::
00572 get_view_mat() const {
00573   if ((_comp_flags & CF_lens_mat) == 0) {
00574     ((Lens *)this)->compute_lens_mat();
00575   }
00576   return _lens_mat;
00577 }
00578 
00579 ////////////////////////////////////////////////////////////////////
00580 //     Function: Lens::set_frustum_from_corners
00581 //       Access: Published
00582 //  Description: Sets up the lens to use the frustum defined by the
00583 //               four indicated points.  This is most useful for a
00584 //               PerspectiveLens, but it may be called for other kinds
00585 //               of lenses as well.
00586 //
00587 //               The frustum will be rooted at the origin (or offset
00588 //               by iod_offset, or by whatever translation might have
00589 //               been specified in a previous call to set_view_mat).
00590 //
00591 //               It is legal for the four points not to be arranged in
00592 //               a rectangle; if this is the case, the frustum will be
00593 //               fitted as tightly as possible to cover all four
00594 //               points.
00595 //
00596 //               The flags parameter contains the union of one or more
00597 //               of the following bits to control the behavior of this
00598 //               function:
00599 //
00600 //               FC_roll - If this is included, the camera may be
00601 //               rotated so that its up vector is perpendicular to the
00602 //               top line.  Otherwise, the standard up vector is used.
00603 //
00604 //               FC_camera_plane - This allows the camera plane to be
00605 //               adjusted to be as nearly perpendicular to the center
00606 //               of the frustum as possible.  Without this bit, the
00607 //               orientation camera plane is defined by position of
00608 //               the four points (which should all be coplanar).  With
00609 //               this bit, the camera plane is arbitarary, and may be
00610 //               chosen so that the four points do not themselves lie
00611 //               in the camera plane (but the points will still be
00612 //               within the frustum).
00613 //
00614 //               FC_off_axis - This allows the resulting frustum to be
00615 //               off-axis to get the tightest possible fit.  Without
00616 //               this bit, the viewing axis will be centered within
00617 //               the frustum, but there may be more wasted space along
00618 //               the edges.
00619 //
00620 //               FC_aspect_ratio - This allows the frustum to be
00621 //               scaled non-proportionately in the vertical and
00622 //               horizontal dimensions, if necessary, to get a tighter
00623 //               fit.  Without this bit, the current aspect ratio will
00624 //               be preserved.
00625 //
00626 //               FC_shear - This allows the frustum to be sheared, if
00627 //               necessary, to get the tightest possible fit.  This
00628 //               may result in a parallelogram-based frustum, which
00629 //               will give a slanted appearance to the rendered image.
00630 //               Without this bit, the frustum will be
00631 //               rectangle-based.
00632 //
00633 //               In general, if 0 is passed in as the value for flags,
00634 //               the generated frustum will be a loose fit but sane;
00635 //               if -1 is passed in, it will be a tighter fit and
00636 //               possibly screwy.
00637 ////////////////////////////////////////////////////////////////////
00638 void Lens::
00639 set_frustum_from_corners(const LVecBase3f &ul, const LVecBase3f &ur,
00640                          const LVecBase3f &ll, const LVecBase3f &lr,
00641                          int flags) {
00642   // We'll need to know the pre-existing eyepoint translation from the
00643   // center, so we can preserve it in the new frustum.  This is
00644   // usually just a shift along the x axis, if anything at all, for
00645   // the iod offset, but it could be an arbitrary vector.
00646   const LMatrix4f &lens_mat_inv = get_lens_mat_inv();
00647   LVector3f eye_offset;
00648   lens_mat_inv.get_row3(eye_offset, 3);
00649 
00650   // Now choose the viewing axis.  If FC_camera_plane is specified,
00651   // we'll pass it through the centroid for the best camera plane;
00652   // otherwise, it's perpendicular to the plane in which the points
00653   // lie.
00654   LVector3f view_vector;
00655   if ((flags & FC_camera_plane) != 0) {
00656     view_vector = (ul + ur + ll + lr) * 0.25f;
00657   } else {
00658     Planef plane(ll, ul, ur);
00659     view_vector = plane.get_normal();
00660   }
00661 
00662   // Now determine the up axis.  If FC_roll is specified, or if our
00663   // view vector is straight up, it is the vector perpendicular to
00664   // both the viewing axis and the top line.  Otherwise, it is the
00665   // standard up axis.
00666   LVector3f up_vector = LVector3f::up(_cs);
00667   if (view_vector == up_vector || ((flags & FC_roll) != 0)) {
00668     LVector3f top = ul - ur;
00669     up_vector = view_vector.cross(top);
00670   }
00671 
00672   // Now compute the matrix that applies this rotation.
00673   LMatrix4f rot_mat;
00674   look_at(rot_mat, view_vector, up_vector, CS_zup_right);
00675 
00676   // And invert it.
00677   LMatrix4f inv_rot_mat;
00678   inv_rot_mat.invert_affine_from(rot_mat);
00679 
00680   // Use that inverse matrix to convert the four corners to a local
00681   // coordinate system, looking down the Y axis.
00682   LPoint3f cul = inv_rot_mat.xform_point(ul);
00683   LPoint3f cur = inv_rot_mat.xform_point(ur);
00684   LPoint3f cll = inv_rot_mat.xform_point(ll);
00685   LPoint3f clr = inv_rot_mat.xform_point(lr);
00686 
00687   // Project all points into the Y == 1 plane, so we can do 2-d
00688   // manipulation on them.
00689   cul /= cul[1];
00690   cur /= cur[1];
00691   cll /= cll[1];
00692   clr /= clr[1];
00693 
00694   LMatrix4f shear_mat = LMatrix4f::ident_mat();
00695   LMatrix4f inv_shear_mat = LMatrix4f::ident_mat();
00696 
00697   // Now, if we're allowed to shear the frustum, do so.
00698   if ((flags & FC_shear) != 0) {
00699     build_shear_mat(shear_mat, cul, cur, cll, clr);
00700     inv_shear_mat.invert_from(shear_mat);
00701   } 
00702 
00703   // Now build the complete view matrix.
00704   LMatrix4f inv_view_mat =
00705     inv_rot_mat *
00706     inv_shear_mat;
00707 
00708   // And reapply the eye offset to this matrix.
00709   inv_view_mat.set_row(3, eye_offset);
00710 
00711   LMatrix4f view_mat;
00712   view_mat.invert_from(inv_view_mat);
00713   set_view_mat(view_mat);
00714 
00715   LPoint3f ful = inv_view_mat.xform_point(ul);
00716   LPoint3f fur = inv_view_mat.xform_point(ur);
00717   LPoint3f fll = inv_view_mat.xform_point(ll);
00718   LPoint3f flr = inv_view_mat.xform_point(lr);
00719 
00720   // Normalize *these* points into the y == 1 plane.
00721   ful /= ful[1];
00722   fur /= fur[1];
00723   fll /= fll[1];
00724   flr /= flr[1];
00725 
00726   // Determine the minimum field of view necesary to cover all four
00727   // transformed points.
00728   float min_x = min(min(ful[0], fur[0]), min(fll[0], flr[0]));
00729   float max_x = max(max(ful[0], fur[0]), max(fll[0], flr[0]));
00730   float min_z = min(min(ful[2], fur[2]), min(fll[2], flr[2]));
00731   float max_z = max(max(ful[2], fur[2]), max(fll[2], flr[2]));
00732 
00733   float x_spread, x_center, z_spread, z_center;
00734 
00735   if ((flags & FC_off_axis) != 0) {
00736     // If we're allowed to make an off-axis projection, then pick the
00737     // best center.
00738     x_center = (max_x + min_x) * 0.5f;
00739     z_center = (max_z + min_z) * 0.5f;
00740     x_spread = x_center - min_x;
00741     z_spread = z_center - min_z;
00742   } else {
00743     // Otherwise, the center must be (0, 0).
00744     x_center = 0.0f;
00745     z_center = 0.0f;
00746     x_spread = max(cabs(max_x), cabs(min_x));
00747     z_spread = max(cabs(max_z), cabs(min_z));
00748   }
00749 
00750   float aspect_ratio = get_aspect_ratio();
00751   if ((flags & FC_aspect_ratio) == 0) {
00752     // If we must preserve the aspect ratio, then the x and z spreads
00753     // must be adjusted to match.
00754     if (x_spread < z_spread * aspect_ratio) {
00755       // x_spread is too small.
00756       x_spread = z_spread * aspect_ratio;
00757     } else if (z_spread < x_spread / aspect_ratio) {
00758       // z_spread is too small.
00759       z_spread = x_spread / aspect_ratio;
00760     }
00761   }
00762 
00763   float hfov = rad_2_deg(catan(x_spread)) * 2.0f;
00764   float vfov = rad_2_deg(catan(z_spread)) * 2.0f;
00765 
00766   set_fov(hfov, vfov);
00767 
00768   if ((flags & FC_aspect_ratio) == 0) {
00769     // If we must preserve the aspect ratio, store it one more time.
00770     // This is mainly in case we have a non-perspective lens with a
00771     // funny relationship between fov and aspect ratio.
00772     set_aspect_ratio(aspect_ratio);
00773   }
00774 
00775   const LVecBase2f &film_size = get_film_size();
00776   set_film_offset(film_size[0] * x_center / (x_spread * 2.0f),
00777                   film_size[1] * z_center / (z_spread * 2.0f));
00778 }
00779 
00780 
00781 ////////////////////////////////////////////////////////////////////
00782 //     Function: Lens::recompute_all
00783 //       Access: Published
00784 //  Description: Forces all internal parameters of the Lens to be
00785 //               recomputed.  Normally, this should never need to be
00786 //               called; it is provided only to assist in debugging.
00787 ////////////////////////////////////////////////////////////////////
00788 void Lens::
00789 recompute_all() {
00790   _comp_flags = 0;
00791 }
00792 
00793 ////////////////////////////////////////////////////////////////////
00794 //     Function: Lens::is_linear
00795 //       Access: Published, Virtual
00796 //  Description: Returns true if the lens represents a linear
00797 //               projection (e.g. PerspectiveLens, OrthographicLens),
00798 //               and therefore there is a valid matrix returned by
00799 //               get_projection_mat(), or false otherwise.
00800 ////////////////////////////////////////////////////////////////////
00801 bool Lens::
00802 is_linear() const {
00803   return false;
00804 }
00805 
00806 ////////////////////////////////////////////////////////////////////
00807 //     Function: Lens::make_geometry
00808 //       Access: Published, Virtual
00809 //  Description: Allocates and returns a new Geom that can be rendered
00810 //               to show a visible representation of the frustum used
00811 //               for this kind of lens, if it makes sense to do
00812 //               so.  If a visible representation cannot be created,
00813 //               returns NULL.
00814 ////////////////////////////////////////////////////////////////////
00815 PT(Geom) Lens::
00816 make_geometry() {
00817   // The default behavior for make_geometry() will be to draw a
00818   // hexahedron around the eight vertices of the frustum.  If the lens
00819   // is non-linear, the hexahedron will be curved; in this case, we'll
00820   // subdivide the lines into several segments to get an approximation
00821   // of the curve.
00822 
00823   // First, define all the points we'll use in this Geom.  That's one
00824   // point at each corner of the near and far planes (and possibly
00825   // more points along the edges).
00826   int num_segments = define_geom_coords();
00827   if (num_segments == 0) {
00828     // Can't do a frustum.
00829     _geom_coords.clear();
00830     return (Geom *)NULL;
00831   }
00832 
00833   // Now string together the line segments.
00834   PTA_ushort vindex;
00835   PTA_int lengths;
00836   PTA_Colorf colors;
00837 
00838   int num_points = num_segments * 4;
00839 
00840   // Draw a frame around the near plane.
00841   int i;
00842   for (i = 0; i < num_points; i++) {
00843     vindex.push_back(i * num_segments * 2);
00844   }
00845   vindex.push_back(0);
00846   lengths.push_back(num_points + 1);
00847 
00848   // Draw a frame around the far plane.
00849   for (i = 0; i < num_points; i++) {
00850     vindex.push_back(i * num_segments * 2 + 1);
00851   }
00852   vindex.push_back(1);
00853   lengths.push_back(num_points + 1);
00854 
00855   // Draw connecting lines at the corners.
00856   vindex.push_back(0);
00857   vindex.push_back(1);
00858   lengths.push_back(2);
00859 
00860   vindex.push_back(num_segments * 2 + 0);
00861   vindex.push_back(num_segments * 2+ 1);
00862   lengths.push_back(2);
00863 
00864   vindex.push_back(num_segments * 4 + 0);
00865   vindex.push_back(num_segments * 4 + 1);
00866   lengths.push_back(2);
00867 
00868   vindex.push_back(num_segments * 6 + 0);
00869   vindex.push_back(num_segments * 6 + 1);
00870   lengths.push_back(2);
00871 
00872   // And one more line for the viewing axis.
00873   vindex.push_back(num_segments * 8 + 0);
00874   vindex.push_back(num_segments * 8 + 1);
00875   lengths.push_back(2);
00876 
00877   // We just specify overall color.
00878   colors.push_back(Colorf(1.0f, 1.0f, 1.0f, 1.0f));
00879 
00880   GeomLinestrip *gline = new GeomLinestrip;
00881   gline->set_coords(_geom_coords, vindex);
00882   gline->set_colors(colors, G_OVERALL);
00883   gline->set_lengths(lengths);
00884   gline->set_num_prims(lengths.size());
00885 
00886   return gline;
00887 }
00888 
00889 ////////////////////////////////////////////////////////////////////
00890 //     Function: Lens::make_bounds
00891 //       Access: Published, Virtual
00892 //  Description: Allocates and returns a new BoundingVolume that
00893 //               encloses the frustum used for this kind of
00894 //               lens, if possible.  If a suitable bounding
00895 //               volume cannot be created, returns NULL.
00896 ////////////////////////////////////////////////////////////////////
00897 PT(BoundingVolume) Lens::
00898 make_bounds() const {
00899   // The default bounding volume is a hexahedron based on the eight
00900   // corners of the frustum.
00901   LPoint3f fll, flr, ful, fur;
00902   LPoint3f nll, nlr, nul, nur;
00903   LPoint2f corner;
00904 
00905   corner[0] = -1.0f; corner[1] = 1.0f;
00906 
00907   // Upper left.
00908   if (!extrude(corner, nul, ful)) {
00909     return (BoundingVolume *)NULL;
00910   }
00911 
00912   corner[0] = 1.0f; corner[1] = 1.0f;
00913 
00914   // Upper right.
00915   if (!extrude(corner, nur, fur)) {
00916     return (BoundingVolume *)NULL;
00917   }
00918 
00919   corner[0] = 1.0f; corner[1] = -1.0f;
00920 
00921   // Lower right.
00922   if (!extrude(corner, nlr, flr)) {
00923     return (BoundingVolume *)NULL;
00924   }
00925 
00926   corner[0] = -1.0f; corner[1] = -1.0f;
00927 
00928   // Lower left.
00929   if (!extrude(corner, nll, fll)) {
00930     return (BoundingVolume *)NULL;
00931   }
00932 
00933   return new BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul);
00934 }
00935 
00936 ////////////////////////////////////////////////////////////////////
00937 //     Function: Lens::get_projection_mat
00938 //       Access: Published
00939 //  Description: Returns the complete transformation matrix from a 3-d
00940 //               point in space to a point on the film, if such a
00941 //               matrix exists, or the identity matrix if the lens is
00942 //               nonlinear.
00943 ////////////////////////////////////////////////////////////////////
00944 const LMatrix4f &Lens::
00945 get_projection_mat() const {
00946   if ((_comp_flags & CF_projection_mat) == 0) {
00947     ((Lens *)this)->compute_projection_mat();
00948   }
00949   return _projection_mat;
00950 }
00951 
00952 ////////////////////////////////////////////////////////////////////
00953 //     Function: Lens::get_projection_mat_inv
00954 //       Access: Public
00955 //  Description: Returns the matrix that transforms from a 2-d point
00956 //               on the film to a 3-d vector in space, if such a
00957 //               matrix exists.
00958 ////////////////////////////////////////////////////////////////////
00959 const LMatrix4f &Lens::
00960 get_projection_mat_inv() const {
00961   if ((_comp_flags & CF_projection_mat_inv) == 0) {
00962     Lens *non_const = (Lens *)this;
00963     const LMatrix4f &projection_mat = get_projection_mat();
00964     non_const->_projection_mat_inv.invert_from(projection_mat);
00965     non_const->adjust_comp_flags(0, CF_projection_mat_inv);
00966   }
00967   return _projection_mat_inv;
00968 }
00969 
00970 ////////////////////////////////////////////////////////////////////
00971 //     Function: Lens::output
00972 //       Access: Published, Virtual
00973 //  Description: 
00974 ////////////////////////////////////////////////////////////////////
00975 void Lens::
00976 output(ostream &out) const {
00977   out << get_type();
00978 }
00979 
00980 ////////////////////////////////////////////////////////////////////
00981 //     Function: Lens::write
00982 //       Access: Published, Virtual
00983 //  Description: 
00984 ////////////////////////////////////////////////////////////////////
00985 void Lens::
00986 write(ostream &out, int indent_level) const {
00987   indent(out, indent_level) << get_type() << " fov = " << get_fov() << "\n";
00988 }
00989 
00990 ////////////////////////////////////////////////////////////////////
00991 //     Function: Lens::throw_change_event
00992 //       Access: Protected
00993 //  Description: Throws the event associated with changing properties
00994 //               on this Lens, if any.
00995 ////////////////////////////////////////////////////////////////////
00996 void Lens::
00997 throw_change_event() {
00998   ++_last_change;
00999 
01000   if (!_change_event.empty()) {
01001     throw_event(_change_event, this);
01002   }
01003 
01004   // Also update the _geom_coords, if it is in use.
01005   if (!_geom_coords.is_null()) {
01006     if (_geom_coords.get_ref_count() == 1) {
01007       // No one's using the array any more (other than us), so release
01008       // it.
01009       _geom_coords.clear();
01010     } else {
01011       // Someone else still has a handle to the array, so recompute
01012       // it.
01013       define_geom_coords();
01014     }
01015   }
01016 }
01017 
01018 ////////////////////////////////////////////////////////////////////
01019 //     Function: Lens::get_film_mat
01020 //       Access: Protected
01021 //  Description: Returns the matrix that transforms from a point
01022 //               behind the lens to a point on the film.
01023 ////////////////////////////////////////////////////////////////////
01024 const LMatrix4f &Lens::
01025 get_film_mat() const {
01026   if ((_comp_flags & CF_film_mat) == 0) {
01027     ((Lens *)this)->compute_film_mat();
01028   }
01029   return _film_mat;
01030 }
01031 
01032 ////////////////////////////////////////////////////////////////////
01033 //     Function: Lens::get_film_mat_inv
01034 //       Access: Protected
01035 //  Description: Returns the matrix that transforms from a point on
01036 //               the film to a point behind the lens.
01037 ////////////////////////////////////////////////////////////////////
01038 const LMatrix4f &Lens::
01039 get_film_mat_inv() const {
01040   if ((_comp_flags & CF_film_mat_inv) == 0) {
01041     Lens *non_const = (Lens *)this;
01042     const LMatrix4f &film_mat = get_film_mat();
01043     non_const->_film_mat_inv.invert_from(film_mat);
01044     non_const->adjust_comp_flags(0, CF_film_mat_inv);
01045   }
01046   return _film_mat_inv;
01047 }
01048 
01049 ////////////////////////////////////////////////////////////////////
01050 //     Function: Lens::get_lens_mat
01051 //       Access: Protected
01052 //  Description: Returns the matrix that transforms from a point
01053 //               in front of the lens to a point in space.
01054 ////////////////////////////////////////////////////////////////////
01055 const LMatrix4f &Lens::
01056 get_lens_mat() const {
01057   if ((_comp_flags & CF_lens_mat) == 0) {
01058     ((Lens *)this)->compute_lens_mat();
01059   }
01060   return _lens_mat;
01061 }
01062 
01063 ////////////////////////////////////////////////////////////////////
01064 //     Function: Lens::get_lens_mat_inv
01065 //       Access: Protected
01066 //  Description: Returns the matrix that transforms from a point in
01067 //               space to a point in front of the lens.
01068 ////////////////////////////////////////////////////////////////////
01069 const LMatrix4f &Lens::
01070 get_lens_mat_inv() const {
01071   if ((_comp_flags & CF_lens_mat_inv) == 0) {
01072     Lens *non_const = (Lens *)this;
01073     const LMatrix4f &lens_mat = get_lens_mat();
01074     non_const->_lens_mat_inv.invert_from(lens_mat);
01075     non_const->adjust_comp_flags(0, CF_lens_mat_inv);
01076   }
01077   return _lens_mat_inv;
01078 }
01079 
01080 ////////////////////////////////////////////////////////////////////
01081 //     Function: Lens::extrude_impl
01082 //       Access: Protected, Virtual
01083 //  Description: Given a 2-d point in the range (-1,1) in both
01084 //               dimensions, where (0,0) is the center of the
01085 //               lens and (-1,-1) is the lower-left corner,
01086 //               compute the corresponding vector in space that maps
01087 //               to this point, if such a vector can be determined.
01088 //               The vector is returned by indicating the points on
01089 //               the near plane and far plane that both map to the
01090 //               indicated 2-d point.
01091 //
01092 //               The z coordinate of the 2-d point is ignored.
01093 //
01094 //               Returns true if the vector is defined, or false
01095 //               otherwise.
01096 ////////////////////////////////////////////////////////////////////
01097 bool Lens::
01098 extrude_impl(const LPoint3f &point2d, LPoint3f &near_point, LPoint3f &far_point) const {
01099   const LMatrix4f &projection_mat_inv = get_projection_mat_inv();
01100   {
01101     LVecBase4f full(point2d[0], point2d[1], -1.0f, 1.0f);
01102     full = projection_mat_inv.xform(full);
01103     if (full[3] == 0.0f) {
01104       return false;
01105     }
01106 
01107     float recip_full3 = 1.0f/full[3];
01108     near_point.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
01109   }
01110   {
01111     LVecBase4f full(point2d[0], point2d[1], 1.0f, 1.0f);
01112     full = projection_mat_inv.xform(full);
01113     if (full[3] == 0.0f) {
01114       return false;
01115     }
01116     float recip_full3 = 1.0f/full[3];
01117     far_point.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
01118   }
01119   return true;
01120 }
01121 
01122 ////////////////////////////////////////////////////////////////////
01123 //     Function: Lens::project_impl
01124 //       Access: Protected, Virtual
01125 //  Description: Given a 3-d point in space, determine the 2-d point
01126 //               this maps to, in the range (-1,1) in both dimensions,
01127 //               where (0,0) is the center of the lens and
01128 //               (-1,-1) is the lower-left corner.
01129 //
01130 //               The z coordinate will also be set to a value in the
01131 //               range (-1, 1), where 1 represents a point on the near
01132 //               plane, and -1 represents a point on the far plane.
01133 //
01134 //               Returns true if the 3-d point is in front of the lens
01135 //               and within the viewing frustum (in which case point2d
01136 //               is filled in), or false otherwise (in which case
01137 //               point2d will be filled in with something, which may
01138 //               or may not be meaningful).
01139 ////////////////////////////////////////////////////////////////////
01140 bool Lens::
01141 project_impl(const LPoint3f &point3d, LPoint3f &point2d) const {
01142   const LMatrix4f &projection_mat = get_projection_mat();
01143   LVecBase4f full(point3d[0], point3d[1], point3d[2], 1.0f);
01144   full = projection_mat.xform(full);
01145   if (full[3] == 0.0f) {
01146     point2d.set(0.0f, 0.0f, 0.0f);
01147     return false;
01148   }
01149   float recip_full3 = 1.0f/full[3];
01150   point2d.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
01151   return
01152     (full[3] > 0.0f) &&
01153     (point2d[0] >= -1.0f) && (point2d[0] <= 1.0f) && 
01154     (point2d[1] >= -1.0f) && (point2d[1] <= 1.0f);
01155 }
01156 
01157 ////////////////////////////////////////////////////////////////////
01158 //     Function: Lens::compute_film_size
01159 //       Access: Protected, Virtual
01160 //  Description: Computes the size and shape of the film behind the
01161 //               camera, based on the aspect ratio and fov.
01162 ////////////////////////////////////////////////////////////////////
01163 void Lens::
01164 compute_film_size() {
01165   if ((_user_flags & UF_film_width) == 0) {
01166     if ((_user_flags & (UF_hfov | UF_focal_length)) == (UF_hfov | UF_focal_length)) {
01167       _film_size[0] = fov_to_film(_fov[0], _focal_length, true);
01168     } else {
01169       _film_size[0] = 1.0f;
01170     }
01171   }
01172 
01173   if ((_user_flags & UF_film_height) == 0) {
01174     if ((_user_flags & (UF_vfov | UF_focal_length)) == (UF_vfov | UF_focal_length)) {
01175       _film_size[1] = fov_to_film(_fov[1], _focal_length, false);
01176 
01177     } else if ((_user_flags & (UF_hfov | UF_vfov)) == (UF_hfov | UF_vfov)) {
01178       // If we don't have a focal length, but we have an explicit vfov
01179       // and hfov, we can infer the focal length is whatever makes the
01180       // film width, above, be what it is.
01181       if ((_comp_flags & CF_focal_length) == 0) {
01182         _focal_length = fov_to_focal_length(_fov[0], _film_size[0], true);
01183         adjust_comp_flags(0, CF_focal_length);
01184       }
01185       _film_size[1] = fov_to_film(_fov[1], _focal_length, false);
01186 
01187     } else if ((_user_flags & UF_aspect_ratio) != 0) {
01188       _film_size[1] = _film_size[0] / _aspect_ratio;
01189 
01190     } else {
01191       // Default is an aspect ratio of 1.
01192       _film_size[1] = _film_size[0];
01193     }
01194   }
01195 
01196   adjust_comp_flags(0, CF_film_size);
01197 }
01198 
01199 ////////////////////////////////////////////////////////////////////
01200 //     Function: Lens::compute_focal_length
01201 //       Access: Protected, Virtual
01202 //  Description: Computes the focal length of the lens, based on the
01203 //               fov and film size.  This is based on the horizontal
01204 //               dimension.
01205 ////////////////////////////////////////////////////////////////////
01206 void Lens::
01207 compute_focal_length() {
01208   if ((_user_flags & UF_focal_length) == 0) {
01209     const LVecBase2f &film_size = get_film_size();
01210     const LVecBase2f &fov = get_fov();
01211     _focal_length = fov_to_focal_length(fov[0], film_size[0], true);
01212   }
01213 
01214   adjust_comp_flags(0, CF_focal_length);
01215 }
01216 
01217 ////////////////////////////////////////////////////////////////////
01218 //     Function: Lens::compute_fov
01219 //       Access: Protected, Virtual
01220 //  Description: Computes the field of view of the lens, based on the
01221 //               film size and focal length.
01222 ////////////////////////////////////////////////////////////////////
01223 void Lens::
01224 compute_fov() {
01225   const LVecBase2f &film_size = get_film_size();
01226 
01227   if ((_user_flags & UF_hfov) == 0) {
01228     if ((_user_flags & UF_focal_length) != 0) {
01229       _fov[0] = film_to_fov(film_size[0], _focal_length, true);
01230     } else {
01231       _fov[0] = _default_fov;
01232     }
01233   }
01234 
01235   if ((_user_flags & UF_vfov) == 0) {
01236     if ((_user_flags & UF_focal_length) == 0 &&
01237         (_comp_flags & CF_focal_length) == 0) {
01238       // If we don't have an explicit focal length, we can infer it
01239       // from the above.
01240       _focal_length = fov_to_focal_length(_fov[0], film_size[0], true);
01241       adjust_comp_flags(0, CF_focal_length);
01242     }
01243     _fov[1] = film_to_fov(film_size[1], _focal_length, false);
01244   }
01245 
01246   adjust_comp_flags(0, CF_fov);
01247 }
01248 
01249 ////////////////////////////////////////////////////////////////////
01250 //     Function: Lens::compute_aspect_ratio
01251 //       Access: Protected, Virtual
01252 //  Description: Computes the aspect ratio of the film rectangle, as a
01253 //               ratio of width to height.
01254 ////////////////////////////////////////////////////////////////////
01255 void Lens::
01256 compute_aspect_ratio() {
01257   if ((_user_flags & UF_aspect_ratio) == 0) {
01258     const LVecBase2f &film_size = get_film_size();
01259     if (film_size[1] == 0.0f) {
01260       _aspect_ratio = 1.0f;
01261     } else {
01262       _aspect_ratio = film_size[0] / film_size[1];
01263     }
01264     adjust_comp_flags(0, CF_aspect_ratio);
01265   }
01266 }
01267 
01268 ////////////////////////////////////////////////////////////////////
01269 //     Function: Lens::compute_view_hpr
01270 //       Access: Protected, Virtual
01271 //  Description: Computes the Euler angles representing the lens'
01272 //               rotation.
01273 ////////////////////////////////////////////////////////////////////
01274 void Lens::
01275 compute_view_hpr() {
01276   if ((_user_flags & UF_view_hpr) == 0) {
01277     const LMatrix4f &view_mat = get_view_mat();
01278     LVecBase3f scale, translate;
01279     decompose_matrix(view_mat, scale, _view_hpr, translate, _cs);
01280   }
01281   adjust_comp_flags(0, CF_view_hpr);
01282 }
01283 
01284 ////////////////////////////////////////////////////////////////////
01285 //     Function: Lens::compute_view_vector
01286 //       Access: Protected, Virtual
01287 //  Description: Computes the view vector and up vector for the lens.
01288 ////////////////////////////////////////////////////////////////////
01289 void Lens::
01290 compute_view_vector() {
01291   if ((_user_flags & UF_view_vector) == 0) {
01292     const LMatrix4f &view_mat = get_view_mat();
01293     _view_vector = LVector3f::forward(_cs) * view_mat;
01294     _up_vector = LVector3f::up(_cs) * view_mat;
01295   }
01296   adjust_comp_flags(0, CF_view_vector);
01297 }
01298 
01299 ////////////////////////////////////////////////////////////////////
01300 //     Function: Lens::compute_iod_offset
01301 //       Access: Protected, Virtual
01302 //  Description: Computes the IOD offset: the translation along the
01303 //               "right" axis.
01304 ////////////////////////////////////////////////////////////////////
01305 void Lens::
01306 compute_iod_offset() {
01307   if ((_user_flags & UF_iod_offset) == 0) {
01308     const LMatrix4f &lens_mat_inv = get_lens_mat_inv();
01309     LVector3f translate;
01310     lens_mat_inv.get_row3(translate, 3);
01311     _iod_offset = -translate.dot(LVector3f::right(_cs));
01312   }
01313   adjust_comp_flags(0, CF_iod_offset);
01314 }
01315 
01316 ////////////////////////////////////////////////////////////////////
01317 //     Function: Lens::compute_projection_mat
01318 //       Access: Protected, Virtual
01319 //  Description: Computes the complete transformation matrix from 3-d
01320 //               point to 2-d point, if the lens is linear.
01321 ////////////////////////////////////////////////////////////////////
01322 void Lens::
01323 compute_projection_mat() {
01324   _projection_mat = LMatrix4f::ident_mat();
01325   _projection_mat_inv = _projection_mat;
01326   adjust_comp_flags(0, CF_projection_mat | CF_projection_mat_inv);
01327 }
01328 
01329 ////////////////////////////////////////////////////////////////////
01330 //     Function: Lens::compute_film_mat
01331 //       Access: Protected, Virtual
01332 //  Description: Computes the matrix that transforms from a point
01333 //               behind the lens to a point on the film.
01334 ////////////////////////////////////////////////////////////////////
01335 void Lens::
01336 compute_film_mat() {
01337   // The lens will return a point in the range [-film_size/2,
01338   // film_size/2] in each dimension.  Convert this to [-1, 1], and
01339   // also apply the offset.
01340 
01341   // We declare these two as local variables, instead of references,
01342   // to work around a VC7 compiler bug.
01343   LVecBase2f film_size = get_film_size();
01344   LVector2f film_offset = get_film_offset();
01345 
01346   /* this line triggers a VC7 opt bug, so explicitly set matrix below instead
01347   _film_mat =
01348     LMatrix4f::translate_mat(-film_offset[0], -film_offset[1], 0.0f) *
01349     LMatrix4f::scale_mat(2.0f / film_size[0], 2.0f / film_size[1], 1.0f);
01350    */ 
01351 
01352   float scale_x = 2.0f / film_size[0];
01353   float scale_y = 2.0f / film_size[1];
01354   _film_mat.set(scale_x,      0.0f,   0.0f,  0.0f,
01355                    0.0f,   scale_y,   0.0f,  0.0f,
01356                    0.0f,      0.0f,   1.0f,  0.0f,
01357         -film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f,  1.0f);
01358 
01359   adjust_comp_flags(CF_film_mat_inv,
01360                     CF_film_mat);
01361 }
01362 
01363 ////////////////////////////////////////////////////////////////////
01364 //     Function: Lens::compute_lens_mat
01365 //       Access: Protected, Virtual
01366 //  Description: Computes the matrix that transforms from a point
01367 //               in front of the lens to a point in space.
01368 ////////////////////////////////////////////////////////////////////
01369 void Lens::
01370 compute_lens_mat() {
01371   if ((_user_flags & UF_view_mat) == 0) {
01372     if ((_user_flags & UF_view_hpr) != 0) {
01373       compose_matrix(_lens_mat,
01374                      LVecBase3f(1.0f, 1.0f, 1.0f), _view_hpr,
01375                      LVecBase3f(0.0f, 0.0f, 0.0f), _cs);
01376 
01377     } else if ((_user_flags & UF_view_vector) != 0) {
01378       look_at(_lens_mat, _view_vector, _up_vector, _cs);
01379 
01380     } else {
01381       _lens_mat = LMatrix4f::ident_mat();
01382     }
01383 
01384     if ((_user_flags & UF_iod_offset) != 0) {
01385       LVector3f iod_vector = _iod_offset * LVector3f::right(_cs);
01386       _lens_mat = LMatrix4f::translate_mat(iod_vector) * _lens_mat;
01387     }
01388   }
01389   adjust_comp_flags(CF_lens_mat_inv,
01390                     CF_lens_mat);
01391 }
01392 
01393 ////////////////////////////////////////////////////////////////////
01394 //     Function: Lens::fov_to_film
01395 //       Access: Protected, Virtual
01396 //  Description: Given a field of view in degrees and a focal length,
01397 //               compute the corresponding width (or height) on the
01398 //               film.  If horiz is true, this is in the horizontal
01399 //               direction; otherwise, it is in the vertical direction
01400 //               (some lenses behave differently in each direction).
01401 ////////////////////////////////////////////////////////////////////
01402 float Lens::
01403 fov_to_film(float, float, bool) const {
01404   return 1.0f;
01405 }
01406 
01407 ////////////////////////////////////////////////////////////////////
01408 //     Function: Lens::fov_to_focal_length
01409 //       Access: Protected, Virtual
01410 //  Description: Given a field of view in degrees and a width (or
01411 //               height) on the film, compute the focal length of the
01412 //               lens.  If horiz is true, this is in the horizontal
01413 //               direction; otherwise, it is in the vertical direction
01414 //               (some lenses behave differently in each direction).
01415 ////////////////////////////////////////////////////////////////////
01416 float Lens::
01417 fov_to_focal_length(float, float, bool) const {
01418   return 1.0f;
01419 }
01420 
01421 ////////////////////////////////////////////////////////////////////
01422 //     Function: Lens::film_to_fov
01423 //       Access: Protected, Virtual
01424 //  Description: Given a width (or height) on the film and a focal
01425 //               length, compute the field of view in degrees.  If
01426 //               horiz is true, this is in the horizontal direction;
01427 //               otherwise, it is in the vertical direction (some
01428 //               lenses behave differently in each direction).
01429 ////////////////////////////////////////////////////////////////////
01430 float Lens::
01431 film_to_fov(float, float, bool) const {
01432   return _default_fov;
01433 }
01434 
01435 ////////////////////////////////////////////////////////////////////
01436 //     Function: Lens::resequence_fov_triad
01437 //       Access: Private, Static
01438 //  Description: Called whenever the user changes one of the three FOV
01439 //               parameters: fov, focal length, or film size.  This
01440 //               rearranges the three sequence numbers so the newest
01441 //               parameter has value 2, and the older parameters are
01442 //               kept in sequence order.
01443 //
01444 //               This is used to determine which two parameters of the
01445 //               three are the most recently changed, and conversely,
01446 //               which one the user has *not* changed recently.  It is
01447 //               this third value which should be discarded.
01448 ////////////////////////////////////////////////////////////////////
01449 void Lens::
01450 resequence_fov_triad(char &newest, char &older_a, char &older_b) {
01451   switch (newest) {
01452   case 0:
01453     newest = 2;
01454     older_a--;
01455     older_b--;
01456     nassertv(older_a + older_b == 1);
01457     return;
01458 
01459   case 1:
01460     newest = 2;
01461     if (older_a == 2) {
01462       nassertv(older_b == 0);
01463       older_a = 1;
01464     } else {
01465       nassertv(older_a == 0 && older_b == 2);
01466       older_b = 1;
01467     }
01468     return;
01469 
01470   case 2:
01471     nassertv(older_a + older_b == 1);
01472     return;
01473 
01474   default:
01475     gobj_cat.error()
01476       << "Invalid fov sequence numbers in lens: " << newest << ", " << older_a
01477       << ", " << older_b << "\n";
01478     nassertv(false);
01479     return;
01480   }
01481 }
01482 
01483 ////////////////////////////////////////////////////////////////////
01484 //     Function: Lens::define_geom_coords
01485 //       Access: Private
01486 //  Description: Adjusts (or defines for the first time) all the
01487 //               vertices in the _geom_coords array to match the
01488 //               properties of the lens.  This will update the visual
01489 //               representation of the lens's frustum to match the
01490 //               changing parameters.  Returns the number of line
01491 //               segments per edge.
01492 ////////////////////////////////////////////////////////////////////
01493 int Lens::
01494 define_geom_coords() {
01495   int num_segments = 1;
01496   if (!is_linear()) {
01497     num_segments = 10;
01498   }
01499 
01500   PTA_Vertexf coords;
01501   LPoint3f near_point, far_point;
01502   for (int si = 0; si < num_segments; si++) {
01503     float t = 2.0f * (float)si / (float)num_segments;
01504 
01505     // Upper left, top edge.
01506     LPoint2f p1(-1.0f + t, 1.0f);
01507     if (!extrude(p1, near_point, far_point)) {
01508       // Hey, this point is off the lens!  Can't do a frustum.
01509       return 0;
01510     }
01511     coords.push_back(near_point);
01512     coords.push_back(far_point);
01513 
01514     // Upper right, right edge.
01515     LPoint2f p2(1.0f, 1.0f - t);
01516     if (!extrude(p2, near_point, far_point)) {
01517       // Hey, this point is off the lens!  Can't do a frustum.
01518       return 0;
01519     }
01520     coords.push_back(near_point);
01521     coords.push_back(far_point);
01522 
01523     // Lower right, bottom edge.
01524     LPoint2f p3(1.0f - t, -1.0f);
01525     if (!extrude(p3, near_point, far_point)) {
01526       // Hey, this point is off the lens!  Can't do a frustum.
01527       return 0;
01528     }
01529     coords.push_back(near_point);
01530     coords.push_back(far_point);
01531 
01532     // Lower left, left edge.
01533     LPoint2f p4(-1.0f, -1.0f + t);
01534     if (!extrude(p4, near_point, far_point)) {
01535       // Hey, this point is off the lens!  Can't do a frustum.
01536       return 0;
01537     }
01538     coords.push_back(near_point);
01539     coords.push_back(far_point);
01540   }
01541 
01542   // Finally, push one more pair for the viewing axis.
01543   LPoint3f near_axis = LPoint3f::origin(_cs) + LVector3f::forward(_cs) * _near_distance;
01544   LPoint3f far_axis = LPoint3f::origin(_cs) + LVector3f::forward(_cs) * _far_distance;
01545   const LMatrix4f &lens_mat = get_lens_mat();
01546   near_axis = near_axis * lens_mat;
01547   far_axis = far_axis * lens_mat;
01548   coords.push_back(near_axis);
01549   coords.push_back(far_axis);
01550   
01551   if (_geom_coords.is_null()) {
01552     // If the previous array is NULL, simply assign the pointer.
01553     _geom_coords = coords;
01554   } else {
01555     // Otherwise, swap out the vectors within the array so other
01556     // objects that share the PTA will get the new data.
01557     _geom_coords.v().swap(coords.v());
01558   }
01559 
01560   return num_segments;
01561 }
01562 
01563 ////////////////////////////////////////////////////////////////////
01564 //     Function: Lens::build_shear_mat
01565 //       Access: Private, Static
01566 //  Description: A support function for set_frustum_from_corners(),
01567 //               this computes a matrix that will shear the four
01568 //               indicated points to the most nearly rectangular.
01569 ////////////////////////////////////////////////////////////////////
01570 void Lens::
01571 build_shear_mat(LMatrix4f &shear_mat,
01572                 const LPoint3f &cul, const LPoint3f &cur,
01573                 const LPoint3f &cll, const LPoint3f &clr) {
01574   // Fit a parallelogram around these four points.
01575 
01576   // Put the points in an array so we can rotate it around to find
01577   // the longest edge.
01578   LPoint3f points[4] = {
01579     cul, cur, clr, cll
01580   };
01581 
01582   float max_edge_length = -1.0f;
01583   int base_edge = -1;
01584   for (int i = 0; i < 4; i++) {
01585     LVector3f edge = points[(i + 1) % 4] - points[i];
01586     float length = edge.length_squared();
01587     if (length > max_edge_length) {
01588       base_edge = i;
01589       max_edge_length = length;
01590     }
01591   }
01592 
01593   const LPoint3f &base_origin = points[base_edge];
01594   LVector3f base_vec = points[(base_edge + 1) % 4] - base_origin;
01595 
01596   float base_edge_length = csqrt(max_edge_length);
01597 
01598   // The longest edge is the base of our parallelogram.  The parallel
01599   // edge must pass through the point furthest from this edge.
01600 
01601   int a = (base_edge + 2) % 4;
01602   int b = (base_edge + 3) % 4;
01603 
01604   float a_dist = sqr_dist_to_line(points[a], base_origin, base_vec);
01605   float b_dist = sqr_dist_to_line(points[b], base_origin, base_vec);
01606 
01607   int far_point;
01608   float dist;
01609   if (a_dist > b_dist) {
01610     far_point = a;
01611     dist = csqrt(a_dist);
01612   } else {
01613     far_point = b;
01614     dist = csqrt(b_dist);
01615   }
01616 
01617   // Try to make the parallelogram as nearly rectangular as possible.
01618   // How suitable is a true rectangle?
01619   LVector3f perpendic = base_vec.cross(LVector3f(0.0f, -1.0f, 0.0f));
01620   perpendic.normalize();
01621   perpendic *= dist;
01622   LPoint3f parallel_origin = points[base_edge] + perpendic;
01623 
01624   // It follows that far_point is on the line passing through the
01625   // parallel edge.  Is it within the endpoints?
01626   LVector3f base_norm_vec = base_vec / base_edge_length;
01627 
01628   LVector3f far_point_delta = points[far_point] - parallel_origin;
01629   float far_point_pos = far_point_delta.dot(base_norm_vec);
01630 
01631   if (far_point_pos < 0.0f) {
01632     // We have to slide the parallel_origin back to include far_point.
01633     parallel_origin += base_norm_vec * far_point_pos;
01634 
01635   } else if (far_point_pos > base_edge_length) {
01636     // We have to slide the parallel_origin forward to include
01637     // far_point.
01638     parallel_origin += base_norm_vec * (far_point_pos - base_edge_length);
01639   }
01640 
01641   // Finally, is the other point within the parallelogram?
01642   float t;
01643   float Ox = parallel_origin[0];
01644   float Oy = parallel_origin[2];
01645   float Vx = base_vec[0];
01646   float Vy = base_vec[2];
01647   float Ax, Ay, Bx, By;
01648 
01649   if (far_point == a) {
01650     // near point is b
01651     LVector3f v = points[b] - base_origin;
01652     Ax = points[b][0];
01653     Ay = points[b][2];
01654     Bx = v[0];
01655     By = v[2];
01656   } else {
01657     // near point is a
01658     LVector3f v = points[a] - (base_origin + base_vec);
01659     Ax = points[a][0];
01660     Ay = points[a][2];
01661     Bx = v[0];
01662     By = v[2];
01663   }
01664   t = ((Ox - Ax) * By + (Ay - Oy) * Bx) / (Bx * Vy - By * Vx);
01665 
01666   if (t < 0.0f) {
01667     // We need to slide the parallel_origin back to include
01668     // the near point.
01669     parallel_origin += base_vec * t;
01670   } else if (t > 1.0f) {
01671     // We need to slide the parallel_origin forward to include the far
01672     // point.
01673     parallel_origin += base_vec * (1.0f - t);
01674   }
01675 
01676   LVector3f adjacent_norm_vec = parallel_origin - base_origin;
01677   adjacent_norm_vec.normalize();
01678 
01679   // Now we've defined a parallelogram that includes all four points,
01680   // and we're ready to build a shear transform.
01681   shear_mat = LMatrix4f::ident_mat();
01682 
01683   // The edges of the parallelogram become the axes.
01684   switch (base_edge) {
01685   case 0:
01686     // The base_origin is the upper-left corner.  X axis is base_norm_vec,
01687     // Z axis is -adjacent_norm_vec.
01688     shear_mat.set_row(0, base_norm_vec);
01689     shear_mat.set_row(2, -adjacent_norm_vec);
01690     break;
01691 
01692   case 1:
01693     // The base_origin is the upper-right corner.  X axis is
01694     // -adjacent_norm_vec, Z axis is -base_norm_vec.
01695     shear_mat.set_row(0, -adjacent_norm_vec);
01696     shear_mat.set_row(2, -base_norm_vec);
01697     break;
01698 
01699   case 2:
01700     // The base_origin is the lower-right corner.  X axis is
01701     // -base_norm_vec, Z axis is adjacent_norm_vec.
01702     shear_mat.set_row(0, -base_norm_vec);
01703     shear_mat.set_row(2, adjacent_norm_vec);
01704     break;
01705 
01706   case 3:
01707     // The base_origin is the lower-left corner.  X axis is
01708     // adjacent_norm_vec, Z axis is base_norm_vec.
01709     shear_mat.set_row(0, adjacent_norm_vec);
01710     shear_mat.set_row(2, base_norm_vec);
01711     break;
01712     
01713   default:
01714     nassertv(false);
01715   }
01716 }
01717 
01718 ////////////////////////////////////////////////////////////////////
01719 //     Function: Lens::sqr_dist_to_line
01720 //       Access: Private, Static
01721 //  Description: A support function for build_shear_mat(), this
01722 //               computes the minimum distance from a point to a line,
01723 //               and returns the distance squared.
01724 ////////////////////////////////////////////////////////////////////
01725 float Lens::
01726 sqr_dist_to_line(const LPoint3f &point, const LPoint3f &origin, 
01727                  const LVector3f &vec) {
01728   LVector3f norm = vec;
01729   norm.normalize();
01730   LVector3f d = point - origin;
01731   float hyp_2 = d.length_squared();
01732   float leg = d.dot(norm);
01733   return hyp_2 - leg * leg;
01734 }

Generated on Fri May 2 00:39:34 2003 for Panda by doxygen1.3