Files
FC1/Editor/TerrainViewport.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

696 lines
18 KiB
C++

// TerrainViewport.cpp : implementation file
//
#include "stdafx.h"
#include "TerrainViewport.h"
#include "CryEditDoc.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CTerrainViewport
// Used to give each static object type a different color
static DWORD StatObjColorArray[] =
{
0x00FF0000,
0x0000FF00,
0x000000FF,
0x00FFFFFF,
0x00FF00FF,
0x00FFFF00,
0x0000FFFF,
0x007F00FF,
0x007FFF7F,
0x00FF7F00,
0x0000FF7F,
0x007F7F7F,
0x00000000
};
CTerrainViewport::CTerrainViewport()
{
m_heightmapSize.cx = GetDocument()->m_cHeightmap.GetWidth();
m_heightmapSize.cy = GetDocument()->m_cHeightmap.GetHeight();
VERIFY(m_dcView.CreateCompatibleDC(NULL));
// Create a DC and a bitmap
VERIFY(m_dcTerrain.CreateCompatibleDC(NULL));
VERIFY(m_bmpTerrain.CreateBitmap(m_heightmapSize.cx,m_heightmapSize.cy, 1, 32, NULL));
m_dcTerrain.SelectObject(&m_bmpTerrain);
m_iBrushSize = 50;
}
CTerrainViewport::~CTerrainViewport()
{
}
BEGIN_MESSAGE_MAP(CTerrainViewport, CViewport)
//{{AFX_MSG_MAP(CTerrainViewport)
ON_WM_PAINT()
ON_WM_MOUSEWHEEL()
ON_WM_MOUSEMOVE()
ON_WM_ERASEBKGND()
ON_WM_SIZE()
ON_WM_RBUTTONDOWN()
ON_WM_RBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CTerrainViewport message handlers
//////////////////////////////////////////////////////////////////////////
CPoint CTerrainViewport::WorldToView( Vec3d wp )
{
CPoint p;
float x = wp.x / 2.0f;
float y = wp.y / 2.0f;
float lTemp;
// Swap the axis
lTemp = x;
x = y;
y = lTemp;
x = (x * GetZoomFactor());
y = (y * GetZoomFactor());
// Add the offset
x += GetScrollOffset().x;
y += GetScrollOffset().y;
p.x = (int)x;
p.y = (int)y;
return p;
}
//////////////////////////////////////////////////////////////////////////
Vec3d CTerrainViewport::ViewToWorld( CPoint vp,bool *collideWithTerrain,bool onlyTerrain )
{
vp.x += abs(GetScrollOffset().x);
vp.y += abs(GetScrollOffset().y);
// Swap the axis
int lTemp = vp.x;
vp.x = vp.y;
vp.y = lTemp;
// Scale with the zoom factor
float x = (float)vp.x / GetZoomFactor();
float y = (float)vp.y / GetZoomFactor();
Vec3d wp;
wp.x = x * 2.0f;
wp.y = y * 2.0f;
wp.z = GetIEditor()->GetTerrainElevation(wp.x,wp.y);
if (collideWithTerrain)
*collideWithTerrain = true;
/*
CEngineSingleton::GetGameInterface()->GetInterface()->
MovePlayer(point.x * 2.0f, point.y * 2.0f, CEngineSingleton::
GetGameInterface()->GetInterface()->GetTerrainElevation(point.x * 2.0f, point.y * 2.0f));
*/
return wp;
}
void CTerrainViewport::ViewToWorldRay( CPoint vp,Vec3 &raySrc,Vec3 &rayDir )
{
raySrc = ViewToWorld( vp );
raySrc.z = 1000;
rayDir(0,0,-1);
}
bool CTerrainViewport::SetScrollOffset(long iX, long iY)
{
////////////////////////////////////////////////////////////////////////
// Set the scrolling offset. If the passed values are invalid, the
// nearest valid values will be set and false is returned
////////////////////////////////////////////////////////////////////////
bool bRangeValid = true;
RECT rcWndPos;
long iScaledTexWidth = (long) (m_heightmapSize.cx * GetZoomFactor());
// Obtain the size of the window
GetClientRect(&rcWndPos);
// Don't allow to scroll beyond the lower right corner
if (abs(iX) > iScaledTexWidth - rcWndPos.right)
{
iX = -(iScaledTexWidth - rcWndPos.right);
bRangeValid = false;
}
if (abs(iY) > iScaledTexWidth - rcWndPos.bottom)
{
iY = -(iScaledTexWidth - rcWndPos.bottom);
bRangeValid = false;
}
// Don't allow to scroll beyond the upper left corner
if (iX > 0)
{
iX = 0;
bRangeValid = false;
}
if (iY > 0)
{
iY = 0;
bRangeValid = false;
}
// Save the (eventually corrected) scroll offset
m_cScrollOffset = CPoint(iX, iY);
RedrawWindow();
return bRangeValid;
}
void CTerrainViewport::ResetContent()
{
////////////////////////////////////////////////////////////////////////
// Create a surface texture that consists entirely of water
////////////////////////////////////////////////////////////////////////
DWORD *pWaterTexData = NULL;
DWORD *pSurfaceTextureData = NULL;
DWORD *pPixData = NULL, *pPixDataEnd = NULL;
CBitmap bmpLoad;
bool bReturn;
// Load the water texture out of the ressource
bReturn = bmpLoad.LoadBitmap(MAKEINTRESOURCE(IDB_WATER));
ASSERT(bReturn);
// Allocate new memory to hold the bitmap data
pWaterTexData = new DWORD[128 * 128];
ASSERT(pWaterTexData);
// Retrieve the bits from the bitmap
VERIFY(bmpLoad.GetBitmapBits(128 * 128 * sizeof(DWORD), pWaterTexData));
// Allocate memory for the surface texture
pSurfaceTextureData = new DWORD[m_heightmapSize.cx*m_heightmapSize.cy];
ASSERT(pSurfaceTextureData);
// Fill the surface texture with the water texture, tile as needed
for (int j=0; j<m_heightmapSize.cy; j++)
for (int i=0; i<m_heightmapSize.cx; i++)
{
pSurfaceTextureData[i + j * m_heightmapSize.cx] =
pWaterTexData[(i % 128) + (j % 128) * 128];
}
// Set the loop pointers
pPixData = pSurfaceTextureData;
pPixDataEnd = &pSurfaceTextureData[m_heightmapSize.cx*m_heightmapSize.cy];
/*
// Switch R and B
while (pPixData != pPixDataEnd)
{
// Extract the bits, shift them, put them back and advance to the next pixel
*pPixData++ = ((* pPixData & 0x00FF0000) >> 16) |
(* pPixData & 0x0000FF00) | ((* pPixData & 0x000000FF) << 16);
}
*/
// Load it into the bitmap
m_bmpTerrain.SetBitmapBits(m_heightmapSize.cx*m_heightmapSize.cy*4, pSurfaceTextureData);
if (::IsWindow(m_hWnd))
RedrawWindow();
// Free surface mem
delete [] pSurfaceTextureData;
pSurfaceTextureData = 0;
// Free the water texture data
delete [] pWaterTexData;
pWaterTexData = 0;
}
void CTerrainViewport::UpdateContent( int flags )
{
////////////////////////////////////////////////////////////////////////
// Generate a new surface texture
////////////////////////////////////////////////////////////////////////
DWORD *pSurfaceData = NULL;
bool bReturn;
// Allocate memory
pSurfaceData = new DWORD[m_heightmapSize.cx*m_heightmapSize.cy];
ASSERT(pSurfaceData);
bReturn = GetDocument()->GetHeightmap().GetPreviewBitmap( (DWORD*)pSurfaceData,m_heightmapSize.cx,false,false );
/*
// Fill in the surface data into the array. Apply lighting and water, use
// the settings from the document
bReturn = cSurfaceTexture.GenerateSurface(pSurfaceData, SURFACE_TEXTURE_WIDTH,
SURFACE_TEXTURE_WIDTH, true, true, GLOBAL_GET_DOC->GetWaterLevel(), true);
*/
if (bReturn)
{
m_bmpTerrain.SetBitmapBits( m_heightmapSize.cx*m_heightmapSize.cy*4, pSurfaceData);
if (IsWindow(GetSafeHwnd()))
RedrawWindow();
}
// Free the surface data array
delete [] pSurfaceData;
pSurfaceData = 0;
/*
CCryEditDoc *doc = GLOBAL_GET_DOC;
if (!doc)
return;
m_heightmapSize.cx = doc->m_cHeightmap.GetWidth();
m_heightmapSize.cy = doc->m_cHeightmap.GetHeight();
m_waterLevel = doc->GetWaterLevel();
/*
////////////////////////////////////////////////////////////////////////
// Generate a new surface texture
////////////////////////////////////////////////////////////////////////
CTerrainTexture cSurfaceTexture;
bool bReturn;
CLogFile::FormatLine("Updating top view surface data (%i x %i)...",
m_textureSize.cx, m_textureSize.cy);
// Allocate memory
if (m_textureData)
delete []m_textureData;
if (!m_bShowHeightmap)
{
m_textureSize.cx = SURFACE_TEXTURE_WIDTH;
m_textureSize.cy = SURFACE_TEXTURE_WIDTH;
m_textureData = new uint[m_textureSize.cx * m_textureSize.cy];
ASSERT(m_textureData);
// Fill in the surface data into the array. Apply lighting and water, use
// the settings from the document
bReturn = cSurfaceTexture.GenerateSurface( (unsigned long*)m_textureData,m_textureSize.cx,m_textureSize.cy,true,true,m_waterLevel,true);
}
else
{
m_textureSize.cx = 512;
m_textureSize.cy = 512;
m_textureData = new uint[m_textureSize.cx * m_textureSize.cy];
ASSERT(m_textureData);
// Show heightmap data.
//GLOBAL_GET_DOC->m_cHeightmap.GetDataEx(pHeightmap, (m_rcView.right - m_rcView.left), false, false);
GLOBAL_GET_DOC->m_cHeightmap.GetPreviewBitmap( (DWORD*)m_textureData,m_textureSize.cx,false,false );
}
m_textureValid = false;
if (bReturn)
{
if (::IsWindow(m_hWnd))
RedrawWindow();
}
if (m_bShowStatObjects)
{
int w,h;
GetIEditor()->GetDocument()->GetStatObjDistArray( w,h );
if (m_statObjTexData)
delete []m_statObjTexData;
m_statObjTexSize.cx = w;
m_statObjTexSize.cy = h;
m_statObjTexData = new uint[w*h];
memset( m_statObjTexData,0,w*h );
DrawStaticObjects();
}
*/
SetZoomFactor( GetZoomFactor() );
}
void CTerrainViewport::SetZoomFactor(float fZoomFactor)
{
////////////////////////////////////////////////////////////////////////
// Adjust the zoom factor of the view. If the passed values are invalid,
// the nearest valid values will be set and false is returned
////////////////////////////////////////////////////////////////////////
RECT rcWndPos;
bool bRangeValid = true;
float fNewSizeTemp = 0;
float fEnlargementFactor;
// Zero can produce artifacts and errors
if (fZoomFactor == 0.0f)
{
fZoomFactor = m_fZoomFactor;
bRangeValid = false;
}
// Obtain the dimensions of the window
GetClientRect(&rcWndPos);
// Is the new zoom factor smaller than the last one ?
if (fZoomFactor < m_fZoomFactor)
{
// We might run into the problem that the map is smaller than the
// the window when displayed with the specified zoom factor
if (m_heightmapSize.cx * fZoomFactor < rcWndPos.right)
{
// Calculate the new size
fZoomFactor = (float) rcWndPos.right / m_heightmapSize.cx;
bRangeValid = false;
}
if (m_heightmapSize.cx * fZoomFactor < rcWndPos.bottom)
{
// Calculate the new size
fNewSizeTemp = (float) rcWndPos.bottom / m_heightmapSize.cx;
// Only set the new zoom if it is larger than the previous
fZoomFactor = (fZoomFactor > fNewSizeTemp) ? fZoomFactor : fNewSizeTemp;
bRangeValid = false;
}
}
// Calculate how much larger the new zoom is compared to the old one
fEnlargementFactor = fZoomFactor / m_fZoomFactor;
// Save the new zoom factor
m_fZoomFactor = fZoomFactor;
if (m_cScrollOffset.x + m_heightmapSize.cx*fZoomFactor < rcWndPos.right)
m_cScrollOffset.x = rcWndPos.right - m_heightmapSize.cx*fZoomFactor;
if (m_cScrollOffset.y + m_heightmapSize.cy*fZoomFactor < rcWndPos.bottom)
m_cScrollOffset.y = rcWndPos.bottom - m_heightmapSize.cy*fZoomFactor;
if (m_cScrollOffset.x > 0) m_cScrollOffset.x = 0;
if (m_cScrollOffset.y > 0) m_cScrollOffset.y = 0;
RedrawWindow();
}
void CTerrainViewport::OnPaint()
{
CPaintDC dc(this); // device context for painting
if (!GetDocument()) return;
int iX, iY, iWidth, iHeight;
/* OLD
// Blit the image
dc.SetStretchBltMode(HALFTONE);
dc.StretchBlt(iX, iY, iWidth, iHeight, &m_dcTerrain, 0, 0, m_heightmapSize.cx, m_heightmapSize.cy, SRCCOPY);
// Reset brush origin after strech blit.
dc.SetBrushOrg( 0,0 );
*/
// Free any old bitmap first
if (m_bmpView.GetSafeHandle())
{
//m_dcView.SelectObject(NULL);
::SelectObject(m_dcView.m_hDC, NULL);
m_bmpView.DeleteObject();
}
CRect rc( dc.m_ps.rcPaint );
// Calculate the position and dimension of the image
iX = m_cScrollOffset.x;
iY = m_cScrollOffset.y;
iWidth = (m_heightmapSize.cx * m_fZoomFactor);
iHeight = (m_heightmapSize.cy * m_fZoomFactor);
CPoint hp1 = ViewToHeightmap( CPoint(rc.left,rc.top) );
CPoint hp2 = ViewToHeightmap( CPoint(rc.right,rc.bottom) );
// Create bitmap
BITMAPINFO BmpInfo;
BmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BmpInfo.bmiHeader.biWidth = hp2.x - hp1.x;
BmpInfo.bmiHeader.biHeight = -(hp2.y - hp1.y);
BmpInfo.bmiHeader.biPlanes = 1;
BmpInfo.bmiHeader.biBitCount = 32;
BmpInfo.bmiHeader.biCompression = BI_RGB;
BmpInfo.bmiHeader.biSizeImage = 0;
BmpInfo.bmiHeader.biXPelsPerMeter = 0;
BmpInfo.bmiHeader.biYPelsPerMeter = 0;
BmpInfo.bmiHeader.biClrUsed = 0;
BmpInfo.bmiHeader.biClrImportant = 0;
DWORD *pBits = NULL;
// Create the DIB section to draw into
HBITMAP bmpView = ::CreateDIBSection(m_dcView.m_hDC, &BmpInfo, DIB_RGB_COLORS,(void**)&pBits, NULL, 0);
ASSERT(bmpView);
m_bmpView.Attach(bmpView);
// Select the DIB into the DC
m_dcView.SelectObject( m_bmpView );
////////////////////////////////////////////////////////////////////////
// Draw the heightmap into the final image
////////////////////////////////////////////////////////////////////////
//m_dcView.BitBlt( 0,0,m_heightmapSize.cx,m_heightmapSize.cy,&m_dcTerrain, 0, 0, SRCCOPY);
m_dcView.BitBlt( 0, 0,hp2.x-hp1.x,hp2.y-hp1.y, &m_dcTerrain, hp1.x,hp1.y, SRCCOPY);
//m_dcView.SetBrushOrg( 0,0 );
DrawStaticObjects( pBits, hp2.x-hp1.x,hp2.y-hp1.y, CRect(hp1,hp2) );
//dc.BitBlt( 0,0,rc.right,rc.bottom,&m_dcView, 0, 0, SRCCOPY);
dc.SetStretchBltMode(COLORONCOLOR);
dc.StretchBlt(iX, iY, iWidth, iHeight, &m_dcView, 0, 0, hp2.x-hp1.x,hp2.y-hp1.y, SRCCOPY);
//dc.BitBlt(0,0,rc.right,rc.bottom, &m_dcView, 0, 0, SRCCOPY);
// Reset brush origin after strech blit.
dc.SetBrushOrg( 0,0 );
/*
CPoint p;
p.x = m_viewerPos.x;
p.y = m_viewerPos.y;
HMCoordToWndCoord( &p );
dc.Ellipse( p.x-3,p.y-3,p.x+3,p.y+3 );
*/
// Do not call CViewport::OnPaint() for painting messages
}
void CTerrainViewport::DrawStaticObjects( DWORD* pBits,int trgWidth,int trgHeight,CRect rc )
{
}
BOOL CTerrainViewport::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
float prevz = GetZoomFactor();
float z = GetZoomFactor() + (zDelta / 120.0f) * 2.0f;
// Zoom to mouse position.
float x = (float)(m_cMousePos.x - m_cScrollOffset.x)/prevz;
float y = (float)(m_cMousePos.y - m_cScrollOffset.y)/prevz;
m_cScrollOffset.x = m_cMousePos.x - z*x;
m_cScrollOffset.y = m_cMousePos.y - z*y;
SetZoomFactor( z );
return TRUE;
}
void CTerrainViewport::OnMouseMove(UINT nFlags, CPoint point)
{
m_cMousePos = point;
if (GetViewMode() == ScrollZoomMode)
{
CRect rc;
// You can only scroll while the middle or right mouse button is down
if (nFlags & MK_RBUTTON || nFlags & MK_MBUTTON)
{
if (nFlags & MK_SHIFT)
{
// Get the dimensions of the window
GetClientRect(&rc);
CRect rc;
GetClientRect( rc );
int w = rc.right;
int h = rc.bottom;
// Zoom to mouse position.
float z = m_prevZoomFactor + (point.y - m_cMouseDownPos.y) * 0.1f;
float x = (float)(m_cMouseDownPos.x - m_prevScrollOffset.cx)/m_prevZoomFactor;
float y = (float)(m_cMouseDownPos.y - m_prevScrollOffset.cy)/m_prevZoomFactor;
m_cScrollOffset.x = m_cMouseDownPos.x - z*x;
m_cScrollOffset.y = m_cMouseDownPos.y - z*y;
SetZoomFactor( z );
}
else
{
// Set the new scrolled coordinates
SetScrollOffset(GetScrollOffset() + (point - m_cPrevMousePos));
}
m_cPrevMousePos = point;
}
return;
}
// Only proceed when the mouse is over the drawing area
if (nFlags & MK_LBUTTON)
{
// Calculate the coordinates in heightmap coordinates
// Translate into drawing window coordinates
CPoint hp = ViewToHeightmap(point);
/*
if (!(nFlags & MK_CONTROL))
{
// Add new static objects
GetIEditor()->GetDocument()->GetStatObjMap()->DistributeObjects( hp, m_iBrushSize );
}
else
{
// Remove static objects
GetIEditor()->GetDocument()->GetStatObjMap()->ClearObjects( hp, m_iBrushSize );
}
*/
/*
// Redraw the modified area
rcRedraw.left = point.x - m_scaledBrushSize;
rcRedraw.top = point.y - m_scaledBrushSize;
rcRedraw.right = point.x + m_scaledBrushSize;
rcRedraw.bottom = point.y + m_scaledBrushSize;
RedrawWindow(&rcRedraw, NULL, RDW_NOERASE | RDW_INVALIDATE);
*/
RedrawWindow();
}
CViewport::OnMouseMove(nFlags, point);
}
BOOL CTerrainViewport::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void CTerrainViewport::OnSize(UINT nType, int cx, int cy)
{
CViewport::OnSize(nType, cx, cy);
////////////////////////////////////////////////////////////////////////
// Re-evaluate the zoom / scroll offset values
// TODO: Restore the zoom rectangle instead of resetting it
////////////////////////////////////////////////////////////////////////
float fZoomFac1, fZoomFac2;
fZoomFac1 = (float) cx / m_heightmapSize.cx;
fZoomFac2 = (float) cx / m_heightmapSize.cy;
SetZoomFactor((fZoomFac1 < fZoomFac2) ? fZoomFac1 : fZoomFac2);
}
void CTerrainViewport::OnRButtonDown(UINT nFlags, CPoint point)
{
if (GetViewMode() != NothingMode)
return;
/*
if (GetIEditor()->GetEditMode() == eEditModeCreate)
{
// Stop creation.
GetIEditor()->GetObjectManager()->MouseCreateCallback( this,MOUSECREATE_RPOINT,point,0 );
}
*/
// Save the mouse down position
m_cMouseDownPos = point;
m_cPrevMousePos = point;
m_prevZoomFactor = GetZoomFactor();
m_prevScrollOffset = m_cScrollOffset;
SetCapture();
SetViewMode( ScrollZoomMode );
CViewport::OnRButtonDown(nFlags, point);
}
void CTerrainViewport::OnRButtonUp(UINT nFlags, CPoint point)
{
ReleaseCapture();
SetViewMode( NothingMode );
CViewport::OnRButtonUp(nFlags, point);
}
void CTerrainViewport::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CViewport::OnLButtonDown(nFlags, point);
}
void CTerrainViewport::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CViewport::OnLButtonUp(nFlags, point);
}
CPoint CTerrainViewport::ViewToHeightmap( CPoint hp )
{
hp.x -= m_cScrollOffset.x;
hp.y -= m_cScrollOffset.y;
// Scale with the zoom factor
float z1 = 1.0f / GetZoomFactor();
int x = FloatToIntRet((float)hp.x * z1);
int y = FloatToIntRet((float)hp.y * z1);
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x >= m_heightmapSize.cx) x = m_heightmapSize.cx-1;
if (y >= m_heightmapSize.cx) y = m_heightmapSize.cx-1;
return CPoint( x,y );
}
CPoint CTerrainViewport::HeightmapToView( CPoint hp )
{
int x = FloatToIntRet(hp.x * GetZoomFactor());
int y = FloatToIntRet(hp.y * GetZoomFactor());
// Add the offset
x += m_cScrollOffset.x;
y += m_cScrollOffset.y;
return CPoint( x,y );
}