//--------------------------------------------------------------------------------------
// File: DXUTcamera.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
//
// http://go.microsoft.com/fwlink/?LinkId=320437
//--------------------------------------------------------------------------------------
#include "DXUT.h"
#include "DXUTcamera.h"
#include "DXUTres.h"

using namespace DirectX;

//======================================================================================
// CD3DArcBall
//======================================================================================

//--------------------------------------------------------------------------------------
CD3DArcBall::CD3DArcBall() noexcept :
    m_Offset{ 0, 0 },
    m_vDownPt( 0, 0, 0 ),
    m_vCurrentPt( 0, 0, 0 )
{
    Reset();

    RECT rc;
    GetClientRect( GetForegroundWindow(), &rc );
    SetWindow( rc.right, rc.bottom );
}


//--------------------------------------------------------------------------------------
void CD3DArcBall::Reset()
{
    XMVECTOR qid = XMQuaternionIdentity();
    XMStoreFloat4( &m_qDown, qid );
    XMStoreFloat4( &m_qNow, qid );

    XMMATRIX id = XMMatrixIdentity();
    XMStoreFloat4x4( &m_mRotation, id );
    XMStoreFloat4x4( &m_mTranslation, id );
    XMStoreFloat4x4( &m_mTranslationDelta, id );

    m_bDrag = false;
    m_fRadiusTranslation = 1.0f;
    m_fRadius = 1.0f;
}


//--------------------------------------------------------------------------------------
void CD3DArcBall::OnBegin( _In_ int nX, _In_ int nY )
{
    // Only enter the drag state if the click falls
    // inside the click rectangle.
    if( nX >= m_Offset.x &&
        nX < m_Offset.x + m_nWidth &&
        nY >= m_Offset.y &&
        nY < m_Offset.y + m_nHeight )
    {
        m_bDrag = true;
        m_qDown = m_qNow;
        XMVECTOR v = ScreenToVector( float(nX), float(nY) );
        XMStoreFloat3( &m_vDownPt, v );
    }
}


//--------------------------------------------------------------------------------------
void CD3DArcBall::OnMove( _In_ int nX, _In_ int nY )
{
    if( m_bDrag )
    {
        XMVECTOR curr = ScreenToVector( ( float )nX, ( float )nY ); 
        XMStoreFloat3( &m_vCurrentPt, curr );

        XMVECTOR down = XMLoadFloat3( &m_vDownPt );
        XMVECTOR qdown = XMLoadFloat4( &m_qDown );

        XMVECTOR result = XMQuaternionMultiply( qdown, QuatFromBallPoints( down, curr ) );
        XMStoreFloat4( &m_qNow, result );
    }
}


//--------------------------------------------------------------------------------------
void CD3DArcBall::OnEnd()
{
    m_bDrag = false;
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
LRESULT CD3DArcBall::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    // Current mouse position
    int iMouseX = ( short )LOWORD( lParam );
    int iMouseY = ( short )HIWORD( lParam );

    switch( uMsg )
    {
        case WM_LBUTTONDOWN:
        case WM_LBUTTONDBLCLK:
            SetCapture( hWnd );
            OnBegin( iMouseX, iMouseY );
            return TRUE;

        case WM_LBUTTONUP:
            ReleaseCapture();
            OnEnd();
            return TRUE;
        case WM_CAPTURECHANGED:
            if( ( HWND )lParam != hWnd )
            {
                ReleaseCapture();
                OnEnd();
            }
            return TRUE;

        case WM_RBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
        case WM_MBUTTONDOWN:
        case WM_MBUTTONDBLCLK:
            SetCapture( hWnd );
            // Store off the position of the cursor when the button is pressed
            m_ptLastMouse.x = iMouseX;
            m_ptLastMouse.y = iMouseY;
            return TRUE;

        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
            ReleaseCapture();
            return TRUE;

        case WM_MOUSEMOVE:
            if( MK_LBUTTON & wParam )
            {
                OnMove( iMouseX, iMouseY );
            }
            else if( ( MK_RBUTTON & wParam ) || ( MK_MBUTTON & wParam ) )
            {
                // Normalize based on size of window and bounding sphere radius
                float fDeltaX = ( m_ptLastMouse.x - iMouseX ) * m_fRadiusTranslation / m_nWidth;
                float fDeltaY = ( m_ptLastMouse.y - iMouseY ) * m_fRadiusTranslation / m_nHeight;

                XMMATRIX mTranslationDelta;
                XMMATRIX mTranslation = XMLoadFloat4x4( &m_mTranslation );
                if( wParam & MK_RBUTTON )
                {
                    mTranslationDelta = XMMatrixTranslation( -2 * fDeltaX, 2 * fDeltaY, 0.0f );
                    mTranslation = XMMatrixMultiply( mTranslation, mTranslationDelta );
                }
                else  // wParam & MK_MBUTTON
                {
                    mTranslationDelta = XMMatrixTranslation( 0.0f, 0.0f, 5 * fDeltaY );
                    mTranslation = XMMatrixMultiply( mTranslation, mTranslationDelta );
                }

                XMStoreFloat4x4( &m_mTranslationDelta, mTranslationDelta );
                XMStoreFloat4x4( &m_mTranslation, mTranslation );

                // Store mouse coordinate
                m_ptLastMouse.x = iMouseX;
                m_ptLastMouse.y = iMouseY;
            }
            return TRUE;
    }

    return FALSE;
}


//======================================================================================
// CBaseCamera
//======================================================================================

