You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2049 lines
51 KiB
2049 lines
51 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Implements the 3D view message handling. This class is responsible |
|
// for 3D camera control, activating tools in the 3D view, calling |
|
// into the renderer when necessary, and synchronizing the 2D camera |
|
// information with the 3D camera. |
|
// |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include <oleauto.h> |
|
#include <oaidl.h> |
|
#if _MSC_VER < 1300 |
|
#include <afxpriv.h> |
|
#endif |
|
#include <mmsystem.h> |
|
#include "Camera.h" |
|
#include "GlobalFunctions.h" |
|
#include "Gizmo.h" |
|
#include "History.h" |
|
#include "Keyboard.h" |
|
#include "MainFrm.h" |
|
#include "MapDoc.h" |
|
#include "MapDecal.h" |
|
#include "MapEntity.h" |
|
#include "MapSolid.h" |
|
#include "MapStudioModel.h" |
|
#include "MapWorld.h" |
|
#include "MapView3D.h" |
|
#include "MapView2D.h" |
|
#include "ObjectBar.h" |
|
#include "Options.h" |
|
#include "StatusBarIDs.h" |
|
#include "TitleWnd.h" |
|
#include "ToolManager.h" |
|
#include "hammer.h" |
|
#include "mathlib/vector.h" |
|
#include "MapOverlay.h" |
|
#include "engine_launcher_api.h" |
|
#include "vgui/Cursor.h" |
|
#include "ToolCamera.h" |
|
#include "HammerVGui.h" |
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include <tier0/memdbgon.h> |
|
|
|
#pragma warning(disable:4244 4305) |
|
|
|
|
|
typedef struct |
|
{ |
|
CMapObjectList *pList; |
|
POINT pt; |
|
CMapWorld *pWorld; |
|
} SELECT3DINFO; |
|
|
|
|
|
int g_nClipPoints = 0; |
|
Vector g_ClipPoints[4]; |
|
|
|
|
|
// |
|
// Defines the logical keys. |
|
// |
|
#define LOGICAL_KEY_FORWARD 0 |
|
#define LOGICAL_KEY_BACK 1 |
|
#define LOGICAL_KEY_LEFT 2 |
|
#define LOGICAL_KEY_RIGHT 3 |
|
#define LOGICAL_KEY_UP 4 |
|
#define LOGICAL_KEY_DOWN 5 |
|
#define LOGICAL_KEY_PITCH_UP 6 |
|
#define LOGICAL_KEY_PITCH_DOWN 7 |
|
#define LOGICAL_KEY_YAW_LEFT 8 |
|
#define LOGICAL_KEY_YAW_RIGHT 9 |
|
|
|
// |
|
// Rotation speeds, in degrees per second. |
|
// |
|
#define YAW_SPEED 180 |
|
#define PITCH_SPEED 180 |
|
#define ROLL_SPEED 180 |
|
|
|
|
|
IMPLEMENT_DYNCREATE(CMapView3D, CView) |
|
|
|
|
|
BEGIN_MESSAGE_MAP(CMapView3D, CView) |
|
//{{AFX_MSG_MAP(CMapView3D) |
|
ON_WM_KILLFOCUS() |
|
ON_WM_TIMER() |
|
ON_WM_KEYDOWN() |
|
ON_WM_KEYUP() |
|
ON_WM_SIZE() |
|
ON_WM_CONTEXTMENU() |
|
ON_WM_LBUTTONDOWN() |
|
ON_WM_LBUTTONUP() |
|
ON_WM_LBUTTONDBLCLK() |
|
ON_WM_RBUTTONDOWN() |
|
ON_WM_MOUSEMOVE() |
|
ON_WM_MOUSEWHEEL() |
|
ON_WM_RBUTTONUP() |
|
ON_WM_CHAR() |
|
ON_WM_SETFOCUS() |
|
ON_WM_NCPAINT() |
|
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint) |
|
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint) |
|
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) |
|
ON_COMMAND(ID_VIEW_3DWIREFRAME, OnView3dWireframe) |
|
ON_COMMAND(ID_VIEW_3DPOLYGON, OnView3dPolygon) |
|
ON_COMMAND(ID_VIEW_3DTEXTURED, OnView3dTextured) |
|
ON_COMMAND(ID_VIEW_3DLIGHTMAP_GRID, OnView3dLightmapGrid) |
|
ON_COMMAND(ID_VIEW_LIGHTINGPREVIEW, OnView3dLightingPreview) |
|
ON_COMMAND(ID_VIEW_LIGHTINGPREVIEW_RAYTRACED, OnView3dLightingPreviewRayTraced) |
|
//}}AFX_MSG_MAP |
|
END_MESSAGE_MAP() |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor. Initializes data members to default values. |
|
//----------------------------------------------------------------------------- |
|
CMapView3D::CMapView3D(void) |
|
{ |
|
m_eDrawType = VIEW3D_WIREFRAME; |
|
m_pRender = NULL; |
|
m_pCamera = NULL; |
|
|
|
m_dwTimeLastInputSample = 0; |
|
|
|
m_fForwardSpeed = 0; |
|
m_fStrafeSpeed = 0; |
|
m_fVerticalSpeed = 0; |
|
|
|
m_pwndTitle = NULL; |
|
m_bLightingPreview = false; |
|
|
|
m_bMouseLook = false; |
|
m_bStrafing = false; |
|
m_bRotating = false; |
|
|
|
m_ptLastMouseMovement.x = 0; |
|
m_ptLastMouseMovement.y = 0; |
|
|
|
m_nLastRaytracedBitmapRenderTimeStamp = -1; |
|
|
|
m_bCameraPosChanged = false; |
|
m_bClippingChanged = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Destructor. Releases dynamically allocated resources. |
|
//----------------------------------------------------------------------------- |
|
CMapView3D::~CMapView3D(void) |
|
{ |
|
if (m_pCamera != NULL) |
|
{ |
|
delete m_pCamera; |
|
} |
|
|
|
if (m_pRender != NULL) |
|
{ |
|
m_pRender->ShutDown(); |
|
delete m_pRender; |
|
} |
|
|
|
if (m_pwndTitle != NULL) |
|
{ |
|
delete m_pwndTitle; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : cs - |
|
// Output : Returns TRUE on success, FALSE on failure. |
|
//----------------------------------------------------------------------------- |
|
BOOL CMapView3D::PreCreateWindow(CREATESTRUCT& cs) |
|
{ |
|
static CString className; |
|
|
|
if(className.IsEmpty()) |
|
{ |
|
// |
|
// We need the CS_OWNDC bit so that we don't need to call GetDC every time we render. That fixes the flicker under Win98. |
|
// |
|
className = AfxRegisterWndClass(CS_BYTEALIGNCLIENT | CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW | CS_OWNDC, NULL, HBRUSH(GetStockObject(BLACK_BRUSH))); |
|
} |
|
|
|
cs.lpszClass = className; |
|
|
|
return CView::PreCreateWindow(cs); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Disables mouselook when the view loses focus. This ensures that the |
|
// cursor is shown and not locked in the center of the 3D view. |
|
// Input : pNewWnd - The window getting focus. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnKillFocus(CWnd *pNewWnd) |
|
{ |
|
EnableMouseLook(false); |
|
EnableRotating(false); |
|
EnableStrafing(false); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : nDrawType - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::SetDrawType(DrawType_t eDrawType) |
|
{ |
|
EditorRenderMode_t eRenderMode; |
|
|
|
// Turn off the dialog. |
|
if ( m_eDrawType == VIEW3D_SMOOTHING_GROUP ) |
|
{ |
|
CMainFrame *pMainFrame = GetMainWnd(); |
|
if ( pMainFrame ) |
|
{ |
|
CFaceSmoothingVisualDlg *pSmoothDlg = pMainFrame->GetSmoothingGroupDialog(); |
|
pSmoothDlg->ShowWindow( SW_HIDE ); |
|
} |
|
} |
|
|
|
if (m_pwndTitle != NULL) |
|
{ |
|
m_pwndTitle->SetTitle("camera"); |
|
} |
|
|
|
m_bLightingPreview = false; |
|
switch (eDrawType) |
|
{ |
|
case VIEW3D_WIREFRAME: |
|
{ |
|
eRenderMode = RENDER_MODE_WIREFRAME; |
|
break; |
|
} |
|
|
|
case VIEW3D_POLYGON: |
|
{ |
|
eRenderMode = RENDER_MODE_FLAT; |
|
break; |
|
} |
|
|
|
case VIEW3D_TEXTURED: |
|
{ |
|
eRenderMode = RENDER_MODE_TEXTURED; |
|
break; |
|
} |
|
|
|
case VIEW3D_TEXTURED_SHADED: |
|
{ |
|
eRenderMode = RENDER_MODE_TEXTURED_SHADED; |
|
break; |
|
} |
|
case VIEW3D_LIGHTMAP_GRID: |
|
{ |
|
eRenderMode = RENDER_MODE_LIGHTMAP_GRID; |
|
break; |
|
} |
|
|
|
case VIEW3D_LIGHTING_PREVIEW2: |
|
{ |
|
eRenderMode = RENDER_MODE_LIGHT_PREVIEW2; |
|
break; |
|
} |
|
|
|
case VIEW3D_LIGHTING_PREVIEW_RAYTRACED: |
|
{ |
|
eRenderMode = RENDER_MODE_LIGHT_PREVIEW_RAYTRACED; |
|
break; |
|
} |
|
|
|
case VIEW3D_SMOOTHING_GROUP: |
|
{ |
|
CMainFrame *pMainFrame = GetMainWnd(); |
|
if ( pMainFrame ) |
|
{ |
|
CFaceSmoothingVisualDlg *pSmoothDlg = pMainFrame->GetSmoothingGroupDialog(); |
|
pSmoothDlg->ShowWindow( SW_SHOW ); |
|
} |
|
|
|
// Always set the initial group to visualize (zero). |
|
CMapDoc *pDoc = GetMapDoc(); |
|
pDoc->SetSmoothingGroupVisual( 0 ); |
|
|
|
eRenderMode = RENDER_MODE_SMOOTHING_GROUP; |
|
break; |
|
} |
|
|
|
//case VIEW3D_ENGINE: |
|
//{ |
|
// eRenderMode = RENDER_MODE_TEXTURED; |
|
// if ( IsRunningInEngine() ) |
|
// { |
|
// CMapDoc *pMapDoc = CMapDoc::GetActiveMapDoc(); |
|
// if ( pMapDoc ) |
|
// { |
|
// const char *pFullPathName = pMapDoc->GetPathName(); |
|
// if ( pFullPathName && pFullPathName[0] ) |
|
// { |
|
// char buf[MAX_PATH]; |
|
// Q_FileBase( pFullPathName, buf, MAX_PATH ); |
|
// |
|
// // Don't do it if we're untitled |
|
// //if ( !Q_stristr( buf, "untitled" ) ) |
|
// { |
|
// g_pEngineAPI->SetEngineWindow( m_hWnd ); |
|
// //g_pEngineAPI->SetMap( buf ); |
|
// g_pEngineAPI->ActivateSimulation( true ); |
|
// } |
|
// } |
|
// } |
|
// } |
|
// if (m_pwndTitle != NULL) |
|
// { |
|
// m_pwndTitle->SetTitle("engine"); |
|
// } |
|
// break; |
|
//} |
|
|
|
default: |
|
{ |
|
Assert(FALSE); |
|
eDrawType = VIEW3D_WIREFRAME; |
|
eRenderMode = RENDER_MODE_WIREFRAME; |
|
break; |
|
} |
|
} |
|
|
|
m_eDrawType = eDrawType; |
|
|
|
// |
|
// Set renderer to use the new rendering mode. |
|
// |
|
if (m_pRender != NULL) |
|
{ |
|
m_pRender->SetDefaultRenderMode(eRenderMode); |
|
m_pRender->SetInLightingPreview( m_bLightingPreview ); |
|
|
|
// Somehow, this drop down box screws up MFC's notion |
|
// of what we're supposed to be updating. This is a workaround. |
|
m_pRender->ResetFocus(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the position and direction of the camera for this view. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::SetCamera(const Vector &vecPos, const Vector &vecLookAt) |
|
{ |
|
m_pCamera->SetViewPoint(vecPos); |
|
m_pCamera->SetViewTarget(vecLookAt); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Prepares to print. |
|
// Input : Per CView::OnPreparePrinting. |
|
// Output : Returns nonzero to begin printing, zero to cancel printing. |
|
//----------------------------------------------------------------------------- |
|
BOOL CMapView3D::OnPreparePrinting(CPrintInfo* pInfo) |
|
{ |
|
return(DoPreparePrinting(pInfo)); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Debugging functions. |
|
//----------------------------------------------------------------------------- |
|
#ifdef _DEBUG |
|
void CMapView3D::AssertValid() const |
|
{ |
|
CView::AssertValid(); |
|
} |
|
|
|
void CMapView3D::Dump(CDumpContext& dc) const |
|
{ |
|
CView::Dump(dc); |
|
} |
|
#endif //_DEBUG |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : nIDEvent - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnTimer(UINT nIDEvent) |
|
{ |
|
static bool s_bPicking = false; // picking mutex |
|
|
|
switch (nIDEvent) |
|
{ |
|
case MVTIMER_PICKNEXT: |
|
{ |
|
if ( !s_bPicking ) |
|
{ |
|
s_bPicking = true; |
|
// set current document hit |
|
GetMapDoc()->GetSelection()->SetCurrentHit(hitNext); |
|
s_bPicking = false; |
|
} |
|
break; |
|
} |
|
} |
|
|
|
CView::OnTimer(nIDEvent); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called just before we are destroyed. |
|
//----------------------------------------------------------------------------- |
|
BOOL CMapView3D::DestroyWindow() |
|
{ |
|
KillTimer(MVTIMER_PICKNEXT); |
|
return CView::DestroyWindow(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::UpdateStatusBar(void) |
|
{ |
|
if (!IsWindow(m_hWnd)) |
|
{ |
|
return; |
|
} |
|
|
|
SetStatusText(SBI_GRIDZOOM, ""); |
|
SetStatusText(SBI_COORDS, ""); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets up key bindings for the 3D view. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::InitializeKeyMap(void) |
|
{ |
|
m_Keyboard.RemoveAllKeyMaps(); |
|
|
|
if (!Options.view2d.bNudge) |
|
{ |
|
m_Keyboard.AddKeyMap(VK_LEFT, 0, LOGICAL_KEY_YAW_LEFT); |
|
m_Keyboard.AddKeyMap(VK_RIGHT, 0, LOGICAL_KEY_YAW_RIGHT); |
|
m_Keyboard.AddKeyMap(VK_DOWN, 0, LOGICAL_KEY_PITCH_DOWN); |
|
m_Keyboard.AddKeyMap(VK_UP, 0, LOGICAL_KEY_PITCH_UP); |
|
|
|
m_Keyboard.AddKeyMap(VK_LEFT, KEY_MOD_SHIFT, LOGICAL_KEY_LEFT); |
|
m_Keyboard.AddKeyMap(VK_RIGHT, KEY_MOD_SHIFT, LOGICAL_KEY_RIGHT); |
|
m_Keyboard.AddKeyMap(VK_DOWN, KEY_MOD_SHIFT, LOGICAL_KEY_DOWN); |
|
m_Keyboard.AddKeyMap(VK_UP, KEY_MOD_SHIFT, LOGICAL_KEY_UP); |
|
} |
|
|
|
if (Options.view3d.bUseMouseLook) |
|
{ |
|
m_Keyboard.AddKeyMap('W', 0, LOGICAL_KEY_FORWARD); |
|
m_Keyboard.AddKeyMap('A', 0, LOGICAL_KEY_LEFT); |
|
m_Keyboard.AddKeyMap('D', 0, LOGICAL_KEY_RIGHT); |
|
m_Keyboard.AddKeyMap('S', 0, LOGICAL_KEY_BACK); |
|
} |
|
else |
|
{ |
|
m_Keyboard.AddKeyMap('D', 0, LOGICAL_KEY_FORWARD); |
|
m_Keyboard.AddKeyMap('C', 0, LOGICAL_KEY_BACK); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pWnd - |
|
// point - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnContextMenu(CWnd *pWnd, CPoint point) |
|
{ |
|
// Pass the message to the active tool. |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool) |
|
{ |
|
if ( pTool->OnContextMenu3D(this, 0, Vector2D(point.x, point.y) ) ) |
|
{ |
|
return; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles the key down event. |
|
// Input : Per CWnd::OnKeyDown. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
CMapDoc *pDoc = GetMapDoc(); |
|
if (pDoc == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
// |
|
// 'z' toggles mouselook. |
|
// |
|
if (((char)tolower(nChar) == 'z') && !(nFlags & 0x4000) && (Options.view3d.bUseMouseLook)) |
|
{ |
|
if (pDoc != NULL) |
|
{ |
|
EnableMouseLook(!m_bMouseLook); |
|
|
|
// |
|
// If we just stopped mouse looking, update the camera variables. |
|
// |
|
if (!m_bMouseLook) |
|
{ |
|
UpdateCameraVariables(); |
|
} |
|
} |
|
|
|
return; |
|
} |
|
|
|
// Got to check for m_pToolManager here because otherwise it can crash on startup if they have keys pressed. |
|
if ( m_pToolManager ) |
|
{ |
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool) |
|
{ |
|
if (pTool->OnKeyDown3D(this, nChar, nRepCnt, nFlags)) |
|
{ |
|
return; |
|
} |
|
} |
|
} |
|
|
|
m_Keyboard.OnKeyDown(nChar, nRepCnt, nFlags); |
|
|
|
switch (nChar) |
|
{ |
|
case VK_DELETE: |
|
{ |
|
pDoc->OnCmdMsg(ID_EDIT_DELETE, CN_COMMAND, NULL, NULL); |
|
break; |
|
} |
|
|
|
case VK_NEXT: |
|
{ |
|
pDoc->OnCmdMsg(ID_EDIT_SELNEXT, CN_COMMAND, NULL, NULL); |
|
break; |
|
} |
|
|
|
case VK_PRIOR: |
|
{ |
|
pDoc->OnCmdMsg(ID_EDIT_SELPREV, CN_COMMAND, NULL, NULL); |
|
break; |
|
} |
|
|
|
// |
|
// Move the back clipping plane closer in. |
|
// |
|
case '1': |
|
{ |
|
float fBack = m_pCamera->GetFarClip(); |
|
if (fBack >= 2000) |
|
{ |
|
m_pCamera->SetFarClip(fBack - 1000); |
|
Options.view3d.iBackPlane = fBack; |
|
} |
|
else if (fBack > 500) |
|
{ |
|
m_pCamera->SetFarClip(fBack - 250); |
|
Options.view3d.iBackPlane = fBack; |
|
} |
|
m_bUpdateView = true; |
|
m_bClippingChanged = true; |
|
break; |
|
} |
|
|
|
// |
|
// Move the back clipping plane farther away. |
|
// |
|
case '2': |
|
{ |
|
float fBack = m_pCamera->GetFarClip(); |
|
if ((fBack <= 9000) && (fBack > 1000)) |
|
{ |
|
m_pCamera->SetFarClip(fBack + 1000); |
|
Options.view3d.iBackPlane = fBack; |
|
} |
|
else if (fBack < 10000) |
|
{ |
|
m_pCamera->SetFarClip(fBack + 250); |
|
Options.view3d.iBackPlane = fBack; |
|
} |
|
m_bUpdateView = true; |
|
m_bClippingChanged = true; |
|
break; |
|
} |
|
|
|
case 'O': |
|
case 'o': |
|
{ |
|
m_pRender->DebugHook1(); |
|
break; |
|
} |
|
|
|
case 'I': |
|
case 'i': |
|
{ |
|
m_pRender->DebugHook2(); |
|
break; |
|
} |
|
|
|
case 'P': |
|
case 'p': |
|
{ |
|
pDoc->OnToggle3DGrid(); |
|
break; |
|
} |
|
|
|
default: |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
CView::OnKeyDown(nChar, nRepCnt, nFlags); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles key release events. |
|
// Input : Per CWnd::OnKeyup |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
// Got to check for m_pToolManager here because otherwise it can crash on startup if they have keys pressed. |
|
if ( m_pToolManager ) |
|
{ |
|
// Pass the message to the active tool. |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool) |
|
{ |
|
if (pTool->OnKeyUp3D(this, nChar, nRepCnt, nFlags)) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
m_Keyboard.OnKeyUp(nChar, nRepCnt, nFlags); |
|
|
|
UpdateCameraVariables(); |
|
} |
|
|
|
CView::OnKeyUp(nChar, nRepCnt, nFlags); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when the view is resized. |
|
// Input : nType - |
|
// cx - |
|
// cy - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnSize(UINT nType, int cx, int cy) |
|
{ |
|
if ( m_pCamera ) |
|
{ |
|
m_pCamera->SetViewPort( cx, cy ); |
|
} |
|
|
|
CView::OnSize(nType, cx, cy); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Finds the axis that is most closely aligned with the given vector. |
|
// Input : Vector - Vector to find closest axis to. |
|
// Output : Returns an axis index as follows: |
|
// 0 - Positive X axis. |
|
// 1 - Positive Y axis. |
|
// 2 - Positive Z axis. |
|
// 3 - Negative X axis. |
|
// 4 - Negative Y axis. |
|
// 5 - Negative Z axis. |
|
//----------------------------------------------------------------------------- |
|
const Vector&ClosestAxis(const Vector& v) |
|
{ |
|
static Vector vBestAxis; |
|
float fBestDot = -1; |
|
Vector vNormal = v; |
|
|
|
VectorNormalize( vNormal ); |
|
vBestAxis.Init(); |
|
|
|
for (int i = 0; i < 6; i++) |
|
{ |
|
Vector vTestAxis(0,0,0); |
|
|
|
vTestAxis[i%3] = (i>=3)?-1:1; |
|
|
|
float fTestDot = DotProduct(v, vTestAxis); |
|
if (fTestDot > fBestDot) |
|
{ |
|
fBestDot = fTestDot; |
|
vBestAxis = vTestAxis; |
|
} |
|
} |
|
|
|
return vBestAxis; |
|
} |
|
|
|
void CMapView3D::GetBestTransformPlane( Vector &horzAxis, Vector &vertAxis, Vector &thirdAxis) |
|
{ |
|
Vector vAxis; |
|
|
|
m_pCamera->GetViewRight( vAxis ); |
|
horzAxis = ClosestAxis( vAxis ); |
|
|
|
m_pCamera->GetViewUp( vAxis ); |
|
vertAxis = ClosestAxis( vAxis ); |
|
|
|
m_pCamera->GetViewForward( vAxis ); |
|
thirdAxis = ClosestAxis( vAxis ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Synchronizes the 2D camera information with the 3D view. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::UpdateCameraVariables(void) |
|
{ |
|
Camera3D *pCamTool = dynamic_cast<Camera3D*>(m_pToolManager->GetToolForID( TOOL_CAMERA )); |
|
|
|
if (!m_pCamera || !pCamTool ) |
|
return; |
|
|
|
Vector viewPoint,viewForward; |
|
|
|
m_pCamera->GetViewPoint(viewPoint); |
|
m_pCamera->GetViewForward(viewForward); |
|
|
|
// tell camera tool to update active camera |
|
pCamTool->UpdateActiveCamera( viewPoint, viewForward ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles the left mouse button double click event. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnLButtonDblClk(UINT nFlags, CPoint point) |
|
{ |
|
// |
|
// Don't forward message if we are controlling the camera. |
|
// |
|
if ((GetAsyncKeyState(VK_SPACE) & 0x8000) != 0) |
|
{ |
|
return; |
|
} |
|
|
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
Vector2D vPoint( point.x,point.y); |
|
if (pTool->OnLMouseDblClk3D( this, nFlags, vPoint )) |
|
{ |
|
return; |
|
} |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles the left mouse button down event. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnLButtonDown(UINT nFlags, CPoint point) |
|
{ |
|
if ((GetAsyncKeyState(VK_SPACE) & 0x8000) != 0) |
|
{ |
|
EnableRotating(true); |
|
return; |
|
} |
|
|
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
Vector2D vPoint( point.x,point.y); |
|
if (pTool->OnLMouseDown3D(this, nFlags, vPoint)) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
CView::OnLButtonDown(nFlags, point); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the selection tool to begin timed selection by depth. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::BeginPick(void) |
|
{ |
|
SetTimer(MVTIMER_PICKNEXT, 500, NULL); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by the selection tool to end timed selection by depth. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::EndPick(void) |
|
{ |
|
// |
|
// Kill pick timer. |
|
// |
|
KillTimer(MVTIMER_PICKNEXT); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : nFlags - |
|
// point - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnLButtonUp(UINT nFlags, CPoint point) |
|
{ |
|
if (m_bRotating) |
|
{ |
|
EnableRotating(false); |
|
UpdateCameraVariables(); |
|
return; |
|
} |
|
|
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
Vector2D vPoint( point.x,point.y); |
|
if (pTool->OnLMouseUp3D(this, nFlags, vPoint)) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
CView::OnLButtonUp(nFlags, point); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Creates the renderer and the camera and initializes them. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnInitialUpdate(void) |
|
{ |
|
InitializeKeyMap(); |
|
|
|
// |
|
// Create a title window. |
|
// |
|
m_pwndTitle = CTitleWnd::CreateTitleWnd(this, ID_2DTITLEWND); |
|
Assert(m_pwndTitle != NULL); |
|
if (m_pwndTitle != NULL) |
|
{ |
|
m_pwndTitle->SetTitle("camera"); |
|
} |
|
|
|
// |
|
// CMainFrame::LoadWindowStates calls InitialUpdateFrame which causes us to get two |
|
// OnInitialUpdate messages! Check for a NULL renderer to avoid processing twice. |
|
// |
|
if (m_pRender != NULL) |
|
{ |
|
return; |
|
} |
|
|
|
// |
|
// Create and initialize the renderer. |
|
// |
|
m_pRender = new CRender3D(); |
|
|
|
CMapDoc *pDoc = GetMapDoc(); |
|
if (pDoc == NULL) |
|
{ |
|
Assert(pDoc != NULL); |
|
return; |
|
} |
|
|
|
m_pRender->SetView( this ); |
|
|
|
m_pToolManager = pDoc->GetTools(); |
|
|
|
SetDrawType(m_eDrawType); |
|
|
|
|
|
// |
|
// Create and initialize the camera. |
|
// |
|
m_pCamera = new CCamera(); |
|
Assert(m_pCamera != NULL); |
|
if (m_pCamera == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
CRect rect; |
|
GetClientRect( rect ); |
|
m_pCamera->SetViewPort( rect.Width(), rect.Height() ); |
|
|
|
m_fForwardSpeedMax = Options.view3d.nForwardSpeedMax; |
|
m_fStrafeSpeedMax = Options.view3d.nForwardSpeedMax * 0.75f; |
|
m_fVerticalSpeedMax = Options.view3d.nForwardSpeedMax * 0.5f; |
|
|
|
// |
|
// Calculate the acceleration based on max speed and the time to max speed. |
|
// |
|
if (Options.view3d.nTimeToMaxSpeed != 0) |
|
{ |
|
m_fForwardAcceleration = m_fForwardSpeedMax / (Options.view3d.nTimeToMaxSpeed / 1000.0f); |
|
m_fStrafeAcceleration = m_fStrafeSpeedMax / (Options.view3d.nTimeToMaxSpeed / 1000.0f); |
|
m_fVerticalAcceleration = m_fVerticalSpeedMax / (Options.view3d.nTimeToMaxSpeed / 1000.0f); |
|
} |
|
else |
|
{ |
|
m_fForwardAcceleration = 0; |
|
m_fStrafeAcceleration = 0; |
|
m_fVerticalAcceleration = 0; |
|
} |
|
|
|
// |
|
// Set up the frustum. We set the vertical FOV to zero because the renderer |
|
// only uses the horizontal FOV. |
|
// |
|
if ( Options.general.bRadiusCulling ) |
|
{ |
|
// Hack! Don't use frustum culling when doing radial distance culling (slam the distance to 10K) |
|
m_pCamera->SetPerspective( Options.view3d.fFOV, CAMERA_FRONT_PLANE_DISTANCE, 10000); |
|
} |
|
else |
|
{ |
|
m_pCamera->SetPerspective( Options.view3d.fFOV, CAMERA_FRONT_PLANE_DISTANCE, Options.view3d.iBackPlane); |
|
} |
|
|
|
// |
|
// Set the distance at which studio models become bounding boxes. |
|
// |
|
CMapStudioModel::SetRenderDistance(Options.view3d.nModelDistance); |
|
CMapStudioModel::EnableAnimation(Options.view3d.bAnimateModels); |
|
|
|
// |
|
// Enable or disable reverse selection. |
|
// |
|
m_pRender->RenderEnable(RENDER_REVERSE_SELECTION, (Options.view3d.bReverseSelection == TRUE)); |
|
|
|
// |
|
// Enable or disable the 3D grid. |
|
// |
|
m_pRender->RenderEnable(RENDER_GRID, pDoc->Is3DGridEnabled()); |
|
|
|
// |
|
// Enable or disable texture filtering. |
|
// |
|
m_pRender->RenderEnable(RENDER_FILTER_TEXTURES, (Options.view3d.bFilterTextures == TRUE)); |
|
|
|
// Get the initial viewpoint and view direction from the default camera in the document. |
|
|
|
Camera3D *pCamTool = dynamic_cast<Camera3D*>(m_pToolManager->GetToolForID( TOOL_CAMERA )); |
|
if ( pCamTool ) |
|
{ |
|
Vector vecPos,vecLookAt; |
|
pCamTool->GetCameraPos( vecPos,vecLookAt ); |
|
SetCamera(vecPos, vecLookAt); |
|
} |
|
|
|
CView::OnInitialUpdate(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns on wireframe mode from the floating "Camera" menu. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnView3dWireframe(void) |
|
{ |
|
SetDrawType(VIEW3D_WIREFRAME); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns on flat shaded mode from the floating "Camera" menu. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnView3dPolygon(void) |
|
{ |
|
SetDrawType(VIEW3D_POLYGON); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns on textured mode from the floating "Camera" menu. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnView3dTextured(void) |
|
{ |
|
SetDrawType(VIEW3D_TEXTURED); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns on lightmap grid mode from the floating "Camera" menu. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnView3dLightmapGrid(void) |
|
{ |
|
SetDrawType(VIEW3D_LIGHTMAP_GRID); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns on lighting preview mode from the floating "Camera" menu. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnView3dLightingPreview(void) |
|
{ |
|
SetDrawType(VIEW3D_LIGHTING_PREVIEW2); |
|
} |
|
|
|
void CMapView3D::OnView3dLightingPreviewRayTraced(void) |
|
{ |
|
SetDrawType(VIEW3D_LIGHTING_PREVIEW_RAYTRACED); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Turns on engine mode from the floating "Camera" menu. |
|
//----------------------------------------------------------------------------- |
|
//void CMapView3D::OnView3dEngine(void) |
|
//{ |
|
// SetDrawType(VIEW3D_ENGINE); |
|
//} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : bActivate - |
|
// pActivateView - |
|
// pDeactiveView - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ActivateView(bool bActivate) |
|
{ |
|
CMapView::ActivateView(bActivate); |
|
|
|
if (bActivate) |
|
{ |
|
CMapDoc *pDoc = GetMapDoc(); |
|
CMapDoc::SetActiveMapDoc(pDoc); |
|
|
|
UpdateStatusBar(); |
|
|
|
// tell doc to update title |
|
pDoc->UpdateTitle(this); |
|
|
|
m_Keyboard.ClearKeyStates(); |
|
|
|
// |
|
// Reset the last input sample time. |
|
// |
|
m_dwTimeLastInputSample = 0; |
|
} |
|
} |
|
|
|
void CMapView3D::OnDraw(CDC *pDC) |
|
{ |
|
CWnd *focusWnd = GetForegroundWindow(); |
|
|
|
if ( focusWnd && focusWnd->ContinueModal() ) |
|
{ |
|
// render the view now since were not running the main loop |
|
RenderView(); |
|
} |
|
else |
|
{ |
|
// just flag view to be update with next main loop |
|
m_bUpdateView = true; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::RenderView() |
|
{ |
|
Render(); |
|
m_bUpdateView = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
bool CMapView3D::ShouldRender() |
|
{ |
|
if ( m_eDrawType == VIEW3D_LIGHTING_PREVIEW_RAYTRACED ) |
|
{ |
|
// check if we have new results from lpreview thread |
|
// if ( m_nLastRaytracedBitmapRenderTimeStamp != |
|
// GetUpdateCounter( EVTYPE_BITMAP_RECEIVED_FROM_LPREVIEW ) ) |
|
// return true; |
|
} |
|
else |
|
{ |
|
// don't animate ray traced displays |
|
if ( Options.view3d.bAnimateModels ) |
|
{ |
|
DWORD dwTimeElapsed = timeGetTime() - m_dwTimeLastRender; |
|
|
|
if ( (dwTimeElapsed/1000.0f) > 1.0f/20.0f) |
|
{ |
|
m_bUpdateView = true; |
|
} |
|
} |
|
} |
|
return CMapView::ShouldRender(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pDC - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnSetFocus(CWnd *pOldWnd) |
|
{ |
|
// Make sure the whole window region is marked as invalid |
|
m_bUpdateView = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called to paint the non client area of the window. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnNcPaint(void) |
|
{ |
|
// Make sure the whole window region is marked as invalid |
|
m_bUpdateView = true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : pSender - |
|
// lHint - |
|
// pHint - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::UpdateView(int nFlags) |
|
{ |
|
if ( !m_pRender ) |
|
return; |
|
|
|
if (nFlags & ( MAPVIEW_UPDATE_ONLY_2D | MAPVIEW_UPDATE_ONLY_LOGICAL ) ) |
|
return; |
|
|
|
// |
|
// One of the options in the 3D options page is changing. |
|
// |
|
if (nFlags & MAPVIEW_OPTIONS_CHANGED) |
|
{ |
|
InitializeKeyMap(); |
|
|
|
CMapStudioModel::SetRenderDistance(Options.view3d.nModelDistance); |
|
CMapStudioModel::EnableAnimation(Options.view3d.bAnimateModels); |
|
|
|
m_pRender->RenderEnable(RENDER_REVERSE_SELECTION, (Options.view3d.bReverseSelection == TRUE)); |
|
|
|
m_fForwardSpeedMax = Options.view3d.nForwardSpeedMax; |
|
m_fStrafeSpeedMax = Options.view3d.nForwardSpeedMax * 0.75f; |
|
m_fVerticalSpeedMax = Options.view3d.nForwardSpeedMax * 0.5f; |
|
|
|
// |
|
// Calculate the acceleration based on max speed and the time to max speed. |
|
// |
|
if (Options.view3d.nTimeToMaxSpeed != 0) |
|
{ |
|
m_fForwardAcceleration = m_fForwardSpeedMax / (Options.view3d.nTimeToMaxSpeed / 1000.0f); |
|
m_fStrafeAcceleration = m_fStrafeSpeedMax / (Options.view3d.nTimeToMaxSpeed / 1000.0f); |
|
m_fVerticalAcceleration = m_fVerticalSpeedMax / (Options.view3d.nTimeToMaxSpeed / 1000.0f); |
|
} |
|
else |
|
{ |
|
m_fForwardAcceleration = 0; |
|
m_fStrafeAcceleration = 0; |
|
m_fVerticalAcceleration = 0; |
|
} |
|
|
|
m_pCamera->SetPerspective( Options.view3d.fFOV, CAMERA_FRONT_PLANE_DISTANCE, Options.view3d.iBackPlane); |
|
|
|
CMapDoc *pDoc = GetMapDoc(); |
|
if ((pDoc != NULL) && (m_pRender != NULL)) |
|
{ |
|
m_pRender->RenderEnable(RENDER_GRID, pDoc->Is3DGridEnabled()); |
|
m_pRender->RenderEnable(RENDER_FILTER_TEXTURES, (Options.view3d.bFilterTextures == TRUE)); |
|
} |
|
} |
|
|
|
if (nFlags & MAPVIEW_UPDATE_OBJECTS) |
|
{ |
|
// dvs: could use this hint to update the octree |
|
} |
|
|
|
CMapView::UpdateView( nFlags ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Determines the object at the point (point.x, point.y) in the 3D view. |
|
// Input : point - Point to use for hit test. |
|
// ulFace - Index of face in object that was hit. |
|
// Output : Returns a pointer to the CMapClass object at the coordinates, NULL if none. |
|
//----------------------------------------------------------------------------- |
|
CMapClass *CMapView3D::NearestObjectAt( const Vector2D &vPoint, ULONG &ulFace, unsigned int nFlags, VMatrix *pLocalMatrix ) |
|
{ |
|
ulFace = 0; |
|
if (m_pRender == NULL) |
|
{ |
|
return(NULL); |
|
} |
|
|
|
HitInfo_t Hits; |
|
|
|
if (m_pRender->ObjectsAt( vPoint.x, vPoint.y, 1, 1, &Hits, 1, nFlags ) != 0) |
|
{ |
|
// |
|
// If they clicked on a solid, the index of the face they clicked on is stored |
|
// in array index [1]. |
|
// |
|
CMapAtom *pObject = (CMapAtom *)Hits.pObject; |
|
CMapSolid *pSolid = dynamic_cast<CMapSolid *>(pObject); |
|
if ( pLocalMatrix != NULL ) |
|
{ |
|
*pLocalMatrix = Hits.m_LocalMatrix; |
|
} |
|
if (pSolid != NULL) |
|
{ |
|
ulFace = Hits.uData; |
|
return(pSolid); |
|
} |
|
|
|
return((CMapClass *)pObject); |
|
} |
|
|
|
return(NULL); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Casts a ray from the viewpoint through the given plane and determines |
|
// the point of intersection of the ray on the plane. |
|
// Input : point - Point in client screen coordinates. |
|
// plane - Plane being 'clicked' on. |
|
// pos - Returns the point on the plane that projects to the given point. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::GetHitPos(const Vector2D &point, PLANE &plane, Vector &pos) |
|
{ |
|
// |
|
// Find the point they clicked on in world coordinates. It lies on the near |
|
// clipping plane. |
|
// |
|
Vector ClickPoint; |
|
|
|
ClientToWorld( ClickPoint, point ); |
|
|
|
// |
|
// Build a ray from the viewpoint through the point on the near clipping plane. |
|
// |
|
Vector ViewPoint; |
|
Vector Ray; |
|
m_pCamera->GetViewPoint(ViewPoint); |
|
VectorSubtract(ClickPoint, ViewPoint, Ray); |
|
|
|
// |
|
// Find the point of intersection of the ray with the given plane. |
|
// |
|
float t = DotProduct(plane.normal, ViewPoint) - plane.dist; |
|
t = t / -DotProduct(plane.normal, Ray); |
|
|
|
pos = ViewPoint + t * Ray; |
|
} |
|
|
|
bool CMapView3D::HitTest( const Vector2D &vPoint, const Vector& mins, const Vector& maxs) |
|
{ |
|
Vector vStart, vEnd; |
|
int nFace; |
|
BuildRay( vPoint, vStart, vEnd ); |
|
return IntersectionLineAABBox( mins, maxs, vStart, vEnd, nFace ) >= 0.0f; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Finds all objects under the given rectangular region in the view. |
|
// Input : x - Client x coordinate. |
|
// y - Client y coordinate. |
|
// fWidth - Width of region in client pixels. |
|
// fHeight - Height of region in client pixels. |
|
// pObjects - Receives objects in the given region. |
|
// nMaxObjects - Size of the array pointed to by pObjects. |
|
// Output : Returns the number of objects in the given region. |
|
//----------------------------------------------------------------------------- |
|
int CMapView3D::ObjectsAt( const Vector2D &vPoint, HitInfo_t *pObjects, int nMaxObjects, unsigned int nFlags ) |
|
{ |
|
if (m_pRender != NULL) |
|
{ |
|
return m_pRender->ObjectsAt( vPoint.x, vPoint.y, 1, 1, pObjects, nMaxObjects, nFlags ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Makes sure that this view has focus if the mouse moves over it. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnMouseMove(UINT nFlags, CPoint point) |
|
{ |
|
// |
|
// Make sure we are the active view. |
|
// |
|
if (!IsActive()) |
|
{ |
|
CMapDoc *pDoc = GetMapDoc(); |
|
pDoc->SetActiveView(this); |
|
} |
|
|
|
// |
|
// If we are the active application, make sure this view has the input focus. |
|
// |
|
if (APP()->IsActiveApp()) |
|
{ |
|
if (GetFocus() != this) |
|
{ |
|
SetFocus(); |
|
} |
|
} |
|
|
|
|
|
CView::OnMouseMove(nFlags, point); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ProcessInput(void) |
|
{ |
|
if (m_dwTimeLastInputSample == 0) |
|
{ |
|
m_dwTimeLastInputSample = timeGetTime(); |
|
} |
|
|
|
DWORD dwTimeNow = timeGetTime(); |
|
|
|
float fElapsedTime = (float)(dwTimeNow - m_dwTimeLastInputSample) / 1000.0f; |
|
|
|
m_dwTimeLastInputSample = dwTimeNow; |
|
|
|
// Clamp (can get really big when we cache textures in ) |
|
if (fElapsedTime > 0.3f) |
|
{ |
|
fElapsedTime = 0.3f; |
|
} |
|
else if ( fElapsedTime <=0 ) |
|
{ |
|
return; // dont process input |
|
} |
|
|
|
ProcessKeys( fElapsedTime ); |
|
|
|
ProcessMouse(); |
|
|
|
if ( Options.general.bRadiusCulling ) |
|
{ |
|
ProcessCulling(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Applies an acceleration to a velocity, allowing instantaneous direction |
|
// change and zeroing the velocity in the absence of acceleration. |
|
// Input : fVelocity - Current velocity. |
|
// fAccel - Amount of acceleration to apply. |
|
// fTimeScale - The time for which the acceleration should be applied. |
|
// fMaxVelocity - The maximum velocity to allow. |
|
// Output : Returns the new velocity. |
|
//----------------------------------------------------------------------------- |
|
static float Accelerate(float fVelocity, float fAccel, float fAccelScale, float fTimeScale, float fVelocityMax) |
|
{ |
|
// |
|
// If we have a finite acceleration in this direction, apply it to the velocity. |
|
// |
|
if ((fAccel != 0) && (fAccelScale != 0)) |
|
{ |
|
// |
|
// Check for direction reversal - zero velocity when reversing. |
|
// |
|
if (fAccelScale > 0) |
|
{ |
|
if (fVelocity < 0) |
|
{ |
|
fVelocity = 0; |
|
} |
|
} |
|
else if (fAccelScale < 0) |
|
{ |
|
if (fVelocity > 0) |
|
{ |
|
fVelocity = 0; |
|
} |
|
} |
|
|
|
// |
|
// Apply the acceleration. |
|
// |
|
fVelocity += fAccel * fAccelScale * fTimeScale; |
|
if (fVelocity > fVelocityMax) |
|
{ |
|
fVelocity = fVelocityMax; |
|
} |
|
else if (fVelocity < -fVelocityMax) |
|
{ |
|
fVelocity = -fVelocityMax; |
|
} |
|
|
|
} |
|
// |
|
// If we have infinite acceleration, go straight to maximum velocity. |
|
// |
|
else if (fAccelScale != 0) |
|
{ |
|
fVelocity = fVelocityMax * fAccelScale; |
|
} |
|
// |
|
// Else no velocity in this direction at all. |
|
// |
|
else |
|
{ |
|
fVelocity = 0; |
|
} |
|
|
|
return(fVelocity); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Moves the camera based on the keyboard state. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ProcessMovementKeys(float fElapsedTime) |
|
{ |
|
// |
|
// Read the state of the camera movement keys. |
|
// |
|
float fBack = m_Keyboard.GetKeyScale(LOGICAL_KEY_BACK); |
|
float fMoveForward = m_Keyboard.GetKeyScale(LOGICAL_KEY_FORWARD) - fBack; |
|
|
|
float fLeft = m_Keyboard.GetKeyScale(LOGICAL_KEY_LEFT); |
|
float fMoveRight = m_Keyboard.GetKeyScale(LOGICAL_KEY_RIGHT) - fLeft; |
|
|
|
float fDown = m_Keyboard.GetKeyScale(LOGICAL_KEY_DOWN); |
|
float fMoveUp = m_Keyboard.GetKeyScale(LOGICAL_KEY_UP) - fDown; |
|
|
|
float fPitchUp = m_Keyboard.GetKeyScale(LOGICAL_KEY_PITCH_UP); |
|
float fPitchDown = m_Keyboard.GetKeyScale(LOGICAL_KEY_PITCH_DOWN); |
|
|
|
float fYawLeft = m_Keyboard.GetKeyScale(LOGICAL_KEY_YAW_LEFT); |
|
float fYawRight = m_Keyboard.GetKeyScale(LOGICAL_KEY_YAW_RIGHT); |
|
|
|
// |
|
// Apply pitch and yaw if they are nonzero. |
|
// |
|
if ((fPitchDown - fPitchUp) != 0) |
|
{ |
|
m_pCamera->Pitch((fPitchDown - fPitchUp) * fElapsedTime * PITCH_SPEED); |
|
m_bUpdateView = true; |
|
} |
|
|
|
if ((fYawRight - fYawLeft) != 0) |
|
{ |
|
m_pCamera->Yaw((fYawRight - fYawLeft) * fElapsedTime * YAW_SPEED); |
|
m_bUpdateView = true; |
|
} |
|
|
|
// |
|
// Apply the accelerations to the forward, strafe, and vertical speeds. They are actually |
|
// velocities because they are signed values. |
|
// |
|
m_fForwardSpeed = Accelerate(m_fForwardSpeed, m_fForwardAcceleration, fMoveForward, fElapsedTime, m_fForwardSpeedMax); |
|
m_fStrafeSpeed = Accelerate(m_fStrafeSpeed, m_fStrafeAcceleration, fMoveRight, fElapsedTime, m_fStrafeSpeedMax); |
|
m_fVerticalSpeed = Accelerate(m_fVerticalSpeed, m_fVerticalAcceleration, fMoveUp, fElapsedTime, m_fVerticalSpeedMax); |
|
|
|
// |
|
// Move the camera if any of the speeds are nonzero. |
|
// |
|
if (m_fForwardSpeed != 0) |
|
{ |
|
m_pCamera->MoveForward(m_fForwardSpeed * fElapsedTime); |
|
m_bUpdateView = true; |
|
m_bCameraPosChanged = true; |
|
} |
|
|
|
if (m_fStrafeSpeed != 0) |
|
{ |
|
m_pCamera->MoveRight(m_fStrafeSpeed * fElapsedTime); |
|
m_bUpdateView = true; |
|
m_bCameraPosChanged = true; |
|
} |
|
|
|
if (m_fVerticalSpeed != 0) |
|
{ |
|
m_pCamera->MoveUp(m_fVerticalSpeed * fElapsedTime); |
|
m_bUpdateView = true; |
|
m_bCameraPosChanged = true; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ProcessKeys(float fElapsedTime) |
|
{ |
|
ProcessMovementKeys(fElapsedTime); |
|
|
|
m_Keyboard.ClearImpulseFlags(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ProcessCulling( void ) |
|
{ |
|
if ( m_bCameraPosChanged || m_bClippingChanged ) |
|
{ |
|
CMapDoc *pDoc = GetMapDoc(); |
|
pDoc->UpdateVisibilityAll(); |
|
|
|
m_bClippingChanged = false; |
|
m_bCameraPosChanged = false; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CMapView3D::ControlCamera(const CPoint &point) |
|
{ |
|
if (!m_bStrafing && !m_bRotating && !m_bMouseLook) |
|
{ |
|
return false; |
|
} |
|
|
|
bool bShift = ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0); |
|
|
|
CRect rect; |
|
GetClientRect(&rect); |
|
// get mouse distance to client window center |
|
CPoint WindowCenter = rect.CenterPoint(); |
|
CSize MouseLookDelta = point - WindowCenter; |
|
|
|
// camera look is on, but no mouse changes |
|
if ( MouseLookDelta.cx == 0 && MouseLookDelta.cy == 0 ) |
|
true; |
|
|
|
// |
|
// If strafing, left-right movement moves the camera from side to side. |
|
// Up-down movement either moves the camera forward and back if the SHIFT |
|
// key is held down, or up and down if the SHIFT key is not held down. |
|
// If rotating and strafing simultaneously, the behavior is as if SHIFT is |
|
// held down. |
|
// |
|
if (m_bStrafing) |
|
{ |
|
if (bShift || m_bRotating) |
|
{ |
|
MoveForward(-MouseLookDelta.cy * 2); |
|
} |
|
else |
|
{ |
|
MoveUp(-MouseLookDelta.cy * 2); |
|
} |
|
|
|
MoveRight(MouseLookDelta.cx * 2); |
|
|
|
m_bCameraPosChanged = true; |
|
} |
|
// |
|
// If mouse looking, left-right movement controls yaw, and up-down |
|
// movement controls pitch. |
|
// |
|
else |
|
{ |
|
// |
|
// Up-down mouse movement changes the camera pitch. |
|
// |
|
if (MouseLookDelta.cy) |
|
{ |
|
float fTheta = MouseLookDelta.cy * 0.4; |
|
if (Options.view3d.bReverseY) |
|
{ |
|
fTheta = -fTheta; |
|
} |
|
|
|
Pitch(fTheta); |
|
} |
|
|
|
// |
|
// Left-right mouse movement changes the camera yaw. |
|
// |
|
if (MouseLookDelta.cx) |
|
{ |
|
float fTheta = MouseLookDelta.cx * 0.4; |
|
Yaw(fTheta); |
|
} |
|
} |
|
|
|
// move mouse back to center |
|
CWnd::ClientToScreen(&WindowCenter); |
|
SetCursorPos(WindowCenter.x, WindowCenter.y); |
|
m_bUpdateView = true; |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called by RunFrame to tell this view to process mouse input. This |
|
// function samples the cursor position and takes the appropriate |
|
// action based on the current mode (camera, morphing). |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ProcessMouse(void) |
|
{ |
|
// |
|
// Get the cursor position in client coordinates. |
|
// |
|
|
|
CPoint point; |
|
GetCursorPos(&point); |
|
ScreenToClient(&point); |
|
|
|
if ( point == m_ptLastMouseMovement ) |
|
return; |
|
|
|
m_ptLastMouseMovement = point; |
|
|
|
if ( ControlCamera( point ) ) |
|
{ |
|
return; |
|
} |
|
|
|
// If not in mouselook mode, only process mouse messages if there |
|
// is an active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
// |
|
// Pass the message to the tool. |
|
// |
|
|
|
int nFlags = 0; |
|
|
|
if ((GetAsyncKeyState(VK_CONTROL) & 0x8000) != 0) |
|
{ |
|
nFlags |= MK_CONTROL; |
|
} |
|
|
|
if ((GetAsyncKeyState(VK_SHIFT) & 0x8000) != 0) |
|
{ |
|
nFlags |= MK_SHIFT; |
|
} |
|
|
|
Vector2D vPoint( point.x,point.y); |
|
pTool->OnMouseMove3D(this, nFlags, vPoint); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles mouse wheel events. The mouse wheel is used in camera mode |
|
// to dolly the camera forward and back. |
|
// Input : Per CWnd::OnMouseWheel. |
|
//----------------------------------------------------------------------------- |
|
BOOL CMapView3D::OnMouseWheel(UINT nFlags, short zDelta, CPoint point) |
|
{ |
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
Vector2D vPoint( point.x,point.y); |
|
if (pTool->OnMouseWheel3D(this, nFlags, zDelta, vPoint)) |
|
{ |
|
return(TRUE); |
|
} |
|
} |
|
|
|
m_pCamera->MoveForward(zDelta / 2); |
|
|
|
// |
|
// Render now to avoid an ugly lag between the 2D views and the 3D view |
|
// when "center 2D views on camera" is enabled. |
|
// |
|
m_bUpdateView = true; |
|
m_bCameraPosChanged = true; |
|
|
|
UpdateCameraVariables(); |
|
|
|
return CView::OnMouseWheel(nFlags, zDelta, point); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles right mouse button down events. |
|
// Input : Per CWnd::OnRButtonDown. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnRButtonDown(UINT nFlags, CPoint point) |
|
{ |
|
if ((GetAsyncKeyState(VK_SPACE) & 0x8000) != 0) |
|
{ |
|
EnableStrafing(true); |
|
return; |
|
} |
|
|
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
Vector2D vPoint( point.x,point.y); |
|
if (pTool->OnRMouseDown3D( this, nFlags, vPoint )) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
CView::OnRButtonDown(nFlags, point); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles right mouse button up events. |
|
// Input : Per CWnd::OnRButtonUp. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnRButtonUp(UINT nFlags, CPoint point) |
|
{ |
|
if (m_bStrafing) |
|
{ |
|
// |
|
// Turn off strafing and update the 2D views. |
|
// |
|
EnableStrafing(false); |
|
UpdateCameraVariables(); |
|
return; |
|
} |
|
|
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
Vector2D vPoint( point.x,point.y); |
|
if (pTool->OnRMouseUp3D( this, nFlags, vPoint )) |
|
{ |
|
return; |
|
} |
|
} |
|
|
|
CView::OnRButtonUp(nFlags, point); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles character events. |
|
// Input : Per CWnd::OnChar. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) |
|
{ |
|
// Got to check for m_pToolManager here because otherwise it can crash on startup if they have keys pressed. |
|
if ( m_pToolManager ) |
|
{ |
|
// |
|
// Pass the message to the active tool. |
|
// |
|
CBaseTool *pTool = m_pToolManager->GetActiveTool(); |
|
if (pTool != NULL) |
|
{ |
|
if (pTool->OnChar3D(this, nChar, nRepCnt, nFlags)) |
|
{ |
|
return; |
|
} |
|
} |
|
} |
|
|
|
CView::OnChar(nChar, nRepCnt, nFlags); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called when mouselook is enabled. The cursor is moved to the center |
|
// of the screen and hidden. |
|
// Input : bEnable - true to lock and hide the cursor, false to unlock and show it. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::EnableCrosshair(bool bEnable) |
|
{ |
|
CRect Rect; |
|
CPoint Point; |
|
|
|
GetClientRect(&Rect); |
|
CWnd::ClientToScreen(&Rect); |
|
Point = Rect.CenterPoint(); |
|
SetCursorPos(Point.x, Point.y); |
|
|
|
if (bEnable) |
|
{ |
|
ClipCursor(&Rect); |
|
} |
|
else |
|
{ |
|
ClipCursor(NULL); |
|
} |
|
|
|
ShowCursor(bEnable ? FALSE : TRUE); |
|
m_pRender->RenderEnable(RENDER_CENTER_CROSSHAIR, bEnable); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Enables or disables mouselook. When mouselooking, the cursor is hidden |
|
// and a crosshair is rendered in the center of the view. |
|
// Input : bEnable - TRUE to enable, FALSE to disable mouselook. |
|
//----------------------------------------------------------------------------- |
|
//void CMapView3D::EnableMouseLook(bool bEnable) |
|
//{ |
|
// if (m_bMouseLook != bEnable) |
|
// { |
|
// CMapDoc *pDoc = GetDocument(); |
|
// if (pDoc != NULL) |
|
// { |
|
// EnableCrosshair(bEnable); |
|
// m_bMouseLook = bEnable; |
|
// } |
|
// } |
|
//} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Enables or disables mouselook. When mouselooking, the cursor is hidden |
|
// and a crosshair is rendered in the center of the view. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::EnableMouseLook(bool bEnable) |
|
{ |
|
if (m_bMouseLook != bEnable) |
|
{ |
|
if (!(m_bStrafing || m_bRotating)) |
|
{ |
|
EnableCrosshair(bEnable); |
|
} |
|
|
|
m_bMouseLook = bEnable; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Enables or disables camera rotating. When rotating, the cursor is hidden |
|
// and a crosshair is rendered in the center of the view. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::EnableRotating(bool bEnable) |
|
{ |
|
if (m_bRotating != bEnable) |
|
{ |
|
if (!(m_bStrafing || m_bMouseLook)) |
|
{ |
|
EnableCrosshair(bEnable); |
|
} |
|
|
|
m_bRotating = bEnable; |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Enables or disables camera strafing. When strafing, the cursor is hidden |
|
// and a crosshair is rendered in the center of the view. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::EnableStrafing(bool bEnable) |
|
{ |
|
if (m_bStrafing != bEnable) |
|
{ |
|
if (!(m_bMouseLook || m_bRotating)) |
|
{ |
|
EnableCrosshair(bEnable); |
|
} |
|
|
|
m_bStrafing = bEnable; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Actually renders the 3D view. Called from the frame loop and from |
|
// some mouse messages when timely updating is important. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::Render(void) |
|
{ |
|
if ( m_pRender != NULL ) |
|
{ |
|
m_pRender->Render(); |
|
} |
|
|
|
if (m_pwndTitle != NULL) |
|
{ |
|
m_pwndTitle->BringWindowToTop(); |
|
m_pwndTitle->Invalidate(); |
|
m_pwndTitle->UpdateWindow(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *pObject - |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::RenderPreloadObject(CMapAtom *pObject) |
|
{ |
|
if ((pObject != NULL) && (m_pRender != NULL)) |
|
{ |
|
pObject->RenderPreload(m_pRender, false); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Release all video memory. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::ReleaseVideoMemory(void) |
|
{ |
|
m_pRender->UncacheAllTextures(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Moves the camera forward by flDistance units. Negative units move back. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::MoveForward(float flDistance) |
|
{ |
|
if (m_pCamera != NULL) |
|
{ |
|
m_pCamera->MoveForward(flDistance); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Moves the camera up by flDistance units. Negative units move down. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::MoveUp(float flDistance) |
|
{ |
|
if (m_pCamera != NULL) |
|
{ |
|
m_pCamera->MoveUp(flDistance); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Moves the camera right by flDistance units. Negative units move left. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::MoveRight(float flDistance) |
|
{ |
|
if (m_pCamera != NULL) |
|
{ |
|
m_pCamera->MoveRight(flDistance); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Pitches the camera forward by flDegrees degrees. Negative units pitch back. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::Pitch(float flDegrees) |
|
{ |
|
if (m_pCamera != NULL) |
|
{ |
|
m_pCamera->Pitch(flDegrees); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Yaws the camera left by flDegrees degrees. Negative units yaw right. |
|
//----------------------------------------------------------------------------- |
|
void CMapView3D::Yaw(float flDegrees) |
|
{ |
|
if (m_pCamera != NULL) |
|
{ |
|
m_pCamera->Yaw(flDegrees); |
|
} |
|
} |
|
|
|
|
|
|
|
void CMapView3D::WorldToClient(Vector2D &vClient, const Vector &vWorld) |
|
{ |
|
m_pCamera->WorldToView( vWorld, vClient ); |
|
} |
|
|
|
void CMapView3D::ClientToWorld(Vector &vWorld, const Vector2D &vClient) |
|
{ |
|
m_pCamera->ViewToWorld( vClient, vWorld ); |
|
} |
|
|
|
void CMapView3D::SetCursor( vgui::HCursor hCursor ) |
|
{ |
|
// translate VGUI -> GDI cursors |
|
switch( hCursor ) |
|
{ |
|
case vgui::dc_arrow : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); break; |
|
case vgui::dc_sizenwse : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENWSE)); break; |
|
case vgui::dc_sizenesw : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENESW)); break; |
|
case vgui::dc_sizewe : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEWE)); break; |
|
case vgui::dc_sizens : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZENS)); break; |
|
case vgui::dc_sizeall : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEALL)); break; |
|
case vgui::dc_crosshair : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS)); break; |
|
default : ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW)); break; |
|
} |
|
} |
|
|
|
|
|
LRESULT CMapView3D::WindowProc( UINT message, WPARAM wParam, LPARAM lParam ) |
|
{ |
|
switch ( message ) |
|
{ |
|
case WM_KILLFOCUS: |
|
m_Keyboard.ClearKeyStates(); |
|
// Msg( mwStatus, "debug: lost focus, clearing key states\n"); |
|
break; |
|
} |
|
|
|
return CView::WindowProc( message, wParam, lParam ) ; |
|
} |