SA-MP/saco/d3d9/common/DXUTmisc.cpp

2876 lines
101 KiB
C++

//--------------------------------------------------------------------------------------
// File: DXUTMisc.cpp
//
// Shortcut macros and functions for using DX objects
//
// Copyright (c) Microsoft Corporation. All rights reserved
//--------------------------------------------------------------------------------------
#include "dxstdafx.h"
#undef min // use __min instead
#undef max // use __max instead
//--------------------------------------------------------------------------------------
// Global/Static Members
//--------------------------------------------------------------------------------------
// MATCH
CDXUTResourceCache& DXUTGetGlobalResourceCache()
{
// Using an accessor function gives control of the construction order
static CDXUTResourceCache cache;
return cache;
}
// MATCH
CDXUTTimer* DXUTGetGlobalTimer()
{
// Using an accessor function gives control of the construction order
static CDXUTTimer timer;
return &timer;
}
//--------------------------------------------------------------------------------------
// Internal functions forward declarations
//--------------------------------------------------------------------------------------
bool DXUTFindMediaSearchTypicalDirs( TCHAR* strSearchPath, int cchSearch, LPCTSTR strLeaf, TCHAR* strExePath, TCHAR* strExeName );
bool DXUTFindMediaSearchParentDirs( TCHAR* strSearchPath, int cchSearch, TCHAR* strStartAt, TCHAR* strLeafName );
INT_PTR CALLBACK DisplaySwitchToREFWarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
//--------------------------------------------------------------------------------------
// Shared code for samples to ask user if they want to use a REF device or quit
//--------------------------------------------------------------------------------------
void DXUTDisplaySwitchingToREFWarning()
{
if( DXUTGetShowMsgBoxOnError() )
{
// Open the appropriate registry key
DWORD dwSkipWarning = 0;
HKEY hKey;
LONG lResult = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\DirectX 9.0 SDK", 0, KEY_READ, &hKey );
if( ERROR_SUCCESS == lResult )
{
DWORD dwType;
DWORD dwSize = sizeof(DWORD);
lResult = RegQueryValueEx( hKey, "Skip Warning On REF", NULL, &dwType, (BYTE*)&dwSkipWarning, &dwSize );
RegCloseKey( hKey );
}
if( dwSkipWarning == 0 )
{
// Compact code to create a custom dialog box without using a template in a resource file.
// If this dialog were in a .rc file, this would be a lot simpler but every sample calling this function would
// need a copy of the dialog in its own .rc file. Also MessageBox API could be used here instead, but
// the MessageBox API is simpler to call but it can't provide a "Don't show again" checkbox
typedef struct { DLGITEMTEMPLATE a; WORD b; WORD c; WORD d; WORD e; WORD f; } DXUT_DLG_ITEM;
typedef struct { DLGTEMPLATE a; WORD b; WORD c; TCHAR d[2]; WORD e; TCHAR f[14]; DXUT_DLG_ITEM i1; DXUT_DLG_ITEM i2; DXUT_DLG_ITEM i3; DXUT_DLG_ITEM i4; DXUT_DLG_ITEM i5; } DXUT_DLG_DATA;
DXUT_DLG_DATA dtp =
{
{WS_CAPTION|WS_POPUP|WS_VISIBLE|WS_SYSMENU|DS_ABSALIGN|DS_3DLOOK|DS_SETFONT|DS_MODALFRAME|DS_CENTER,0,5,0,0,269,82},0,0," ",8,"MS Sans Serif",
{{WS_CHILD|WS_VISIBLE|SS_ICON|SS_CENTERIMAGE,0,7,7,24,24,0x100},0xFFFF,0x0082,0,0,0}, // icon
{{WS_CHILD|WS_VISIBLE,0,40,7,230,25,0x101},0xFFFF,0x0082,0,0,0}, // static text
{{WS_CHILD|WS_VISIBLE|BS_DEFPUSHBUTTON,0,80,39,50,14,IDYES},0xFFFF,0x0080,0,0,0}, // Yes button
{{WS_CHILD|WS_VISIBLE,0,133,39,50,14,IDNO},0xFFFF,0x0080,0,0,0}, // No button
{{WS_CHILD|WS_VISIBLE|BS_CHECKBOX,0,7,59,70,16,IDIGNORE},0xFFFF,0x0080,0,0,0}, // checkbox
};
int nResult = (int) DialogBoxIndirect( DXUTGetHINSTANCE(), (DLGTEMPLATE*)&dtp, DXUTGetHWND(), DisplaySwitchToREFWarningProc );
if( (nResult & 0x80) == 0x80 ) // "Don't show again" checkbox was checked
{
lResult = RegOpenKeyEx( HKEY_CURRENT_USER, "Software\\Microsoft\\DirectX 9.0 SDK", 0, KEY_WRITE, &hKey );
if( ERROR_SUCCESS == lResult )
{
dwSkipWarning = 1;
RegSetValueEx( hKey, "Skip Warning On REF", 0, REG_DWORD, (BYTE*)&dwSkipWarning, sizeof(DWORD) );
RegCloseKey( hKey );
}
}
// User choose not to continue
if( (nResult & 0x0F) == IDNO )
DXUTShutdown(1);
}
}
}
//--------------------------------------------------------------------------------------
// MsgProc for DXUTDisplaySwitchingToREFWarning() dialog box
//--------------------------------------------------------------------------------------
INT_PTR CALLBACK DisplaySwitchToREFWarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
// Easier to set text here than in the DLGITEMTEMPLATE
SetWindowText( hDlg, DXUTGetWindowTitle() );
SendMessage( GetDlgItem(hDlg, 0x100), STM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIcon(0, IDI_QUESTION));
SetDlgItemText( hDlg, 0x101, "Switching to the Direct3D reference rasterizer, a software device\nthat implements the entire Direct3D feature set, but runs very slowly.\nDo you wish to continue?" );
SetDlgItemText( hDlg, IDYES, "&Yes" );
SetDlgItemText( hDlg, IDNO, "&No" );
SetDlgItemText( hDlg, IDIGNORE, "&Don't show again" );
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDIGNORE: CheckDlgButton( hDlg, IDIGNORE, (IsDlgButtonChecked( hDlg, IDIGNORE ) == BST_CHECKED) ? BST_UNCHECKED : BST_CHECKED ); EnableWindow( GetDlgItem( hDlg, IDNO ), (IsDlgButtonChecked( hDlg, IDIGNORE ) != BST_CHECKED) ); break;
case IDNO: EndDialog(hDlg, (IsDlgButtonChecked( hDlg, IDIGNORE ) == BST_CHECKED) ? IDNO|0x80 : IDNO|0x00 ); return TRUE;
case IDCANCEL:
case IDYES: EndDialog(hDlg, (IsDlgButtonChecked( hDlg, IDIGNORE ) == BST_CHECKED) ? IDYES|0x80 : IDYES|0x00 ); return TRUE;
}
break;
}
return FALSE;
}
//--------------------------------------------------------------------------------------
// MATCH
CDXUTTimer::CDXUTTimer()
{
m_bUsingQPF = false;
m_bTimerStopped = true;
m_llQPFTicksPerSec = 0;
m_llStopTime = 0;
m_llLastElapsedTime = 0;
m_llBaseTime = 0;
// Use QueryPerformanceFrequency() to get frequency of timer.
LARGE_INTEGER qwTicksPerSec;
m_bUsingQPF = (bool) (QueryPerformanceFrequency( &qwTicksPerSec ) != 0);
m_llQPFTicksPerSec = qwTicksPerSec.QuadPart;
}
//--------------------------------------------------------------------------------------
// MATCH
void CDXUTTimer::Reset()
{
if( !m_bUsingQPF )
return;
// Get either the current time or the stop time
LARGE_INTEGER qwTime;
if( m_llStopTime != 0 )
qwTime.QuadPart = m_llStopTime;
else
QueryPerformanceCounter( &qwTime );
m_llBaseTime = qwTime.QuadPart;
m_llLastElapsedTime = qwTime.QuadPart;
m_llStopTime = 0;
m_bTimerStopped = FALSE;
}
//--------------------------------------------------------------------------------------
// MATCH
void CDXUTTimer::Start()
{
if( !m_bUsingQPF )
return;
// Get the current time
LARGE_INTEGER qwTime;
QueryPerformanceCounter( &qwTime );
if( m_bTimerStopped )
m_llBaseTime += qwTime.QuadPart - m_llStopTime;
m_llStopTime = 0;
m_llLastElapsedTime = qwTime.QuadPart;
m_bTimerStopped = FALSE;
}
//--------------------------------------------------------------------------------------
// MATCH
void CDXUTTimer::Stop()
{
if( !m_bUsingQPF )
return;
if( !m_bTimerStopped )
{
// Get either the current time or the stop time
LARGE_INTEGER qwTime;
if( m_llStopTime != 0 )
qwTime.QuadPart = m_llStopTime;
else
QueryPerformanceCounter( &qwTime );
m_llStopTime = qwTime.QuadPart;
m_llLastElapsedTime = qwTime.QuadPart;
m_bTimerStopped = TRUE;
}
}
//--------------------------------------------------------------------------------------
// MATCH
void CDXUTTimer::Advance()
{
if( !m_bUsingQPF )
return;
m_llStopTime += m_llQPFTicksPerSec/10;
}
//--------------------------------------------------------------------------------------
// MATCH
double CDXUTTimer::GetAbsoluteTime()
{
if( !m_bUsingQPF )
return -1.0;
// Get either the current time or the stop time
LARGE_INTEGER qwTime;
if( m_llStopTime != 0 )
qwTime.QuadPart = m_llStopTime;
else
QueryPerformanceCounter( &qwTime );
double fTime = qwTime.QuadPart / (double) m_llQPFTicksPerSec;
return fTime;
}
//--------------------------------------------------------------------------------------
// MATCH
double CDXUTTimer::GetTime()
{
if( !m_bUsingQPF )
return -1.0;
// Get either the current time or the stop time
LARGE_INTEGER qwTime;
if( m_llStopTime != 0 )
qwTime.QuadPart = m_llStopTime;
else
QueryPerformanceCounter( &qwTime );
double fAppTime = (double) ( qwTime.QuadPart - m_llBaseTime ) / (double) m_llQPFTicksPerSec;
return fAppTime;
}
//--------------------------------------------------------------------------------------
// MATCH
double CDXUTTimer::GetElapsedTime()
{
if( !m_bUsingQPF )
return -1.0;
// Get either the current time or the stop time
LARGE_INTEGER qwTime;
if( m_llStopTime != 0 )
qwTime.QuadPart = m_llStopTime;
else
QueryPerformanceCounter( &qwTime );
double fElapsedTime = (double) ( qwTime.QuadPart - m_llLastElapsedTime ) / (double) m_llQPFTicksPerSec;
m_llLastElapsedTime = qwTime.QuadPart;
return fElapsedTime;
}
//--------------------------------------------------------------------------------------
// MATCH
bool CDXUTTimer::IsStopped()
{
return m_bTimerStopped;
}
//--------------------------------------------------------------------------------------
// Returns pointer to static media search buffer
//--------------------------------------------------------------------------------------
// MATCH
TCHAR* DXUTMediaSearchPath()
{
static TCHAR s_strMediaSearchPath[MAX_PATH] = {0};
return s_strMediaSearchPath;
}
//--------------------------------------------------------------------------------------
// MATCH
LPCTSTR DXUTGetMediaSearchPath()
{
return DXUTMediaSearchPath();
}
//--------------------------------------------------------------------------------------
// MATCH
HRESULT DXUTSetMediaSearchPath( LPCTSTR strPath )
{
HRESULT hr;
TCHAR* s_strSearchPath = DXUTMediaSearchPath();
hr = StringCchCopy( s_strSearchPath, MAX_PATH, strPath );
if( SUCCEEDED(hr) )
{
// append slash if needed
size_t ch;
hr = StringCchLength( s_strSearchPath, MAX_PATH, &ch );
if( SUCCEEDED(hr) && s_strSearchPath[ch-1] != L'\\')
{
hr = StringCchCat( s_strSearchPath, MAX_PATH, "\\" );
}
}
return hr;
}
//--------------------------------------------------------------------------------------
// Tries to find the location of a SDK media file
// cchDest is the size in TCHARs of strDestPath. Be careful not to
// pass in sizeof(strDest) on UNICODE builds.
//--------------------------------------------------------------------------------------
HRESULT DXUTFindDXSDKMediaFileCch( TCHAR* strDestPath, int cchDest, LPCTSTR strFilename )
{
bool bFound;
TCHAR strSearchFor[MAX_PATH];
if( NULL==strFilename || strFilename[0] == 0 || NULL==strDestPath || cchDest < 10 )
return E_INVALIDARG;
// Get the exe name, and exe path
TCHAR strExePath[MAX_PATH] = {0};
TCHAR strExeName[MAX_PATH] = {0};
TCHAR* strLastSlash = NULL;
GetModuleFileName( NULL, strExePath, MAX_PATH );
strExePath[MAX_PATH-1]=0;
strLastSlash = strrchr( strExePath, TEXT('\\') );
if( strLastSlash )
{
StringCchCopy( strExeName, MAX_PATH, &strLastSlash[1] );
// Chop the exe name from the exe path
*strLastSlash = 0;
// Chop the .exe from the exe name
strLastSlash = strrchr( strExeName, TEXT('.') );
if( strLastSlash )
*strLastSlash = 0;
}
// Typical directories:
// .\
// ..\
// ..\..\
// %EXE_DIR%\
// %EXE_DIR%\..\
// %EXE_DIR%\..\..\
// %EXE_DIR%\..\%EXE_NAME%
// %EXE_DIR%\..\..\%EXE_NAME%
// Typical directory search
bFound = DXUTFindMediaSearchTypicalDirs( strDestPath, cchDest, strFilename, strExePath, strExeName );
if( bFound )
return S_OK;
// Typical directory search again, but also look in a subdir called "\media\"
StringCchPrintf( strSearchFor, MAX_PATH, "media\\%s", strFilename );
bFound = DXUTFindMediaSearchTypicalDirs( strDestPath, cchDest, strSearchFor, strExePath, strExeName );
if( bFound )
return S_OK;
TCHAR strLeafName[MAX_PATH] = {0};
// Search all parent directories starting at .\ and using strFilename as the leaf name
StringCchCopy( strLeafName, MAX_PATH, strFilename );
bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, ".", strLeafName );
if( bFound )
return S_OK;
// Search all parent directories starting at the exe's dir and using strFilename as the leaf name
bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, strExePath, strLeafName );
if( bFound )
return S_OK;
// Search all parent directories starting at .\ and using "media\strFilename" as the leaf name
StringCchPrintf( strLeafName, MAX_PATH, "media\\%s", strFilename );
bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, ".", strLeafName );
if( bFound )
return S_OK;
// Search all parent directories starting at the exe's dir and using "media\strFilename" as the leaf name
bFound = DXUTFindMediaSearchParentDirs( strDestPath, cchDest, strExePath, strLeafName );
if( bFound )
return S_OK;
// On failure, return the file as the path but also return an error code
StringCchCopy( strDestPath, cchDest, strFilename );
return DXUTERR_MEDIANOTFOUND;
}
//--------------------------------------------------------------------------------------
// Search a set of typical directories
//--------------------------------------------------------------------------------------
// MATCH
bool DXUTFindMediaSearchTypicalDirs( TCHAR* strSearchPath, int cchSearch, LPCTSTR strLeaf,
TCHAR* strExePath, TCHAR* strExeName )
{
// Typical directories:
// .\
// ..\
// ..\..\
// %EXE_DIR%\
// %EXE_DIR%\..\
// %EXE_DIR%\..\..\
// %EXE_DIR%\..\%EXE_NAME%
// %EXE_DIR%\..\..\%EXE_NAME%
// DXSDK media path
// Search in .\
StringCchCopy( strSearchPath, cchSearch, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in ..\
StringCchPrintf( strSearchPath, cchSearch, "..\\%s", strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in ..\..\
StringCchPrintf( strSearchPath, cchSearch, "..\\..\\%s", strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in ..\..\
StringCchPrintf( strSearchPath, cchSearch, "..\\..\\%s", strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in the %EXE_DIR%\
StringCchPrintf( strSearchPath, cchSearch, "%s\\%s", strExePath, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in the %EXE_DIR%\..\
StringCchPrintf( strSearchPath, cchSearch, "%s\\..\\%s", strExePath, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in the %EXE_DIR%\..\..\
StringCchPrintf( strSearchPath, cchSearch, "%s\\..\\..\\%s", strExePath, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in "%EXE_DIR%\..\%EXE_NAME%\". This matches the DirectX SDK layout
StringCchPrintf( strSearchPath, cchSearch, "%s\\..\\%s\\%s", strExePath, strExeName, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in "%EXE_DIR%\..\..\%EXE_NAME%\". This matches the DirectX SDK layout
StringCchPrintf( strSearchPath, cchSearch, "%s\\..\\..\\%s\\%s", strExePath, strExeName, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
// Search in media search dir
TCHAR* s_strSearchPath = DXUTMediaSearchPath();
if( s_strSearchPath[0] != 0 )
{
StringCchPrintf( strSearchPath, cchSearch, "%s%s", s_strSearchPath, strLeaf );
if( GetFileAttributes( strSearchPath ) != 0xFFFFFFFF )
return true;
}
return false;
}
//--------------------------------------------------------------------------------------
// Search parent directories starting at strStartAt, and appending strLeafName
// at each parent directory. It stops at the root directory.
//--------------------------------------------------------------------------------------
// MATCH
bool DXUTFindMediaSearchParentDirs( TCHAR* strSearchPath, int cchSearch, TCHAR* strStartAt, TCHAR* strLeafName )
{
TCHAR strFullPath[MAX_PATH] = {0};
TCHAR strFullFileName[MAX_PATH] = {0};
TCHAR strSearch[MAX_PATH] = {0};
TCHAR* strFilePart = NULL;
GetFullPathName( strStartAt, MAX_PATH, strFullPath, &strFilePart );
if( strFilePart == NULL )
return false;
while( strFilePart != NULL && *strFilePart != '\0' )
{
StringCchPrintf( strFullFileName, MAX_PATH, "%s\\%s", strFullPath, strLeafName );
if( GetFileAttributes( strFullFileName ) != 0xFFFFFFFF )
{
StringCchCopy( strSearchPath, cchSearch, strFullFileName );
return true;
}
StringCchPrintf( strSearch, MAX_PATH, "%s\\..", strFullPath );
GetFullPathName( strSearch, MAX_PATH, strFullPath, &strFilePart );
}
return false;
}
//--------------------------------------------------------------------------------------
// CDXUTResourceCache
//--------------------------------------------------------------------------------------
//MATCH
CDXUTResourceCache::~CDXUTResourceCache()
{
OnDestroyDevice();
m_TextureCache.RemoveAll();
m_EffectCache.RemoveAll();
m_FontCache.RemoveAll();
}
HRESULT CDXUTResourceCache::CreateTextureFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, LPDIRECT3DTEXTURE9 *ppTexture )
{
return CreateTextureFromFileEx( pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,
0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT,
0, NULL, NULL, ppTexture );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateTextureFromFileEx( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, UINT Width, UINT Height, UINT MipLevels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, DWORD Filter, DWORD MipFilter, D3DCOLOR ColorKey, D3DXIMAGE_INFO *pSrcInfo, PALETTEENTRY *pPalette, LPDIRECT3DTEXTURE9 *ppTexture )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_TextureCache.GetSize(); ++i )
{
DXUTCache_Texture &Entry = m_TextureCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_FILE &&
!lstrcmpA( Entry.wszSource, pSrcFile ) &&
Entry.Width == Width &&
Entry.Height == Height &&
Entry.MipLevels == MipLevels &&
Entry.Usage == Usage &&
Entry.Format == Format &&
Entry.Pool == Pool &&
Entry.Type == D3DRTYPE_TEXTURE )
{
// A match is found. Obtain the IDirect3DTexture9 interface and return that.
return Entry.pTexture->QueryInterface( IID_IDirect3DTexture9, (LPVOID*)ppTexture );
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateTextureFromFileEx( pDevice, pSrcFile, Width, Height, MipLevels, Usage, Format,
Pool, Filter, MipFilter, ColorKey, pSrcInfo, pPalette, ppTexture );
if( FAILED( hr ) )
return hr;
DXUTCache_Texture NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_FILE;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcFile );
NewEntry.Width = Width;
NewEntry.Height = Height;
NewEntry.MipLevels = MipLevels;
NewEntry.Usage = Usage;
NewEntry.Format = Format;
NewEntry.Pool = Pool;
NewEntry.Type = D3DRTYPE_TEXTURE;
(*ppTexture)->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&NewEntry.pTexture );
m_TextureCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateTextureFromResource( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, LPDIRECT3DTEXTURE9 *ppTexture )
{
return CreateTextureFromResourceEx( pDevice, hSrcModule, pSrcResource, D3DX_DEFAULT, D3DX_DEFAULT,
D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT,
D3DX_DEFAULT, 0, NULL, NULL, ppTexture );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateTextureFromResourceEx( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, UINT Width, UINT Height, UINT MipLevels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, DWORD Filter, DWORD MipFilter, D3DCOLOR ColorKey, D3DXIMAGE_INFO *pSrcInfo, PALETTEENTRY *pPalette, LPDIRECT3DTEXTURE9 *ppTexture )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_TextureCache.GetSize(); ++i )
{
DXUTCache_Texture &Entry = m_TextureCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_RESOURCE &&
Entry.hSrcModule == hSrcModule &&
!lstrcmpA( Entry.wszSource, pSrcResource ) &&
Entry.Width == Width &&
Entry.Height == Height &&
Entry.MipLevels == MipLevels &&
Entry.Usage == Usage &&
Entry.Format == Format &&
Entry.Pool == Pool &&
Entry.Type == D3DRTYPE_TEXTURE )
{
// A match is found. Obtain the IDirect3DTexture9 interface and return that.
return Entry.pTexture->QueryInterface( IID_IDirect3DTexture9, (LPVOID*)ppTexture );
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateTextureFromResourceEx( pDevice, hSrcModule, pSrcResource, Width, Height, MipLevels, Usage,
Format, Pool, Filter, MipFilter, ColorKey, pSrcInfo, pPalette, ppTexture );
if( FAILED( hr ) )
return hr;
DXUTCache_Texture NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_RESOURCE;
NewEntry.hSrcModule = hSrcModule;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcResource );
NewEntry.Width = Width;
NewEntry.Height = Height;
NewEntry.MipLevels = MipLevels;
NewEntry.Usage = Usage;
NewEntry.Format = Format;
NewEntry.Pool = Pool;
NewEntry.Type = D3DRTYPE_TEXTURE;
(*ppTexture)->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&NewEntry.pTexture );
m_TextureCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateCubeTextureFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, LPDIRECT3DCUBETEXTURE9 *ppCubeTexture )
{
return CreateCubeTextureFromFileEx( pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, 0,
D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT,
0, NULL, NULL, ppCubeTexture );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateCubeTextureFromFileEx( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, UINT Size, UINT MipLevels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, DWORD Filter, DWORD MipFilter, D3DCOLOR ColorKey, D3DXIMAGE_INFO *pSrcInfo, PALETTEENTRY *pPalette, LPDIRECT3DCUBETEXTURE9 *ppCubeTexture )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_TextureCache.GetSize(); ++i )
{
DXUTCache_Texture &Entry = m_TextureCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_FILE &&
!lstrcmpA( Entry.wszSource, pSrcFile ) &&
Entry.Width == Size &&
Entry.MipLevels == MipLevels &&
Entry.Usage == Usage &&
Entry.Format == Format &&
Entry.Pool == Pool &&
Entry.Type == D3DRTYPE_CUBETEXTURE )
{
// A match is found. Obtain the IDirect3DCubeTexture9 interface and return that.
return Entry.pTexture->QueryInterface( IID_IDirect3DCubeTexture9, (LPVOID*)ppCubeTexture );
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateCubeTextureFromFileEx( pDevice, pSrcFile, Size, MipLevels, Usage, Format, Pool, Filter,
MipFilter, ColorKey, pSrcInfo, pPalette, ppCubeTexture );
if( FAILED( hr ) )
return hr;
DXUTCache_Texture NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_FILE;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcFile );
NewEntry.Width = Size;
NewEntry.MipLevels = MipLevels;
NewEntry.Usage = Usage;
NewEntry.Format = Format;
NewEntry.Pool = Pool;
NewEntry.Type = D3DRTYPE_CUBETEXTURE;
(*ppCubeTexture)->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&NewEntry.pTexture );
m_TextureCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateCubeTextureFromResource( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, LPDIRECT3DCUBETEXTURE9 *ppCubeTexture )
{
return CreateCubeTextureFromResourceEx( pDevice, hSrcModule, pSrcResource, D3DX_DEFAULT, D3DX_DEFAULT,
0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT,
0, NULL, NULL, ppCubeTexture );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateCubeTextureFromResourceEx( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, UINT Size, UINT MipLevels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, DWORD Filter, DWORD MipFilter, D3DCOLOR ColorKey, D3DXIMAGE_INFO *pSrcInfo, PALETTEENTRY *pPalette, LPDIRECT3DCUBETEXTURE9 *ppCubeTexture )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_TextureCache.GetSize(); ++i )
{
DXUTCache_Texture &Entry = m_TextureCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_RESOURCE &&
Entry.hSrcModule == hSrcModule &&
!lstrcmpA( Entry.wszSource, pSrcResource ) &&
Entry.Width == Size &&
Entry.MipLevels == MipLevels &&
Entry.Usage == Usage &&
Entry.Format == Format &&
Entry.Pool == Pool &&
Entry.Type == D3DRTYPE_CUBETEXTURE )
{
// A match is found. Obtain the IDirect3DCubeTexture9 interface and return that.
return Entry.pTexture->QueryInterface( IID_IDirect3DCubeTexture9, (LPVOID*)ppCubeTexture );
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateCubeTextureFromResourceEx( pDevice, hSrcModule, pSrcResource, Size, MipLevels, Usage, Format,
Pool, Filter, MipFilter, ColorKey, pSrcInfo, pPalette, ppCubeTexture );
if( FAILED( hr ) )
return hr;
DXUTCache_Texture NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_RESOURCE;
NewEntry.hSrcModule = hSrcModule;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcResource );
NewEntry.Width = Size;
NewEntry.MipLevels = MipLevels;
NewEntry.Usage = Usage;
NewEntry.Format = Format;
NewEntry.Pool = Pool;
NewEntry.Type = D3DRTYPE_CUBETEXTURE;
(*ppCubeTexture)->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&NewEntry.pTexture );
m_TextureCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateVolumeTextureFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, LPDIRECT3DVOLUMETEXTURE9 *ppVolumeTexture )
{
return CreateVolumeTextureFromFileEx( pDevice, pSrcFile, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT,
0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_DEFAULT, D3DX_DEFAULT,
0, NULL, NULL, ppVolumeTexture );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateVolumeTextureFromFileEx( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, UINT Width, UINT Height, UINT Depth, UINT MipLevels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, DWORD Filter, DWORD MipFilter, D3DCOLOR ColorKey, D3DXIMAGE_INFO *pSrcInfo, PALETTEENTRY *pPalette, LPDIRECT3DVOLUMETEXTURE9 *ppTexture )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_TextureCache.GetSize(); ++i )
{
DXUTCache_Texture &Entry = m_TextureCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_FILE &&
!lstrcmpA( Entry.wszSource, pSrcFile ) &&
Entry.Width == Width &&
Entry.Height == Height &&
Entry.Depth == Depth &&
Entry.MipLevels == MipLevels &&
Entry.Usage == Usage &&
Entry.Format == Format &&
Entry.Pool == Pool &&
Entry.Type == D3DRTYPE_VOLUMETEXTURE )
{
// A match is found. Obtain the IDirect3DVolumeTexture9 interface and return that.
return Entry.pTexture->QueryInterface( IID_IDirect3DVolumeTexture9, (LPVOID*)ppTexture );
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateVolumeTextureFromFileEx( pDevice, pSrcFile, Width, Height, Depth, MipLevels, Usage, Format,
Pool, Filter, MipFilter, ColorKey, pSrcInfo, pPalette, ppTexture );
if( FAILED( hr ) )
return hr;
DXUTCache_Texture NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_FILE;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcFile );
NewEntry.Width = Width;
NewEntry.Height = Height;
NewEntry.Depth = Depth;
NewEntry.MipLevels = MipLevels;
NewEntry.Usage = Usage;
NewEntry.Format = Format;
NewEntry.Pool = Pool;
NewEntry.Type = D3DRTYPE_VOLUMETEXTURE;
(*ppTexture)->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&NewEntry.pTexture );
m_TextureCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateVolumeTextureFromResource( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, LPDIRECT3DVOLUMETEXTURE9 *ppVolumeTexture )
{
return CreateVolumeTextureFromResourceEx( pDevice, hSrcModule, pSrcResource, D3DX_DEFAULT, D3DX_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,
D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, ppVolumeTexture );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateVolumeTextureFromResourceEx( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, UINT Width, UINT Height, UINT Depth, UINT MipLevels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, DWORD Filter, DWORD MipFilter, D3DCOLOR ColorKey, D3DXIMAGE_INFO *pSrcInfo, PALETTEENTRY *pPalette, LPDIRECT3DVOLUMETEXTURE9 *ppVolumeTexture )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_TextureCache.GetSize(); ++i )
{
DXUTCache_Texture &Entry = m_TextureCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_RESOURCE &&
Entry.hSrcModule == hSrcModule &&
!lstrcmpA( Entry.wszSource, pSrcResource ) &&
Entry.Width == Width &&
Entry.Height == Height &&
Entry.Depth == Depth &&
Entry.MipLevels == MipLevels &&
Entry.Usage == Usage &&
Entry.Format == Format &&
Entry.Pool == Pool &&
Entry.Type == D3DRTYPE_VOLUMETEXTURE )
{
// A match is found. Obtain the IDirect3DVolumeTexture9 interface and return that.
return Entry.pTexture->QueryInterface( IID_IDirect3DVolumeTexture9, (LPVOID*)ppVolumeTexture );
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateVolumeTextureFromResourceEx( pDevice, hSrcModule, pSrcResource, Width, Height, Depth, MipLevels, Usage,
Format, Pool, Filter, MipFilter, ColorKey, pSrcInfo, pPalette, ppVolumeTexture );
if( FAILED( hr ) )
return hr;
DXUTCache_Texture NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_RESOURCE;
NewEntry.hSrcModule = hSrcModule;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcResource );
NewEntry.Width = Width;
NewEntry.Height = Height;
NewEntry.Depth = Depth;
NewEntry.MipLevels = MipLevels;
NewEntry.Usage = Usage;
NewEntry.Format = Format;
NewEntry.Pool = Pool;
NewEntry.Type = D3DRTYPE_VOLUMETEXTURE;
(*ppVolumeTexture)->QueryInterface( IID_IDirect3DBaseTexture9, (LPVOID*)&NewEntry.pTexture );
m_TextureCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateFont( LPDIRECT3DDEVICE9 pDevice, UINT Height, UINT Width, UINT Weight, UINT MipLevels, BOOL Italic, DWORD CharSet, DWORD OutputPrecision, DWORD Quality, DWORD PitchAndFamily, LPCTSTR pFacename, LPD3DXFONT *ppFont )
{
D3DXFONT_DESC Desc;
Desc.Height = Height;
Desc.Width = Width;
Desc.Weight = Weight;
Desc.MipLevels = MipLevels;
Desc.Italic = Italic;
Desc.CharSet = (BYTE)CharSet;
Desc.OutputPrecision = (BYTE)OutputPrecision;
Desc.Quality = (BYTE)Quality;
Desc.PitchAndFamily = (BYTE)PitchAndFamily;
StringCchCopy( Desc.FaceName, LF_FACESIZE, pFacename );
return CreateFontIndirect( pDevice, &Desc, ppFont );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateFontIndirect( LPDIRECT3DDEVICE9 pDevice, CONST D3DXFONT_DESC *pDesc, LPD3DXFONT *ppFont )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_FontCache.GetSize(); ++i )
{
DXUTCache_Font &Entry = m_FontCache[i];
if( Entry.Width == pDesc->Width &&
Entry.Height == pDesc->Height &&
Entry.Weight == pDesc->Weight &&
Entry.MipLevels == pDesc->MipLevels &&
Entry.Italic == pDesc->Italic &&
Entry.CharSet == pDesc->CharSet &&
Entry.OutputPrecision == pDesc->OutputPrecision &&
Entry.Quality == pDesc->Quality &&
Entry.PitchAndFamily == pDesc->PitchAndFamily &&
CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE,
Entry.FaceName, -1,
pDesc->FaceName, -1 ) == CSTR_EQUAL )
{
// A match is found. Increment the reference and return the ID3DXFont object.
Entry.pFont->AddRef();
*ppFont = Entry.pFont;
return S_OK;
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateFontIndirect( pDevice, pDesc, ppFont );
if( FAILED( hr ) )
return hr;
DXUTCache_Font NewEntry;
(D3DXFONT_DESC &)NewEntry = *pDesc;
NewEntry.pFont = *ppFont;
NewEntry.pFont->AddRef();
m_FontCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateEffectFromFile( LPDIRECT3DDEVICE9 pDevice, LPCTSTR pSrcFile, const D3DXMACRO *pDefines, LPD3DXINCLUDE pInclude, DWORD Flags, LPD3DXEFFECTPOOL pPool, LPD3DXEFFECT *ppEffect, LPD3DXBUFFER *ppCompilationErrors )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_EffectCache.GetSize(); ++i )
{
DXUTCache_Effect &Entry = m_EffectCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_FILE &&
!lstrcmpA( Entry.wszSource, pSrcFile ) &&
Entry.dwFlags == Flags )
{
// A match is found. Increment the ref coutn and return the ID3DXEffect object.
*ppEffect = Entry.pEffect;
(*ppEffect)->AddRef();
return S_OK;
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateEffectFromFile( pDevice, pSrcFile, pDefines, pInclude, Flags, pPool, ppEffect, ppCompilationErrors );
if( FAILED( hr ) )
return hr;
DXUTCache_Effect NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_FILE;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcFile );
NewEntry.dwFlags = Flags;
NewEntry.pEffect = *ppEffect;
NewEntry.pEffect->AddRef();
m_EffectCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::CreateEffectFromResource( LPDIRECT3DDEVICE9 pDevice, HMODULE hSrcModule, LPCTSTR pSrcResource, const D3DXMACRO *pDefines, LPD3DXINCLUDE pInclude, DWORD Flags, LPD3DXEFFECTPOOL pPool, LPD3DXEFFECT *ppEffect, LPD3DXBUFFER *ppCompilationErrors )
{
// Search the cache for a matching entry.
for( int i = 0; i < m_EffectCache.GetSize(); ++i )
{
DXUTCache_Effect &Entry = m_EffectCache[i];
if( Entry.Location == DXUTCACHE_LOCATION_RESOURCE &&
Entry.hSrcModule == hSrcModule &&
!lstrcmpA( Entry.wszSource, pSrcResource ) &&
Entry.dwFlags == Flags )
{
// A match is found. Increment the ref coutn and return the ID3DXEffect object.
*ppEffect = Entry.pEffect;
(*ppEffect)->AddRef();
return S_OK;
}
}
HRESULT hr;
// No matching entry. Load the resource and create a new entry.
hr = D3DXCreateEffectFromResource( pDevice, hSrcModule, pSrcResource, pDefines, pInclude, Flags,
pPool, ppEffect, ppCompilationErrors );
if( FAILED( hr ) )
return hr;
DXUTCache_Effect NewEntry;
NewEntry.Location = DXUTCACHE_LOCATION_RESOURCE;
NewEntry.hSrcModule = hSrcModule;
StringCchCopy( NewEntry.wszSource, MAX_PATH, pSrcResource );
NewEntry.dwFlags = Flags;
NewEntry.pEffect = *ppEffect;
NewEntry.pEffect->AddRef();
m_EffectCache.Add( NewEntry );
return S_OK;
}
//--------------------------------------------------------------------------------------
// Device event callbacks
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
HRESULT CDXUTResourceCache::OnCreateDevice( IDirect3DDevice9 *pd3dDevice )
{
return S_OK;
}
//--------------------------------------------------------------------------------------
// MATCH
HRESULT CDXUTResourceCache::OnResetDevice( IDirect3DDevice9 *pd3dDevice )
{
// Call OnResetDevice on all effect and font objects
for( int i = 0; i < m_EffectCache.GetSize(); ++i )
m_EffectCache[i].pEffect->OnResetDevice();
for( int i = 0; i < m_FontCache.GetSize(); ++i )
m_FontCache[i].pFont->OnResetDevice();
return S_OK;
}
//--------------------------------------------------------------------------------------
// MATCH
HRESULT CDXUTResourceCache::OnLostDevice()
{
// Call OnLostDevice on all effect and font objects
for( int i = 0; i < m_EffectCache.GetSize(); ++i )
m_EffectCache[i].pEffect->OnLostDevice();
for( int i = 0; i < m_FontCache.GetSize(); ++i )
m_FontCache[i].pFont->OnLostDevice();
// Release all the default pool textures
for( int i = m_TextureCache.GetSize() - 1; i >= 0; --i )
if( m_TextureCache[i].Pool == D3DPOOL_DEFAULT )
{
SAFE_RELEASE( m_TextureCache[i].pTexture );
m_TextureCache.Remove( i ); // Remove the entry
}
return S_OK;
}
//--------------------------------------------------------------------------------------
// MATCH
HRESULT CDXUTResourceCache::OnDestroyDevice()
{
// Release all resources
for( int i = m_EffectCache.GetSize() - 1; i >= 0; --i )
{
SAFE_RELEASE( m_EffectCache[i].pEffect );
m_EffectCache.Remove( i );
}
for( int i = m_FontCache.GetSize() - 1; i >= 0; --i )
{
SAFE_RELEASE( m_FontCache[i].pFont );
m_FontCache.Remove( i );
}
for( int i = m_TextureCache.GetSize() - 1; i >= 0; --i )
{
SAFE_RELEASE( m_TextureCache[i].pTexture );
m_TextureCache.Remove( i );
}
return S_OK;
}
//--------------------------------------------------------------------------------------
CD3DArcBall::CD3DArcBall()
{
Reset();
m_vDownPt = D3DXVECTOR3(0,0,0);
m_vCurrentPt = D3DXVECTOR3(0,0,0);
m_Offset.x = m_Offset.y = 0;
RECT rc;
GetClientRect( GetForegroundWindow(), &rc );
SetWindow( rc.right, rc.bottom );
}
//--------------------------------------------------------------------------------------
void CD3DArcBall::Reset()
{
D3DXQuaternionIdentity( &m_qDown );
D3DXQuaternionIdentity( &m_qNow );
D3DXMatrixIdentity( &m_mRotation );
D3DXMatrixIdentity( &m_mTranslation );
D3DXMatrixIdentity( &m_mTranslationDelta );
m_bDrag = FALSE;
m_fRadiusTranslation = 1.0f;
m_fRadius = 1.0f;
}
//--------------------------------------------------------------------------------------
D3DXVECTOR3 CD3DArcBall::ScreenToVector( float fScreenPtX, float fScreenPtY )
{
// Scale to screen
FLOAT x = -(fScreenPtX - m_Offset.x - m_nWidth/2) / (m_fRadius*m_nWidth/2);
FLOAT y = (fScreenPtY - m_Offset.y - m_nHeight/2) / (m_fRadius*m_nHeight/2);
FLOAT z = 0.0f;
FLOAT mag = x*x + y*y;
if( mag > 1.0f )
{
FLOAT scale = 1.0f/sqrtf(mag);
x *= scale;
y *= scale;
}
else
z = sqrtf( 1.0f - mag );
// Return vector
return D3DXVECTOR3( x, y, z );
}
//--------------------------------------------------------------------------------------
D3DXQUATERNION CD3DArcBall::QuatFromBallPoints(const D3DXVECTOR3 &vFrom, const D3DXVECTOR3 &vTo)
{
D3DXVECTOR3 vPart;
float fDot = D3DXVec3Dot(&vFrom, &vTo);
D3DXVec3Cross(&vPart, &vFrom, &vTo);
return D3DXQUATERNION(vPart.x, vPart.y, vPart.z, fDot);
}
//--------------------------------------------------------------------------------------
void CD3DArcBall::OnBegin( int nX, 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;
m_vDownPt = ScreenToVector( (float)nX, (float)nY );
}
}
//--------------------------------------------------------------------------------------
void CD3DArcBall::OnMove( int nX, int nY )
{
if (m_bDrag)
{
m_vCurrentPt = ScreenToVector( (float)nX, (float)nY );
m_qNow = m_qDown * QuatFromBallPoints( m_vDownPt, m_vCurrentPt );
}
}
//--------------------------------------------------------------------------------------
void CD3DArcBall::OnEnd()
{
m_bDrag = false;
}
//--------------------------------------------------------------------------------------
// Desc:
//--------------------------------------------------------------------------------------
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_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;
if( wParam & MK_RBUTTON )
{
D3DXMatrixTranslation( &m_mTranslationDelta, -2*fDeltaX, 2*fDeltaY, 0.0f );
D3DXMatrixMultiply( &m_mTranslation, &m_mTranslation, &m_mTranslationDelta );
}
else // wParam & MK_MBUTTON
{
D3DXMatrixTranslation( &m_mTranslationDelta, 0.0f, 0.0f, 5*fDeltaY );
D3DXMatrixMultiply( &m_mTranslation, &m_mTranslation, &m_mTranslationDelta );
}
// Store mouse coordinate
m_ptLastMouse.x = iMouseX;
m_ptLastMouse.y = iMouseY;
}
return TRUE;
}
return FALSE;
}
//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CBaseCamera::CBaseCamera()
{
ZeroMemory( m_aKeys, sizeof(BYTE)*CAM_MAX_KEYS );
// Set attributes for the view matrix
D3DXVECTOR3 vEyePt = D3DXVECTOR3(0.0f,0.0f,0.0f);
D3DXVECTOR3 vLookatPt = D3DXVECTOR3(0.0f,0.0f,1.0f);
// Setup the view matrix
SetViewParams( &vEyePt, &vLookatPt );
// Setup the projection matrix
SetProjParams( D3DX_PI/4, 1.0f, 1.0f, 1000.0f );
GetCursorPos( &m_ptLastMousePosition );
m_bMouseLButtonDown = false;
m_bMouseMButtonDown = false;
m_bMouseRButtonDown = false;
m_nCurrentButtonMask = 0;
m_nMouseWheelDelta = 0;
m_fCameraYawAngle = 0.0f;
m_fCameraPitchAngle = 0.0f;
SetRect( &m_rcDrag, LONG_MIN, LONG_MIN, LONG_MAX, LONG_MAX );
m_vVelocity = D3DXVECTOR3(0,0,0);
m_bMovementDrag = false;
m_vVelocityDrag = D3DXVECTOR3(0,0,0);
m_fDragTimer = 0.0f;
m_fTotalDragTimeToZero = 0.25;
m_vRotVelocity = D3DXVECTOR2(0,0);
m_fRotationScaler = 0.01f;
m_fMoveScaler = 5.0f;
m_bInvertPitch = false;
m_bEnableYAxisMovement = true;
m_bEnablePositionMovement = true;
m_vMouseDelta = D3DXVECTOR2(0,0);
m_fFramesToSmoothMouseData = 2.0f;
m_bClipToBoundary = false;
m_vMinBoundary = D3DXVECTOR3(-1,-1,-1);
m_vMaxBoundary = D3DXVECTOR3(1,1,1);
m_bResetCursorAfterMove = false;
}
//--------------------------------------------------------------------------------------
// Client can call this to change the position and direction of camera
//--------------------------------------------------------------------------------------
VOID CBaseCamera::SetViewParams( D3DXVECTOR3* pvEyePt, D3DXVECTOR3* pvLookatPt )
{
if( NULL == pvEyePt || NULL == pvLookatPt )
return;
m_vDefaultEye = m_vEye = *pvEyePt;
m_vDefaultLookAt = m_vLookAt = *pvLookatPt;
// Calc the view matrix
D3DXVECTOR3 vUp(0,1,0);
D3DXMatrixLookAtLH( &m_mView, pvEyePt, pvLookatPt, &vUp );
D3DXMATRIX mInvView;
D3DXMatrixInverse( &mInvView, NULL, &m_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
D3DXVECTOR3* pZBasis = (D3DXVECTOR3*) &mInvView._31;
m_fCameraYawAngle = atan2f( pZBasis->x, pZBasis->z );
float fLen = sqrtf(pZBasis->z*pZBasis->z + pZBasis->x*pZBasis->x);
m_fCameraPitchAngle = -atan2f( pZBasis->y, fLen );
}
//--------------------------------------------------------------------------------------
// Calculates the projection matrix based on input params
//--------------------------------------------------------------------------------------
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;
D3DXMatrixPerspectiveFovLH( &m_mProj, fFOV, fAspect, fNearPlane, fFarPlane );
}
//--------------------------------------------------------------------------------------
// Call this from your message proc so this class can handle window messages
//--------------------------------------------------------------------------------------
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 )
{
if( FALSE == IsKeyDown(m_aKeys[mappedKey]) )
m_aKeys[ mappedKey ] = KEY_WAS_DOWN_MASK | KEY_IS_DOWN_MASK;
}
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;
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_MOUSEWHEEL:
// Update member var state
m_nMouseWheelDelta = (short)HIWORD(wParam) / 120;
break;
}
return FALSE;
}
//--------------------------------------------------------------------------------------
// Figure out the mouse delta based on mouse movement
//--------------------------------------------------------------------------------------
void CBaseCamera::UpdateMouseDelta( float fElapsedTime )
{
UNREFERENCED_PARAMETER( fElapsedTime );
POINT ptCurMouseDelta;
POINT ptCurMousePos;
// Get current position of mouse
GetCursorPos( &ptCurMousePos );
// Calc how far it's moved since last frame
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 )
{
// 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;
RECT rcDesktop;
GetWindowRect( GetDesktopWindow(), &rcDesktop );
ptCenter.x = (rcDesktop.right - rcDesktop.left) / 2;
ptCenter.y = (rcDesktop.bottom - rcDesktop.top) / 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 = m_vMouseDelta * m_fRotationScaler;
}
//--------------------------------------------------------------------------------------
// Figure out the velocity based on keyboard input & drag if any
//--------------------------------------------------------------------------------------
void CBaseCamera::UpdateVelocity( float fElapsedTime )
{
D3DXMATRIX mRotDelta;
D3DXVECTOR3 vAccel = D3DXVECTOR3(0,0,0);
if( m_bEnablePositionMovement )
{
// Update acceleration vector based on keyboard state
if( IsKeyDown(m_aKeys[CAM_MOVE_FORWARD]) )
vAccel.z += 1.0f;
if( IsKeyDown(m_aKeys[CAM_MOVE_BACKWARD]) )
vAccel.z -= 1.0f;
if( m_bEnableYAxisMovement )
{
if( IsKeyDown(m_aKeys[CAM_MOVE_UP]) )
vAccel.y += 1.0f;
if( IsKeyDown(m_aKeys[CAM_MOVE_DOWN]) )
vAccel.y -= 1.0f;
}
if( IsKeyDown(m_aKeys[CAM_STRAFE_RIGHT]) )
vAccel.x += 1.0f;
if( IsKeyDown(m_aKeys[CAM_STRAFE_LEFT]) )
vAccel.x -= 1.0f;
}
// Normalize vector so if moving 2 dirs (left & forward),
// the camera doesn't move faster than if moving in 1 dir
D3DXVec3Normalize( &vAccel, &vAccel );
// Scale the acceleration vector
vAccel *= m_fMoveScaler;
if( m_bMovementDrag )
{
// Is there any acceleration this frame?
if( D3DXVec3LengthSq( &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
m_vVelocity = vAccel;
m_fDragTimer = m_fTotalDragTimeToZero;
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
m_vVelocity -= m_vVelocityDrag * fElapsedTime;
m_fDragTimer -= fElapsedTime;
}
else
{
// Zero velocity
m_vVelocity = D3DXVECTOR3(0,0,0);
}
}
}
else
{
// No drag, so immediately change the velocity
m_vVelocity = vAccel;
}
}
//--------------------------------------------------------------------------------------
// Clamps pV to lie inside m_vMinBoundary & m_vMaxBoundary
//--------------------------------------------------------------------------------------
void CBaseCamera::ConstrainToBoundary( D3DXVECTOR3* pV )
{
// Constrain vector to a bounding box
pV->x = __max(pV->x, m_vMinBoundary.x);
pV->y = __max(pV->y, m_vMinBoundary.y);
pV->z = __max(pV->z, m_vMinBoundary.z);
pV->x = __min(pV->x, m_vMaxBoundary.x);
pV->y = __min(pV->y, m_vMaxBoundary.y);
pV->z = __min(pV->z, m_vMaxBoundary.z);
}
//--------------------------------------------------------------------------------------
// Maps a windows virtual key to an enum
//--------------------------------------------------------------------------------------
D3DUtil_CameraKeys CBaseCamera::MapKey( 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()
{
SetViewParams( &m_vDefaultEye, &m_vDefaultLookAt );
}
//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CFirstPersonCamera::CFirstPersonCamera() :
m_nActiveButtonMask( 0x07 )
{
}
//--------------------------------------------------------------------------------------
// Update the view matrix based on user input & elapsed time
//--------------------------------------------------------------------------------------
VOID CFirstPersonCamera::FrameMove( FLOAT fElapsedTime )
{
if( DXUTGetGlobalTimer()->IsStopped() )
fElapsedTime = 1.0f / DXUTGetFPS();
if( IsKeyDown(m_aKeys[CAM_RESET]) )
Reset();
// Get the mouse movement (if any) if the mouse button are down
if( m_nActiveButtonMask & m_nCurrentButtonMask )
UpdateMouseDelta( fElapsedTime );
// Get amount of velocity based on the keyboard input and drag (if any)
UpdateVelocity( fElapsedTime );
// Simple euler method to calculate position delta
D3DXVECTOR3 vPosDelta = m_vVelocity * fElapsedTime;
// If rotating the camera
if( m_nActiveButtonMask & m_nCurrentButtonMask )
{
// 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 = __max( -D3DX_PI/2.0f, m_fCameraPitchAngle );
m_fCameraPitchAngle = __min( +D3DX_PI/2.0f, m_fCameraPitchAngle );
}
// Make a rotation matrix based on the camera's yaw & pitch
D3DXMATRIX mCameraRot;
D3DXMatrixRotationYawPitchRoll( &mCameraRot, m_fCameraYawAngle, m_fCameraPitchAngle, 0 );
// Transform vectors based on camera's rotation matrix
D3DXVECTOR3 vWorldUp, vWorldAhead;
D3DXVECTOR3 vLocalUp = D3DXVECTOR3(0,1,0);
D3DXVECTOR3 vLocalAhead = D3DXVECTOR3(0,0,1);
D3DXVec3TransformCoord( &vWorldUp, &vLocalUp, &mCameraRot );
D3DXVec3TransformCoord( &vWorldAhead, &vLocalAhead, &mCameraRot );
// Transform the position delta by the camera's rotation
D3DXVECTOR3 vPosDeltaWorld;
if( !m_bEnableYAxisMovement )
{
// If restricting Y movement, do not include pitch
// when transforming position delta vector.
D3DXMatrixRotationYawPitchRoll( &mCameraRot, m_fCameraYawAngle, 0.0f, 0.0f );
}
D3DXVec3TransformCoord( &vPosDeltaWorld, &vPosDelta, &mCameraRot );
// Move the eye position
m_vEye += vPosDeltaWorld;
if( m_bClipToBoundary )
ConstrainToBoundary( &m_vEye );
// Update the lookAt position based on the eye position
m_vLookAt = m_vEye + vWorldAhead;
// Update the view matrix
D3DXMatrixLookAtLH( &m_mView, &m_vEye, &m_vLookAt, &vWorldUp );
D3DXMatrixInverse( &m_mCameraWorld, NULL, &m_mView );
}
//--------------------------------------------------------------------------------------
// Enable or disable each of the mouse buttons for rotation drag.
//--------------------------------------------------------------------------------------
void CFirstPersonCamera::SetRotateButtons( bool bLeft, bool bMiddle, bool bRight )
{
m_nActiveButtonMask = ( bLeft ? MOUSE_LEFT_BUTTON : 0 ) |
( bMiddle ? MOUSE_MIDDLE_BUTTON : 0 ) |
( bRight ? MOUSE_RIGHT_BUTTON : 0 );
}
//--------------------------------------------------------------------------------------
// Constructor
//--------------------------------------------------------------------------------------
CModelViewerCamera::CModelViewerCamera()
{
D3DXMatrixIdentity( &m_mWorld );
D3DXMatrixIdentity( &m_mModelRot );
D3DXMatrixIdentity( &m_mModelLastRot );
D3DXMatrixIdentity( &m_mCameraRotLast );
m_vModelCenter = D3DXVECTOR3(0,0,0);
m_fRadius = 5.0f;
m_fDefaultRadius = 5.0f;
m_fMinRadius = 1.0f;
m_fMaxRadius = FLT_MAX;
m_bLimitPitch = false;
m_bEnablePositionMovement = false;
m_bAttachCameraToModel = false;
m_nRotateModelButtonMask = MOUSE_LEFT_BUTTON;
m_nZoomButtonMask = MOUSE_WHEEL;
m_nRotateCameraButtonMask = MOUSE_RIGHT_BUTTON;
m_bDragSinceLastUpdate = true;
}
//--------------------------------------------------------------------------------------
// Update the view matrix & the model's world matrix based
// on user input & elapsed time
//--------------------------------------------------------------------------------------
VOID CModelViewerCamera::FrameMove( FLOAT fElapsedTime )
{
if( IsKeyDown(m_aKeys[CAM_RESET]) )
Reset();
// If no dragged has happend since last time FrameMove is called,
// no need to handle again.
if( !m_bDragSinceLastUpdate )
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 );
// Get amount of velocity based on the keyboard input and drag (if any)
UpdateVelocity( fElapsedTime );
// Simple euler method to calculate position delta
D3DXVECTOR3 vPosDelta = 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;
m_fRadius = __min( m_fMaxRadius, m_fRadius );
m_fRadius = __max( m_fMinRadius, m_fRadius );
m_nMouseWheelDelta = 0;
// Get the inverse of the arcball's rotation matrix
D3DXMATRIX mCameraRot;
D3DXMatrixInverse( &mCameraRot, NULL, m_ViewArcBall.GetRotationMatrix() );
// Transform vectors based on camera's rotation matrix
D3DXVECTOR3 vWorldUp, vWorldAhead;
D3DXVECTOR3 vLocalUp = D3DXVECTOR3(0,1,0);
D3DXVECTOR3 vLocalAhead = D3DXVECTOR3(0,0,1);
D3DXVec3TransformCoord( &vWorldUp, &vLocalUp, &mCameraRot );
D3DXVec3TransformCoord( &vWorldAhead, &vLocalAhead, &mCameraRot );
// Transform the position delta by the camera's rotation
D3DXVECTOR3 vPosDeltaWorld;
D3DXVec3TransformCoord( &vPosDeltaWorld, &vPosDelta, &mCameraRot );
// Move the lookAt position
m_vLookAt += vPosDeltaWorld;
if( m_bClipToBoundary )
ConstrainToBoundary( &m_vLookAt );
// Update the eye point based on a radius away from the lookAt position
m_vEye = m_vLookAt - vWorldAhead * m_fRadius;
// Update the view matrix
D3DXMatrixLookAtLH( &m_mView, &m_vEye, &m_vLookAt, &vWorldUp );
D3DXMATRIX mInvView;
D3DXMatrixInverse( &mInvView, NULL, &m_mView );
mInvView._41 = mInvView._42 = mInvView._43 = 0;
D3DXMATRIX mModelLastRotInv;
D3DXMatrixInverse(&mModelLastRotInv, NULL, &m_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.
D3DXMATRIX mModelRot;
mModelRot = *m_WorldArcBall.GetRotationMatrix();
m_mModelRot *= m_mView * mModelLastRotInv * mModelRot * mInvView;
if( m_ViewArcBall.IsBeingDragged() && m_bAttachCameraToModel && !IsKeyDown(m_aKeys[CAM_CONTROLDOWN]) )
{
// Attach camera to model by inverse of the model rotation
D3DXMATRIX mCameraLastRotInv;
D3DXMatrixInverse(&mCameraLastRotInv, NULL, &m_mCameraRotLast);
D3DXMATRIX mCameraRotDelta = mCameraLastRotInv * mCameraRot; // local to world matrix
m_mModelRot *= mCameraRotDelta;
}
m_mCameraRotLast = mCameraRot;
m_mModelLastRot = mModelRot;
// Since we're accumulating delta rotations, we need to orthonormalize
// the matrix to prevent eventual matrix skew
D3DXVECTOR3* pXBasis = (D3DXVECTOR3*) &m_mModelRot._11;
D3DXVECTOR3* pYBasis = (D3DXVECTOR3*) &m_mModelRot._21;
D3DXVECTOR3* pZBasis = (D3DXVECTOR3*) &m_mModelRot._31;
D3DXVec3Normalize( pXBasis, pXBasis );
D3DXVec3Cross( pYBasis, pZBasis, pXBasis );
D3DXVec3Normalize( pYBasis, pYBasis );
D3DXVec3Cross( pZBasis, pXBasis, pYBasis );
// Translate the rotation matrix to the same position as the lookAt position
m_mModelRot._41 = m_vLookAt.x;
m_mModelRot._42 = m_vLookAt.y;
m_mModelRot._43 = m_vLookAt.z;
// Translate world matrix so its at the center of the model
D3DXMATRIX mTrans;
D3DXMatrixTranslation( &mTrans, -m_vModelCenter.x, -m_vModelCenter.y, -m_vModelCenter.z );
m_mWorld = mTrans * m_mModelRot;
}
void CModelViewerCamera::SetDragRect( 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();
D3DXMatrixIdentity( &m_mWorld );
D3DXMatrixIdentity( &m_mModelRot );
D3DXMatrixIdentity( &m_mModelLastRot );
D3DXMatrixIdentity( &m_mCameraRotLast );
m_fRadius = m_fDefaultRadius;
m_WorldArcBall.Reset();
m_ViewArcBall.Reset();
}
//--------------------------------------------------------------------------------------
// Override for setting the view parameters
//--------------------------------------------------------------------------------------
void CModelViewerCamera::SetViewParams( D3DXVECTOR3* pvEyePt, D3DXVECTOR3* pvLookatPt )
{
CBaseCamera::SetViewParams( pvEyePt, pvLookatPt );
// Propogate changes to the member arcball
D3DXQUATERNION quat;
D3DXMATRIXA16 mRotation;
D3DXVECTOR3 vUp(0,1,0);
D3DXMatrixLookAtLH( &mRotation, pvEyePt, pvLookatPt, &vUp );
D3DXQuaternionRotationMatrix( &quat, &mRotation );
m_ViewArcBall.SetQuatNow( quat );
// Set the radius according to the distance
D3DXVECTOR3 vEyeToPoint;
D3DXVec3Subtract( &vEyeToPoint, pvLookatPt, pvEyePt );
SetRadius( D3DXVec3Length( &vEyeToPoint ) );
// View information changed. FrameMove should be called.
m_bDragSinceLastUpdate = true;
}
//--------------------------------------------------------------------------------------
// Call this from your message proc so this class can handle window messages
//--------------------------------------------------------------------------------------
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_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;
}
//--------------------------------------------------------------------------------------
// Desc: Returns a view matrix for rendering to a face of a cubemap.
//--------------------------------------------------------------------------------------
D3DXMATRIX DXUTGetCubeMapViewMatrix( DWORD dwFace )
{
D3DXVECTOR3 vEyePt = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vLookDir;
D3DXVECTOR3 vUpDir;
switch( dwFace )
{
case D3DCUBEMAP_FACE_POSITIVE_X:
vLookDir = D3DXVECTOR3( 1.0f, 0.0f, 0.0f );
vUpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
case D3DCUBEMAP_FACE_NEGATIVE_X:
vLookDir = D3DXVECTOR3(-1.0f, 0.0f, 0.0f );
vUpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
case D3DCUBEMAP_FACE_POSITIVE_Y:
vLookDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
vUpDir = D3DXVECTOR3( 0.0f, 0.0f,-1.0f );
break;
case D3DCUBEMAP_FACE_NEGATIVE_Y:
vLookDir = D3DXVECTOR3( 0.0f,-1.0f, 0.0f );
vUpDir = D3DXVECTOR3( 0.0f, 0.0f, 1.0f );
break;
case D3DCUBEMAP_FACE_POSITIVE_Z:
vLookDir = D3DXVECTOR3( 0.0f, 0.0f, 1.0f );
vUpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
case D3DCUBEMAP_FACE_NEGATIVE_Z:
vLookDir = D3DXVECTOR3( 0.0f, 0.0f,-1.0f );
vUpDir = D3DXVECTOR3( 0.0f, 1.0f, 0.0f );
break;
}
// Set the view transform for this cubemap surface
D3DXMATRIXA16 mView;
D3DXMatrixLookAtLH( &mView, &vEyePt, &vLookDir, &vUpDir );
return mView;
}
//--------------------------------------------------------------------------------------
// Returns the string for the given D3DFORMAT.
//--------------------------------------------------------------------------------------
LPCTSTR DXUTD3DFormatToString( D3DFORMAT format, bool bWithPrefix )
{
TCHAR* pstr = NULL;
switch( format )
{
case D3DFMT_UNKNOWN: pstr = "D3DFMT_UNKNOWN"; break;
case D3DFMT_R8G8B8: pstr = "D3DFMT_R8G8B8"; break;
case D3DFMT_A8R8G8B8: pstr = "D3DFMT_A8R8G8B8"; break;
case D3DFMT_X8R8G8B8: pstr = "D3DFMT_X8R8G8B8"; break;
case D3DFMT_R5G6B5: pstr = "D3DFMT_R5G6B5"; break;
case D3DFMT_X1R5G5B5: pstr = "D3DFMT_X1R5G5B5"; break;
case D3DFMT_A1R5G5B5: pstr = "D3DFMT_A1R5G5B5"; break;
case D3DFMT_A4R4G4B4: pstr = "D3DFMT_A4R4G4B4"; break;
case D3DFMT_R3G3B2: pstr = "D3DFMT_R3G3B2"; break;
case D3DFMT_A8: pstr = "D3DFMT_A8"; break;
case D3DFMT_A8R3G3B2: pstr = "D3DFMT_A8R3G3B2"; break;
case D3DFMT_X4R4G4B4: pstr = "D3DFMT_X4R4G4B4"; break;
case D3DFMT_A2B10G10R10: pstr = "D3DFMT_A2B10G10R10"; break;
case D3DFMT_A8B8G8R8: pstr = "D3DFMT_A8B8G8R8"; break;
case D3DFMT_X8B8G8R8: pstr = "D3DFMT_X8B8G8R8"; break;
case D3DFMT_G16R16: pstr = "D3DFMT_G16R16"; break;
case D3DFMT_A2R10G10B10: pstr = "D3DFMT_A2R10G10B10"; break;
case D3DFMT_A16B16G16R16: pstr = "D3DFMT_A16B16G16R16"; break;
case D3DFMT_A8P8: pstr = "D3DFMT_A8P8"; break;
case D3DFMT_P8: pstr = "D3DFMT_P8"; break;
case D3DFMT_L8: pstr = "D3DFMT_L8"; break;
case D3DFMT_A8L8: pstr = "D3DFMT_A8L8"; break;
case D3DFMT_A4L4: pstr = "D3DFMT_A4L4"; break;
case D3DFMT_V8U8: pstr = "D3DFMT_V8U8"; break;
case D3DFMT_L6V5U5: pstr = "D3DFMT_L6V5U5"; break;
case D3DFMT_X8L8V8U8: pstr = "D3DFMT_X8L8V8U8"; break;
case D3DFMT_Q8W8V8U8: pstr = "D3DFMT_Q8W8V8U8"; break;
case D3DFMT_V16U16: pstr = "D3DFMT_V16U16"; break;
case D3DFMT_A2W10V10U10: pstr = "D3DFMT_A2W10V10U10"; break;
case D3DFMT_UYVY: pstr = "D3DFMT_UYVY"; break;
case D3DFMT_YUY2: pstr = "D3DFMT_YUY2"; break;
case D3DFMT_DXT1: pstr = "D3DFMT_DXT1"; break;
case D3DFMT_DXT2: pstr = "D3DFMT_DXT2"; break;
case D3DFMT_DXT3: pstr = "D3DFMT_DXT3"; break;
case D3DFMT_DXT4: pstr = "D3DFMT_DXT4"; break;
case D3DFMT_DXT5: pstr = "D3DFMT_DXT5"; break;
case D3DFMT_D16_LOCKABLE: pstr = "D3DFMT_D16_LOCKABLE"; break;
case D3DFMT_D32: pstr = "D3DFMT_D32"; break;
case D3DFMT_D15S1: pstr = "D3DFMT_D15S1"; break;
case D3DFMT_D24S8: pstr = "D3DFMT_D24S8"; break;
case D3DFMT_D24X8: pstr = "D3DFMT_D24X8"; break;
case D3DFMT_D24X4S4: pstr = "D3DFMT_D24X4S4"; break;
case D3DFMT_D16: pstr = "D3DFMT_D16"; break;
case D3DFMT_L16: pstr = "D3DFMT_L16"; break;
case D3DFMT_VERTEXDATA: pstr = "D3DFMT_VERTEXDATA"; break;
case D3DFMT_INDEX16: pstr = "D3DFMT_INDEX16"; break;
case D3DFMT_INDEX32: pstr = "D3DFMT_INDEX32"; break;
case D3DFMT_Q16W16V16U16: pstr = "D3DFMT_Q16W16V16U16"; break;
case D3DFMT_MULTI2_ARGB8: pstr = "D3DFMT_MULTI2_ARGB8"; break;
case D3DFMT_R16F: pstr = "D3DFMT_R16F"; break;
case D3DFMT_G16R16F: pstr = "D3DFMT_G16R16F"; break;
case D3DFMT_A16B16G16R16F: pstr = "D3DFMT_A16B16G16R16F"; break;
case D3DFMT_R32F: pstr = "D3DFMT_R32F"; break;
case D3DFMT_G32R32F: pstr = "D3DFMT_G32R32F"; break;
case D3DFMT_A32B32G32R32F: pstr = "D3DFMT_A32B32G32R32F"; break;
case D3DFMT_CxV8U8: pstr = "D3DFMT_CxV8U8"; break;
default: pstr = "Unknown format"; break;
}
if( bWithPrefix || strstr( pstr, "D3DFMT_" )== NULL )
return pstr;
else
return pstr + lstrlen( "D3DFMT_" );
}
//--------------------------------------------------------------------------------------
// Outputs to the debug stream a formatted Unicode string with a variable-argument list.
//--------------------------------------------------------------------------------------
VOID DXUTOutputDebugStringW( LPCTSTR strMsg, ... )
{
#if defined(DEBUG) | defined(_DEBUG)
TCHAR strBuffer[512];
va_list args;
va_start(args, strMsg);
StringCchVPrintfA( strBuffer, 512, strMsg, args );
strBuffer[511] = L'\0';
va_end(args);
OutputDebugString( strBuffer );
#else
UNREFERENCED_PARAMETER(strMsg);
#endif
}
//--------------------------------------------------------------------------------------
// Outputs to the debug stream a formatted MBCS string with a variable-argument list.
//--------------------------------------------------------------------------------------
VOID DXUTOutputDebugStringA( LPCSTR strMsg, ... )
{
#if defined(DEBUG) | defined(_DEBUG)
CHAR strBuffer[512];
va_list args;
va_start(args, strMsg);
StringCchVPrintfA( strBuffer, 512, strMsg, args );
strBuffer[511] = '\0';
va_end(args);
OutputDebugStringA( strBuffer );
#else
UNREFERENCED_PARAMETER(strMsg);
#endif
}
//--------------------------------------------------------------------------------------
CDXUTLineManager::CDXUTLineManager()
{
m_pd3dDevice = NULL;
m_pD3DXLine = NULL;
}
//--------------------------------------------------------------------------------------
CDXUTLineManager::~CDXUTLineManager()
{
OnDeletedDevice();
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::OnCreatedDevice( IDirect3DDevice9* pd3dDevice )
{
m_pd3dDevice = pd3dDevice;
HRESULT hr;
hr = D3DXCreateLine( m_pd3dDevice, &m_pD3DXLine );
if( FAILED(hr) )
return hr;
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::OnResetDevice()
{
if( m_pD3DXLine )
m_pD3DXLine->OnResetDevice();
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::OnRender()
{
HRESULT hr;
if( NULL == m_pD3DXLine )
return E_INVALIDARG;
bool bDrawingHasBegun = false;
float fLastWidth = 0.0f;
bool bLastAntiAlias = false;
for( int i=0; i<m_LinesList.GetSize(); i++ )
{
LINE_NODE* pLineNode = m_LinesList.GetAt(i);
if( pLineNode )
{
if( !bDrawingHasBegun ||
fLastWidth != pLineNode->fWidth ||
bLastAntiAlias != pLineNode->bAntiAlias )
{
if( bDrawingHasBegun )
{
hr = m_pD3DXLine->End();
if( FAILED(hr) )
return hr;
}
m_pD3DXLine->SetWidth( pLineNode->fWidth );
m_pD3DXLine->SetAntialias( pLineNode->bAntiAlias );
fLastWidth = pLineNode->fWidth;
bLastAntiAlias = pLineNode->bAntiAlias;
hr = m_pD3DXLine->Begin();
if( FAILED(hr) )
return hr;
bDrawingHasBegun = true;
}
hr = m_pD3DXLine->Draw( pLineNode->pVertexList, pLineNode->dwVertexListCount, pLineNode->Color );
if( FAILED(hr) )
return hr;
}
}
if( bDrawingHasBegun )
{
hr = m_pD3DXLine->End();
if( FAILED(hr) )
return hr;
}
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::OnLostDevice()
{
if( m_pD3DXLine )
m_pD3DXLine->OnLostDevice();
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::OnDeletedDevice()
{
RemoveAllLines();
SAFE_RELEASE( m_pD3DXLine );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::AddLine( int* pnLineID, D3DXVECTOR2* pVertexList, DWORD dwVertexListCount, D3DCOLOR Color, float fWidth, float fScaleRatio, bool bAntiAlias )
{
if( pVertexList == NULL || dwVertexListCount == 0 )
return E_INVALIDARG;
LINE_NODE* pLineNode = new LINE_NODE;
if( pLineNode == NULL )
return E_OUTOFMEMORY;
ZeroMemory( pLineNode, sizeof(LINE_NODE) );
pLineNode->nLineID = m_LinesList.GetSize();
pLineNode->Color = Color;
pLineNode->fWidth = fWidth;
pLineNode->bAntiAlias = bAntiAlias;
pLineNode->dwVertexListCount = dwVertexListCount;
if( pnLineID )
*pnLineID = pLineNode->nLineID;
pLineNode->pVertexList = new D3DXVECTOR2[dwVertexListCount];
if( pLineNode->pVertexList == NULL )
{
delete pLineNode;
return E_OUTOFMEMORY;
}
for( DWORD i=0; i<dwVertexListCount; i++ )
{
pLineNode->pVertexList[i] = pVertexList[i] * fScaleRatio;
}
m_LinesList.Add( pLineNode );
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::AddRect( int* pnLineID, RECT rc, D3DCOLOR Color, float fWidth, float fScaleRatio, bool bAntiAlias )
{
if( fWidth > 2.0f )
{
D3DXVECTOR2 vertexList[8];
vertexList[0].x = (float)rc.left;
vertexList[0].y = (float)rc.top - (fWidth/2.0f);
vertexList[1].x = (float)rc.left;
vertexList[1].y = (float)rc.bottom + (fWidth/2.0f);
vertexList[2].x = (float)rc.left;
vertexList[2].y = (float)rc.bottom - 0.5f;
vertexList[3].x = (float)rc.right;
vertexList[3].y = (float)rc.bottom - 0.5f;
vertexList[4].x = (float)rc.right;
vertexList[4].y = (float)rc.bottom + (fWidth/2.0f);
vertexList[5].x = (float)rc.right;
vertexList[5].y = (float)rc.top - (fWidth/2.0f);
vertexList[6].x = (float)rc.right;
vertexList[6].y = (float)rc.top;
vertexList[7].x = (float)rc.left;
vertexList[7].y = (float)rc.top;
return AddLine( pnLineID, vertexList, 8, Color, fWidth, fScaleRatio, bAntiAlias );
}
else
{
D3DXVECTOR2 vertexList[5];
vertexList[0].x = (float)rc.left;
vertexList[0].y = (float)rc.top;
vertexList[1].x = (float)rc.left;
vertexList[1].y = (float)rc.bottom;
vertexList[2].x = (float)rc.right;
vertexList[2].y = (float)rc.bottom;
vertexList[3].x = (float)rc.right;
vertexList[3].y = (float)rc.top;
vertexList[4].x = (float)rc.left;
vertexList[4].y = (float)rc.top;
return AddLine( pnLineID, vertexList, 5, Color, fWidth, fScaleRatio, bAntiAlias );
}
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::RemoveLine( int nLineID )
{
for( int i=0; i<m_LinesList.GetSize(); i++ )
{
LINE_NODE* pLineNode = m_LinesList.GetAt(i);
if( pLineNode && pLineNode->nLineID == nLineID )
{
SAFE_DELETE_ARRAY( pLineNode->pVertexList );
delete pLineNode;
m_LinesList.SetAt(i, NULL);
}
}
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTLineManager::RemoveAllLines()
{
for( int i=0; i<m_LinesList.GetSize(); i++ )
{
LINE_NODE* pLineNode = m_LinesList.GetAt(i);
if( pLineNode )
{
SAFE_DELETE_ARRAY( pLineNode->pVertexList );
delete pLineNode;
}
}
m_LinesList.RemoveAll();
return S_OK;
}
//--------------------------------------------------------------------------------------
CDXUTTextHelper::CDXUTTextHelper( ID3DXFont* pFont, ID3DXSprite* pSprite, int nLineHeight )
{
m_pFont = pFont;
m_pSprite = pSprite;
m_clr = D3DXCOLOR(1,1,1,1);
m_pt.x = 0;
m_pt.y = 0;
m_nLineHeight = nLineHeight;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTTextHelper::DrawFormattedTextLine( const TCHAR* strMsg, ... )
{
TCHAR strBuffer[512];
va_list args;
va_start(args, strMsg);
StringCchVPrintf( strBuffer, 512, strMsg, args );
strBuffer[511] = L'\0';
va_end(args);
return DrawTextLine( strBuffer );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTTextHelper::DrawTextLine( const TCHAR* strMsg )
{
if( NULL == m_pFont )
return DXUT_ERR_MSGBOX( "DrawTextLine", E_INVALIDARG );
HRESULT hr;
RECT rc;
SetRect( &rc, m_pt.x, m_pt.y, 0, 0 );
hr = m_pFont->DrawText( m_pSprite, strMsg, -1, &rc, DT_NOCLIP, m_clr );
if( FAILED(hr) )
return DXTRACE_ERR_MSGBOX( "DrawText", hr );
m_pt.y += m_nLineHeight;
return S_OK;
}
HRESULT CDXUTTextHelper::DrawFormattedTextLine( RECT &rc, DWORD dwFlags, const TCHAR* strMsg, ... )
{
TCHAR strBuffer[512];
va_list args;
va_start(args, strMsg);
StringCchVPrintf( strBuffer, 512, strMsg, args );
strBuffer[511] = L'\0';
va_end(args);
return DrawTextLine( rc, dwFlags, strBuffer );
}
HRESULT CDXUTTextHelper::DrawTextLine( RECT &rc, DWORD dwFlags, const TCHAR* strMsg )
{
if( NULL == m_pFont )
return DXUT_ERR_MSGBOX( "DrawTextLine", E_INVALIDARG );
HRESULT hr;
hr = m_pFont->DrawText( m_pSprite, strMsg, -1, &rc, dwFlags, m_clr );
if( FAILED(hr) )
return DXTRACE_ERR_MSGBOX( "DrawText", hr );
m_pt.y += m_nLineHeight;
return S_OK;
}
//--------------------------------------------------------------------------------------
void CDXUTTextHelper::Begin()
{
if( m_pSprite )
m_pSprite->Begin( D3DXSPRITE_ALPHABLEND | D3DXSPRITE_SORT_TEXTURE );
}
void CDXUTTextHelper::End()
{
if( m_pSprite )
m_pSprite->End();
}
//--------------------------------------------------------------------------------------
IDirect3DDevice9* CDXUTDirectionWidget::s_pd3dDevice = NULL;
ID3DXEffect* CDXUTDirectionWidget::s_pEffect = NULL;
ID3DXMesh* CDXUTDirectionWidget::s_pMesh = NULL;
//--------------------------------------------------------------------------------------
CDXUTDirectionWidget::CDXUTDirectionWidget()
{
m_fRadius = 1.0f;
m_vDefaultDir = D3DXVECTOR3(0,1,0);
m_vCurrentDir = m_vDefaultDir;
m_nRotateMask = MOUSE_RIGHT_BUTTON;
D3DXMatrixIdentity( &m_mView );
D3DXMatrixIdentity( &m_mRot );
D3DXMatrixIdentity( &m_mRotSnapshot );
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTDirectionWidget::StaticOnCreateDevice( IDirect3DDevice9* pd3dDevice )
{
TCHAR str[MAX_PATH];
HRESULT hr;
s_pd3dDevice = pd3dDevice;
// Read the D3DX effect file
DXUTFindDXSDKMediaFileCch( str, MAX_PATH, "UI\\DXUTShared.fx" );
// If this fails, there should be debug output as to
// why the .fx file failed to compile
D3DXCreateEffectFromFileA( s_pd3dDevice, str, NULL, NULL, D3DXFX_NOT_CLONEABLE, NULL, &s_pEffect, NULL );
// Load the mesh with D3DX and get back a ID3DXMesh*. For this
// sample we'll ignore the X file's embedded materials since we know
// exactly the model we're loading. See the mesh samples such as
// "OptimizedMesh" for a more generic mesh loading example.
DXUTFindDXSDKMediaFileCch( str, MAX_PATH, "UI\\arrow.x" );
D3DXLoadMeshFromX( str, D3DXMESH_MANAGED, s_pd3dDevice, NULL,
NULL, NULL, NULL, &s_pMesh);
// Optimize the mesh for this graphics card's vertex cache
// so when rendering the mesh's triangle list the vertices will
// cache hit more often so it won't have to re-execute the vertex shader
// on those vertices so it will improve perf.
DWORD* rgdwAdjacency = new DWORD[s_pMesh->GetNumFaces() * 3];
if( rgdwAdjacency == NULL )
return E_OUTOFMEMORY;
s_pMesh->GenerateAdjacency(1e-6f,rgdwAdjacency);
s_pMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE, rgdwAdjacency, NULL, NULL, NULL);
delete []rgdwAdjacency;
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTDirectionWidget::OnResetDevice( const D3DSURFACE_DESC* pBackBufferSurfaceDesc )
{
m_ArcBall.SetWindow( pBackBufferSurfaceDesc->Width, pBackBufferSurfaceDesc->Height );
return S_OK;
}
//--------------------------------------------------------------------------------------
void CDXUTDirectionWidget::StaticOnLostDevice()
{
if( s_pEffect )
s_pEffect->OnLostDevice();
}
//--------------------------------------------------------------------------------------
void CDXUTDirectionWidget::StaticOnDestroyDevice()
{
SAFE_RELEASE(s_pEffect);
SAFE_RELEASE(s_pMesh);
}
//--------------------------------------------------------------------------------------
LRESULT CDXUTDirectionWidget::HandleMessages( HWND hWnd, UINT uMsg,
WPARAM wParam, LPARAM lParam )
{
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;
}
}
return 0;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTDirectionWidget::OnRender( D3DXCOLOR color, const D3DXMATRIX* pmView,
const D3DXMATRIX* pmProj, const D3DXVECTOR3* pEyePt )
{
m_mView = *pmView;
// Render the light spheres so the user can visually see the light dir
UINT iPass, cPasses;
D3DXMATRIX mRotate;
D3DXMATRIX mScale;
D3DXMATRIX mTrans;
D3DXMATRIXA16 mWorldViewProj;
HRESULT hr;
s_pEffect->SetTechnique( "RenderWith1LightNoTexture" );
s_pEffect->SetVector( "g_MaterialDiffuseColor", (D3DXVECTOR4*)&color);
D3DXVECTOR3 vEyePt;
D3DXVec3Normalize( &vEyePt, pEyePt );
V( s_pEffect->SetValue( "g_LightDir", &vEyePt, sizeof(D3DXVECTOR3) ) );
// Rotate arrow model to point towards origin
D3DXMATRIX mRotateA, mRotateB;
D3DXVECTOR3 vAt = D3DXVECTOR3(0,0,0);
D3DXVECTOR3 vUp = D3DXVECTOR3(0,1,0);
D3DXMatrixRotationX( &mRotateB, D3DX_PI );
D3DXMatrixLookAtLH( &mRotateA, &m_vCurrentDir, &vAt, &vUp );
D3DXMatrixInverse( &mRotateA, NULL, &mRotateA );
mRotate = mRotateB * mRotateA;
D3DXVECTOR3 vL = m_vCurrentDir * m_fRadius * 1.0f;
D3DXMatrixTranslation( &mTrans, vL.x, vL.y, vL.z );
D3DXMatrixScaling( &mScale, m_fRadius*0.2f, m_fRadius*0.2f, m_fRadius*0.2f );
D3DXMATRIX mWorld = mRotate * mScale * mTrans;
mWorldViewProj = mWorld * (m_mView) * (*pmProj);
V( s_pEffect->SetMatrix( "g_mWorldViewProjection", &mWorldViewProj ) );
V( s_pEffect->SetMatrix( "g_mWorld", &mWorld ) );
for( int iSubset=0; iSubset<2; iSubset++ )
{
V( s_pEffect->Begin(&cPasses, 0) );
for (iPass = 0; iPass < cPasses; iPass++)
{
V( s_pEffect->BeginPass(iPass) );
V( s_pMesh->DrawSubset(iSubset) );
V( s_pEffect->EndPass() );
}
V( s_pEffect->End() );
}
return S_OK;
}
//--------------------------------------------------------------------------------------
HRESULT CDXUTDirectionWidget::UpdateLightDir()
{
D3DXMATRIX mInvView;
D3DXMatrixInverse(&mInvView, NULL, &m_mView);
mInvView._41 = mInvView._42 = mInvView._43 = 0;
D3DXMATRIX mLastRotInv;
D3DXMatrixInverse(&mLastRotInv, NULL, &m_mRotSnapshot);
D3DXMATRIX mRot = *m_ArcBall.GetRotationMatrix();
m_mRotSnapshot = mRot;
// 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.
m_mRot *= m_mView * mLastRotInv * mRot * mInvView;
// Since we're accumulating delta rotations, we need to orthonormalize
// the matrix to prevent eventual matrix skew
D3DXVECTOR3* pXBasis = (D3DXVECTOR3*) &m_mRot._11;
D3DXVECTOR3* pYBasis = (D3DXVECTOR3*) &m_mRot._21;
D3DXVECTOR3* pZBasis = (D3DXVECTOR3*) &m_mRot._31;
D3DXVec3Normalize( pXBasis, pXBasis );
D3DXVec3Cross( pYBasis, pZBasis, pXBasis );
D3DXVec3Normalize( pYBasis, pYBasis );
D3DXVec3Cross( pZBasis, pXBasis, pYBasis );
// Transform the default direction vector by the light's rotation matrix
D3DXVec3TransformNormal( &m_vCurrentDir, &m_vDefaultDir, &m_mRot );
return S_OK;
}
//--------------------------------------------------------------------------------------
// Direct3D9 dynamic linking support -- calls top-level D3D9 APIs with graceful
// failure if APIs are not present.
//--------------------------------------------------------------------------------------
// Function prototypes
typedef IDirect3D9* (WINAPI * LPDIRECT3DCREATE9) (UINT);
typedef INT (WINAPI * LPD3DPERF_BEGINEVENT)(D3DCOLOR, LPCTSTR);
typedef INT (WINAPI * LPD3DPERF_ENDEVENT)(void);
typedef VOID (WINAPI * LPD3DPERF_SETMARKER)(D3DCOLOR, LPCTSTR);
typedef VOID (WINAPI * LPD3DPERF_SETREGION)(D3DCOLOR, LPCTSTR);
typedef BOOL (WINAPI * LPD3DPERF_QUERYREPEATFRAME)(void);
typedef VOID (WINAPI * LPD3DPERF_SETOPTIONS)( DWORD dwOptions );
typedef DWORD (WINAPI * LPD3DPERF_GETSTATUS)( void );
// Module and function pointers
static HMODULE s_hModD3D9 = NULL;
static LPDIRECT3DCREATE9 s_DynamicDirect3DCreate9 = NULL;
static LPD3DPERF_BEGINEVENT s_DynamicD3DPERF_BeginEvent = NULL;
static LPD3DPERF_ENDEVENT s_DynamicD3DPERF_EndEvent = NULL;
static LPD3DPERF_SETMARKER s_DynamicD3DPERF_SetMarker = NULL;
static LPD3DPERF_SETREGION s_DynamicD3DPERF_SetRegion = NULL;
static LPD3DPERF_QUERYREPEATFRAME s_DynamicD3DPERF_QueryRepeatFrame = NULL;
static LPD3DPERF_SETOPTIONS s_DynamicD3DPERF_SetOptions = NULL;
static LPD3DPERF_GETSTATUS s_DynamicD3DPERF_GetStatus = NULL;
// Ensure function pointers are initialized
// MATCH
static bool DXUT_EnsureD3DAPIs( void )
{
// If module is non-NULL, this function has already been called. Note
// that this doesn't guarantee that all D3D9 procaddresses were found.
if( s_hModD3D9 != NULL )
return true;
// This may fail if DirectX 9 isn't installed
TCHAR wszPath[MAX_PATH+1];
if( !::GetSystemDirectory( wszPath, MAX_PATH+1 ) )
return false;
StringCchCat( wszPath, MAX_PATH, "\\d3d9.dll" );
s_hModD3D9 = LoadLibrary( wszPath );
if( s_hModD3D9 == NULL )
return false;
s_DynamicDirect3DCreate9 = (LPDIRECT3DCREATE9)GetProcAddress( s_hModD3D9, "Direct3DCreate9" );
s_DynamicD3DPERF_BeginEvent = (LPD3DPERF_BEGINEVENT)GetProcAddress( s_hModD3D9, "D3DPERF_BeginEvent" );
s_DynamicD3DPERF_EndEvent = (LPD3DPERF_ENDEVENT)GetProcAddress( s_hModD3D9, "D3DPERF_EndEvent" );
s_DynamicD3DPERF_SetMarker = (LPD3DPERF_SETMARKER)GetProcAddress( s_hModD3D9, "D3DPERF_SetMarker" );
s_DynamicD3DPERF_SetRegion = (LPD3DPERF_SETREGION)GetProcAddress( s_hModD3D9, "D3DPERF_SetRegion" );
s_DynamicD3DPERF_QueryRepeatFrame = (LPD3DPERF_QUERYREPEATFRAME)GetProcAddress( s_hModD3D9, "D3DPERF_QueryRepeatFrame" );
s_DynamicD3DPERF_SetOptions = (LPD3DPERF_SETOPTIONS)GetProcAddress( s_hModD3D9, "D3DPERF_SetOptions" );
s_DynamicD3DPERF_GetStatus = (LPD3DPERF_GETSTATUS)GetProcAddress( s_hModD3D9, "D3DPERF_GetStatus" );
return true;
}
// MATCH
IDirect3D9 * WINAPI DXUT_Dynamic_Direct3DCreate9(UINT SDKVersion)
{
if( DXUT_EnsureD3DAPIs() && s_DynamicDirect3DCreate9 != NULL )
return s_DynamicDirect3DCreate9( SDKVersion );
else
return NULL;
}
// MATCH
int WINAPI DXUT_Dynamic_D3DPERF_BeginEvent( D3DCOLOR col, LPCTSTR wszName )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_BeginEvent != NULL )
return s_DynamicD3DPERF_BeginEvent( col, wszName );
else
return -1;
}
// MATCH
int WINAPI DXUT_Dynamic_D3DPERF_EndEvent( void )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_EndEvent != NULL )
return s_DynamicD3DPERF_EndEvent();
else
return -1;
}
// MATCH
void WINAPI DXUT_Dynamic_D3DPERF_SetMarker( D3DCOLOR col, LPCTSTR wszName )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_SetMarker != NULL )
s_DynamicD3DPERF_SetMarker( col, wszName );
}
// MATCH
void WINAPI DXUT_Dynamic_D3DPERF_SetRegion( D3DCOLOR col, LPCTSTR wszName )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_SetRegion != NULL )
s_DynamicD3DPERF_SetRegion( col, wszName );
}
// MATCH
BOOL WINAPI DXUT_Dynamic_D3DPERF_QueryRepeatFrame( void )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_QueryRepeatFrame != NULL )
return s_DynamicD3DPERF_QueryRepeatFrame();
else
return FALSE;
}
// MATCH
void WINAPI DXUT_Dynamic_D3DPERF_SetOptions( DWORD dwOptions )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_SetOptions != NULL )
s_DynamicD3DPERF_SetOptions( dwOptions );
}
// MATCH
DWORD WINAPI DXUT_Dynamic_D3DPERF_GetStatus( void )
{
if( DXUT_EnsureD3DAPIs() && s_DynamicD3DPERF_GetStatus != NULL )
return s_DynamicD3DPERF_GetStatus();
else
return 0;
}
//------------------------
HRESULT WINAPI DXTraceWrapper( const char* strFile, DWORD dwLine, HRESULT hr, const char* strMsg, BOOL bPopMsgBox )
{
#ifdef _DEBUG
TCHAR szBuffer[1024];
sprintf(szBuffer, "DXTrace: %s (%d) returned 0x%x, msg=%s", strFile, dwLine, hr, strMsg);
OutputDebugString(szBuffer);
#endif
return 0;
}