//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CBaseCamera::CBaseCamera() noexcept :
    m_mView{},
    m_mProj{},
    m_GamePad{},
    m_vGamePadLeftThumb(0,0,0),
    m_vGamePadRightThumb(0,0,0),
    m_GamePadLastActive{},
    m_cKeysDown(0),
    m_aKeys{},
    m_vKeyboardDirection(0,0,0),
    m_ptLastMousePosition{ 0, 0 },
    m_nCurrentButtonMask(0),
    m_nMouseWheelDelta(0),
    m_vMouseDelta(0, 0),
    m_fFramesToSmoothMouseData(2.0f),
    m_vDefaultEye(0, 0, 0),
    m_vDefaultLookAt(0, 0, 0),
    m_vEye(0, 0, 0),
    m_vLookAt(0, 0, 0),
    m_fCameraYawAngle(0.0f),
    m_fCameraPitchAngle(0.0f),
    m_rcDrag{},
    m_vVelocity(0, 0, 0),
    m_vVelocityDrag(0, 0, 0),
    m_fDragTimer(0.0f),
    m_fTotalDragTimeToZero(0.25),
    m_vRotVelocity(0, 0),
    m_fRotationScaler(0.01f),
    m_fMoveScaler(5.0f),
    m_bMouseLButtonDown(false),
    m_bMouseMButtonDown(false),
    m_bMouseRButtonDown(false),
    m_bMovementDrag(false),
    m_bInvertPitch(false),
    m_bEnablePositionMovement(true),
    m_bEnableYAxisMovement(true),
    m_bClipToBoundary(false),
    m_bResetCursorAfterMove(false),
    m_vMinBoundary(-1, -1, -1),
    m_vMaxBoundary(1, 1, 1)
{
    // Setup the view matrix
    SetViewParams( g_XMZero, g_XMIdentityR2 );

    // Setup the projection matrix
    SetProjParams( XM_PI / 4, 1.0f, 1.0f, 1000.0f );

    GetCursorPos( &m_ptLastMousePosition );
    
    SetRect( &m_rcDrag, LONG_MIN, LONG_MIN, LONG_MAX, LONG_MAX );
}


//--------------------------------------------------------------------------------------
// Client can call this to change the position and direction of camera
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void CBaseCamera::SetViewParams( FXMVECTOR vEyePt, FXMVECTOR vLookatPt )
{
    XMStoreFloat3( &m_vEye, vEyePt );
    XMStoreFloat3( &m_vDefaultEye, vEyePt );

    XMStoreFloat3( &m_vLookAt, vLookatPt );
    XMStoreFloat3( &m_vDefaultLookAt , vLookatPt );

    // Calc the view matrix
    XMMATRIX mView = XMMatrixLookAtLH( vEyePt, vLookatPt, g_XMIdentityR1 );
    XMStoreFloat4x4( &m_mView, mView );

    XMMATRIX mInvView = XMMatrixInverse( nullptr, mView );

    // The axis basis vectors and camera position are stored inside the 
    // position matrix in the 4 rows of the camera's world matrix.
    // To figure out the yaw/pitch of the camera, we just need the Z basis vector
    XMFLOAT3 zBasis;
    XMStoreFloat3( &zBasis, mInvView.r[2] );

    m_fCameraYawAngle = atan2f( zBasis.x, zBasis.z );
    float fLen = sqrtf( zBasis.z * zBasis.z + zBasis.x * zBasis.x );
    m_fCameraPitchAngle = -atan2f( zBasis.y, fLen );
}


//--------------------------------------------------------------------------------------
// Calculates the projection matrix based on input params
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void CBaseCamera::SetProjParams( float fFOV, float fAspect, float fNearPlane, float fFarPlane )
{
    // Set attributes for the projection matrix
    m_fFOV = fFOV;
    m_fAspect = fAspect;
    m_fNearPlane = fNearPlane;
    m_fFarPlane = fFarPlane;

    XMMATRIX mProj = XMMatrixPerspectiveFovLH( fFOV, fAspect, fNearPlane, fFarPlane );
    XMStoreFloat4x4( &m_mProj, mProj );
}


//--------------------------------------------------------------------------------------
// Call this from your message proc so this class can handle window messages
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
LRESULT CBaseCamera::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    UNREFERENCED_PARAMETER( hWnd );
    UNREFERENCED_PARAMETER( lParam );

    switch( uMsg )
    {
        case WM_KEYDOWN:
        {
            // Map this key to a D3DUtil_CameraKeys enum and update the
            // state of m_aKeys[] by adding the KEY_WAS_DOWN_MASK|KEY_IS_DOWN_MASK mask
            // only if the key is not down
            D3DUtil_CameraKeys mappedKey = MapKey( ( UINT )wParam );
            if( mappedKey != CAM_UNKNOWN )
            {
                _Analysis_assume_( mappedKey < CAM_MAX_KEYS );
                if( FALSE == IsKeyDown( m_aKeys[mappedKey] ) )
                {
                    m_aKeys[ mappedKey ] = KEY_WAS_DOWN_MASK | KEY_IS_DOWN_MASK;
                    ++m_cKeysDown;
                }
            }
            break;
        }

        case WM_KEYUP:
        {
            // Map this key to a D3DUtil_CameraKeys enum and update the
            // state of m_aKeys[] by removing the KEY_IS_DOWN_MASK mask.
            D3DUtil_CameraKeys mappedKey = MapKey( ( UINT )wParam );
            if( mappedKey != CAM_UNKNOWN && ( DWORD )mappedKey < 8 )
            {
                m_aKeys[ mappedKey ] &= ~KEY_IS_DOWN_MASK;
                --m_cKeysDown;
            }
            break;
        }

        case WM_RBUTTONDOWN:
        case WM_MBUTTONDOWN:
        case WM_LBUTTONDOWN:
        case WM_RBUTTONDBLCLK:
        case WM_MBUTTONDBLCLK:
        case WM_LBUTTONDBLCLK:
            {
                // Compute the drag rectangle in screen coord.
                POINT ptCursor =
                {
                    ( short )LOWORD( lParam ), ( short )HIWORD( lParam )
                };

                // Update member var state
                if( ( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) && PtInRect( &m_rcDrag, ptCursor ) )
                {
                    m_bMouseLButtonDown = true; m_nCurrentButtonMask |= MOUSE_LEFT_BUTTON;
                }
                if( ( uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONDBLCLK ) && PtInRect( &m_rcDrag, ptCursor ) )
                {
                    m_bMouseMButtonDown = true; m_nCurrentButtonMask |= MOUSE_MIDDLE_BUTTON;
                }
                if( ( uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK ) && PtInRect( &m_rcDrag, ptCursor ) )
                {
                    m_bMouseRButtonDown = true; m_nCurrentButtonMask |= MOUSE_RIGHT_BUTTON;
                }

                // Capture the mouse, so if the mouse button is 
                // released outside the window, we'll get the WM_LBUTTONUP message
                SetCapture( hWnd );
                GetCursorPos( &m_ptLastMousePosition );
                return TRUE;
            }

        case WM_RBUTTONUP:
        case WM_MBUTTONUP:
        case WM_LBUTTONUP:
            {
                // Update member var state
                if( uMsg == WM_LBUTTONUP )
                {
                    m_bMouseLButtonDown = false; m_nCurrentButtonMask &= ~MOUSE_LEFT_BUTTON;
                }
                if( uMsg == WM_MBUTTONUP )
                {
                    m_bMouseMButtonDown = false; m_nCurrentButtonMask &= ~MOUSE_MIDDLE_BUTTON;
                }
                if( uMsg == WM_RBUTTONUP )
                {
                    m_bMouseRButtonDown = false; m_nCurrentButtonMask &= ~MOUSE_RIGHT_BUTTON;
                }

                // Release the capture if no mouse buttons down
                if( !m_bMouseLButtonDown &&
                    !m_bMouseRButtonDown &&
                    !m_bMouseMButtonDown )
                {
                    ReleaseCapture();
                }
                break;
            }

        case WM_CAPTURECHANGED:
        {
            if( ( HWND )lParam != hWnd )
            {
                if( ( m_nCurrentButtonMask & MOUSE_LEFT_BUTTON ) ||
                    ( m_nCurrentButtonMask & MOUSE_MIDDLE_BUTTON ) ||
                    ( m_nCurrentButtonMask & MOUSE_RIGHT_BUTTON ) )
                {
                    m_bMouseLButtonDown = false;
                    m_bMouseMButtonDown = false;
                    m_bMouseRButtonDown = false;
                    m_nCurrentButtonMask &= ~MOUSE_LEFT_BUTTON;
                    m_nCurrentButtonMask &= ~MOUSE_MIDDLE_BUTTON;
                    m_nCurrentButtonMask &= ~MOUSE_RIGHT_BUTTON;
                    ReleaseCapture();
                }
            }
            break;
        }

        case WM_MOUSEWHEEL:
            // Update member var state
            m_nMouseWheelDelta += ( short )HIWORD( wParam );
            break;
    }

    return FALSE;
}


