Panda3D
Loading...
Searching...
No Matches
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
26using std::max;
27using std::min;
28
29TypeHandle Lens::_type_handle;
30TypeHandle Lens::CData::_type_handle;
31
32/**
33 *
34 */
35Lens::
36Lens() {
37 clear();
38}
39
40/**
41 *
42 */
43Lens::
44Lens(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 */
53void Lens::
54operator = (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 */
66void Lens::
67set_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 */
78clear() {
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 */
98void Lens::
99set_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 */
133PN_stdfloat Lens::
134get_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 */
147PN_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 */
156PN_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 */
167void Lens::
168set_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 */
182const LVecBase3 &Lens::
183get_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 */
198set_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 */
213const LVector3 &Lens::
214get_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 */
226const LVector3 &Lens::
227get_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 */
239LPoint3 Lens::
240get_nodal_point() const {
241 return get_view_mat().get_row3(3);
242}
243
244/**
245 * Resets the lens transform to identity.
246 */
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 */
271void Lens::
272set_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 */
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 */
306set_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 */
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 * arbitrary, 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 */
375set_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 */
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 */
540is_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 */
549is_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 */
558is_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 */
567PT(Geom) Lens::
568make_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 */
642make_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 */
681void Lens::
682output(std::ostream &out) const {
683 out << get_type();
684}
685
686/**
687 *
688 */
689void Lens::
690write(std::ostream &out, int indent_level) const {
691 indent(out, indent_level) << get_type() << " fov = " << get_fov() << "\n";
692}
693
694/**
695 *
696 */
697void Lens::
698do_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 */
724void Lens::
725do_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 */
757const LVecBase2 &Lens::
758do_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 */
771void Lens::
772do_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 */
799PN_stdfloat Lens::
800do_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 */
810void Lens::
811do_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 */
843void Lens::
844do_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 */
876const LVecBase2 &Lens::
877do_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 */
887void Lens::
888do_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 */
901PN_stdfloat Lens::
902do_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 */
912const LMatrix4 &Lens::
913do_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 */
934const LMatrix4 &Lens::
935do_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 */
973const LMatrix4 &Lens::
974do_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 */
984const LMatrix4 &Lens::
985do_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 */
997const LMatrix4 &Lens::
998do_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 */
1008const LMatrix4 &Lens::
1009do_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 */
1021void Lens::
1022do_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 */
1037void Lens::
1038do_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 */
1053void Lens::
1054do_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 */
1069const LMatrix4 &Lens::
1070do_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 */
1080void Lens::
1081do_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 */
1111bool Lens::
1112do_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 */
1145bool Lens::
1146do_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 */
1162bool Lens::
1163do_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 */
1191bool Lens::
1192do_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 */
1211bool Lens::
1212do_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 */
1232void Lens::
1233do_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 */
1292void Lens::
1293do_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 */
1307void Lens::
1308do_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 */
1382void Lens::
1383do_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 */
1398void Lens::
1399do_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 */
1411void Lens::
1412do_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 */
1425void Lens::
1426do_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 */
1454void Lens::
1455do_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 */
1489void Lens::
1490do_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 */
1515PN_stdfloat Lens::
1516fov_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 */
1526PN_stdfloat Lens::
1527fov_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 */
1537PN_stdfloat Lens::
1538film_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 */
1552void Lens::
1553do_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 */
1612int Lens::
1613do_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 */
1687void Lens::
1688build_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 */
1836PN_stdfloat Lens::
1837sqr_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 */
1852write_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 */
1861void Lens::
1862fillin(DatagramIterator &scan, BamReader *manager) {
1863 TypedWritable::fillin(scan, manager);
1864 manager->read_cdata(scan, _cycler);
1865}
1866
1867/**
1868 *
1869 */
1870Lens::CData::
1871CData() {
1872 clear();
1873}
1874
1875/**
1876 *
1877 */
1878Lens::CData::
1879CData(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 */
1915CycleData *Lens::CData::
1916make_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 */
1924void Lens::CData::
1925write_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 */
1971void Lens::CData::
1972fillin(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 */
2017void Lens::CData::
2018clear() {
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.
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.
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.
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.