diff options
Diffstat (limited to '')
-rw-r--r-- | Tools/QtBiomeVisualiser/.gitignore | 2 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/BiomeView.cpp | 246 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/BiomeView.h | 65 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/Chunk.cpp | 36 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/Chunk.h | 40 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkCache.cpp | 110 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkCache.h | 68 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkLoader.cpp | 29 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkLoader.h | 43 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkSource.cpp | 164 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/ChunkSource.h | 60 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/Globals.h | 386 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/MainWindow.cpp | 104 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/MainWindow.h | 46 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro | 62 | ||||
-rw-r--r-- | Tools/QtBiomeVisualiser/main.cpp | 20 |
16 files changed, 1481 insertions, 0 deletions
diff --git a/Tools/QtBiomeVisualiser/.gitignore b/Tools/QtBiomeVisualiser/.gitignore new file mode 100644 index 000000000..c1b62a8a7 --- /dev/null +++ b/Tools/QtBiomeVisualiser/.gitignore @@ -0,0 +1,2 @@ +*.pro.user +*.pro.user.* diff --git a/Tools/QtBiomeVisualiser/BiomeView.cpp b/Tools/QtBiomeVisualiser/BiomeView.cpp new file mode 100644 index 000000000..be01fd104 --- /dev/null +++ b/Tools/QtBiomeVisualiser/BiomeView.cpp @@ -0,0 +1,246 @@ +#include "Globals.h" +#include "BiomeView.h" +#include "Chunk.h" +#include <QPainter> +#include <QResizeEvent> + + + + + +BiomeView::BiomeView(QWidget * parent) : + super(parent), + m_X(0), + m_Z(0), + m_Zoom(1) +{ + // Create the image used for undefined chunks: + int offset = 0; + for (int y = 0; y < 16; y++) + { + for (int x = 0; x < 16; x++) + { + uchar color = (((x & 8) ^ (y & 8)) == 0) ? 0x44 : 0x88; + m_EmptyChunkImage[offset++] = color; + m_EmptyChunkImage[offset++] = color; + m_EmptyChunkImage[offset++] = color; + m_EmptyChunkImage[offset++] = 0xff; + } + } + + // Create the startup image: + redraw(); + + // Add a chunk-update callback mechanism: + connect(&m_Cache, SIGNAL(chunkAvailable(int, int)), this, SLOT(chunkAvailable(int, int))); +} + + + + +QSize BiomeView::minimumSizeHint() const +{ + return QSize(300, 300); +} + + + + + +QSize BiomeView::sizeHint() const +{ + return QSize(800, 600); +} + + + + + +void BiomeView::setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource) +{ + // Replace the source in the cache: + m_Cache.setChunkSource(a_ChunkSource); + + // Redraw with the new source: + redraw(); +} + + + + + +void BiomeView::redraw() +{ + if (!hasData()) + { + // No data means no image is displayed, no need to compose: + update(); + return; + } + + int chunksize = 16 * m_Zoom; + + // first find the center block position + int centerchunkx = floor(m_X / 16); + int centerchunkz = floor(m_Z / 16); + // and the center of the screen + int centerx = m_Image.width() / 2; + int centery = m_Image.height() / 2; + // and align for panning + centerx -= (m_X - centerchunkx * 16) * m_Zoom; + centery -= (m_Z - centerchunkz * 16) * m_Zoom; + // now calculate the topleft block on the screen + int startx = centerchunkx - centerx / chunksize - 1; + int startz = centerchunkz - centery / chunksize - 1; + // and the dimensions of the screen in blocks + int blockswide = m_Image.width() / chunksize + 3; + int blockstall = m_Image.height() / chunksize + 3; + + for (int z = startz; z < startz + blockstall; z++) + { + for (int x = startx; x < startx + blockswide; x++) + { + drawChunk(x, z); + } + } + update(); +} + + + + + +void BiomeView::chunkAvailable(int a_ChunkX, int a_ChunkZ) +{ + drawChunk(a_ChunkX, a_ChunkZ); + update(); +} + + + + + +void BiomeView::drawChunk(int a_ChunkX, int a_ChunkZ) +{ + if (!hasData()) + { + return; + } + + //fetch the chunk: + ChunkPtr chunk = m_Cache.fetch(a_ChunkX, a_ChunkZ); + + // Figure out where on the screen this chunk should be drawn: + // first find the center chunk + int centerchunkx = floor(m_X / 16); + int centerchunkz = floor(m_Z / 16); + // and the center chunk screen coordinates + int centerx = m_Image.width() / 2; + int centery = m_Image.height() / 2; + // which need to be shifted to account for panning inside that chunk + centerx -= (m_X - centerchunkx * 16) * m_Zoom; + centery -= (m_Z - centerchunkz * 16) * m_Zoom; + // centerx,y now points to the top left corner of the center chunk + // so now calculate our x,y in relation + double chunksize = 16 * m_Zoom; + 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 blockwidth = chunksize, blockheight = chunksize; + // now if we're off the screen we need to crop + if (centerx < 0) + { + skipx = -centerx; + centerx = 0; + } + if (centery < 0) + { + skipy = -centery; + centery = 0; + } + // or the other side, we need to trim + if (centerx + blockwidth > m_Image.width()) + { + blockwidth = m_Image.width() - centerx; + } + if (centery + blockheight > m_Image.height()) + { + blockheight = m_Image.height() - centery; + } + if ((blockwidth <= 0) || (skipx >= blockwidth)) + { + return; + } + 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) + { + src = chunk->getImage(); + } + + // Blit or 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 + { + int xofs = 0; + for (int x = skipx; x < blockwidth; x++, xofs +=4) + { + memcpy(bits + imgoffset + xofs, src + srcoffset + (int)floor((double)x / m_Zoom) * 4, 4); + } + } + } +} + + + + + +void BiomeView::resizeEvent(QResizeEvent * a_Event) +{ + m_Image = QImage(a_Event->size(), QImage::Format_RGB32); + redraw(); +} + + + + + +void BiomeView::paintEvent(QPaintEvent * a_Event) +{ + QPainter p(this); + if (hasData()) + { + p.drawImage(QPoint(0, 0), m_Image); + } + else + { + p.drawText(a_Event->rect(), Qt::AlignCenter, "No chunk source selected"); + } + p.end(); +} + + + + + +void BiomeView::queueChunkRender(ChunkPtr a_Chunk) +{ + +} + + + + diff --git a/Tools/QtBiomeVisualiser/BiomeView.h b/Tools/QtBiomeVisualiser/BiomeView.h new file mode 100644 index 000000000..c54c66491 --- /dev/null +++ b/Tools/QtBiomeVisualiser/BiomeView.h @@ -0,0 +1,65 @@ +#pragma once + +#include <QWidget> +#include "ChunkCache.h" +#include "ChunkSource.h" + + + + + +class BiomeView : + public QWidget +{ + typedef QWidget super; + Q_OBJECT + +public: + explicit BiomeView(QWidget * parent = NULL); + + QSize minimumSizeHint() const; + QSize sizeHint() const; + + /** Replaces the chunk source used by the biome view to get the chunk biome data. + The entire view is then invalidated and regenerated. */ + void setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource); + +signals: + +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); + +protected: + double m_X, m_Z; + int m_Zoom; + ChunkCache m_Cache; + QImage m_Image; + + /** Data used for rendering a chunk that hasn't been loaded yet */ + uchar m_EmptyChunkImage[16 * 16 * 4]; + + + /** Draws the specified chunk into m_Image */ + void drawChunk(int a_ChunkX, int a_ChunkZ); + + /** Returns true iff the biome view has been initialized to contain proper biome data. */ + bool hasData(void) const { return m_Cache.hasData(); } + + /** Called when the widget is resized */ + virtual void resizeEvent(QResizeEvent *) override; + + /** Paints the entire widget */ + virtual void paintEvent(QPaintEvent *) override; + + /** Queues the chunk for rendering. */ + void queueChunkRender(ChunkPtr a_Chunk); +}; + + + + + diff --git a/Tools/QtBiomeVisualiser/Chunk.cpp b/Tools/QtBiomeVisualiser/Chunk.cpp new file mode 100644 index 000000000..d3419af9c --- /dev/null +++ b/Tools/QtBiomeVisualiser/Chunk.cpp @@ -0,0 +1,36 @@ +#include "Globals.h" +#include "Globals.h" +#include "Chunk.h" + + + + + +Chunk::Chunk() : + m_IsValid(false) +{ +} + + + + + +const uchar * Chunk::getImage(void) const +{ + ASSERT(m_IsValid); + return m_Image; +} + + + + + +void Chunk::setImage(const Image & a_Image) +{ + memcpy(m_Image, a_Image, sizeof(a_Image)); + m_IsValid = true; +} + + + + diff --git a/Tools/QtBiomeVisualiser/Chunk.h b/Tools/QtBiomeVisualiser/Chunk.h new file mode 100644 index 000000000..03e7bd1b3 --- /dev/null +++ b/Tools/QtBiomeVisualiser/Chunk.h @@ -0,0 +1,40 @@ +#pragma once + +#include <qglobal.h> + + + + + +class Chunk +{ +public: + /** The type used for storing image data for a chunk. */ + typedef uchar Image[16 * 16 * 4]; + + + Chunk(void); + + /** 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 image data for this chunk. */ + void setImage(const Image & a_Image); + +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; +}; + +typedef std::shared_ptr<Chunk> ChunkPtr; + + + + + diff --git a/Tools/QtBiomeVisualiser/ChunkCache.cpp b/Tools/QtBiomeVisualiser/ChunkCache.cpp new file mode 100644 index 000000000..b2230def0 --- /dev/null +++ b/Tools/QtBiomeVisualiser/ChunkCache.cpp @@ -0,0 +1,110 @@ +#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::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/ChunkCache.h b/Tools/QtBiomeVisualiser/ChunkCache.h new file mode 100644 index 000000000..0efa7fc39 --- /dev/null +++ b/Tools/QtBiomeVisualiser/ChunkCache.h @@ -0,0 +1,68 @@ +#pragma once + +#include <QObject> +#include <QCache> +#include <QMutex> + + + + + +class Chunk; +typedef std::shared_ptr<Chunk> ChunkPtr; + +class ChunkSource; + + + + + +/** Caches chunk data for reuse */ +class ChunkCache : + public QObject +{ + typedef QObject super; + Q_OBJECT + +public: + explicit ChunkCache(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); + + /** Replaces the chunk source used by the biome view to get the chunk biome data. + The cache is then invalidated. */ + void setChunkSource(std::shared_ptr<ChunkSource> a_ChunkSource); + + /** Returns true iff the chunk source has been initialized. */ + bool hasData(void) const { return (m_ChunkSource.get() != nullptr); } + +signals: + void chunkAvailable(int a_ChunkX, int a_ChunkZ); + +protected slots: + void gotChunk(int a_ChunkX, int a_ChunkZ); + +protected: + /** The cache of the chunks */ + QCache<quint32, ChunkPtr> m_Cache; + + /** Locks te cache against multithreaded access */ + QMutex m_Mtx; + + /** The source used to get the biome data. */ + std::shared_ptr<ChunkSource> m_ChunkSource; + + + /** Returns the hash used by the chunk in the cache */ + quint32 getChunkHash(int a_ChunkX, int a_ChunkZ); + + /** Queues the specified chunk for rendering by m_ChunkSource. */ + void queueChunkRender(int a_ChunkX, int a_ChunkZ, ChunkPtr & a_Chunk); +}; + + + + + diff --git a/Tools/QtBiomeVisualiser/ChunkLoader.cpp b/Tools/QtBiomeVisualiser/ChunkLoader.cpp new file mode 100644 index 000000000..3d0123b23 --- /dev/null +++ b/Tools/QtBiomeVisualiser/ChunkLoader.cpp @@ -0,0 +1,29 @@ +#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 new file mode 100644 index 000000000..3565434b9 --- /dev/null +++ b/Tools/QtBiomeVisualiser/ChunkLoader.h @@ -0,0 +1,43 @@ +#pragma once +#include <QObject> +#include <QRunnable> + + + + +// 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 new file mode 100644 index 000000000..44dcf1fa7 --- /dev/null +++ b/Tools/QtBiomeVisualiser/ChunkSource.cpp @@ -0,0 +1,164 @@ +#include "Globals.h" +#include "ChunkSource.h" +#include "Generating/BioGen.h" + + + + + +/** 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[0]; + color[1] = biomeColors[i].m_Color[1]; + color[2] = biomeColors[i].m_Color[2]; + color[3] = 0xff; + } + } +} biomeColorInitializer; + + + + + +/** Converts biomes in an array into the chunk image data. */ +static void biomesToImage(cChunkDef::BiomeMap & a_Biomes, Chunk::Image & a_Image) +{ + // 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]; + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// BioGenSource: + +BioGenSource::BioGenSource(cBiomeGen * a_BiomeGen) : + m_BiomeGen(a_BiomeGen) +{ +} + + + + + +void BioGenSource::getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) +{ + // TODO: To make use of multicore machines, we need multiple copies of the biomegen + // Right now we have only one, so we can let only one thread use it (hence the mutex) + QMutexLocker lock(&m_Mtx); + cChunkDef::BiomeMap biomes; + m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, biomes); + Chunk::Image img; + biomesToImage(biomes, img); + a_DestChunk->setImage(img); +} + + + + diff --git a/Tools/QtBiomeVisualiser/ChunkSource.h b/Tools/QtBiomeVisualiser/ChunkSource.h new file mode 100644 index 000000000..d6eb2e3cb --- /dev/null +++ b/Tools/QtBiomeVisualiser/ChunkSource.h @@ -0,0 +1,60 @@ +#pragma once +#include "Chunk.h" + + + + + +// fwd: +class cBiomeGen; + + + + + +/** Abstract interface for getting biome data for chunks. */ +class ChunkSource +{ +public: + virtual ~ChunkSource() {} + + /** 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; +}; + + + + + + +class BioGenSource : + public ChunkSource +{ +public: + /** Constructs a new BioGenSource based on the biome generator given. + Takes ownership of a_BiomeGen */ + BioGenSource(cBiomeGen * a_BiomeGen); + + virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override; + +protected: + std::shared_ptr<cBiomeGen> m_BiomeGen; + QMutex m_Mtx; +}; + + + + +class AnvilSource : + public ChunkSource +{ +public: + // TODO + + virtual void getChunkBiomes(int a_ChunkX, int a_ChunkZ, ChunkPtr a_DestChunk) override; +}; + + + + diff --git a/Tools/QtBiomeVisualiser/Globals.h b/Tools/QtBiomeVisualiser/Globals.h new file mode 100644 index 000000000..d3c7f0675 --- /dev/null +++ b/Tools/QtBiomeVisualiser/Globals.h @@ -0,0 +1,386 @@ +#pragma once + + + + + +// Compiler-dependent stuff: +#if defined(_MSC_VER) + // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether + #pragma warning(disable:4481) + + // Disable some warnings that we don't care about: + #pragma warning(disable:4100) // Unreferenced formal parameter + + // Useful warnings from warning level 4: + #pragma warning(3 : 4127) // Conditional expression is constant + #pragma warning(3 : 4189) // Local variable is initialized but not referenced + #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed/unsigned mismatch + #pragma warning(3 : 4310) // Cast truncates constant value + #pragma warning(3 : 4389) // Signed/unsigned mismatch + #pragma warning(3 : 4505) // Unreferenced local function has been removed + #pragma warning(3 : 4701) // Potentially unitialized local variable used + #pragma warning(3 : 4702) // Unreachable code + #pragma warning(3 : 4706) // Assignment within conditional expression + + // Disabling this warning, because we know what we're doing when we're doing this: + #pragma warning(disable: 4355) // 'this' used in initializer list + + // Disabled because it's useless: + #pragma warning(disable: 4512) // 'class': assignment operator could not be generated - reported for each class that has a reference-type member + + // 2014_01_06 xoft: Disabled this warning because MSVC is stupid and reports it in obviously wrong places + // #pragma warning(3 : 4244) // Conversion from 'type1' to 'type2', possible loss of data + + #define OBSOLETE __declspec(deprecated) + + // No alignment needed in MSVC + #define ALIGN_8 + #define ALIGN_16 + + #define FORMATSTRING(formatIndex, va_argsIndex) + + // MSVC has its own custom version of zu format + #define SIZE_T_FMT "%Iu" + #define SIZE_T_FMT_PRECISION(x) "%" #x "Iu" + #define SIZE_T_FMT_HEX "%Ix" + + #define NORETURN __declspec(noreturn) + +#elif defined(__GNUC__) + + // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? + #define abstract + + // override is part of c++11 + #if __cplusplus < 201103L + #define override + #endif + + #define OBSOLETE __attribute__((deprecated)) + + #define ALIGN_8 __attribute__((aligned(8))) + #define ALIGN_16 __attribute__((aligned(16))) + + // Some portability macros :) + #define stricmp strcasecmp + + #define FORMATSTRING(formatIndex, va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex))) + + #if defined(_WIN32) + // We're compiling on MinGW, which uses an old MSVCRT library that has no support for size_t printfing. + // We need direct size formats: + #if defined(_WIN64) + #define SIZE_T_FMT "%I64u" + #define SIZE_T_FMT_PRECISION(x) "%" #x "I64u" + #define SIZE_T_FMT_HEX "%I64x" + #else + #define SIZE_T_FMT "%u" + #define SIZE_T_FMT_PRECISION(x) "%" #x "u" + #define SIZE_T_FMT_HEX "%x" + #endif + #else + // We're compiling on Linux, so we can use libc's size_t printf format: + #define SIZE_T_FMT "%zu" + #define SIZE_T_FMT_PRECISION(x) "%" #x "zu" + #define SIZE_T_FMT_HEX "%zx" + #endif + + #define NORETURN __attribute((__noreturn__)) + +#else + + #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" + + /* + // Copy and uncomment this into another #elif section based on your compiler identification + + // Explicitly mark classes as abstract (no instances can be created) + #define abstract + + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + // Mark functions as obsolete, so that their usage results in a compile-time warning + #define OBSOLETE + + // Mark types / variables for alignment. Do the platforms need it? + #define ALIGN_8 + #define ALIGN_16 + */ + +#endif + + +#ifdef _DEBUG + #define NORETURNDEBUG NORETURN +#else + #define NORETURNDEBUG +#endif + + +#include <stddef.h> + + +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; + +typedef unsigned char Byte; + + +// If you get an error about specialization check the size of integral types +template <typename T, size_t Size, bool x = sizeof(T) == Size> +class SizeChecker; + +template <typename T, size_t Size> +class SizeChecker<T, Size, true> +{ + T v; +}; + +template class SizeChecker<Int64, 8>; +template class SizeChecker<Int32, 4>; +template class SizeChecker<Int16, 2>; + +template class SizeChecker<UInt64, 8>; +template class SizeChecker<UInt32, 4>; +template class SizeChecker<UInt16, 2>; + +// A macro to disallow the copy constructor and operator = functions +// This should be used in the private: declarations for any class that shouldn't allow copying itself +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &); \ + void operator =(const TypeName &) + +// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc +#define UNUSED(X) (void)(X) + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + + #define _WIN32_WINNT 0x501 // We want to target WinXP and higher + + #include <Windows.h> + #include <winsock2.h> + #include <Ws2tcpip.h> // IPv6 stuff + + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage + #undef min + #undef max + + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant + #ifdef GetFreeSpace + #undef GetFreeSpace + #endif // GetFreeSpace +#else + #include <sys/types.h> + #include <sys/time.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <time.h> + #include <dirent.h> + #include <errno.h> + #include <iostream> + #include <cstdio> + #include <cstring> + #include <pthread.h> + #include <semaphore.h> + #include <errno.h> + #include <fcntl.h> +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include <sys/stat.h> +#include <assert.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> + + + + + +// STL stuff: +#include <vector> +#include <list> +#include <deque> +#include <string> +#include <map> +#include <algorithm> +#include <memory> +#include <set> +#include <queue> +#include <limits> + + + +#ifndef TEST_GLOBALS + // Common headers (part 1, without macros): + #include "StringUtils.h" + #include "OSSupport/Sleep.h" + #include "OSSupport/CriticalSection.h" + #include "OSSupport/Semaphore.h" + #include "OSSupport/Event.h" + #include "OSSupport/Thread.h" + #include "OSSupport/File.h" + #include "Logger.h" +#else + // Logging functions +void inline LOGERROR(const char* a_Format, ...) FORMATSTRING(1, 2); + +void inline LOGERROR(const char* a_Format, ...) +{ + va_list argList; + va_start(argList, a_Format); + vprintf(a_Format, argList); + va_end(argList); +} +#endif + + + + + +// Common definitions: + +/// Evaluates to the number of elements in an array (compile-time!) +#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) + +/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)") +#define KiB * 1024 +#define MiB * 1024 * 1024 + +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div) (((x) - (((x) < 0) ? ((div) - 1) : 0)) / (div)) + +// Own version of assert() that writes failed assertions to the log for review +#ifdef TEST_GLOBALS + + class cAssertFailure + { + }; + + #ifdef _WIN32 + #if (defined(_MSC_VER) && defined(_DEBUG)) + #define DBG_BREAK _CrtDbgBreak() + #else + #define DBG_BREAK + #endif + #define REPORT_ERROR(FMT, ...) \ + { \ + AString msg = Printf(FMT, __VA_ARGS__); \ + puts(msg.c_str()); \ + fflush(stdout); \ + OutputDebugStringA(msg.c_str()); \ + DBG_BREAK; \ + } + #else + #define REPORT_ERROR(FMT, ...) \ + { \ + AString msg = Printf(FMT, __VA_ARGS__); \ + puts(msg.c_str()); \ + fflush(stdout); \ + } + #endif + #define ASSERT(x) do { if (!(x)) { throw cAssertFailure();} } while (0) + #define testassert(x) do { if (!(x)) { REPORT_ERROR("Test failure: %s, file %s, line %d\n", #x, __FILE__, __LINE__); exit(1); } } while (0) + #define CheckAsserts(x) do { try {x} catch (cAssertFailure) { break; } REPORT_ERROR("Test failure: assert didn't fire for %s, file %s, line %d\n", #x, __FILE__, __LINE__); exit(1); } while (0) + +#else + #ifdef _DEBUG + #define ASSERT( x) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__), assert(0), 0)) + #else + #define ASSERT(x) ((void)(x)) + #endif +#endif + +// Pretty much the same as ASSERT() but stays in Release builds +#define VERIFY( x) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__), exit(1), 0)) + +// Same as assert but in all Self test builds +#ifdef SELF_TEST + #define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0)) +#endif + +// Allow both Older versions of MSVC and newer versions of everything use a shared_ptr: +// Note that we cannot typedef, because C++ doesn't allow (partial) templates to be typedeffed. +#if (defined(_MSC_VER) && (_MSC_VER < 1600)) + // MSVC before 2010 doesn't have std::shared_ptr, but has std::tr1::shared_ptr, defined in <memory> included earlier + #define SharedPtr std::tr1::shared_ptr +#elif (defined(_MSC_VER) || (__cplusplus >= 201103L)) + // C++11 has std::shared_ptr in <memory>, included earlier + #define SharedPtr std::shared_ptr +#else + // C++03 has std::tr1::shared_ptr in <tr1/memory> + #include <tr1/memory> + #define SharedPtr std::tr1::shared_ptr +#endif + + + + + +/** A generic interface used mainly in ForEach() functions */ +template <typename Type> class cItemCallback +{ +public: + virtual ~cItemCallback() {} + + /** Called for each item in the internal list; return true to stop the loop, or false to continue enumerating */ + virtual bool Item(Type * a_Type) = 0; +} ; + + + + +/** Clamp X to the specified range. */ +template <typename T> +T Clamp(T a_Value, T a_Min, T a_Max) +{ + return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); +} + + + + + +#ifndef TOLUA_TEMPLATE_BIND + #define TOLUA_TEMPLATE_BIND(x) +#endif + + + + + +// Common headers (part 2, with macros): +#include "ChunkDef.h" +#include "BiomeDef.h" +#include "BlockID.h" +#include "BlockInfo.h" + + + + + diff --git a/Tools/QtBiomeVisualiser/MainWindow.cpp b/Tools/QtBiomeVisualiser/MainWindow.cpp new file mode 100644 index 000000000..8b98c0b0e --- /dev/null +++ b/Tools/QtBiomeVisualiser/MainWindow.cpp @@ -0,0 +1,104 @@ +#include "Globals.h" +#include "MainWindow.h" +#include <QVBoxLayout> +#include <QAction> +#include <QMenuBar> +#include <QApplication> +#include <QFileDialog> +#include "inifile/iniFile.h" +#include "ChunkSource.h" +#include "Generating/BioGen.h" + + + + + +MainWindow::MainWindow(QWidget * parent) : + QMainWindow(parent) +{ + createActions(); + createMenus(); + + m_BiomeView = new BiomeView(this); + setCentralWidget(m_BiomeView); +} + + + + + +MainWindow::~MainWindow() +{ + +} + + + + + +void MainWindow::generate() +{ + QString worldIni = QFileDialog::getOpenFileName(this, tr("Open world.ini"), QString(), tr("world.ini (world.ini)")); + cIniFile ini; + if (!ini.ReadFile(worldIni.toStdString())) + { + return; + } + int seed = ini.GetValueSetI("Seed", "Seed", 0); + bool unused = false; + cBiomeGen * biomeGen = cBiomeGen::CreateBiomeGen(ini, seed, unused); + if (biomeGen == nullptr) + { + return; + } + m_BiomeView->setChunkSource(std::shared_ptr<BioGenSource>(new BioGenSource(biomeGen))); + m_BiomeView->redraw(); +} + + + + + +void MainWindow::open() +{ + // TODO +} + + + + + +void MainWindow::createActions() +{ + m_actGen = new QAction(tr("&Generate..."), this); + m_actGen->setShortcut(tr("Ctrl+N")); + m_actGen->setStatusTip(tr("Open a generator INI file and display the generated biomes")); + connect(m_actGen, SIGNAL(triggered()), this, SLOT(generate())); + + m_actOpen = new QAction(tr("&Open world..."), this); + m_actOpen->setShortcut(tr("Ctrl+O")); + m_actOpen->setStatusTip(tr("Open an existing world and display its biomes")); + connect(m_actOpen, SIGNAL(triggered()), this, SLOT(open())); + + m_actExit = new QAction(tr("E&xit"), this); + m_actExit->setShortcut(tr("Alt+X")); + m_actExit->setStatusTip(tr("Exit %1").arg(QApplication::instance()->applicationName())); + connect(m_actExit, SIGNAL(triggered()), this, SLOT(close())); +} + + + + + +void MainWindow::createMenus() +{ + QMenu * mFile = menuBar()->addMenu(tr("&World")); + mFile->addAction(m_actGen); + mFile->addAction(m_actOpen); + mFile->addSeparator(); + mFile->addAction(m_actExit); +} + + + + diff --git a/Tools/QtBiomeVisualiser/MainWindow.h b/Tools/QtBiomeVisualiser/MainWindow.h new file mode 100644 index 000000000..f6028aff1 --- /dev/null +++ b/Tools/QtBiomeVisualiser/MainWindow.h @@ -0,0 +1,46 @@ +#pragma once + +#include <QMainWindow> +#include "BiomeView.h" + + + + + +class MainWindow : + public QMainWindow +{ + Q_OBJECT + + BiomeView * m_BiomeView; + +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + +private slots: + /** Opens a generator definition and generates the biomes based on that. */ + void generate(); + + /** Opens an existing world and displays the loaded biomes. */ + void open(); + +protected: + // Actions: + QAction * m_actGen; + QAction * m_actOpen; + QAction * m_actExit; + + + /** Creates the actions that the UI supports. */ + void createActions(); + + /** Creates the menu bar and connects its events. */ + void createMenus(); +}; + + + + + + diff --git a/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro new file mode 100644 index 000000000..0329d5607 --- /dev/null +++ b/Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro @@ -0,0 +1,62 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2014-09-11T15:22:43 +# +#------------------------------------------------- + +QT += core gui + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = QtBiomeVisualiser +TEMPLATE = app + + +SOURCES += main.cpp\ + 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 \ + Chunk.cpp \ + ChunkSource.cpp \ + ChunkLoader.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 \ + Chunk.h \ + ChunkSource.h \ + ChunkLoader.h + +INCLUDEPATH += $$_PRO_FILE_PWD_ \ + $$_PRO_FILE_PWD_/../../src \ + $$_PRO_FILE_PWD_/../../lib + + +CONFIG += STATIC + + + + diff --git a/Tools/QtBiomeVisualiser/main.cpp b/Tools/QtBiomeVisualiser/main.cpp new file mode 100644 index 000000000..f41cdcfb2 --- /dev/null +++ b/Tools/QtBiomeVisualiser/main.cpp @@ -0,0 +1,20 @@ +#include "Globals.h" +#include "MainWindow.h" +#include <QApplication> + + + + + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + MainWindow w; + w.show(); + + return a.exec(); +} + + + + |