//--------------------------------------------------------------------------------------
// Figure out the velocity based on keyboard input & drag if any
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void CBaseCamera::GetInput( bool bGetKeyboardInput, bool bGetMouseInput, bool bGetGamepadInput )
{
    m_vKeyboardDirection = XMFLOAT3( 0, 0, 0 );
    if( bGetKeyboardInput )
    {
        // Update acceleration vector based on keyboard state
        if( IsKeyDown( m_aKeys[CAM_MOVE_FORWARD] ) )
            m_vKeyboardDirection.z += 1.0f;
        if( IsKeyDown( m_aKeys[CAM_MOVE_BACKWARD] ) )
            m_vKeyboardDirection.z -= 1.0f;
        if( m_bEnableYAxisMovement )
        {
            if( IsKeyDown( m_aKeys[CAM_MOVE_UP] ) )
                m_vKeyboardDirection.y += 1.0f;
            if( IsKeyDown( m_aKeys[CAM_MOVE_DOWN] ) )
                m_vKeyboardDirection.y -= 1.0f;
        }
        if( IsKeyDown( m_aKeys[CAM_STRAFE_RIGHT] ) )
            m_vKeyboardDirection.x += 1.0f;
        if( IsKeyDown( m_aKeys[CAM_STRAFE_LEFT] ) )
            m_vKeyboardDirection.x -= 1.0f;
    }

    if( bGetMouseInput )
    {
        UpdateMouseDelta();
    }

    if( bGetGamepadInput )
    {
        m_vGamePadLeftThumb = XMFLOAT3( 0, 0, 0 );
        m_vGamePadRightThumb = XMFLOAT3( 0, 0, 0 );

        // Get controller state
        for( DWORD iUserIndex = 0; iUserIndex < DXUT_MAX_CONTROLLERS; iUserIndex++ )
        {
            DXUTGetGamepadState( iUserIndex, &m_GamePad[iUserIndex], true, true );

            // Mark time if the controller is in a non-zero state
            if( m_GamePad[iUserIndex].wButtons ||
                m_GamePad[iUserIndex].sThumbLX || m_GamePad[iUserIndex].sThumbLY ||
                m_GamePad[iUserIndex].sThumbRX || m_GamePad[iUserIndex].sThumbRY ||
                m_GamePad[iUserIndex].bLeftTrigger || m_GamePad[iUserIndex].bRightTrigger )
            {
                m_GamePadLastActive[iUserIndex] = DXUTGetTime();
            }
        }

        // Find out which controller was non-zero last
        int iMostRecentlyActive = -1;
        double fMostRecentlyActiveTime = 0.0f;
        for( DWORD iUserIndex = 0; iUserIndex < DXUT_MAX_CONTROLLERS; iUserIndex++ )
        {
            if( m_GamePadLastActive[iUserIndex] > fMostRecentlyActiveTime )
            {
                fMostRecentlyActiveTime = m_GamePadLastActive[iUserIndex];
                iMostRecentlyActive = iUserIndex;
            }
        }

        // Use the most recent non-zero controller if its connected
        if( iMostRecentlyActive >= 0 && m_GamePad[iMostRecentlyActive].bConnected )
        {
            m_vGamePadLeftThumb.x = m_GamePad[iMostRecentlyActive].fThumbLX;
            m_vGamePadLeftThumb.y = 0.0f;
            m_vGamePadLeftThumb.z = m_GamePad[iMostRecentlyActive].fThumbLY;

            m_vGamePadRightThumb.x = m_GamePad[iMostRecentlyActive].fThumbRX;
            m_vGamePadRightThumb.y = 0.0f;
            m_vGamePadRightThumb.z = m_GamePad[iMostRecentlyActive].fThumbRY;
        }
    }
}


