Cloned library of VTK-5.0.0 with extra build files for internal package management.
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

/*=========================================================================
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);
}