You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
479 lines
15 KiB
479 lines
15 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkPerspectiveTransform.cxx,v $
|
|
|
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
|
All rights reserved.
|
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
|
|
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. See the above copyright notice for more information.
|
|
|
|
=========================================================================*/
|
|
#include "vtkPerspectiveTransform.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkObjectFactory.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
vtkCxxRevisionMacro(vtkPerspectiveTransform, "$Revision: 1.29 $");
|
|
vtkStandardNewMacro(vtkPerspectiveTransform);
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkPerspectiveTransform::vtkPerspectiveTransform()
|
|
{
|
|
this->Input = NULL;
|
|
|
|
// most of the functionality is provided by the concatenation
|
|
this->Concatenation = vtkTransformConcatenation::New();
|
|
|
|
// the stack will be allocated the first time Push is called
|
|
this->Stack = NULL;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkPerspectiveTransform::~vtkPerspectiveTransform()
|
|
{
|
|
this->SetInput(NULL);
|
|
|
|
if (this->Concatenation)
|
|
{
|
|
this->Concatenation->Delete();
|
|
}
|
|
if (this->Stack)
|
|
{
|
|
this->Stack->Delete();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkPerspectiveTransform::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Update();
|
|
|
|
this->Superclass::PrintSelf(os, indent);
|
|
os << indent << "Input: (" << this->Input << ")\n";
|
|
os << indent << "InverseFlag: " << this->GetInverseFlag() << "\n";
|
|
os << indent << "NumberOfConcatenatedTransforms: " <<
|
|
this->GetNumberOfConcatenatedTransforms() << "\n";
|
|
if (this->GetNumberOfConcatenatedTransforms() != 0)
|
|
{
|
|
int n = this->GetNumberOfConcatenatedTransforms();
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
vtkHomogeneousTransform *t = this->GetConcatenatedTransform(i);
|
|
os << indent << " " << i << ": " << t->GetClassName() << " at " <<
|
|
t << "\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkPerspectiveTransform::Concatenate(vtkHomogeneousTransform *transform)
|
|
{
|
|
if (transform->CircuitCheck(this))
|
|
{
|
|
vtkErrorMacro("Concatenate: this would create a circular reference.");
|
|
return;
|
|
}
|
|
this->Concatenation->Concatenate(transform);
|
|
this->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkPerspectiveTransform::SetInput(vtkHomogeneousTransform *input)
|
|
{
|
|
if (this->Input == input)
|
|
{
|
|
return;
|
|
}
|
|
if (input && input->CircuitCheck(this))
|
|
{
|
|
vtkErrorMacro("SetInput: this would create a circular reference.");
|
|
return;
|
|
}
|
|
if (this->Input)
|
|
{
|
|
this->Input->Delete();
|
|
}
|
|
this->Input = input;
|
|
if (this->Input)
|
|
{
|
|
this->Input->Register(this);
|
|
}
|
|
this->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkPerspectiveTransform::CircuitCheck(vtkAbstractTransform *transform)
|
|
{
|
|
if (this->vtkHomogeneousTransform::CircuitCheck(transform) ||
|
|
this->Input && this->Input->CircuitCheck(transform))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
int n = this->Concatenation->GetNumberOfTransforms();
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
if (this->Concatenation->GetTransform(i)->CircuitCheck(transform))
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkAbstractTransform *vtkPerspectiveTransform::MakeTransform()
|
|
{
|
|
return vtkPerspectiveTransform::New();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
unsigned long vtkPerspectiveTransform::GetMTime()
|
|
{
|
|
unsigned long mtime = this->vtkHomogeneousTransform::GetMTime();
|
|
unsigned long mtime2;
|
|
|
|
if (this->Input)
|
|
{
|
|
mtime2 = this->Input->GetMTime();
|
|
if (mtime2 > mtime)
|
|
{
|
|
mtime = mtime2;
|
|
}
|
|
}
|
|
mtime2 = this->Concatenation->GetMaxMTime();
|
|
if (mtime2 > mtime)
|
|
{
|
|
return mtime2;
|
|
}
|
|
return mtime;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkPerspectiveTransform::InternalDeepCopy(vtkAbstractTransform *gtrans)
|
|
{
|
|
vtkPerspectiveTransform *transform = (vtkPerspectiveTransform *)gtrans;
|
|
|
|
// copy the input
|
|
this->SetInput(transform->Input);
|
|
|
|
// copy the concatenation
|
|
this->Concatenation->DeepCopy(transform->Concatenation);
|
|
|
|
// copy the stack
|
|
if (transform->Stack)
|
|
{
|
|
if (this->Stack == NULL)
|
|
{
|
|
this->Stack = vtkTransformConcatenationStack::New();
|
|
}
|
|
this->Stack->DeepCopy(transform->Stack);
|
|
}
|
|
else
|
|
{
|
|
if (this->Stack)
|
|
{
|
|
this->Stack->Delete();
|
|
this->Stack = NULL;
|
|
}
|
|
}
|
|
|
|
// defer to superclass
|
|
this->vtkHomogeneousTransform::InternalDeepCopy(transform);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkPerspectiveTransform::InternalUpdate()
|
|
{
|
|
// copy matrix from input
|
|
if (this->Input)
|
|
{
|
|
this->Matrix->DeepCopy(this->Input->GetMatrix());
|
|
// if inverse flag is set, invert the matrix
|
|
if (this->Concatenation->GetInverseFlag())
|
|
{
|
|
this->Matrix->Invert();
|
|
}
|
|
}
|
|
else
|
|
// no input, start with identity
|
|
{
|
|
this->Matrix->Identity();
|
|
}
|
|
|
|
int i;
|
|
int nTransforms = this->Concatenation->GetNumberOfTransforms();
|
|
int nPreTransforms = this->Concatenation->GetNumberOfPreTransforms();
|
|
|
|
// concatenate PreTransforms
|
|
for (i = nPreTransforms-1; i >= 0; i--)
|
|
{
|
|
vtkHomogeneousTransform *transform =
|
|
(vtkHomogeneousTransform *)this->Concatenation->GetTransform(i);
|
|
vtkMatrix4x4::Multiply4x4(this->Matrix,transform->GetMatrix(),
|
|
this->Matrix);
|
|
}
|
|
|
|
// concatenate PostTransforms
|
|
for (i = nPreTransforms; i < nTransforms; i++)
|
|
{
|
|
vtkHomogeneousTransform *transform =
|
|
(vtkHomogeneousTransform *)this->Concatenation->GetTransform(i);
|
|
vtkMatrix4x4::Multiply4x4(transform->GetMatrix(),this->Matrix,
|
|
this->Matrix);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Utility for adjusting the window range to a new one. Usually the
|
|
// previous range was ([-1,+1],[-1,+1]) as per Ortho and Frustum, and you
|
|
// are mapping to the display coordinate range ([0,width-1],[0,height-1]).
|
|
void vtkPerspectiveTransform::AdjustViewport(double oldXMin, double oldXMax,
|
|
double oldYMin, double oldYMax,
|
|
double newXMin, double newXMax,
|
|
double newYMin, double newYMax)
|
|
{
|
|
double matrix[4][4];
|
|
vtkMatrix4x4::Identity(*matrix);
|
|
|
|
matrix[0][0] = (newXMax - newXMin)/(oldXMax - oldXMin);
|
|
matrix[1][1] = (newYMax - newYMin)/(oldYMax - oldYMin);
|
|
|
|
matrix[0][3] = (newXMin*oldXMax - newXMax*oldXMin)/(oldXMax - oldXMin);
|
|
matrix[1][3] = (newYMin*oldYMax - newYMax*oldYMin)/(oldYMax - oldYMin);
|
|
|
|
this->Concatenate(*matrix);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// Utility for adjusting the min/max range of the Z buffer. Usually
|
|
// the oldZMin, oldZMax are [-1,+1] as per Ortho and Frustum, and
|
|
// you are mapping the Z buffer to a new range.
|
|
void vtkPerspectiveTransform::AdjustZBuffer(double oldZMin, double oldZMax,
|
|
double newZMin, double newZMax)
|
|
{
|
|
double matrix[4][4];
|
|
vtkMatrix4x4::Identity(*matrix);
|
|
|
|
matrix[2][2] = (newZMax - newZMin)/(oldZMax - oldZMin);
|
|
matrix[2][3] = (newZMin*oldZMax - newZMax*oldZMin)/(oldZMax - oldZMin);
|
|
|
|
this->Concatenate(*matrix);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The orthographic perspective maps [xmin,xmax], [ymin,ymax], [-znear,-zfar]
|
|
// to [-1,+1], [-1,+1], [-1,+1].
|
|
// From the OpenGL Programmer's guide, 2nd Ed.
|
|
void vtkPerspectiveTransform::Ortho(double xmin, double xmax,
|
|
double ymin, double ymax,
|
|
double znear, double zfar)
|
|
{
|
|
double matrix[4][4];
|
|
vtkMatrix4x4::Identity(*matrix);
|
|
|
|
matrix[0][0] = 2/(xmax - xmin);
|
|
matrix[1][1] = 2/(ymax - ymin);
|
|
matrix[2][2] = -2/(zfar - znear);
|
|
|
|
matrix[0][3] = -(xmin + xmax)/(xmax - xmin);
|
|
matrix[1][3] = -(ymin + ymax)/(ymax - ymin);
|
|
matrix[2][3] = -(znear + zfar)/(zfar - znear);
|
|
|
|
this->Concatenate(*matrix);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The frustrum perspective maps a frustum with the front plane at -znear
|
|
// which has extent [xmin,xmax],[ymin,ymax] and a back plane at -zfar
|
|
// to [-1,+1], [-1,+1], [-1,+1].
|
|
// From the OpenGL Programmer's guide, 2nd Ed.
|
|
void vtkPerspectiveTransform::Frustum(double xmin, double xmax,
|
|
double ymin, double ymax,
|
|
double znear, double zfar)
|
|
{
|
|
double matrix[4][4];
|
|
|
|
matrix[0][0] = 2*znear/(xmax - xmin);
|
|
matrix[1][0] = 0;
|
|
matrix[2][0] = 0;
|
|
matrix[3][0] = 0;
|
|
|
|
matrix[0][1] = 0;
|
|
matrix[1][1] = 2*znear/(ymax - ymin);
|
|
matrix[2][1] = 0;
|
|
matrix[3][1] = 0;
|
|
|
|
matrix[0][2] = (xmin + xmax)/(xmax - xmin);
|
|
matrix[1][2] = (ymin + ymax)/(ymax - ymin);
|
|
matrix[2][2] = -(znear + zfar)/(zfar - znear);
|
|
matrix[3][2] = -1;
|
|
|
|
matrix[0][3] = 0;
|
|
matrix[1][3] = 0;
|
|
matrix[2][3] = -2*znear*zfar/(zfar - znear);
|
|
matrix[3][3] = 0;
|
|
|
|
this->Concatenate(*matrix);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// For convenience, an easy way to set up a symmetrical frustum.
|
|
void vtkPerspectiveTransform::Perspective(double angle, double aspect,
|
|
double znear, double zfar)
|
|
{
|
|
double ymax = tan(angle*vtkMath::DoubleDegreesToRadians()/2)*znear;
|
|
double ymin = -ymax;
|
|
|
|
double xmax = ymax*aspect;
|
|
double xmin = -xmax;
|
|
|
|
this->Frustum(xmin, xmax, ymin, ymax, znear, zfar);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The Shear method can be used after Perspective to create correct
|
|
// perspective views for use with head-tracked stereo on a flat, fixed
|
|
// (i.e. not head-mounted) viewing screen.
|
|
//
|
|
// You must measure the eye position relative to the center of the
|
|
// RenderWindow (or the center of the screen, if the window is
|
|
// full-screen). The following applies: +x is 'right', +y is 'up',
|
|
// and zplane is the distance from screen to the eye.
|
|
//
|
|
// Here is some info on how to set this up properly:
|
|
//
|
|
// - Decide on a real-world-coords to virtual-world-coords conversion
|
|
// factor that is appropriate for the scene you are viewing.
|
|
// - The screen is the focal plane, the near clipping plane lies in
|
|
// front of the screen and far clipping plane lies behind.
|
|
// Measure the (x,y,z) displacent from the center of the screen to
|
|
// your eye. Scale these by the factor you chose.
|
|
// - After you have scaled x, y, and z call Shear(x/z,y/z,z).
|
|
//
|
|
// We're not done yet!
|
|
//
|
|
// - When you set up the view using SetupCamera(), the camera should
|
|
// be placed the same distance from the screen as your eye, but so
|
|
// that it looks at the screen straight-on. I.e. it must lie along
|
|
// the ray perpendicular to the screen which passes through the center
|
|
// of screen (i.e. the center of the screen, in world coords, corresponds
|
|
// to the focal point). Whatever 'z' you used in Shear(), the
|
|
// camera->focalpoint distance should be the same. If you are
|
|
// wondering why you don't set the camera position to be the eye
|
|
// position, don't worry -- the combination of SetupCamera() and
|
|
// an Oblique() shear about the focal plane does precisely that.
|
|
//
|
|
// - When you set up the view frustum using Perspective(),
|
|
// set the angle to 2*atan(0.5*height/z) where 'height' is
|
|
// the height of your screen multiplied by the real-to-virtual
|
|
// scale factor. Don't forget to convert the angle to degrees.
|
|
// - Though it is not absolutely necessary, you might want to
|
|
// keep your near and far clipping planes at constant distances
|
|
// from the focal point. By default, they are set up relative
|
|
// to the camera position.
|
|
//
|
|
// The order in which you apply the transformations, in
|
|
// PreMultiply mode, is:
|
|
// 1) Perspective(), 2) Shear(), 3) SetupCamera()
|
|
//
|
|
// Take the above advice with a grain of salt... I've never actually
|
|
// tried any of this except for with pencil & paper. Looks good on
|
|
// paper, though!
|
|
void vtkPerspectiveTransform::Shear(double dxdz, double dydz, double zplane)
|
|
{
|
|
double matrix[4][4];
|
|
vtkMatrix4x4::Identity(*matrix);
|
|
|
|
// everything is negative because the position->focalpoint vector
|
|
// is in the -z direction, hence z distances along that vector
|
|
// are negative.
|
|
|
|
// shear according to the eye position relative to the screen
|
|
matrix[0][2] = -dxdz;
|
|
matrix[1][2] = -dydz;
|
|
|
|
// shift so that view rays converge in the focal plane
|
|
matrix[0][3] = -zplane*dxdz;
|
|
matrix[1][3] = -zplane*dydz;
|
|
|
|
// concatenate with the current matrix
|
|
this->Concatenate(*matrix);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// For convenience -- this is sufficient for most people's stereo needs.
|
|
// Set the angle to negative for left eye, positive for right eye.
|
|
void vtkPerspectiveTransform::Stereo(double angle, double focaldistance)
|
|
{
|
|
double dxdz = tan(angle*vtkMath::DoubleDegreesToRadians());
|
|
|
|
this->Shear(dxdz, 0.0, focaldistance);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkPerspectiveTransform::SetupCamera(const double position[3],
|
|
const double focalPoint[3],
|
|
const double viewUp[3])
|
|
{
|
|
double matrix[4][4];
|
|
vtkMatrix4x4::Identity(*matrix);
|
|
|
|
// the view directions correspond to the rows of the rotation matrix,
|
|
// so we'll make the connection explicit
|
|
double *viewSideways = matrix[0];
|
|
double *orthoViewUp = matrix[1];
|
|
double *viewPlaneNormal = matrix[2];
|
|
|
|
// set the view plane normal from the view vector
|
|
viewPlaneNormal[0] = position[0] - focalPoint[0];
|
|
viewPlaneNormal[1] = position[1] - focalPoint[1];
|
|
viewPlaneNormal[2] = position[2] - focalPoint[2];
|
|
vtkMath::Normalize(viewPlaneNormal);
|
|
|
|
// orthogonalize viewUp and compute viewSideways
|
|
vtkMath::Cross(viewUp,viewPlaneNormal,viewSideways);
|
|
vtkMath::Normalize(viewSideways);
|
|
vtkMath::Cross(viewPlaneNormal,viewSideways,orthoViewUp);
|
|
|
|
// translate by the vector from the position to the origin
|
|
double delta[4];
|
|
delta[0] = -position[0];
|
|
delta[1] = -position[1];
|
|
delta[2] = -position[2];
|
|
delta[3] = 0.0; // yes, this should be zero, not one
|
|
|
|
vtkMatrix4x4::MultiplyPoint(*matrix,delta,delta);
|
|
|
|
matrix[0][3] = delta[0];
|
|
matrix[1][3] = delta[1];
|
|
matrix[2][3] = delta[2];
|
|
|
|
// apply the transformation
|
|
this->Concatenate(*matrix);
|
|
}
|
|
|
|
void vtkPerspectiveTransform::SetupCamera(double p0, double p1, double p2,
|
|
double fp0, double fp1, double fp2,
|
|
double vup0, double vup1, double vup2)
|
|
{
|
|
double p[3], fp[3], vup[3];
|
|
p[0] = p0;
|
|
p[1] = p1;
|
|
p[2] = p2;
|
|
fp[0] = fp0;
|
|
fp[1] = fp1;
|
|
fp[2] = fp2;
|
|
vup[0] = vup0;
|
|
vup[1] = vup1;
|
|
vup[2] = vup2;
|
|
|
|
this->SetupCamera(p, fp, vup);
|
|
}
|
|
|
|
|