summaryrefslogtreecommitdiffstats
path: root/Tools/QtBiomeVisualiser
diff options
context:
space:
mode:
authorHowaner <franzi.moos@googlemail.com>2014-09-14 14:16:17 +0200
committerHowaner <franzi.moos@googlemail.com>2014-09-14 14:16:17 +0200
commit92e7e5c615efb591210ffd978afa012027f2c84f (patch)
tree1271b2281ab938f59f3f2d933a6c873fd3f633ba /Tools/QtBiomeVisualiser
parent1.8: Updated scoreboard packets. (diff)
parentAdded new Qt-based biome visualiser. (diff)
downloadcuberite-92e7e5c615efb591210ffd978afa012027f2c84f.tar
cuberite-92e7e5c615efb591210ffd978afa012027f2c84f.tar.gz
cuberite-92e7e5c615efb591210ffd978afa012027f2c84f.tar.bz2
cuberite-92e7e5c615efb591210ffd978afa012027f2c84f.tar.lz
cuberite-92e7e5c615efb591210ffd978afa012027f2c84f.tar.xz
cuberite-92e7e5c615efb591210ffd978afa012027f2c84f.tar.zst
cuberite-92e7e5c615efb591210ffd978afa012027f2c84f.zip
Diffstat (limited to 'Tools/QtBiomeVisualiser')
-rw-r--r--Tools/QtBiomeVisualiser/.gitignore2
-rw-r--r--Tools/QtBiomeVisualiser/BiomeView.cpp246
-rw-r--r--Tools/QtBiomeVisualiser/BiomeView.h65
-rw-r--r--Tools/QtBiomeVisualiser/Chunk.cpp36
-rw-r--r--Tools/QtBiomeVisualiser/Chunk.h40
-rw-r--r--Tools/QtBiomeVisualiser/ChunkCache.cpp110
-rw-r--r--Tools/QtBiomeVisualiser/ChunkCache.h68
-rw-r--r--Tools/QtBiomeVisualiser/ChunkLoader.cpp29
-rw-r--r--Tools/QtBiomeVisualiser/ChunkLoader.h43
-rw-r--r--Tools/QtBiomeVisualiser/ChunkSource.cpp164
-rw-r--r--Tools/QtBiomeVisualiser/ChunkSource.h60
-rw-r--r--Tools/QtBiomeVisualiser/Globals.h386
-rw-r--r--Tools/QtBiomeVisualiser/MainWindow.cpp104
-rw-r--r--Tools/QtBiomeVisualiser/MainWindow.h46
-rw-r--r--Tools/QtBiomeVisualiser/QtBiomeVisualiser.pro62
-rw-r--r--Tools/QtBiomeVisualiser/main.cpp20
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();
+}
+
+
+
+