mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 02:42:49 -04:00
2111 lines
75 KiB
C++
2111 lines
75 KiB
C++
// Filename: lens.cxx
|
|
// Created by: drose (18Feb99)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
//
|
|
// All use of this software is subject to the terms of the revised BSD
|
|
// license. You should have received a copy of this license along
|
|
// with this source code in a file named "LICENSE."
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "lens.h"
|
|
#include "throw_event.h"
|
|
#include "compose_matrix.h"
|
|
#include "look_at.h"
|
|
#include "geom.h"
|
|
#include "geomLinestrips.h"
|
|
#include "geomVertexWriter.h"
|
|
#include "boundingHexahedron.h"
|
|
#include "indent.h"
|
|
#include "config_gobj.h"
|
|
#include "plane.h"
|
|
|
|
TypeHandle Lens::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
Lens::
|
|
Lens() {
|
|
clear();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::Copy Constructor
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
Lens::
|
|
Lens(const Lens ©) {
|
|
(*this) = copy;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::Copy Assignment Operator
|
|
// Access: Public
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
operator = (const Lens ©) {
|
|
_change_event = copy._change_event;
|
|
_cs = copy._cs;
|
|
_film_size = copy._film_size;
|
|
_film_offset = copy._film_offset;
|
|
_focal_length = copy._focal_length;
|
|
_fov = copy._fov;
|
|
_aspect_ratio = copy._aspect_ratio;
|
|
_near_distance = copy._near_distance;
|
|
_far_distance = copy._far_distance;
|
|
|
|
_view_hpr = copy._view_hpr;
|
|
_view_vector = copy._view_vector;
|
|
_interocular_distance = copy._interocular_distance;
|
|
_convergence_distance = copy._convergence_distance;
|
|
_keystone = copy._keystone;
|
|
|
|
_user_flags = copy._user_flags;
|
|
_comp_flags = 0;
|
|
|
|
_focal_length_seq = copy._focal_length_seq;
|
|
_fov_seq = copy._fov_seq;
|
|
_film_size_seq = copy._film_size_seq;
|
|
|
|
// We don't copy the _geom_data. That's unique to each Lens.
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_coordinate_system
|
|
// Access: Published
|
|
// Description: Specifies the coordinate system that all 3-d
|
|
// computations are performed within for this
|
|
// Lens. Normally, this is CS_default.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_coordinate_system(CoordinateSystem cs) {
|
|
_cs = cs;
|
|
adjust_comp_flags(CF_mat | CF_view_hpr | CF_view_vector, 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::clear
|
|
// Access: Published
|
|
// Description: Resets all lens parameters to their initial default
|
|
// settings.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
clear() {
|
|
_change_event = "";
|
|
_cs = CS_default;
|
|
_film_size.set(1.0f, 1.0f);
|
|
_film_offset.set(0.0f, 0.0f);
|
|
_focal_length = 1.0f;
|
|
_fov.set(default_fov, default_fov);
|
|
_aspect_ratio = 1.0f;
|
|
_near_distance = default_near;
|
|
_far_distance = default_far;
|
|
_view_hpr.set(0.0f, 0.0f, 0.0f);
|
|
_view_vector.set(0.0f, 1.0f, 0.0f);
|
|
_up_vector.set(0.0f, 0.0f, 1.0f);
|
|
_keystone.set(0.0f, 0.0f);
|
|
|
|
_user_flags = 0;
|
|
_comp_flags = CF_fov;
|
|
|
|
set_interocular_distance(default_iod);
|
|
set_convergence_distance(default_converge);
|
|
|
|
if (default_keystone.has_value()) {
|
|
_keystone.set(default_keystone[0], default_keystone[1]);
|
|
_user_flags |= UF_keystone;
|
|
}
|
|
|
|
// Assign an initial arbitrary sequence to these three.
|
|
_film_size_seq = 0;
|
|
_focal_length_seq = 1;
|
|
_fov_seq = 2;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_film_size
|
|
// Access: Published
|
|
// Description: Sets the horizontal size of the film without changing
|
|
// its shape. The aspect ratio remains unchanged; this
|
|
// computes the vertical size of the film to
|
|
// automatically maintain the aspect ratio.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_film_size(float width) {
|
|
_film_size.set(width, width / get_aspect_ratio());
|
|
|
|
// We can't specify all three of focal length, fov, and film size.
|
|
// Throw out the oldest one.
|
|
resequence_fov_triad(_film_size_seq, _focal_length_seq, _fov_seq);
|
|
|
|
if (_fov_seq == 0) {
|
|
// Throw out fov if it's oldest.
|
|
adjust_user_flags(UF_hfov | UF_vfov | UF_min_fov | UF_film_height,
|
|
UF_film_width);
|
|
} else {
|
|
// Otherwise, throw out focal length.
|
|
nassertv(_focal_length_seq == 0);
|
|
adjust_user_flags(UF_focal_length | UF_film_height,
|
|
UF_film_width);
|
|
}
|
|
adjust_comp_flags(CF_mat | CF_focal_length | CF_fov,
|
|
CF_film_size);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_film_size
|
|
// Access: Published
|
|
// Description: Sets the size and shape of the "film" within the
|
|
// lens. This both establishes the units used by
|
|
// calls like set_focal_length(), and establishes the
|
|
// aspect ratio of the frame.
|
|
//
|
|
// In a physical camera, the field of view of a lens is
|
|
// determined by the lens' focal length and by the size
|
|
// of the film area exposed by the lens. For instance,
|
|
// a 35mm camera exposes a rectangle on the film about
|
|
// 24mm x 36mm, which means a 50mm lens gives about a
|
|
// 40-degree horizontal field of view.
|
|
//
|
|
// In the virtual camera, you may set the film size to
|
|
// any units here, and specify a focal length in the
|
|
// same units to simulate the same effect. Or, you may
|
|
// ignore this parameter, and specify the field of view
|
|
// and aspect ratio of the lens directly.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_film_size(const LVecBase2f &film_size) {
|
|
_film_size = film_size;
|
|
|
|
// We can't specify all three of focal length, fov, and film size.
|
|
// Throw out the oldest one.
|
|
resequence_fov_triad(_film_size_seq, _focal_length_seq, _fov_seq);
|
|
|
|
if (_fov_seq == 0) {
|
|
// Throw out fov if it's oldest.
|
|
adjust_user_flags(UF_hfov | UF_vfov | UF_min_fov | UF_aspect_ratio,
|
|
UF_film_width | UF_film_height);
|
|
} else {
|
|
// Otherwise, throw out focal length.
|
|
nassertv(_focal_length_seq == 0);
|
|
adjust_user_flags(UF_focal_length | UF_vfov | UF_aspect_ratio,
|
|
UF_film_width | UF_film_height);
|
|
}
|
|
adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_aspect_ratio,
|
|
CF_film_size);
|
|
|
|
// Also, the user has implicitly specified an aspect ratio. Make it
|
|
// stick until the user tells us something different.
|
|
compute_aspect_ratio();
|
|
adjust_user_flags(0, UF_aspect_ratio);
|
|
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_film_size
|
|
// Access: Published
|
|
// Description: Returns the horizontal and vertical film size of
|
|
// the virtual film. See set_film_size().
|
|
////////////////////////////////////////////////////////////////////
|
|
const LVecBase2f &Lens::
|
|
get_film_size() const {
|
|
if ((_comp_flags & CF_film_size) == 0) {
|
|
// We pretend this is a const method, even though it may call a
|
|
// non-const method to recompute the internal values. We can do
|
|
// this because this is just compute-on-demand.
|
|
((Lens *)this)->compute_film_size();
|
|
}
|
|
return _film_size;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_focal_length
|
|
// Access: Published
|
|
// Description: Sets the focal length of the lens. This may adjust
|
|
// the field-of-view correspondingly, and is an
|
|
// alternate way to specify field of view.
|
|
//
|
|
// For certain kinds of lenses (e.g. OrthographicLens),
|
|
// the focal length has no meaning.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_focal_length(float focal_length) {
|
|
_focal_length = focal_length;
|
|
|
|
// We can't specify all three of focal length, fov, and film size.
|
|
// Throw out the oldest one.
|
|
resequence_fov_triad(_focal_length_seq, _film_size_seq, _fov_seq);
|
|
|
|
if (_film_size_seq == 0) {
|
|
// Throw out film size if it's oldest.
|
|
adjust_user_flags(UF_film_width | UF_film_height,
|
|
UF_focal_length);
|
|
} else {
|
|
// Otherwise, throw out the fov.
|
|
nassertv(_fov_seq == 0);
|
|
adjust_user_flags(UF_hfov | UF_vfov | UF_min_fov,
|
|
UF_focal_length);
|
|
}
|
|
|
|
adjust_comp_flags(CF_mat | CF_fov | CF_film_size,
|
|
CF_focal_length);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_focal_length
|
|
// Access: Published
|
|
// Description: Returns the focal length of the lens. This may have
|
|
// been set explicitly by a previous call to
|
|
// set_focal_length(), or it may be computed based on
|
|
// the lens' fov and film_size. For certain kinds of
|
|
// lenses, the focal length has no meaning.
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_focal_length() const {
|
|
if ((_comp_flags & CF_focal_length) == 0) {
|
|
((Lens *)this)->compute_focal_length();
|
|
}
|
|
return _focal_length;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_min_fov
|
|
// Access: Published
|
|
// Description: Sets the field of view of the smallest dimension of
|
|
// the window. If the window is wider than it is tall,
|
|
// this specifies the vertical field of view; if it is
|
|
// taller than it is wide, this specifies the horizontal
|
|
// field of view.
|
|
//
|
|
// In many cases, this is preferable to setting either
|
|
// the horizontal or vertical field of view explicitly.
|
|
// Setting this parameter means that pulling the window
|
|
// wider will widen the field of view, which is usually
|
|
// what you expect to happen.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_min_fov(float min_fov) {
|
|
_min_fov = min_fov;
|
|
|
|
// We can't specify all three of focal length, fov, and film size.
|
|
// Throw out the oldest one.
|
|
resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq);
|
|
|
|
if (_focal_length_seq == 0) {
|
|
// Throw out focal length if it's oldest.
|
|
adjust_user_flags(UF_focal_length | UF_vfov | UF_hfov,
|
|
UF_min_fov);
|
|
} else {
|
|
// Otherwise, throw out film size.
|
|
nassertv(_film_size_seq == 0);
|
|
|
|
// Make sure we save the aspect ratio first.
|
|
compute_aspect_ratio();
|
|
adjust_user_flags(UF_film_width | UF_film_height | UF_vfov | UF_hfov,
|
|
UF_min_fov);
|
|
}
|
|
adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_film_size,
|
|
0);
|
|
// We leave CF_fov off of comp_flags, because we will still need to
|
|
// recompute the vertical fov. It's not exactly the same as hfov *
|
|
// get_aspect_ratio().
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_fov
|
|
// Access: Published
|
|
// Description: Sets the horizontal field of view of the lens without
|
|
// changing the aspect ratio. The vertical field of
|
|
// view is adjusted to maintain the same aspect ratio.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_fov(float hfov) {
|
|
_fov[0] = hfov;
|
|
|
|
// We can't specify all three of focal length, fov, and film size.
|
|
// Throw out the oldest one.
|
|
resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq);
|
|
|
|
if (_focal_length_seq == 0) {
|
|
// Throw out focal length if it's oldest.
|
|
adjust_user_flags(UF_focal_length | UF_vfov | UF_min_fov,
|
|
UF_hfov);
|
|
} else {
|
|
// Otherwise, throw out film size.
|
|
nassertv(_film_size_seq == 0);
|
|
|
|
// Make sure we save the aspect ratio first.
|
|
compute_aspect_ratio();
|
|
adjust_user_flags(UF_film_width | UF_film_height | UF_vfov | UF_min_fov,
|
|
UF_hfov);
|
|
}
|
|
adjust_comp_flags(CF_mat | CF_focal_length | CF_fov | CF_film_size,
|
|
0);
|
|
// We leave CF_fov off of comp_flags, because we will still need to
|
|
// recompute the vertical fov. It's not exactly the same as hfov *
|
|
// get_aspect_ratio().
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_fov
|
|
// Access: Published
|
|
// Description: Sets the field of view of the lens in both
|
|
// dimensions. This establishes both the field of view
|
|
// and the aspect ratio of the lens. This is one way to
|
|
// specify the field of view of a lens;
|
|
// set_focal_length() is another way.
|
|
//
|
|
// For certain kinds of lenses (like OrthographicLens),
|
|
// the field of view has no meaning.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_fov(const LVecBase2f &fov) {
|
|
_fov = fov;
|
|
|
|
// We can't specify all three of focal length, fov, and film size.
|
|
// Throw out the oldest one.
|
|
resequence_fov_triad(_fov_seq, _focal_length_seq, _film_size_seq);
|
|
|
|
if (_focal_length_seq == 0) {
|
|
// Throw out focal length if it's oldest.
|
|
adjust_user_flags(UF_focal_length | UF_film_height | UF_aspect_ratio,
|
|
UF_hfov | UF_vfov | UF_min_fov);
|
|
} else {
|
|
// Otherwise, throw out film size.
|
|
nassertv(_film_size_seq == 0);
|
|
adjust_user_flags(UF_film_width | UF_film_height | UF_aspect_ratio,
|
|
UF_hfov | UF_vfov | UF_min_fov);
|
|
}
|
|
adjust_comp_flags(CF_mat | CF_focal_length | CF_film_size | CF_aspect_ratio,
|
|
CF_fov);
|
|
|
|
// Also, the user has implicitly specified an aspect ratio. Make it
|
|
// stick until the user tells us something different.
|
|
compute_aspect_ratio();
|
|
adjust_user_flags(0, UF_aspect_ratio);
|
|
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_fov
|
|
// Access: Published
|
|
// Description: Returns the horizontal and vertical film size of
|
|
// the virtual film. See set_fov().
|
|
////////////////////////////////////////////////////////////////////
|
|
const LVecBase2f &Lens::
|
|
get_fov() const {
|
|
if ((_comp_flags & CF_fov) == 0) {
|
|
((Lens *)this)->compute_fov();
|
|
}
|
|
return _fov;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_min_fov
|
|
// Access: Published
|
|
// Description: Returns the field of view of the narrowest dimension
|
|
// of the window. See set_min_fov().
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_min_fov() const {
|
|
if ((_comp_flags & CF_fov) == 0) {
|
|
((Lens *)this)->compute_fov();
|
|
}
|
|
return _min_fov;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_aspect_ratio
|
|
// Access: Published
|
|
// Description: Sets the aspect ratio of the lens. This is the ratio
|
|
// of the height to the width of the generated image.
|
|
// Setting this overrides the two-parameter fov or film
|
|
// size setting.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_aspect_ratio(float aspect_ratio) {
|
|
_aspect_ratio = aspect_ratio;
|
|
adjust_user_flags(UF_film_height | UF_vfov,
|
|
UF_aspect_ratio);
|
|
adjust_comp_flags(CF_mat | CF_film_size | CF_fov | CF_focal_length,
|
|
CF_aspect_ratio);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_aspect_ratio
|
|
// Access: Published
|
|
// Description: Returns the aspect ratio of the Lens. This is
|
|
// determined based on the indicated film size; see
|
|
// set_film_size().
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_aspect_ratio() const {
|
|
if ((_comp_flags & CF_aspect_ratio) == 0) {
|
|
((Lens *)this)->compute_aspect_ratio();
|
|
}
|
|
return _aspect_ratio;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_default_near
|
|
// Access: Published, Static
|
|
// Description: Returns the default near plane distance that will be
|
|
// assigned to each newly-created lens. This is read
|
|
// from the Configrc file.
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_default_near() {
|
|
return default_near;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_default_far
|
|
// Access: Published, Static
|
|
// Description: Returns the default far plane distance that will be
|
|
// assigned to each newly-created lens. This is read
|
|
// from the Configrc file.
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_default_far() {
|
|
return default_far;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_view_hpr
|
|
// Access: Published
|
|
// Description: Sets the direction in which the lens is facing.
|
|
// Normally, this is down the forward axis (usually the
|
|
// Y axis), but it may be rotated. This is only one way
|
|
// of specifying the rotation; you may also specify an
|
|
// explicit vector in which to look, or you may give a
|
|
// complete transformation matrix.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_view_hpr(const LVecBase3f &view_hpr) {
|
|
_view_hpr = view_hpr;
|
|
adjust_user_flags(UF_view_vector | UF_view_mat,
|
|
UF_view_hpr);
|
|
adjust_comp_flags(CF_mat | CF_view_vector,
|
|
CF_view_hpr);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_view_hpr
|
|
// Access: Published
|
|
// Description: Returns the direction in which the lens is facing.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LVecBase3f &Lens::
|
|
get_view_hpr() const {
|
|
if ((_comp_flags & CF_view_hpr) == 0) {
|
|
((Lens *)this)->compute_view_hpr();
|
|
}
|
|
return _view_hpr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_view_vector
|
|
// Access: Published
|
|
// Description: Specifies the direction in which the lens is facing
|
|
// by giving an axis to look along, and a perpendicular
|
|
// (or at least non-parallel) up axis.
|
|
//
|
|
// See also set_view_hpr().
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_view_vector(const LVector3f &view_vector, const LVector3f &up_vector) {
|
|
_view_vector = view_vector;
|
|
_up_vector = up_vector;
|
|
adjust_user_flags(UF_view_hpr | UF_view_mat,
|
|
UF_view_vector);
|
|
adjust_comp_flags(CF_mat | CF_view_hpr,
|
|
CF_view_vector);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_view_vector
|
|
// Access: Published
|
|
// Description: Returns the axis along which the lens is facing.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LVector3f &Lens::
|
|
get_view_vector() const {
|
|
if ((_comp_flags & CF_view_vector) == 0) {
|
|
((Lens *)this)->compute_view_vector();
|
|
}
|
|
return _view_vector;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_up_vector
|
|
// Access: Published
|
|
// Description: Returns the axis perpendicular to the camera's view
|
|
// vector that indicates the "up" direction.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LVector3f &Lens::
|
|
get_up_vector() const {
|
|
if ((_comp_flags & CF_view_vector) == 0) {
|
|
((Lens *)this)->compute_view_vector();
|
|
}
|
|
return _up_vector;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_nodal_point
|
|
// Access: Published
|
|
// Description: Returns the center point of the lens: the point from
|
|
// which the lens is viewing.
|
|
////////////////////////////////////////////////////////////////////
|
|
LPoint3f Lens::
|
|
get_nodal_point() const {
|
|
return get_view_mat().get_row3(3);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_interocular_distance
|
|
// Access: Published
|
|
// Description: Sets the distance between the left and right eyes of
|
|
// a stereo camera. This distance is used to apply a
|
|
// stereo effect when the lens is rendered on a stereo
|
|
// display region. It only has an effect on a
|
|
// PerspectiveLens.
|
|
//
|
|
// The left eye and the right eye are each offset along
|
|
// the X axis by half of this distance, so that this
|
|
// parameter specifies the total distance between them.
|
|
//
|
|
// Also see set_convergence_distance(), which relates.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_interocular_distance(float interocular_distance) {
|
|
_interocular_distance = interocular_distance;
|
|
if (_interocular_distance == 0.0f) {
|
|
adjust_user_flags(UF_interocular_distance, 0);
|
|
} else {
|
|
adjust_user_flags(0, UF_interocular_distance);
|
|
}
|
|
|
|
adjust_comp_flags(CF_mat, 0);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_interocular_distance
|
|
// Access: Published
|
|
// Description: See set_interocular_distance().
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_interocular_distance() const {
|
|
return _interocular_distance;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_convergence_distance
|
|
// Access: Published
|
|
// Description: Sets the distance between between the camera plane
|
|
// and the point in the distance that the left and right
|
|
// eyes are both looking at. This distance is used to
|
|
// apply a stereo effect when the lens is rendered on a
|
|
// stereo display region. It only has an effect on a
|
|
// PerspectiveLens.
|
|
//
|
|
// This parameter must be greater than 0, but may be as
|
|
// large as you like. It controls the amount to which
|
|
// the two eyes are directed inwards towards each other,
|
|
// which is a normal property of stereo vision. It is a
|
|
// distance, not an angle; normally this should be set
|
|
// to the distance from the camera to the area of
|
|
// interest in your scene. If you want to simulate
|
|
// parallel stereo, set this value to a very large
|
|
// number.
|
|
//
|
|
// Also see set_interocular_distance(), which relates.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_convergence_distance(float convergence_distance) {
|
|
_convergence_distance = convergence_distance;
|
|
if (_convergence_distance == 0.0f) {
|
|
adjust_user_flags(UF_convergence_distance, 0);
|
|
} else {
|
|
adjust_user_flags(0, UF_convergence_distance);
|
|
}
|
|
|
|
adjust_comp_flags(CF_mat, 0);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_convergence_distance
|
|
// Access: Published
|
|
// Description: See set_convergence_distance().
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
get_convergence_distance() const {
|
|
return _convergence_distance;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_view_mat
|
|
// Access: Published
|
|
// Description: Sets an arbitrary transformation on the lens. This
|
|
// replaces the individual transformation components
|
|
// like set_view_hpr().
|
|
//
|
|
// Setting a transformation here will have a slightly
|
|
// different effect than putting one on the LensNode
|
|
// that contains this lens. In particular, lighting and
|
|
// other effects computations will still be performed on
|
|
// the lens in its untransformed (facing forward)
|
|
// position, but the actual projection matrix will be
|
|
// transformed by this matrix.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_view_mat(const LMatrix4f &view_mat) {
|
|
_lens_mat = view_mat;
|
|
adjust_user_flags(UF_view_vector | UF_view_hpr,
|
|
UF_view_mat);
|
|
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv |
|
|
CF_projection_mat_left_inv | CF_projection_mat_right_inv |
|
|
CF_lens_mat_inv | CF_view_hpr | CF_view_vector,
|
|
CF_lens_mat);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_view_mat
|
|
// Access: Published
|
|
// Description: Returns the direction in which the lens is facing.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_view_mat() const {
|
|
if ((_comp_flags & CF_lens_mat) == 0) {
|
|
((Lens *)this)->compute_lens_mat();
|
|
}
|
|
return _lens_mat;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::clear_view_mat
|
|
// Access: Published
|
|
// Description: Resets the lens transform to identity.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
clear_view_mat() {
|
|
_lens_mat = LMatrix4f::ident_mat();
|
|
adjust_user_flags(0, UF_view_vector | UF_view_hpr | UF_view_mat);
|
|
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv |
|
|
CF_projection_mat_left_inv | CF_projection_mat_right_inv |
|
|
CF_lens_mat_inv | CF_view_hpr | CF_view_vector,
|
|
CF_lens_mat);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_keystone
|
|
// Access: Published
|
|
// Description: Indicates the ratio of keystone correction to perform
|
|
// on the lens, in each of three axes. This will build
|
|
// a special non-affine scale factor into the projection
|
|
// matrix that will compensate for keystoning of a
|
|
// projected image; this can be used to compensate for a
|
|
// projector that for physical reasons cannot be aimed
|
|
// directly at its screen.
|
|
//
|
|
// The default value is taken from the default-keystone
|
|
// Config variable. 0, 0 indicates no keystone
|
|
// correction; specify a small value (usually in the
|
|
// range -1 .. 1) in either the x or y position to
|
|
// generate a keystone correction in that axis.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_keystone(const LVecBase2f &keystone) {
|
|
_keystone = keystone;
|
|
adjust_user_flags(0, UF_keystone);
|
|
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv |
|
|
CF_projection_mat_left_inv | CF_projection_mat_right_inv |
|
|
CF_film_mat | CF_film_mat_inv, 0);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::clear_keystone
|
|
// Access: Published
|
|
// Description: Disables the lens keystone correction.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
clear_keystone() {
|
|
_keystone.set(0.0f, 0.0f);
|
|
adjust_user_flags(UF_keystone, 0);
|
|
adjust_comp_flags(CF_projection_mat | CF_projection_mat_inv |
|
|
CF_projection_mat_left_inv | CF_projection_mat_right_inv |
|
|
CF_film_mat | CF_film_mat_inv, 0);
|
|
throw_change_event();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::set_frustum_from_corners
|
|
// Access: Published
|
|
// Description: Sets up the lens to use the frustum defined by the
|
|
// four indicated points. This is most useful for a
|
|
// PerspectiveLens, but it may be called for other kinds
|
|
// of lenses as well.
|
|
//
|
|
// The frustum will be rooted at the origin (or by
|
|
// whatever translation might have been specified in a
|
|
// previous call to set_view_mat).
|
|
//
|
|
// It is legal for the four points not to be arranged in
|
|
// a rectangle; if this is the case, the frustum will be
|
|
// fitted as tightly as possible to cover all four
|
|
// points.
|
|
//
|
|
// The flags parameter contains the union of one or more
|
|
// of the following bits to control the behavior of this
|
|
// function:
|
|
//
|
|
// FC_roll - If this is included, the camera may be
|
|
// rotated so that its up vector is perpendicular to the
|
|
// top line. Otherwise, the standard up vector is used.
|
|
//
|
|
// FC_camera_plane - This allows the camera plane to be
|
|
// adjusted to be as nearly perpendicular to the center
|
|
// of the frustum as possible. Without this bit, the
|
|
// orientation camera plane is defined by position of
|
|
// the four points (which should all be coplanar). With
|
|
// this bit, the camera plane is arbitarary, and may be
|
|
// chosen so that the four points do not themselves lie
|
|
// in the camera plane (but the points will still be
|
|
// within the frustum).
|
|
//
|
|
// FC_off_axis - This allows the resulting frustum to be
|
|
// off-axis to get the tightest possible fit. Without
|
|
// this bit, the viewing axis will be centered within
|
|
// the frustum, but there may be more wasted space along
|
|
// the edges.
|
|
//
|
|
// FC_aspect_ratio - This allows the frustum to be
|
|
// scaled non-proportionately in the vertical and
|
|
// horizontal dimensions, if necessary, to get a tighter
|
|
// fit. Without this bit, the current aspect ratio will
|
|
// be preserved.
|
|
//
|
|
// FC_shear - This allows the frustum to be sheared, if
|
|
// necessary, to get the tightest possible fit. This
|
|
// may result in a parallelogram-based frustum, which
|
|
// will give a slanted appearance to the rendered image.
|
|
// Without this bit, the frustum will be
|
|
// rectangle-based.
|
|
//
|
|
// In general, if 0 is passed in as the value for flags,
|
|
// the generated frustum will be a loose fit but sane;
|
|
// if -1 is passed in, it will be a tighter fit and
|
|
// possibly screwy.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
set_frustum_from_corners(const LVecBase3f &ul, const LVecBase3f &ur,
|
|
const LVecBase3f &ll, const LVecBase3f &lr,
|
|
int flags) {
|
|
// We'll need to know the pre-existing eyepoint translation from the
|
|
// center, so we can preserve it in the new frustum. This is
|
|
// usually (0, 0, 0), but it could be an arbitrary vector.
|
|
const LMatrix4f &lens_mat_inv = get_lens_mat_inv();
|
|
LVector3f eye_offset;
|
|
lens_mat_inv.get_row3(eye_offset, 3);
|
|
|
|
// Now choose the viewing axis. If FC_camera_plane is specified,
|
|
// we'll pass it through the centroid for the best camera plane;
|
|
// otherwise, it's perpendicular to the plane in which the points
|
|
// lie.
|
|
LVector3f view_vector;
|
|
if ((flags & FC_camera_plane) != 0) {
|
|
view_vector = (ul + ur + ll + lr) * 0.25f;
|
|
} else {
|
|
Planef plane(ll, ul, ur);
|
|
view_vector = plane.get_normal();
|
|
}
|
|
|
|
// Now determine the up axis. If FC_roll is specified, or if our
|
|
// view vector is straight up, it is the vector perpendicular to
|
|
// both the viewing axis and the top line. Otherwise, it is the
|
|
// standard up axis.
|
|
LVector3f up_vector = LVector3f::up(_cs);
|
|
if (view_vector == up_vector || ((flags & FC_roll) != 0)) {
|
|
LVector3f top = ul - ur;
|
|
up_vector = view_vector.cross(top);
|
|
}
|
|
|
|
// Now compute the matrix that applies this rotation.
|
|
LMatrix4f rot_mat;
|
|
look_at(rot_mat, view_vector, up_vector, CS_zup_right);
|
|
|
|
// And invert it.
|
|
LMatrix4f inv_rot_mat;
|
|
inv_rot_mat.invert_affine_from(rot_mat);
|
|
|
|
// Use that inverse matrix to convert the four corners to a local
|
|
// coordinate system, looking down the Y axis.
|
|
LPoint3f cul = inv_rot_mat.xform_point(ul);
|
|
LPoint3f cur = inv_rot_mat.xform_point(ur);
|
|
LPoint3f cll = inv_rot_mat.xform_point(ll);
|
|
LPoint3f clr = inv_rot_mat.xform_point(lr);
|
|
|
|
// Project all points into the Y == 1 plane, so we can do 2-d
|
|
// manipulation on them.
|
|
cul /= cul[1];
|
|
cur /= cur[1];
|
|
cll /= cll[1];
|
|
clr /= clr[1];
|
|
|
|
LMatrix4f shear_mat = LMatrix4f::ident_mat();
|
|
LMatrix4f inv_shear_mat = LMatrix4f::ident_mat();
|
|
|
|
// Now, if we're allowed to shear the frustum, do so.
|
|
if ((flags & FC_shear) != 0) {
|
|
build_shear_mat(shear_mat, cul, cur, cll, clr);
|
|
inv_shear_mat.invert_from(shear_mat);
|
|
}
|
|
|
|
// Now build the complete view matrix.
|
|
LMatrix4f inv_view_mat =
|
|
inv_rot_mat *
|
|
inv_shear_mat;
|
|
|
|
// And reapply the eye offset to this matrix.
|
|
inv_view_mat.set_row(3, eye_offset);
|
|
|
|
LMatrix4f view_mat;
|
|
view_mat.invert_from(inv_view_mat);
|
|
set_view_mat(view_mat);
|
|
|
|
LPoint3f ful = inv_view_mat.xform_point(ul);
|
|
LPoint3f fur = inv_view_mat.xform_point(ur);
|
|
LPoint3f fll = inv_view_mat.xform_point(ll);
|
|
LPoint3f flr = inv_view_mat.xform_point(lr);
|
|
|
|
// Normalize *these* points into the y == 1 plane.
|
|
ful /= ful[1];
|
|
fur /= fur[1];
|
|
fll /= fll[1];
|
|
flr /= flr[1];
|
|
|
|
// Determine the minimum field of view necesary to cover all four
|
|
// transformed points.
|
|
float min_x = min(min(ful[0], fur[0]), min(fll[0], flr[0]));
|
|
float max_x = max(max(ful[0], fur[0]), max(fll[0], flr[0]));
|
|
float min_z = min(min(ful[2], fur[2]), min(fll[2], flr[2]));
|
|
float max_z = max(max(ful[2], fur[2]), max(fll[2], flr[2]));
|
|
|
|
float x_spread, x_center, z_spread, z_center;
|
|
|
|
if ((flags & FC_off_axis) != 0) {
|
|
// If we're allowed to make an off-axis projection, then pick the
|
|
// best center.
|
|
x_center = (max_x + min_x) * 0.5f;
|
|
z_center = (max_z + min_z) * 0.5f;
|
|
x_spread = x_center - min_x;
|
|
z_spread = z_center - min_z;
|
|
} else {
|
|
// Otherwise, the center must be (0, 0).
|
|
x_center = 0.0f;
|
|
z_center = 0.0f;
|
|
x_spread = max(cabs(max_x), cabs(min_x));
|
|
z_spread = max(cabs(max_z), cabs(min_z));
|
|
}
|
|
|
|
float aspect_ratio = get_aspect_ratio();
|
|
if ((flags & FC_aspect_ratio) == 0) {
|
|
// If we must preserve the aspect ratio, then the x and z spreads
|
|
// must be adjusted to match.
|
|
if (x_spread < z_spread * aspect_ratio) {
|
|
// x_spread is too small.
|
|
x_spread = z_spread * aspect_ratio;
|
|
} else if (z_spread < x_spread / aspect_ratio) {
|
|
// z_spread is too small.
|
|
z_spread = x_spread / aspect_ratio;
|
|
}
|
|
}
|
|
|
|
float hfov = rad_2_deg(catan(x_spread)) * 2.0f;
|
|
float vfov = rad_2_deg(catan(z_spread)) * 2.0f;
|
|
|
|
set_fov(hfov, vfov);
|
|
|
|
if ((flags & FC_aspect_ratio) == 0) {
|
|
// If we must preserve the aspect ratio, store it one more time.
|
|
// This is mainly in case we have a non-perspective lens with a
|
|
// funny relationship between fov and aspect ratio.
|
|
set_aspect_ratio(aspect_ratio);
|
|
}
|
|
|
|
const LVecBase2f &film_size = get_film_size();
|
|
set_film_offset(film_size[0] * x_center / (x_spread * 2.0f),
|
|
film_size[1] * z_center / (z_spread * 2.0f));
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::recompute_all
|
|
// Access: Published
|
|
// Description: Forces all internal parameters of the Lens to be
|
|
// recomputed. Normally, this should never need to be
|
|
// called; it is provided only to assist in debugging.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
recompute_all() {
|
|
_comp_flags = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::is_linear
|
|
// Access: Published, Virtual
|
|
// Description: Returns true if the lens represents a linear
|
|
// projection (e.g. PerspectiveLens, OrthographicLens),
|
|
// and therefore there is a valid matrix returned by
|
|
// get_projection_mat(), or false otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Lens::
|
|
is_linear() const {
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::is_perspective
|
|
// Access: Published, Virtual
|
|
// Description: Returns true if the lens represents a perspective
|
|
// projection (i.e. it is a PerspectiveLens), false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Lens::
|
|
is_perspective() const {
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::is_orthographic
|
|
// Access: Published, Virtual
|
|
// Description: Returns true if the lens represents a orthographic
|
|
// projection (i.e. it is a OrthographicLens), false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Lens::
|
|
is_orthographic() const {
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::make_geometry
|
|
// Access: Published, Virtual
|
|
// Description: Allocates and returns a new Geom that can be rendered
|
|
// to show a visible representation of the frustum used
|
|
// for this kind of lens, if it makes sense to do
|
|
// so. If a visible representation cannot be created,
|
|
// returns NULL.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(Geom) Lens::
|
|
make_geometry() {
|
|
// The default behavior for make_geometry() will be to draw a
|
|
// hexahedron around the eight vertices of the frustum. If the lens
|
|
// is non-linear, the hexahedron will be curved; in that case, we'll
|
|
// subdivide the lines into several segments to get an approximation
|
|
// of the curve.
|
|
|
|
// First, define all the points we'll use in this Geom. That's one
|
|
// point at each corner of the near and far planes (and possibly
|
|
// more points along the edges).
|
|
int num_segments = define_geom_data();
|
|
if (num_segments == 0) {
|
|
// Can't do a frustum.
|
|
_geom_data.clear();
|
|
return (Geom *)NULL;
|
|
}
|
|
|
|
// Now string together the line segments.
|
|
PT(GeomLinestrips) line = new GeomLinestrips(Geom::UH_static);
|
|
|
|
// Draw a frame around the near plane.
|
|
int i, si;
|
|
for (i = 0; i < 4; ++i) {
|
|
for (si = 0; si < num_segments; ++si) {
|
|
line->add_vertex(i * 2 + si * (4 * 2) + 0);
|
|
}
|
|
}
|
|
line->add_vertex(0);
|
|
line->close_primitive();
|
|
|
|
// Draw a frame around the far plane.
|
|
for (i = 0; i < 4; ++i) {
|
|
for (si = 0; si < num_segments; ++si) {
|
|
line->add_vertex(i * 2 + si * (4 * 2) + 1);
|
|
}
|
|
}
|
|
line->add_vertex(1);
|
|
line->close_primitive();
|
|
|
|
// Draw connecting lines at the corners.
|
|
line->add_vertex(0 * 2 + 0);
|
|
line->add_vertex(0 * 2 + 1);
|
|
line->close_primitive();
|
|
|
|
line->add_vertex(1 * 2 + 0);
|
|
line->add_vertex(1 * 2 + 1);
|
|
line->close_primitive();
|
|
|
|
line->add_vertex(2 * 2 + 0);
|
|
line->add_vertex(2 * 2 + 1);
|
|
line->close_primitive();
|
|
|
|
line->add_vertex(3 * 2 + 0);
|
|
line->add_vertex(3 * 2 + 1);
|
|
line->close_primitive();
|
|
|
|
// And one more line for the viewing axis.
|
|
line->add_vertex(num_segments * (4 * 2) + 0);
|
|
line->add_vertex(num_segments * (4 * 2) + 1);
|
|
line->close_primitive();
|
|
|
|
PT(Geom) geom = new Geom(_geom_data);
|
|
geom->add_primitive(line);
|
|
|
|
return geom.p();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::make_bounds
|
|
// Access: Published, Virtual
|
|
// Description: Allocates and returns a new BoundingVolume that
|
|
// encloses the frustum used for this kind of
|
|
// lens, if possible. If a suitable bounding
|
|
// volume cannot be created, returns NULL.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(BoundingVolume) Lens::
|
|
make_bounds() const {
|
|
// The default bounding volume is a hexahedron based on the eight
|
|
// corners of the frustum.
|
|
LPoint3f fll, flr, ful, fur;
|
|
LPoint3f nll, nlr, nul, nur;
|
|
LPoint2f corner;
|
|
|
|
corner[0] = -1.0f; corner[1] = 1.0f;
|
|
|
|
// Upper left.
|
|
if (!extrude(corner, nul, ful)) {
|
|
return (BoundingVolume *)NULL;
|
|
}
|
|
|
|
corner[0] = 1.0f; corner[1] = 1.0f;
|
|
|
|
// Upper right.
|
|
if (!extrude(corner, nur, fur)) {
|
|
return (BoundingVolume *)NULL;
|
|
}
|
|
|
|
corner[0] = 1.0f; corner[1] = -1.0f;
|
|
|
|
// Lower right.
|
|
if (!extrude(corner, nlr, flr)) {
|
|
return (BoundingVolume *)NULL;
|
|
}
|
|
|
|
corner[0] = -1.0f; corner[1] = -1.0f;
|
|
|
|
// Lower left.
|
|
if (!extrude(corner, nll, fll)) {
|
|
return (BoundingVolume *)NULL;
|
|
}
|
|
|
|
return new BoundingHexahedron(fll, flr, fur, ful, nll, nlr, nur, nul);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_projection_mat
|
|
// Access: Published
|
|
// Description: Returns the complete transformation matrix from a 3-d
|
|
// point in space to a point on the film, if such a
|
|
// matrix exists, or the identity matrix if the lens is
|
|
// nonlinear.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_projection_mat(StereoChannel channel) const {
|
|
if ((_comp_flags & CF_projection_mat) == 0) {
|
|
((Lens *)this)->compute_projection_mat();
|
|
}
|
|
|
|
switch (channel) {
|
|
case SC_left:
|
|
return _projection_mat_left;
|
|
case SC_right:
|
|
return _projection_mat_right;
|
|
case SC_mono:
|
|
case SC_stereo:
|
|
return _projection_mat;
|
|
}
|
|
|
|
return _projection_mat;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_projection_mat_inv
|
|
// Access: Published
|
|
// Description: Returns the matrix that transforms from a 2-d point
|
|
// on the film to a 3-d vector in space, if such a
|
|
// matrix exists.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_projection_mat_inv(StereoChannel stereo_channel) const {
|
|
switch (stereo_channel) {
|
|
case SC_left:
|
|
{
|
|
if ((_comp_flags & CF_projection_mat_left_inv) == 0) {
|
|
Lens *non_const = (Lens *)this;
|
|
const LMatrix4f &projection_mat_left = get_projection_mat(SC_left);
|
|
non_const->_projection_mat_left_inv.invert_from(projection_mat_left);
|
|
non_const->adjust_comp_flags(0, CF_projection_mat_left_inv);
|
|
}
|
|
}
|
|
return _projection_mat_left_inv;
|
|
|
|
case SC_right:
|
|
{
|
|
if ((_comp_flags & CF_projection_mat_right_inv) == 0) {
|
|
Lens *non_const = (Lens *)this;
|
|
const LMatrix4f &projection_mat_right = get_projection_mat(SC_right);
|
|
non_const->_projection_mat_right_inv.invert_from(projection_mat_right);
|
|
non_const->adjust_comp_flags(0, CF_projection_mat_right_inv);
|
|
}
|
|
}
|
|
return _projection_mat_right_inv;
|
|
|
|
case SC_mono:
|
|
case SC_stereo:
|
|
break;
|
|
}
|
|
|
|
if ((_comp_flags & CF_projection_mat_inv) == 0) {
|
|
Lens *non_const = (Lens *)this;
|
|
const LMatrix4f &projection_mat = get_projection_mat();
|
|
non_const->_projection_mat_inv.invert_from(projection_mat);
|
|
non_const->adjust_comp_flags(0, CF_projection_mat_inv);
|
|
}
|
|
return _projection_mat_inv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_film_mat
|
|
// Access: Published
|
|
// Description: Returns the matrix that transforms from a point
|
|
// behind the lens to a point on the film.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_film_mat() const {
|
|
if ((_comp_flags & CF_film_mat) == 0) {
|
|
((Lens *)this)->compute_film_mat();
|
|
}
|
|
return _film_mat;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_film_mat_inv
|
|
// Access: Published
|
|
// Description: Returns the matrix that transforms from a point on
|
|
// the film to a point behind the lens.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_film_mat_inv() const {
|
|
if ((_comp_flags & CF_film_mat_inv) == 0) {
|
|
Lens *non_const = (Lens *)this;
|
|
const LMatrix4f &film_mat = get_film_mat();
|
|
non_const->_film_mat_inv.invert_from(film_mat);
|
|
non_const->adjust_comp_flags(0, CF_film_mat_inv);
|
|
}
|
|
return _film_mat_inv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_lens_mat
|
|
// Access: Published
|
|
// Description: Returns the matrix that transforms from a point
|
|
// in front of the lens to a point in space.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_lens_mat() const {
|
|
if ((_comp_flags & CF_lens_mat) == 0) {
|
|
((Lens *)this)->compute_lens_mat();
|
|
}
|
|
return _lens_mat;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::get_lens_mat_inv
|
|
// Access: Published
|
|
// Description: Returns the matrix that transforms from a point in
|
|
// space to a point in front of the lens.
|
|
////////////////////////////////////////////////////////////////////
|
|
const LMatrix4f &Lens::
|
|
get_lens_mat_inv() const {
|
|
if ((_comp_flags & CF_lens_mat_inv) == 0) {
|
|
Lens *non_const = (Lens *)this;
|
|
const LMatrix4f &lens_mat = get_lens_mat();
|
|
non_const->_lens_mat_inv.invert_from(lens_mat);
|
|
non_const->adjust_comp_flags(0, CF_lens_mat_inv);
|
|
}
|
|
return _lens_mat_inv;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::output
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
output(ostream &out) const {
|
|
out << get_type();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::write
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
write(ostream &out, int indent_level) const {
|
|
indent(out, indent_level) << get_type() << " fov = " << get_fov() << "\n";
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::throw_change_event
|
|
// Access: Protected
|
|
// Description: Throws the event associated with changing properties
|
|
// on this Lens, if any.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
throw_change_event() {
|
|
++_last_change;
|
|
|
|
if (!_change_event.empty()) {
|
|
throw_event(_change_event, this);
|
|
}
|
|
|
|
if (!_geom_data.is_null()) {
|
|
if (_geom_data->get_ref_count() == 1) {
|
|
// No one's using the data any more (there are no references to
|
|
// it other than this one), so don't bother to recompute it;
|
|
// just release it.
|
|
_geom_data.clear();
|
|
} else {
|
|
// Someone still has a handle to the data, so recompute it for
|
|
// them.
|
|
define_geom_data();
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::extrude_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Given a 2-d point in the range (-1,1) in both
|
|
// dimensions, where (0,0) is the center of the
|
|
// lens and (-1,-1) is the lower-left corner,
|
|
// compute the corresponding vector in space that maps
|
|
// to this point, if such a vector can be determined.
|
|
// The vector is returned by indicating the points on
|
|
// the near plane and far plane that both map to the
|
|
// indicated 2-d point.
|
|
//
|
|
// The z coordinate of the 2-d point is ignored.
|
|
//
|
|
// Returns true if the vector is defined, or false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Lens::
|
|
extrude_impl(const LPoint3f &point2d, LPoint3f &near_point, LPoint3f &far_point) const {
|
|
const LMatrix4f &projection_mat_inv = get_projection_mat_inv();
|
|
{
|
|
LVecBase4f full(point2d[0], point2d[1], -1.0f, 1.0f);
|
|
full = projection_mat_inv.xform(full);
|
|
|
|
float recip_full3 = 1.0f / max(full[3], 0.00001f);
|
|
near_point.set(full[0] * recip_full3,
|
|
full[1] * recip_full3,
|
|
full[2] * recip_full3);
|
|
}
|
|
{
|
|
LVecBase4f full(point2d[0], point2d[1], 1.0f, 1.0f);
|
|
full = projection_mat_inv.xform(full);
|
|
|
|
// We can truncate the weight factor at near 0. If it goes too
|
|
// close to zero, or becomes negative, the far plane moves out
|
|
// past infinity and comes back in behind the lens, which is just
|
|
// crazy. Truncating it to zero keeps the far plane from moving
|
|
// too far out.
|
|
float recip_full3 = 1.0f / max(full[3], 0.00001f);
|
|
far_point.set(full[0] * recip_full3,
|
|
full[1] * recip_full3,
|
|
full[2] * recip_full3);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::extrude_vec_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Given a 2-d point in the range (-1,1) in both
|
|
// dimensions, where (0,0) is the center of the
|
|
// lens and (-1,-1) is the lower-left corner,
|
|
// compute the vector that corresponds to the view
|
|
// direction. This will be parallel to the normal on
|
|
// the surface (the far plane) corresponding to the lens
|
|
// shape at this point.
|
|
//
|
|
// Generally, for all rational lenses, the center of the
|
|
// film at (0,0) computes a vector that is in the same
|
|
// direction as the vector specified by
|
|
// set_view_vector().
|
|
//
|
|
// For all linear lenses, including perspective and
|
|
// orthographic lenses, all points on the film compute
|
|
// this same vector (the far plane is a flat plane, so
|
|
// the normal is the same everywhere). For curved
|
|
// lenses like fisheye and cylindrical lenses, different
|
|
// points may compute different vectors (the far "plane"
|
|
// on these lenses is a curved surface).
|
|
//
|
|
// The z coordinate of the 2-d point is ignored.
|
|
//
|
|
// Returns true if the vector is defined, or false
|
|
// otherwise.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Lens::
|
|
extrude_vec_impl(const LPoint3f &point2d, LVector3f &vec) const {
|
|
vec = LVector3f::forward(_cs) * get_lens_mat();
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::project_impl
|
|
// Access: Protected, Virtual
|
|
// Description: Given a 3-d point in space, determine the 2-d point
|
|
// this maps to, in the range (-1,1) in both dimensions,
|
|
// where (0,0) is the center of the lens and
|
|
// (-1,-1) is the lower-left corner.
|
|
//
|
|
// The z coordinate will also be set to a value in the
|
|
// range (-1, 1), where 1 represents a point on the near
|
|
// plane, and -1 represents a point on the far plane.
|
|
//
|
|
// Returns true if the 3-d point is in front of the lens
|
|
// and within the viewing frustum (in which case point2d
|
|
// is filled in), or false otherwise (in which case
|
|
// point2d will be filled in with something, which may
|
|
// or may not be meaningful).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool Lens::
|
|
project_impl(const LPoint3f &point3d, LPoint3f &point2d) const {
|
|
const LMatrix4f &projection_mat = get_projection_mat();
|
|
LVecBase4f full(point3d[0], point3d[1], point3d[2], 1.0f);
|
|
full = projection_mat.xform(full);
|
|
if (full[3] == 0.0f) {
|
|
point2d.set(0.0f, 0.0f, 0.0f);
|
|
return false;
|
|
}
|
|
float recip_full3 = 1.0f/full[3];
|
|
point2d.set(full[0] * recip_full3, full[1] * recip_full3, full[2] * recip_full3);
|
|
return
|
|
(full[3] > 0.0f) &&
|
|
(point2d[0] >= -1.0f) && (point2d[0] <= 1.0f) &&
|
|
(point2d[1] >= -1.0f) && (point2d[1] <= 1.0f);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_film_size
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the size and shape of the film behind the
|
|
// camera, based on the aspect ratio and fov.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_film_size() {
|
|
if ((_user_flags & (UF_min_fov | UF_focal_length)) == (UF_min_fov | UF_focal_length)) {
|
|
// If we just have a min FOV and a focal length, that determines
|
|
// the smaller of the two film_sizes, and the larger is simply
|
|
// chosen according to the aspect ratio.
|
|
float fs = fov_to_film(_min_fov, _focal_length, true);
|
|
nassertv((_user_flags & UF_aspect_ratio) != 0 ||
|
|
(_comp_flags & CF_aspect_ratio) != 0);
|
|
|
|
if (_aspect_ratio < 1.0f) {
|
|
_film_size[1] = fs / _aspect_ratio;
|
|
_film_size[0] = fs;
|
|
|
|
} else {
|
|
_film_size[0] = fs * _aspect_ratio;
|
|
_film_size[1] = fs;
|
|
}
|
|
|
|
} else {
|
|
if ((_user_flags & UF_film_width) == 0) {
|
|
if ((_user_flags & (UF_hfov | UF_focal_length)) == (UF_hfov | UF_focal_length)) {
|
|
_film_size[0] = fov_to_film(_fov[0], _focal_length, true);
|
|
} else {
|
|
_film_size[0] = 1.0f;
|
|
}
|
|
}
|
|
|
|
if ((_user_flags & UF_film_height) == 0) {
|
|
if ((_user_flags & (UF_vfov | UF_focal_length)) == (UF_vfov | UF_focal_length)) {
|
|
_film_size[1] = fov_to_film(_fov[1], _focal_length, false);
|
|
|
|
} else if ((_user_flags & (UF_hfov | UF_vfov)) == (UF_hfov | UF_vfov)) {
|
|
// If we don't have a focal length, but we have an explicit vfov
|
|
// and hfov, we can infer the focal length is whatever makes the
|
|
// film width, above, be what it is.
|
|
if ((_comp_flags & CF_focal_length) == 0) {
|
|
_focal_length = fov_to_focal_length(_fov[0], _film_size[0], true);
|
|
adjust_comp_flags(0, CF_focal_length);
|
|
}
|
|
_film_size[1] = fov_to_film(_fov[1], _focal_length, false);
|
|
|
|
} else if ((_user_flags & UF_aspect_ratio) != 0 ||
|
|
(_comp_flags & CF_aspect_ratio) != 0) {
|
|
_film_size[1] = _film_size[0] / _aspect_ratio;
|
|
|
|
} else {
|
|
// Default is an aspect ratio of 1.
|
|
_film_size[1] = _film_size[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
adjust_comp_flags(0, CF_film_size);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_focal_length
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the focal length of the lens, based on the
|
|
// fov and film size. This is based on the horizontal
|
|
// dimension.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_focal_length() {
|
|
if ((_user_flags & UF_focal_length) == 0) {
|
|
const LVecBase2f &film_size = get_film_size();
|
|
const LVecBase2f &fov = get_fov();
|
|
_focal_length = fov_to_focal_length(fov[0], film_size[0], true);
|
|
}
|
|
|
|
adjust_comp_flags(0, CF_focal_length);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_fov
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the field of view of the lens, based on the
|
|
// film size and focal length.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_fov() {
|
|
const LVecBase2f &film_size = get_film_size();
|
|
|
|
bool got_hfov = ((_user_flags & UF_hfov) != 0);
|
|
bool got_vfov = ((_user_flags & UF_vfov) != 0);
|
|
bool got_min_fov = ((_user_flags & UF_min_fov) != 0);
|
|
|
|
if (!got_hfov && !got_vfov && !got_min_fov) {
|
|
// If the user hasn't specified any FOV, we have to compute it.
|
|
if ((_user_flags & UF_focal_length) != 0) {
|
|
// The FOV is determined from the film size and focal length.
|
|
_fov[0] = film_to_fov(film_size[0], _focal_length, true);
|
|
_fov[1] = film_to_fov(film_size[1], _focal_length, true);
|
|
got_hfov = true;
|
|
got_vfov = true;
|
|
|
|
} else {
|
|
// We can't compute the FOV; take the default.
|
|
_min_fov = default_fov;
|
|
got_min_fov = true;
|
|
}
|
|
}
|
|
|
|
if (got_min_fov) {
|
|
// If we have just a min_fov, use it to derive whichever fov is
|
|
// smaller.
|
|
if (film_size[0] < film_size[1]) {
|
|
_fov[0] = _min_fov;
|
|
got_hfov = true;
|
|
} else {
|
|
_fov[1] = _min_fov;
|
|
got_vfov = true;
|
|
}
|
|
}
|
|
|
|
// Now compute whichever fov is remaining.
|
|
if (!got_hfov) {
|
|
if ((_user_flags & UF_focal_length) == 0 &&
|
|
(_comp_flags & CF_focal_length) == 0) {
|
|
// If we don't have an explicit focal length, we can infer it
|
|
// from the above.
|
|
nassertv(got_vfov);
|
|
_focal_length = fov_to_focal_length(_fov[1], film_size[1], true);
|
|
adjust_comp_flags(0, CF_focal_length);
|
|
}
|
|
_fov[0] = film_to_fov(film_size[0], _focal_length, false);
|
|
got_hfov = true;
|
|
}
|
|
|
|
if (!got_vfov) {
|
|
if ((_user_flags & UF_focal_length) == 0 &&
|
|
(_comp_flags & CF_focal_length) == 0) {
|
|
// If we don't have an explicit focal length, we can infer it
|
|
// from the above.
|
|
nassertv(got_hfov);
|
|
_focal_length = fov_to_focal_length(_fov[0], film_size[0], true);
|
|
adjust_comp_flags(0, CF_focal_length);
|
|
}
|
|
_fov[1] = film_to_fov(film_size[1], _focal_length, false);
|
|
got_vfov = true;
|
|
}
|
|
|
|
if (!got_min_fov) {
|
|
_min_fov = film_size[0] < film_size[1] ? _fov[0] : _fov[1];
|
|
got_min_fov = true;
|
|
}
|
|
|
|
nassertv(got_hfov && got_vfov && got_min_fov);
|
|
adjust_comp_flags(0, CF_fov);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_aspect_ratio
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the aspect ratio of the film rectangle, as a
|
|
// ratio of width to height.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_aspect_ratio() {
|
|
if ((_user_flags & UF_aspect_ratio) == 0) {
|
|
const LVecBase2f &film_size = get_film_size();
|
|
if (film_size[1] == 0.0f) {
|
|
_aspect_ratio = 1.0f;
|
|
} else {
|
|
_aspect_ratio = film_size[0] / film_size[1];
|
|
}
|
|
}
|
|
adjust_comp_flags(0, CF_aspect_ratio);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_view_hpr
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the Euler angles representing the lens'
|
|
// rotation.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_view_hpr() {
|
|
if ((_user_flags & UF_view_hpr) == 0) {
|
|
const LMatrix4f &view_mat = get_view_mat();
|
|
LVecBase3f scale, shear, translate;
|
|
decompose_matrix(view_mat, scale, shear, _view_hpr, translate, _cs);
|
|
}
|
|
adjust_comp_flags(0, CF_view_hpr);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_view_vector
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the view vector and up vector for the lens.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_view_vector() {
|
|
if ((_user_flags & UF_view_vector) == 0) {
|
|
const LMatrix4f &view_mat = get_view_mat();
|
|
_view_vector = LVector3f::forward(_cs) * view_mat;
|
|
_up_vector = LVector3f::up(_cs) * view_mat;
|
|
}
|
|
adjust_comp_flags(0, CF_view_vector);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_projection_mat
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the complete transformation matrix from 3-d
|
|
// point to 2-d point, if the lens is linear.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_projection_mat() {
|
|
_projection_mat =
|
|
_projection_mat_left =
|
|
_projection_mat_right =
|
|
_projection_mat_inv =
|
|
_projection_mat_left_inv =
|
|
_projection_mat_right_inv =
|
|
LMatrix4f::ident_mat();
|
|
adjust_comp_flags(0, CF_projection_mat | CF_projection_mat_inv |
|
|
CF_projection_mat_left_inv |CF_projection_mat_right_inv);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_film_mat
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the matrix that transforms from a point
|
|
// behind the lens to a point on the film.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_film_mat() {
|
|
// The lens will return a point in the range [-film_size/2,
|
|
// film_size/2] in each dimension. Convert this to [-1, 1], and
|
|
// also apply the offset.
|
|
|
|
// We declare these two as local variables, instead of references,
|
|
// to work around a VC7 compiler bug.
|
|
LVecBase2f film_size = get_film_size();
|
|
LVector2f film_offset = get_film_offset();
|
|
|
|
float scale_x = 2.0f / film_size[0];
|
|
float scale_y = 2.0f / film_size[1];
|
|
_film_mat.set(scale_x, 0.0f, 0.0f, 0.0f,
|
|
0.0f, scale_y, 0.0f, 0.0f,
|
|
0.0f, 0.0f, 1.0f, 0.0f,
|
|
-film_offset[0] * scale_x, -film_offset[1] * scale_y, 0.0f, 1.0f);
|
|
|
|
if ((_user_flags & UF_keystone) != 0) {
|
|
_film_mat = LMatrix4f(1.0f, 0.0f, _keystone[0], _keystone[0],
|
|
0.0f, 1.0f, _keystone[1], _keystone[1],
|
|
0.0f, 0.0f, 1.0f, 0.0f,
|
|
0.0f, 0.0f, 0.0f, 1.0f) * _film_mat;
|
|
}
|
|
|
|
adjust_comp_flags(CF_film_mat_inv,
|
|
CF_film_mat);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::compute_lens_mat
|
|
// Access: Protected, Virtual
|
|
// Description: Computes the matrix that transforms from a point
|
|
// in front of the lens to a point in space.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
compute_lens_mat() {
|
|
if ((_user_flags & UF_view_mat) == 0) {
|
|
if ((_user_flags & UF_view_hpr) != 0) {
|
|
compose_matrix(_lens_mat,
|
|
LVecBase3f(1.0f, 1.0f, 1.0f),
|
|
LVecBase3f(0.0f, 0.0f, 0.0f),
|
|
_view_hpr,
|
|
LVecBase3f(0.0f, 0.0f, 0.0f), _cs);
|
|
|
|
} else if ((_user_flags & UF_view_vector) != 0) {
|
|
look_at(_lens_mat, _view_vector, _up_vector, _cs);
|
|
|
|
} else {
|
|
_lens_mat = LMatrix4f::ident_mat();
|
|
}
|
|
}
|
|
adjust_comp_flags(CF_lens_mat_inv,
|
|
CF_lens_mat);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::fov_to_film
|
|
// Access: Protected, Virtual
|
|
// Description: Given a field of view in degrees and a focal length,
|
|
// compute the corresponding width (or height) on the
|
|
// film. If horiz is true, this is in the horizontal
|
|
// direction; otherwise, it is in the vertical direction
|
|
// (some lenses behave differently in each direction).
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
fov_to_film(float, float, bool) const {
|
|
return 1.0f;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::fov_to_focal_length
|
|
// Access: Protected, Virtual
|
|
// Description: Given a field of view in degrees and a width (or
|
|
// height) on the film, compute the focal length of the
|
|
// lens. If horiz is true, this is in the horizontal
|
|
// direction; otherwise, it is in the vertical direction
|
|
// (some lenses behave differently in each direction).
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
fov_to_focal_length(float, float, bool) const {
|
|
return 1.0f;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::film_to_fov
|
|
// Access: Protected, Virtual
|
|
// Description: Given a width (or height) on the film and a focal
|
|
// length, compute the field of view in degrees. If
|
|
// horiz is true, this is in the horizontal direction;
|
|
// otherwise, it is in the vertical direction (some
|
|
// lenses behave differently in each direction).
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
film_to_fov(float, float, bool) const {
|
|
return default_fov;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::resequence_fov_triad
|
|
// Access: Private, Static
|
|
// Description: Called whenever the user changes one of the three FOV
|
|
// parameters: fov, focal length, or film size. This
|
|
// rearranges the three sequence numbers so the newest
|
|
// parameter has value 2, and the older parameters are
|
|
// kept in sequence order.
|
|
//
|
|
// This is used to determine which two parameters of the
|
|
// three are the most recently changed, and conversely,
|
|
// which one the user has *not* changed recently. It is
|
|
// this third value which should be discarded.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
resequence_fov_triad(char &newest, char &older_a, char &older_b) {
|
|
nassertv(newest + older_a + older_b == 3);
|
|
switch (newest) {
|
|
case 0:
|
|
newest = 2;
|
|
older_a--;
|
|
older_b--;
|
|
nassertv(older_a + older_b == 1);
|
|
return;
|
|
|
|
case 1:
|
|
newest = 2;
|
|
if (older_a == 2) {
|
|
nassertv(older_b == 0);
|
|
older_a = 1;
|
|
} else {
|
|
nassertv(older_a == 0 && older_b == 2);
|
|
older_b = 1;
|
|
}
|
|
return;
|
|
|
|
case 2:
|
|
nassertv(older_a + older_b == 1);
|
|
return;
|
|
|
|
default:
|
|
gobj_cat.error()
|
|
<< "Invalid fov sequence numbers in lens: " << newest << ", " << older_a
|
|
<< ", " << older_b << "\n";
|
|
nassertv(false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::define_geom_data
|
|
// Access: Private
|
|
// Description: Adjusts (or defines for the first time) all the
|
|
// vertices in the _geom_data to match the properties of
|
|
// the lens. This will update the visual representation
|
|
// of the lens's frustum to match the changing
|
|
// parameters. Returns the number of line segments per
|
|
// edge.
|
|
////////////////////////////////////////////////////////////////////
|
|
int Lens::
|
|
define_geom_data() {
|
|
int num_segments = 1;
|
|
if (!is_linear()) {
|
|
num_segments = 10;
|
|
}
|
|
|
|
if (_geom_data == (GeomVertexData *)NULL) {
|
|
_geom_data = new GeomVertexData
|
|
("lens", GeomVertexFormat::get_v3(),
|
|
Geom::UH_dynamic);
|
|
}
|
|
|
|
GeomVertexWriter vertex(_geom_data, InternalName::get_vertex());
|
|
LPoint3f near_point, far_point;
|
|
for (int si = 0; si < num_segments; si++) {
|
|
float t = 2.0f * (float)si / (float)num_segments;
|
|
|
|
// Upper left, top edge.
|
|
LPoint2f p1(-1.0f + t, 1.0f);
|
|
if (!extrude(p1, near_point, far_point)) {
|
|
// Hey, this point is off the lens! Can't do a frustum.
|
|
return 0;
|
|
}
|
|
vertex.add_data3f(near_point);
|
|
vertex.add_data3f(far_point);
|
|
|
|
// Upper right, right edge.
|
|
LPoint2f p2(1.0f, 1.0f - t);
|
|
if (!extrude(p2, near_point, far_point)) {
|
|
// Hey, this point is off the lens! Can't do a frustum.
|
|
return 0;
|
|
}
|
|
vertex.add_data3f(near_point);
|
|
vertex.add_data3f(far_point);
|
|
|
|
// Lower right, bottom edge.
|
|
LPoint2f p3(1.0f - t, -1.0f);
|
|
if (!extrude(p3, near_point, far_point)) {
|
|
// Hey, this point is off the lens! Can't do a frustum.
|
|
return 0;
|
|
}
|
|
vertex.add_data3f(near_point);
|
|
vertex.add_data3f(far_point);
|
|
|
|
// Lower left, left edge.
|
|
LPoint2f p4(-1.0f, -1.0f + t);
|
|
if (!extrude(p4, near_point, far_point)) {
|
|
// Hey, this point is off the lens! Can't do a frustum.
|
|
return 0;
|
|
}
|
|
vertex.add_data3f(near_point);
|
|
vertex.add_data3f(far_point);
|
|
}
|
|
|
|
// Finally, add one more pair for the viewing axis.
|
|
LPoint3f near_axis = LPoint3f::origin(_cs) + LVector3f::forward(_cs) * _near_distance;
|
|
LPoint3f far_axis = LPoint3f::origin(_cs) + LVector3f::forward(_cs) * _far_distance;
|
|
const LMatrix4f &lens_mat = get_lens_mat();
|
|
near_axis = near_axis * lens_mat;
|
|
far_axis = far_axis * lens_mat;
|
|
vertex.add_data3f(near_axis);
|
|
vertex.add_data3f(far_axis);
|
|
|
|
return num_segments;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::build_shear_mat
|
|
// Access: Private, Static
|
|
// Description: A support function for set_frustum_from_corners(),
|
|
// this computes a matrix that will shear the four
|
|
// indicated points to the most nearly rectangular.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
build_shear_mat(LMatrix4f &shear_mat,
|
|
const LPoint3f &cul, const LPoint3f &cur,
|
|
const LPoint3f &cll, const LPoint3f &clr) {
|
|
// Fit a parallelogram around these four points.
|
|
|
|
// Put the points in an array so we can rotate it around to find
|
|
// the longest edge.
|
|
LPoint3f points[4] = {
|
|
cul, cur, clr, cll
|
|
};
|
|
|
|
float max_edge_length = -1.0f;
|
|
int base_edge = -1;
|
|
for (int i = 0; i < 4; i++) {
|
|
LVector3f edge = points[(i + 1) % 4] - points[i];
|
|
float length = edge.length_squared();
|
|
if (length > max_edge_length) {
|
|
base_edge = i;
|
|
max_edge_length = length;
|
|
}
|
|
}
|
|
|
|
const LPoint3f &base_origin = points[base_edge];
|
|
LVector3f base_vec = points[(base_edge + 1) % 4] - base_origin;
|
|
|
|
float base_edge_length = csqrt(max_edge_length);
|
|
|
|
// The longest edge is the base of our parallelogram. The parallel
|
|
// edge must pass through the point furthest from this edge.
|
|
|
|
int a = (base_edge + 2) % 4;
|
|
int b = (base_edge + 3) % 4;
|
|
|
|
float a_dist = sqr_dist_to_line(points[a], base_origin, base_vec);
|
|
float b_dist = sqr_dist_to_line(points[b], base_origin, base_vec);
|
|
|
|
int far_point;
|
|
float dist;
|
|
if (a_dist > b_dist) {
|
|
far_point = a;
|
|
dist = csqrt(a_dist);
|
|
} else {
|
|
far_point = b;
|
|
dist = csqrt(b_dist);
|
|
}
|
|
|
|
// Try to make the parallelogram as nearly rectangular as possible.
|
|
// How suitable is a true rectangle?
|
|
LVector3f perpendic = base_vec.cross(LVector3f(0.0f, -1.0f, 0.0f));
|
|
perpendic.normalize();
|
|
perpendic *= dist;
|
|
LPoint3f parallel_origin = points[base_edge] + perpendic;
|
|
|
|
// It follows that far_point is on the line passing through the
|
|
// parallel edge. Is it within the endpoints?
|
|
LVector3f base_norm_vec = base_vec / base_edge_length;
|
|
|
|
LVector3f far_point_delta = points[far_point] - parallel_origin;
|
|
float far_point_pos = far_point_delta.dot(base_norm_vec);
|
|
|
|
if (far_point_pos < 0.0f) {
|
|
// We have to slide the parallel_origin back to include far_point.
|
|
parallel_origin += base_norm_vec * far_point_pos;
|
|
|
|
} else if (far_point_pos > base_edge_length) {
|
|
// We have to slide the parallel_origin forward to include
|
|
// far_point.
|
|
parallel_origin += base_norm_vec * (far_point_pos - base_edge_length);
|
|
}
|
|
|
|
// Finally, is the other point within the parallelogram?
|
|
float t;
|
|
float Ox = parallel_origin[0];
|
|
float Oy = parallel_origin[2];
|
|
float Vx = base_vec[0];
|
|
float Vy = base_vec[2];
|
|
float Ax, Ay, Bx, By;
|
|
|
|
if (far_point == a) {
|
|
// near point is b
|
|
LVector3f v = points[b] - base_origin;
|
|
Ax = points[b][0];
|
|
Ay = points[b][2];
|
|
Bx = v[0];
|
|
By = v[2];
|
|
} else {
|
|
// near point is a
|
|
LVector3f v = points[a] - (base_origin + base_vec);
|
|
Ax = points[a][0];
|
|
Ay = points[a][2];
|
|
Bx = v[0];
|
|
By = v[2];
|
|
}
|
|
t = ((Ox - Ax) * By + (Ay - Oy) * Bx) / (Bx * Vy - By * Vx);
|
|
|
|
if (t < 0.0f) {
|
|
// We need to slide the parallel_origin back to include
|
|
// the near point.
|
|
parallel_origin += base_vec * t;
|
|
} else if (t > 1.0f) {
|
|
// We need to slide the parallel_origin forward to include the far
|
|
// point.
|
|
parallel_origin += base_vec * (1.0f - t);
|
|
}
|
|
|
|
LVector3f adjacent_norm_vec = parallel_origin - base_origin;
|
|
adjacent_norm_vec.normalize();
|
|
|
|
// Now we've defined a parallelogram that includes all four points,
|
|
// and we're ready to build a shear transform.
|
|
shear_mat = LMatrix4f::ident_mat();
|
|
|
|
// The edges of the parallelogram become the axes.
|
|
switch (base_edge) {
|
|
case 0:
|
|
// The base_origin is the upper-left corner. X axis is base_norm_vec,
|
|
// Z axis is -adjacent_norm_vec.
|
|
shear_mat.set_row(0, base_norm_vec);
|
|
shear_mat.set_row(2, -adjacent_norm_vec);
|
|
break;
|
|
|
|
case 1:
|
|
// The base_origin is the upper-right corner. X axis is
|
|
// -adjacent_norm_vec, Z axis is -base_norm_vec.
|
|
shear_mat.set_row(0, -adjacent_norm_vec);
|
|
shear_mat.set_row(2, -base_norm_vec);
|
|
break;
|
|
|
|
case 2:
|
|
// The base_origin is the lower-right corner. X axis is
|
|
// -base_norm_vec, Z axis is adjacent_norm_vec.
|
|
shear_mat.set_row(0, -base_norm_vec);
|
|
shear_mat.set_row(2, adjacent_norm_vec);
|
|
break;
|
|
|
|
case 3:
|
|
// The base_origin is the lower-left corner. X axis is
|
|
// adjacent_norm_vec, Z axis is base_norm_vec.
|
|
shear_mat.set_row(0, adjacent_norm_vec);
|
|
shear_mat.set_row(2, base_norm_vec);
|
|
break;
|
|
|
|
default:
|
|
nassertv(false);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::sqr_dist_to_line
|
|
// Access: Private, Static
|
|
// Description: A support function for build_shear_mat(), this
|
|
// computes the minimum distance from a point to a line,
|
|
// and returns the distance squared.
|
|
////////////////////////////////////////////////////////////////////
|
|
float Lens::
|
|
sqr_dist_to_line(const LPoint3f &point, const LPoint3f &origin,
|
|
const LVector3f &vec) {
|
|
LVector3f norm = vec;
|
|
norm.normalize();
|
|
LVector3f d = point - origin;
|
|
float hyp_2 = d.length_squared();
|
|
float leg = d.dot(norm);
|
|
return hyp_2 - leg * leg;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
write_datagram(BamWriter *manager, Datagram &dg) {
|
|
TypedWritable::write_datagram(manager, dg);
|
|
|
|
dg.add_string(_change_event);
|
|
dg.add_uint8((int)_cs);
|
|
_film_size.write_datagram(dg);
|
|
_film_offset.write_datagram(dg);
|
|
dg.add_float32(_focal_length);
|
|
_fov.write_datagram(dg);
|
|
dg.add_float32(_aspect_ratio);
|
|
dg.add_float32(_near_distance);
|
|
dg.add_float32(_far_distance);
|
|
dg.add_uint16(_user_flags);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: Lens::fillin
|
|
// Access: Protected
|
|
// Description: This internal function is called by make_from_bam to
|
|
// read in all of the relevant data from the BamFile for
|
|
// the new Lens.
|
|
////////////////////////////////////////////////////////////////////
|
|
void Lens::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
TypedWritable::fillin(scan, manager);
|
|
|
|
_change_event = scan.get_string();
|
|
_cs = (CoordinateSystem)scan.get_uint8();
|
|
_film_size.read_datagram(scan);
|
|
_film_offset.read_datagram(scan);
|
|
_focal_length = scan.get_float32();
|
|
_fov.read_datagram(scan);
|
|
_aspect_ratio = scan.get_float32();
|
|
_near_distance = scan.get_float32();
|
|
_far_distance = scan.get_float32();
|
|
_user_flags = scan.get_uint16();
|
|
|
|
_comp_flags = 0;
|
|
}
|