|
// 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.
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