diff options
Diffstat (limited to 'Tools/QtBiomeVisualiser/BiomeView.cpp')
-rw-r--r-- | Tools/QtBiomeVisualiser/BiomeView.cpp | 414 |
1 files changed, 414 insertions, 0 deletions
diff --git a/Tools/QtBiomeVisualiser/BiomeView.cpp b/Tools/QtBiomeVisualiser/BiomeView.cpp new file mode 100644 index 000000000..b44b935d7 --- /dev/null +++ b/Tools/QtBiomeVisualiser/BiomeView.cpp @@ -0,0 +1,414 @@ +#include "Globals.h" +#include "BiomeView.h" +#include "QtChunk.h" +#include <QPainter> +#include <QResizeEvent> + + + + + +static const int DELTA_STEP = 120; // The normal per-notch wheel delta + + + + + +BiomeView::BiomeView(QWidget * parent) : + super(parent), + m_X(0), + m_Z(0), + m_Zoom(1), + m_IsMouseDragging(false), + m_MouseWheelDelta(0) +{ + // 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))); + + // Allow mouse and keyboard interaction: + setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); +} + + + + +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::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()) + { + // 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::reload() +{ + if (!hasData()) + { + return; + } + m_Cache.reload(); + + redraw(); +} + + + + + +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::mousePressEvent(QMouseEvent * a_Event) +{ + m_LastX = a_Event->x(); + m_LastY = a_Event->y(); + m_IsMouseDragging = true; +} + + + + + +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: + m_X += (m_LastX - a_Event->x()) / m_Zoom; + m_Z += (m_LastY - a_Event->y()) / m_Zoom; + m_LastX = a_Event->x(); + m_LastY = a_Event->y(); + redraw(); + return; + } + + // 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 chunkX, chunkZ; + int relX = blockX, relY, relZ = blockZ; + cChunkDef::AbsoluteToRelative(relX, relY, relZ, chunkX, chunkZ); + auto chunk = m_Cache.fetch(chunkX, chunkZ); + int biome = (chunk.get() != nullptr) ? chunk->getBiome(relX, relZ) : biInvalidBiome; + emit hoverChanged(blockX, blockZ, biome); +} + + + + + +void BiomeView::mouseReleaseEvent(QMouseEvent *) +{ + m_IsMouseDragging = false; +} + + + + + +void BiomeView::wheelEvent(QWheelEvent * a_Event) +{ + m_MouseWheelDelta += a_Event->delta(); + while (m_MouseWheelDelta >= DELTA_STEP) + { + emit wheelUp(); + m_MouseWheelDelta -= DELTA_STEP; + } + while (m_MouseWheelDelta <= -DELTA_STEP) + { + emit wheelDown(); + m_MouseWheelDelta += DELTA_STEP; + } +} + + + + + +void BiomeView::keyPressEvent(QKeyEvent * a_Event) +{ + switch (a_Event->key()) + { + case Qt::Key_Up: + case Qt::Key_W: + { + m_Z -= 10.0 / m_Zoom; + redraw(); + break; + } + + case Qt::Key_Down: + case Qt::Key_S: + { + m_Z += 10.0 / m_Zoom; + redraw(); + break; + } + + case Qt::Key_Left: + case Qt::Key_A: + { + m_X -= 10.0 / m_Zoom; + redraw(); + break; + } + + case Qt::Key_Right: + case Qt::Key_D: + { + m_X += 10.0 / m_Zoom; + redraw(); + break; + } + + case Qt::Key_PageUp: + case Qt::Key_Q: + { + emit increaseZoom(); + break; + } + + case Qt::Key_PageDown: + case Qt::Key_E: + { + emit decreaseZoom(); + break; + } + } +} + + + + |