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