//--------------------------------------------------------------------------------------
// Figure out the mouse delta based on mouse movement
//--------------------------------------------------------------------------------------
void CBaseCamera::UpdateMouseDelta()
{
    // Get current position of mouse
    POINT ptCurMousePos;
    GetCursorPos( &ptCurMousePos );

    // Calc how far it's moved since last frame
    POINT ptCurMouseDelta;
    ptCurMouseDelta.x = ptCurMousePos.x - m_ptLastMousePosition.x;
    ptCurMouseDelta.y = ptCurMousePos.y - m_ptLastMousePosition.y;

    // Record current position for next time
    m_ptLastMousePosition = ptCurMousePos;

    if( m_bResetCursorAfterMove && DXUTIsActive() )
    {
        // Set position of camera to center of desktop, 
        // so it always has room to move.  This is very useful
        // if the cursor is hidden.  If this isn't done and cursor is hidden, 
        // then invisible cursor will hit the edge of the screen 
        // and the user can't tell what happened
        POINT ptCenter;

        // Get the center of the current monitor
        MONITORINFO mi;
        mi.cbSize = sizeof( MONITORINFO );
        DXUTGetMonitorInfo( DXUTMonitorFromWindow( DXUTGetHWND(), MONITOR_DEFAULTTONEAREST ), &mi );
        ptCenter.x = ( mi.rcMonitor.left + mi.rcMonitor.right ) / 2;
        ptCenter.y = ( mi.rcMonitor.top + mi.rcMonitor.bottom ) / 2;
        SetCursorPos( ptCenter.x, ptCenter.y );
        m_ptLastMousePosition = ptCenter;
    }

    // Smooth the relative mouse data over a few frames so it isn't 
    // jerky when moving slowly at low frame rates.
    float fPercentOfNew = 1.0f / m_fFramesToSmoothMouseData;
    float fPercentOfOld = 1.0f - fPercentOfNew;
    m_vMouseDelta.x = m_vMouseDelta.x * fPercentOfOld + ptCurMouseDelta.x * fPercentOfNew;
    m_vMouseDelta.y = m_vMouseDelta.y * fPercentOfOld + ptCurMouseDelta.y * fPercentOfNew;

    m_vRotVelocity.x = m_vMouseDelta.x * m_fRotationScaler;
    m_vRotVelocity.y = m_vMouseDelta.y * m_fRotationScaler;
}


//--------------------------------------------------------------------------------------
// Figure out the velocity based on keyboard input & drag if any
//--------------------------------------------------------------------------------------
void CBaseCamera::UpdateVelocity( _In_ float fElapsedTime )
{
    XMVECTOR vGamePadRightThumb = XMVectorSet( m_vGamePadRightThumb.x, -m_vGamePadRightThumb.z, 0, 0 );

    XMVECTOR vMouseDelta = XMLoadFloat2( &m_vMouseDelta );
    XMVECTOR vRotVelocity = vMouseDelta * m_fRotationScaler + vGamePadRightThumb * 0.02f;

    XMStoreFloat2( &m_vRotVelocity, vRotVelocity );
    
    XMVECTOR vKeyboardDirection = XMLoadFloat3( &m_vKeyboardDirection );
    XMVECTOR vGamePadLeftThumb = XMLoadFloat3( &m_vGamePadLeftThumb );
    XMVECTOR vAccel = vKeyboardDirection + vGamePadLeftThumb;

    // Normalize vector so if moving 2 dirs (left & forward), 
    // the camera doesn't move faster than if moving in 1 dir
    vAccel = XMVector3Normalize( vAccel );

    // Scale the acceleration vector
    vAccel *= m_fMoveScaler;

    if( m_bMovementDrag )
    {
        // Is there any acceleration this frame?
        if( XMVectorGetX( XMVector3LengthSq( vAccel ) ) > 0 )
        {
            // If so, then this means the user has pressed a movement key
            // so change the velocity immediately to acceleration 
            // upon keyboard input.  This isn't normal physics
            // but it will give a quick response to keyboard input
            XMStoreFloat3( &m_vVelocity, vAccel );

            m_fDragTimer = m_fTotalDragTimeToZero;
            
            XMStoreFloat3( &m_vVelocityDrag, vAccel / m_fDragTimer );
        }
        else
        {
            // If no key being pressed, then slowly decrease velocity to 0
            if( m_fDragTimer > 0 )
            {
                // Drag until timer is <= 0
                XMVECTOR vVelocity = XMLoadFloat3( &m_vVelocity );
                XMVECTOR vVelocityDrag = XMLoadFloat3( &m_vVelocityDrag );

                vVelocity -= vVelocityDrag * fElapsedTime;

                XMStoreFloat3( &m_vVelocity, vVelocity );

                m_fDragTimer -= fElapsedTime;
            }
            else
            {
                // Zero velocity
                m_vVelocity = XMFLOAT3( 0, 0, 0 );
            }
        }
    }
    else
    {
        // No drag, so immediately change the velocity
        XMStoreFloat3( &m_vVelocity, vAccel );
    }
}


//--------------------------------------------------------------------------------------
// Maps a windows virtual key to an enum
//--------------------------------------------------------------------------------------
D3DUtil_CameraKeys CBaseCamera::MapKey( _In_ UINT nKey )
{
    // This could be upgraded to a method that's user-definable but for 
    // simplicity, we'll use a hardcoded mapping.
    switch( nKey )
    {
        case VK_CONTROL:
            return CAM_CONTROLDOWN;
        case VK_LEFT:
            return CAM_STRAFE_LEFT;
        case VK_RIGHT:
            return CAM_STRAFE_RIGHT;
        case VK_UP:
            return CAM_MOVE_FORWARD;
        case VK_DOWN:
            return CAM_MOVE_BACKWARD;
        case VK_PRIOR:
            return CAM_MOVE_UP;        // pgup
        case VK_NEXT:
            return CAM_MOVE_DOWN;      // pgdn

        case 'A':
            return CAM_STRAFE_LEFT;
        case 'D':
            return CAM_STRAFE_RIGHT;
        case 'W':
            return CAM_MOVE_FORWARD;
        case 'S':
            return CAM_MOVE_BACKWARD;
        case 'Q':
            return CAM_MOVE_DOWN;
        case 'E':
            return CAM_MOVE_UP;

        case VK_NUMPAD4:
            return CAM_STRAFE_LEFT;
        case VK_NUMPAD6:
            return CAM_STRAFE_RIGHT;
        case VK_NUMPAD8:
            return CAM_MOVE_FORWARD;
        case VK_NUMPAD2:
            return CAM_MOVE_BACKWARD;
        case VK_NUMPAD9:
            return CAM_MOVE_UP;
        case VK_NUMPAD3:
            return CAM_MOVE_DOWN;

        case VK_HOME:
            return CAM_RESET;
    }

    return CAM_UNKNOWN;
}


