forked from 0ad/0ad
588 lines
14 KiB
C++
Executable File
588 lines
14 KiB
C++
Executable File
// ScEdView.cpp : implementation of the CScEdView class
|
|
//
|
|
|
|
#include "precompiled.h"
|
|
#include "stdafx.h"
|
|
#define _IGNORE_WGL_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"
|
|
#undef _IGNORE_WGL_H_
|
|
|
|
#include "SelectObjectTool.h"
|
|
#include "Game.h"
|
|
|
|
#include "lib/res/file/vfs.h"
|
|
|
|
#undef CRect // because it was redefined to PS_Rect in Overlay.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)
|
|
{
|
|
// make sure the delay-loaded OpenGL has been loaded before calling
|
|
// any graphical functions
|
|
glGetError();
|
|
|
|
// 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(!oglHaveExtension("GL_ARB_multitexture") || !oglHaveExtension("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);
|
|
}
|
|
|
|
|
|
extern void ScEd_Init();
|
|
ScEd_Init();
|
|
|
|
// 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();
|
|
|
|
extern void ScEd_Shutdown();
|
|
ScEd_Shutdown();
|
|
|
|
// 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, // alpha bits
|
|
0, // shift bit ignored
|
|
0, // no accumulation buffer
|
|
0, 0, 0, 0, // accum bits ignored
|
|
24, // 24-bit z-buffer
|
|
0, // 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 representing intersection of
|
|
// ray with terrain plane
|
|
CVector3D pos;
|
|
pos.X=float(CELL_SIZE * g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide()) * float(point.x+200-m_Width)/200.0f;
|
|
pos.Y=-g_EditorData.m_TerrainPlane.m_Dist;
|
|
pos.Z=float(CELL_SIZE * g_Game->GetWorld()->GetTerrain()->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.x<rect.left+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;
|
|
} else if (pt.x>rect.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.y<rect.top+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;
|
|
}
|
|
|
|
if (changed) {
|
|
g_EditorData.OnCameraChanged();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// store frame time
|
|
m_LastFrameDuration=diff;
|
|
|
|
// store current time
|
|
m_LastTickTime=curtime;
|
|
|
|
// finally, redraw view
|
|
HWND hWnd = GetSafeHwnd();
|
|
HDC hDC = ::GetDC(hWnd);
|
|
wglMakeCurrent(hDC,m_hGLRC);
|
|
g_EditorData.OnDraw();
|
|
SwapBuffers(hDC);
|
|
}
|
|
|
|
|
|
LRESULT CScEdView::OnMouseWheel(WPARAM wParam,LPARAM lParam)
|
|
{
|
|
int xPos = LOWORD(lParam);
|
|
int yPos = HIWORD(lParam);
|
|
SHORT fwKeys = LOWORD(wParam);
|
|
SHORT zDelta = HIWORD(wParam);
|
|
|
|
g_NaviCam.OnMouseWheelScroll(0,xPos,yPos,float(zDelta)/120.0f);
|
|
return 0;
|
|
}
|
|
|
|
|
|
void CScEdView::OnMButtonDown(UINT nFlags, CPoint point)
|
|
{
|
|
g_NaviCam.OnMButtonDown(0,point.x,point.y);
|
|
}
|
|
|
|
void CScEdView::OnMButtonUp(UINT nFlags, CPoint point)
|
|
{
|
|
g_NaviCam.OnMButtonUp(0,point.x,point.y);
|
|
}
|
|
|
|
|
|
BOOL CScEdView::PreTranslateMessage(MSG* pMsg)
|
|
{
|
|
if (pMsg->message >= 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;
|
|
case VK_DELETE:
|
|
CSelectObjectTool::GetTool()->DeleteSelected();
|
|
break;
|
|
}
|
|
}
|
|
return 1;
|
|
} else {
|
|
return CView::PreTranslateMessage(pMsg);
|
|
}
|
|
}
|