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