//--------------------------------------------------------------------------------------
// Reset the camera's position back to the default
//--------------------------------------------------------------------------------------
void CBaseCamera::Reset()
{
    XMVECTOR vDefaultEye = XMLoadFloat3( &m_vDefaultEye );
    XMVECTOR vDefaultLookAt = XMLoadFloat3( &m_vDefaultLookAt );

    SetViewParams( vDefaultEye, vDefaultLookAt );
}


//======================================================================================
// CFirstPersonCamera
//======================================================================================

CFirstPersonCamera::CFirstPersonCamera() noexcept :
    m_mCameraWorld{},
    m_nActiveButtonMask( 0x07 ),
    m_bRotateWithoutButtonDown(false)
{
}


//--------------------------------------------------------------------------------------
// Update the view matrix based on user input & elapsed time
//--------------------------------------------------------------------------------------
void CFirstPersonCamera::FrameMove( _In_ float fElapsedTime )
{
    if( DXUTGetGlobalTimer()->IsStopped() )
    {
        if (DXUTGetFPS() == 0.0f)
            fElapsedTime = 0;
        else
            fElapsedTime = 1.0f / DXUTGetFPS();
    }

    if( IsKeyDown( m_aKeys[CAM_RESET] ) )
    {
        Reset();
    }

    // Get keyboard/mouse/gamepad input
    GetInput( m_bEnablePositionMovement, ( m_nActiveButtonMask & m_nCurrentButtonMask ) || m_bRotateWithoutButtonDown, true );

    //// Get the mouse movement (if any) if the mouse button are down
    //if( (m_nActiveButtonMask & m_nCurrentButtonMask) || m_bRotateWithoutButtonDown )
    //    UpdateMouseDelta( fElapsedTime );

    // Get amount of velocity based on the keyboard input and drag (if any)
    UpdateVelocity( fElapsedTime );

    // Simple euler method to calculate position delta
    XMVECTOR vVelocity = XMLoadFloat3( &m_vVelocity );
    XMVECTOR vPosDelta = vVelocity * fElapsedTime;

    // If rotating the camera 
    if( ( m_nActiveButtonMask & m_nCurrentButtonMask )
        || m_bRotateWithoutButtonDown
        || m_vGamePadRightThumb.x != 0
        || m_vGamePadRightThumb.z != 0 )
    {
        // Update the pitch & yaw angle based on mouse movement
        float fYawDelta = m_vRotVelocity.x;
        float fPitchDelta = m_vRotVelocity.y;

        // Invert pitch if requested
        if( m_bInvertPitch )
            fPitchDelta = -fPitchDelta;

        m_fCameraPitchAngle += fPitchDelta;
        m_fCameraYawAngle += fYawDelta;

        // Limit pitch to straight up or straight down
        m_fCameraPitchAngle = std::max( -XM_PI / 2.0f, m_fCameraPitchAngle );
        m_fCameraPitchAngle = std::min( +XM_PI / 2.0f, m_fCameraPitchAngle );
    }

    // Make a rotation matrix based on the camera's yaw & pitch
    XMMATRIX mCameraRot = XMMatrixRotationRollPitchYaw( m_fCameraPitchAngle, m_fCameraYawAngle, 0 );

    // Transform vectors based on camera's rotation matrix
    XMVECTOR vWorldUp = XMVector3TransformCoord( g_XMIdentityR1, mCameraRot );
    XMVECTOR vWorldAhead = XMVector3TransformCoord( g_XMIdentityR2, mCameraRot );

    // Transform the position delta by the camera's rotation 
    if( !m_bEnableYAxisMovement )
    {
        // If restricting Y movement, do not include pitch
        // when transforming position delta vector.
        mCameraRot = XMMatrixRotationRollPitchYaw( 0.0f, m_fCameraYawAngle, 0.0f );
    }
    XMVECTOR vPosDeltaWorld = XMVector3TransformCoord( vPosDelta, mCameraRot );

    // Move the eye position 
    XMVECTOR vEye = XMLoadFloat3( &m_vEye );
    vEye += vPosDeltaWorld;
    if( m_bClipToBoundary )
        vEye = ConstrainToBoundary( vEye );
    XMStoreFloat3( &m_vEye, vEye );

    // Update the lookAt position based on the eye position
    XMVECTOR vLookAt = vEye + vWorldAhead;
    XMStoreFloat3( &m_vLookAt, vLookAt );

    // Update the view matrix
    XMMATRIX mView = XMMatrixLookAtLH( vEye, vLookAt, vWorldUp );
    XMStoreFloat4x4( &m_mView, mView );

    XMMATRIX mCameraWorld = XMMatrixInverse( nullptr, mView );
    XMStoreFloat4x4( &m_mCameraWorld, mCameraWorld );
}


//--------------------------------------------------------------------------------------
// Enable or disable each of the mouse buttons for rotation drag.
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void CFirstPersonCamera::SetRotateButtons( bool bLeft, bool bMiddle, bool bRight, bool bRotateWithoutButtonDown )
{
    m_nActiveButtonMask = ( bLeft ? MOUSE_LEFT_BUTTON : 0 ) |
        ( bMiddle ? MOUSE_MIDDLE_BUTTON : 0 ) |
        ( bRight ? MOUSE_RIGHT_BUTTON : 0 );
    m_bRotateWithoutButtonDown = bRotateWithoutButtonDown;
}



//======================================================================================
// CModelViewerCamera
//======================================================================================

CModelViewerCamera::CModelViewerCamera() noexcept :
    m_nRotateModelButtonMask(MOUSE_LEFT_BUTTON),
    m_nZoomButtonMask(MOUSE_WHEEL),
    m_nRotateCameraButtonMask(MOUSE_RIGHT_BUTTON),
    m_bAttachCameraToModel(false),
    m_bLimitPitch(false),
    m_bDragSinceLastUpdate(true),
    m_fRadius(5.0f),
    m_fDefaultRadius(5.0f),
    m_fMinRadius(1.0f),
    m_fMaxRadius(FLT_MAX)
{
    XMMATRIX id = XMMatrixIdentity();

    XMStoreFloat4x4( &m_mWorld, id );
    XMStoreFloat4x4( &m_mModelRot, id );
    XMStoreFloat4x4( &m_mModelLastRot, id );
    XMStoreFloat4x4( &m_mCameraRotLast, id );
    m_vModelCenter = XMFLOAT3( 0, 0, 0 );

    m_bEnablePositionMovement = false;
}


