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::
157 get_default_far() {
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::
214 get_view_vector() const {
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::
248 clear_view_mat() {
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::
287 clear_keystone() {
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::
529 recompute_all() {
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::
558 is_orthographic() const {
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 - NEARLY_ZERO(PN_stdfloat)) && (point2d[0] <= 1.0f + NEARLY_ZERO(PN_stdfloat)) &&
1225  (point2d[1] >= -1.0f - NEARLY_ZERO(PN_stdfloat)) && (point2d[1] <= 1.0f + NEARLY_ZERO(PN_stdfloat));
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  */
1852 write_datagram(BamWriter *manager, Datagram &dg) {
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 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:83
void read_cdata(DatagramIterator &scan, PipelineCyclerBase &cycler)
Reads in the indicated CycleData object.
Definition: bamReader.cxx:695
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being written.
Definition: bamWriter.I:59
This defines a bounding convex hexahedron.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
bool has_value() const
Returns true if this variable has an explicit value, either from a prc file or locally set,...
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
const CycleDataType * p() const
This allows the CycleDataReader to be passed to any function that expects a const CycleDataType point...
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
PN_stdfloat get_stdfloat()
Extracts either a 32-bit or a 64-bit floating-point number, according to Datagram::set_stdfloat_doubl...
uint16_t get_uint16()
Extracts an unsigned 16-bit integer.
std::string get_string()
Extracts a variable-length string.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
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
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
Defines a series of line strips.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition: geom.h:54
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
get_view_hpr
Returns the direction in which the lens is facing.
Definition: lens.h:122
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
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
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
get_view_mat
Returns the direction in which the lens is facing.
Definition: lens.h:141
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()
Resets all lens parameters to their initial default settings.
Definition: lens.cxx:78
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
virtual bool is_linear() const
Returns true if the lens represents a linear projection (e.g.
Definition: lens.cxx:540
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 clear_keystone()
Disables the lens keystone correction.
Definition: lens.cxx:287
set_view_hpr
Sets the direction in which the lens is facing.
Definition: lens.h:122
set_keystone
Indicates the ratio of keystone correction to perform on the lens, in each of three axes.
Definition: lens.h:146
get_min_fov
Returns the field of view of the narrowest dimension of the window.
Definition: lens.h:102
void recompute_all()
Forces all internal parameters of the Lens to be recomputed.
Definition: lens.cxx:529
set_coordinate_system
Specifies the coordinate system that all 3-d computations are performed within for this Lens.
Definition: lens.h:74
virtual bool is_orthographic() const
Returns true if the lens represents a orthographic projection (i.e.
Definition: lens.cxx:558
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
virtual bool is_perspective() const
Returns true if the lens represents a perspective projection (i.e.
Definition: lens.cxx:549
set_min_fov
Sets the field of view of the smallest dimension of the window.
Definition: lens.h:102
void clear_view_mat()
Resets the lens transform to identity.
Definition: lens.cxx:248
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
get_nodal_point
Returns the center point of the lens: the point from which the lens is viewing.
Definition: lens.h:129
get_fov
Returns the horizontal and vertical film size of the virtual film.
Definition: lens.h:101
void clear_custom_film_mat()
Disables the lens custom_film_mat correction.
Definition: lens.cxx:321
const LVector3 & get_view_vector() const
Returns the axis along which the lens is facing.
Definition: lens.cxx:214
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.