diff options
author | Howaner <franzi.moos@googlemail.com> | 2014-11-18 14:56:32 +0100 |
---|---|---|
committer | Howaner <franzi.moos@googlemail.com> | 2014-11-18 14:56:32 +0100 |
commit | 42120e2ea5db0cdb9920ff1c5efef33e0f496d48 (patch) | |
tree | 20ba1ae0a53f757cb8814b6cd6a466fe5acf1308 /Tools/QtBiomeVisualiser | |
parent | Fixed compile errors. (diff) | |
parent | Merge pull request #1598 from mc-server/SignEditor (diff) | |
download | cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.tar cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.tar.gz cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.tar.bz2 cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.tar.lz cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.tar.xz cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.tar.zst cuberite-42120e2ea5db0cdb9920ff1c5efef33e0f496d48.zip |
Diffstat (limited to '')
20 files changed, 977 insertions, 551 deletions
diff --git a/Tools/QtBiomeVisualiser/BiomeView.cpp b/Tools/QtBiomeVisualiser/BiomeView.cpp index ce5a870cd..c77b39482 100644 --- a/Tools/QtBiomeVisualiser/BiomeView.cpp +++ b/Tools/QtBiomeVisualiser/BiomeView.cpp @@ -1,8 +1,8 @@ #include "Globals.h" #include "BiomeView.h" -#include "QtChunk.h" #include <QPainter> #include <QResizeEvent> +#include "Region.h" @@ -14,6 +14,116 @@ static const int DELTA_STEP = 120; // The normal per-notch wheel delta +/** Map for converting biome values to colors. Initialized from biomeColors[]. */ +static uchar biomeToColor[256 * 4]; + +/** Map for converting biome values to colors. Used to initialize biomeToColor[].*/ +static struct +{ + EMCSBiome m_Biome; + uchar m_Color[3]; +} biomeColors[] = +{ + { biOcean, { 0x00, 0x00, 0x70 }, }, + { biPlains, { 0x8d, 0xb3, 0x60 }, }, + { biDesert, { 0xfa, 0x94, 0x18 }, }, + { biExtremeHills, { 0x60, 0x60, 0x60 }, }, + { biForest, { 0x05, 0x66, 0x21 }, }, + { biTaiga, { 0x0b, 0x66, 0x59 }, }, + { biSwampland, { 0x2f, 0xff, 0xda }, }, + { biRiver, { 0x30, 0x30, 0xaf }, }, + { biHell, { 0x7f, 0x00, 0x00 }, }, + { biSky, { 0x00, 0x7f, 0xff }, }, + { biFrozenOcean, { 0xa0, 0xa0, 0xdf }, }, + { biFrozenRiver, { 0xa0, 0xa0, 0xff }, }, + { biIcePlains, { 0xff, 0xff, 0xff }, }, + { biIceMountains, { 0xa0, 0xa0, 0xa0 }, }, + { biMushroomIsland, { 0xff, 0x00, 0xff }, }, + { biMushroomShore, { 0xa0, 0x00, 0xff }, }, + { biBeach, { 0xfa, 0xde, 0x55 }, }, + { biDesertHills, { 0xd2, 0x5f, 0x12 }, }, + { biForestHills, { 0x22, 0x55, 0x1c }, }, + { biTaigaHills, { 0x16, 0x39, 0x33 }, }, + { biExtremeHillsEdge, { 0x7f, 0x8f, 0x7f }, }, + { biJungle, { 0x53, 0x7b, 0x09 }, }, + { biJungleHills, { 0x2c, 0x42, 0x05 }, }, + + { biJungleEdge, { 0x62, 0x8b, 0x17 }, }, + { biDeepOcean, { 0x00, 0x00, 0x30 }, }, + { biStoneBeach, { 0xa2, 0xa2, 0x84 }, }, + { biColdBeach, { 0xfa, 0xf0, 0xc0 }, }, + { biBirchForest, { 0x30, 0x74, 0x44 }, }, + { biBirchForestHills, { 0x1f, 0x5f, 0x32 }, }, + { biRoofedForest, { 0x40, 0x51, 0x1a }, }, + { biColdTaiga, { 0x31, 0x55, 0x4a }, }, + { biColdTaigaHills, { 0x59, 0x7d, 0x72 }, }, + { biMegaTaiga, { 0x59, 0x66, 0x51 }, }, + { biMegaTaigaHills, { 0x59, 0x66, 0x59 }, }, + { biExtremeHillsPlus, { 0x50, 0x70, 0x50 }, }, + { biSavanna, { 0xbd, 0xb2, 0x5f }, }, + { biSavannaPlateau, { 0xa7, 0x9d, 0x64 }, }, + { biMesa, { 0xd9, 0x45, 0x15 }, }, + { biMesaPlateauF, { 0xb0, 0x97, 0x65 }, }, + { biMesaPlateau, { 0xca, 0x8c, 0x65 }, }, + + // M variants: + { biSunflowerPlains, { 0xb5, 0xdb, 0x88 }, }, + { biDesertM, { 0xff, 0xbc, 0x40 }, }, + { biExtremeHillsM, { 0x88, 0x88, 0x88 }, }, + { biFlowerForest, { 0x2d, 0x8e, 0x49 }, }, + { biTaigaM, { 0x33, 0x8e, 0x81 }, }, + { biSwamplandM, { 0x07, 0xf9, 0xb2 }, }, + { biIcePlainsSpikes, { 0xb4, 0xdc, 0xdc }, }, + { biJungleM, { 0x7b, 0xa3, 0x31 }, }, + { biJungleEdgeM, { 0x62, 0x8b, 0x17 }, }, + { biBirchForestM, { 0x58, 0x9c, 0x6c }, }, + { biBirchForestHillsM, { 0x47, 0x87, 0x5a }, }, + { biRoofedForestM, { 0x68, 0x79, 0x42 }, }, + { biColdTaigaM, { 0x24, 0x3f, 0x36 }, }, + { biMegaSpruceTaiga, { 0x45, 0x4f, 0x3e }, }, + { biMegaSpruceTaigaHills, { 0x45, 0x4f, 0x4e }, }, + { biExtremeHillsPlusM, { 0x78, 0x98, 0x78 }, }, + { biSavannaM, { 0xe5, 0xda, 0x87 }, }, + { biSavannaPlateauM, { 0xa7, 0x9d, 0x74 }, }, + { biMesaBryce, { 0xff, 0x6d, 0x3d }, }, + { biMesaPlateauFM, { 0xd8, 0xbf, 0x8d }, }, + { biMesaPlateauM, { 0xf2, 0xb4, 0x8d }, }, +} ; + + + + + +static class BiomeColorsInitializer +{ +public: + BiomeColorsInitializer(void) + { + // Reset all colors to gray: + for (size_t i = 0; i < ARRAYCOUNT(biomeToColor); i++) + { + biomeToColor[i] = 0x7f; + } + + // Set known biomes to their colors: + for (size_t i = 0; i < ARRAYCOUNT(biomeColors); i++) + { + uchar * color = &biomeToColor[4 * biomeColors[i].m_Biome]; + color[0] = biomeColors[i].m_Color[2]; + color[1] = biomeColors[i].m_Color[1]; + color[2] = biomeColors[i].m_Color[0]; + color[3] = 0xff; + } + } +} biomeColorInitializer; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// BiomeView: + BiomeView::BiomeView(QWidget * parent) : super(parent), m_X(0), @@ -40,10 +150,11 @@ BiomeView::BiomeView(QWidget * parent) : redraw(); // Add a chunk-update callback mechanism: - connect(&m_Cache, SIGNAL(chunkAvailable(int, int)), this, SLOT(chunkAvailable(int, int))); + connect(&m_Cache, SIGNAL(regionAvailable(int, int)), this, SLOT(regionAvailable(int, int))); - // Allow keyboard interaction: + // Allow mouse and keyboard interaction: setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); } @@ -80,6 +191,27 @@ void BiomeView::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource) +void BiomeView::setPosition(int a_BlockX, int a_BlockZ) +{ + m_X = a_BlockX; + m_Z = a_BlockZ; + redraw(); +} + + + + + +void BiomeView::setZoomLevel(double a_ZoomLevel) +{ + m_Zoom = a_ZoomLevel; + redraw(); +} + + + + + void BiomeView::redraw() { if (!hasData()) @@ -121,9 +253,15 @@ void BiomeView::redraw() -void BiomeView::chunkAvailable(int a_ChunkX, int a_ChunkZ) +void BiomeView::regionAvailable(int a_RegionX, int a_RegionZ) { - drawChunk(a_ChunkX, a_ChunkZ); + for (int z = 0; z < 32; z++) + { + for (int x = 0; x < 32; x++) + { + drawChunk(a_RegionX * 32 + x, a_RegionZ * 32 + z); + } + } update(); } @@ -153,8 +291,11 @@ void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ) return; } - //fetch the chunk: - ChunkPtr chunk = m_Cache.fetch(a_ChunkX, a_ChunkZ); + // Fetch the region: + int regionX; + int regionZ; + Region::chunkToRegion(a_ChunkX, a_ChunkZ, regionX, regionZ); + RegionPtr region = m_Cache.fetch(regionX, regionZ); // Figure out where on the screen this chunk should be drawn: // first find the center chunk @@ -172,11 +313,10 @@ void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ) centerx += (a_ChunkX - centerchunkx) * chunksize; centery += (a_ChunkZ - centerchunkz) * chunksize; - int srcoffset = 0; uchar * bits = m_Image.bits(); int imgstride = m_Image.bytesPerLine(); - int skipx = 0,skipy = 0; + int skipx = 0, skipy = 0; int blockwidth = chunksize, blockheight = chunksize; // now if we're off the screen we need to crop if (centerx < 0) @@ -205,29 +345,52 @@ void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ) int imgoffset = centerx * 4 + centery * imgstride; // If the chunk is valid, use its data; otherwise use the empty placeholder: - const uchar * src = m_EmptyChunkImage; - if (chunk.get() != nullptr) + const short * src = m_EmptyChunkBiomes; + if (region.get() != nullptr) { - src = chunk->getImage(); + int relChunkX = a_ChunkX - regionX * 32; + int relChunkZ = a_ChunkZ - regionZ * 32; + Chunk & chunk = region->getRelChunk(relChunkX, relChunkZ); + if (chunk.isValid()) + { + src = chunk.getBiomes(); + } } - // Blit or scale-blit the image: + // Scale-blit the image: for (int z = skipy; z < blockheight; z++, imgoffset += imgstride) { - srcoffset = floor((double)z / m_Zoom) * 16 * 4; - if (m_Zoom == 1.0) - { - memcpy(bits + imgoffset, src + srcoffset + skipx * 4, (blockwidth - skipx) * 4); - } - else + size_t srcoffset = static_cast<size_t>(std::floor((double)z / m_Zoom)) * 16; + int imgxoffset = imgoffset; + for (int x = skipx; x < blockwidth; x++) { - int xofs = 0; - for (int x = skipx; x < blockwidth; x++, xofs +=4) + short biome = src[srcoffset + static_cast<size_t>(std::floor((double)x / m_Zoom))]; + const uchar * color; + if (biome < 0) { - memcpy(bits + imgoffset + xofs, src + srcoffset + (int)floor((double)x / m_Zoom) * 4, 4); + static const uchar emptyBiome1[] = { 0x44, 0x44, 0x44, 0xff }; + static const uchar emptyBiome2[] = { 0x88, 0x88, 0x88, 0xff }; + color = ((x & 8) ^ (z & 8)) ? emptyBiome1 : emptyBiome2; } - } - } + else + { + if (biome * 4 >= ARRAYCOUNT(biomeToColor)) + { + static const uchar errorImage[] = { 0xff, 0x00, 0x00, 0xff }; + color = errorImage; + } + else + { + color = biomeToColor + biome * 4; + } + } + bits[imgxoffset] = color[0]; + bits[imgxoffset + 1] = color[1]; + bits[imgxoffset + 2] = color[2]; + bits[imgxoffset + 3] = color[3]; + imgxoffset += 4; + } // for x + } // for z } @@ -275,6 +438,12 @@ void BiomeView::mousePressEvent(QMouseEvent * a_Event) void BiomeView::mouseMoveEvent(QMouseEvent * a_Event) { + // If there's no data displayed, bail out: + if (!hasData()) + { + return; + } + if (m_IsMouseDragging) { // The user is dragging the mouse, move the view around: @@ -286,7 +455,16 @@ void BiomeView::mouseMoveEvent(QMouseEvent * a_Event) return; } - // TODO: Update the status bar info for the biome currently pointed at + // Update the status bar info text: + int blockX = floor((a_Event->x() - width() / 2) / m_Zoom + m_X); + int blockZ = floor((a_Event->y() - height() / 2) / m_Zoom + m_Z); + int regionX, regionZ; + Region::blockToRegion(blockX, blockZ, regionX, regionZ); + int relX = blockX - regionX * 512; + int relZ = blockZ - regionZ * 512; + auto region = m_Cache.fetch(regionX, regionZ); + int biome = (region.get() != nullptr) ? region->getRelBiome(relX, relZ) : biInvalidBiome; + emit hoverChanged(blockX, blockZ, biome); } @@ -307,12 +485,12 @@ void BiomeView::wheelEvent(QWheelEvent * a_Event) m_MouseWheelDelta += a_Event->delta(); while (m_MouseWheelDelta >= DELTA_STEP) { - increaseZoom(); + emit wheelUp(); m_MouseWheelDelta -= DELTA_STEP; } while (m_MouseWheelDelta <= -DELTA_STEP) { - decreaseZoom(); + emit wheelDown(); m_MouseWheelDelta += DELTA_STEP; } } @@ -360,14 +538,14 @@ void BiomeView::keyPressEvent(QKeyEvent * a_Event) case Qt::Key_PageUp: case Qt::Key_Q: { - increaseZoom(); + emit increaseZoom(); break; } case Qt::Key_PageDown: case Qt::Key_E: { - decreaseZoom(); + emit decreaseZoom(); break; } } @@ -376,52 +554,3 @@ void BiomeView::keyPressEvent(QKeyEvent * a_Event) - -void BiomeView::decreaseZoom() -{ - if (m_Zoom > 1.001) - { - m_Zoom--; - if (m_Zoom < 1.0) - { - // Just crossed the 100%, fixate the 100% threshold: - m_Zoom = 1.0; - } - } - else if (m_Zoom > 0.01) - { - m_Zoom = m_Zoom / 2; - } - redraw(); -} - - - - - -void BiomeView::increaseZoom() -{ - if (m_Zoom > 0.99) - { - if (m_Zoom > 20.0) - { - // Zoom too large - return; - } - m_Zoom++; - } - else - { - m_Zoom = m_Zoom * 2; - if (m_Zoom > 1.0) - { - // Just crossed the 100%, fixate the 100% threshold: - m_Zoom = 1.0; - } - } - redraw(); -} - - - - diff --git a/Tools/QtBiomeVisualiser/BiomeView.h b/Tools/QtBiomeVisualiser/BiomeView.h index f0521571d..cd9c7ead9 100644 --- a/Tools/QtBiomeVisualiser/BiomeView.h +++ b/Tools/QtBiomeVisualiser/BiomeView.h @@ -2,7 +2,7 @@ #include <QWidget> #include <memory> -#include "ChunkCache.h" +#include "RegionCache.h" #include "ChunkSource.h" @@ -25,14 +25,34 @@ public: The entire view is then invalidated and regenerated. */ void setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource); + /** Sets the position of the central pixel of the map to the specified point and redraws the view. */ + void setPosition(int a_BlockX, int a_BlockZ); + + /** Sets the zoom level to the specified value and redraws the view. */ + void setZoomLevel(double a_ZoomLevel); + signals: + /** Signalled when the user uses the wheel to scroll upwards. */ + void wheelUp(); + + /** Signalled when the user uses the wheel to scroll downwards. */ + void wheelDown(); + + /** Signalled when the user presses a key to increase zoom. */ + void increaseZoom(); + + /** Signalled when the user presses a key to decrease zoom. */ + void decreaseZoom(); + + /** Emitted when the user moves the mouse, to reflect the current block under the cursor. */ + void hoverChanged(int a_BlockX, int a_BlockZ, int a_Biome); public slots: /** Redraw the entire widget area. */ void redraw(); - /** A specified chunk has become available, redraw it. */ - void chunkAvailable(int a_ChunkX, int a_ChunkZ); + /** A specified region has become available, redraw it. */ + void regionAvailable(int a_RegionX, int a_RegionZ); /** Reloads the current chunk source and redraws the entire workspace. */ void reload(); @@ -42,7 +62,7 @@ protected: double m_Zoom; /** Cache for the loaded chunk data. */ - ChunkCache m_Cache; + RegionCache m_Cache; /** The entire view's contents in an offscreen image. */ QImage m_Image; @@ -59,6 +79,9 @@ protected: /** Data used for rendering a chunk that hasn't been loaded yet */ uchar m_EmptyChunkImage[16 * 16 * 4]; + /** Data placeholder for chunks that aren't valid. */ + short m_EmptyChunkBiomes[16 * 16]; + /** Draws the specified chunk into m_Image */ void drawChunk(int a_ChunkX, int a_ChunkZ); @@ -86,12 +109,6 @@ protected: /** Called when the user presses a key. */ virtual void keyPressEvent(QKeyEvent * a_Event) override; - - /** Decreases the zoom level and queues a redraw. */ - void decreaseZoom(); - - /** Increases the zoom level and queues a redraw. */ - void increaseZoom(); }; diff --git a/Tools/QtBiomeVisualiser/ChunkCache.cpp b/Tools/QtBiomeVisualiser/ChunkCache.cpp deleted file mode 100644 index 05c267d30..000000000 --- a/Tools/QtBiomeVisualiser/ChunkCache.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "Globals.h" -#include "ChunkCache.h" -#include <QMutexLocker> -#include <QThreadPool> -#include "ChunkSource.h" -#include "ChunkLoader.h" - - - - - -ChunkCache::ChunkCache(QObject * parent) : - super(parent) -{ - m_Cache.setMaxCost(1024 * 1024 * 1024); // 1 GiB of memory for the cache -} - - - - - -ChunkPtr ChunkCache::fetch(int a_ChunkX, int a_ChunkZ) -{ - // Retrieve from the cache: - quint32 hash = getChunkHash(a_ChunkX, a_ChunkZ); - ChunkPtr * res; - { - QMutexLocker lock(&m_Mtx); - res = m_Cache[hash]; - // If succesful and chunk loaded, return the retrieved value: - if ((res != nullptr) && (*res)->isValid()) - { - return *res; - } - } - - // If the chunk is in cache but not valid, it means it has been already queued for rendering, do nothing now: - if (res != nullptr) - { - return ChunkPtr(nullptr); - } - - // There's no such item in the cache, create it now: - res = new ChunkPtr(new Chunk); - if (res == nullptr) - { - return ChunkPtr(nullptr); - } - { - QMutexLocker lock(&m_Mtx); - m_Cache.insert(hash, res, sizeof(Chunk)); - } - - // Queue the chunk for rendering: - queueChunkRender(a_ChunkX, a_ChunkZ, *res); - - // Return failure, the chunk is not yet rendered: - return ChunkPtr(nullptr); -} - - - - - -void ChunkCache::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource) -{ - // Replace the chunk source: - m_ChunkSource = a_ChunkSource; - - // Clear the cache: - QMutexLocker lock(&m_Mtx); - m_Cache.clear(); -} - - - - - -void ChunkCache::reload() -{ - assert(m_ChunkSource.get() != nullptr); - - // Reload the chunk source: - m_ChunkSource->reload(); - - // Clear the cache: - QMutexLocker lock(&m_Mtx); - m_Cache.clear(); -} - - - - - -void ChunkCache::gotChunk(int a_ChunkX, int a_ChunkZ) -{ - emit chunkAvailable(a_ChunkX, a_ChunkZ); -} - - - - - -quint32 ChunkCache::getChunkHash(int a_ChunkX, int a_ChunkZ) -{ - // Simply join the two coords into a single int - // The coords will never be larger than 16-bits, so we can do this safely - return (((static_cast<quint32>(a_ChunkX) & 0xffff) << 16) | (static_cast<quint32>(a_ChunkZ) & 0xffff)); -} - - - - - -void ChunkCache::queueChunkRender(int a_ChunkX, int a_ChunkZ, ChunkPtr & a_Chunk) -{ - // Create a new loader task: - ChunkLoader * loader = new ChunkLoader(a_ChunkX, a_ChunkZ, a_Chunk, m_ChunkSource); - connect(loader, SIGNAL(loaded(int, int)), this, SLOT(gotChunk(int, int))); - - QThreadPool::globalInstance()->start(loader); -} - - - - diff --git a/Tools/QtBiomeVisualiser/ChunkLoader.cpp b/Tools/QtBiomeVisualiser/ChunkLoader.cpp deleted file mode 100644 index 3d0123b23..000000000 --- a/Tools/QtBiomeVisualiser/ChunkLoader.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "Globals.h" -#include "ChunkLoader.h" -#include "ChunkSource.h" - - - - - -ChunkLoader::ChunkLoader(int a_ChunkX, int a_ChunkZ, ChunkPtr a_Chunk, ChunkSourcePtr a_ChunkSource) : - m_ChunkX(a_ChunkX), - m_ChunkZ(a_ChunkZ), - m_Chunk(a_Chunk), - m_ChunkSource(a_ChunkSource) -{ -} - - - - - -void ChunkLoader::run() -{ - m_ChunkSource->getChunkBiomes(m_ChunkX, m_ChunkZ, m_Chunk); - emit loaded(m_ChunkX, m_ChunkZ); -} - - - - diff --git a/Tools/QtBiomeVisualiser/ChunkLoader.h b/Tools/QtBiomeVisualiser/ChunkLoader.h deleted file mode 100644 index 4d026a45e..000000000 --- a/Tools/QtBiomeVisualiser/ChunkLoader.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include <QObject> -#include <QRunnable> -#include <memory> - - - - -// fwd: -class Chunk; -typedef std::shared_ptr<Chunk> ChunkPtr; - -class ChunkSource; -typedef std::shared_ptr<ChunkSource> ChunkSourcePtr; - - - - - -class ChunkLoader : - public QObject, - public QRunnable -{ - Q_OBJECT - -public: - ChunkLoader(int a_ChunkX, int a_ChunkZ, ChunkPtr a_Chunk, ChunkSourcePtr a_ChunkSource); - virtual ~ChunkLoader() {} - -signals: - void loaded(int a_ChunkX, int a_ChunkZ); - -protected: - virtual void run() override; - -private: - int m_ChunkX, m_ChunkZ; - ChunkPtr m_Chunk; - ChunkSourcePtr m_ChunkSource; -}; - - - - diff --git a/Tools/QtBiomeVisualiser/ChunkSource.cpp b/Tools/QtBiomeVisualiser/ChunkSource.cpp index bebf89a0a..ea3346f04 100644 --- a/Tools/QtBiomeVisualiser/ChunkSource.cpp +++ b/Tools/QtBiomeVisualiser/ChunkSource.cpp @@ -4,177 +4,89 @@ #include "src/Generating/BioGen.h" #include "src/StringCompression.h" #include "src/WorldStorage/FastNBT.h" -#include "inifile/iniFile.h" +#include "src/IniFile.h" -/** Map for converting biome values to colors. Initialized from biomeColors[]. */ -static uchar biomeToColor[256 * 4]; +//////////////////////////////////////////////////////////////////////////////// +// BioGenSource: -/** Map for converting biome values to colors. Used to initialize biomeToColor[].*/ -static struct -{ - EMCSBiome m_Biome; - uchar m_Color[3]; -} biomeColors[] = -{ - { biOcean, { 0x00, 0x00, 0x70 }, }, - { biPlains, { 0x8d, 0xb3, 0x60 }, }, - { biDesert, { 0xfa, 0x94, 0x18 }, }, - { biExtremeHills, { 0x60, 0x60, 0x60 }, }, - { biForest, { 0x05, 0x66, 0x21 }, }, - { biTaiga, { 0x0b, 0x66, 0x59 }, }, - { biSwampland, { 0x2f, 0xff, 0xda }, }, - { biRiver, { 0x30, 0x30, 0xaf }, }, - { biHell, { 0x7f, 0x00, 0x00 }, }, - { biSky, { 0x00, 0x7f, 0xff }, }, - { biFrozenOcean, { 0xa0, 0xa0, 0xdf }, }, - { biFrozenRiver, { 0xa0, 0xa0, 0xff }, }, - { biIcePlains, { 0xff, 0xff, 0xff }, }, - { biIceMountains, { 0xa0, 0xa0, 0xa0 }, }, - { biMushroomIsland, { 0xff, 0x00, 0xff }, }, - { biMushroomShore, { 0xa0, 0x00, 0xff }, }, - { biBeach, { 0xfa, 0xde, 0x55 }, }, - { biDesertHills, { 0xd2, 0x5f, 0x12 }, }, - { biForestHills, { 0x22, 0x55, 0x1c }, }, - { biTaigaHills, { 0x16, 0x39, 0x33 }, }, - { biExtremeHillsEdge, { 0x7f, 0x8f, 0x7f }, }, - { biJungle, { 0x53, 0x7b, 0x09 }, }, - { biJungleHills, { 0x2c, 0x42, 0x05 }, }, - - { biJungleEdge, { 0x62, 0x8b, 0x17 }, }, - { biDeepOcean, { 0x00, 0x00, 0x30 }, }, - { biStoneBeach, { 0xa2, 0xa2, 0x84 }, }, - { biColdBeach, { 0xfa, 0xf0, 0xc0 }, }, - { biBirchForest, { 0x30, 0x74, 0x44 }, }, - { biBirchForestHills, { 0x1f, 0x5f, 0x32 }, }, - { biRoofedForest, { 0x40, 0x51, 0x1a }, }, - { biColdTaiga, { 0x31, 0x55, 0x4a }, }, - { biColdTaigaHills, { 0x59, 0x7d, 0x72 }, }, - { biMegaTaiga, { 0x59, 0x66, 0x51 }, }, - { biMegaTaigaHills, { 0x59, 0x66, 0x59 }, }, - { biExtremeHillsPlus, { 0x50, 0x70, 0x50 }, }, - { biSavanna, { 0xbd, 0xb2, 0x5f }, }, - { biSavannaPlateau, { 0xa7, 0x9d, 0x64 }, }, - { biMesa, { 0xd9, 0x45, 0x15 }, }, - { biMesaPlateauF, { 0xb0, 0x97, 0x65 }, }, - { biMesaPlateau, { 0xca, 0x8c, 0x65 }, }, - - // M variants: - { biSunflowerPlains, { 0xb5, 0xdb, 0x88 }, }, - { biDesertM, { 0xff, 0xbc, 0x40 }, }, - { biExtremeHillsM, { 0x88, 0x88, 0x88 }, }, - { biFlowerForest, { 0x2d, 0x8e, 0x49 }, }, - { biTaigaM, { 0x33, 0x8e, 0x81 }, }, - { biSwamplandM, { 0x07, 0xf9, 0xb2 }, }, - { biIcePlainsSpikes, { 0xb4, 0xdc, 0xdc }, }, - { biJungleM, { 0x7b, 0xa3, 0x31 }, }, - { biJungleEdgeM, { 0x62, 0x8b, 0x17 }, }, - { biBirchForestM, { 0x58, 0x9c, 0x6c }, }, - { biBirchForestHillsM, { 0x47, 0x87, 0x5a }, }, - { biRoofedForestM, { 0x68, 0x79, 0x42 }, }, - { biColdTaigaM, { 0x24, 0x3f, 0x36 }, }, - { biMegaSpruceTaiga, { 0x45, 0x4f, 0x3e }, }, - { biMegaSpruceTaigaHills, { 0x45, 0x4f, 0x4e }, }, - { biExtremeHillsPlusM, { 0x78, 0x98, 0x78 }, }, - { biSavannaM, { 0xe5, 0xda, 0x87 }, }, - { biSavannaPlateauM, { 0xa7, 0x9d, 0x74 }, }, - { biMesaBryce, { 0xff, 0x6d, 0x3d }, }, - { biMesaPlateauFM, { 0xd8, 0xbf, 0x8d }, }, - { biMesaPlateauM, { 0xf2, 0xb4, 0x8d }, }, -} ; - - - - - -static class BiomeColorsInitializer +BioGenSource::BioGenSource(cIniFilePtr a_IniFile) : + m_IniFile(a_IniFile), + m_Mtx(QMutex::Recursive) { -public: - BiomeColorsInitializer(void) - { - // Reset all colors to gray: - for (size_t i = 0; i < ARRAYCOUNT(biomeToColor); i++) - { - biomeToColor[i] = 0x7f; - } - - // Set known biomes to their colors: - for (size_t i = 0; i < ARRAYCOUNT(biomeColors); i++) - { - uchar * color = &biomeToColor[4 * biomeColors[i].m_Biome]; - color[0] = biomeColors[i].m_Color[2]; - color[1] = biomeColors[i].m_Color[1]; - color[2] = biomeColors[i].m_Color[0]; - color[3] = 0xff; - } - } -} biomeColorInitializer; + reload(); +} -/** Converts biomes in an array into the chunk image data. */ -static void biomesToImage(cChunkDef::BiomeMap & a_Biomes, Chunk::Image & a_Image) +void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) { - // Make sure the two arrays are of the same size, compile-time. - // Note that a_Image is actually 4 items per pixel, so the array is 4 times bigger: - static const char Check1[4 * ARRAYCOUNT(a_Biomes) - ARRAYCOUNT(a_Image) + 1] = {}; - static const char Check2[ARRAYCOUNT(a_Image) - 4 * ARRAYCOUNT(a_Biomes) + 1] = {}; - - // Convert the biomes into color: - for (size_t i = 0; i < ARRAYCOUNT(a_Biomes); i++) - { - a_Image[4 * i + 0] = biomeToColor[4 * a_Biomes[i] + 0]; - a_Image[4 * i + 1] = biomeToColor[4 * a_Biomes[i] + 1]; - a_Image[4 * i + 2] = biomeToColor[4 * a_Biomes[i] + 2]; - a_Image[4 * i + 3] = biomeToColor[4 * a_Biomes[i] + 3]; - } + cChunkDef::BiomeMap biomes; + int tag; + cBiomeGenPtr biomeGen = getBiomeGen(tag); + biomeGen->GenBiomes(a_ChunkX, a_ChunkZ, biomes); + releaseBiomeGen(std::move(biomeGen), tag); + a_DestChunk.setBiomes(biomes); } -//////////////////////////////////////////////////////////////////////////////// -// BioGenSource: - -BioGenSource::BioGenSource(cIniFilePtr a_IniFile) : - m_IniFile(a_IniFile), - m_Mtx(QMutex::Recursive) +void BioGenSource::reload() { - reload(); + QMutexLocker lock(&m_Mtx); + m_CurrentTag += 1; + m_BiomeGens.clear(); } -void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) +cBiomeGenPtr BioGenSource::getBiomeGen(int & a_Tag) { - cChunkDef::BiomeMap biomes; + QMutexLocker lock(&m_Mtx); + a_Tag = m_CurrentTag; + if (m_BiomeGens.empty()) { - QMutexLocker lock(&m_Mtx); - m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, biomes); + // Create a new biogen: + lock.unlock(); + int seed = m_IniFile->GetValueSetI("Seed", "Seed", 0); + bool unused; + cBiomeGenPtr res = cBiomeGen::CreateBiomeGen(*m_IniFile, seed, unused); + return res; + } + else + { + // Return an existing biogen: + cBiomeGenPtr res = m_BiomeGens.back(); + m_BiomeGens.pop_back(); + return res; } - Chunk::Image img; - biomesToImage(biomes, img); - a_DestChunk->setImage(img); } -void BioGenSource::reload() +void BioGenSource::releaseBiomeGen(cBiomeGenPtr && a_BiomeGen, int a_Tag) { - int seed = m_IniFile->GetValueSetI("Generator", "Seed", 0); - bool unused = false; QMutexLocker lock(&m_Mtx); - m_BiomeGen.reset(cBiomeGen::CreateBiomeGen(*m_IniFile, seed, unused)); + + // If the tag differs, the source has been reloaded and this biogen is old, dispose: + if (a_Tag != m_CurrentTag) + { + return; + } + + // The tag is the same, put the biogen back to list: + m_BiomeGens.push_back(std::move(a_BiomeGen)); } @@ -291,7 +203,7 @@ AnvilSource::AnvilSource(QString a_WorldRegionFolder) : -void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) +void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) { // Load the compressed data: AString compressedChunkData = getCompressedChunkData(a_ChunkX, a_ChunkZ); @@ -331,10 +243,7 @@ void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChun { biomeMap[i] = (EMCSBiome)GetBEInt(beBiomes + 4 * i); } - // Render the biomes: - Chunk::Image img; - biomesToImage(biomeMap, img); - a_DestChunk->setImage(img); + a_DestChunk.setBiomes(biomeMap); return; } @@ -350,10 +259,7 @@ void AnvilSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChun { biomeMap[i] = EMCSBiome(vanillaBiomes[i]); } - // Render the biomes: - Chunk::Image img; - biomesToImage(biomeMap, img); - a_DestChunk->setImage(img); + a_DestChunk.setBiomes(biomeMap); } @@ -397,7 +303,7 @@ AnvilSource::AnvilFilePtr AnvilSource::getAnvilFile(int a_ChunkX, int a_ChunkZ) // Search the cache for the file: QMutexLocker lock(&m_Mtx); - for (auto itr = m_Files.cbegin(), end = m_Files.cend(); itr != end; ++itr) + for (auto itr = m_Files.begin(), end = m_Files.end(); itr != end; ++itr) { if (((*itr)->m_RegionX == RegionX) && ((*itr)->m_RegionZ == RegionZ)) { diff --git a/Tools/QtBiomeVisualiser/ChunkSource.h b/Tools/QtBiomeVisualiser/ChunkSource.h index 7bd1865ff..62f9b5626 100644 --- a/Tools/QtBiomeVisualiser/ChunkSource.h +++ b/Tools/QtBiomeVisualiser/ChunkSource.h @@ -10,7 +10,7 @@ // fwd: class cBiomeGen; -typedef std::shared_ptr<cBiomeGen> cBiomeGenPtr; +typedef SharedPtr<cBiomeGen> cBiomeGenPtr; class cIniFile; typedef std::shared_ptr<cIniFile> cIniFilePtr; @@ -26,7 +26,7 @@ public: /** Fills the a_DestChunk with the biomes for the specified coords. It is expected to be thread-safe and re-entrant. Usually QThread::idealThreadCount() threads are used. */ - virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) = 0; + virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) = 0; /** Forces a fresh reload of the source. Useful mainly for the generator, whose underlying definition file may have been changed. */ virtual void reload() = 0; @@ -45,7 +45,7 @@ public: BioGenSource(cIniFilePtr a_IniFile); // ChunkSource overrides: - virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override; + virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) override; virtual void reload(void) override; protected: @@ -53,10 +53,30 @@ protected: cIniFilePtr m_IniFile; /** The generator used for generating biomes. */ - std::unique_ptr<cBiomeGen> m_BiomeGen; + std::vector<cBiomeGenPtr> m_BiomeGens; - /** Guards m_BiomeGen against multithreaded access. */ + /** Guards m_BiomeGens against multithreaded access. */ QMutex m_Mtx; + + /** Keeps track of the current settings of the biomegens. + Incremented by one each time reload() is called. Provides the means of releasing old biomegens that were + in use while reload() was being processed and thus couldn't be changed back then. releaseBiomeGen() does + the job of filtering the biogens before reusing them. */ + int m_CurrentTag; + + + /** Retrieves one cBiomeGenPtr from m_BiomeGens. + If there's no biogen available there, creates a new one based on the ini file. + When done with it, the caller should call releaseBiomeGen() to put the biogen back to m_BiomeGens. + a_Tag receives the value of m_CurrentTag from when the lock was held; it should be passed to + releaseBiomeGen() together with the biogen. */ + cBiomeGenPtr getBiomeGen(int & a_Tag); + + /** Marks the specified biogen as available for reuse (puts it back into m_BiomeGens). + a_Tag is the value of m_CurrentTag from the time when the biogen was retrieved; if it is different from + current m_CurrentTagValue, the biogen will be disposed of (because reload() has been called in the + meantime). */ + void releaseBiomeGen(cBiomeGenPtr && a_BiomeGen, int a_Tag); }; @@ -70,7 +90,7 @@ public: AnvilSource(QString a_WorldRegionFolder); // ChunkSource overrides: - virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override; + virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, Chunk & a_DestChunk) override; virtual void reload() override; protected: diff --git a/Tools/QtBiomeVisualiser/GeneratorSetup.cpp b/Tools/QtBiomeVisualiser/GeneratorSetup.cpp index f5412404c..8f97e1f37 100644 --- a/Tools/QtBiomeVisualiser/GeneratorSetup.cpp +++ b/Tools/QtBiomeVisualiser/GeneratorSetup.cpp @@ -3,7 +3,7 @@ #include <QLabel> #include <QLineEdit> #include "src/Generating/BioGen.h" -#include "inifile/iniFile.h" +#include "src/IniFile.h" @@ -14,6 +14,8 @@ static const QString s_GeneratorNames[] = QString("Checkerboard"), QString("Constant"), QString("DistortedVoronoi"), + QString("Grown"), + QString("GrownProt"), QString("MultiStepMap"), QString("TwoLevel"), QString("Voronoi"), @@ -31,6 +33,7 @@ GeneratorSetup::GeneratorSetup(const AString & a_IniFileName, QWidget * a_Parent m_eSeed = new QLineEdit(); m_eSeed->setValidator(new QIntValidator()); m_eSeed->setText("0"); + m_eSeed->setProperty("INI.SectionName", QVariant("Seed")); m_eSeed->setProperty("INI.ItemName", QVariant("Seed")); m_cbGenerator = new QComboBox(); m_cbGenerator->setMinimumWidth(120); @@ -53,17 +56,12 @@ GeneratorSetup::GeneratorSetup(const AString & a_IniFileName, QWidget * a_Parent setLayout(m_MainLayout); // Load the INI file, if specified, otherwise set defaults: - if (!a_IniFileName.empty() && m_IniFile->ReadFile(a_IniFileName)) - { - m_cbGenerator->setCurrentText(QString::fromStdString(m_IniFile->GetValue("Generator", "BiomeGen"))); - m_eSeed->setText(QString::number(m_IniFile->GetValueI("Generator", "Seed"))); - } - else + if (a_IniFileName.empty() || !m_IniFile->ReadFile(a_IniFileName)) { m_IniFile->SetValue("Generator", "Generator", "Composable"); m_IniFile->SetValue("Generator", "BiomeGen", m_cbGenerator->currentText().toStdString()); bool dummy; - delete cBiomeGen::CreateBiomeGen(*m_IniFile, 0, dummy); + cBiomeGen::CreateBiomeGen(*m_IniFile, 0, dummy); } updateFromIni(); @@ -95,7 +93,7 @@ void GeneratorSetup::generatorChanged(const QString & a_NewName) // Create a dummy biome gen from the INI file, this will create the defaults in the INI file: bool dummy; - delete cBiomeGen::CreateBiomeGen(*m_IniFile, m_Seed, dummy); + cBiomeGen::CreateBiomeGen(*m_IniFile, m_Seed, dummy); // Read all values from the INI file and put them into the form layout: updateFromIni(); @@ -110,8 +108,9 @@ void GeneratorSetup::generatorChanged(const QString & a_NewName) void GeneratorSetup::editChanged(const QString & a_NewValue) { - QString itemName = sender()->property("INI.ItemName").toString(); - m_IniFile->SetValue("Generator", itemName.toStdString(), a_NewValue.toStdString()); + QString sectionName = sender()->property("INI.SectionName").toString(); + QString itemName = sender()->property("INI.ItemName").toString(); + m_IniFile->SetValue(sectionName.toStdString(), itemName.toStdString(), a_NewValue.toStdString()); emit generatorUpdated(); } @@ -121,6 +120,7 @@ void GeneratorSetup::editChanged(const QString & a_NewValue) void GeneratorSetup::updateFromIni() { + m_eSeed->setText(QString::number(m_IniFile->GetValueI("Seed", "Seed", 0))); int keyID = m_IniFile->FindKey("Generator"); if (keyID <= -1) { @@ -141,6 +141,7 @@ void GeneratorSetup::updateFromIni() QLineEdit * edit = new QLineEdit(); edit->setText(QString::fromStdString(itemValue)); + edit->setProperty("INI.SectionName", QVariant("Generator")); edit->setProperty("INI.ItemName", QVariant(QString::fromStdString(itemName))); // Remove the generator name prefix from the item name, for clarity purposes: diff --git a/Tools/QtBiomeVisualiser/Globals.h b/Tools/QtBiomeVisualiser/Globals.h index 8d2e913b7..e2e9a9970 100644 --- a/Tools/QtBiomeVisualiser/Globals.h +++ b/Tools/QtBiomeVisualiser/Globals.h @@ -158,8 +158,17 @@ template class SizeChecker<UInt16, 2>; TypeName(const TypeName &); \ void operator =(const TypeName &) +// A macro that is used to mark unused local variables, to avoid pedantic warnings in gcc / clang / MSVC +// Note that in MSVC it requires the full type of X to be known +#define UNUSED_VAR(X) (void)(X) + // A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc -#define UNUSED(X) (void)(X) +// Written so that the full type of param needn't be known +#ifdef _MSC_VER + #define UNUSED(X) +#else + #define UNUSED UNUSED_VAR +#endif diff --git a/Tools/QtBiomeVisualiser/MainWindow.cpp b/Tools/QtBiomeVisualiser/MainWindow.cpp index eb45690c1..c6ea8656e 100644 --- a/Tools/QtBiomeVisualiser/MainWindow.cpp +++ b/Tools/QtBiomeVisualiser/MainWindow.cpp @@ -7,12 +7,23 @@ #include <QFileDialog> #include <QSettings> #include <QDirIterator> -#include "inifile/iniFile.h" +#include <QStatusBar> #include "ChunkSource.h" +#include "src/IniFile.h" #include "src/Generating/BioGen.h" #include "src/StringCompression.h" #include "src/WorldStorage/FastNBT.h" #include "GeneratorSetup.h" +#include "RegionLoader.h" + + + + + +const double MainWindow::m_ViewZooms[] = +{ + 0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16, 24, +}; @@ -21,11 +32,27 @@ MainWindow::MainWindow(QWidget * parent) : QMainWindow(parent), m_GeneratorSetup(nullptr), - m_LineSeparator(nullptr) + m_LineSeparator(nullptr), + m_CurrentZoomLevel(2) { initMinecraftPath(); m_BiomeView = new BiomeView(); + connect(m_BiomeView, SIGNAL(increaseZoom()), this, SLOT(increaseZoom())); + connect(m_BiomeView, SIGNAL(decreaseZoom()), this, SLOT(decreaseZoom())); + connect(m_BiomeView, SIGNAL(wheelUp()), this, SLOT(increaseZoom())); + connect(m_BiomeView, SIGNAL(wheelDown()), this, SLOT(decreaseZoom())); + m_BiomeView->setZoomLevel(m_ViewZooms[m_CurrentZoomLevel]); + + m_StatusBar = new QStatusBar(); + this->setStatusBar(m_StatusBar); + m_StatusBlockX = new QLabel(tr("X")); + m_StatusBlockZ = new QLabel(tr("Z")); + m_StatusBiome = new QLabel(tr("B")); + m_StatusBar->addPermanentWidget(m_StatusBlockX); + m_StatusBar->addPermanentWidget(m_StatusBlockZ); + m_StatusBar->addPermanentWidget(m_StatusBiome); + m_MainLayout = new QHBoxLayout(); m_MainLayout->addWidget(m_BiomeView, 1); m_MainLayout->setMenuBar(menuBar()); @@ -36,6 +63,8 @@ MainWindow::MainWindow(QWidget * parent) : createActions(); createMenus(); + + connect(m_BiomeView, SIGNAL(hoverChanged(int, int, int)), this, SLOT(hoverChanged(int, int, int))); } @@ -44,7 +73,7 @@ MainWindow::MainWindow(QWidget * parent) : MainWindow::~MainWindow() { - + RegionLoader::shutdown(); } @@ -129,6 +158,80 @@ void MainWindow::openVanillaWorld() +void MainWindow::centerView() +{ + m_BiomeView->setPosition(0, 0); +} + + + + + +void MainWindow::setViewZoom() +{ + // The zoom level is stored in the sender action's data, retrieve it: + QAction * action = qobject_cast<QAction *>(sender()); + if (action == nullptr) + { + return; + } + m_CurrentZoomLevel = action->data().toInt(); + double newZoom = m_ViewZooms[m_CurrentZoomLevel]; + m_BiomeView->setZoomLevel(newZoom); + action->setChecked(true); +} + + + + + +void MainWindow::increaseZoom() +{ + // If already at max zoom, bail out: + if (m_CurrentZoomLevel >= ARRAYCOUNT(m_ViewZooms) - 1) + { + return; + } + + // Increase the zoom level: + m_CurrentZoomLevel += 1; + m_actViewZoom[m_CurrentZoomLevel]->setChecked(true); + m_BiomeView->setZoomLevel(m_ViewZooms[m_CurrentZoomLevel]); +} + + + + + +void MainWindow::decreaseZoom() +{ + // If already at min zoom, bail out: + if (m_CurrentZoomLevel == 0) + { + return; + } + + // Decrease the zoom level: + m_CurrentZoomLevel -= 1; + m_actViewZoom[m_CurrentZoomLevel]->setChecked(true); + m_BiomeView->setZoomLevel(m_ViewZooms[m_CurrentZoomLevel]); +} + + + + + +void MainWindow::hoverChanged(int a_BlockX, int a_BlockZ, int a_Biome) +{ + m_StatusBlockX->setText(tr("X: %1").arg(a_BlockX)); + m_StatusBlockZ->setText(tr("Z: %1").arg(a_BlockZ)); + m_StatusBiome->setText (tr("B: %1 (%2)").arg(BiomeToString(a_Biome).c_str()).arg(a_Biome)); +} + + + + + void MainWindow::initMinecraftPath() { #ifdef Q_OS_MAC @@ -147,6 +250,7 @@ void MainWindow::initMinecraftPath() void MainWindow::createActions() { + // Map menu: createWorldActions(); m_actNewGen = new QAction(tr("&New generator"), this); @@ -173,6 +277,22 @@ void MainWindow::createActions() m_actExit->setShortcut(tr("Alt+X")); m_actExit->setStatusTip(tr("Exit %1").arg(QApplication::instance()->applicationName())); connect(m_actExit, SIGNAL(triggered()), this, SLOT(close())); + + // View menu: + m_actViewCenter = new QAction(tr("&Reset to center"), this); + m_actViewCenter->setStatusTip(tr("Scrolls the view back to the map center")); + connect(m_actViewCenter, SIGNAL(triggered()), this, SLOT(centerView())); + + QActionGroup * zoomGroup = new QActionGroup(this); + for (int i = 0; i < ARRAYCOUNT(m_ViewZooms); i++) + { + m_actViewZoom[i] = new QAction(tr("&Zoom %1%").arg(std::floor(m_ViewZooms[i] * 100)), this); + m_actViewZoom[i]->setCheckable(true); + m_actViewZoom[i]->setData(QVariant(i)); + zoomGroup->addAction(m_actViewZoom[i]); + connect(m_actViewZoom[i], SIGNAL(triggered()), this, SLOT(setViewZoom())); + } + m_actViewZoom[m_CurrentZoomLevel]->setChecked(true); } @@ -220,11 +340,12 @@ void MainWindow::createWorldActions() void MainWindow::createMenus() { + // Map menu: QMenu * file = menuBar()->addMenu(tr("&Map")); file->addAction(m_actNewGen); file->addAction(m_actOpenGen); file->addSeparator(); - QMenu * worlds = file->addMenu(tr("Open existing")); + QMenu * worlds = file->addMenu(tr("Open &existing")); worlds->addActions(m_WorldActions); if (m_WorldActions.empty()) { @@ -235,6 +356,15 @@ void MainWindow::createMenus() file->addAction(m_actReload); file->addSeparator(); file->addAction(m_actExit); + + // View menu: + QMenu * view = menuBar()->addMenu(tr("&View")); + view->addAction(m_actViewCenter); + view->addSeparator(); + for (size_t i = 0; i < ARRAYCOUNT(m_actViewZoom); i++) + { + view->addAction(m_actViewZoom[i]); + } } diff --git a/Tools/QtBiomeVisualiser/MainWindow.h b/Tools/QtBiomeVisualiser/MainWindow.h index 6490a937f..27faae7a8 100644 --- a/Tools/QtBiomeVisualiser/MainWindow.h +++ b/Tools/QtBiomeVisualiser/MainWindow.h @@ -4,6 +4,7 @@ #include <QList> #include <QMainWindow> #include <QHBoxLayout> +#include <QLabel> #include "BiomeView.h" @@ -39,13 +40,33 @@ private slots: /** Opens a vanilla world that is specified by the calling action. */ void openVanillaWorld(); + /** Moves the view to the map's center. */ + void centerView(); + + /** Sets the zoom level specified in the triggering action. */ + void setViewZoom(); + + /** Sets a zoom level one step larger than current, if allowed. */ + void increaseZoom(); + + /** Sets a zoom level one step smaller than current, if allowed. */ + void decreaseZoom(); + + /** Updates the statusbar for the specified info about the current block under the cursor. */ + void hoverChanged(int a_BlockX, int a_BlockZ, int a_Biome); + protected: + /** The zoom levels */ + static const double m_ViewZooms[10]; + // Actions: QAction * m_actNewGen; QAction * m_actOpenGen; QAction * m_actOpenWorld; QAction * m_actReload; QAction * m_actExit; + QAction * m_actViewCenter; + QAction * m_actViewZoom[ARRAYCOUNT(m_ViewZooms)]; /** List of actions that open the specific vanilla world. */ QList<QAction *> m_WorldActions; @@ -62,9 +83,19 @@ protected: /** The layout for the window. */ QHBoxLayout * m_MainLayout; + /** The status bar that displays the current hover information. */ + QStatusBar * m_StatusBar; + + QLabel * m_StatusBlockX; + QLabel * m_StatusBlockZ; + QLabel * m_StatusBiome; + /** The separator line between biome view and generator setup. */ QWidget * m_LineSeparator; + /** Index into m_ViewZooms[] for the current zoom level. */ + size_t m_CurrentZoomLevel; + /** Initializes the m_MinecraftPath based on the proper MC path */ void initMinecraftPath(); diff --git a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro index 9e5d1303c..9522491a8 100644 --- a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro +++ b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro @@ -12,76 +12,86 @@ TARGET = QtBiomeVisualiser TEMPLATE = app -SOURCES +=\ - MainWindow.cpp \ - BiomeView.cpp \ - ../../src/Generating/BioGen.cpp \ - ../../src/VoronoiMap.cpp \ - ../../src/Noise.cpp \ - ../../src/StringUtils.cpp \ - ../../src/LoggerListeners.cpp \ - ../../src/Logger.cpp \ - ../../lib/inifile/iniFile.cpp \ - ../../src/OSSupport/File.cpp \ - ../../src/OSSupport/CriticalSection.cpp \ - ../../src/OSSupport/IsThread.cpp \ - ../../src/BiomeDef.cpp \ - ChunkCache.cpp \ - ChunkSource.cpp \ - ChunkLoader.cpp \ - ../../src/StringCompression.cpp \ - ../../src/WorldStorage/FastNBT.cpp \ - ../../lib/zlib/adler32.c \ - ../../lib/zlib/compress.c \ - ../../lib/zlib/crc32.c \ - ../../lib/zlib/deflate.c \ - ../../lib/zlib/gzclose.c \ - ../../lib/zlib/gzlib.c \ - ../../lib/zlib/gzread.c \ - ../../lib/zlib/gzwrite.c \ - ../../lib/zlib/infback.c \ - ../../lib/zlib/inffast.c \ - ../../lib/zlib/inflate.c \ - ../../lib/zlib/inftrees.c \ - ../../lib/zlib/trees.c \ - ../../lib/zlib/uncompr.c \ - ../../lib/zlib/zutil.c \ - GeneratorSetup.cpp \ - QtBiomeVisualiser.cpp \ - QtChunk.cpp - -HEADERS += MainWindow.h \ - Globals.h \ - BiomeView.h \ - ../../src/Generating/BioGen.h \ - ../../src/VoronoiMap.h \ - ../../src/Noise.h \ - ../../src/StringUtils.h \ - ../../src/LoggerListeners.h \ - ../../src/Logger.h \ - ../../lib/inifile/iniFile.h \ - ../../src/OSSupport/File.h \ - ../../src/OSSupport/CriticalSection.h \ - ../../src/OSSupport/IsThread.h \ - ../../src/BiomeDef.h \ - ChunkCache.h \ - ChunkSource.h \ - ChunkLoader.h \ - ../../src/StringCompression.h \ - ../../src/WorldStorage/FastNBT.h \ - ../../lib/zlib/crc32.h \ - ../../lib/zlib/deflate.h \ - ../../lib/zlib/gzguts.h \ - ../../lib/zlib/inffast.h \ - ../../lib/zlib/inffixed.h \ - ../../lib/zlib/inflate.h \ - ../../lib/zlib/inftrees.h \ - ../../lib/zlib/trees.h \ - ../../lib/zlib/zconf.h \ - ../../lib/zlib/zlib.h \ - ../../lib/zlib/zutil.h \ - GeneratorSetup.h \ - QtChunk.h +SOURCES += \ + MainWindow.cpp \ + BiomeView.cpp \ + ../../src/Generating/BioGen.cpp \ + ../../src/VoronoiMap.cpp \ + ../../src/Noise.cpp \ + ../../src/StringUtils.cpp \ + ../../src/LoggerListeners.cpp \ + ../../src/Logger.cpp \ + ../../src/IniFile.cpp \ + ../../src/OSSupport/File.cpp \ + ../../src/OSSupport/CriticalSection.cpp \ + ../../src/OSSupport/IsThread.cpp \ + ../../src/BiomeDef.cpp \ + ../../src/StringCompression.cpp \ + ../../src/WorldStorage/FastNBT.cpp \ + ../../lib/zlib/adler32.c \ + ../../lib/zlib/compress.c \ + ../../lib/zlib/crc32.c \ + ../../lib/zlib/deflate.c \ + ../../lib/zlib/gzclose.c \ + ../../lib/zlib/gzlib.c \ + ../../lib/zlib/gzread.c \ + ../../lib/zlib/gzwrite.c \ + ../../lib/zlib/infback.c \ + ../../lib/zlib/inffast.c \ + ../../lib/zlib/inflate.c \ + ../../lib/zlib/inftrees.c \ + ../../lib/zlib/trees.c \ + ../../lib/zlib/uncompr.c \ + ../../lib/zlib/zutil.c \ + GeneratorSetup.cpp \ + QtBiomeVisualiser.cpp \ + QtChunk.cpp \ + RegionCache.cpp \ + Region.cpp \ + ChunkSource.cpp \ + RegionLoader.cpp + + + +HEADERS += \ + MainWindow.h \ + QtChunk.h \ + Globals.h \ + BiomeView.h \ + ../../src/Generating/BioGen.h \ + ../../src/Generating/IntGen.h \ + ../../src/Generating/ProtIntGen.h \ + ../../src/VoronoiMap.h \ + ../../src/Noise.h \ + ../../src/StringUtils.h \ + ../../src/LoggerListeners.h \ + ../../src/Logger.h \ + ../../src/IniFile.h \ + ../../src/OSSupport/File.h \ + ../../src/OSSupport/CriticalSection.h \ + ../../src/OSSupport/IsThread.h \ + ../../src/BiomeDef.h \ + ../../src/StringCompression.h \ + ../../src/WorldStorage/FastNBT.h \ + ../../lib/zlib/crc32.h \ + ../../lib/zlib/deflate.h \ + ../../lib/zlib/gzguts.h \ + ../../lib/zlib/inffast.h \ + ../../lib/zlib/inffixed.h \ + ../../lib/zlib/inflate.h \ + ../../lib/zlib/inftrees.h \ + ../../lib/zlib/trees.h \ + ../../lib/zlib/zconf.h \ + ../../lib/zlib/zlib.h \ + ../../lib/zlib/zutil.h \ + GeneratorSetup.h \ + QtChunk.h \ + RegionCache.h \ + Region.h \ + ChunkSource.h \ + RegionLoader.h + + INCLUDEPATH += $$_PRO_FILE_PWD_ \ $$_PRO_FILE_PWD_/../../lib \ diff --git a/Tools/QtBiomeVisualiser/QtChunk.cpp b/Tools/QtBiomeVisualiser/QtChunk.cpp index 80109b2f8..f201ef220 100644 --- a/Tools/QtBiomeVisualiser/QtChunk.cpp +++ b/Tools/QtBiomeVisualiser/QtChunk.cpp @@ -14,20 +14,26 @@ Chunk::Chunk() : -const uchar * Chunk::getImage(void) const +void Chunk::setBiomes(const cChunkDef::BiomeMap & a_Biomes) { - ASSERT(m_IsValid); - return m_Image; + for (size_t idx = 0; idx < ARRAYCOUNT(a_Biomes); ++idx) + { + m_Biomes[idx] = static_cast<short>(a_Biomes[idx]); + } + m_IsValid = true; } -void Chunk::setImage(const Image & a_Image) +EMCSBiome Chunk::getBiome(int a_RelX, int a_RelZ) { - memcpy(m_Image, a_Image, sizeof(a_Image)); - m_IsValid = true; + if (!m_IsValid) + { + return biInvalidBiome; + } + return static_cast<EMCSBiome>(m_Biomes[a_RelX + 16 * a_RelZ]); } diff --git a/Tools/QtBiomeVisualiser/QtChunk.h b/Tools/QtBiomeVisualiser/QtChunk.h index 03e7bd1b3..d806d18bb 100644 --- a/Tools/QtBiomeVisualiser/QtChunk.h +++ b/Tools/QtBiomeVisualiser/QtChunk.h @@ -18,18 +18,23 @@ public: /** Returns true iff the chunk data is valid - loaded or generated. */ bool isValid(void) const { return m_IsValid; } - /** Returns the image of the chunk's biomes. Assumes that the chunk is valid. */ - const uchar * getImage(void) const; + /** Sets the biomes to m_Biomes and renders them into m_Image. */ + void setBiomes(const cChunkDef::BiomeMap & a_Biomes); - /** Sets the image data for this chunk. */ - void setImage(const Image & a_Image); + /** Returns the biome at the specified relative coords, or biInvalidBiome if not valid. + Coords must be valid inside this chunk. */ + EMCSBiome getBiome(int a_RelX, int a_RelZ); + + /** Returns the raw biome data for this chunk. */ + const short * getBiomes(void) const { return m_Biomes; } protected: /** Flag that specifies if the chunk data is valid - loaded or generated. */ bool m_IsValid; - /** Cached rendered image of this chunk's biomes. Updated in render(). */ - Image m_Image; + /** Biomes comprising the chunk, in the X + 16 * Z ordering. + Typed as short to save on memory, converted automatically when needed. */ + short m_Biomes[16 * 16]; }; typedef std::shared_ptr<Chunk> ChunkPtr; diff --git a/Tools/QtBiomeVisualiser/Region.cpp b/Tools/QtBiomeVisualiser/Region.cpp new file mode 100644 index 000000000..d8a0a2f76 --- /dev/null +++ b/Tools/QtBiomeVisualiser/Region.cpp @@ -0,0 +1,72 @@ + +#include "Globals.h" +#include "Region.h" + + + + + +Region::Region() +{ +} + + + + + +Chunk & Region::getRelChunk(int a_RelChunkX, int a_RelChunkZ) +{ + ASSERT(a_RelChunkX >= 0); + ASSERT(a_RelChunkZ >= 0); + ASSERT(a_RelChunkX < 32); + ASSERT(a_RelChunkZ < 32); + + return m_Chunks[a_RelChunkX + a_RelChunkZ * 32]; +} + + + + + +int Region::getRelBiome(int a_RelBlockX, int a_RelBlockZ) +{ + ASSERT(a_RelBlockX >= 0); + ASSERT(a_RelBlockZ >= 0); + ASSERT(a_RelBlockX < 512); + ASSERT(a_RelBlockZ < 512); + + int chunkX = a_RelBlockX / 16; + int chunkZ = a_RelBlockZ / 16; + Chunk & chunk = m_Chunks[chunkX + 32 * chunkZ]; + if (chunk.isValid()) + { + return chunk.getBiome(a_RelBlockX - 16 * chunkX, a_RelBlockZ - 16 * chunkZ); + } + else + { + return biInvalidBiome; + } +} + + + + +void Region::blockToRegion(int a_BlockX, int a_BlockZ, int & a_RegionX, int & a_RegionZ) +{ + a_RegionX = static_cast<int>(std::floor(static_cast<float>(a_BlockX) / 512)); + a_RegionZ = static_cast<int>(std::floor(static_cast<float>(a_BlockZ) / 512)); +} + + + + + +void Region::chunkToRegion(int a_ChunkX, int a_ChunkZ, int & a_RegionX, int & a_RegionZ) +{ + a_RegionX = static_cast<int>(std::floor(static_cast<float>(a_ChunkX) / 32)); + a_RegionZ = static_cast<int>(std::floor(static_cast<float>(a_ChunkZ) / 32)); +} + + + + diff --git a/Tools/QtBiomeVisualiser/Region.h b/Tools/QtBiomeVisualiser/Region.h new file mode 100644 index 000000000..863c0ac02 --- /dev/null +++ b/Tools/QtBiomeVisualiser/Region.h @@ -0,0 +1,46 @@ +#pragma once + +#include "QtChunk.h" + + + + + +class Region +{ +public: + Region(); + + /** Retrieves the chunk with the specified relative coords. */ + Chunk & getRelChunk(int a_RelChunkX, int a_RelChunkZ); + + /** Returns true iff the chunk data for all chunks has been loaded. + This doesn't mean that all the chunks are valid, only that the entire region has been processed and should + be displayed. */ + bool isValid(void) const { return m_IsValid; } + + /** Returns the biome in the block coords relative to this region. + Returns biInvalidBiome if the underlying chunk is not valid. */ + int getRelBiome(int a_RelBlockX, int a_RelBlockZ); + + /** Converts block coordinates into region coordinates. */ + static void blockToRegion(int a_BlockX, int a_BlockZ, int & a_RegionX, int & a_RegionZ); + + /** Converts chunk coordinates into region coordinates. */ + static void chunkToRegion(int a_ChunkX, int a_ChunkZ, int & a_RegionX, int & a_RegionZ); + +protected: + friend class RegionLoader; + + + Chunk m_Chunks[32 * 32]; + + /** True iff the data for all the chunks has been loaded. + This doesn't mean that all the chunks are valid, only that the entire region has been processed and should + be displayed. */ + bool m_IsValid; +}; + + + + diff --git a/Tools/QtBiomeVisualiser/RegionCache.cpp b/Tools/QtBiomeVisualiser/RegionCache.cpp new file mode 100644 index 000000000..e46fd222a --- /dev/null +++ b/Tools/QtBiomeVisualiser/RegionCache.cpp @@ -0,0 +1,138 @@ +#include "Globals.h" +#include "RegionCache.h" +#include <QMutexLocker> +#include <QThreadPool> +#include "ChunkSource.h" +#include "RegionLoader.h" +#include "Region.h" + + + + + +RegionCache::RegionCache(QObject * parent) : + super(parent) +{ + m_Cache.setMaxCost(1024 * 1024 * 1024); // 1 GiB of memory for the cache +} + + + + + +RegionPtr RegionCache::fetch(int a_RegionX, int a_RegionZ) +{ + // Retrieve from the cache: + quint32 hash = getRegionHash(a_RegionX, a_RegionZ); + RegionPtr * res; + { + QMutexLocker lock(&m_Mtx); + res = m_Cache[hash]; + // If succesful and region loaded, return the retrieved value: + if ((res != nullptr) && (*res)->isValid()) + { + return *res; + } + } + + // If the region is in cache but not valid, it means it has been already queued for rendering, do nothing now: + if (res != nullptr) + { + return RegionPtr(nullptr); + } + + // There's no such item in the cache, create it now: + try + { + res = new RegionPtr(new Region); + } + catch (const std::bad_alloc &) + { + /* Allocation failed (32-bit process hit the 2 GiB barrier?) + This may happen even with the cache set to 1 GiB, because it contains shared ptrs and so they may be + held by another place in the code even when they are removed from cache. + */ + return RegionPtr(nullptr); + } + if (res == nullptr) + { + return RegionPtr(nullptr); + } + { + QMutexLocker lock(&m_Mtx); + m_Cache.insert(hash, res, sizeof(Region)); + } + + // Queue the region for rendering: + queueRegionRender(a_RegionX, a_RegionZ, *res); + + // Return failure, the region is not yet rendered: + return RegionPtr(nullptr); +} + + + + + +void RegionCache::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource) +{ + // Replace the chunk source: + m_ChunkSource = a_ChunkSource; + + // Clear the cache: + QMutexLocker lock(&m_Mtx); + m_Cache.clear(); +} + + + + + +void RegionCache::reload() +{ + assert(m_ChunkSource.get() != nullptr); + + // Reload the chunk source: + m_ChunkSource->reload(); + + // Clear the cache: + QMutexLocker lock(&m_Mtx); + m_Cache.clear(); +} + + + + + +void RegionCache::gotRegion(int a_RegionX, int a_RegionZ) +{ + emit regionAvailable(a_RegionX, a_RegionZ); +} + + + + + +quint32 RegionCache::getRegionHash(int a_RegionX, int a_RegionZ) +{ + // Simply join the two coords into a single int + // The coords will never be larger than 16-bits, so we can do this safely + return (((static_cast<quint32>(a_RegionX) & 0xffff) << 16) | (static_cast<quint32>(a_RegionZ) & 0xffff)); +} + + + + + +void RegionCache::queueRegionRender(int a_RegionX, int a_RegionZ, RegionPtr & a_Region) +{ + // Create a new loader task: + RegionLoader * loader = new RegionLoader(a_RegionX, a_RegionZ, a_Region, m_ChunkSource); + connect(loader, SIGNAL(loaded(int, int)), this, SLOT(gotRegion(int, int))); + + QThreadPool::globalInstance()->start(loader); +} + + + + diff --git a/Tools/QtBiomeVisualiser/ChunkCache.h b/Tools/QtBiomeVisualiser/RegionCache.h index 8d198f02f..c343e4ba9 100644 --- a/Tools/QtBiomeVisualiser/ChunkCache.h +++ b/Tools/QtBiomeVisualiser/RegionCache.h @@ -9,8 +9,9 @@ -class Chunk; -typedef std::shared_ptr<Chunk> ChunkPtr; +// fwd: +class Region; +typedef std::shared_ptr<Region> RegionPtr; class ChunkSource; @@ -18,19 +19,19 @@ class ChunkSource; -/** Caches chunk data for reuse */ -class ChunkCache : +/** Caches regions' chunk data for reuse */ +class RegionCache : public QObject { typedef QObject super; Q_OBJECT public: - explicit ChunkCache(QObject * parent = NULL); + explicit RegionCache(QObject * parent = NULL); - /** Retrieves the specified chunk from the cache. - Only returns valid chunks; if the chunk is invalid, queues it for rendering and returns an empty ptr. */ - ChunkPtr fetch(int a_ChunkX, int a_ChunkZ); + /** Retrieves the specified region from the cache. + Only returns valid regions; if the region is invalid, queues it for rendering and returns an empty ptr. */ + RegionPtr fetch(int a_RegionX, int a_RegionZ); /** Replaces the chunk source used by the biome view to get the chunk biome data. The cache is then invalidated. */ @@ -43,16 +44,16 @@ public: void reload(); signals: - void chunkAvailable(int a_ChunkX, int a_ChunkZ); + void regionAvailable(int a_RegionX, int a_RegionZ); protected slots: - void gotChunk(int a_ChunkX, int a_ChunkZ); + void gotRegion(int a_RegionX, int a_RegionZ); protected: /** The cache of the chunks */ - QCache<quint32, ChunkPtr> m_Cache; + QCache<quint32, RegionPtr> m_Cache; - /** Locks te cache against multithreaded access */ + /** Locks the cache against multithreaded access */ QMutex m_Mtx; /** The source used to get the biome data. */ @@ -60,10 +61,10 @@ protected: /** Returns the hash used by the chunk in the cache */ - quint32 getChunkHash(int a_ChunkX, int a_ChunkZ); + quint32 getRegionHash(int a_RegionX, int a_RegionZ); - /** Queues the specified chunk for rendering by m_ChunkSource. */ - void queueChunkRender(int a_ChunkX, int a_ChunkZ, ChunkPtr & a_Chunk); + /** Queues the specified region for rendering by m_RegionSource. */ + void queueRegionRender(int a_RegionX, int a_RegionZ, RegionPtr & a_Region); }; diff --git a/Tools/QtBiomeVisualiser/RegionLoader.cpp b/Tools/QtBiomeVisualiser/RegionLoader.cpp new file mode 100644 index 000000000..2a318098b --- /dev/null +++ b/Tools/QtBiomeVisualiser/RegionLoader.cpp @@ -0,0 +1,49 @@ +#include "Globals.h" +#include "RegionLoader.h" +#include "ChunkSource.h" +#include "Region.h" + + + + + +volatile bool RegionLoader::m_IsShuttingDown = false; + + + + + +RegionLoader::RegionLoader(int a_RegionX, int a_RegionZ, RegionPtr a_Region, ChunkSourcePtr a_ChunkSource) : + m_RegionX(a_RegionX), + m_RegionZ(a_RegionZ), + m_Region(a_Region), + m_ChunkSource(a_ChunkSource) +{ +} + + + + + +void RegionLoader::run() +{ + // Load all the chunks in this region: + for (int z = 0; z < 32; z++) + { + for (int x = 0; x < 32; x++) + { + m_ChunkSource->getChunkBiomes(m_RegionX * 32 + x, m_RegionZ * 32 + z, m_Region->getRelChunk(x, z)); + if (m_IsShuttingDown) + { + return; + } + } + } + m_Region->m_IsValid = true; + + emit loaded(m_RegionX, m_RegionZ); +} + + + + diff --git a/Tools/QtBiomeVisualiser/RegionLoader.h b/Tools/QtBiomeVisualiser/RegionLoader.h new file mode 100644 index 000000000..6bbb4aa60 --- /dev/null +++ b/Tools/QtBiomeVisualiser/RegionLoader.h @@ -0,0 +1,56 @@ +#pragma once + +#include <QObject> +#include <QRunnable> +#include <memory> + + + + +// fwd: +class Region; +typedef std::shared_ptr<Region> RegionPtr; + +class ChunkSource; +typedef std::shared_ptr<ChunkSource> ChunkSourcePtr; + + + + + +class RegionLoader : + public QObject, + public QRunnable +{ + Q_OBJECT + +public: + RegionLoader(int a_RegionX, int a_RegionZ, RegionPtr a_Region, ChunkSourcePtr a_ChunkSource); + virtual ~RegionLoader() {} + + /** Signals to all loaders that the app is shutting down and the loading should be aborted. */ + static void shutdown() { m_IsShuttingDown = true; } + +signals: + void loaded(int a_RegionX, int a_RegionZ); + +protected: + virtual void run() override; + +private: + /** Coords of the region to be loaded. */ + int m_RegionX, m_RegionZ; + + /** The region to be loaded. */ + RegionPtr m_Region; + + /** The chunk source to be used for individual chunks within the region. */ + ChunkSourcePtr m_ChunkSource; + + /** Flag that is set upon app exit to terminate the queued loaders faster. */ + static volatile bool m_IsShuttingDown; +}; + + + + |