//--------------------------------------------------------------------------------------
// Update the view matrix & the model's world matrix based 
//       on user input & elapsed time
//--------------------------------------------------------------------------------------
void CModelViewerCamera::FrameMove( _In_ float fElapsedTime )
{
    if( IsKeyDown( m_aKeys[CAM_RESET] ) )
        Reset();

    // If no dragged has happend since last time FrameMove is called,
    // and no camera key is held down, then no need to handle again.
    if( !m_bDragSinceLastUpdate && 0 == m_cKeysDown )
        return;

    m_bDragSinceLastUpdate = false;

    //// If no mouse button is held down, 
    //// Get the mouse movement (if any) if the mouse button are down
    //if( m_nCurrentButtonMask != 0 ) 
    //    UpdateMouseDelta( fElapsedTime );

    GetInput( m_bEnablePositionMovement, m_nCurrentButtonMask != 0, true );

    // Get amount of velocity based on the keyboard input and drag (if any)
    UpdateVelocity( fElapsedTime );

    // Simple euler method to calculate position delta
    XMVECTOR vPosDelta = XMLoadFloat3( &m_vVelocity ) * fElapsedTime;

    // Change the radius from the camera to the model based on wheel scrolling
    if( m_nMouseWheelDelta && m_nZoomButtonMask == MOUSE_WHEEL )
        m_fRadius -= m_nMouseWheelDelta * m_fRadius * 0.1f / 120.0f;
    m_fRadius = std::min( m_fMaxRadius, m_fRadius );
    m_fRadius = std::max( m_fMinRadius, m_fRadius );
    m_nMouseWheelDelta = 0;

    // Get the inverse of the arcball's rotation matrix
    XMMATRIX mCameraRot = XMMatrixInverse( nullptr, m_ViewArcBall.GetRotationMatrix() );

    // Transform vectors based on camera's rotation matrix
    XMVECTOR vWorldUp = XMVector3TransformCoord( g_XMIdentityR1, mCameraRot );
    XMVECTOR vWorldAhead = XMVector3TransformCoord( g_XMIdentityR2, mCameraRot );

    // Transform the position delta by the camera's rotation 
    XMVECTOR vPosDeltaWorld = XMVector3TransformCoord( vPosDelta, mCameraRot );

    // Move the lookAt position 
    XMVECTOR vLookAt = XMLoadFloat3( &m_vLookAt );
    vLookAt += vPosDeltaWorld;
    if( m_bClipToBoundary )
        vLookAt = ConstrainToBoundary( vLookAt );
    XMStoreFloat3( &m_vLookAt, vLookAt );

    // Update the eye point based on a radius away from the lookAt position
    XMVECTOR vEye = vLookAt - vWorldAhead * m_fRadius;
    XMStoreFloat3( &m_vEye, vEye );

    // Update the view matrix
    XMMATRIX mView = XMMatrixLookAtLH( vEye, vLookAt, vWorldUp );
    XMStoreFloat4x4( &m_mView, mView );

    XMMATRIX mInvView = XMMatrixInverse( nullptr, mView );
    mInvView.r[3] = XMVectorSelect( mInvView.r[3], g_XMZero, g_XMSelect1110 );

    XMMATRIX mModelLastRot = XMLoadFloat4x4( &m_mModelLastRot );
    XMMATRIX mModelLastRotInv = XMMatrixInverse( nullptr, mModelLastRot );

    // Accumulate the delta of the arcball's rotation in view space.
    // Note that per-frame delta rotations could be problematic over long periods of time.
    XMMATRIX mModelRot0 = m_WorldArcBall.GetRotationMatrix();
    XMMATRIX mModelRot = XMLoadFloat4x4( &m_mModelRot );
    mModelRot *= mView * mModelLastRotInv * mModelRot0 * mInvView;

    if( m_ViewArcBall.IsBeingDragged() && m_bAttachCameraToModel && !IsKeyDown( m_aKeys[CAM_CONTROLDOWN] ) )
    {
        // Attach camera to model by inverse of the model rotation
        XMMATRIX mCameraRotLast = XMLoadFloat4x4( &m_mCameraRotLast );
        XMMATRIX mCameraLastRotInv = XMMatrixInverse( nullptr, mCameraRotLast );
        XMMATRIX mCameraRotDelta = mCameraLastRotInv * mCameraRot; // local to world matrix
        mModelRot *= mCameraRotDelta;
    }

    XMStoreFloat4x4( &m_mModelLastRot, mModelRot0 );
    XMStoreFloat4x4( &m_mCameraRotLast, mCameraRot );

    // Since we're accumulating delta rotations, we need to orthonormalize 
    // the matrix to prevent eventual matrix skew
    XMVECTOR xBasis = XMVector3Normalize( mModelRot.r[0] );
    XMVECTOR yBasis = XMVector3Cross( mModelRot.r[2], xBasis );
    yBasis = XMVector3Normalize( yBasis );
    XMVECTOR zBasis = XMVector3Cross( xBasis, yBasis );

    mModelRot.r[0] = XMVectorSelect( mModelRot.r[0], xBasis, g_XMSelect1110 );
    mModelRot.r[1] = XMVectorSelect( mModelRot.r[1], yBasis, g_XMSelect1110 );
    mModelRot.r[2] = XMVectorSelect( mModelRot.r[2], zBasis, g_XMSelect1110 );

    // Translate the rotation matrix to the same position as the lookAt position
    mModelRot.r[3] = XMVectorSelect( mModelRot.r[3], vLookAt, g_XMSelect1110 );
    
    XMStoreFloat4x4( &m_mModelRot, mModelRot );

    // Translate world matrix so its at the center of the model
    XMMATRIX mTrans = XMMatrixTranslation( -m_vModelCenter.x, -m_vModelCenter.y, -m_vModelCenter.z );
    XMMATRIX mWorld = mTrans * mModelRot;
    XMStoreFloat4x4( &m_mWorld, mWorld );
}


//--------------------------------------------------------------------------------------
void CModelViewerCamera::SetDragRect( _In_ const RECT& rc )
{
    CBaseCamera::SetDragRect( rc );

    m_WorldArcBall.SetOffset( rc.left, rc.top );
    m_ViewArcBall.SetOffset( rc.left, rc.top );

    SetWindow( rc.right - rc.left, rc.bottom - rc.top );
}


