// ScEdView.cpp : implementation of the CScEdView class // #include "stdafx.h" #include "ScEd.h" #include "ScEdDoc.h" #include "ScEdView.h" #include "ToolManager.h" #include "EditorData.h" #include "UserConfig.h" #include "MainFrm.h" #include "timer.h" #include "ogl.h" #include "res/vfs.h" int g_ClickMode=0; static unsigned int GetModifierKeyFlags() { unsigned int flags=0; if (::GetAsyncKeyState(VK_MENU) & 0x8000) flags|=TOOL_MOUSEFLAG_ALTDOWN; if (::GetAsyncKeyState(VK_SHIFT) & 0x8000) flags|=TOOL_MOUSEFLAG_SHIFTDOWN; if (::GetAsyncKeyState(VK_CONTROL) & 0x8000) flags|=TOOL_MOUSEFLAG_CTRLDOWN; return flags; } ///////////////////////////////////////////////////////////////////////////// // CScEdView IMPLEMENT_DYNCREATE(CScEdView, CView) BEGIN_MESSAGE_MAP(CScEdView, CView) //{{AFX_MSG_MAP(CScEdView) ON_WM_ERASEBKGND() ON_WM_CREATE() ON_WM_DESTROY() ON_WM_SIZE() ON_WM_MOUSEMOVE() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_RBUTTONDOWN() ON_WM_RBUTTONUP() ON_WM_MBUTTONDOWN() ON_WM_MBUTTONUP() ON_MESSAGE(WM_MOUSEWHEEL,OnMouseWheel) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CScEdView construction/destruction CScEdView::CScEdView() : m_hGLRC(0), m_LastTickTime(-1.0f), m_LastFrameDuration(-1.0f) { } CScEdView::~CScEdView() { } BOOL CScEdView::PreCreateWindow(CREATESTRUCT& cs) { cs.lpszClass = ::AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS | CS_OWNDC, ::LoadCursor(NULL, IDC_ARROW), NULL, NULL); cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN; return CView::PreCreateWindow(cs); } ///////////////////////////////////////////////////////////////////////////// // CScEdView drawing void CScEdView::OnDraw(CDC* pDC) { HWND hWnd = GetSafeHwnd(); HDC hDC = ::GetDC(hWnd); wglMakeCurrent(hDC,m_hGLRC); g_EditorData.OnDraw(); SwapBuffers(hDC); } ///////////////////////////////////////////////////////////////////////////// // CScEdView diagnostics #ifdef _DEBUG void CScEdView::AssertValid() const { CView::AssertValid(); } void CScEdView::Dump(CDumpContext& dc) const { CView::Dump(dc); } CScEdDoc* CScEdView::GetDocument() // non-debug version is inline { ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CScEdDoc))); return (CScEdDoc*)m_pDocument; } #endif //_DEBUG ///////////////////////////////////////////////////////////////////////////// // CScEdView message handlers BOOL CScEdView::OnEraseBkgnd(CDC* pDC) { return TRUE; } int CScEdView::OnCreate(LPCREATESTRUCT lpCreateStruct) { // base initialisation first if (CView::OnCreate(lpCreateStruct) == -1) return -1; // get device context for this window HDC dc=::GetDC(m_hWnd); // try and setup a default pixel format if (!SetupPixelFormat(dc)) { return -1; } // create context, make it current m_hGLRC=wglCreateContext(dc); wglMakeCurrent(dc,m_hGLRC); // initialise gl stuff (extensions, etc) oglInit(); // check for minimum requirements if(!oglExtAvail("GL_ARB_multitexture") || !oglExtAvail("GL_ARB_texture_env_combine")) { const char* err="No graphics card support for multitexturing found; please visit the 0AD Forums for more information."; ::MessageBox(0,err,"Error",MB_OK); exit(0); } // initialise VFS paths char path[256]; ::GetModuleFileName(0,path,256); file_rel_chdir(path, "../data"); vfs_mount("", "mods/official", 0); // create renderer related stuff new CRenderer; // start up the renderer g_Renderer.Open(0,0,::GetDeviceCaps(dc,BITSPIXEL)); // initialise document data if (!g_EditorData.Init()) return -1; // store current mouse pos ::GetCursorPos(&m_LastMousePos); return 0; } void CScEdView::OnDestroy() { // close down editor resources g_EditorData.Terminate(); // destroy renderer related stuff delete CRenderer::GetSingletonPtr(); // release rendering context if (m_hGLRC) { wglMakeCurrent(0,0); wglDeleteContext(m_hGLRC); m_hGLRC=0; } // base destruction CView::OnDestroy(); } void CScEdView::OnSize(UINT nType, int cx, int cy) { // give base class a shout .. CView::OnSize(nType, cx, cy); m_Width=cx; m_Height=cy; g_Renderer.Resize(m_Width,m_Height); g_EditorData.OnCameraChanged(); } bool CScEdView::SetupPixelFormat(HDC dc) { int bpp=::GetDeviceCaps(dc,BITSPIXEL); PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type bpp==16 ? 16 : 24, // 16/24 bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored bpp==16 ? 0 : 8, // 16/24 bit color depth 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 24, // 24-bit z-buffer 8, // 8-bit stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; int format=::ChoosePixelFormat(dc,&pfd); if (format==0) { // ack - can't choose format; bail out return false; } if(::SetPixelFormat(dc,format,&pfd) == false) { // ugh - still can't get anything; bail out return false; } // check we've got an accelerated format available if (::DescribePixelFormat(dc,format,sizeof(pfd),&pfd)) { if (pfd.dwFlags & PFD_GENERIC_FORMAT) { const char* err="No hardware accelerated graphics support found; please visit the 0AD Forums for more information."; ::MessageBox(0,err,"Error",MB_OK); exit(0); } } return true; } static CPoint lastClientMousePos; void CScEdView::OnMouseLeave(const CPoint& point) { } void CScEdView::OnMouseEnter(const CPoint& point) { unsigned int flags=GetModifierKeyFlags(); // trigger left button up and right button up events as required if (!(::GetAsyncKeyState(VK_LBUTTON) & 0x8000)) { g_ToolMan.OnLButtonUp(flags,point.x,point.y); } if (!(::GetAsyncKeyState(VK_RBUTTON) & 0x8000)) { g_ToolMan.OnRButtonUp(flags,point.x,point.y); } } void CScEdView::OnMouseMove(UINT nFlags, CPoint point) { SetFocus(); if (nFlags & MK_MBUTTON) { // middle mouse down .. forward move to the navicam and // we're done u32 flags=GetModifierKeyFlags(); g_NaviCam.OnMouseMove(flags,point.x,point.y); return; } static bool mouseInView=false; // get view rect CRect rect; GetClientRect(rect); // inside? if(rect.PtInRect(point)) { // yes - previously inside? if (!mouseInView) { // nope - enable mouse capture SetCapture(); // run any necessary mouse leave code OnMouseEnter(point); } mouseInView=true; // store mouse position lastClientMousePos=point; // now actually handle everything required for the actual move event // assume we want to update tile selection on mouse move bool updateSelection=true; // check for left mouse down on minimap following click on minimap if (nFlags & MK_LBUTTON) { if (g_ClickMode==0) { if (point.x>m_Width-200 && point.y>m_Height-200) { AdjustCameraViaMinimapClick(point); // minimap moved, so don't update selection updateSelection=false; } } } if (updateSelection) { unsigned int flags=GetModifierKeyFlags(); g_ToolMan.OnMouseMove(flags,point.x,point.y); } // store this position as last m_LastMousePos=point; } else { // previously inside view? if (mouseInView) { // yes - run any necessary mouse leave code OnMouseLeave(point); // release capture ReleaseCapture(); } // note mouse no longer in view mouseInView=false; } } void CScEdView::OnScreenShot() { static int counter=1; // generate filename, create directory if required char buf[512]; sprintf(buf,"../screenshots"); mkdir(buf,0); sprintf(buf,"%s/%08d.tga",buf,counter++); // make context current HWND hWnd = GetSafeHwnd(); HDC hDC = ::GetDC(hWnd); wglMakeCurrent(hDC,m_hGLRC); // call on editor to make screenshot g_EditorData.OnScreenShot(buf); } void CScEdView::OnRButtonUp(UINT nFlags, CPoint point) { g_ClickMode=1; unsigned int flags=GetModifierKeyFlags(); g_ToolMan.OnRButtonUp(flags,point.x,point.y); } void CScEdView::OnRButtonDown(UINT nFlags, CPoint point) { if (point.x>m_Width-200 && point.y>m_Height-200) { } else { g_ClickMode=1; unsigned int flags=GetModifierKeyFlags(); g_ToolMan.OnRButtonDown(flags,point.x,point.y); } } void CScEdView::OnLButtonUp(UINT nFlags, CPoint point) { g_ClickMode=1; unsigned int flags=GetModifierKeyFlags(); g_ToolMan.OnLButtonUp(flags,point.x,point.y); } void CScEdView::OnLButtonDown(UINT nFlags, CPoint point) { if (point.x>m_Width-200 && point.y>m_Height-200) { g_ClickMode=0; AdjustCameraViaMinimapClick(point); } else { g_ClickMode=1; unsigned int flags=GetModifierKeyFlags(); g_ToolMan.OnLButtonDown(flags,point.x,point.y); } } void CScEdView::AdjustCameraViaMinimapClick(CPoint point) { // convert from screen space point back to world space point representating intersection of // ray with terrain plane CVector3D pos; pos.X=float(CELL_SIZE*g_Terrain.GetVerticesPerSide())*float(point.x+200-m_Width)/200.0f; pos.Y=-g_EditorData.m_TerrainPlane.m_Dist; pos.Z=float(CELL_SIZE*g_Terrain.GetVerticesPerSide())*float(m_Height-point.y)/197.0f; // calculate desired camera point from this CVector3D startpos=g_NaviCam.GetCamera().m_Orientation.GetTranslation(); CVector3D rayDir=g_NaviCam.GetCamera().m_Orientation.GetIn(); float distToPlane=g_EditorData.m_TerrainPlane.DistanceToPlane(startpos); float dot=rayDir.Dot(g_EditorData.m_TerrainPlane.m_Norm); CVector3D endpos=pos+(rayDir*(distToPlane/dot)); // translate camera from old point to new CVector3D trans=endpos-startpos; g_NaviCam.GetCamera().m_Orientation.Translate(trans); g_EditorData.OnCameraChanged(); } bool CScEdView::AppHasFocus() { CWnd* wnd=AfxGetMainWnd(); if (!wnd) return false; CWnd* focuswnd=GetFocus(); while (focuswnd) { if (focuswnd==wnd) return true; focuswnd=focuswnd->GetParent(); } return false; } void CScEdView::IdleTimeProcess() { if (m_LastTickTime==-1) { m_LastTickTime=get_time(); return; } // fake a mouse move from current position if either mouse button is down unsigned int flags=0; if ((::GetAsyncKeyState(VK_LBUTTON) & 0x8000) || (::GetAsyncKeyState(VK_RBUTTON) & 0x8000)) { unsigned int flags=GetModifierKeyFlags(); g_ToolMan.OnMouseMove(flags,m_LastMousePos.x,m_LastMousePos.y); } double curtime=get_time(); double diff=curtime-m_LastTickTime; if (m_LastFrameDuration>0) { g_EditorData.UpdateWorld(float(m_LastFrameDuration)); // check app has focus if (AppHasFocus()) { POINT pt; if (GetCursorPos(&pt)) { // want to scroll? int scrollspeed=g_UserCfg.GetOptionInt(CFG_SCROLLSPEED); if (scrollspeed>0) { RECT rect; AfxGetMainWnd()->GetWindowRect(&rect); // scale translation by distance from terrain float h=g_NaviCam.GetCamera().m_Orientation.GetTranslation().Y; float speed=h*0.1f; // scale translation to account for fact that we might not be running at the same rate as the timer speed*=float(diff); // scale by user requested speed speed*=scrollspeed; bool changed=false; if (pt.xrect.right-16) { CVector3D left=g_NaviCam.GetCamera().m_Orientation.GetLeft(); // strip vertical movement, to prevent moving into/away from terrain plane left.Y=0; left.Normalize(); g_NaviCam.GetCamera().m_Orientation.Translate(left*(-speed)); changed=true; } if (pt.y>rect.bottom-16) { CVector3D up=g_NaviCam.GetCamera().m_Orientation.GetUp(); // strip vertical movement, to prevent moving into/away from terrain plane up.Y=0; up.Normalize(); g_NaviCam.GetCamera().m_Orientation.Translate(up*(-speed)); changed=true; } else if (pt.ymessage >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) { if (pMsg->message==WM_KEYDOWN) { int key=(int) pMsg->wParam; CMainFrame* mainfrm=(CMainFrame*)AfxGetApp()->m_pMainWnd; switch (key) { case VK_F1: mainfrm->OnViewRenderStats(); break; case VK_F9: mainfrm->OnViewScreenshot(); break; case 'Z': if (GetAsyncKeyState(VK_CONTROL)) mainfrm->OnEditUndo(); break; case 'Y': if (GetAsyncKeyState(VK_CONTROL)) mainfrm->OnEditRedo(); break; } } return 1; } else { return CView::PreTranslateMessage(pMsg); } }