ArcObjects Library Reference  

Fly

About the Custom scene navigation commands Sample

[C#]

Fly.cs

using System;
using System.Drawing;
using ESRI.ArcGIS.Analyst3D;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Controls;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using System.Runtime.InteropServices;

namespace sceneTools
{
	[ClassInterface(ClassInterfaceType.None)]
	[Guid("485BE349-31DA-4cd5-B6A4-69E4758F2541")]

	public sealed class Fly : BaseTool 
	{
		[DllImport("user32")] public static extern int SetCursor(int hCursor);  
		[DllImport("user32")] public static extern int GetClientRect(int hwnd, ref  Rectangle lpRect);

        #region COM Registration Function(s)
        [ComRegisterFunction()]
        [ComVisible(false)]
        static void RegisterFunction(Type registerType)
        {
            // Required for ArcGIS Component Category Registrar support
            ArcGISCategoryRegistration(registerType);

            //
            // TODO: Add any COM registration code here
            //
        }

        [ComUnregisterFunction()]
        [ComVisible(false)]
        static void UnregisterFunction(Type registerType)
        {
            // Required for ArcGIS Component Category Registrar support
            ArcGISCategoryUnregistration(registerType);

            //
            // TODO: Add any COM unregistration code here
            //
        }

        #region ArcGIS Component Category Registrar generated code
        /// <summary>
        /// Required method for ArcGIS Component Category registration -
        /// Do not modify the contents of this method with the code editor.
        /// </summary>
        private static void ArcGISCategoryRegistration(Type registerType)
        {
            string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
            ControlsCommands.Register(regKey);

        }
        /// <summary>
        /// Required method for ArcGIS Component Category unregistration -
        /// Do not modify the contents of this method with the code editor.
        /// </summary>
        private static void ArcGISCategoryUnregistration(Type registerType)
        {
            string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
            ControlsCommands.Unregister(regKey);

        }

        #endregion
        #endregion

		private ISceneHookHelper m_pSceneHookHelper;
		private bool m_bInUse;
		bool bCancel = false;
		private long m_lMouseX;
		private long m_lMouseY;
		private double m_dMotion; //speed of the scene fly through in scene units
		private IPoint m_pPointObs; //observer
		private IPoint m_pPointTgt; //target
		private double m_dDistance; //distance between target and observer
		private double m_dElevation; //normal fly angles in radians
		private double m_dAzimut;  //normal fly angles in radians
		private int m_iSpeed; 
		private System.Windows.Forms.Cursor m_flyCur;
		private System.Windows.Forms.Cursor m_moveFlyCur;
		
		public Fly()
		{
			base.m_category = "Sample_SceneControl(C#)";
			base.m_caption = "Fly";
			base.m_toolTip = "Fly";
			base.m_name = "Sample_SceneControl(C#)/Fly";
			base.m_message = "Flies through the scene";

			//Load resources
			string[] res = GetType().Assembly.GetManifestResourceNames();
			if(res.GetLength(0) > 0)
			{
				base.m_bitmap = new System.Drawing.Bitmap(GetType().Assembly.GetManifestResourceStream("sceneTools.fly.bmp"));
			}
			m_flyCur = new System.Windows.Forms.Cursor(GetType().Assembly.GetManifestResourceStream("sceneTools.fly.cur"));
			m_moveFlyCur = new System.Windows.Forms.Cursor(GetType().Assembly.GetManifestResourceStream("sceneTools.fly1.cur"));
			m_pSceneHookHelper = new SceneHookHelperClass ();
			m_iSpeed = 0;
		}

       	public override void OnCreate(object hook)
		{
			m_pSceneHookHelper.Hook = hook;
		}
	
		public override bool Enabled
		{
			get
			{
				//Disable if orthographic (2D) view
				if (m_pSceneHookHelper.Hook == null || m_pSceneHookHelper.Scene == null)
				{
					return false;
				}
				else
				{
					ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera;
					if(pCamera.ProjectionType == esri3DProjectionType.esriOrthoProjection)
						return false;
					else
						return true;
				}	
			}
		}
	
		public override int Cursor
		{
			get
			{
				if(m_bInUse)
					return m_moveFlyCur.Handle.ToInt32();
				else
					return m_flyCur.Handle.ToInt32();
			}
		}
			
		public override bool Deactivate()
		{
			return true;
		}
		
		public override void OnMouseUp(int Button, int Shift, int X, int Y)
		{
			if (! m_bInUse)
			{
				m_lMouseX = X;
				m_lMouseY = Y;

				if(m_iSpeed == 0)
					StartFlight();
			}
			else
			{
				//Set the speed
				if (Button == 1)
					m_iSpeed = m_iSpeed + 1;
				else if (Button == 2)
					m_iSpeed = m_iSpeed - 1;

				//Start or end the flight
				if (m_iSpeed == 0)
					EndFlight();
				else
					StartFlight();
			}
		}
	
		public override void OnMouseMove(int Button, int Shift, int X, int Y)
		{
			if (! m_bInUse) return;

			m_lMouseX = X;
			m_lMouseY = Y;
		}

		public override void OnKeyUp(int keyCode, int Shift)
		{
			if(m_bInUse == true)
			{
				//Slow down the speed of the fly through
				if(keyCode == 40 || keyCode == 37)
					m_dMotion = m_dMotion / 2;
				//Speed up the speed of the fly through
				else if (keyCode == 38 || keyCode == 39)
					m_dMotion = m_dMotion * 2;
				else if (keyCode == 27)
					bCancel = true;
			}
		}
				
		public void StartFlight()
		{
			m_bInUse = true;
			
			//Get the extent of the scene graph
			IEnvelope pEnvelope;
			pEnvelope = m_pSceneHookHelper.SceneGraph.Extent;

			if (pEnvelope.IsEmpty) return;

			//Query the coordinates of the extent
			double dXmin, dXmax, dYmin, dYmax;
			pEnvelope.QueryCoords(out dXmin, out dYmin, out dXmax, out dYmax);

			//Set the speed of the scene
			if((dXmax - dXmin) > (dYmax - dYmin))
				m_dMotion = (dXmax - dXmin)/100;
			else
				m_dMotion = (dYmax - dYmin) / 100;

			//Get camera's current observer and target
			ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera;
			m_pPointObs = pCamera.Observer;
			m_pPointTgt = pCamera.Target;
			
			//Get the differences between the observer and target
			double dx, dy, dz;

			dx = m_pPointTgt.X - m_pPointObs.X;
			dy = m_pPointTgt.Y - m_pPointObs.Y;
			dz = m_pPointTgt.Z - m_pPointObs.Z;

			//Determine the elevation and azimuth in radians and
			//the distance between the target and observer
			m_dElevation = Math.Atan(dz/ Math.Sqrt(dx*dx + dy*dy));
			m_dAzimut = Math.Atan(dy / dx);
			m_dDistance = Math.Sqrt((dx*dx) + (dy*dy) + (dz*dz));
			
			//Windows API call to set cursor
			SetCursor(m_moveFlyCur.Handle.ToInt32());
	
			//Continue the flight
			Flight();
		}

		public void Flight()
		{
			//Get IMessageDispatcher interface
			IMessageDispatcher pMessageDispatcher;
			pMessageDispatcher = new MessageDispatcherClass();

			//Set the ESC key to be seen as a cancel action
			pMessageDispatcher.CancelOnClick = false;
			pMessageDispatcher.CancelOnEscPress = true;

			//Get the scene graph
			ISceneGraph pSceneGraph = (ISceneGraph) m_pSceneHookHelper.SceneGraph;

			//Get the scene viewer
			ISceneViewer pSceneViewer = (ISceneViewer) m_pSceneHookHelper.ActiveViewer;

			//Get the camera
			ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera;

			bCancel = false;

			do
			{
				//Get the elapsed time
				double dlastFrameDuration, dMeanFrameRate;

				pSceneGraph.GetDrawingTimeInfo(out dlastFrameDuration, out dMeanFrameRate);

				if(dlastFrameDuration < 0.01)
					dlastFrameDuration = 0.01;

				if(dlastFrameDuration > 1)
					dlastFrameDuration = 1;

				//Windows API call to get windows client coordinates
				Rectangle rect = new Rectangle();
				if (GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd, ref rect) == 0) return;
				
				//Get normal vectors
				double dXMouseNormal, dYMouseNormal;

				dXMouseNormal = 2 * ((double)m_lMouseX / (double)rect.Right) - 1;
				dYMouseNormal = 2 * ((double)m_lMouseY / (double)rect.Bottom) - 1;
				
				//Set elevation and azimuth in radians for normal rotation
				m_dElevation = m_dElevation - (dlastFrameDuration * dYMouseNormal * Math.Abs(dYMouseNormal));
				m_dAzimut = m_dAzimut - (dlastFrameDuration * dXMouseNormal * Math.Abs(dXMouseNormal));
				if(m_dElevation > 0.45 * 3.141592)
					m_dElevation = 0.45 * 3.141592;

				if(m_dElevation < -0.45 * 3.141592)
					m_dElevation = -0.45 * 3.141592;

				if(m_dAzimut < 0)
					m_dAzimut = m_dAzimut + (2 * 3.141592);

				if(m_dAzimut > 2 * 3.141592)
					m_dAzimut = m_dAzimut - (2 * 3.141592);
				
				double dx, dy, dz;

				dx = Math.Cos(m_dElevation) * Math.Cos(m_dAzimut);
				dy = Math.Cos(m_dElevation) * Math.Sin(m_dAzimut);
				dz = Math.Sin(m_dElevation);

				//Change the viewing directions (target)
				m_pPointTgt.X = m_pPointObs.X + (m_dDistance * dx);
				m_pPointTgt.Y = m_pPointObs.Y + (m_dDistance * dy);
				m_pPointTgt.Z = m_pPointObs.Z + (m_dDistance * dz);

				//Move the camera in the viewing directions
				m_pPointObs.X = m_pPointObs.X + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dx);
				m_pPointObs.Y = m_pPointObs.Y + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dy);
				m_pPointTgt.X = m_pPointTgt.X + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dx);
				m_pPointTgt.Y = m_pPointTgt.Y + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dy);
				m_pPointObs.Z = m_pPointObs.Z + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dz);
				m_pPointTgt.Z = m_pPointTgt.Z + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dz);

				pCamera.Observer = m_pPointObs;
				pCamera.Target = m_pPointTgt;
				
				//Set the angle of the camera about the line of sight between the observer and target
				pCamera.RollAngle = 10 * dXMouseNormal * Math.Abs(dXMouseNormal);

				//Redraw the scene viewer 
				pSceneViewer.Redraw(true);

				object objCancel;

				//Dispatch any waiting messages: OnMouseMove / OnMouseUp / OnKeyUp events
				//object objCancel = bCancel as object;
				pMessageDispatcher.Dispatch(m_pSceneHookHelper.ActiveViewer.hWnd, false, out objCancel);
				
				//End flight if ESC key pressed
				if (bCancel == true)
					EndFlight();
			}
			while(m_bInUse == true && bCancel == false);

			bCancel = false;
		}

		public void EndFlight()
		{
			m_bInUse = false;

			//Get the scene graph
			ISceneGraph pSceneGraph = (ISceneGraph) m_pSceneHookHelper.SceneGraph;

			IPoint pPointTgt;
			pPointTgt = new PointClass();
			object pOwner, pObject;
			Rectangle rect = new Rectangle();

			//Windows API call to get windows client coordinates
			if(GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd, ref rect) != 0)
			{
				//Translate coordinates into a 3D point
				pSceneGraph.Locate(pSceneGraph.ActiveViewer, rect.Right / 2, rect.Bottom / 2, esriScenePickMode.esriScenePickAll, true, out pPointTgt, out pOwner, out pObject);
			}

			//Get the camera
			ICamera pCamera = (ICamera) m_pSceneHookHelper.Camera;

			if(pPointTgt != null)
			{
				//Reposition target and observer
				pCamera.Target = pPointTgt;
				pCamera.Observer = m_pPointObs;
			}

			//Set the angle of the camera about the line
			//of sight between the observer and target
			pCamera.RollAngle = 0;
			pCamera.PropertiesChanged();

			//Windows API call to set cursor
			SetCursor(m_moveFlyCur.Handle.ToInt32());
			m_iSpeed = 0;
		}
	
		public override void OnKeyDown(int keyCode, int Shift)
		{
			if(keyCode == 27) //ESC is pressed
			{
				bCancel = true;
			}
		}
	}
}

