// Main.cpp
//
// High level code for the NewView demo
//
// Copyright (C) 2009 John Hilton
//
// CMainImpl : IGfx<T>::ICallbacks
// m_GfxWinOpenGL CGfxWin<CGfxOpenGLf> : CGfxOpenGLf, CWindowImpl
// m_GfxWinDirect3D CGfxWin<CGfxDirect3Df> : CGfxDirect3Df, CWindowImpl
// CGfxOpenGLf : IGfx<float>
// CGfxDirect3Df : IGfx<float>
// CGfxOpenGLImpl<T> : CGfxSubclass<T>
// CGfxDirect3DImpl<T> : CGfxSubclass<T>
// CGfxSubclass<T> : CWindowImpl
#include "stdafx.h"
#include "Main.h"
#include "Gfx.h"
#include "Interact.h"
#define _USE_MATH_DEFINES
#include "math.h"
#pragma region SpatialMath
template< typename T >
union TVec3
{
T v3[3];
struct { T x, y, z; };
};
template< typename T >
union TRotTrn
{
T v12[12];
T m4x3[4][3];
struct {
T m3x3[3][3];
union { T v3[3]; struct { T x, y, z; }; };
};
};
template< typename T >
TVec3<T> operator/( TVec3<T>& vec, TRotTrn<T>& RotTrn )
{
// Translate vec by -trn
TVec3<T> vA = { vec.x-RotTrn.x, vec.y-RotTrn.y, vec.z-RotTrn.z };
// Rotate vA by Inverse(rot)
TVec3<T> vB;
for (int i=0; i<3; i++)
vB.v3[i] = vA.x * RotTrn.m3x3[i][0]
+ vA.y * RotTrn.m3x3[i][1]
+ vA.z * RotTrn.m3x3[i][2];
return vB;
}
typedef TVec3<float> TVec3f;
typedef TRotTrn<float> TRotTrnf;
#pragma endregion
static DWORD Cube[] = {
// Front, back, bottom, top, left and right faces
//GFXCOLOR(1.0,0.5,0.9), GFXTRIANGLESTRIP(2), GFXV(0,0,1), GFXV(0,1,1), GFXV(1,0,1), GFXV(1,1,1),
GFXCOLOR(0.5,0.9,0.8), GFXTRIANGLESTRIP(2), GFXV(0,0,0), GFXV(1,0,0), GFXV(0,1,0), GFXV(1,1,0),
GFXCOLOR(0.7,0.4,0.2), GFXTRIANGLESTRIP(2), GFXV(0,0,0), GFXV(0,0,1), GFXV(1,0,0), GFXV(1,0,1),
GFXCOLOR(0.3,0.8,0.5), GFXTRIANGLESTRIP(2), GFXV(0,1,0), GFXV(1,1,0), GFXV(0,1,1), GFXV(1,1,1),
GFXCOLOR(1.0,1.0,1.0), GFXTRIANGLESTRIP(2), GFXV(0,0,0), GFXV(0,1,0), GFXV(0,0,1), GFXV(0,1,1),
GFXCOLOR(0.9,0.2,0.4), GFXTRIANGLESTRIP(2), GFXV(1,0,0), GFXV(1,0,1), GFXV(1,1,0), GFXV(1,1,1)
};
static int Cubelength = sizeof(Cube)/sizeof(*Cube);
struct IGfxWinCallbacks {
virtual void OnLbuttonDown( HWND hWnd, UINT nFlags, POINTS Points, IGfx<float>& gfx ) = 0;
virtual void OnMouseMove( HWND hWnd, UINT nFlags, POINTS Points, IGfx<float>& gfx ) = 0;
};
// CGfxWin - combines CWindowImpl<> with an IGfx derived object
template< typename TGfx >
class CGfxWin : public TGfx, public CWindowImpl<CGfxWin<TGfx>>
{
public:
IGfxWinCallbacks* m_pGfxWinCallbacks;
CGfxWin( typename TGfx::ICallbacks& GfxCallbacks, typename IGfxWinCallbacks& GfxWinCallbacks )
: TGfx(GfxCallbacks)
, m_pGfxWinCallbacks(&GfxWinCallbacks)
{
}
void Create( HWND hWndParent, RECT& rect )
{
CWindowImpl<CGfxWin>::Create( hWndParent, rect );
TGfx::Attach(m_hWnd);
}
BEGIN_MSG_MAP(CGfxWin)
MESSAGE_HANDLER( WM_MOUSEMOVE, OnMouseMove );
MESSAGE_HANDLER( WM_ERASEBKGND, OnEraseBkgnd );
MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLbuttonDown );
MESSAGE_HANDLER( WM_LBUTTONUP, OnLbuttonUp );
END_MSG_MAP()
LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
uMsg; wParam; lParam; bHandled;
// do nothing - don't draw the background
return 0L;
}
LRESULT OnLbuttonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
uMsg; bHandled;
UINT nFlags = (UINT) wParam;
POINTS MousePos = MAKEPOINTS(lParam);
m_pGfxWinCallbacks->OnLbuttonDown( m_hWnd, nFlags, MousePos, *this );
SetCapture();
return 0L;
}
LRESULT OnLbuttonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
uMsg; wParam; lParam; bHandled;
ReleaseCapture();
return 0L;
}
LRESULT OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
uMsg; bHandled;
UINT nFlags = (UINT) wParam;
// Ignore the mouse move unless the left mouse button is down
if (~nFlags & MK_LBUTTON)
return 0L;
POINTS MousePos = MAKEPOINTS(lParam);
m_pGfxWinCallbacks->OnMouseMove( m_hWnd, nFlags, MousePos, *this );
return 0L;
}
};
class CMainImpl
: public IGfx<float>::ICallbacks
, public IGfxWinCallbacks
, public CWindowImpl<CMainImpl> // intercept WM_SIZE
, public CInteract
{
public:
CGfxWin<CGfxOpenGLf> m_GfxWinOpenGL;
CGfxWin<CGfxDirect3Df> m_GfxWinDirect3D;
float m_ViewAngle; // radians
float m_HalfViewSize;
float m_ViewVolume[7];
float m_ViewToWorld[4][3];
BEGIN_MSG_MAP(CMainImpl)
MESSAGE_HANDLER( WM_SIZE, OnSize );
MESSAGE_HANDLER( WM_CHAR, OnChar );
END_MSG_MAP()
CMainImpl( HWND hWnd ) // constructor
: CInteract()
, m_GfxWinOpenGL( *this, *this )
, m_GfxWinDirect3D( *this, *this )
{
ResetView();
VERIFY(SubclassWindow( hWnd ));
// Get the client rectangle
RECT rectClient;
GetClientRect( &rectClient );
// Split it vertically in two
LONG width = rectClient.right;
LONG height = rectClient.bottom;
LONG mid = width / 2;
// Create the OpenGL window and attach it to the CGfxOpenGL object
RECT rectOpenGL = { 0, 0, mid, height };
m_GfxWinOpenGL.Create( hWnd, rectOpenGL );
// Create the Direct3D window and attach it to the CGfxDirect3D object
RECT rectDirect3D = { mid, 0, width, height };
m_GfxWinDirect3D.Create( hWnd, rectDirect3D );
}
~CMainImpl()
{
if (m_hWnd) UnsubclassWindow();
}
LRESULT OnSize(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
wParam; nMsg; bHandled; lParam;
//int cx = LOWORD(lParam);
int cy = HIWORD(lParam);
if (!m_hWnd || !cy) return 0L;
// Get the client rectangle
RECT rectClient;
GetClientRect( &rectClient );
// Split it vertically in two
LONG width = rectClient.right;
LONG height = rectClient.bottom;
LONG mid = width / 2;
// Create the OpenGL window and attach it to the CGfxOpenGL object
RECT rectOpenGL = { 0, 0, mid, height };
m_GfxWinOpenGL.MoveWindow( &rectOpenGL );
// Create the Direct3D window and attach it to the CGfxDirect3D object
RECT rectDirect3D = { mid, 0, width, height };
m_GfxWinDirect3D.MoveWindow( &rectDirect3D );
bHandled = FALSE; // pass on to default handler
return 0L;
}
LRESULT OnChar(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
nMsg; lParam; bHandled;
switch (wParam)
{
case ' ': ResetView(); break;
case 'P':
case 'p': TogglePerspective(); break;
}
return 0L;
}
void ResetView()
{
m_ViewAngle = 45 * (float)M_PI/180;
m_HalfViewSize = 10;
static const float identity[4][3] = {{1,0,0},{0,1,0},{0,0,1},{0,0,0}};
struct Tv12 { float v12[12]; };
*(Tv12*) m_ViewToWorld = *(Tv12*) identity;
if (IsWindow())
Invalidate(FALSE);
}
void TogglePerspective()
{
if (m_ViewAngle)
m_ViewAngle = 0;
else
m_ViewAngle = 45 * (float)M_PI/180;
Invalidate(FALSE);
}
virtual void OnPaint( IGfx<float>& gfx )
{
float ViewVolume[7];
CalcViewVolume( ViewVolume, gfx );
gfx.ConfigureView( ViewVolume, m_ViewToWorld );
gfx.Push();
gfx.Translate( -4, 0, 0 );
gfx.Scale( 4 );
gfx.Translate( -0.5, -0.5, -0.5 );
gfx.Render( Cube, Cubelength );
gfx.Pop();
gfx.Translate( 4, 0, 0 );
gfx.Scale( 4 );
gfx.Translate( -0.5, -0.5, -0.5 );
gfx.Render( Cube, Cubelength );
}
void CalcViewVolume( float ViewVolume[7], IGfx<float>& gfx )
{
TViewVolumef& vv = *(TViewVolumef*) ViewVolume;
vv.hw = vv.hh = m_HalfViewSize;
vv.zn = 10;
vv.zf = -10;
vv.iez = tan(0.5f*m_ViewAngle)/m_HalfViewSize;
vv.tsx = vv.tsy = 0;
// Match the aspect ratio by extending hw or hh
// Get the client rectangle
RECT rectClient;
if (&gfx == &m_GfxWinOpenGL)
m_GfxWinOpenGL.GetClientRect( &rectClient );
else
m_GfxWinDirect3D.GetClientRect( &rectClient );
float width = (float) rectClient.right;
float height = (float) rectClient.bottom;
// Scale the width or height up to match the aspect ratio
if (width >= height)
vv.hw *= width / height;
else
vv.hh *= height / width;
}
virtual void IGfx<float>::ICallbacks::OnFinalMessage( HWND hWnd )
{
hWnd;
// do nothing
}
virtual void IGfxWinCallbacks::OnLbuttonDown( HWND hWnd, UINT nFlags, POINTS MousePos, IGfx<float>& gfx )
{
hWnd; nFlags; gfx;
CInteract::OnLbuttonDown( MousePos );
}
virtual void IGfxWinCallbacks::OnMouseMove( HWND hWnd, UINT nFlags, POINTS MousePos, IGfx<float>& gfx )
{
TViewVolumef ViewVolume;
CalcViewVolume( ViewVolume.v7, gfx );
BOOL Redraw = CInteract::OnMouseMove( hWnd, nFlags, MousePos, ViewVolume.v7, m_ViewToWorld );
m_HalfViewSize = min( ViewVolume.hw, ViewVolume.hh );
if (Redraw)
InvalidateRect( NULL, FALSE );
}
};
CMain::CMain( HWND hWnd )
{
m_pImpl = new CMainImpl( hWnd );
ASSERT(m_pImpl);
MessageBox( NULL,
_T("The left window is rendered with OpenGL and the right with Direct3D.\r\n")
_T("Use the left mouse button (LMB) to pan, LMB+SHIFT to zoom/spinZ and\r\n")
_T("LMB+CTRL to spinXY.\r\n")
_T("Press SPACE to reset the view and P to toggle perspective.\r\n"), _T("NewView"), MB_OK );
}
CMain::~CMain()
{
if (m_pImpl) delete m_pImpl;
}