diff --git a/BiomeVisualiser/BiomeCache.cpp b/BiomeVisualiser/BiomeCache.cpp new file mode 100644 index 000000000..962f4a3f0 --- /dev/null +++ b/BiomeVisualiser/BiomeCache.cpp @@ -0,0 +1,333 @@ + +// BiomeCache.cpp + +// Implements the cBiomeCache class representing a biome source that caches data from the underlying biome source + +#include "Globals.h" +#include "BiomeCache.h" +#include "Timer.h" + + + + + +static int GetNumCores(void) +{ + // Get number of cores by querying the system process affinity mask + DWORD Affinity, ProcAffinity; + GetProcessAffinityMask(GetCurrentProcess(), &ProcAffinity, &Affinity); + int NumCores = 0; + while (Affinity > 0) + { + if ((Affinity & 1) == 1) + { + NumCores++; + } + Affinity >>= 1; + } // while (Affinity > 0) + return NumCores; +} + + + + + +cBiomeCache::cBiomeCache(void) : + m_Source(NULL), + m_BaseX(MAXINT), + m_BaseZ(MAXINT), + m_Available(NULL), + m_IsTerminatingThreads(false) +{ + int NumThreads = GetNumCores(); + NumThreads--; // One core should be left for the system to run on ;) + for (int i = NumThreads; i > 0; i--) + { + cThread * Thread = new cThread(*this); + m_Threads.push_back(Thread); + Thread->Start(); + } +} + + + + +cBiomeCache::~cBiomeCache() +{ + m_IsTerminatingThreads = true; + for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr) + { + m_evtQueued.Set(); + } + for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr) + { + delete *itr; + } + m_Threads.clear(); + + SetSource(NULL); +} + + + + + +cBiomeSource::eAvailability cBiomeCache::GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) +{ + if (m_Source == NULL) + { + return baNever; + } + + // Look up using the cache: + int x = a_ChunkX - m_BaseX; + int z = a_ChunkZ - m_BaseZ; + if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height)) + { + // Outside the cached region + return baNever; + } + + cCSLock Lock(m_CS); + cItem * Item = m_Available[x + m_Width * z]; + if (Item == NULL) + { + // Item hasn't been processed yet + return baLater; + } + if (Item->m_IsValid) + { + memcpy(a_Biomes, Item->m_Biomes, sizeof(a_Biomes)); + return baNow; + } + + // Item has been processed, but the underlying source refused to give the data to us + return baNever; +} + + + + + +void cBiomeCache::HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) +{ + cTimer Timer("Cache: HintViewArea"); + + if ( + (a_MinChunkX == m_BaseX) && + (a_MaxChunkX == m_BaseX + m_Width - 1) && + (a_MinChunkZ == m_BaseZ) && + (a_MaxChunkZ == m_BaseZ + m_Height - 1) + ) + { + // The same set of parameters, bail out + return; + } + + if (m_Source != NULL) + { + m_Source->HintViewArea(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ); + } + + int NewWidth = a_MaxChunkX - a_MinChunkX + 1; + int NewHeight = a_MaxChunkZ - a_MinChunkZ + 1; + + // Make a new empty cache table: + pItem * NewAvailable = new pItem[NewWidth * NewHeight]; + for (int i = NewWidth * NewHeight - 1; i >= 0; --i) + { + NewAvailable[i] = NULL; + } + + // Move the common contents of the old table into the new table: + cCSLock Lock(m_CS); + for (int z = 0; z < NewHeight; z++) + { + int OldZ = z + a_MinChunkZ - m_BaseZ; + if ((OldZ < 0) || (OldZ >= m_Height)) + { + continue; + } + for (int x = 0; x < NewWidth; x++) + { + int OldX = x + a_MinChunkX - m_BaseX; + if ((OldX < 0) || (OldX >= m_Width)) + { + continue; + } + NewAvailable[x + NewWidth * z] = m_Available[OldX + m_Width * OldZ]; + m_Available[OldX + m_Width * OldZ] = NULL; + } // for x + } // for z + + // All items that aren't common go into the pool: + for (int idx = 0, z = 0; z < m_Height; z++) + { + for (int x = 0; x < m_Width; ++x, ++idx) + { + if (m_Available[idx] != NULL) + { + m_Pool.push_back(m_Available[idx]); + m_Available[idx] = NULL; + } + } + } + + // Replace the cache table: + delete m_Available; + m_Available = NewAvailable; + m_Width = NewWidth; + m_Height = NewHeight; + m_BaseX = a_MinChunkX; + m_BaseZ = a_MinChunkZ; + + // Remove all items outside the coords: + FilterOutItems(m_Queue, a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ); + + // Queue all items from inside the coords into m_Queue: + for (int z = 0; z < NewHeight; z++) + { + for (int x = 0; x < NewWidth; x++) + { + if (m_Available[x + m_Width * z] != NULL) + { + // Already calculated, skip + continue; + } + + if (m_Pool.empty()) + { + m_Pool.push_back(new cItem(x + a_MinChunkX, z + a_MinChunkZ)); + } + ASSERT(!m_Pool.empty()); + m_Pool.back()->m_ChunkX = x + a_MinChunkX; + m_Pool.back()->m_ChunkZ = z + a_MinChunkZ; + m_Queue.push_back(m_Pool.back()); + m_Pool.pop_back(); + m_evtQueued.Set(); + } // for x + } // for z +} + + + + + +void cBiomeCache::SetSource(cBiomeSource * a_Source) +{ + // TODO: Stop all threads, so that they don't use the source anymore! + + delete m_Source; + m_Source = a_Source; + + // Invalidate cache contents: + cCSLock Lock(m_CS); + m_BaseX = MAXINT; + m_BaseZ = MAXINT; + m_Pool.splice(m_Pool.end(), m_Queue); +} + + + + + +void cBiomeCache::FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) +{ + for (cItems::iterator itr = a_Items.begin(); itr != a_Items.end();) + { + if ( + ((*itr)->m_ChunkX < a_MinChunkX) || + ((*itr)->m_ChunkX > a_MaxChunkX) || + ((*itr)->m_ChunkX < a_MinChunkX) || + ((*itr)->m_ChunkX > a_MaxChunkX) + ) + { + m_Pool.push_back(*itr); + itr = a_Items.erase(itr); + } + else + { + ++itr; + } + } +} + + + + + +void cBiomeCache::thrProcessQueueItem(void) +{ + cItem * Item = NULL; + { + cCSLock Lock(m_CS); + if (m_Queue.empty()) + { + cCSUnlock Unlock(Lock); + m_evtQueued.Wait(); + } + if (m_IsTerminatingThreads || m_Queue.empty()) + { + // We've been woken up only to die / spurious wakeup + return; + } + Item = m_Queue.back(); + m_Queue.pop_back(); + } + + // Process the item: + Item->m_IsValid = (m_Source->GetBiome(Item->m_ChunkX, Item->m_ChunkZ, Item->m_Biomes) == baNow); + + // Store result: + cCSLock Lock(m_CS); + int x = Item->m_ChunkX - m_BaseX; + int z = Item->m_ChunkZ - m_BaseZ; + if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height)) + { + // The cache rectangle has changed under our fingers, drop this chunk + return; + } + m_Available[x + m_Width * z] = Item; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBiomeCache::cItem: + +cBiomeCache::cItem::cItem(int a_ChunkX, int a_ChunkZ) : + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBiomeCache::cThread: + +cBiomeCache::cThread::cThread(cBiomeCache & a_Parent) : + super("Biome cache thread"), + m_Parent(a_Parent) +{ +} + + + + + +void cBiomeCache::cThread::Execute(void) +{ + while (!m_ShouldTerminate && !m_Parent.m_IsTerminatingThreads) + { + m_Parent.thrProcessQueueItem(); + } +} + + + + diff --git a/BiomeVisualiser/BiomeCache.h b/BiomeVisualiser/BiomeCache.h new file mode 100644 index 000000000..86602a19d --- /dev/null +++ b/BiomeVisualiser/BiomeCache.h @@ -0,0 +1,96 @@ + +// BiomeCache.h + +// Declares the cBiomeCache class representing a biome source that caches data from the underlying biome source + +/* +This cache works a bit differently than regular caches. +It first receives the hint of area that it will need to provide. +The Cache uses several threads to request biomes from the underlying source to fill that area. +While the area is being filled, requests for biomes may already come, such requests are answered with baLater if no data yet. +*/ + +#pragma once + + + + + +#include "BiomeSource.h" +#include "../source/OSSupport/IsThread.h" + + + + + +class cBiomeCache : + public cBiomeSource +{ +public: + cBiomeCache(void); + ~cBiomeCache(); + + // cBiomeSource overrides: + virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override; + virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) override; + + void SetSource(cBiomeSource * a_Source); // Takes ownership of the source ptr + +protected: + class cItem + { + public: + cItem(int a_ChunkX, int a_ChunkZ); + + int m_ChunkX; + int m_ChunkZ; + bool m_IsValid; + cChunkDef::BiomeMap m_Biomes; + } ; + + typedef cItem * pItem; + typedef std::list cItems; + + class cThread : + public cIsThread + { + typedef cIsThread super; + + public: + cThread(cBiomeCache & a_Parent); + + // cIsThread overrides: + virtual void Execute(void) override; + + protected: + cBiomeCache & m_Parent; + } ; + + typedef std::list cThreads; + + cBiomeSource * m_Source; + + cCriticalSection m_CS; + int m_BaseX; ///< MinChunkX for the m_Available rectangle + int m_BaseZ; ///< MinChunkZ for the m_Available rectangle + int m_Width; ///< Width of the m_Available rectangle + int m_Height; ///< Height of the m_Available rectangle + pItem * m_Available; ///< Items that have already been processed (baNow or baNever), [x + m_Width * z] + cItems m_Queue; ///< Items that are queued for processing (baLater) + cItems m_Pool; ///< Items that are not needed anymore, can be reused for other coords + + cEvent m_evtQueued; // Triggerred when an item is added to m_Queue + + cThreads m_Threads; // Threads that update the cache. + bool m_IsTerminatingThreads; // Set to true to indicate to all threads that they should exit + + /// Removes from a_Items all items that are outside of the given coords, moves those into m_Pool + void FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ); + + /// Processes one item from m_Queue into m_Available. Blocks if m_Queue is empty; respects m_IsTerminatingThreads + void thrProcessQueueItem(void); +} ; + + + + diff --git a/BiomeVisualiser/BiomeRenderer.cpp b/BiomeVisualiser/BiomeRenderer.cpp new file mode 100644 index 000000000..00d6d684d --- /dev/null +++ b/BiomeVisualiser/BiomeRenderer.cpp @@ -0,0 +1,147 @@ + +// BiomeRenderer.cpp + +// Implements the cBiomeRenderer class representing the rendering engine + +#include "Globals.h" +#include "BiomeRenderer.h" +#include "Pixmap.h" +#include "Timer.h" + + + + + +cBiomeRenderer::cBiomeRenderer(void) : + m_OriginX(160), + m_OriginY(160), + m_Zoom(1) +{ +} + + + + +void cBiomeRenderer::SetSource(cBiomeSource * a_Source) +{ + m_Cache.SetSource(a_Source); +} + + + + + +bool cBiomeRenderer::Render(cPixmap & a_Pixmap) +{ + cTimer Timer("cBiomeRenderer::Render"); + + int Wid = a_Pixmap.GetWidth(); + int Hei = a_Pixmap.GetHeight(); + + // Hint the approximate view area to the biome source so that it can adjust its caches: + int MinBlockX = ( - m_OriginX) / m_Zoom; + int MaxBlockX = (Wid - m_OriginX) / m_Zoom; + int MinBlockZ = ( - m_OriginY) / m_Zoom; + int MaxBlockZ = (Hei - m_OriginY) / m_Zoom; + m_Cache.HintViewArea(MinBlockX / 16 - 1, MaxBlockX / 16 + 1, MinBlockZ / 16 - 1, MaxBlockZ / 16 + 1); + + // Hold one current chunk of biome data: + int CurChunkX = MAXINT32; + int CurChunkZ = MAXINT32; + cChunkDef::BiomeMap CurBiomes; + + bool res = false; + + for (int y = 0; y < Hei; y++) + { + int BlockZ = (y - m_OriginY) / m_Zoom; + int ChunkZ = (BlockZ >= 0) ? (BlockZ / 16) : ((BlockZ + 1) / 16 - 1); + int RelZ = BlockZ - ChunkZ * 16; + for (int x = 0; x < Wid; x++) + { + int BlockX = (x - m_OriginX) / m_Zoom; + int ChunkX = (BlockX >= 0) ? (BlockX / 16) : ((BlockX + 1) / 16 - 1); + int RelX = BlockX - ChunkX * 16; + if ((ChunkZ != CurChunkZ) || (ChunkX != CurChunkX)) + { + CurChunkX = ChunkX; + CurChunkZ = ChunkZ; + switch (m_Cache.GetBiome(CurChunkX, CurChunkZ, CurBiomes)) + { + case cBiomeSource::baLater: + { + res = true; + // fallthrough: + } + case cBiomeSource::baNever: + { + for (int i = 0; i < ARRAYCOUNT(CurBiomes); i++) + { + CurBiomes[i] = (EMCSBiome)-1; + } + break; + } + } // switch (Biome availability) + } + EMCSBiome Biome = cChunkDef::GetBiome(CurBiomes, RelX, RelZ); + a_Pixmap.SetPixel(x, y, GetBiomeColor(Biome)); + } // for x + } // for y + return res; +} + + + + + +int cBiomeRenderer::GetBiomeColor(EMCSBiome a_Biome) +{ + if ((a_Biome < 0) || (a_Biome > biMaxBiome)) + { + return 0xcfcfcf; // LtGray for unknown biomes + } + + static int BiomeColor[] = + { + // RGB: + 0x0000ff, /* Ocean */ + 0x00cf3f, /* Plains */ + 0xffff00, /* Desert */ + 0x7f7f7f, /* Extreme Hills */ + 0x00cf00, /* Forest */ + 0x007f3f, /* Taiga */ + 0x3f7f00, /* Swampland */ + 0x003fff, /* River */ + 0x7f0000, /* Hell */ + 0x007fff, /* Sky */ + 0x3f3fff, /* Frozen Ocean */ + 0x3f3fff, /* Frozen River */ + 0x7fffcf, /* Ice Plains */ + 0x3fcf7f, /* Ice Mountains */ + 0xcf00cf, /* Mushroom Island */ + 0x7f00ff, /* Mushroom Island Shore */ + 0xffff3f, /* Beach */ + 0xcfcf00, /* Desert Hills */ + 0x00cf3f, /* Forest Hills */ + 0x006f1f, /* Taiga Hills */ + 0x7f8f7f, /* Extreme Hills Edge */ + 0x004f00, /* Jungle */ + 0x003f00, /* Jungle Hills */ + } ; + + return BiomeColor[a_Biome]; +} + + + + + +void cBiomeRenderer::MoveViewBy(int a_OffsX, int a_OffsY) +{ + m_OriginX += a_OffsX; + m_OriginY += a_OffsY; +} + + + + diff --git a/BiomeVisualiser/BiomeRenderer.h b/BiomeVisualiser/BiomeRenderer.h new file mode 100644 index 000000000..369c0f114 --- /dev/null +++ b/BiomeVisualiser/BiomeRenderer.h @@ -0,0 +1,50 @@ + +// BiomeRenderer.h + +// Declares the cBiomeRenderer class representing the rendering engine + + + + + +#pragma once + +#include "BiomeCache.h" + + + + + +// fwd: Pixmap.h +class cPixmap; + + + + + +class cBiomeRenderer +{ +public: + cBiomeRenderer(void); + + void SetSource(cBiomeSource * a_Source); // Takes ownership of the source + + /// Renders the biomes into the given pixmap. Returns true if some biome data was missing and can be retrieved later + bool Render(cPixmap & a_Pixmap); + + /// Returns the RGB color value for the specified biome + int GetBiomeColor(EMCSBiome a_Biome); + + void MoveViewBy(int a_OffsX, int a_OffsY); + +protected: + cBiomeCache m_Cache; + + int m_OriginX; + int m_OriginY; + int m_Zoom; +} ; + + + + diff --git a/BiomeVisualiser/BiomeSource.h b/BiomeVisualiser/BiomeSource.h new file mode 100644 index 000000000..e09242d04 --- /dev/null +++ b/BiomeVisualiser/BiomeSource.h @@ -0,0 +1,37 @@ + +// BiomeSource.h + +// Declares the cBiomeSource abstract class used as an interface for getting biomes from any source + +#pragma once + + + + + +#include "ChunkDef.h" + + + + + +class cBiomeSource abstract +{ +public: + enum eAvailability + { + baNow, // Data returned now + baLater, // Data not returned, but will be available later, try again after a while + baNever, // Data not returned, will not be available at all + } ; + + /// Fills a_Biomes with the biomes for the chunk specified + virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) = 0; + + /// Used to inform the source about the view area that will be queried in the near future. + virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) = 0; +} ; + + + + diff --git a/BiomeVisualiser/BiomeViewWnd.cpp b/BiomeVisualiser/BiomeViewWnd.cpp new file mode 100644 index 000000000..cb634034c --- /dev/null +++ b/BiomeVisualiser/BiomeViewWnd.cpp @@ -0,0 +1,190 @@ + +// BiomeViewWnd.cpp + +// Implements the cBiomeViewWnd class representing the window that displays biomes + +#include "Globals.h" +#include "BiomeViewWnd.h" +#include "BiomeCache.h" +#include "GeneratorBiomeSource.h" +#include "../iniFile/iniFile.h" + + + + + +const int TIMER_RERENDER = 1200; + + + + + +cBiomeViewWnd::cBiomeViewWnd(void) : + m_Wnd(NULL), + m_Thunk(&cBiomeViewWnd::WndProc, this), + m_IsLButtonDown(false) +{ +} + + + + + +bool cBiomeViewWnd::Create(HWND a_ParentWnd, LPCTSTR a_Title) +{ + ASSERT(m_Wnd == NULL); + + // Create a regular STATIC window, then override its window procedure with our own. No need for obnoxious RegisterWindowClass() stuff. + m_Wnd = CreateWindow("STATIC", a_Title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 400, 300, a_ParentWnd, NULL, GetModuleHandle(NULL), NULL); + if (m_Wnd == NULL) + { + LOGERROR("Cannot create main window: %d", GetLastError()); + return false; + } + SetWindowLongPtr(m_Wnd, GWLP_WNDPROC, m_Thunk); + + cIniFile IniFile; + cBiomeGen * BioGen = new cBioGenMultiStepMap(0); + BioGen->Initialize(IniFile); + m_Renderer.SetSource(new cGeneratorBiomeSource(BioGen)); + + return true; +} + + + + + + +LRESULT cBiomeViewWnd::WndProc(HWND a_Wnd, UINT a_Msg, WPARAM wParam, LPARAM lParam) +{ + switch (a_Msg) + { + case WM_CLOSE: return OnClose(); + case WM_COMMAND: return OnCommand(wParam, lParam); + case WM_LBUTTONDOWN: return OnLButtonDown(wParam, lParam); + case WM_LBUTTONUP: return OnLButtonUp (wParam, lParam); + case WM_MOUSEMOVE: return OnMouseMove (wParam, lParam); + case WM_PAINT: return OnPaint(); + case WM_TIMER: return OnTimer(wParam); + } + return ::DefWindowProc(a_Wnd, a_Msg, wParam, lParam); +} + + + + + +LRESULT cBiomeViewWnd::OnClose(void) +{ + PostQuitMessage(0); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnCommand(WPARAM wParam, LPARAM lParam) +{ + // TODO: Handle menu commands, when we get menu + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam) +{ + m_IsLButtonDown = true; + GetCursorPos(&m_MouseDown); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnMouseMove(WPARAM wParam, LPARAM lParam) +{ + if (!m_IsLButtonDown) + { + return 0; + } + POINT pnt; + GetCursorPos(&pnt); + m_Renderer.MoveViewBy(pnt.x - m_MouseDown.x, pnt.y - m_MouseDown.y); + if (m_Renderer.Render(m_Pixmap)) + { + SetTimer(m_Wnd, TIMER_RERENDER, 200, NULL); + } + m_MouseDown = pnt; + InvalidateRect(m_Wnd, NULL, FALSE); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnLButtonUp(WPARAM wParam, LPARAM lParam) +{ + OnMouseMove(wParam, lParam); // Last movement - if the mouse move hasn't been reported due to speed + m_IsLButtonDown = false; + InvalidateRect(m_Wnd, NULL, FALSE); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnPaint(void) +{ + PAINTSTRUCT ps; + HDC DC = BeginPaint(m_Wnd, &ps); + + RECT rc; + GetClientRect(m_Wnd, &rc); + int Wid = rc.right - rc.left; + int Hei = rc.bottom - rc.top; + if ((m_Pixmap.GetWidth() != Wid) || (m_Pixmap.GetHeight() != Hei)) + { + m_Pixmap.SetSize(Wid, Hei); + if (m_Renderer.Render(m_Pixmap)) + { + SetTimer(m_Wnd, TIMER_RERENDER, 200, NULL); + } + } + + m_Pixmap.DrawToDC(DC, 0, 0); + + EndPaint(m_Wnd, &ps); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnTimer(WPARAM wParam) +{ + switch (wParam) + { + case TIMER_RERENDER: + { + if (!m_Renderer.Render(m_Pixmap)) + { + KillTimer(m_Wnd, TIMER_RERENDER); + } + InvalidateRect(m_Wnd, NULL, FALSE); + break; + } + } + return 0; +} + + + + diff --git a/BiomeVisualiser/BiomeViewWnd.h b/BiomeVisualiser/BiomeViewWnd.h new file mode 100644 index 000000000..d8ad8d182 --- /dev/null +++ b/BiomeVisualiser/BiomeViewWnd.h @@ -0,0 +1,46 @@ + +// BiomeViewWnd.h + +// Declares the cBiomeViewWnd class representing the window that displays biomes + +#include "WndProcThunk.h" +#include "BiomeRenderer.h" +#include "BiomeCache.h" +#include "Pixmap.h" + + + + + +class cBiomeViewWnd +{ +public: + cBiomeViewWnd(void); + + bool Create(HWND a_ParentWnd, LPCTSTR a_Title); + +protected: + HWND m_Wnd; + CWndProcThunk m_Thunk; + + cBiomeRenderer m_Renderer; + cPixmap m_Pixmap; + + bool m_IsLButtonDown; + POINT m_MouseDown; + + LRESULT WndProc(HWND a_Wnd, UINT a_Msg, WPARAM wParam, LPARAM lParam); + + // Message handlers: + LRESULT OnClose (void); + LRESULT OnCommand (WPARAM wParam, LPARAM lParam); + LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam); + LRESULT OnMouseMove (WPARAM wParam, LPARAM lParam); + LRESULT OnLButtonUp (WPARAM wParam, LPARAM lParam); + LRESULT OnPaint (void); + LRESULT OnTimer (WPARAM wParam); +} ; + + + + diff --git a/BiomeVisualiser/BiomeVisualiser.cpp b/BiomeVisualiser/BiomeVisualiser.cpp new file mode 100644 index 000000000..dc1d490e8 --- /dev/null +++ b/BiomeVisualiser/BiomeVisualiser.cpp @@ -0,0 +1,52 @@ + +// BiomeVisualiser.cpp + +// Implements the cBiomeVisualiser class representing the entire app. Also implements the WinMain() entrypoint + +#include "Globals.h" +#include "time.h" +#include "BiomeVisualiser.h" + + + + + +int WINAPI WinMain(HINSTANCE a_Instance, HINSTANCE a_PrevInstance, LPSTR a_CmdLine, int a_ShowCmd) +{ + cBiomeVisualiser App; + return App.Run(); +} + + + + + +cBiomeVisualiser::cBiomeVisualiser(void) + // : m_Logger(Printf("BiomeVisualiser_%08x", time(NULL))) +{ +} + + + + + +int cBiomeVisualiser::Run(void) +{ + if (!m_MainWnd.Create(GetDesktopWindow(), TEXT("BiomeVisualiser"))) + { + LOGERROR("Cannot create main window: %d", GetLastError()); + return 1; + } + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } // while (GetMessage) + return msg.lParam; +} + + + + diff --git a/BiomeVisualiser/BiomeVisualiser.h b/BiomeVisualiser/BiomeVisualiser.h new file mode 100644 index 000000000..4788bb7ea --- /dev/null +++ b/BiomeVisualiser/BiomeVisualiser.h @@ -0,0 +1,31 @@ + +// BiomeVisualiser.h + +// Declares the cBiomeVisualiser class representing the entire application + + + + + +#include "BiomeViewWnd.h" + + + + + +class cBiomeVisualiser +{ +public: + cBiomeVisualiser(void); + + int Run(void); + +protected: + cBiomeViewWnd m_MainWnd; + + cMCLogger m_Logger; +} ; + + + + diff --git a/BiomeVisualiser/BiomeVisualiser.sln b/BiomeVisualiser/BiomeVisualiser.sln new file mode 100644 index 000000000..b10d65f2f --- /dev/null +++ b/BiomeVisualiser/BiomeVisualiser.sln @@ -0,0 +1,23 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BiomeVisualiser", "BiomeVisualiser.vcproj", "{6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release profiled|Win32 = Release profiled|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Debug|Win32.Build.0 = Debug|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release profiled|Win32.ActiveCfg = Release profiled|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release profiled|Win32.Build.0 = Release profiled|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release|Win32.ActiveCfg = Release|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/BiomeVisualiser/BiomeVisualiser.vcproj b/BiomeVisualiser/BiomeVisualiser.vcproj new file mode 100644 index 000000000..45eb337e3 --- /dev/null +++ b/BiomeVisualiser/BiomeVisualiser.vcproj @@ -0,0 +1,483 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BiomeVisualiser/GeneratorBiomeSource.h b/BiomeVisualiser/GeneratorBiomeSource.h new file mode 100644 index 000000000..34970491e --- /dev/null +++ b/BiomeVisualiser/GeneratorBiomeSource.h @@ -0,0 +1,42 @@ + +// GeneratorBiomeSource.h + +// Declares the cGeneratorBiomeSource that adapts a cBiomeGen into a cBiomeSource + +#include "../source/Generating/BioGen.h" +#include "BiomeSource.h" + + + + + +class cGeneratorBiomeSource : + public cBiomeSource +{ +public: + cGeneratorBiomeSource(cBiomeGen * a_Generator) : m_Generator(a_Generator) {} // Takes ownership of the generator ptr + + ~cGeneratorBiomeSource() + { + delete m_Generator; + } + + // cBiomeSource overrides: + virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override + { + m_Generator->GenBiomes(a_ChunkX, a_ChunkZ, a_Biomes); + return baNow; + } + + virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) override + { + // Nothing needed + } + +protected: + cBiomeGen * m_Generator; +} ; + + + + diff --git a/BiomeVisualiser/Pixmap.cpp b/BiomeVisualiser/Pixmap.cpp new file mode 100644 index 000000000..39a015b9c --- /dev/null +++ b/BiomeVisualiser/Pixmap.cpp @@ -0,0 +1,120 @@ + +// Pixmap.cpp + +// Implements the cPixmap class that represents a RGB pixmap and allows simple operations on it + +#include "Globals.h" +#include "Pixmap.h" + + + + + +cPixmap::cPixmap(void) : + m_Width(0), + m_Height(0), + m_Stride(0), + m_Pixels(NULL) +{ +} + + + + + +cPixmap::cPixmap(int a_Width, int a_Height) : + m_Width(0), + m_Height(0), + m_Stride(0), + m_Pixels(NULL) +{ + SetSize(a_Width, a_Height); +} + + + + + +cPixmap::~cPixmap() +{ + delete m_Pixels; +} + + + + + +void cPixmap::SetSize(int a_Width, int a_Height) +{ + delete m_Pixels; + m_Pixels = new int[a_Width * a_Height]; + m_Width = a_Width; + m_Height = a_Height; + m_Stride = m_Width; // Currently we don't need a special stride value, but let's support it for the future :) +} + + + + + +void cPixmap::SetPixel(int a_X, int a_Y, int a_Color) +{ + ASSERT(a_X >= 0); + ASSERT(a_X < m_Width); + ASSERT(a_Y >= 0); + ASSERT(a_Y < m_Height); + + m_Pixels[a_X + a_Y * m_Stride] = a_Color; +} + + + + + +int cPixmap::GetPixel(int a_X, int a_Y) const +{ + ASSERT(a_X >= 0); + ASSERT(a_X < m_Width); + ASSERT(a_Y >= 0); + ASSERT(a_Y < m_Height); + + return m_Pixels[a_X + a_Y * m_Stride]; +} + + + + + +void cPixmap::Fill(int a_Color) +{ + int NumElements = m_Height * m_Stride; + for (int i = 0; i < NumElements; i++) + { + m_Pixels[i] = a_Color; + } +} + + + + + +void cPixmap::DrawToDC(HDC a_DC, int a_OriginX, int a_OriginY) +{ + BITMAPINFO bmi; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = m_Width; + bmi.bmiHeader.biHeight = -m_Height; // Negative, we are top-down, unlike BMPs + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = m_Stride * m_Height * 4; + bmi.bmiHeader.biXPelsPerMeter = 1440; + bmi.bmiHeader.biYPelsPerMeter = 1440; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + SetDIBitsToDevice(a_DC, a_OriginX, a_OriginY, m_Width, m_Height, 0, 0, 0, m_Height, m_Pixels, &bmi, DIB_RGB_COLORS); +} + + + + diff --git a/BiomeVisualiser/Pixmap.h b/BiomeVisualiser/Pixmap.h new file mode 100644 index 000000000..d0159a886 --- /dev/null +++ b/BiomeVisualiser/Pixmap.h @@ -0,0 +1,39 @@ + +// Pixmap.h + +// Declares a cPixmap class that represents a RGB pixmap and allows simple operations on it + +#pragma once + + + + + +class cPixmap +{ +public: + cPixmap(void); + cPixmap(int a_Width, int a_Height); + ~cPixmap(); + + void SetSize(int a_Width, int a_Height); + + int GetWidth (void) const { return m_Width; } + int GetHeight(void) const { return m_Height; } + + void SetPixel(int a_X, int a_Y, int a_Color); + int GetPixel(int a_X, int a_Y) const; + void Fill(int a_Color); + + void DrawToDC(HDC a_DC, int a_OriginX, int a_OriginY); + +protected: + int m_Width; + int m_Height; + int m_Stride; + int * m_Pixels; +} ; + + + + diff --git a/BiomeVisualiser/Timer.h b/BiomeVisualiser/Timer.h new file mode 100644 index 000000000..b18d37cb7 --- /dev/null +++ b/BiomeVisualiser/Timer.h @@ -0,0 +1,40 @@ + +// Timer.h + +// Declares the cTimer class representing a RAII class that measures time from its creation till its destruction + + + + + +#pragma once + +#include "time.h" + + + + + +class cTimer +{ +public: + cTimer(const AString & a_Title) : + m_Title(a_Title), + m_StartTime(clock()) + { + } + + ~cTimer() + { + clock_t NumTicks = clock() - m_StartTime; + LOG("%s took %d ticks (%.02f sec)", m_Title.c_str(), NumTicks, (double)NumTicks / CLOCKS_PER_SEC); + } + +protected: + AString m_Title; + clock_t m_StartTime; +} ; + + + + diff --git a/BiomeVisualiser/WndProcThunk.h b/BiomeVisualiser/WndProcThunk.h new file mode 100644 index 000000000..6f33b5c3f --- /dev/null +++ b/BiomeVisualiser/WndProcThunk.h @@ -0,0 +1,143 @@ + +// WndProcThunk.h + +// Interfaces to the CWndProcThunk class responsible for WNDPROC class-thunking +// For details, see http://www.hackcraft.net/cpp/windowsThunk/thiscall/ +// Also available is a CDlgProcThunk class doing the same work for DIALOGPROC + +// MD: Made NX-compat by allocating the code structure using VirtualAlloc(..., PAGE_EXECUTE_READWRITE) + + + + + +// fwd: +template class CWndProcThunk; + + + + + +#ifndef WNDPROCTHUNK_H_INCLUDED +#define WNDPROCTHUNK_H_INCLUDED + + + + +template inline To union_cast(From fr) throw() +{ + union + { + From f; + To t; + } uc; + uc.f = fr; + return uc.t; +} + + + + + +#pragma warning(push) +#pragma warning(disable : 4355) + +#if defined(_M_IX86) + +#pragma pack(push,1) + +template class CWndProcThunk +{ + typedef ::LRESULT (W::* WndProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM); + typedef CWndProcThunk ThisClass; + + struct SCode + { + BYTE m_mov; // mov ECX, m_this + W * m_this; // + BYTE m_jmp; // jmp m_relproc + ptrdiff_t m_relproc; // relative jmp + }; + + SCode * Code; + +public: + ThisClass(WndProc proc, W * obj) + { + Code = (SCode *)VirtualAlloc(NULL, sizeof(SCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + Code->m_mov = 0xB9, + Code->m_this = obj, + Code->m_jmp = 0xE9, + Code->m_relproc = union_cast(proc) - reinterpret_cast(Code) - sizeof(*Code); + ::FlushInstructionCache(::GetCurrentProcess(), Code, sizeof(*Code)); + } + + virtual ~CWndProcThunk() + { + VirtualFree(Code, sizeof(*Code), MEM_RELEASE); + Code = NULL; + } + + operator ::WNDPROC() const {return reinterpret_cast<::WNDPROC>(Code); } + operator ::LONG_PTR() const {return reinterpret_cast<::LONG_PTR>(Code); } +} ; + + + + + +template class CDlgProcThunk +{ + typedef ::BOOL (W::* DlgProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM); + typedef CDlgProcThunk ThisClass; + + struct SCode + { + BYTE m_mov; // mov ECX, m_this + W * m_this; // + BYTE m_jmp; // jmp m_relproc + ptrdiff_t m_relproc; // relative jmp + }; + + SCode * Code; + +public: + CDlgProcThunk(DlgProc proc, W * obj) + { + Code = (SCode *)VirtualAlloc(NULL, sizeof(SCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + Code->m_mov = 0xB9, + Code->m_this = obj, + Code->m_jmp = 0xE9, + Code->m_relproc = union_cast(proc) - reinterpret_cast(Code) - sizeof(*Code); + ::FlushInstructionCache(::GetCurrentProcess(), Code, sizeof(*Code)); + } + + virtual ~CDlgProcThunk() + { + VirtualFree(Code, sizeof(*Code), MEM_RELEASE); + Code = NULL; + } + + operator ::DLGPROC() const {return reinterpret_cast<::DLGPROC>(Code); } + operator ::LONG_PTR() const {return reinterpret_cast<::LONG_PTR>(Code); } +} ; + + + + + + #pragma pack(pop) + +#else // _M_IX86 + #error Only X86 supported +#endif + + + + + +#endif // WNDPROCTHUNK_H_INCLUDED + + + + diff --git a/BiomeVisualiser/profile_run.cmd b/BiomeVisualiser/profile_run.cmd new file mode 100644 index 000000000..753ff18fb --- /dev/null +++ b/BiomeVisualiser/profile_run.cmd @@ -0,0 +1,70 @@ +@echo off +:: +:: Profiling using a MSVC standalone profiler +:: +:: See http://www.codeproject.com/Articles/144643/Profiling-of-C-Applications-in-Visual-Studio-for-F for details +:: + + + + +set pt="C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Performance Tools" +set appdir="Release profiled" +set app="Release profiled\BiomeVisualiser.exe" +set args="" + +:: outputdir is relative to appdir! +set outputdir=Profiling +set output=profile.vsp + + + + + +::Create the output directory, if it didn't exist +mkdir %outputdir% + + + + + +:: Start the profiler +%pt%\vsperfcmd /start:sample /output:%outputdir%\%output% +if errorlevel 1 goto haderror + +:: Launch the application via the profiler +%pt%\vsperfcmd /launch:%app% /args:%args% +if errorlevel 1 goto haderror + +:: Shut down the profiler (this command waits, until the application is terminated) +%pt%\vsperfcmd /shutdown +if errorlevel 1 goto haderror + + + + + +:: cd to outputdir, so that the reports are generated there +cd %outputdir% + +:: generate the report files (.csv) +%pt%\vsperfreport /summary:all %output% /symbolpath:"srv*C:\Programovani\Symbols*http://msdl.microsoft.com/download/symbols" +if errorlevel 1 goto haderror + + + + + +goto finished + + + + +:haderror +echo An error was encountered +pause + + + + +:finished diff --git a/source/Log.cpp b/source/Log.cpp index 42c58eb27..ed1a9beb8 100644 --- a/source/Log.cpp +++ b/source/Log.cpp @@ -138,7 +138,7 @@ void cLog::Log(const char * a_Format, va_list argList) #if defined (_WIN32) && defined(_DEBUG) // In a Windows Debug build, output the log to debug console as well: - OutputDebugString(Line.c_str()); + OutputDebugStringA(Line.c_str()); #endif // _WIN32 } diff --git a/source/MCLogger.cpp b/source/MCLogger.cpp index daf43f796..94a2ea1c7 100644 --- a/source/MCLogger.cpp +++ b/source/MCLogger.cpp @@ -8,7 +8,11 @@ -cMCLogger* cMCLogger::s_MCLogger = 0; +cMCLogger * cMCLogger::s_MCLogger = NULL; + + + + cMCLogger* cMCLogger::GetInstance() { @@ -19,10 +23,10 @@ cMCLogger* cMCLogger::GetInstance() -cMCLogger::cMCLogger() +cMCLogger::cMCLogger(void) { AString FileName; - Printf(FileName, "LOG_%d.txt", (int)time(0) ); + Printf(FileName, "LOG_%d.txt", (int)time(NULL)); m_Log = new cLog(FileName); m_Log->Log("--- Started Log ---"); @@ -33,9 +37,9 @@ cMCLogger::cMCLogger() -cMCLogger::cMCLogger( char* a_File ) +cMCLogger::cMCLogger(const AString & a_FileName) { - m_Log = new cLog( a_File ); + m_Log = new cLog(a_FileName); } diff --git a/source/MCLogger.h b/source/MCLogger.h index e918de37c..0c4566400 100644 --- a/source/MCLogger.h +++ b/source/MCLogger.h @@ -13,8 +13,8 @@ class cLog; class cMCLogger // tolua_export { // tolua_export public: // tolua_export - cMCLogger(); - cMCLogger( char* a_File ); // tolua_export + cMCLogger(void); + cMCLogger(const AString & a_FileName); // tolua_export ~cMCLogger(); // tolua_export void Log(const char* a_Format, va_list a_ArgList);