// DrawWnd.cpp : implementation file // #include "stdafx.h" #include "DrawWnd.h" #include "Heightmap.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #pragma warning (disable : 4800) ///////////////////////////////////////////////////////////////////////////// // CDrawWnd CDrawWnd::CDrawWnd() { //////////////////////////////////////////////////////////////////////// // Load the brushes for drawing and create a DC for them //////////////////////////////////////////////////////////////////////// CBitmap bmpLoad; // Load the ressource VERIFY(m_bmpBrushes.LoadBitmap(IDB_BRUSH)); // Make pink (255, 0, 255) pixels transparent MakeAlpha(m_bmpBrushes); // Create the DC m_dcBrushes.CreateCompatibleDC(NULL); m_dcBrushes.SelectObject(&m_bmpBrushes); // Allocate new memory to hold the water bitmap data m_pWaterTexData = new DWORD[128 * 128]; ASSERT(m_pWaterTexData); // Load the water texture out of the ressource VERIFY(bmpLoad.Attach(::LoadBitmap(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(IDB_WATER)))); // Retrieve the bits from the bitmap VERIFY(bmpLoad.GetBitmapBits(128 * 128 * sizeof(DWORD), m_pWaterTexData)); int HX = GetIEditor()->GetHeightmap()->GetWidth(); // Init other members m_iCurBrush = 2; m_fZoomFactor = 512.0f / HX; m_cScrollOffset = CPoint(0, 0); m_fOpacity = 16; m_pCoordWnd = NULL; m_bShowWater = true; m_bShowMapObj = true; m_fSetToHeight = -1.0f; m_bUseNoiseBrush = false; m_ptMarker = CPoint(0, 0); m_heightmap = GetIEditor()->GetHeightmap(); } CDrawWnd::~CDrawWnd() { if (m_pWaterTexData) { delete [] m_pWaterTexData; m_pWaterTexData = NULL; } if (m_pCoordWnd) m_pCoordWnd->Detach(); } BEGIN_MESSAGE_MAP(CDrawWnd, CWnd) //{{AFX_MSG_MAP(CDrawWnd) ON_WM_PAINT() ON_WM_MOUSEMOVE() ON_WM_MOUSEWHEEL() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_RBUTTONDOWN() ON_WM_MBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CDrawWnd message handlers void CDrawWnd::OnPaint() { //////////////////////////////////////////////////////////////////////// // Paint the window with the heightmap //////////////////////////////////////////////////////////////////////// CPaintDC dc(this); // device context for painting CRect rect; CDC dcHeightmap; CBitmap bmpHeightmap; CHeightmap *pHeightmap = GetIEditor()->GetHeightmap(); DWORD *pImageData = NULL; float fScaleX, fScaleY; int i, j; float fWaterLevel = pHeightmap->GetWaterLevel(); long iYPreCalc; CPoint cTransfPt; CPoint ptTransfMarker = m_ptMarker; // Get the rect of the client window GetClientRect(&rect); // Allocate memory for the bitmap data pImageData = new DWORD[rect.right * rect.bottom]; ASSERT(pImageData); // Calculate the scaling fScaleX = 1.0f / m_fZoomFactor; fScaleY = 1.0f / m_fZoomFactor; int maxHX = pHeightmap->GetWidth(); int maxHY = pHeightmap->GetHeight(); // Copy the heightmap into the bitmap data for (j=0; j= maxHY) continue; for (i=0; i= maxHX) continue; // Get the grayscale value from the heightmap array float h = pHeightmap->GetXY(iXPreCalc,iYPreCalc); // Only draw the water if the flag has been set if (h < fWaterLevel && m_bShowWater) { // Water, fetch a tiled texel from the water texture pImageData[i + j * rect.right] = m_pWaterTexData[i % 128 + ((j % 128) << 7)]; } else { int iColor = ftoi(h); if (iColor > 255) iColor = 255; // Create a ABGR color and store it in the bitmap array pImageData[i + j * rect.right] = (iColor << 16) | (iColor << 8) | iColor; } } } // Create DC and bitmap for the heightmap VERIFY(dcHeightmap.CreateCompatibleDC(&dc)); VERIFY(bmpHeightmap.CreateBitmap(rect.right, rect.bottom, 1, 32, pImageData)); dcHeightmap.SelectObject(&bmpHeightmap); // Free the temporary image data if (pImageData) { delete [] pImageData; pImageData = NULL; } /* if (m_bShowMapObj) { // Draw the map objects over the preview for (i=0; i<(long) GLOBAL_GET_DOC->m_sMapObjects.GetSize(); i++) { // Convert the position to map coordinates cTransfPt.x = (long) (GLOBAL_GET_DOC->m_sMapObjects[i].fX / 2.0f); cTransfPt.y = (long) (GLOBAL_GET_DOC->m_sMapObjects[i].fY / 2.0f); // Convert it to window coordinates HMCoordToWndCoord(&cTransfPt); // Calculate the rectangle that is used to draw the position of the map object ::SetRect(&rcObject, cTransfPt.x - 1, cTransfPt.y - 1, cTransfPt.x + 1, cTransfPt.y + 1); // Get the color of the object switch (GLOBAL_GET_DOC->m_sMapObjects[i].eType) { case MOEntity: // Entities are red dwMapObjColor = 0x000000FF; break; case MOTagPoint: // Tag points are white dwMapObjColor = 0x00FFFFFF; break; case MOBuilding: // Buldings are yellow dwMapObjColor = 0x0000FFFF; break; default: // Use white as default color dwMapObjColor = 0x00FFFFFF; break; } // Draw the rectangle dcHeightmap.FillRect(&rcObject, &CBrush(dwMapObjColor)); } } */ // Draw the current position marker HMCoordToWndCoord(&ptTransfMarker); dcHeightmap.Ellipse(ptTransfMarker.x - 3, ptTransfMarker.y - 3, ptTransfMarker.x + 3, ptTransfMarker.y + 3); // Blit the heightmap to the window dc.BitBlt(0, 0, rect.right, rect.bottom, &dcHeightmap, 0, 0, SRCCOPY); // Do not call CWnd::OnPaint() for painting messages } bool CDrawWnd::Create(CWnd *pwndParent) { //////////////////////////////////////////////////////////////////////// // Use this function instead of the base class versions to create the // window //////////////////////////////////////////////////////////////////////// RECT rcDefault; // Create the window with a custom window class return CreateEx(NULL, AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, AfxGetApp()->LoadCursor(IDC_HAND_INTERNAL), NULL, NULL), "DrawWindow", WS_CHILD | WS_VISIBLE, rcDefault, pwndParent, NULL); } void CDrawWnd::MakeAlpha(CBitmap &ioBM) { //////////////////////////////////////////////////////////////////////// // Make pink pixels transparent //////////////////////////////////////////////////////////////////////// struct RGBAQUAD { BYTE rgbatBlue; BYTE rgbatGreen; BYTE rgbatRed; BYTE rgbatAlpha; }; RGBAQUAD *pixels; // Figure out how many pixels there are in the bitmap BITMAP bmInfo; VERIFY(ioBM.GetBitmap (&bmInfo)); // Add support for additional bit depths if you choose VERIFY(bmInfo.bmBitsPixel == 32); VERIFY(bmInfo.bmWidthBytes == (bmInfo.bmWidth * 4)); if (bmInfo.bmBitsPixel != 32) { CLogFile::WriteLine("Drawing Error: Dektop is set to 16 bit, need 32 bit"); AfxMessageBox("Please set your desktop color depth to 32 bits !"); } const UINT numPixels (bmInfo.bmHeight * bmInfo.bmWidth); // Allocate space to receive the pixel data pixels = new RGBAQUAD[bmInfo.bmHeight * bmInfo.bmWidth]; // Retrieve the pixels VERIFY(ioBM.GetBitmapBits(bmInfo.bmHeight * bmInfo.bmWidth * sizeof(RGBAQUAD), pixels) != 0); // Loop trough all pixels for (UINT i = 0; i < numPixels; ++i) { if (pixels[i].rgbatBlue == 255 && pixels[i].rgbatGreen == 0 && pixels[i].rgbatRed == 255) { pixels[i].rgbatGreen = 0; pixels[i].rgbatRed = 0; pixels[i].rgbatBlue = 0; // Skip pixels that have the background color continue; } } // Write the bitmap data back VERIFY(ioBM.SetBitmapBits(bmInfo.bmHeight * bmInfo.bmWidth * sizeof(RGBAQUAD), pixels) != 0); // Free the pixels delete [] pixels; pixels = 0; } BOOL CDrawWnd::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) { //////////////////////////////////////////////////////////////////////// // Mousewheel is used to control zooming //////////////////////////////////////////////////////////////////////// // Scroll in / out relative to the mouse wheel direction SetZoomFactor(GetZoomFactor() + (zDelta / 120.0f) * 0.15f); // Redraw the window RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); return 0; } bool CDrawWnd::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 iScaledWidth = (long) (m_heightmap->GetWidth() * GetZoomFactor()); // Obtain the size of the window GetClientRect(&rcWndPos); // Don't allow to scroll beyond the lower right corner if (abs(iX) > iScaledWidth - rcWndPos.right) { iX = -(iScaledWidth - rcWndPos.right); bRangeValid = false; } if (abs(iY) > iScaledWidth - rcWndPos.bottom) { iY = -(iScaledWidth - 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); // Redraw the window RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); return bRangeValid; } bool CDrawWnd::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; } // 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 // Obtain the dimensions of the window GetClientRect(&rcWndPos); if (m_heightmap->GetWidth() * fZoomFactor < rcWndPos.right) { // Calculate the new size fZoomFactor = (float) rcWndPos.right / m_heightmap->GetWidth(); bRangeValid = false; } if (m_heightmap->GetWidth() * fZoomFactor < rcWndPos.bottom) { // Calculate the new size fNewSizeTemp = (float) rcWndPos.bottom / m_heightmap->GetWidth(); // 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; // Let SetScrollOffset() find a valid scroll offset // TODO: Scroll centering is broken SetScrollOffset((long) (GetScrollOffset().x * fEnlargementFactor), (long) (GetScrollOffset().y * fEnlargementFactor)); return bRangeValid; } void CDrawWnd::OnMButtonDown(UINT nFlags, CPoint point) { //////////////////////////////////////////////////////////////////////// // New marker position //////////////////////////////////////////////////////////////////////// m_ptMarker = point; WndCoordToHMCoord(&m_ptMarker); RedrawWindow(); CWnd::OnMButtonDown(nFlags, point); } afx_msg void CDrawWnd::OnLButtonDown(UINT nFlags, CPoint point) { //////////////////////////////////////////////////////////////////////// // User pressed the left mouse button //////////////////////////////////////////////////////////////////////// RECT rcClient; GetClientRect(&rcClient); // Save the mouse down position m_cMouseDownPos = point; // Capture mouse input for this window, we need it for the scrolling SetCapture(); // Call the mouse move function to immediatly draw a spot OnMouseMove(nFlags, point); } afx_msg void CDrawWnd::OnRButtonDown(UINT nFlags, CPoint point) { //////////////////////////////////////////////////////////////////////// // User pressed the right mouse button //////////////////////////////////////////////////////////////////////// RECT rcClient; GetClientRect(&rcClient); // Save the mouse down position m_cMouseDownPos = point; // Capture mouse input for this window, we need it for the scrolling SetCapture(); // Call the mouse move function to immediatly draw a spot OnMouseMove(nFlags, point); } void CDrawWnd::OnMouseMove(UINT nFlags, CPoint point) { RECT rcWin; char szCoordText[256]; if (nFlags & MK_LBUTTON || nFlags & MK_MBUTTON) { // CTRL or SHIFT indicate scrolling and zooming if (nFlags & MK_CONTROL || nFlags & MK_MBUTTON) { // You can only scroll while the left mouse button is down and you hold down CTRL // Set the new scrolled coordinates SetScrollOffset(GetScrollOffset() + (point - m_cMouseDownPos)); } else if (nFlags & MK_SHIFT) { // You can only zoom while the left mouse button is down and you hold down SHIFT // Get the dimensions of the window GetClientRect(&rcWin); // Zoom SetZoomFactor(GetZoomFactor() + ((point.y - m_cMouseDownPos.y) / (float) rcWin.bottom)); } else { // None of the modificator keys pressed, draw // Get the heightmap coordinates of the clicked point WndCoordToHMCoord(&point); // Draw a spot m_heightmap->DrawSpot(point.y, point.x, (uint8) (10.0f + m_iCurBrush * 25.0f), m_fOpacity, m_fSetToHeight, m_bUseNoiseBrush); } // Redraw the window RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } else if (nFlags & MK_RBUTTON) { // Get the heightmap coordinates of the clicked point WndCoordToHMCoord(&point); // Draw a spot m_heightmap->DrawSpot(point.y, point.x, (uint8) (10.0f + m_iCurBrush * 25.0f), -m_fOpacity, m_fSetToHeight); // Redraw the window RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW); } // Save the new mouse down position m_cMouseDownPos = point; // Display the current coordinates in the coordinate window if (m_pCoordWnd) { // Convert to meters WndCoordToHMCoord(&point); point.x *= 2; point.y *= 2; // Create and set the string sprintf(szCoordText, "X: %i Y: %i", point.x, point.y); m_pCoordWnd->SetWindowText(szCoordText); } CWnd::OnMouseMove(nFlags, point); } afx_msg void CDrawWnd::OnLButtonUp(UINT nFlags, CPoint point) { //////////////////////////////////////////////////////////////////////// // User released the left mouse button //////////////////////////////////////////////////////////////////////// // Release the restriction of the cursor ReleaseCapture(); CWnd::OnLButtonUp(nFlags, point); } void CDrawWnd::WndCoordToHMCoord(CPoint *pWndPt) { //////////////////////////////////////////////////////////////////////// // Transform a point from window space into map space //////////////////////////////////////////////////////////////////////// long lTemp; ASSERT(pWndPt); // Add the offset pWndPt->x += abs(GetScrollOffset().x); pWndPt->y += abs(GetScrollOffset().y); // Swap the axis lTemp = pWndPt->x; pWndPt->x = pWndPt->y; pWndPt->y = lTemp; // Scale with the zoom factor pWndPt->x = (long) (pWndPt->x / GetZoomFactor()); pWndPt->y = (long) (pWndPt->y / GetZoomFactor()); // Scale from map texture coordinates to heightmap coordinates pWndPt->x = (long) (pWndPt->x / (float) m_heightmap->GetWidth() * m_heightmap->GetWidth()); pWndPt->y = (long) (pWndPt->y / (float) m_heightmap->GetHeight() * m_heightmap->GetHeight()); } void CDrawWnd::HMCoordToWndCoord(CPoint *pWndPt) { //////////////////////////////////////////////////////////////////////// // Transform a point from map space into window space //////////////////////////////////////////////////////////////////////// RECT rcWndPos; long lTemp; // Obtain the window dimensions GetClientRect(&rcWndPos); // Swap the axis lTemp = pWndPt->x; pWndPt->x = pWndPt->y; pWndPt->y = lTemp; pWndPt->x = (long) (pWndPt->x / (float) m_heightmap->GetWidth() * (m_heightmap->GetWidth() * GetZoomFactor())); pWndPt->y = (long) (pWndPt->y / (float) m_heightmap->GetHeight() * (m_heightmap->GetWidth() * GetZoomFactor())); // Add the offset pWndPt->x += GetScrollOffset().x; pWndPt->y += GetScrollOffset().y; } /* void CDrawWnd::OnMouseMove(UINT nFlags, CPoint point) { //////////////////////////////////////////////////////////////////////// // Use the current brush / rubber to draw on the heightmap //////////////////////////////////////////////////////////////////////// CPoint TransformedPoint; UINT iScaledBrushWidth, iScaledBrushHeight; BLENDFUNCTION bfBrushBlend; typedef BOOL (CALLBACK * ALPHABLEND) (HDC, int, int, int, int, HDC, int, int, int, int, BLENDFUNCTION); ALPHABLEND pfnAlphaBlend = NULL; BOOL bSuccess; HINSTANCE hLib; RECT rcInvalid; // Only draw when the left mouse button is pressed if (!nFlags & MK_LBUTTON) return; // Transform mouse coordinates into heightmap coordinates TransformedPoint.x = (LONG) ((point.x / 350.0f) * GLOBAL_GET_DOC->m_cHeightmap.GetWidth()); TransformedPoint.y = (LONG) ((point.y / 350.0f) * GLOBAL_GET_DOC->m_cHeightmap.GetHeight()); // Calculate the scaled brush size iScaledBrushWidth = (UINT) ((350.0f / GLOBAL_GET_DOC->m_cHeightmap.GetWidth()) * 32); iScaledBrushHeight = (UINT) ((350.0f / GLOBAL_GET_DOC->m_cHeightmap.GetHeight()) * 32); // Scale the brush (TODO: Make this not hardcoded but selectable) iScaledBrushWidth *= 4; iScaledBrushHeight *= 4; // Center brush TransformedPoint.x -= iScaledBrushWidth / 2; TransformedPoint.y -= iScaledBrushHeight / 2; // Random offset TransformedPoint.x -= 2 - ((LONG) (rand() / (float) RAND_MAX * 4)); TransformedPoint.y -= 2 - ((LONG) (rand() / (float) RAND_MAX * 4)); // Are we in rubber mode ? if (m_bRubberMode) { // Just erase a rectangular area GLOBAL_GET_DOC->m_dcHeightmap.BitBlt(TransformedPoint.x, TransformedPoint.y, iScaledBrushWidth, iScaledBrushHeight, &GLOBAL_GET_DOC->m_dcHeightmap, 0, 0, BLACKNESS); // Update the painted part of the window SetRect(&rcInvalid, point.x - iScaledBrushWidth / 2, point.y - iScaledBrushHeight / 2, point.x + iScaledBrushWidth / 2, point.y + iScaledBrushHeight / 2); InvalidateRect(&rcInvalid); } else { // Load the imaging library hLib = LoadLibrary("msimg32.dll"); assert(hLib); // Query the function pointer for AlphaBlend pfnAlphaBlend = (ALPHABLEND) GetProcAddress(hLib, "AlphaBlend"); if (!pfnAlphaBlend) { CLogFile::WriteLine("Drawing Error: Can't find entry point for AlphaBlend() or can't load msimg32.dll at all"); AfxMessageBox("Can't find entry point for AlphaBlend() or can't load msimg32.dll at all. " \ "Upgrade to Win98 / WinME / Win2K / WinXP or install the needed DLL manually"); assert(pfnAlphaBlend); return; } // Setup the blend function bfBrushBlend.BlendOp = AC_SRC_OVER; bfBrushBlend.BlendFlags = 0; bfBrushBlend.SourceConstantAlpha = 32; bfBrushBlend.AlphaFormat = AC_SRC_NO_PREMULT_ALPHA; // Paint the brush bSuccess = (* pfnAlphaBlend) (GLOBAL_GET_DOC->m_dcHeightmap.m_hDC, TransformedPoint.x, TransformedPoint.y, iScaledBrushWidth, iScaledBrushHeight, m_dcBrushes.m_hDC, m_iCurBrush * 32, 0, 32, 32, bfBrushBlend); assert(bSuccess); // Update the painted part of the window SetRect(&rcInvalid, point.x - iScaledBrushWidth / 2, point.y - iScaledBrushHeight / 2, point.x + iScaledBrushWidth / 2, point.y + iScaledBrushHeight / 2); InvalidateRect(&rcInvalid); // We dont need the imaging library anymore FreeLibrary(hLib); } // We modified the document GLOBAL_GET_DOC->SetModifiedFlag(); // Need to generate all layers from scratch GLOBAL_GET_DOC->InvalidateLayers(); CWnd::OnMouseMove(nFlags, point); } */