//--------------------------------------------------------------------------------------
// Reset the camera's position back to the default
//--------------------------------------------------------------------------------------
void CModelViewerCamera::Reset()
{
    CBaseCamera::Reset();

    XMMATRIX id = XMMatrixIdentity();
    XMStoreFloat4x4( &m_mWorld, id );
    XMStoreFloat4x4( &m_mModelRot, id );
    XMStoreFloat4x4( &m_mModelLastRot, id );
    XMStoreFloat4x4( &m_mCameraRotLast, id );

    m_fRadius = m_fDefaultRadius;
    m_WorldArcBall.Reset();
    m_ViewArcBall.Reset();
}


//--------------------------------------------------------------------------------------
// Override for setting the view parameters
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
void CModelViewerCamera::SetViewParams( FXMVECTOR vEyePt, FXMVECTOR vLookatPt )
{
    CBaseCamera::SetViewParams( vEyePt, vLookatPt );

    // Propogate changes to the member arcball
    XMMATRIX mRotation = XMMatrixLookAtLH( vEyePt, vLookatPt, g_XMIdentityR1 );
    XMVECTOR quat = XMQuaternionRotationMatrix( mRotation );
    m_ViewArcBall.SetQuatNow( quat );

    // Set the radius according to the distance
    XMVECTOR vEyeToPoint = XMVectorSubtract( vLookatPt, vEyePt );
    float len = XMVectorGetX( XMVector3Length( vEyeToPoint ) );
    SetRadius( len );

    // View information changed. FrameMove should be called.
    m_bDragSinceLastUpdate = true;
}


//--------------------------------------------------------------------------------------
// Call this from your message proc so this class can handle window messages
//--------------------------------------------------------------------------------------
_Use_decl_annotations_
LRESULT CModelViewerCamera::HandleMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    CBaseCamera::HandleMessages( hWnd, uMsg, wParam, lParam );

    if( ( ( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) && m_nRotateModelButtonMask & MOUSE_LEFT_BUTTON ) ||
        ( ( uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONDBLCLK ) && m_nRotateModelButtonMask & MOUSE_MIDDLE_BUTTON ) ||
        ( ( uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK ) && m_nRotateModelButtonMask & MOUSE_RIGHT_BUTTON ) )
    {
        int iMouseX = ( short )LOWORD( lParam );
        int iMouseY = ( short )HIWORD( lParam );
        m_WorldArcBall.OnBegin( iMouseX, iMouseY );
    }

    if( ( ( uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK ) && m_nRotateCameraButtonMask & MOUSE_LEFT_BUTTON ) ||
        ( ( uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONDBLCLK ) &&
          m_nRotateCameraButtonMask & MOUSE_MIDDLE_BUTTON ) ||
        ( ( uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONDBLCLK ) && m_nRotateCameraButtonMask & MOUSE_RIGHT_BUTTON ) )
    {
        int iMouseX = ( short )LOWORD( lParam );
        int iMouseY = ( short )HIWORD( lParam );
        m_ViewArcBall.OnBegin( iMouseX, iMouseY );
    }

    if( uMsg == WM_MOUSEMOVE )
    {
        int iMouseX = ( short )LOWORD( lParam );
        int iMouseY = ( short )HIWORD( lParam );
        m_WorldArcBall.OnMove( iMouseX, iMouseY );
        m_ViewArcBall.OnMove( iMouseX, iMouseY );
    }

    if( ( uMsg == WM_LBUTTONUP && m_nRotateModelButtonMask & MOUSE_LEFT_BUTTON ) ||
        ( uMsg == WM_MBUTTONUP && m_nRotateModelButtonMask & MOUSE_MIDDLE_BUTTON ) ||
        ( uMsg == WM_RBUTTONUP && m_nRotateModelButtonMask & MOUSE_RIGHT_BUTTON ) )
    {
        m_WorldArcBall.OnEnd();
    }

    if( ( uMsg == WM_LBUTTONUP && m_nRotateCameraButtonMask & MOUSE_LEFT_BUTTON ) ||
        ( uMsg == WM_MBUTTONUP && m_nRotateCameraButtonMask & MOUSE_MIDDLE_BUTTON ) ||
        ( uMsg == WM_RBUTTONUP && m_nRotateCameraButtonMask & MOUSE_RIGHT_BUTTON ) )
    {
        m_ViewArcBall.OnEnd();
    }

    if( uMsg == WM_CAPTURECHANGED )
    {
        if( ( HWND )lParam != hWnd )
        {
            if( ( m_nRotateModelButtonMask & MOUSE_LEFT_BUTTON ) ||
                ( m_nRotateModelButtonMask & MOUSE_MIDDLE_BUTTON ) ||
                ( m_nRotateModelButtonMask & MOUSE_RIGHT_BUTTON ) )
            {
                m_WorldArcBall.OnEnd();
            }

            if( ( m_nRotateCameraButtonMask & MOUSE_LEFT_BUTTON ) ||
                ( m_nRotateCameraButtonMask & MOUSE_MIDDLE_BUTTON ) ||
                ( m_nRotateCameraButtonMask & MOUSE_RIGHT_BUTTON ) )
            {
                m_ViewArcBall.OnEnd();
            }
        }
    }

    if( uMsg == WM_LBUTTONDOWN ||
        uMsg == WM_LBUTTONDBLCLK ||
        uMsg == WM_MBUTTONDOWN ||
        uMsg == WM_MBUTTONDBLCLK ||
        uMsg == WM_RBUTTONDOWN ||
        uMsg == WM_RBUTTONDBLCLK ||
        uMsg == WM_LBUTTONUP ||
        uMsg == WM_MBUTTONUP ||
        uMsg == WM_RBUTTONUP ||
        uMsg == WM_MOUSEWHEEL ||
        uMsg == WM_MOUSEMOVE )
    {
        m_bDragSinceLastUpdate = true;
    }

    return FALSE;
}


//======================================================================================
// CDXUTDirectionWidget
//======================================================================================

