//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include "stdafx.h" #include "ToolCamera.h" #include "SaveInfo.h" #include "MainFrm.h" // dvs: remove? #include "MapDefs.h" #include "MapDoc.h" #include "MapView2D.h" #include "MapView3D.h" #include "Options.h" #include "Render2D.h" #include "StatusBarIDs.h" // dvs: remove #include "ToolManager.h" #include "hammer_mathlib.h" #include "vgui/Cursor.h" #include "Selection.h" // memdbgon must be the last include file in a .cpp file!!! #include #pragma warning(disable:4244) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- Camera3D::Camera3D(void) { Cameras.EnsureCapacity(16); SetEmpty(); } //----------------------------------------------------------------------------- // Purpose: Returns true if we are dragging a camera, false if not. // dvs: rename //----------------------------------------------------------------------------- bool Camera3D::IsEmpty(void) { return (Cameras.Count() == 0); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Camera3D::SetEmpty(void) { Cameras.RemoveAll(); m_iActiveCamera = -1; } //----------------------------------------------------------------------------- // Purpose: // Input : pt - // BOOL - // Output : int //----------------------------------------------------------------------------- int Camera3D::HitTest(CMapView *pView, const Vector2D &ptClient, bool bTestHandles) { for(int i = 0; i < Cameras.Count(); i++) { for ( int j=0; j<2; j++ ) { if( HitRect( pView, ptClient, Cameras[i].position[j], HANDLE_RADIUS ) ) { return MAKELONG(i+1, j); } } } return FALSE; } //----------------------------------------------------------------------------- // Purpose: Get rid of extra cameras if we have too many. //----------------------------------------------------------------------------- void Camera3D::EnsureMaxCameras() { int nMax = max( Options.general.nMaxCameras, 1 ); int nToRemove = Cameras.Count() - nMax; if ( nToRemove > 0 ) { m_iActiveCamera = max( m_iActiveCamera - nToRemove, 0 ); while ( nToRemove-- ) Cameras.Remove( 0 ); } } //----------------------------------------------------------------------------- // Purpose: // Input : bSave - //----------------------------------------------------------------------------- void Camera3D::FinishTranslation(bool bSave) { if (bSave) { if ( m_iActiveCamera == Cameras.Count() ) { Cameras.AddToTail(); EnsureMaxCameras(); } Cameras[m_iActiveCamera] = m_MoveCamera; } Tool3D::FinishTranslation(bSave); } //----------------------------------------------------------------------------- // Purpose: // Input : pt - // uFlags - // CSize& - // Output : Returns TRUE on success, FALSE on failure. //----------------------------------------------------------------------------- bool Camera3D::UpdateTranslation(const Vector &vUpdate, UINT uFlags) { Vector vCamDelta = m_MoveCamera.position[1] - m_MoveCamera.position[0]; Vector vNewPos = m_vOrgPos + vUpdate; // snap point if need be if ( uFlags & constrainSnap ) m_pDocument->Snap( vNewPos, uFlags ); m_MoveCamera.position[m_nMovePositionIndex] = vNewPos; if(uFlags & constrainMoveAll) { m_MoveCamera.position[(m_nMovePositionIndex+1)%2] = vNewPos + vCamDelta; } m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); return true; } //----------------------------------------------------------------------------- // Purpose: // Input : pCamPos - // iCamera - //----------------------------------------------------------------------------- void Camera3D::GetCameraPos(Vector &vViewPos, Vector &vLookAt) { if(!inrange(m_iActiveCamera, 0, Cameras.Count())) { vViewPos.Init(); vLookAt.Init(); return; } vViewPos = Cameras[m_iActiveCamera].position[0]; vLookAt = Cameras[m_iActiveCamera].position[1]; } //----------------------------------------------------------------------------- // Purpose: // Input : pCamPos - // iCamera - //----------------------------------------------------------------------------- void Camera3D::AddCamera(CAMSTRUCT &camera) { Cameras.AddToTail( camera ); EnsureMaxCameras(); } //----------------------------------------------------------------------------- // Purpose: // Input : pRender - //----------------------------------------------------------------------------- void Camera3D::RenderTool2D(CRender2D *pRender) { for (int i = 0; i < Cameras.Count(); i++) { CAMSTRUCT *pDrawCam = &Cameras[i]; if (IsTranslating() && (i == m_iActiveCamera)) { pDrawCam = &m_MoveCamera; } // // Draw the line between. // if (i == m_iActiveCamera) { pRender->SetDrawColor( 255, 0, 0 ); } else { pRender->SetDrawColor( 0, 255, 255 ); } pRender->DrawLine( pDrawCam->position[MovePos], pDrawCam->position[MoveLook] ); // // Draw camera handle. // pRender->SetHandleStyle(HANDLE_RADIUS, CRender::HANDLE_CIRCLE ); pRender->SetHandleColor( 0, 255, 255 ); pRender->DrawHandle( pDrawCam->position[MovePos] ); } } //----------------------------------------------------------------------------- // Purpose: Handles key values being read from the MAP file. // Input : szKey - Key being loaded. // szValue - Value of the key being loaded. // pCam - Camera structure to place the values into. // Output : Returns ChunkFile_Ok to indicate success. //----------------------------------------------------------------------------- ChunkFileResult_t Camera3D::LoadCameraKeyCallback(const char *szKey, const char *szValue, CAMSTRUCT *pCam) { if (!stricmp(szKey, "look")) { CChunkFile::ReadKeyValueVector3(szValue, pCam->position[MoveLook]); } else if (!stricmp(szKey, "position")) { CChunkFile::ReadKeyValueVector3(szValue, pCam->position[MovePos]); } return(ChunkFile_Ok); } //----------------------------------------------------------------------------- // Purpose: Handles key values being read from the MAP file. // Input : szKey - Key being loaded. // szValue - Value of the key being loaded. // pCam - Camera structure to place the values into. // Output : Returns ChunkFile_Ok to indicate success. //----------------------------------------------------------------------------- ChunkFileResult_t Camera3D::LoadCamerasKeyCallback(const char *szKey, const char *szValue, Camera3D *pCameras) { if (!stricmp(szKey, "activecamera")) { pCameras->m_iActiveCamera = atoi(szValue); } return(ChunkFile_Ok); } //----------------------------------------------------------------------------- // Purpose: // Input : *pLoadInfo - // *pSolid - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t Camera3D::LoadCameraCallback(CChunkFile *pFile, Camera3D *pCameras) { CAMSTRUCT Cam; memset(&Cam, 0, sizeof(Cam)); ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadCameraKeyCallback, &Cam); if (eResult == ChunkFile_Ok) { pCameras->AddCamera( Cam ); } return(eResult); } //----------------------------------------------------------------------------- // Purpose: // Input : *pFile - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t Camera3D::LoadVMF(CChunkFile *pFile) { // // Set up handlers for the subchunks that we are interested in. // CChunkHandlerMap Handlers; Handlers.AddHandler("camera", (ChunkHandler_t)LoadCameraCallback, this); pFile->PushHandlers(&Handlers); ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadCamerasKeyCallback, this); pFile->PopHandlers(); if (eResult == ChunkFile_Ok) { // // Make sure the active camera is legal. // if (Cameras.Count() == 0) { m_iActiveCamera = -1; } else if (!inrange(m_iActiveCamera, 0, Cameras.Count())) { m_iActiveCamera = 0; } } return(eResult); } //----------------------------------------------------------------------------- // Purpose: // Input : &dir - // &pos - //----------------------------------------------------------------------------- void Camera3D::UpdateActiveCamera(Vector &vViewPos, Vector &vDir) { if(!inrange(m_iActiveCamera, 0, Cameras.Count())) return; Vector& camPos = Cameras[m_iActiveCamera].position[MovePos]; Vector& lookPos = Cameras[m_iActiveCamera].position[MoveLook]; // get current length Vector delta; for(int i = 0; i < 3; i++) delta[i] = camPos[i] - lookPos[i]; float length = VectorLength(delta); if ( length < 1 ) length = 1; camPos = vViewPos; for(int i = 0; i < 3; i++) lookPos[i] = camPos[i] + vDir[i] * length; if ( IsActiveTool() ) { if (Options.view2d.bCenteroncamera) { VIEW2DINFO vi; vi.wFlags = VI_CENTER; vi.ptCenter = vViewPos; m_pDocument->SetView2dInfo(vi); } m_pDocument->UpdateAllViews( MAPVIEW_UPDATE_TOOL ); } } //----------------------------------------------------------------------------- // Purpose: // Input : type - //----------------------------------------------------------------------------- void Camera3D::SetNextCamera(SNCTYPE type) { if(Cameras.Count()==0) { m_iActiveCamera = -1; return; } switch(type) { case sncNext: ++m_iActiveCamera; if(m_iActiveCamera >= Cameras.Count() ) m_iActiveCamera = 0; break; case sncPrev: --m_iActiveCamera; if(m_iActiveCamera < 0) m_iActiveCamera = Cameras.Count()-1; break; case sncFirst: m_iActiveCamera = 0; break; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void Camera3D::DeleteActiveCamera() { if(!inrange(m_iActiveCamera, 0, Cameras.Count())) return; Cameras.Remove(m_iActiveCamera); if(m_iActiveCamera >= Cameras.Count() ) m_iActiveCamera = Cameras.Count()-1; } //----------------------------------------------------------------------------- // Purpose: // Input : file - // fIsStoring - //----------------------------------------------------------------------------- void Camera3D::SerializeRMF(std::fstream& file, BOOL fIsStoring) { float fVersion = 0.2f, fThisVersion; int nCameras = Cameras.Count(); if(fIsStoring) { file.write((char*)&fVersion, sizeof(fVersion) ); file.write((char*)&m_iActiveCamera, sizeof(m_iActiveCamera) ); file.write((char*)&nCameras, sizeof(nCameras)); for(int i = 0; i < nCameras; i++) { file.write((char*)&Cameras[i], sizeof(CAMSTRUCT)); } } else { file.read((char*)&fThisVersion, sizeof(fThisVersion) ); if(fThisVersion >= 0.2f) { file.read((char*)&m_iActiveCamera, sizeof(m_iActiveCamera)); } file.read((char*)&nCameras, sizeof (nCameras) ); Cameras.RemoveAll(); Cameras.EnsureCapacity(nCameras); for(int i = 0; i < nCameras; i++) { CAMSTRUCT cam; file.read((char*)&cam, sizeof(CAMSTRUCT)); Cameras.AddToTail( cam ); } EnsureMaxCameras(); Assert( Cameras.Count() == nCameras ); } } //----------------------------------------------------------------------------- // Purpose: // Input : *pFile - // Output : ChunkFileResult_t //----------------------------------------------------------------------------- ChunkFileResult_t Camera3D::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo) { ChunkFileResult_t eResult = pFile->BeginChunk( GetVMFChunkName() ); if (eResult == ChunkFile_Ok) { eResult = pFile->WriteKeyValueInt("activecamera", m_iActiveCamera); } if (eResult == ChunkFile_Ok) { for (int i = 0; i < Cameras.Count(); i++) { eResult = pFile->BeginChunk("camera"); if (eResult == ChunkFile_Ok) { eResult = pFile->WriteKeyValueVector3("position", Cameras[i].position[MovePos]); } if (eResult == ChunkFile_Ok) { eResult = pFile->WriteKeyValueVector3("look", Cameras[i].position[MoveLook]); } if (eResult == ChunkFile_Ok) { eResult = pFile->EndChunk(); } if (eResult != ChunkFile_Ok) { break; } } } if (eResult == ChunkFile_Ok) { eResult = pFile->EndChunk(); } return(eResult); } //----------------------------------------------------------------------------- // Purpose: Handles the key down event in the 2D view. // Input : Per CWnd::OnKeyDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnKeyDown2D(CMapView2D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == VK_DELETE || nChar == VK_NEXT || nChar == VK_PRIOR) { CMapDoc *pDoc = pView->GetMapDoc(); if (nChar == VK_DELETE) { DeleteActiveCamera(); } else if (nChar == VK_NEXT) { SetNextCamera(Camera3D::sncNext); } else { SetNextCamera(Camera3D::sncPrev); } Vector viewPos,lookAt; GetCameraPos( viewPos, lookAt ); pDoc->UpdateAllCameras( &viewPos, &lookAt, NULL ); return true; } else if (nChar == VK_ESCAPE) { OnEscape(); return true; } return false; } //----------------------------------------------------------------------------- // Purpose: Handles the left mouse button down event in the 2D view. // Input : Per CWnd::OnLButtonDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnLMouseDown2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { CMapDoc *pDoc = pView->GetMapDoc(); pView->SetCapture(); // // If there are no cameras created yet or they are holding down // the SHIFT key, create a new camera now. // Vector vecWorld; pView->ClientToWorld( vecWorld, vPoint ); if ( IsEmpty() || (nFlags & MK_SHIFT)) { // // Build a point in world space to place the new camera. // if ( !pDoc->GetSelection()->IsEmpty() ) { Vector vecCenter; pDoc->GetSelection()->GetBoundsCenter(vecCenter); vecWorld[pView->axThird] = vecCenter[pView->axThird]; } else { vecWorld[pView->axThird] = COORD_NOTINIT; pDoc->GetBestVisiblePoint(vecWorld); } // // Create a new camera. // m_vOrgPos = vecWorld; m_MoveCamera.position[MovePos] = vecWorld; m_MoveCamera.position[MoveLook] = vecWorld; m_nMovePositionIndex = MoveLook; // set as active camera m_iActiveCamera = Cameras.AddToTail(m_MoveCamera);; EnsureMaxCameras(); StartTranslation(pView, vPoint ); } // // Otherwise, try to drag an existing camera handle. // else { int dwHit = HitTest( pView, vPoint ); if ( dwHit ) { m_iActiveCamera = LOWORD(dwHit)-1; m_MoveCamera = Cameras[m_iActiveCamera]; m_nMovePositionIndex = HIWORD(dwHit); m_vOrgPos = m_MoveCamera.position[m_nMovePositionIndex]; StartTranslation( pView, vPoint ); } } return true; } //----------------------------------------------------------------------------- // Purpose: Handles the left mouse button up event in the 2D view. // Input : Per CWnd::OnLButtonUp. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnLMouseUp2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { ReleaseCapture(); if (IsTranslating()) { FinishTranslation(true); Vector viewPos, lookAt; GetCameraPos( viewPos, lookAt ); m_pDocument->UpdateAllCameras( &viewPos, &lookAt, NULL ); } m_pDocument->UpdateStatusbar(); return true; } unsigned int Camera3D::GetConstraints(unsigned int nKeyFlags) { unsigned int uConstraints = Tool3D::GetConstraints( nKeyFlags ); if(nKeyFlags & MK_CONTROL) { uConstraints |= constrainMoveAll; } return uConstraints; } //----------------------------------------------------------------------------- // Purpose: Handles the mouse move event in the 2D view. // Input : Per CWnd::OnMouseMove. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnMouseMove2D(CMapView2D *pView, UINT nFlags, const Vector2D &vPoint) { CMapDoc *pDoc = pView->GetMapDoc(); if (!pDoc) { return true; } vgui::HCursor hCursor = vgui::dc_arrow; unsigned int uConstraints = GetConstraints( nFlags ); // Make sure the point is visible. pView->ToolScrollToPoint( vPoint ); // // Convert to world coords. // Vector vecWorld; pView->ClientToWorld(vecWorld, vPoint); // // Update status bar position display. // char szBuf[128]; m_pDocument->Snap(vecWorld,uConstraints); sprintf(szBuf, " @%.0f, %.0f ", vecWorld[pView->axHorz], vecWorld[pView->axVert] ); SetStatusText(SBI_COORDS, szBuf); if (IsTranslating()) { Tool3D::UpdateTranslation(pView, vPoint, uConstraints ); hCursor = vgui::dc_none; } else if ( !IsEmpty() ) { // // If the cursor is on a handle, set it to a cross. // if ( HitTest( pView, vPoint, true) ) { hCursor = vgui::dc_crosshair; } } if ( hCursor != vgui::dc_none ) pView->SetCursor( hCursor ); return true; } //----------------------------------------------------------------------------- // Purpose: Handles the left mouse button down event in the 3D view. // Input : Per CWnd::OnLButtonDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnLMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { pView->EnableRotating(true); return true; } //----------------------------------------------------------------------------- // Purpose: Handles the left mouse up down event in the 3D view. // Input : Per CWnd::OnLButtonUp. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnLMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { pView->EnableRotating(false); pView->UpdateCameraVariables(); return true; } //----------------------------------------------------------------------------- // Purpose: Handles the right mouse button down event in the 3D view. // Input : Per CWnd::OnRButtonDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnRMouseDown3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { pView->EnableStrafing(true); return true; } //----------------------------------------------------------------------------- // Purpose: Handles the right mouse button up event in the 3D view. // Input : Per CWnd::OnRButtonUp. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnRMouseUp3D(CMapView3D *pView, UINT nFlags, const Vector2D &vPoint) { pView->EnableStrafing(false); pView->UpdateCameraVariables(); return true; } //----------------------------------------------------------------------------- // Purpose: Handles the key down event in the 3D view. // Input : Per CWnd::OnKeyDown. // Output : Returns true if the message was handled, false if not. //----------------------------------------------------------------------------- bool Camera3D::OnKeyDown3D(CMapView3D *pView, UINT nChar, UINT nRepCnt, UINT nFlags) { if (nChar == VK_DELETE || nChar == VK_NEXT || nChar == VK_PRIOR) { CMapDoc *pDoc = pView->GetMapDoc(); if (nChar == VK_DELETE) { DeleteActiveCamera(); } else if (nChar == VK_NEXT) { SetNextCamera(Camera3D::sncNext); } else { SetNextCamera(Camera3D::sncPrev); } Vector viewPos, lookAt; GetCameraPos( viewPos, lookAt ); pDoc->UpdateAllCameras( &viewPos, &lookAt, NULL ); return true; } else if (nChar == VK_ESCAPE) { OnEscape(); return true; } return false; } //----------------------------------------------------------------------------- // Purpose: Handles the escape key in the 2D or 3D views. //----------------------------------------------------------------------------- void Camera3D::OnEscape(void) { // // Stop using the camera tool. // m_pDocument->GetTools()->SetTool(TOOL_POINTER); }