Panda3D
lens.cxx
1 // Filename: lens.cxx
2 // Created by: drose (18Feb99)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "lens.h"
16 #include "throw_event.h"
17 #include "compose_matrix.h"
18 #include "look_at.h"
19 #include "geom.h"
20 #include "geomLinestrips.h"
21 #include "geomVertexWriter.h"
22 #include "boundingHexahedron.h"
23 #include "indent.h"
24 #include "config_gobj.h"
25 #include "plane.h"
26 
27 TypeHandle Lens::_type_handle;
28 TypeHandle Lens::CData::_type_handle;
29 
30 ////////////////////////////////////////////////////////////////////
31 // Function: Lens::Constructor
32 // Access: Public
33 // Description:
34 ////////////////////////////////////////////////////////////////////
35 Lens::
36 Lens() {
37  clear();
38 }
39 
40 ////////////////////////////////////////////////////////////////////
41 // Function: Lens::Copy Constructor
42 // Access: Public
43 // Description:
44 ////////////////////////////////////////////////////////////////////
45 Lens::
46 Lens(const Lens &copy) : _cycler(copy._cycler) {
47  // We don't copy the _geom_data. That's unique to each Lens.
48  CDWriter cdata(_cycler, true);
49  cdata->_geom_data = NULL;
50 }
51 
52 ////////////////////////////////////////////////////////////////////
53 // Function: Lens::Copy Assignment Operator
54 // Access: Public
55 // Description:
56 ////////////////////////////////////////////////////////////////////
57 void Lens::
58 operator = (const Lens &copy) {
59  _cycler = copy._cycler;
60 
61  // We don't copy the _geom_data. That's unique to each Lens.
62  CDWriter cdata(_cycler, true);
63  cdata->_geom_data = NULL;
64 }
65 
66 ////////////////////////////////////////////////////////////////////
67 // Function: Lens::set_coordinate_system
68 // Access: Published
69 // Description: Specifies the coordinate system that all 3-d
70 // computations are performed within for this
71 // Lens. Normally, this is CS_default.
72 ////////////////////////////////////////////////////////////////////
73 void Lens::
74 set_coordinate_system(CoordinateSystem cs) {
75  CDWriter cdata(_cycler, true);
76  cdata->_cs = cs;
77  do_adjust_comp_flags(cdata, CF_mat | CF_view_hpr | CF_view_vector, 0);
78  do_throw_change_event(cdata);
79 }
80 
81 ////////////////////////////////////////////////////////////////////
82 // Function: Lens::clear
83 // Access: Published
84 // Description: Resets all lens parameters to their initial default
85 // settings.
86 ////////////////////////////////////////////////////////////////////
87 void Lens::
88 clear() {
89  CDWriter cdata(_cycler, true);
90  cdata->clear();
91 
92  do_set_interocular_distance(cdata, default_iod);
93  do_set_convergence_distance(cdata, default_converge);
94  do_throw_change_event(cdata);
95 }
96 
97 ////////////////////////////////////////////////////////////////////
98 // Function: Lens::set_min_fov
99 // Access: Published
100 // Description: Sets the field of view of the smallest dimension of
101 // the window. If the window is wider than it is tall,
102 // this specifies the vertical field of view; if it is
103 // taller than it is wide, this specifies the horizontal
104 // field of view.
105 //
106 // In many cases, this is preferable to setting either
107 // the horizontal or vertical field of view explicitly.
108 // Setting this parameter means that pulling the window
109 // wider will widen the field of view, which is usually
110 // what you expect to happen.
111 ////////////////////////////////////////////////////////////////////
112 void Lens::
113 set_min_fov(PN_stdfloat min_fov) {
114  nassertv(!cnan(min_fov));
115  CDWriter cdata(_cycler, true);
116  cdata->_min_fov = min_fov;
117 
118  // We can't specify all three of focal length, fov, and film size.
119  // Throw out the oldest one.
120  do_resequence_fov_triad(cdata, cdata->_fov_seq, cdata->_focal_length_seq, cdata->_film_size_seq);
121 
122  if (cdata->_focal_length_seq == 0) {
123  // Throw out focal length if it's oldest.
124  do_adjust_user_flags(cdata, UF_focal_length | UF_vfov | UF_hfov,
125  UF_min_fov);
126  } else {
127  // Otherwise, throw out film size.
128  nassertv(cdata->_film_size_seq == 0);
129 
130  // Make sure we save the aspect ratio first.
131  do_compute_aspect_ratio(cdata);
132  do_adjust_user_flags(cdata, UF_film_width | UF_film_height | UF_vfov | UF_hfov,
133  UF_min_fov);
134  }
135  do_adjust_comp_flags(cdata, CF_mat | CF_focal_length | CF_fov | CF_film_size,
136  0);
137  // We leave CF_fov off of comp_flags, because we will still need to
138  // recompute the vertical fov. It's not exactly the same as hfov *
139  // get_aspect_ratio().
140  do_throw_change_event(cdata);
141 }
142 
143 ////////////////////////////////////////////////////////////////////
144 // Function: Lens::get_min_fov
145 // Access: Published
146 // Description: Returns the field of view of the narrowest dimension
147 // of the window. See set_min_fov().
148 ////////////////////////////////////////////////////////////////////
149 PN_stdfloat Lens::
150 get_min_fov() const {
151  CDReader cdata(_cycler);
152 
153  if ((cdata->_comp_flags & CF_fov) == 0) {
154  ((Lens *)this)->do_compute_fov((CData *)cdata.p());
155  }
156  return cdata->_min_fov;
157 }
158 
159 ////////////////////////////////////////////////////////////////////
160 // Function: Lens::get_default_near
161 // Access: Published, Static
162 // Description: Returns the default near plane distance that will be
163 // assigned to each newly-created lens. This is read
164 // from the Configrc file.
165 ////////////////////////////////////////////////////////////////////
166 PN_stdfloat Lens::
168  return default_near;
169 }
170 
171 ////////////////////////////////////////////////////////////////////
172 // Function: Lens::get_default_far
173 // Access: Published, Static
174 // Description: Returns the default far plane distance that will be
175 // assigned to each newly-created lens. This is read
176 // from the Configrc file.
177 ////////////////////////////////////////////////////////////////////
178 PN_stdfloat Lens::
180  return default_far;
181 }
182 
183 ////////////////////////////////////////////////////////////////////
184 // Function: Lens::set_view_hpr
185 // Access: Published
186 // Description: Sets the direction in which the lens is facing.
187 // Normally, this is down the forward axis (usually the
188 // Y axis), but it may be rotated. This is only one way
189 // of specifying the rotation; you may also specify an
190 // explicit vector in which to look, or you may give a
191 // complete transformation matrix.
192 ////////////////////////////////////////////////////////////////////
193 void Lens::
194 set_view_hpr(const LVecBase3 &view_hpr) {
195  nassertv(!view_hpr.is_nan());
196  CDWriter cdata(_cycler, true);
197  cdata->_view_hpr = view_hpr;
198  do_adjust_user_flags(cdata, UF_view_vector | UF_view_mat,
199  UF_view_hpr);
200  do_adjust_comp_flags(cdata, CF_mat | CF_view_vector,
201  CF_view_hpr);
202  do_throw_change_event(cdata);
203 }
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: Lens::get_view_hpr
207 // Access: Published
208 // Description: Returns the direction in which the lens is facing.
209 ////////////////////////////////////////////////////////////////////
210 const LVecBase3 &Lens::
211 get_view_hpr() const {
212  CDReader cdata(_cycler);
213  if ((cdata->_comp_flags & CF_view_hpr) == 0) {
214  ((Lens *)this)->do_compute_view_hpr((CData *)cdata.p());
215  }
216  return cdata->_view_hpr;
217 }
218 
219 ////////////////////////////////////////////////////////////////////
220 // Function: Lens::set_view_vector
221 // Access: Published
222 // Description: Specifies the direction in which the lens is facing
223 // by giving an axis to look along, and a perpendicular
224 // (or at least non-parallel) up axis.
225 //
226 // See also set_view_hpr().
227 ////////////////////////////////////////////////////////////////////
228 void Lens::
229 set_view_vector(const LVector3 &view_vector, const LVector3 &up_vector) {
230  nassertv(!view_vector.is_nan());
231  CDWriter cdata(_cycler, true);
232  cdata->_view_vector = view_vector;
233  cdata->_up_vector = up_vector;
234  do_adjust_user_flags(cdata, UF_view_hpr | UF_view_mat,
235  UF_view_vector);
236  do_adjust_comp_flags(cdata, CF_mat | CF_view_hpr,
237  CF_view_vector);
238  do_throw_change_event(cdata);
239 }
240 
241 ////////////////////////////////////////////////////////////////////
242 // Function: Lens::get_view_vector
243 // Access: Published
244 // Description: Returns the axis along which the lens is facing.
245 ////////////////////////////////////////////////////////////////////
246 const LVector3 &Lens::
248  CDReader cdata(_cycler);
249  if ((cdata->_comp_flags & CF_view_vector) == 0) {
250  ((Lens *)this)->do_compute_view_vector((CData *)cdata.p());
251  }
252  return cdata->_view_vector;
253 }
254 
255 ////////////////////////////////////////////////////////////////////
256 // Function: Lens::get_up_vector
257 // Access: Published
258 // Description: Returns the axis perpendicular to the camera's view
259 // vector that indicates the "up" direction.
260 ////////////////////////////////////////////////////////////////////
261 const LVector3 &Lens::
262 get_up_vector() const {
263  CDReader cdata(_cycler);
264  if ((cdata->_comp_flags & CF_view_vector) == 0) {
265  ((Lens *)this)->do_compute_view_vector((CData *)cdata.p());
266  }
267  return cdata->_up_vector;
268 }
269 
270 ////////////////////////////////////////////////////////////////////
271 // Function: Lens::get_nodal_point
272 // Access: Published
273 // Description: Returns the center point of the lens: the point from
274 // which the lens is viewing.
275 ////////////////////////////////////////////////////////////////////
278  return get_view_mat().get_row3(3);
279 }
280 
281 ////////////////////////////////////////////////////////////////////
282 // Function: Lens::clear_view_mat
283 // Access: Published
284 // Description: Resets the lens transform to identity.
285 ////////////////////////////////////////////////////////////////////
286 void Lens::
288  CDWriter cdata(_cycler, true);
289  cdata->_lens_mat = LMatrix4::ident_mat();
290  do_adjust_user_flags(cdata, 0, UF_view_vector | UF_view_hpr | UF_view_mat);
291  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv |
292  CF_projection_mat_left_inv | CF_projection_mat_right_inv |
293  CF_lens_mat_inv | CF_view_hpr | CF_view_vector,
294  CF_lens_mat);
295  do_throw_change_event(cdata);
296 }
297 
298 ////////////////////////////////////////////////////////////////////
299 // Function: Lens::set_keystone
300 // Access: Published
301 // Description: Indicates the ratio of keystone correction to perform
302 // on the lens, in each of three axes. This will build
303 // a special non-affine scale factor into the projection
304 // matrix that will compensate for keystoning of a
305 // projected image; this can be used to compensate for a
306 // projector that for physical reasons cannot be aimed
307 // directly at its screen.
308 //
309 // The default value is taken from the default-keystone
310 // Config variable. 0, 0 indicates no keystone
311 // correction; specify a small value (usually in the
312 // range -1 .. 1) in either the x or y position to
313 // generate a keystone correction in that axis.
314 ////////////////////////////////////////////////////////////////////
315 void Lens::
316 set_keystone(const LVecBase2 &keystone) {
317  nassertv(!keystone.is_nan());
318  CDWriter cdata(_cycler, true);
319  cdata->_keystone = keystone;
320  do_adjust_user_flags(cdata, 0, UF_keystone);
321  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv |
322  CF_projection_mat_left_inv | CF_projection_mat_right_inv |
323  CF_film_mat | CF_film_mat_inv, 0);
324  do_throw_change_event(cdata);
325 }
326 
327 ////////////////////////////////////////////////////////////////////
328 // Function: Lens::clear_keystone
329 // Access: Published
330 // Description: Disables the lens keystone correction.
331 ////////////////////////////////////////////////////////////////////
332 void Lens::
334  CDWriter cdata(_cycler, true);
335  cdata->_keystone.set(0.0f, 0.0f);
336  do_adjust_user_flags(cdata, UF_keystone, 0);
337  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv |
338  CF_projection_mat_left_inv | CF_projection_mat_right_inv |
339  CF_film_mat | CF_film_mat_inv, 0);
340  do_throw_change_event(cdata);
341 }
342 
343 ////////////////////////////////////////////////////////////////////
344 // Function: Lens::set_custom_film_mat
345 // Access: Published
346 // Description: Specifies a custom matrix to transform the points on
347 // the film after they have been converted into nominal
348 // film space (-1 .. 1 in U and V). This can be used to
349 // introduce arbitrary scales, rotations, or other
350 // linear transforms to the media plane. This is
351 // normally a 2-d matrix, but a full 4x4 matrix may be
352 // specified. This is applied on top of any film size,
353 // lens shift, and/or keystone correction.
354 ////////////////////////////////////////////////////////////////////
355 void Lens::
356 set_custom_film_mat(const LMatrix4 &custom_film_mat) {
357  nassertv(!custom_film_mat.is_nan());
358  CDWriter cdata(_cycler, true);
359  cdata->_custom_film_mat = custom_film_mat;
360  do_adjust_user_flags(cdata, 0, UF_custom_film_mat);
361  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv |
362  CF_projection_mat_left_inv | CF_projection_mat_right_inv |
363  CF_film_mat | CF_film_mat_inv, 0);
364  do_throw_change_event(cdata);
365 }
366 
367 ////////////////////////////////////////////////////////////////////
368 // Function: Lens::clear_custom_film_mat
369 // Access: Published
370 // Description: Disables the lens custom_film_mat correction.
371 ////////////////////////////////////////////////////////////////////
372 void Lens::
374  CDWriter cdata(_cycler, true);
375  cdata->_custom_film_mat = LMatrix4::ident_mat();
376  do_adjust_user_flags(cdata, UF_custom_film_mat, 0);
377  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv |
378  CF_projection_mat_left_inv | CF_projection_mat_right_inv |
379  CF_film_mat | CF_film_mat_inv, 0);
380  do_throw_change_event(cdata);
381 }
382 
383 ////////////////////////////////////////////////////////////////////
384 // Function: Lens::set_frustum_from_corners
385 // Access: Published
386 // Description: Sets up the lens to use the frustum defined by the
387 // four indicated points. This is most useful for a
388 // PerspectiveLens, but it may be called for other kinds
389 // of lenses as well.
390 //
391 // The frustum will be rooted at the origin (or by
392 // whatever translation might have been specified in a
393 // previous call to set_view_mat).
394 //
395 // It is legal for the four points not to be arranged in
396 // a rectangle; if this is the case, the frustum will be
397 // fitted as tightly as possible to cover all four
398 // points.
399 //
400 // The flags parameter contains the union of one or more
401 // of the following bits to control the behavior of this
402 // function:
403 //
404 // FC_roll - If this is included, the camera may be
405 // rotated so that its up vector is perpendicular to the
406 // top line. Otherwise, the standard up vector is used.
407 //
408 // FC_camera_plane - This allows the camera plane to be
409 // adjusted to be as nearly perpendicular to the center
410 // of the frustum as possible. Without this bit, the
411 // orientation camera plane is defined by position of
412 // the four points (which should all be coplanar). With
413 // this bit, the camera plane is arbitarary, and may be
414 // chosen so that the four points do not themselves lie
415 // in the camera plane (but the points will still be
416 // within the frustum).
417 //
418 // FC_off_axis - This allows the resulting frustum to be
419 // off-axis to get the tightest possible fit. Without
420 // this bit, the viewing axis will be centered within
421 // the frustum, but there may be more wasted space along
422 // the edges.
423 //
424 // FC_aspect_ratio - This allows the frustum to be
425 // scaled non-proportionately in the vertical and
426 // horizontal dimensions, if necessary, to get a tighter
427 // fit. Without this bit, the current aspect ratio will
428 // be preserved.
429 //
430 // FC_shear - This allows the frustum to be sheared, if
431 // necessary, to get the tightest possible fit. This
432 // may result in a parallelogram-based frustum, which
433 // will give a slanted appearance to the rendered image.
434 // Without this bit, the frustum will be
435 // rectangle-based.
436 //
437 // In general, if 0 is passed in as the value for flags,
438 // the generated frustum will be a loose fit but sane;
439 // if -1 is passed in, it will be a tighter fit and
440 // possibly screwy.
441 ////////////////////////////////////////////////////////////////////
442 void Lens::
444  const LVecBase3 &ll, const LVecBase3 &lr,
445  int flags) {
446  nassertv(!ul.is_nan() && !ur.is_nan() && !ll.is_nan() && !lr.is_nan());
447 
448  CDWriter cdata(_cycler, true);
449  // We'll need to know the pre-existing eyepoint translation from the
450  // center, so we can preserve it in the new frustum. This is
451  // usually (0, 0, 0), but it could be an arbitrary vector.
452  const LMatrix4 &lens_mat_inv = do_get_lens_mat_inv(cdata);
453  LVector3 eye_offset;
454  lens_mat_inv.get_row3(eye_offset, 3);
455 
456  // Now choose the viewing axis. If FC_camera_plane is specified,
457  // we'll pass it through the centroid for the best camera plane;
458  // otherwise, it's perpendicular to the plane in which the points
459  // lie.
460  LVector3 view_vector;
461  if ((flags & FC_camera_plane) != 0) {
462  view_vector = (ul + ur + ll + lr) * 0.25;
463  } else {
464  LPlane plane(ll, ul, ur);
465  view_vector = plane.get_normal();
466  nassertv(!view_vector.is_nan() && view_vector.length_squared() != 0.0f);
467  }
468 
469  // Now determine the up axis. If FC_roll is specified, or if our
470  // view vector is straight up, it is the vector perpendicular to
471  // both the viewing axis and the top line. Otherwise, it is the
472  // standard up axis.
473  LVector3 up_vector = LVector3::up(cdata->_cs);
474  if (view_vector == up_vector || ((flags & FC_roll) != 0)) {
475  LVector3 top = ul - ur;
476  up_vector = view_vector.cross(top);
477  nassertv(!up_vector.is_nan() && up_vector.length_squared() != 0.0f);
478  }
479 
480  // Now compute the matrix that applies this rotation.
481  LMatrix4 rot_mat;
482  look_at(rot_mat, view_vector, up_vector, CS_zup_right);
483 
484  // And invert it.
485  LMatrix4 inv_rot_mat;
486  inv_rot_mat.invert_affine_from(rot_mat);
487 
488  // Use that inverse matrix to convert the four corners to a local
489  // coordinate system, looking down the Y axis.
490  LPoint3 cul = inv_rot_mat.xform_point(ul);
491  LPoint3 cur = inv_rot_mat.xform_point(ur);
492  LPoint3 cll = inv_rot_mat.xform_point(ll);
493  LPoint3 clr = inv_rot_mat.xform_point(lr);
494 
495  // Project all points into the Y == 1 plane, so we can do 2-d
496  // manipulation on them.
497  nassertv(cul[1] != 0.0f && cur[1] != 0.0f && cll[1] != 0.0f && clr[1] != 0.0f);
498  cul /= cul[1];
499  cur /= cur[1];
500  cll /= cll[1];
501  clr /= clr[1];
502 
503  LMatrix4 shear_mat = LMatrix4::ident_mat();
504  LMatrix4 inv_shear_mat = LMatrix4::ident_mat();
505 
506  // Now, if we're allowed to shear the frustum, do so.
507  if ((flags & FC_shear) != 0) {
508  build_shear_mat(shear_mat, cul, cur, cll, clr);
509  inv_shear_mat.invert_from(shear_mat);
510  }
511 
512  // Now build the complete view matrix.
513  LMatrix4 inv_view_mat =
514  inv_rot_mat *
515  inv_shear_mat;
516 
517  // And reapply the eye offset to this matrix.
518  inv_view_mat.set_row(3, eye_offset);
519 
520  LMatrix4 view_mat;
521  view_mat.invert_from(inv_view_mat);
522  do_set_view_mat(cdata, view_mat);
523 
524  LPoint3 ful = inv_view_mat.xform_point(ul);
525  LPoint3 fur = inv_view_mat.xform_point(ur);
526  LPoint3 fll = inv_view_mat.xform_point(ll);
527  LPoint3 flr = inv_view_mat.xform_point(lr);
528 
529  // Normalize *these* points into the y == 1 plane.
530  nassertv(ful[1] != 0.0f && fur[1] != 0.0f && fll[1] != 0.0f && flr[1] != 0.0f);
531  ful /= ful[1];
532  fur /= fur[1];
533  fll /= fll[1];
534  flr /= flr[1];
535 
536  // Determine the minimum field of view necesary to cover all four
537  // transformed points.
538  PN_stdfloat min_x = min(min(ful[0], fur[0]), min(fll[0], flr[0]));
539  PN_stdfloat max_x = max(max(ful[0], fur[0]), max(fll[0], flr[0]));
540  PN_stdfloat min_z = min(min(ful[2], fur[2]), min(fll[2], flr[2]));
541  PN_stdfloat max_z = max(max(ful[2], fur[2]), max(fll[2], flr[2]));
542 
543  PN_stdfloat x_spread, x_center, z_spread, z_center;
544 
545  if ((flags & FC_off_axis) != 0) {
546  // If we're allowed to make an off-axis projection, then pick the
547  // best center.
548  x_center = (max_x + min_x) * 0.5f;
549  z_center = (max_z + min_z) * 0.5f;
550  x_spread = x_center - min_x;
551  z_spread = z_center - min_z;
552  } else {
553  // Otherwise, the center must be (0, 0).
554  x_center = 0.0f;
555  z_center = 0.0f;
556  x_spread = max(cabs(max_x), cabs(min_x));
557  z_spread = max(cabs(max_z), cabs(min_z));
558  }
559 
560  PN_stdfloat aspect_ratio = do_get_aspect_ratio(cdata);
561  nassertv(aspect_ratio != 0.0f);
562  if ((flags & FC_aspect_ratio) == 0) {
563  // If we must preserve the aspect ratio, then the x and z spreads
564  // must be adjusted to match.
565  if (x_spread < z_spread * aspect_ratio) {
566  // x_spread is too small.
567  x_spread = z_spread * aspect_ratio;
568  } else if (z_spread < x_spread / aspect_ratio) {
569  // z_spread is too small.
570  z_spread = x_spread / aspect_ratio;
571  }
572  }
573 
574  PN_stdfloat hfov = rad_2_deg(catan(x_spread)) * 2.0f;
575  PN_stdfloat vfov = rad_2_deg(catan(z_spread)) * 2.0f;
576 
577  do_set_fov(cdata, LVecBase2(hfov, vfov));
578 
579  if ((flags & FC_aspect_ratio) == 0) {
580  // If we must preserve the aspect ratio, store it one more time.
581  // This is mainly in case we have a non-perspective lens with a
582  // funny relationship between fov and aspect ratio.
583  do_set_aspect_ratio(cdata, aspect_ratio);
584  }
585 
586  const LVecBase2 &film_size = do_get_film_size(cdata);
587  nassertv(x_spread != 0.0f && z_spread != 0.0f);
588  do_set_film_offset(cdata, LVecBase2(film_size[0] * x_center / (x_spread * 2.0f),
589  film_size[1] * z_center / (z_spread * 2.0f)));
590 }
591 
592 
593 ////////////////////////////////////////////////////////////////////
594 // Function: Lens::recompute_all
595 // Access: Published
596 // Description: Forces all internal parameters of the Lens to be
597 // recomputed. Normally, this should never need to be
598 // called; it is provided only to assist in debugging.
599 ////////////////////////////////////////////////////////////////////
600 void Lens::
602  CDWriter cdata(_cycler);
603  cdata->_comp_flags = 0;
604 }
605 
606 ////////////////////////////////////////////////////////////////////
607 // Function: Lens::is_linear
608 // Access: Published, Virtual
609 // Description: Returns true if the lens represents a linear
610 // projection (e.g. PerspectiveLens, OrthographicLens),
611 // and therefore there is a valid matrix returned by
612 // get_projection_mat(), or false otherwise.
613 ////////////////////////////////////////////////////////////////////
614 bool Lens::
615 is_linear() const {
616  return false;
617 }
618 
619 ////////////////////////////////////////////////////////////////////
620 // Function: Lens::is_perspective
621 // Access: Published, Virtual
622 // Description: Returns true if the lens represents a perspective
623 // projection (i.e. it is a PerspectiveLens), false
624 // otherwise.
625 ////////////////////////////////////////////////////////////////////
626 bool Lens::
627 is_perspective() const {
628  return false;
629 }
630 
631 ////////////////////////////////////////////////////////////////////
632 // Function: Lens::is_orthographic
633 // Access: Published, Virtual
634 // Description: Returns true if the lens represents a orthographic
635 // projection (i.e. it is a OrthographicLens), false
636 // otherwise.
637 ////////////////////////////////////////////////////////////////////
638 bool Lens::
640  return false;
641 }
642 
643 ////////////////////////////////////////////////////////////////////
644 // Function: Lens::make_geometry
645 // Access: Published, Virtual
646 // Description: Allocates and returns a new Geom that can be rendered
647 // to show a visible representation of the frustum used
648 // for this kind of lens, if it makes sense to do
649 // so. If a visible representation cannot be created,
650 // returns NULL.
651 ////////////////////////////////////////////////////////////////////
652 PT(Geom) Lens::
653 make_geometry() {
654  CDWriter cdata(_cycler, true);
655 
656  // The default behavior for make_geometry() will be to draw a
657  // hexahedron around the eight vertices of the frustum. If the lens
658  // is non-linear, the hexahedron will be curved; in that case, we'll
659  // subdivide the lines into several segments to get an approximation
660  // of the curve.
661 
662  // First, define all the points we'll use in this Geom. That's one
663  // point at each corner of the near and far planes (and possibly
664  // more points along the edges).
665  int num_segments = do_define_geom_data(cdata);
666  if (num_segments == 0) {
667  // Can't do a frustum.
668  cdata->_geom_data.clear();
669  return (Geom *)NULL;
670  }
671 
672  // Now string together the line segments.
673  PT(GeomLinestrips) line = new GeomLinestrips(Geom::UH_static);
674 
675  // Draw a frame around the near plane.
676  int i, si;
677  for (i = 0; i < 4; ++i) {
678  for (si = 0; si < num_segments; ++si) {
679  line->add_vertex(i * 2 + si * (4 * 2) + 0);
680  }
681  }
682  line->add_vertex(0);
683  line->close_primitive();
684 
685  // Draw a frame around the far plane.
686  for (i = 0; i < 4; ++i) {
687  for (si = 0; si < num_segments; ++si) {
688  line->add_vertex(i * 2 + si * (4 * 2) + 1);
689  }
690  }
691  line->add_vertex(1);
692  line->close_primitive();
693 
694  // Draw connecting lines at the corners.
695  line->add_vertex(0 * 2 + 0);
696  line->add_vertex(0 * 2 + 1);
697  line->close_primitive();
698 
699  line->add_vertex(1 * 2 + 0);
700  line->add_vertex(1 * 2 + 1);
701  line->close_primitive();
702 
703  line->add_vertex(2 * 2 + 0);
704  line->add_vertex(2 * 2 + 1);
705  line->close_primitive();
706 
707  line->add_vertex(3 * 2 + 0);
708  line->add_vertex(3 * 2 + 1);
709  line->close_primitive();
710 
711  // And one more line for the viewing axis.
712  line->add_vertex(num_segments * (4 * 2) + 0);
713  line->add_vertex(num_segments * (4 * 2) + 1);
714  line->close_primitive();
715 
716  PT(Geom) geom = new Geom(cdata->_geom_data);
717  geom->add_primitive(line);
718 
719  return geom.p();
720 }
721 
722 ////////////////////////////////////////////////////////////////////
723 // Function: Lens::make_bounds
724 // Access: Published, Virtual
725 // Description: Allocates and returns a new BoundingVolume that
726 // encloses the frustum used for this kind of
727 // lens, if possible. If a suitable bounding
728 // volume cannot be created, returns NULL.
729 ////////////////////////////////////////////////////////////////////
730 PT(BoundingVolume) Lens::
731 make_bounds() const {
732  CDReader cdata(_cycler);
733 
734  // The default bounding volume is a hexahedron based on the eight
735  // corners of the frustum.
736  LPoint3 fll, flr, ful, fur;
737  LPoint3 nll, nlr, nul, nur;
738  LPoint3 corner;
739 
740  // Upper left.
741  corner.set(-1.0f, 1.0f, 0.0f);
742  if (!do_extrude(cdata, corner, nul, ful)) {
743  return (BoundingVolume *)NULL;
744  }
745 
746  // Upper right.
747  corner.set(1.0f, 1.0f, 0.0f);
748  if (!do_extrude(cdata, corner, nur, fur)) {
749  return (BoundingVolume *)NULL;
750  }
751 
752  // Lower right.
753  corner.set(1.0f, -1.0f, 0.0f);
754  if (!do_extrude(cdata, corner, nlr, flr)) {
755  return (BoundingVolume *)NULL;
756  }
757 
758  // Lower left.
759  corner.set(-1.0f, -1.0f, 0.0f);
760  if (!do_extrude(cdata, corner, nll, fll)) {
761  return (BoundingVolume *)NULL;
762  }
763 
764  return new BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul);
765 }
766 
767 ////////////////////////////////////////////////////////////////////
768 // Function: Lens::output
769 // Access: Published, Virtual
770 // Description:
771 ////////////////////////////////////////////////////////////////////
772 void Lens::
773 output(ostream &out) const {
774  out << get_type();
775 }
776 
777 ////////////////////////////////////////////////////////////////////
778 // Function: Lens::write
779 // Access: Published, Virtual
780 // Description:
781 ////////////////////////////////////////////////////////////////////
782 void Lens::
783 write(ostream &out, int indent_level) const {
784  indent(out, indent_level) << get_type() << " fov = " << get_fov() << "\n";
785 }
786 
787 ////////////////////////////////////////////////////////////////////
788 // Function: Lens::do_set_film_size
789 // Access: Protected
790 // Description:
791 ////////////////////////////////////////////////////////////////////
792 void Lens::
793 do_set_film_size(CData *cdata, PN_stdfloat width) {
794  nassertv(!cnan(width));
795  cdata->_film_size.set(width, width / do_get_aspect_ratio(cdata));
796 
797  // We can't specify all three of focal length, fov, and film size.
798  // Throw out the oldest one.
799  do_resequence_fov_triad(cdata, cdata->_film_size_seq, cdata->_focal_length_seq, cdata->_fov_seq);
800 
801  if (cdata->_fov_seq == 0) {
802  // Throw out fov if it's oldest.
803  do_adjust_user_flags(cdata, UF_hfov | UF_vfov | UF_min_fov | UF_film_height,
804  UF_film_width);
805  } else {
806  // Otherwise, throw out focal length.
807  nassertv(cdata->_focal_length_seq == 0);
808  do_adjust_user_flags(cdata, UF_focal_length | UF_film_height,
809  UF_film_width);
810  }
811  do_adjust_comp_flags(cdata, CF_mat | CF_focal_length | CF_fov,
812  CF_film_size);
813  do_throw_change_event(cdata);
814 }
815 
816 ////////////////////////////////////////////////////////////////////
817 // Function: Lens::do_set_film_size
818 // Access: Protected
819 // Description:
820 ////////////////////////////////////////////////////////////////////
821 void Lens::
822 do_set_film_size(CData *cdata, const LVecBase2 &film_size) {
823  nassertv(!film_size.is_nan());
824  cdata->_film_size = film_size;
825 
826  // We can't specify all three of focal length, fov, and film size.
827  // Throw out the oldest one.
828  do_resequence_fov_triad(cdata, cdata->_film_size_seq, cdata->_focal_length_seq, cdata->_fov_seq);
829 
830  if (cdata->_fov_seq == 0) {
831  // Throw out fov if it's oldest.
832  do_adjust_user_flags(cdata, UF_hfov | UF_vfov | UF_min_fov | UF_aspect_ratio,
833  UF_film_width | UF_film_height);
834  } else {
835  // Otherwise, throw out focal length.
836  nassertv(cdata->_focal_length_seq == 0);
837  do_adjust_user_flags(cdata, UF_focal_length | UF_vfov | UF_aspect_ratio,
838  UF_film_width | UF_film_height);
839  }
840  do_adjust_comp_flags(cdata, CF_mat | CF_focal_length | CF_fov | CF_aspect_ratio,
841  CF_film_size);
842 
843  // Also, the user has implicitly specified an aspect ratio. Make it
844  // stick until the user tells us something different.
845  do_compute_aspect_ratio(cdata);
846  do_adjust_user_flags(cdata, 0, UF_aspect_ratio);
847 
848  do_throw_change_event(cdata);
849 }
850 
851 ////////////////////////////////////////////////////////////////////
852 // Function: Lens::do_get_film_size
853 // Access: Protected
854 // Description:
855 ////////////////////////////////////////////////////////////////////
856 const LVecBase2 &Lens::
857 do_get_film_size(const CData *cdata) const {
858  if ((cdata->_comp_flags & CF_film_size) == 0) {
859  // We pretend this is a const method, even though it may call a
860  // non-const method to recompute the internal values. We can do
861  // this because this is just compute-on-demand.
862  ((Lens *)this)->do_compute_film_size((CData *)cdata);
863  }
864  return cdata->_film_size;
865 }
866 
867 ////////////////////////////////////////////////////////////////////
868 // Function: Lens::do_set_focal_length
869 // Access: Protected
870 // Description:
871 ////////////////////////////////////////////////////////////////////
872 void Lens::
873 do_set_focal_length(CData *cdata, PN_stdfloat focal_length) {
874  nassertv(!cnan(focal_length));
875  cdata->_focal_length = focal_length;
876 
877  // We can't specify all three of focal length, fov, and film size.
878  // Throw out the oldest one.
879  do_resequence_fov_triad(cdata, cdata->_focal_length_seq, cdata->_film_size_seq, cdata->_fov_seq);
880 
881  if (cdata->_film_size_seq == 0) {
882  // Throw out film size if it's oldest.
883  do_adjust_user_flags(cdata, UF_film_width | UF_film_height,
884  UF_focal_length);
885  } else {
886  // Otherwise, throw out the fov.
887  nassertv(cdata->_fov_seq == 0);
888  do_adjust_user_flags(cdata, UF_hfov | UF_vfov | UF_min_fov,
889  UF_focal_length);
890  }
891 
892  do_adjust_comp_flags(cdata, CF_mat | CF_fov | CF_film_size,
893  CF_focal_length);
894  do_throw_change_event(cdata);
895 }
896 
897 ////////////////////////////////////////////////////////////////////
898 // Function: Lens::do_get_focal_length
899 // Access: Protected
900 // Description:
901 ////////////////////////////////////////////////////////////////////
902 PN_stdfloat Lens::
903 do_get_focal_length(const CData *cdata) const {
904  if ((cdata->_comp_flags & CF_focal_length) == 0) {
905  ((Lens *)this)->do_compute_focal_length((CData *)cdata);
906  }
907  return cdata->_focal_length;
908 }
909 
910 ////////////////////////////////////////////////////////////////////
911 // Function: Lens::do_set_fov
912 // Access: Protected
913 // Description:
914 ////////////////////////////////////////////////////////////////////
915 void Lens::
916 do_set_fov(CData *cdata, PN_stdfloat hfov) {
917  nassertv(!cnan(hfov));
918  cdata->_fov[0] = hfov;
919 
920  // We can't specify all three of focal length, fov, and film size.
921  // Throw out the oldest one.
922  do_resequence_fov_triad(cdata, cdata->_fov_seq, cdata->_focal_length_seq, cdata->_film_size_seq);
923 
924  if (cdata->_focal_length_seq == 0) {
925  // Throw out focal length if it's oldest.
926  do_adjust_user_flags(cdata, UF_focal_length | UF_vfov | UF_min_fov,
927  UF_hfov);
928  } else {
929  // Otherwise, throw out film size.
930  nassertv(cdata->_film_size_seq == 0);
931 
932  // Make sure we save the aspect ratio first.
933  do_compute_aspect_ratio(cdata);
934  do_adjust_user_flags(cdata, UF_film_width | UF_film_height | UF_vfov | UF_min_fov,
935  UF_hfov);
936  }
937  do_adjust_comp_flags(cdata, CF_mat | CF_focal_length | CF_fov | CF_film_size,
938  0);
939  // We leave CF_fov off of comp_flags, because we will still need to
940  // recompute the vertical fov. It's not exactly the same as hfov *
941  // get_aspect_ratio().
942  do_throw_change_event(cdata);
943 }
944 
945 ////////////////////////////////////////////////////////////////////
946 // Function: Lens::do_set_fov
947 // Access: Protected
948 // Description:
949 ////////////////////////////////////////////////////////////////////
950 void Lens::
951 do_set_fov(CData *cdata, const LVecBase2 &fov) {
952  nassertv(!fov.is_nan());
953  cdata->_fov = fov;
954 
955  // We can't specify all three of focal length, fov, and film size.
956  // Throw out the oldest one.
957  do_resequence_fov_triad(cdata, cdata->_fov_seq, cdata->_focal_length_seq, cdata->_film_size_seq);
958 
959  if (cdata->_focal_length_seq == 0) {
960  // Throw out focal length if it's oldest.
961  do_adjust_user_flags(cdata, UF_focal_length | UF_film_height | UF_min_fov | UF_aspect_ratio,
962  UF_hfov | UF_vfov);
963  } else {
964  // Otherwise, throw out film size.
965  nassertv(cdata->_film_size_seq == 0);
966  do_adjust_user_flags(cdata, UF_film_width | UF_film_height | UF_min_fov | UF_aspect_ratio,
967  UF_hfov | UF_vfov);
968  }
969  do_adjust_comp_flags(cdata, CF_mat | CF_focal_length | CF_film_size | CF_aspect_ratio,
970  CF_fov);
971 
972  // Also, the user has implicitly specified an aspect ratio. Make it
973  // stick until the user tells us something different.
974  do_compute_aspect_ratio(cdata);
975  do_adjust_user_flags(cdata, 0, UF_aspect_ratio);
976 
977  do_throw_change_event(cdata);
978 }
979 
980 ////////////////////////////////////////////////////////////////////
981 // Function: Lens::do_get_fov
982 // Access: Protected
983 // Description:
984 ////////////////////////////////////////////////////////////////////
985 const LVecBase2 &Lens::
986 do_get_fov(const CData *cdata) const {
987  if ((cdata->_comp_flags & CF_fov) == 0) {
988  ((Lens *)this)->do_compute_fov((CData *)cdata);
989  }
990  return cdata->_fov;
991 }
992 
993 ////////////////////////////////////////////////////////////////////
994 // Function: Lens::do_set_aspect_ratio
995 // Access: Protected
996 // Description:
997 ////////////////////////////////////////////////////////////////////
998 void Lens::
999 do_set_aspect_ratio(CData *cdata, PN_stdfloat aspect_ratio) {
1000  nassertv(!cnan(aspect_ratio));
1001  cdata->_aspect_ratio = aspect_ratio;
1002  do_adjust_user_flags(cdata, UF_film_height | UF_vfov,
1003  UF_aspect_ratio);
1004  do_adjust_comp_flags(cdata, CF_mat | CF_film_size | CF_fov | CF_focal_length,
1005  CF_aspect_ratio);
1006  do_throw_change_event(cdata);
1007 }
1008 
1009 ////////////////////////////////////////////////////////////////////
1010 // Function: Lens::do_get_aspect_ratio
1011 // Access: Protected
1012 // Description:
1013 ////////////////////////////////////////////////////////////////////
1014 PN_stdfloat Lens::
1015 do_get_aspect_ratio(const CData *cdata) const {
1016  if ((cdata->_comp_flags & CF_aspect_ratio) == 0) {
1017  ((Lens *)this)->do_compute_aspect_ratio((CData *)cdata);
1018  }
1019  return cdata->_aspect_ratio;
1020 }
1021 
1022 ////////////////////////////////////////////////////////////////////
1023 // Function: Lens::do_get_projection_mat
1024 // Access: Protected
1025 // Description:
1026 ////////////////////////////////////////////////////////////////////
1027 const LMatrix4 &Lens::
1028 do_get_projection_mat(const CData *cdata, StereoChannel channel) const {
1029  if ((cdata->_comp_flags & CF_projection_mat) == 0) {
1030  ((Lens *)this)->do_compute_projection_mat((CData *)cdata);
1031  }
1032 
1033  switch (channel) {
1034  case SC_left:
1035  return cdata->_projection_mat_left;
1036  case SC_right:
1037  return cdata->_projection_mat_right;
1038  case SC_mono:
1039  case SC_stereo:
1040  return cdata->_projection_mat;
1041  }
1042 
1043  return cdata->_projection_mat;
1044 }
1045 
1046 ////////////////////////////////////////////////////////////////////
1047 // Function: Lens::do_get_projection_mat_inv
1048 // Access: Protected
1049 // Description:
1050 ////////////////////////////////////////////////////////////////////
1051 const LMatrix4 &Lens::
1052 do_get_projection_mat_inv(const CData *cdata, StereoChannel stereo_channel) const {
1053  switch (stereo_channel) {
1054  case SC_left:
1055  {
1056  if ((cdata->_comp_flags & CF_projection_mat_left_inv) == 0) {
1057  const LMatrix4 &projection_mat_left = do_get_projection_mat(cdata, SC_left);
1058  ((CData *)cdata)->_projection_mat_left_inv.invert_from(projection_mat_left);
1059  ((Lens *)this)->do_adjust_comp_flags((CData *)cdata, 0, CF_projection_mat_left_inv);
1060  }
1061  }
1062  return cdata->_projection_mat_left_inv;
1063 
1064  case SC_right:
1065  {
1066  if ((cdata->_comp_flags & CF_projection_mat_right_inv) == 0) {
1067  const LMatrix4 &projection_mat_right = do_get_projection_mat(cdata, SC_right);
1068  ((CData *)cdata)->_projection_mat_right_inv.invert_from(projection_mat_right);
1069  ((Lens *)this)->do_adjust_comp_flags((CData *)cdata, 0, CF_projection_mat_right_inv);
1070  }
1071  }
1072  return cdata->_projection_mat_right_inv;
1073 
1074  case SC_mono:
1075  case SC_stereo:
1076  break;
1077  }
1078 
1079  if ((cdata->_comp_flags & CF_projection_mat_inv) == 0) {
1080  const LMatrix4 &projection_mat = do_get_projection_mat(cdata);
1081  ((CData *)cdata)->_projection_mat_inv.invert_from(projection_mat);
1082  ((Lens *)this)->do_adjust_comp_flags((CData *)cdata, 0, CF_projection_mat_inv);
1083  }
1084  return cdata->_projection_mat_inv;
1085 }
1086 
1087 ////////////////////////////////////////////////////////////////////
1088 // Function: Lens::do_get_film_mat
1089 // Access: Protected
1090 // Description:
1091 ////////////////////////////////////////////////////////////////////
1092 const LMatrix4 &Lens::
1093 do_get_film_mat(const CData *cdata) const {
1094  if ((cdata->_comp_flags & CF_film_mat) == 0) {
1095  ((Lens *)this)->do_compute_film_mat((CData *)cdata);
1096  }
1097  return cdata->_film_mat;
1098 }
1099 
1100 ////////////////////////////////////////////////////////////////////
1101 // Function: Lens::do_get_film_mat_inv
1102 // Access: Protected
1103 // Description:
1104 ////////////////////////////////////////////////////////////////////
1105 const LMatrix4 &Lens::
1106 do_get_film_mat_inv(const CData *cdata) const {
1107  if ((cdata->_comp_flags & CF_film_mat_inv) == 0) {
1108  const LMatrix4 &film_mat = do_get_film_mat(cdata);
1109  ((CData *)cdata)->_film_mat_inv.invert_from(film_mat);
1110  ((Lens *)this)->do_adjust_comp_flags((CData *)cdata, 0, CF_film_mat_inv);
1111  }
1112  return cdata->_film_mat_inv;
1113 }
1114 
1115 ////////////////////////////////////////////////////////////////////
1116 // Function: Lens::do_get_lens_mat
1117 // Access: Protected
1118 // Description:
1119 ////////////////////////////////////////////////////////////////////
1120 const LMatrix4 &Lens::
1121 do_get_lens_mat(const CData *cdata) const {
1122  if ((cdata->_comp_flags & CF_lens_mat) == 0) {
1123  ((Lens *)this)->do_compute_lens_mat((CData *)cdata);
1124  }
1125  return cdata->_lens_mat;
1126 }
1127 
1128 ////////////////////////////////////////////////////////////////////
1129 // Function: Lens::do_get_lens_mat_inv
1130 // Access: Protected
1131 // Description:
1132 ////////////////////////////////////////////////////////////////////
1133 const LMatrix4 &Lens::
1134 do_get_lens_mat_inv(const CData *cdata) const {
1135  if ((cdata->_comp_flags & CF_lens_mat_inv) == 0) {
1136  const LMatrix4 &lens_mat = do_get_lens_mat(cdata);
1137  ((CData *)cdata)->_lens_mat_inv.invert_from(lens_mat);
1138  ((Lens *)this)->do_adjust_comp_flags((CData *)cdata, 0, CF_lens_mat_inv);
1139  }
1140  return cdata->_lens_mat_inv;
1141 }
1142 
1143 ////////////////////////////////////////////////////////////////////
1144 // Function: Lens::do_set_interocular_distance
1145 // Access: Protected
1146 // Description:
1147 ////////////////////////////////////////////////////////////////////
1148 void Lens::
1149 do_set_interocular_distance(CData *cdata, PN_stdfloat interocular_distance) {
1150  nassertv(!cnan(interocular_distance));
1151  cdata->_interocular_distance = interocular_distance;
1152  if (cdata->_interocular_distance == 0.0f) {
1153  do_adjust_user_flags(cdata, UF_interocular_distance, 0);
1154  } else {
1155  do_adjust_user_flags(cdata, 0, UF_interocular_distance);
1156  }
1157 
1158  do_adjust_comp_flags(cdata, CF_mat, 0);
1159 }
1160 
1161 ////////////////////////////////////////////////////////////////////
1162 // Function: Lens::do_set_convergence_distance
1163 // Access: Protected
1164 // Description:
1165 ////////////////////////////////////////////////////////////////////
1166 void Lens::
1167 do_set_convergence_distance(CData *cdata, PN_stdfloat convergence_distance) {
1168  nassertv(!cnan(convergence_distance));
1169  cdata->_convergence_distance = convergence_distance;
1170  if (cdata->_convergence_distance == 0.0f) {
1171  do_adjust_user_flags(cdata, UF_convergence_distance, 0);
1172  } else {
1173  do_adjust_user_flags(cdata, 0, UF_convergence_distance);
1174  }
1175 
1176  do_adjust_comp_flags(cdata, CF_mat, 0);
1177 }
1178 
1179 ////////////////////////////////////////////////////////////////////
1180 // Function: Lens::do_set_view_mat
1181 // Access: Protected
1182 // Description:
1183 ////////////////////////////////////////////////////////////////////
1184 void Lens::
1185 do_set_view_mat(CData *cdata, const LMatrix4 &view_mat) {
1186  nassertv(!view_mat.is_nan());
1187  cdata->_lens_mat = view_mat;
1188  do_adjust_user_flags(cdata, UF_view_vector | UF_view_hpr,
1189  UF_view_mat);
1190  do_adjust_comp_flags(cdata, CF_projection_mat | CF_projection_mat_inv |
1191  CF_projection_mat_left_inv | CF_projection_mat_right_inv |
1192  CF_lens_mat_inv | CF_view_hpr | CF_view_vector,
1193  CF_lens_mat);
1194  do_throw_change_event(cdata);
1195 }
1196 
1197 ////////////////////////////////////////////////////////////////////
1198 // Function: Lens::do_get_view_mat
1199 // Access: Protected
1200 // Description:
1201 ////////////////////////////////////////////////////////////////////
1202 const LMatrix4 &Lens::
1203 do_get_view_mat(const CData *cdata) const {
1204  if ((cdata->_comp_flags & CF_lens_mat) == 0) {
1205  ((Lens *)this)->do_compute_lens_mat((CData *)cdata);
1206  }
1207  return cdata->_lens_mat;
1208 }
1209 
1210 ////////////////////////////////////////////////////////////////////
1211 // Function: Lens::do_throw_change_event
1212 // Access: Protected
1213 // Description: Throws the event associated with changing properties
1214 // on this Lens, if any.
1215 ////////////////////////////////////////////////////////////////////
1216 void Lens::
1217 do_throw_change_event(CData *cdata) {
1218  ++(cdata->_last_change);
1219 
1220  if (!cdata->_change_event.empty()) {
1221  throw_event(cdata->_change_event, this);
1222  }
1223 
1224  if (!cdata->_geom_data.is_null()) {
1225  if (cdata->_geom_data->get_ref_count() == 1) {
1226  // No one's using the data any more (there are no references to
1227  // it other than this one), so don't bother to recompute it;
1228  // just release it.
1229  cdata->_geom_data.clear();
1230  } else {
1231  // Someone still has a handle to the data, so recompute it for
1232  // them.
1233  do_define_geom_data(cdata);
1234  }
1235  }
1236 }
1237 
1238 ////////////////////////////////////////////////////////////////////
1239 // Function: Lens::do_extrude
1240 // Access: Protected, Virtual
1241 // Description: Given a 2-d point in the range (-1,1) in both
1242 // dimensions, where (0,0) is the center of the
1243 // lens and (-1,-1) is the lower-left corner,
1244 // compute the corresponding vector in space that maps
1245 // to this point, if such a vector can be determined.
1246 // The vector is returned by indicating the points on
1247 // the near plane and far plane that both map to the
1248 // indicated 2-d point.
1249 //
1250 // The z coordinate of the 2-d point is ignored.
1251 //
1252 // Returns true if the vector is defined, or false
1253 // otherwise.
1254 ////////////////////////////////////////////////////////////////////
1255 bool Lens::
1256 do_extrude(const CData *cdata,
1257  const LPoint3 &point2d, LPoint3 &near_point, LPoint3 &far_point) const {
1258  const LMatrix4 &projection_mat_inv = do_get_projection_mat_inv(cdata);
1259  {
1260  LVecBase4 full(point2d[0], point2d[1], -1.0f, 1.0f);
1261  full = projection_mat_inv.xform(full);
1262 
1263  PN_stdfloat recip_full3 = 1.0 / max((double)full[3], (double)lens_far_limit);
1264  near_point.set(full[0] * recip_full3,
1265  full[1] * recip_full3,
1266  full[2] * recip_full3);
1267  }
1268  {
1269  LVecBase4 full(point2d[0], point2d[1], 1.0f, 1.0f);
1270  full = projection_mat_inv.xform(full);
1271 
1272  // We can truncate the weight factor at near 0. If it goes too
1273  // close to zero, or becomes negative, the far plane moves out
1274  // past infinity and comes back in behind the lens, which is just
1275  // crazy. Truncating it to zero keeps the far plane from moving
1276  // too far out.
1277  PN_stdfloat recip_full3 = 1.0 / max((double)full[3], (double)lens_far_limit);
1278  far_point.set(full[0] * recip_full3,
1279  full[1] * recip_full3,
1280  full[2] * recip_full3);
1281  }
1282  return true;
1283 }
1284 
1285 ////////////////////////////////////////////////////////////////////
1286 // Function: Lens::do_extrude_depth
1287 // Access: Protected, Virtual
1288 // Description: This is the generic implementation, which is based on
1289 // do_extrude() and assumes a linear distribution of
1290 // depth values between the near and far points.
1291 ////////////////////////////////////////////////////////////////////
1292 bool Lens::
1293 do_extrude_depth(const CData *cdata,
1294  const LPoint3 &point2d, LPoint3 &point3d) const {
1295  LPoint3 near_point, far_point;
1296  bool result = extrude(point2d, near_point, far_point);
1297 
1298  // The depth point is, by convention, in the range -1 to 1. Scale
1299  // this to 0 .. 1 for the linear interpolation.
1300  PN_stdfloat t = point2d[2] * 0.5 + 0.5;
1301  point3d = near_point + (far_point - near_point) * t;
1302  return result;
1303 }
1304 
1305 ////////////////////////////////////////////////////////////////////
1306 // Function: Lens::do_extrude_depth_with_mat
1307 // Access: Protected
1308 // Description: Implements do_extrude_depth() by using the projection
1309 // matrix. This is efficient, but works only for a
1310 // linear (Perspective or Orthographic) lens.
1311 ////////////////////////////////////////////////////////////////////
1312 bool Lens::
1313 do_extrude_depth_with_mat(const CData *cdata,
1314  const LPoint3 &point2d, LPoint3 &point3d) const {
1315  const LMatrix4 &projection_mat_inv = do_get_projection_mat_inv(cdata);
1316  point3d = projection_mat_inv.xform_point_general(point2d);
1317  return true;
1318 }
1319 
1320 ////////////////////////////////////////////////////////////////////
1321 // Function: Lens::do_extrude_vec
1322 // Access: Protected, Virtual
1323 // Description: Given a 2-d point in the range (-1,1) in both
1324 // dimensions, where (0,0) is the center of the
1325 // lens and (-1,-1) is the lower-left corner,
1326 // compute the vector that corresponds to the view
1327 // direction. This will be parallel to the normal on
1328 // the surface (the far plane) corresponding to the lens
1329 // shape at this point.
1330 //
1331 // Generally, for all rational lenses, the center of the
1332 // film at (0,0) computes a vector that is in the same
1333 // direction as the vector specified by
1334 // set_view_vector().
1335 //
1336 // For all linear lenses, including perspective and
1337 // orthographic lenses, all points on the film compute
1338 // this same vector (the far plane is a flat plane, so
1339 // the normal is the same everywhere). For curved
1340 // lenses like fisheye and cylindrical lenses, different
1341 // points may compute different vectors (the far "plane"
1342 // on these lenses is a curved surface).
1343 //
1344 // The z coordinate of the 2-d point is ignored.
1345 //
1346 // Returns true if the vector is defined, or false
1347 // otherwise.
1348 ////////////////////////////////////////////////////////////////////
1349 bool Lens::
1350 do_extrude_vec(const CData *cdata, const LPoint3 &point2d, LVector3 &vec) const {
1351  vec = LVector3::forward(cdata->_cs) * do_get_lens_mat(cdata);
1352  return true;
1353 }
1354 
1355 ////////////////////////////////////////////////////////////////////
1356 // Function: Lens::do_project
1357 // Access: Protected, Virtual
1358 // Description: Given a 3-d point in space, determine the 2-d point
1359 // this maps to, in the range (-1,1) in both dimensions,
1360 // where (0,0) is the center of the lens and
1361 // (-1,-1) is the lower-left corner.
1362 //
1363 // The z coordinate will also be set to a value in the
1364 // range (-1, 1), where -1 represents a point on the near
1365 // plane, and 1 represents a point on the far plane.
1366 //
1367 // Returns true if the 3-d point is in front of the lens
1368 // and within the viewing frustum (in which case point2d
1369 // is filled in), or false otherwise (in which case
1370 // point2d will be filled in with something, which may
1371 // or may not be meaningful).
1372 ////////////////////////////////////////////////////////////////////
1373 bool Lens::
1374 do_project(const CData *cdata, const LPoint3 &point3d, LPoint3 &point2d) const {
1375  const LMatrix4 &projection_mat = do_get_projection_mat(cdata);
1376  LVecBase4 full(point3d[0], point3d[1], point3d[2], 1.0f);
1377  full = projection_mat.xform(full);
1378  if (full[3] == 0.0f) {
1379  point2d.set(0.0f, 0.0f, 0.0f);
1380  return false;
1381  }
1382  PN_stdfloat recip_full3 = 1.0f/full[3];
1383  point2d.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
1384  return
1385  (full[3] > 0.0f) &&
1386  (point2d[0] >= -1.0f) && (point2d[0] <= 1.0f) &&
1387  (point2d[1] >= -1.0f) && (point2d[1] <= 1.0f);
1388 }
1389 
1390 ////////////////////////////////////////////////////////////////////
1391 // Function: Lens::do_compute_film_size
1392 // Access: Protected, Virtual
1393 // Description: Computes the size and shape of the film behind the
1394 // camera, based on the aspect ratio and fov.
1395 ////////////////////////////////////////////////////////////////////
1396 void Lens::
1397 do_compute_film_size(CData *cdata) {
1398  if ((cdata->_user_flags & (UF_min_fov | UF_focal_length)) == (UF_min_fov | UF_focal_length)) {
1399  // If we just have a min FOV and a focal length, that determines
1400  // the smaller of the two film_sizes, and the larger is simply
1401  // chosen according to the aspect ratio.
1402  PN_stdfloat fs = fov_to_film(cdata->_min_fov, cdata->_focal_length, true);
1403  nassertv((cdata->_user_flags & UF_aspect_ratio) != 0 ||
1404  (cdata->_comp_flags & CF_aspect_ratio) != 0);
1405 
1406  if (cdata->_aspect_ratio < 1.0f) {
1407  cdata->_film_size[1] = fs / cdata->_aspect_ratio;
1408  cdata->_film_size[0] = fs;
1409 
1410  } else {
1411  cdata->_film_size[0] = fs * cdata->_aspect_ratio;
1412  cdata->_film_size[1] = fs;
1413  }
1414 
1415  } else {
1416  if ((cdata->_user_flags & UF_film_width) == 0) {
1417  if ((cdata->_user_flags & (UF_hfov | UF_focal_length)) == (UF_hfov | UF_focal_length)) {
1418  cdata->_film_size[0] = fov_to_film(cdata->_fov[0], cdata->_focal_length, true);
1419  } else {
1420  cdata->_film_size[0] = 1.0f;
1421  }
1422  }
1423 
1424  if ((cdata->_user_flags & UF_film_height) == 0) {
1425  if ((cdata->_user_flags & (UF_vfov | UF_focal_length)) == (UF_vfov | UF_focal_length)) {
1426  cdata->_film_size[1] = fov_to_film(cdata->_fov[1], cdata->_focal_length, false);
1427 
1428  } else if ((cdata->_user_flags & (UF_hfov | UF_vfov)) == (UF_hfov | UF_vfov)) {
1429  // If we don't have a focal length, but we have an explicit vfov
1430  // and hfov, we can infer the focal length is whatever makes the
1431  // film width, above, be what it is.
1432  if ((cdata->_comp_flags & CF_focal_length) == 0) {
1433  cdata->_focal_length = fov_to_focal_length(cdata->_fov[0], cdata->_film_size[0], true);
1434  do_adjust_comp_flags(cdata, 0, CF_focal_length);
1435  }
1436  cdata->_film_size[1] = fov_to_film(cdata->_fov[1], cdata->_focal_length, false);
1437 
1438  } else if ((cdata->_user_flags & UF_aspect_ratio) != 0 ||
1439  (cdata->_comp_flags & CF_aspect_ratio) != 0) {
1440  cdata->_film_size[1] = cdata->_film_size[0] / cdata->_aspect_ratio;
1441 
1442  } else {
1443  // Default is an aspect ratio of 1.
1444  cdata->_film_size[1] = cdata->_film_size[0];
1445  }
1446  }
1447  }
1448 
1449  do_adjust_comp_flags(cdata, 0, CF_film_size);
1450 }
1451 
1452 ////////////////////////////////////////////////////////////////////
1453 // Function: Lens::do_compute_focal_length
1454 // Access: Protected, Virtual
1455 // Description: Computes the focal length of the lens, based on the
1456 // fov and film size. This is based on the horizontal
1457 // dimension.
1458 ////////////////////////////////////////////////////////////////////
1459 void Lens::
1460 do_compute_focal_length(CData *cdata) {
1461  if ((cdata->_user_flags & UF_focal_length) == 0) {
1462  const LVecBase2 &film_size = do_get_film_size(cdata);
1463  const LVecBase2 &fov = do_get_fov(cdata);
1464  cdata->_focal_length = fov_to_focal_length(fov[0], film_size[0], true);
1465  }
1466 
1467  do_adjust_comp_flags(cdata, 0, CF_focal_length);
1468 }
1469 
1470 ////////////////////////////////////////////////////////////////////
1471 // Function: Lens::do_compute_fov
1472 // Access: Protected, Virtual
1473 // Description: Computes the field of view of the lens, based on the
1474 // film size and focal length.
1475 ////////////////////////////////////////////////////////////////////
1476 void Lens::
1477 do_compute_fov(CData *cdata) {
1478  const LVecBase2 &film_size = do_get_film_size(cdata);
1479 
1480  bool got_hfov = ((cdata->_user_flags & UF_hfov) != 0);
1481  bool got_vfov = ((cdata->_user_flags & UF_vfov) != 0);
1482  bool got_min_fov = ((cdata->_user_flags & UF_min_fov) != 0);
1483 
1484  if (!got_hfov && !got_vfov && !got_min_fov) {
1485  // If the user hasn't specified any FOV, we have to compute it.
1486  if ((cdata->_user_flags & UF_focal_length) != 0) {
1487  // The FOV is determined from the film size and focal length.
1488  cdata->_fov[0] = film_to_fov(film_size[0], cdata->_focal_length, true);
1489  cdata->_fov[1] = film_to_fov(film_size[1], cdata->_focal_length, true);
1490  got_hfov = true;
1491  got_vfov = true;
1492 
1493  } else {
1494  // We can't compute the FOV; take the default.
1495  cdata->_min_fov = default_fov;
1496  got_min_fov = true;
1497  }
1498  }
1499 
1500  if (got_min_fov) {
1501  // If we have just a min_fov, use it to derive whichever fov is
1502  // smaller.
1503  if (film_size[0] < film_size[1]) {
1504  cdata->_fov[0] = cdata->_min_fov;
1505  got_hfov = true;
1506  } else {
1507  cdata->_fov[1] = cdata->_min_fov;
1508  got_vfov = true;
1509  }
1510  }
1511 
1512  // Now compute whichever fov is remaining.
1513  if (!got_hfov) {
1514  if ((cdata->_user_flags & UF_focal_length) == 0 &&
1515  (cdata->_comp_flags & CF_focal_length) == 0) {
1516  // If we don't have an explicit focal length, we can infer it
1517  // from the above.
1518  nassertv(got_vfov);
1519  cdata->_focal_length = fov_to_focal_length(cdata->_fov[1], film_size[1], true);
1520  do_adjust_comp_flags(cdata, 0, CF_focal_length);
1521  }
1522  cdata->_fov[0] = film_to_fov(film_size[0], cdata->_focal_length, false);
1523  got_hfov = true;
1524  }
1525 
1526  if (!got_vfov) {
1527  if ((cdata->_user_flags & UF_focal_length) == 0 &&
1528  (cdata->_comp_flags & CF_focal_length) == 0) {
1529  // If we don't have an explicit focal length, we can infer it
1530  // from the above.
1531  nassertv(got_hfov);
1532  cdata->_focal_length = fov_to_focal_length(cdata->_fov[0], film_size[0], true);
1533  do_adjust_comp_flags(cdata, 0, CF_focal_length);
1534  }
1535  cdata->_fov[1] = film_to_fov(film_size[1], cdata->_focal_length, false);
1536  got_vfov = true;
1537  }
1538 
1539  if (!got_min_fov) {
1540  cdata->_min_fov = film_size[0] < film_size[1] ? cdata->_fov[0] : cdata->_fov[1];
1541  got_min_fov = true;
1542  }
1543 
1544  nassertv(got_hfov && got_vfov && got_min_fov);
1545  do_adjust_comp_flags(cdata, 0, CF_fov);
1546 }
1547 
1548 ////////////////////////////////////////////////////////////////////
1549 // Function: Lens::do_compute_aspect_ratio
1550 // Access: Protected, Virtual
1551 // Description: Computes the aspect ratio of the film rectangle, as a
1552 // ratio of width to height.
1553 ////////////////////////////////////////////////////////////////////
1554 void Lens::
1555 do_compute_aspect_ratio(CData *cdata) {
1556  if ((cdata->_user_flags & UF_aspect_ratio) == 0) {
1557  const LVecBase2 &film_size = do_get_film_size(cdata);
1558  if (film_size[1] == 0.0f) {
1559  cdata->_aspect_ratio = 1.0f;
1560  } else {
1561  cdata->_aspect_ratio = film_size[0] / film_size[1];
1562  }
1563  }
1564  do_adjust_comp_flags(cdata, 0, CF_aspect_ratio);
1565 }
1566 
1567 ////////////////////////////////////////////////////////////////////
1568 // Function: Lens::do_compute_view_hpr
1569 // Access: Protected, Virtual
1570 // Description: Computes the Euler angles representing the lens'
1571 // rotation.
1572 ////////////////////////////////////////////////////////////////////
1573 void Lens::
1574 do_compute_view_hpr(CData *cdata) {
1575  if ((cdata->_user_flags & UF_view_hpr) == 0) {
1576  const LMatrix4 &view_mat = do_get_view_mat(cdata);
1577  LVecBase3 scale, shear, translate;
1578  decompose_matrix(view_mat, scale, shear, cdata->_view_hpr, translate, cdata->_cs);
1579  }
1580  do_adjust_comp_flags(cdata, 0, CF_view_hpr);
1581 }
1582 
1583 ////////////////////////////////////////////////////////////////////
1584 // Function: Lens::do_compute_view_vector
1585 // Access: Protected, Virtual
1586 // Description: Computes the view vector and up vector for the lens.
1587 ////////////////////////////////////////////////////////////////////
1588 void Lens::
1589 do_compute_view_vector(CData *cdata) {
1590  if ((cdata->_user_flags & UF_view_vector) == 0) {
1591  const LMatrix4 &view_mat = do_get_view_mat(cdata);
1592  cdata->_view_vector = LVector3::forward(cdata->_cs) * view_mat;
1593  cdata->_up_vector = LVector3::up(cdata->_cs) * view_mat;
1594  }
1595  do_adjust_comp_flags(cdata, 0, CF_view_vector);
1596 }
1597 
1598 ////////////////////////////////////////////////////////////////////
1599 // Function: Lens::do_compute_projection_mat
1600 // Access: Protected, Virtual
1601 // Description: Computes the complete transformation matrix from 3-d
1602 // point to 2-d point, if the lens is linear.
1603 ////////////////////////////////////////////////////////////////////
1604 void Lens::
1605 do_compute_projection_mat(CData *lens_cdata) {
1606  // This is the implementation used by non-linear lenses. The linear
1607  // lenses (PerspectiveLens and OrthographicLens) will customize this
1608  // method appropriate for themselves.
1609 
1610  // By convention, the coordinate-system conversion is baked into the
1611  // projection mat. Our non-linear lenses are implemented with code
1612  // that assumes CS_zup_right, so we bake the appropriate rotation in
1613  // here.
1614  CoordinateSystem cs = lens_cdata->_cs;
1615  if (cs == CS_default) {
1616  cs = get_default_coordinate_system();
1617  }
1618  lens_cdata->_projection_mat = LMatrix4::convert_mat(cs, CS_zup_right);
1619  lens_cdata->_projection_mat_inv = LMatrix4::convert_mat(CS_zup_right, cs);
1620 
1621  // We don't apply any left/right offsets for non-linear lenses by
1622  // default, at least not here in the projection matrix.
1623  lens_cdata->_projection_mat_left = lens_cdata->_projection_mat_right = lens_cdata->_projection_mat;
1624  lens_cdata->_projection_mat_left_inv = lens_cdata->_projection_mat_right_inv = lens_cdata->_projection_mat_inv;
1625 
1626  do_adjust_comp_flags(lens_cdata, 0, CF_projection_mat | CF_projection_mat_inv |
1627  CF_projection_mat_left_inv | CF_projection_mat_right_inv);
1628 }
1629 
1630 ////////////////////////////////////////////////////////////////////
1631 // Function: Lens::do_compute_film_mat
1632 // Access: Protected, Virtual
1633 // Description: Computes the matrix that transforms from a point
1634 // behind the lens to a point on the film.
1635 ////////////////////////////////////////////////////////////////////
1636 void Lens::
1637 do_compute_film_mat(CData *cdata) {
1638  // The lens will return a point in the range [-film_size/2,
1639  // film_size/2] in each dimension. Convert this to [-1, 1], and
1640  // also apply the offset.
1641 
1642  // We declare these two as local variables, instead of references,
1643  // to work around a VC7 compiler bug.
1644  LVecBase2 film_size = do_get_film_size(cdata);
1645  LVector2 film_offset = do_get_film_offset(cdata);
1646 
1647  PN_stdfloat scale_x = 2.0f / film_size[0];
1648  PN_stdfloat scale_y = 2.0f / film_size[1];
1649  cdata->_film_mat.set(scale_x, 0.0f, 0.0f, 0.0f,
1650  0.0f, scale_y, 0.0f, 0.0f,
1651  0.0f, 0.0f, 1.0f, 0.0f,
1652  -film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f, 1.0f);
1653 
1654  if ((cdata->_user_flags & UF_keystone) != 0) {
1655  cdata->_film_mat = LMatrix4(1.0f, 0.0f, cdata->_keystone[0], cdata->_keystone[0],
1656  0.0f, 1.0f, cdata->_keystone[1], cdata->_keystone[1],
1657  0.0f, 0.0f, 1.0f, 0.0f,
1658  0.0f, 0.0f, 0.0f, 1.0f) * cdata->_film_mat;
1659  }
1660 
1661  if ((cdata->_user_flags & UF_custom_film_mat) != 0) {
1662  cdata->_film_mat = cdata->_film_mat * cdata->_custom_film_mat;
1663  }
1664 
1665  do_adjust_comp_flags(cdata, CF_film_mat_inv, CF_film_mat);
1666 }
1667 
1668 ////////////////////////////////////////////////////////////////////
1669 // Function: Lens::do_compute_lens_mat
1670 // Access: Protected, Virtual
1671 // Description: Computes the matrix that transforms from a point
1672 // in front of the lens to a point in space.
1673 ////////////////////////////////////////////////////////////////////
1674 void Lens::
1675 do_compute_lens_mat(CData *cdata) {
1676  if ((cdata->_user_flags & UF_view_mat) == 0) {
1677  if ((cdata->_user_flags & UF_view_hpr) != 0) {
1678  compose_matrix(cdata->_lens_mat,
1679  LVecBase3(1.0f, 1.0f, 1.0f),
1680  LVecBase3(0.0f, 0.0f, 0.0f),
1681  cdata->_view_hpr,
1682  LVecBase3(0.0f, 0.0f, 0.0f), cdata->_cs);
1683 
1684  } else if ((cdata->_user_flags & UF_view_vector) != 0) {
1685  look_at(cdata->_lens_mat, cdata->_view_vector, cdata->_up_vector, cdata->_cs);
1686 
1687  } else {
1688  cdata->_lens_mat = LMatrix4::ident_mat();
1689  }
1690  }
1691  do_adjust_comp_flags(cdata, CF_lens_mat_inv, CF_lens_mat);
1692 }
1693 
1694 ////////////////////////////////////////////////////////////////////
1695 // Function: Lens::fov_to_film
1696 // Access: Protected, Virtual
1697 // Description: Given a field of view in degrees and a focal length,
1698 // compute the corresponding width (or height) on the
1699 // film. If horiz is true, this is in the horizontal
1700 // direction; otherwise, it is in the vertical direction
1701 // (some lenses behave differently in each direction).
1702 ////////////////////////////////////////////////////////////////////
1703 PN_stdfloat Lens::
1704 fov_to_film(PN_stdfloat, PN_stdfloat, bool) const {
1705  return 1.0f;
1706 }
1707 
1708 ////////////////////////////////////////////////////////////////////
1709 // Function: Lens::fov_to_focal_length
1710 // Access: Protected, Virtual
1711 // Description: Given a field of view in degrees and a width (or
1712 // height) on the film, compute the focal length of the
1713 // lens. If horiz is true, this is in the horizontal
1714 // direction; otherwise, it is in the vertical direction
1715 // (some lenses behave differently in each direction).
1716 ////////////////////////////////////////////////////////////////////
1717 PN_stdfloat Lens::
1718 fov_to_focal_length(PN_stdfloat, PN_stdfloat, bool) const {
1719  return 1.0f;
1720 }
1721 
1722 ////////////////////////////////////////////////////////////////////
1723 // Function: Lens::film_to_fov
1724 // Access: Protected, Virtual
1725 // Description: Given a width (or height) on the film and a focal
1726 // length, compute the field of view in degrees. If
1727 // horiz is true, this is in the horizontal direction;
1728 // otherwise, it is in the vertical direction (some
1729 // lenses behave differently in each direction).
1730 ////////////////////////////////////////////////////////////////////
1731 PN_stdfloat Lens::
1732 film_to_fov(PN_stdfloat, PN_stdfloat, bool) const {
1733  return default_fov;
1734 }
1735 
1736 ////////////////////////////////////////////////////////////////////
1737 // Function: Lens::do_resequence_fov_triad
1738 // Access: Private
1739 // Description: Called whenever the user changes one of the three FOV
1740 // parameters: fov, focal length, or film size. This
1741 // rearranges the three sequence numbers so the newest
1742 // parameter has value 2, and the older parameters are
1743 // kept in sequence order.
1744 //
1745 // This is used to determine which two parameters of the
1746 // three are the most recently changed, and conversely,
1747 // which one the user has *not* changed recently. It is
1748 // this third value which should be discarded.
1749 ////////////////////////////////////////////////////////////////////
1750 void Lens::
1751 do_resequence_fov_triad(const CData *cdata, char &newest, char &older_a, char &older_b) const {
1752  nassertv(newest + older_a + older_b == 3);
1753  switch (newest) {
1754  case 0:
1755  newest = 2;
1756  older_a--;
1757  older_b--;
1758  nassertv(older_a + older_b == 1);
1759  break;
1760 
1761  case 1:
1762  newest = 2;
1763  if (older_a == 2) {
1764  nassertv(older_b == 0);
1765  older_a = 1;
1766  } else {
1767  nassertv(older_a == 0 && older_b == 2);
1768  older_b = 1;
1769  }
1770  break;
1771 
1772  case 2:
1773  nassertv(older_a + older_b == 1);
1774  break;
1775 
1776  default:
1777  gobj_cat.error()
1778  << "Invalid fov sequence numbers in lens: "
1779  << (int)newest << ", " << (int)older_a << ", " << (int)older_b << "\n";
1780  nassertv(false);
1781  return;
1782  }
1783 
1784  if (gobj_cat.is_debug()) {
1785  gobj_cat.debug()
1786  << "Lens.do_resequence_fov_triad():";
1787  for (int i = 2; i >= 0; --i) {
1788  if (cdata->_fov_seq == i) {
1789  gobj_cat.debug(false)
1790  << " fov";
1791  } else if (cdata->_focal_length_seq == i) {
1792  gobj_cat.debug(false)
1793  << " focal_length";
1794  } else if (cdata->_film_size_seq == i) {
1795  gobj_cat.debug(false)
1796  << " film_size";
1797  }
1798  }
1799  gobj_cat.debug(false)
1800  << "\n";
1801  }
1802 }
1803 
1804 ////////////////////////////////////////////////////////////////////
1805 // Function: Lens::do_define_geom_data
1806 // Access: Private
1807 // Description: Adjusts (or defines for the first time) all the
1808 // vertices in the _geom_data to match the properties of
1809 // the lens. This will update the visual representation
1810 // of the lens's frustum to match the changing
1811 // parameters. Returns the number of line segments per
1812 // edge.
1813 ////////////////////////////////////////////////////////////////////
1814 int Lens::
1815 do_define_geom_data(CData *cdata) {
1816  int num_segments = 1;
1817  if (!is_linear()) {
1818  num_segments = lens_geom_segments;
1819  }
1820 
1821  if (cdata->_geom_data == (GeomVertexData *)NULL) {
1822  cdata->_geom_data = new GeomVertexData
1823  ("lens", GeomVertexFormat::get_v3(),
1824  Geom::UH_dynamic);
1825  }
1826  cdata->_geom_data->unclean_set_num_rows(num_segments * 8 + 2);
1827 
1828  GeomVertexWriter vertex(cdata->_geom_data, InternalName::get_vertex());
1829  LPoint3 near_point, far_point;
1830  for (int si = 0; si < num_segments; si++) {
1831  PN_stdfloat t = 2.0f * (PN_stdfloat)si / (PN_stdfloat)num_segments;
1832 
1833  // Upper left, top edge.
1834  LPoint3 p1(-1.0f + t, 1.0f, 0.0f);
1835  if (!do_extrude(cdata, p1, near_point, far_point)) {
1836  // Hey, this point is off the lens! Can't do a frustum.
1837  return 0;
1838  }
1839  vertex.set_data3(near_point);
1840  vertex.set_data3(far_point);
1841 
1842  // Upper right, right edge.
1843  LPoint3 p2(1.0f, 1.0f - t, 0.0f);
1844  if (!do_extrude(cdata, p2, near_point, far_point)) {
1845  // Hey, this point is off the lens! Can't do a frustum.
1846  return 0;
1847  }
1848  vertex.set_data3(near_point);
1849  vertex.set_data3(far_point);
1850 
1851  // Lower right, bottom edge.
1852  LPoint3 p3(1.0f - t, -1.0f, 0.0f);
1853  if (!do_extrude(cdata, p3, near_point, far_point)) {
1854  // Hey, this point is off the lens! Can't do a frustum.
1855  return 0;
1856  }
1857  vertex.set_data3(near_point);
1858  vertex.set_data3(far_point);
1859 
1860  // Lower left, left edge.
1861  LPoint3 p4(-1.0f, -1.0f + t, 0.0f);
1862  if (!do_extrude(cdata, p4, near_point, far_point)) {
1863  // Hey, this point is off the lens! Can't do a frustum.
1864  return 0;
1865  }
1866  vertex.set_data3(near_point);
1867  vertex.set_data3(far_point);
1868  }
1869 
1870 
1871  // Finally, add one more pair for the viewing axis (or more specifically, the
1872  // center of the lens).
1873  LPoint3 pc(0);
1874  if (!do_extrude(cdata, pc, near_point, far_point)) {
1875  vertex.set_data3(0.0f, 0.0f, 0.0f);
1876  vertex.set_data3(0.0f, 0.0f, 0.0f);
1877  } else {
1878  vertex.set_data3(near_point);
1879  vertex.set_data3(far_point);
1880  }
1881 
1882  return num_segments;
1883 }
1884 
1885 ////////////////////////////////////////////////////////////////////
1886 // Function: Lens::build_shear_mat
1887 // Access: Private, Static
1888 // Description: A support function for set_frustum_from_corners(),
1889 // this computes a matrix that will shear the four
1890 // indicated points to the most nearly rectangular.
1891 ////////////////////////////////////////////////////////////////////
1892 void Lens::
1893 build_shear_mat(LMatrix4 &shear_mat,
1894  const LPoint3 &cul, const LPoint3 &cur,
1895  const LPoint3 &cll, const LPoint3 &clr) {
1896  // Fit a parallelogram around these four points.
1897 
1898  // Put the points in an array so we can rotate it around to find
1899  // the longest edge.
1900  LPoint3 points[4] = {
1901  cul, cur, clr, cll
1902  };
1903 
1904  PN_stdfloat max_edge_length = -1.0f;
1905  int base_edge = -1;
1906  for (int i = 0; i < 4; i++) {
1907  LVector3 edge = points[(i + 1) % 4] - points[i];
1908  PN_stdfloat length = edge.length_squared();
1909  if (length > max_edge_length) {
1910  base_edge = i;
1911  max_edge_length = length;
1912  }
1913  }
1914 
1915  const LPoint3 &base_origin = points[base_edge];
1916  LVector3 base_vec = points[(base_edge + 1) % 4] - base_origin;
1917 
1918  PN_stdfloat base_edge_length = csqrt(max_edge_length);
1919 
1920  // The longest edge is the base of our parallelogram. The parallel
1921  // edge must pass through the point furthest from this edge.
1922 
1923  int a = (base_edge + 2) % 4;
1924  int b = (base_edge + 3) % 4;
1925 
1926  PN_stdfloat a_dist = sqr_dist_to_line(points[a], base_origin, base_vec);
1927  PN_stdfloat b_dist = sqr_dist_to_line(points[b], base_origin, base_vec);
1928 
1929  int far_point;
1930  PN_stdfloat dist;
1931  if (a_dist > b_dist) {
1932  far_point = a;
1933  dist = csqrt(a_dist);
1934  } else {
1935  far_point = b;
1936  dist = csqrt(b_dist);
1937  }
1938 
1939  // Try to make the parallelogram as nearly rectangular as possible.
1940  // How suitable is a true rectangle?
1941  LVector3 perpendic = base_vec.cross(LVector3(0.0f, -1.0f, 0.0f));
1942  perpendic.normalize();
1943  perpendic *= dist;
1944  LPoint3 parallel_origin = points[base_edge] + perpendic;
1945 
1946  // It follows that far_point is on the line passing through the
1947  // parallel edge. Is it within the endpoints?
1948  LVector3 base_norm_vec = base_vec / base_edge_length;
1949 
1950  LVector3 far_point_delta = points[far_point] - parallel_origin;
1951  PN_stdfloat far_point_pos = far_point_delta.dot(base_norm_vec);
1952 
1953  if (far_point_pos < 0.0f) {
1954  // We have to slide the parallel_origin back to include far_point.
1955  parallel_origin += base_norm_vec * far_point_pos;
1956 
1957  } else if (far_point_pos > base_edge_length) {
1958  // We have to slide the parallel_origin forward to include
1959  // far_point.
1960  parallel_origin += base_norm_vec * (far_point_pos - base_edge_length);
1961  }
1962 
1963  // Finally, is the other point within the parallelogram?
1964  PN_stdfloat t;
1965  PN_stdfloat Ox = parallel_origin[0];
1966  PN_stdfloat Oy = parallel_origin[2];
1967  PN_stdfloat Vx = base_vec[0];
1968  PN_stdfloat Vy = base_vec[2];
1969  PN_stdfloat Ax, Ay, Bx, By;
1970 
1971  if (far_point == a) {
1972  // near point is b
1973  LVector3 v = points[b] - base_origin;
1974  Ax = points[b][0];
1975  Ay = points[b][2];
1976  Bx = v[0];
1977  By = v[2];
1978  } else {
1979  // near point is a
1980  LVector3 v = points[a] - (base_origin + base_vec);
1981  Ax = points[a][0];
1982  Ay = points[a][2];
1983  Bx = v[0];
1984  By = v[2];
1985  }
1986  t = ((Ox - Ax) * By + (Ay - Oy) * Bx) / (Bx * Vy - By * Vx);
1987 
1988  if (t < 0.0f) {
1989  // We need to slide the parallel_origin back to include
1990  // the near point.
1991  parallel_origin += base_vec * t;
1992  } else if (t > 1.0f) {
1993  // We need to slide the parallel_origin forward to include the far
1994  // point.
1995  parallel_origin += base_vec * (1.0f - t);
1996  }
1997 
1998  LVector3 adjacent_norm_vec = parallel_origin - base_origin;
1999  adjacent_norm_vec.normalize();
2000 
2001  // Now we've defined a parallelogram that includes all four points,
2002  // and we're ready to build a shear transform.
2003  shear_mat = LMatrix4::ident_mat();
2004 
2005  // The edges of the parallelogram become the axes.
2006  switch (base_edge) {
2007  case 0:
2008  // The base_origin is the upper-left corner. X axis is base_norm_vec,
2009  // Z axis is -adjacent_norm_vec.
2010  shear_mat.set_row(0, base_norm_vec);
2011  shear_mat.set_row(2, -adjacent_norm_vec);
2012  break;
2013 
2014  case 1:
2015  // The base_origin is the upper-right corner. X axis is
2016  // -adjacent_norm_vec, Z axis is -base_norm_vec.
2017  shear_mat.set_row(0, -adjacent_norm_vec);
2018  shear_mat.set_row(2, -base_norm_vec);
2019  break;
2020 
2021  case 2:
2022  // The base_origin is the lower-right corner. X axis is
2023  // -base_norm_vec, Z axis is adjacent_norm_vec.
2024  shear_mat.set_row(0, -base_norm_vec);
2025  shear_mat.set_row(2, adjacent_norm_vec);
2026  break;
2027 
2028  case 3:
2029  // The base_origin is the lower-left corner. X axis is
2030  // adjacent_norm_vec, Z axis is base_norm_vec.
2031  shear_mat.set_row(0, adjacent_norm_vec);
2032  shear_mat.set_row(2, base_norm_vec);
2033  break;
2034 
2035  default:
2036  nassertv(false);
2037  }
2038 }
2039 
2040 ////////////////////////////////////////////////////////////////////
2041 // Function: Lens::sqr_dist_to_line
2042 // Access: Private, Static
2043 // Description: A support function for build_shear_mat(), this
2044 // computes the minimum distance from a point to a line,
2045 // and returns the distance squared.
2046 ////////////////////////////////////////////////////////////////////
2047 PN_stdfloat Lens::
2048 sqr_dist_to_line(const LPoint3 &point, const LPoint3 &origin,
2049  const LVector3 &vec) {
2050  LVector3 norm = vec;
2051  norm.normalize();
2052  LVector3 d = point - origin;
2053  PN_stdfloat hyp_2 = d.length_squared();
2054  PN_stdfloat leg = d.dot(norm);
2055  return hyp_2 - leg * leg;
2056 }
2057 
2058 ////////////////////////////////////////////////////////////////////
2059 // Function: Lens::write_datagram
2060 // Access: Public, Virtual
2061 // Description: Writes the contents of this object to the datagram
2062 // for shipping out to a Bam file.
2063 ////////////////////////////////////////////////////////////////////
2064 void Lens::
2066  TypedWritable::write_datagram(manager, dg);
2067  manager->write_cdata(dg, _cycler);
2068 }
2069 
2070 ////////////////////////////////////////////////////////////////////
2071 // Function: Lens::fillin
2072 // Access: Protected
2073 // Description: This internal function is called by make_from_bam to
2074 // read in all of the relevant data from the BamFile for
2075 // the new Lens.
2076 ////////////////////////////////////////////////////////////////////
2077 void Lens::
2078 fillin(DatagramIterator &scan, BamReader *manager) {
2079  TypedWritable::fillin(scan, manager);
2080  manager->read_cdata(scan, _cycler);
2081 }
2082 
2083 ////////////////////////////////////////////////////////////////////
2084 // Function: Lens::CData::Constructor
2085 // Access: Public
2086 // Description:
2087 ////////////////////////////////////////////////////////////////////
2088 Lens::CData::
2089 CData() {
2090  clear();
2091 }
2092 
2093 ////////////////////////////////////////////////////////////////////
2094 // Function: Lens::CData::Copy Constructor
2095 // Access: Public
2096 // Description:
2097 ////////////////////////////////////////////////////////////////////
2098 Lens::CData::
2099 CData(const Lens::CData &copy) {
2100  _change_event = copy._change_event;
2101  _cs = copy._cs;
2102  _film_size = copy._film_size;
2103  _film_offset = copy._film_offset;
2104  _focal_length = copy._focal_length;
2105  _fov = copy._fov;
2106  _min_fov = copy._min_fov;
2107  _aspect_ratio = copy._aspect_ratio;
2108  _near_distance = copy._near_distance;
2109  _far_distance = copy._far_distance;
2110 
2111  _view_hpr = copy._view_hpr;
2112  _view_vector = copy._view_vector;
2113  _interocular_distance = copy._interocular_distance;
2114  _convergence_distance = copy._convergence_distance;
2115  _keystone = copy._keystone;
2116 
2117  // This matrix might have been explicitly set by the user (if
2118  // UF_view_mat is applied), so we must preserve it. Other matrices
2119  // are implicitly computed.
2120  _lens_mat = copy._lens_mat;
2121 
2122  _user_flags = copy._user_flags;
2123  _comp_flags = 0;
2124 
2125  _focal_length_seq = copy._focal_length_seq;
2126  _fov_seq = copy._fov_seq;
2127  _film_size_seq = copy._film_size_seq;
2128 
2129  _geom_data = copy._geom_data;
2130 }
2131 
2132 ////////////////////////////////////////////////////////////////////
2133 // Function: Lens::CData::make_copy
2134 // Access: Public, Virtual
2135 // Description:
2136 ////////////////////////////////////////////////////////////////////
2137 CycleData *Lens::CData::
2138 make_copy() const {
2139  return new CData(*this);
2140 }
2141 
2142 ////////////////////////////////////////////////////////////////////
2143 // Function: Lens::CData::write_datagram
2144 // Access: Public, Virtual
2145 // Description: Writes the contents of this object to the datagram
2146 // for shipping out to a Bam file.
2147 ////////////////////////////////////////////////////////////////////
2148 void Lens::CData::
2149 write_datagram(BamWriter *manager, Datagram &dg) const {
2150  dg.add_string(_change_event);
2151  dg.add_uint8((int)_cs);
2152  _film_size.write_datagram(dg);
2153  _film_offset.write_datagram(dg);
2154  dg.add_stdfloat(_focal_length);
2155  _fov.write_datagram(dg);
2156  dg.add_stdfloat(_aspect_ratio);
2157  dg.add_stdfloat(_near_distance);
2158  dg.add_stdfloat(_far_distance);
2159  dg.add_uint16(_user_flags);
2160 }
2161 
2162 ////////////////////////////////////////////////////////////////////
2163 // Function: Lens::CData::fillin
2164 // Access: Public, Virtual
2165 // Description: This internal function is called by make_from_bam to
2166 // read in all of the relevant data from the BamFile for
2167 // the new Geom.
2168 ////////////////////////////////////////////////////////////////////
2169 void Lens::CData::
2170 fillin(DatagramIterator &scan, BamReader *manager) {
2171  _change_event = scan.get_string();
2172  _cs = (CoordinateSystem)scan.get_uint8();
2173  _film_size.read_datagram(scan);
2174  _film_offset.read_datagram(scan);
2175  _focal_length = scan.get_stdfloat();
2176  _fov.read_datagram(scan);
2177  _aspect_ratio = scan.get_stdfloat();
2178  _near_distance = scan.get_stdfloat();
2179  _far_distance = scan.get_stdfloat();
2180  _user_flags = scan.get_uint16();
2181 
2182  _comp_flags = 0;
2183 }
2184 
2185 ////////////////////////////////////////////////////////////////////
2186 // Function: Lens::CData::clear
2187 // Access: Public
2188 // Description:
2189 ////////////////////////////////////////////////////////////////////
2190 void Lens::CData::
2191 clear() {
2192  _change_event = "";
2193  _cs = CS_default;
2194  _film_size.set(1.0f, 1.0f);
2195  _film_offset.set(0.0f, 0.0f);
2196  _focal_length = 1.0f;
2197  _fov.set(default_fov, default_fov);
2198  _aspect_ratio = 1.0f;
2199  _near_distance = default_near;
2200  _far_distance = default_far;
2201  _view_hpr.set(0.0f, 0.0f, 0.0f);
2202  _view_vector.set(0.0f, 1.0f, 0.0f);
2203  _up_vector.set(0.0f, 0.0f, 1.0f);
2204  _keystone.set(0.0f, 0.0f);
2205  _custom_film_mat = LMatrix4::ident_mat();
2206 
2207  _user_flags = 0;
2208  _comp_flags = CF_fov;
2209 
2210  _interocular_distance = 0.0;
2211  _convergence_distance = 0.0;
2212 
2213  if (default_keystone.has_value()) {
2214  _keystone.set(default_keystone[0], default_keystone[1]);
2215  _user_flags |= UF_keystone;
2216  }
2217 
2218  // Assign an initial arbitrary sequence to these three.
2219  _film_size_seq = 0;
2220  _focal_length_seq = 1;
2221  _fov_seq = 2;
2222 }
bool invert_affine_from(const LMatrix4f &other)
Performs an invert of the indicated matrix, storing the result in this matrix.
Definition: lmatrix.h:2249
virtual bool is_linear() const
Returns true if the lens represents a linear projection (e.g.
Definition: lens.cxx:615
float length_squared() const
Returns the square of the vector&#39;s length, cheap and easy.
Definition: lvecBase3.h:749
const CycleDataType * p() const
This allows the CycleDataReader to be passed to any function that expects a const CycleDataType point...
static const LMatrix4f & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:903
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
void set_keystone(const LVecBase2 &keystone)
Indicates the ratio of keystone correction to perform on the lens, in each of three axes...
Definition: lens.cxx:316
void add_uint8(PN_uint8 value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:138
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
void add_string(const string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:351
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:45
virtual bool is_orthographic() const
Returns true if the lens represents a orthographic projection (i.e.
Definition: lens.cxx:639
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
void set_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:753
LPoint3 get_nodal_point() const
Returns the center point of the lens: the point from which the lens is viewing.
Definition: lens.cxx:277
void clear()
Resets all lens parameters to their initial default settings.
Definition: lens.cxx:88
const LVecBase2 & get_fov() const
Returns the horizontal and vertical film size of the virtual film.
Definition: lens.I:423
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:398
void set_coordinate_system(CoordinateSystem cs)
Specifies the coordinate system that all 3-d computations are performed within for this Lens...
Definition: lens.cxx:74
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
Definition: lvecBase3.h:464
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
static const LMatrix4f & convert_mat(CoordinateSystem from, CoordinateSystem to)
Returns a matrix that transforms from the indicated coordinate system to the indicated coordinate sys...
Definition: lmatrix.cxx:656
bool is_nan() const
Returns true if any component of the matrix is not-a-number, false otherwise.
Definition: lmatrix.h:1417
string get_string()
Extracts a variable-length string.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class&#39;s make_from_bam() method to read in all...
PN_uint16 get_uint16()
Extracts an unsigned 16-bit integer.
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
static PN_stdfloat get_default_far()
Returns the default far plane distance that will be assigned to each newly-created lens...
Definition: lens.cxx:179
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
const LVector3 & get_up_vector() const
Returns the axis perpendicular to the camera&#39;s view vector that indicates the "up" direction...
Definition: lens.cxx:262
static LVector3f forward(CoordinateSystem cs=CS_default)
Returns the forward vector for the given coordinate system.
Definition: lvector3.h:579
void add_stdfloat(PN_stdfloat value)
Adds either a 32-bit or a 64-bit floating-point number, according to set_stdfloat_double().
Definition: datagram.I:240
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
void set_frustum_from_corners(const LVecBase3 &ul, const LVecBase3 &ur, const LVecBase3 &ll, const LVecBase3 &lr, int flags)
Sets up the lens to use the frustum defined by the four indicated points.
Definition: lens.cxx:443
void set_view_hpr(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the direction in which the lens is facing.
Definition: lens.I:549
virtual bool is_perspective() const
Returns true if the lens represents a perspective projection (i.e.
Definition: lens.cxx:627
const LVecBase3 & get_view_hpr() const
Returns the direction in which the lens is facing.
Definition: lens.cxx:211
PN_stdfloat get_min_fov() const
Returns the field of view of the narrowest dimension of the window.
Definition: lens.cxx:150
LVecBase3f xform_point(const LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
Definition: lmatrix.h:1667
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition: lens.cxx:2065
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
void set_min_fov(PN_stdfloat min_fov)
Sets the field of view of the smallest dimension of the window.
Definition: lens.cxx:113
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
bool invert_from(const LMatrix4f &other)
Computes the inverse of the other matrix, and stores the result in this matrix.
Definition: lmatrix.h:2173
This is the base class for all two-component vectors and points.
Definition: lvecBase2.h:105
static PN_stdfloat get_default_near()
Returns the default near plane distance that will be assigned to each newly-created lens...
Definition: lens.cxx:167
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
LVecBase4f xform(const LVecBase4f &v) const
4-component vector or point times matrix.
Definition: lmatrix.h:1647
void add_uint16(PN_uint16 value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:181
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This is a two-component vector offset.
Definition: lvector2.h:91
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set...
Defines a series of line strips.
void recompute_all()
Forces all internal parameters of the Lens to be recomputed.
Definition: lens.cxx:601
bool extrude(const LPoint2 &point2d, LPoint3 &near_point, LPoint3 &far_point) const
Given a 2-d point in the range (-1,1) in both dimensions, where (0,0) is the center of the lens and (...
Definition: lens.I:32
void clear_keystone()
Disables the lens keystone correction.
Definition: lens.cxx:333
void set_row(int row, const LVecBase4f &v)
Replaces the indicated row of the matrix.
Definition: lmatrix.h:1187
bool is_nan() const
Returns true if any component of the vector is not-a-number, false otherwise.
Definition: lvecBase2.h:431
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
void set_view_vector(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat i, PN_stdfloat j, PN_stdfloat k)
Specifies the direction in which the lens is facing by giving an axis to look along, and a perpendicular (or at least non-parallel) up axis.
Definition: lens.I:563
LVecBase3f get_row3(int row) const
Retrieves the row column of the matrix as a 3-component vector, ignoring the last column...
Definition: lmatrix.h:1312
void clear_custom_film_mat()
Disables the lens custom_film_mat correction.
Definition: lens.cxx:373
static LVector3f up(CoordinateSystem cs=CS_default)
Returns the up vector for the given coordinate system.
Definition: lvector3.h:541
const LVector3 & get_view_vector() const
Returns the axis along which the lens is facing.
Definition: lens.cxx:247
LVecBase3f xform_point_general(const LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
Definition: lmatrix.h:1692
This defines a bounding convex hexahedron.
void set_custom_film_mat(const LMatrix4 &custom_film_mat)
Specifies a custom matrix to transform the points on the film after they have been converted into nom...
Definition: lens.cxx:356
const LMatrix4 & get_view_mat() const
Returns the direction in which the lens is facing.
Definition: lens.I:679
void clear_view_mat()
Resets the lens transform to identity.
Definition: lens.cxx:287