[Visual Basic .NET]

Fly.vb

Imports System.Drawing
Imports ESRI.ArcGIS.Analyst3D
Imports ESRI.ArcGIS.GeomeTry
Imports ESRI.ArcGIS.Controls
Imports ESRI.ArcGIS.ADF.BaseClasses
Imports ESRI.ArcGIS.ADF.CATIDs
Imports System.Runtime.InteropServices

<ComClass(Fly.ClassId, Fly.InterfaceId, Fly.EventsId)> _
Public NotInheritable Class Fly
    Inherits BaseTool

#Region "COM GUIDs"
    ' These  GUIDs provide the COM identity for this class 
    ' and its COM interfaces. If you change them, existing 
    ' clients will no longer be able to access the class.
    Public Const ClassId As String = "C74AE939-B3B1-4F28-9E79-D172CF1F2043"
    Public Const InterfaceId As String = "86B05C26-D3C0-49E9-957D-7F54B35C8940"
    Public Const EventsId As String = "E419FD47-DA90-45B7-8563-BD4D9D023555"
#End Region
#Region "COM Registration Function(s)"
    <ComRegisterFunction(), ComVisibleAttribute(False)> _
    Public Shared Sub RegisterFunction(ByVal registerType As Type)
        ' Required for ArcGIS Component Category Registrar support
        ArcGISCategoryRegistration(registerType)

        'Add any COM registration code after the ArcGISCategoryRegistration() call

    End Sub

    <ComUnregisterFunction(), ComVisibleAttribute(False)> _
    Public Shared Sub UnregisterFunction(ByVal registerType As Type)
        ' Required for ArcGIS Component Category Registrar support
        ArcGISCategoryUnregistration(registerType)

        'Add any COM unregistration code after the ArcGISCategoryUnregistration() call

    End Sub

