Serious-Engine/Sources/WorldEditor/Viewers.cpp
2016-03-11 18:20:51 -06:00

466 lines
15 KiB
C++

/* Copyright (c) 2002-2012 Croteam Ltd.
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as published by
the Free Software Foundation
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
#include "StdAfx.h"
#include "WorldEditor.h"
/////////////////////////////////////////////////////////////////////
// CSlaveViewer
#define MIN_TARGET_DISTANCE 0.1f
#define MAX_TARGET_DISTANCE 640000.0f
extern FLOAT wed_fFrontClipDistance;
/*
* Default constructor
*/
CMasterViewer::CMasterViewer(void)
{
// set initial viewer variables, viewing tovards origin from ten meters distance
mv_plViewer.pl_PositionVector = FLOAT3D(3.0f, 4.0f, 10.0f);
mv_plViewer.pl_OrientationAngle = ANGLE3D(AngleDeg( 20.0f), AngleDeg( -20.0f), 0);
mv_fTargetDistance = 10.0f; // this must never be very small!
}
// get placement of the virtual target
CPlacement3D CMasterViewer::GetTargetPlacement(void) const
{
CPlacement3D plTarget;
// copy viewer to target
plTarget = mv_plViewer;
// get the direction vector of viewer's sight
FLOAT3D vDirection;
AnglesToDirectionVector(mv_plViewer.pl_OrientationAngle, vDirection);
// offset the target placement by target distance along the vector
plTarget.pl_PositionVector += vDirection*mv_fTargetDistance;
// return the target placement
return plTarget;
}
// set placement of the virtual target
void CMasterViewer::SetTargetPlacement(FLOAT3D f3dTarget)
{
CPlacement3D plViewer;
// copy the viewer's orientation, it will stay the same
plViewer.pl_OrientationAngle = mv_plViewer.pl_OrientationAngle;
// set position vector of new target
plViewer.pl_PositionVector = f3dTarget;
// translate viewer back from target for their distance
plViewer.Translate_OwnSystem( FLOAT3D( 0.0f, 0.0f, mv_fTargetDistance) );
// set new viewer's pacement
mv_plViewer = plViewer;
}
/*
* Convert from slave.
*/
void CMasterViewer::operator=(const CSlaveViewer &svSlave)
{
// copy target distance
mv_fTargetDistance = svSlave.sv_fTargetDistance;
// if is perspective
if( svSlave.IsPerspective())
{
// copy viewer placement
mv_plViewer = svSlave.sv_plViewer;
}
// if is isometric
else
{
// copy slave viewer position to master viewer position
mv_plViewer.pl_PositionVector = svSlave.sv_plViewer.pl_PositionVector;
// get the ray of the slave viewing direction
FLOAT3D vSlaveDirection;
AnglesToDirectionVector(svSlave.sv_plViewer.pl_OrientationAngle, vSlaveDirection);
// offset the viewer forward by target distance along the slave direction vector
mv_plViewer.pl_PositionVector += vSlaveDirection*mv_fTargetDistance;
// get the ray of the master viewing direction
FLOAT3D vMasterDirection;
AnglesToDirectionVector(mv_plViewer.pl_OrientationAngle, vMasterDirection);
// offset the viewer backward by target distance along the slave direction vector
mv_plViewer.pl_PositionVector -= vMasterDirection*mv_fTargetDistance;
}
}
/////////////////////////////////////////////////////////////////////
// CSlaveViewer
// get orientation angles for type of isometric viewer
ANGLE3D CSlaveViewer::GetAngleForIsometricType(void) const
{
// depending on the projection type, return angles
switch (sv_ProjectionType) {
case PT_ISOMETRIC_FRONT:
return ANGLE3D((ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 );
case PT_ISOMETRIC_RIGHT:
return ANGLE3D((ANGLE)ANGLE_90 ,(ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 );
case PT_ISOMETRIC_TOP:
return ANGLE3D((ANGLE)ANGLE_0 ,(ANGLE)ANGLE_270,(ANGLE)ANGLE_0 );
case PT_ISOMETRIC_BACK:
return ANGLE3D((ANGLE)ANGLE_180,(ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 );
case PT_ISOMETRIC_LEFT:
return ANGLE3D((ANGLE)ANGLE_270,(ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 );
case PT_ISOMETRIC_BOTTOM:
return ANGLE3D((ANGLE)ANGLE_0 ,(ANGLE)ANGLE_90 ,(ANGLE)ANGLE_0 );
default:
ASSERT(FALSE);
return ANGLE3D((ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 ,(ANGLE)ANGLE_0 );
}
}
/*
* Get zoom factor for the viewer.
*/
FLOAT CSlaveViewer::GetZoomFactor(void)
{
// calculate the zoom factor that as ratio of size in pixels and size in meters
// for target object
FLOAT fScreenX = (float)sv_pdpDrawPort->GetWidth();
ANGLE aHalfI = AngleDeg(90.0f)/2; // assume FOV of 90 degrees for perspective
FLOAT fPerspectiveRatio = fScreenX/(2.0f*Tan(aHalfI));
FLOAT fZoomFactor = fPerspectiveRatio/sv_fTargetDistance;
ASSERT( fZoomFactor > 0);
return fZoomFactor;
}
/*
* Get distance for wanted zoom factor for the viewer.
*/
FLOAT CSlaveViewer::GetDistanceForZoom(FLOAT fZoom)
{
// calculate the zoom factor that as ratio of size in pixels and size in meters
// for target object
FLOAT fScreenX = (float)sv_pdpDrawPort->GetWidth();
ANGLE aHalfI = AngleDeg(90.0f)/2; // assume FOV of 90 degrees for perspective
FLOAT fPerspectiveRatio = fScreenX/(2.0f*Tan(aHalfI));
FLOAT fDistance = fPerspectiveRatio / fZoom;
ASSERT( fDistance > 0);
return fDistance;
}
// get placement of the virtual target
CPlacement3D CSlaveViewer::GetTargetPlacement(void) const
{
CPlacement3D plTarget;
// copy viewer to target
plTarget = sv_plViewer;
// get the direction vector of viewer's sight
FLOAT3D vDirection;
AnglesToDirectionVector(sv_plViewer.pl_OrientationAngle, vDirection);
// offset the target placement by target distance along the vector
plTarget.pl_PositionVector += vDirection*sv_fTargetDistance;
// return the target placement
return plTarget;
}
/*
* Default constructor
*/
CSlaveViewer::CSlaveViewer(const CMasterViewer &mvMaster,
enum ProjectionType ptProjectionType,
const CPlacement3D &plGrid,
CDrawPort *pdpDrawPort)
{
// remember the drawport
sv_pdpDrawPort = pdpDrawPort;
// set projection type
sv_ProjectionType = ptProjectionType;
// set grid placement
sv_plGrid = plGrid;
// copy target distance
sv_fTargetDistance = mvMaster.mv_fTargetDistance;
// if is perspective
if( IsPerspective())
{
// copy viewer placement
sv_plViewer = mvMaster.mv_plViewer;
}
// if is isometric
else
{
// copy target position to slave viewer position
sv_plViewer.pl_PositionVector = mvMaster.GetTargetPlacement().pl_PositionVector;
// get the orientation depending on the isometry type
sv_plViewer.pl_OrientationAngle = GetAngleForIsometricType();
// transform to grid system
sv_plViewer.AbsoluteToRelative(sv_plGrid);
// get the ray of the viewing direction
FLOAT3D vDirection;
AnglesToDirectionVector(sv_plViewer.pl_OrientationAngle, vDirection);
// offset the viewer backward by target distance along the vector
sv_plViewer.pl_PositionVector -= vDirection*sv_fTargetDistance;
// transform back from grid system
sv_plViewer.RelativeToAbsolute(sv_plGrid);
}
}
/*
* create a perspective projection for this viewer
*/
void CSlaveViewer::MakePerspectiveProjection(CPerspectiveProjection3D &prPerspectiveProjection)
{
// if is perspective
ASSERT( IsPerspective());
// init projection parameters
prPerspectiveProjection.FOVL() = AngleDeg(90.0f);
prPerspectiveProjection.ScreenBBoxL() = FLOATaabbox2D(
FLOAT2D(0.0f, 0.0f),
FLOAT2D((float)sv_pdpDrawPort->GetWidth(), (float)sv_pdpDrawPort->GetHeight())
);
wed_fFrontClipDistance = Clamp( wed_fFrontClipDistance, 0.02f, 2.00f);
prPerspectiveProjection.FrontClipDistanceL() = wed_fFrontClipDistance;
prPerspectiveProjection.AspectRatioL() = 1.0f;
prPerspectiveProjection.ObjectStretchL() = FLOAT3D(1.0f, 1.0f, 1.0f);
// set up viewer position and placement
prPerspectiveProjection.ViewerPlacementL() = sv_plViewer;
prPerspectiveProjection.ObjectPlacementL().pl_PositionVector = FLOAT3D(0.0f, 0.0f, 0.0f);
prPerspectiveProjection.ObjectPlacementL().pl_OrientationAngle = ANGLE3D(0, 0, 0);
}
/*
* create a projection for this viewer
*/
void CSlaveViewer::MakeProjection(CAnyProjection3D &prProjection)
{
// if is perspective
if( IsPerspective()) {
// use perspective projection
CPerspectiveProjection3D prPerspectiveProjection;
// make perspective projection
MakePerspectiveProjection( prPerspectiveProjection);
// return the result
prProjection = prPerspectiveProjection;
// if is isometric
} else {
// use isometric projection
CIsometricProjection3D prIsometricProjection;
// init projection parameters
prIsometricProjection.ZoomFactorL() = GetZoomFactor();
prIsometricProjection.ScreenBBoxL() = FLOATaabbox2D(
FLOAT2D(0.0f, 0.0f),
FLOAT2D((float)sv_pdpDrawPort->GetWidth(), (float)sv_pdpDrawPort->GetHeight())
);
wed_fFrontClipDistance = Clamp( wed_fFrontClipDistance, 0.02f, 2.00f);
prIsometricProjection.AspectRatioL() = 1.0f;
prIsometricProjection.FrontClipDistanceL() = wed_fFrontClipDistance;
prIsometricProjection.ObjectStretchL() = FLOAT3D(1.0f, 1.0f, 1.0f);
// set up viewer position and placement
prIsometricProjection.ViewerPlacementL() = sv_plViewer;
prIsometricProjection.ObjectPlacementL().pl_PositionVector = FLOAT3D(0.0f, 0.0f, 0.0f);
prIsometricProjection.ObjectPlacementL().pl_OrientationAngle = ANGLE3D(0, 0, 0);
// return the result
prProjection = prIsometricProjection;
}
}
/*
* Translate slave viewer in his own system
*/
void CSlaveViewer::Translate_OwnSystem( PIX pixDI, PIX pixDJ, PIX pixDK)
{
FLOAT fZoom = GetZoomFactor();
sv_fTargetDistance += pixDK/fZoom;
if( sv_fTargetDistance < MIN_TARGET_DISTANCE)
{
sv_fTargetDistance = MIN_TARGET_DISTANCE;
return;
}
if( sv_fTargetDistance > MAX_TARGET_DISTANCE)
{
sv_fTargetDistance = MAX_TARGET_DISTANCE;
return;
}
ASSERT( sv_fTargetDistance > 0);
sv_plViewer.Translate_OwnSystem( FLOAT3D(-pixDI/fZoom, pixDJ/fZoom, pixDK/fZoom));
}
/*
* Translate slave viewer without stretching zoom factor
*/
void CSlaveViewer::Translate_Local_OwnSystem(FLOAT fdX, FLOAT fdY, FLOAT fdZ)
{
sv_plViewer.Translate_OwnSystem( FLOAT3D(fdX, fdY, fdZ));
}
/*
* Scales target distance using given factor
*/
void CSlaveViewer::ScaleTargetDistance( FLOAT fFactor)
{
// if very close to the target, and trying to go even closer or
// if very far away and trying to move further away
if( (sv_fTargetDistance <MIN_TARGET_DISTANCE && fFactor<1.0f) ||
(sv_fTargetDistance >MAX_TARGET_DISTANCE && fFactor>1.0f) )
{
// don't do anything
return;
}
// change along z axis
FLOAT dz = sv_fTargetDistance * fFactor;
sv_plViewer.Translate_OwnSystem( FLOAT3D( 0.0f, 0.0f, dz));
sv_fTargetDistance += dz;
}
/*
* Rotate slave viewer arround target using HPB method
*/
void CSlaveViewer::Rotate_HPB( PIX pixDI, PIX pixDJ, PIX pixDK)
{
// if is perspective
if( IsPerspective())
{
// get target placement
CPlacement3D plTarget = GetTargetPlacement();
// project viewer placement to the target placement
sv_plViewer.AbsoluteToRelative(plTarget);
// rotate the target
plTarget.Rotate_HPB( ANGLE3D( AngleDeg((FLOAT)-pixDI),
AngleDeg((FLOAT)-pixDJ),
AngleDeg((FLOAT)-pixDK)));
// project viewer placement back from the target placement
sv_plViewer.RelativeToAbsolute(plTarget);
}
// if is isometric
else
{
// can't rotate isometric projections
}
}
/*
* Rotate slave viewer in his own system using HPB method
*/
void CSlaveViewer::Rotate_Local_HPB( FLOAT fdX, FLOAT fdY, FLOAT fdZ)
{
// if is perspective
if( IsPerspective())
{
// rotate the viewer
sv_plViewer.Rotate_HPB( ANGLE3D( AngleDeg(-fdX), AngleDeg(-fdY), AngleDeg(-fdZ)));
}
// if is isometric
else
{
// can't rotate isometric projections
}
}
// translate a placement in given system
void CSlaveViewer::TranslatePlacement_OtherSystem(CPlacement3D &plToTranslate,
CPlacement3D &plOtherSystem,
PIX pixDI, PIX pixDJ, PIX pixDK)
{
FLOAT fZoom = GetZoomFactor();
// project the placement to the viewer's system
plToTranslate.AbsoluteToRelative(sv_plViewer);
// project the placement to the given system
plToTranslate.AbsoluteToRelative(plOtherSystem);
// translate it
plToTranslate.Translate_AbsoluteSystem( FLOAT3D(pixDI/fZoom, -pixDJ/fZoom, pixDK/fZoom));
// project the placement back from given system
plToTranslate.RelativeToAbsolute(plOtherSystem);
// project the placement back from viewer's system
plToTranslate.RelativeToAbsolute(sv_plViewer);
}
// translate a placement in viewer's system
void CSlaveViewer::TranslatePlacement_OwnSystem(CPlacement3D &plToTranslate,
PIX pixDI, PIX pixDJ, PIX pixDK)
{
FLOAT fZoom = GetZoomFactor();
// project the placement to the viewer's system
plToTranslate.AbsoluteToRelative(sv_plViewer);
// translate it
plToTranslate.Translate_AbsoluteSystem( FLOAT3D(pixDI/fZoom, -pixDJ/fZoom, pixDK/fZoom));
// project the placement back from viewer's system
plToTranslate.RelativeToAbsolute(sv_plViewer);
}
// convert offset from pixels to meters
FLOAT CSlaveViewer::PixelsToMeters(PIX pix)
{
FLOAT fZoom = GetZoomFactor();
return pix/fZoom;
}
// translate a placement in viewer's system
void CSlaveViewer::RotatePlacement_HPB(CPlacement3D &plToRotate,
PIX pixDI, PIX pixDJ, PIX pixDK)
{
// project the placement to the viewer's system
plToRotate.AbsoluteToRelative(sv_plViewer);
// rotate it
plToRotate.Rotate_HPB( ANGLE3D( AngleDeg((FLOAT)-pixDI),
AngleDeg((FLOAT)-pixDJ),
AngleDeg((FLOAT)-pixDK)));
// project the placement back from viewer's system
plToRotate.RelativeToAbsolute(sv_plViewer);
}
// translate a placement in viewer's system
void CSlaveViewer::RotatePlacement_TrackBall(CPlacement3D &plToRotate,
PIX pixDI, PIX pixDJ, PIX pixDK)
{
// if viewer is perspective
if( IsPerspective()) {
// project the placement to the viewer's system
plToRotate.AbsoluteToRelative(sv_plViewer);
// rotate it
plToRotate.Rotate_TrackBall( ANGLE3D( AngleDeg((FLOAT)-pixDI),
AngleDeg((FLOAT)-pixDJ),
AngleDeg((FLOAT)-pixDK)));
// project the placement back from viewer's system
plToRotate.RelativeToAbsolute(sv_plViewer);
// if viewer is isometric
} else {
// project the placement to the viewer's system
plToRotate.AbsoluteToRelative(sv_plViewer);
// rotate it only around banking axis
plToRotate.Rotate_TrackBall( ANGLE3D( 0,0, AngleDeg((FLOAT)-pixDI)));
// project the placement back from viewer's system
plToRotate.RelativeToAbsolute(sv_plViewer);
}
}