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