Click here to Skip to main content
15,894,106 members
Articles / Multimedia / DirectX

A New Perspective on Viewing

Rate me:
Please Sign up or sign in to vote.
4.93/5 (44 votes)
6 Oct 2009CPOL16 min read 111.6K   2.6K   90  
Simple yet comprehensive viewing code for OpenGL and Direct3D.
// Interact.cpp
//
// Simple 3D interaction
//
// Copyright (C) 2009 John Hilton
//
// Considerable use of the SpatialMath types and operators is used to greatly
// simplify the code. An understanding of SpatialMath's operators is needed to
// read the code.

#include <stdafx.h>
#include "gfx.h"
#include "interact.h"
#include "spatialmath.h"

//
template< typename T > union AVec2 { T v2[2]; T x, y; };
typedef AVec2<float> AVec2f;

class CInteractImpl
{
public:
    POINTS          m_LastMouse;
    AVec3f          m_MovedFrom;    // view space point
    AVec3f          m_MovedTo;      // view space point
    TViewVolumef   *m_pViewVolume;
    AMat4x3f       *m_pViewToWorld;
    float           m_PixelToViewRectFactor;
    static const float m_kVerySmall;

    void OnLmbDown( POINTS& MousePos )
    {
        // Update m_LastMouse
        m_LastMouse = MousePos;
    }
    BOOL OnMouseMove( HWND hWnd, UINT nFlags, POINTS& MousePos, float ViewVolume[7], float ViewToWorld[4][3] )
    {
        // Ignore the mouse move unless the left mouse button is down
        if (~nFlags & MK_LBUTTON)
            return FALSE;

        // Save the parameters
        m_pViewVolume    = (TViewVolumef *) ViewVolume;
        m_pViewToWorld   = (AMat4x3f*) ViewToWorld;

        // Calculate the pixel-to-view-rectangle scale factor
        RECT rect;
        GetClientRect( hWnd, &rect );
        m_PixelToViewRectFactor = m_pViewVolume->hw * 2.0f / rect.right;

        // Transform the mouse move from the physical pixel space with the
        // origin at the top left to the virtual view space on the z=0 plane.
        m_MovedTo.x   =   MousePos.x * m_PixelToViewRectFactor - m_pViewVolume->hw;
        m_MovedTo.y   = -(MousePos.y * m_PixelToViewRectFactor - m_pViewVolume->hh);
        m_MovedTo.z   = 0;
        m_MovedFrom.x =   m_LastMouse.x * m_PixelToViewRectFactor - m_pViewVolume->hw;
        m_MovedFrom.y = -(m_LastMouse.y * m_PixelToViewRectFactor - m_pViewVolume->hh);
        m_MovedFrom.z = 0;

        // Update m_LastMouse
        m_LastMouse = MousePos;

        // Select either Pan, ZoomSpinZ or Spin
        BOOL Redraw;
        if (nFlags & MK_CONTROL)        Redraw = SpinXY();
        else if (nFlags & MK_SHIFT)     Redraw = ZoomSpinZ();
        else                            Redraw = Pan();

        // forget the parameters
        m_pViewVolume = NULL;
        m_pViewToWorld = NULL;

        return Redraw;
    }

    BOOL Pan()
    {
        // Calculate the view space pan vector, transform it to world space
        // and subtract it from the ViewToWorld.trn
        m_pViewToWorld->trn -= (m_MovedTo - m_MovedFrom) * m_pViewToWorld->rot;
        return TRUE;
    }

    BOOL ZoomSpinZ()
    {
        // This is similar interaction to the modern multitouch screen interaction
        // of 2D images with one finger at the center of the screen. Casual User's
        // prefer zoom as an independent interaction.

        float MovedFromLength = LENGTH m_MovedFrom;
        float MovedToLength   = LENGTH m_MovedTo;
        if (abs(MovedFromLength) < m_kVerySmall || abs(MovedToLength) < m_kVerySmall)
            return FALSE;

        // Calculate the spin angle
        float SpinAngle = atan2( m_MovedTo.y  , m_MovedTo.x )
                        - atan2( m_MovedFrom.y, m_MovedFrom.x );

        // Calculate the view space z-axis rotation matrix
        float cs = cos(SpinAngle);
        float sn = sin(SpinAngle);
        AMat3x3f Spin = CMat3x3f( cs, -sn, 0,
                                  sn,  cs, 0,
                                   0,  0, 1 );

        // Rotate about the view space origin (world space m_pViewToWorld.trn stays put)
        m_pViewToWorld->rot = Spin * m_pViewToWorld->rot;

        // Calculate the inverse zoom factor
        float InverseZoomFactor = MovedFromLength / MovedToLength;

        // Zoom about the view space origin
        m_pViewVolume->hw *= InverseZoomFactor;
        m_pViewVolume->hh *= InverseZoomFactor;

        return TRUE;
    }
    BOOL SpinXY()
    {
        // Provide rotation so horizontal movement corresponds to a
        // vertical spin and vertical movement corresponds to a horizontal
        // spin.

        AVec3f Delta = m_MovedTo - m_MovedFrom;
        float LengthDelta = LENGTH Delta;
        if (LengthDelta < m_kVerySmall)
            return FALSE;
        Delta /= LengthDelta;   // make a unit vector

        // Say that moving 1/4 of the longest of the Window's width or height
        // equals one rotation.
        float HalfRotationDistance = max( m_pViewVolume->hw, m_pViewVolume->hh );
        float SpinAngle = LengthDelta * (float) M_PI / HalfRotationDistance;

        // Calculate the view space spin axis
        AVec3f SpinAxis = CVec3f(0,0,1) CROSS Delta;

        // Rotate about the view space origin (world space m_pViewToWorld.trn stays put)
        m_pViewToWorld->rot = CMat3x3f( SpinAngle, SpinAxis ) * m_pViewToWorld->rot;

        return TRUE;
    }
};
const float CInteractImpl::m_kVerySmall = 1e-6f;

#pragma region CInteract

CInteract::CInteract()
{
    m_pImpl = new CInteractImpl;
    ASSERT( m_pImpl );
}

CInteract::~CInteract()
{
    if (m_pImpl) delete m_pImpl;
}

void CInteract::OnLbuttonDown( POINTS& MousePos )
{
    m_pImpl->OnLmbDown( MousePos );
}

BOOL CInteract::OnMouseMove( HWND hWnd, UINT nFlags, POINTS& MousePos,
                             float ViewVolume[7], float ViewToWorld[4][3] )
{
    return m_pImpl->OnMouseMove( hWnd, nFlags, MousePos, ViewVolume, ViewToWorld );
}

#pragma endregion

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Spatial Freedom
Australia Australia
Software engineer, mechanical engineer, electronics engineer, inventor, manager, entrepreneur, husband, father, friend.
B.Sc. B.E.(Hons) M.Eng.Sc.
Some things I've done
- Invented the Spaceball(R)/1983 and Astroid(R)/2002 3D mice
- Patents: 3D mouse, data compression, acoustic transducer
- Wrote animation software in mid 1980s for TV commercials
- Wrote a basic CAD drawing program in 1980s
- Lived in Boston, Massachusetts for 11 years
- Architected and managed full custom ASIC chip
- Reviewed bionic eye technology for investment purposes
- Product development on CPR aid for heart attacks
- Developed an electronic sports whistle
- Was actually stranded on a deserted Pacific island
- Software: lots - embedded, device driver, applications
Some things I want to do
- Develop more cool hardware/software products
- Solve the 3D mouse software barrier to proliferate 3D mice
- Help bring 3D to the masses
- Help others

Comments and Discussions