CDXUTDirectionWidget::CDXUTDirectionWidget() noexcept :
    m_fRadius(1.0f),
    m_nRotateMask(MOUSE_RIGHT_BUTTON)
{
    m_vDefaultDir = XMFLOAT3( 0, 1, 0 );
    m_vCurrentDir = m_vDefaultDir;

    XMMATRIX id = XMMatrixIdentity();

    XMStoreFloat4x4( &m_mView, id );
    XMStoreFloat4x4( &m_mRot, id );
    XMStoreFloat4x4( &m_mRotSnapshot, id );
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
LRESULT CDXUTDirectionWidget::HandleMessages( HWND hWnd, UINT uMsg,
                                              WPARAM wParam, LPARAM lParam )
{
    UNREFERENCED_PARAMETER(wParam);

    switch( uMsg )
    {
        case WM_LBUTTONDOWN:
        case WM_MBUTTONDOWN:
        case WM_RBUTTONDOWN:
            {
                if( ( ( m_nRotateMask & MOUSE_LEFT_BUTTON ) != 0 && uMsg == WM_LBUTTONDOWN ) ||
                    ( ( m_nRotateMask & MOUSE_MIDDLE_BUTTON ) != 0 && uMsg == WM_MBUTTONDOWN ) ||
                    ( ( m_nRotateMask & MOUSE_RIGHT_BUTTON ) != 0 && uMsg == WM_RBUTTONDOWN ) )
                {
                    int iMouseX = ( int )( short )LOWORD( lParam );
                    int iMouseY = ( int )( short )HIWORD( lParam );
                    m_ArcBall.OnBegin( iMouseX, iMouseY );
                    SetCapture( hWnd );
                }
                return TRUE;
            }

        case WM_MOUSEMOVE:
        {
            if( m_ArcBall.IsBeingDragged() )
            {
                int iMouseX = ( int )( short )LOWORD( lParam );
                int iMouseY = ( int )( short )HIWORD( lParam );
                m_ArcBall.OnMove( iMouseX, iMouseY );
                UpdateLightDir();
            }
            return TRUE;
        }

        case WM_LBUTTONUP:
        case WM_MBUTTONUP:
        case WM_RBUTTONUP:
            {
                if( ( ( m_nRotateMask & MOUSE_LEFT_BUTTON ) != 0 && uMsg == WM_LBUTTONUP ) ||
                    ( ( m_nRotateMask & MOUSE_MIDDLE_BUTTON ) != 0 && uMsg == WM_MBUTTONUP ) ||
                    ( ( m_nRotateMask & MOUSE_RIGHT_BUTTON ) != 0 && uMsg == WM_RBUTTONUP ) )
                {
                    m_ArcBall.OnEnd();
                    ReleaseCapture();
                }

                UpdateLightDir();
                return TRUE;
            }

        case WM_CAPTURECHANGED:
        {
            if( ( HWND )lParam != hWnd )
            {
                if( ( m_nRotateMask & MOUSE_LEFT_BUTTON ) ||
                    ( m_nRotateMask & MOUSE_MIDDLE_BUTTON ) ||
                    ( m_nRotateMask & MOUSE_RIGHT_BUTTON ) )
                {
                    m_ArcBall.OnEnd();
                    ReleaseCapture();
                }
            }
            return TRUE;
        }
    }

    return 0;
}


//--------------------------------------------------------------------------------------
HRESULT CDXUTDirectionWidget::UpdateLightDir()
{
    XMMATRIX mView = XMLoadFloat4x4( &m_mView );

    XMMATRIX mInvView = XMMatrixInverse( nullptr, mView );
    mInvView.r[3] = XMVectorSelect( mInvView.r[3], g_XMZero, g_XMSelect1110 );

    XMMATRIX mRotSnapshot = XMLoadFloat4x4( &m_mRotSnapshot );
    XMMATRIX mLastRotInv = XMMatrixInverse( nullptr, mRotSnapshot );

    XMMATRIX mRot0 = m_ArcBall.GetRotationMatrix();
    XMStoreFloat4x4( &m_mRotSnapshot, mRot0 );

    // Accumulate the delta of the arcball's rotation in view space.
    // Note that per-frame delta rotations could be problematic over long periods of time.
    XMMATRIX mRot = XMLoadFloat4x4( &m_mRot );
    mRot *= mView * mLastRotInv * mRot0 * mInvView;

    // Since we're accumulating delta rotations, we need to orthonormalize 
    // the matrix to prevent eventual matrix skew
    XMVECTOR xBasis = XMVector3Normalize( mRot.r[0] );
    XMVECTOR yBasis = XMVector3Cross( mRot.r[2], xBasis );
    yBasis = XMVector3Normalize( yBasis );
    XMVECTOR zBasis = XMVector3Cross( xBasis, yBasis );
    mRot.r[0] = XMVectorSelect( mRot.r[0], xBasis, g_XMSelect1110 );
    mRot.r[1] = XMVectorSelect( mRot.r[1], yBasis, g_XMSelect1110 );
    mRot.r[2] = XMVectorSelect( mRot.r[2], zBasis, g_XMSelect1110 );
    XMStoreFloat4x4( &m_mRot, mRot );

    // Transform the default direction vector by the light's rotation matrix
    XMVECTOR vDefaultDir = XMLoadFloat3( &m_vDefaultDir );
    XMVECTOR vCurrentDir = XMVector3TransformNormal( vDefaultDir, mRot );
    XMStoreFloat3( &m_vCurrentDir, vCurrentDir );

    return S_OK;
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT CDXUTDirectionWidget::OnRender( FXMVECTOR color, CXMMATRIX mView, CXMMATRIX mProj, FXMVECTOR vEyePt )
{
    UNREFERENCED_PARAMETER(color);
    UNREFERENCED_PARAMETER(mView);
    UNREFERENCED_PARAMETER(mProj);
    UNREFERENCED_PARAMETER(vEyePt);
    // TODO - 
    return S_OK;
}


//--------------------------------------------------------------------------------------
_Use_decl_annotations_
HRESULT CDXUTDirectionWidget::StaticOnD3D11CreateDevice( ID3D11Device* pd3dDevice, ID3D11DeviceContext* pd3dImmediateContext )
{
    UNREFERENCED_PARAMETER(pd3dDevice);
    UNREFERENCED_PARAMETER(pd3dImmediateContext);
    // TODO -
    return S_OK;
}


//--------------------------------------------------------------------------------------
void CDXUTDirectionWidget::StaticOnD3D11DestroyDevice()
{
    // TODO -
}