#Region "ArcGIS Component Category Registrar generated code"
    ''' <summary>
    ''' Required method for ArcGIS Component Category registration -
    ''' Do not modify the contents of this method with the code editor.
    ''' </summary>
    Private Shared Sub ArcGISCategoryRegistration(ByVal registerType As Type)
        Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
        ControlsCommands.Register(regKey)

    End Sub
    ''' <summary>
    ''' Required method for ArcGIS Component Category unregistration -
    ''' Do not modify the contents of this method with the code editor.
    ''' </summary>
    Private Shared Sub ArcGISCategoryUnregistration(ByVal registerType As Type)
        Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
        ControlsCommands.Unregister(regKey)

    End Sub

#End Region
#End Region
    Declare Function GetClientRect Lib "user32" (ByVal hwnd As Integer, ByRef lpRect As Rectangle) As Integer
    Declare Function SetCursor Lib "user32" (ByVal hCursor As Integer) As Integer

    Private m_pSceneHookHelper As ISceneHookHelper
    Private m_bInUse As Boolean
    Dim bCancel As Boolean = False
    Private m_lMouseX As Long
    Private m_lMouseY As Long
    Private m_dMotion As Double 'speed of the scene fly through in scene units
    Private m_pPointObs As IPoint 'observer
    Private m_pPointTgt As IPoint 'target
    Private m_dDistance As Double 'distance between target and observer
    Private m_dElevation As Double 'normal fly angles in radians
    Private m_dAzimut As Double 'normal fly angles in radians
    Private m_iSpeed As Integer 'speed of a flight
    Private m_flyCur As System.Windows.Forms.Cursor
    Private m_moveFlyCur As System.Windows.Forms.Cursor

  ' A creatable COM class must have a Public Sub New() 
  ' with no parameters, otherwise, the class will not be 
  ' registered in the COM registry and cannot be created 
  ' via CreateObject.
    Public Sub New()
        MyBase.New()

        MyBase.m_category = "Sample_SceneControl(VB.NET)"
        MyBase.m_caption = "Fly"
        MyBase.m_toolTip = "Fly"
        MyBase.m_name = "Sample_SceneControl(VB.NET)/Fly"
        MyBase.m_message = "Flies through the scene"

        'Load resources
        Dim res() As String = GetType(Fly).Assembly.GetManifestResourceNames()
        If res.GetLength(0) > 0 Then
            MyBase.m_bitmap = New System.Drawing.Bitmap(GetType(Fly).Assembly.GetManifestResourceStream("SceneToolsVB.fly.bmp"))
        End If
        m_flyCur = New System.Windows.Forms.Cursor(GetType(Fly).Assembly.GetManifestResourceStream("SceneToolsVB.fly.cur"))
        m_moveFlyCur = New System.Windows.Forms.Cursor(GetType(Fly).Assembly.GetManifestResourceStream("SceneToolsVB.fly1.cur"))
        m_pSceneHookHelper = New SceneHookHelperClass
        m_iSpeed = 0
    End Sub

    Public Overrides Sub OnCreate(ByVal hook As Object)
        m_pSceneHookHelper.Hook = hook
    End Sub

    Public Overrides ReadOnly Property Enabled() As Boolean
        Get
            'Disable if orthographic (2D) view
            If m_pSceneHookHelper.Hook Is Nothing Or m_pSceneHookHelper.Scene Is Nothing Then
                Return False
            Else
                Dim pCamera As ICamera = CType(m_pSceneHookHelper.Camera, ICamera)
                If pCamera.ProjectionType = esri3DProjectionType.esriOrthoProjection Then
                    Return False
                Else
                    Return True
                End If
            End If
        End Get
    End Property

    Public Overrides ReadOnly Property Cursor() As Integer
        Get
            If (m_bInUse) Then
                Return m_moveFlyCur.Handle.ToInt32()
            Else
                Return m_flyCur.Handle.ToInt32()
            End If
        End Get
    End Property

    Public Overrides Function Deactivate() As Boolean
        Return True
    End Function

    Public Overrides Sub OnMouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Integer, ByVal Y As Integer)
        If (Not m_bInUse) Then
            m_lMouseX = X
            m_lMouseY = Y

            If (m_iSpeed = 0) Then
                StartFlight()
            End If
        Else
            'Set the speed
            If (Button = 1) Then
                m_iSpeed += 1
            ElseIf (Button = 2) Then
                m_iSpeed -= 1
            End If

            'Start or end the flight
            If (m_iSpeed = 0) Then
                EndFlight()
            Else
                StartFlight()
            End If
        End If
    End Sub

    Public Overrides Sub OnMouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Integer, ByVal Y As Integer)
        If (Not m_bInUse) Then
            Return
        End If
        m_lMouseX = X
        m_lMouseY = Y
    End Sub

    Public Overrides Sub OnKeyUp(ByVal keyCode As Integer, ByVal Shift As Integer)
        If (m_bInUse) Then
            'Slow down the speed of the fly through
            If (keyCode = 40 Or keyCode = 37) Then
                m_dMotion = m_dMotion / 2
                'Speed up the speed of the fly through
            ElseIf (keyCode = 38 Or keyCode = 39) Then
                m_dMotion = m_dMotion * 2
            End If
        End If
    End Sub

    Public Sub StartFlight()
        m_bInUse = True

        'Get the extent of the scene graph
        Dim pEnvelope As IEnvelope
        pEnvelope = m_pSceneHookHelper.SceneGraph.Extent

        If (pEnvelope.IsEmpty) Then
            Return
        End If

        'Query the coordinates of the extent
        Dim dXmin, dXmax, dYmin, dYmax As Double
        pEnvelope.QueryCoords(dXmin, dYmin, dXmax, dYmax)

        'Set the speed of the scene
        If ((dXmax - dXmin) > (dYmax - dYmin)) Then
            m_dMotion = (dXmax - dXmin) / 100
        Else
            m_dMotion = (dYmax - dYmin) / 100
        End If

        'Get camera's current observer and target
        Dim pCamera As ICamera = CType(m_pSceneHookHelper.Camera, ICamera)
        m_pPointObs = pCamera.Observer
        m_pPointTgt = pCamera.Target

        'Get the differences between the observer and target
        Dim dx, dy, dz As Double
        dx = m_pPointTgt.X - m_pPointObs.X
        dy = m_pPointTgt.Y - m_pPointObs.Y
        dz = m_pPointTgt.Z - m_pPointObs.Z

        'Determine the elevation and azimuth in radians and
        'the distance between the target and observer
        m_dElevation = Math.Atan(dz / Math.Sqrt(dx * dx + dy * dy))
        m_dAzimut = Math.Atan(dy / dx)
        m_dDistance = Math.Sqrt((dx * dx) + (dy * dy) + (dz * dz))

        'Windows API call to set cursor
        SetCursor(m_moveFlyCur.Handle.ToInt32())

        'Continue the flight
        Flight()

    End Sub

    Public Sub Flight()
        'Get IMessageDispatcher interface
        Dim pMessageDispatcher As IMessageDispatcher
        pMessageDispatcher = New MessageDispatcherClass

        'Set the ESC key to be seen as a cancel action
        pMessageDispatcher.CancelOnClick = False
        pMessageDispatcher.CancelOnEscPress = True

        'Get the scene graph
        Dim pSceneGraph As ISceneGraph = CType(m_pSceneHookHelper.SceneGraph, ISceneGraph)

        'Get the scene viewer
        Dim pSceneViewer As ISceneViewer = CType(m_pSceneHookHelper.ActiveViewer, ISceneViewer)

        'Get the camera
        Dim pCamera As ICamera = CType(m_pSceneHookHelper.Camera, ICamera)

        bCancel = False

        Do
            'Get the elapsed time
            Dim dlastFrameDuration, dMeanFrameRate As Double
            pSceneGraph.GetDrawingTimeInfo(dlastFrameDuration, dMeanFrameRate)

            If (dlastFrameDuration < 0.01) Then
                dlastFrameDuration = 0.01
            End If

            If (dlastFrameDuration > 1) Then
                dlastFrameDuration = 1
            End If

            'Windows API call to get windows client coordinates
            Dim rect As Rectangle
            rect = New Rectangle

            If (GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd, rect) = 0) Then
                Return
            End If

            'Get normal vectors
            Dim dXMouseNormal, dYMouseNormal As Double
            dXMouseNormal = 2 * (m_lMouseX / rect.Right) - 1 'should be double
            dYMouseNormal = 2 * (m_lMouseY / rect.Bottom) - 1

            'Set elevation and azimuth in radians for normal rotation
            m_dElevation = m_dElevation - (dlastFrameDuration * dYMouseNormal * Math.Abs(dYMouseNormal))
            m_dAzimut = m_dAzimut - (dlastFrameDuration * dXMouseNormal * Math.Abs(dXMouseNormal))

            If (m_dElevation > 0.45 * 3.141592) Then
                m_dElevation = 0.45 * 3.141592
            End If

            If (m_dElevation < -0.45 * 3.141592) Then
                m_dElevation = -0.45 * 3.141592
            End If

            If (m_dAzimut < 0) Then
                m_dAzimut = m_dAzimut + (2 * 3.141592)
            End If

            If (m_dAzimut > 2 * 3.141592) Then
                m_dAzimut = m_dAzimut - (2 * 3.141592)
            End If

            Dim dx, dy, dz As Double

            dx = Math.Cos(m_dElevation) * Math.Cos(m_dAzimut)
            dy = Math.Cos(m_dElevation) * Math.Sin(m_dAzimut)
            dz = Math.Sin(m_dElevation)

            'Change the viewing directions (target)
            m_pPointTgt.X = m_pPointObs.X + (m_dDistance * dx)
            m_pPointTgt.Y = m_pPointObs.Y + (m_dDistance * dy)
            m_pPointTgt.Z = m_pPointObs.Z + (m_dDistance * dz)

            'Move the camera in the viewing directions
            m_pPointObs.X = m_pPointObs.X + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dx)
            m_pPointObs.Y = m_pPointObs.Y + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dy)
            m_pPointTgt.X = m_pPointTgt.X + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dx)
            m_pPointTgt.Y = m_pPointTgt.Y + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dy)
            m_pPointObs.Z = m_pPointObs.Z + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dz)
            m_pPointTgt.Z = m_pPointTgt.Z + (dlastFrameDuration * (2 ^ m_iSpeed) * m_dMotion * dz)

            pCamera.Observer = m_pPointObs
            pCamera.Target = m_pPointTgt

            'Set the angle of the camera about the line of sight between the observer and target
            pCamera.RollAngle = 10 * dXMouseNormal * Math.Abs(dXMouseNormal)

            'Redraw the scene viewer 
            pSceneViewer.Redraw(True)

            Dim objCancel As Object = Nothing

            'Dispatch any waiting messages: OnMouseMove / OnMouseUp / OnKeyUp events
            'object objCancel = bCancel as object;
            pMessageDispatcher.Dispatch(m_pSceneHookHelper.ActiveViewer.hWnd, False, objCancel)

            'End flight if ESC key pressed
            If (bCancel = True) Then
                EndFlight()
            End If
        Loop While m_bInUse = True And bCancel = False

        SetCursor(m_flyCur.Handle.ToInt32())
        bCancel = False

    End Sub

    Public Sub EndFlight()
        m_bInUse = False

        'Get the scene graph
        Dim pSceneGraph As ISceneGraph = CType(m_pSceneHookHelper.SceneGraph, ISceneGraph)

        Dim pPointTgt As IPoint
        pPointTgt = New PointClass
        Dim pOwner As Object = Nothing, pObject As Object = Nothing
        Dim rect As Rectangle
        rect = New Rectangle

        'Windows API call to get windows client coordinates
        If (GetClientRect(m_pSceneHookHelper.ActiveViewer.hWnd, rect) <> 0) Then
            'Translate coordinates into a 3D point
            pSceneGraph.Locate(pSceneGraph.ActiveViewer, rect.Right / 2, rect.Bottom / 2, esriScenePickMode.esriScenePickAll, True, pPointTgt, pOwner, pObject)
        End If

        'Get the camera
        Dim pCamera As ICamera = CType(m_pSceneHookHelper.Camera, ICamera)

        If (Not pPointTgt Is Nothing) Then
            'Reposition target and observer
            pCamera.Target = pPointTgt
            pCamera.Observer = m_pPointObs
        End If

        'Set the angle of the camera about the line
        'of sight between the observer and target
        pCamera.RollAngle = 0
        pCamera.PropertiesChanged()

        'Windows API call to set cursor
        SetCursor(m_moveFlyCur.Handle.ToInt32())
        m_iSpeed = 0

    End Sub

    Public Overrides Sub OnKeyDown(ByVal keyCode As Integer, ByVal Shift As Integer)
        If (keyCode = 27) Then 'ESC is pressed
            bCancel = True
        End If
    End Sub
End Class