Skip to content

Commit

Permalink
Added support for specifying a tile drawing offset
Browse files Browse the repository at this point in the history
Each tileset can now define an offset that is applied when drawing its
tiles. This is useful when combining tilesets with a different baseline,
or lining up the base of the tiles with the tile grid.

This new tileset property is used to improve the tile positions in the
'perspective walls' and 'isometric grass and water' examples.

This closes mapeditor#16

Sponsored-by: Clint Bellanger
  • Loading branch information
bjorn committed Oct 28, 2011
1 parent 058c752 commit 694257b
Show file tree
Hide file tree
Showing 16 changed files with 392 additions and 186 deletions.
1 change: 1 addition & 0 deletions examples/isometric_grass_and_water.tmx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<!DOCTYPE map SYSTEM "http://mapeditor.org/dtd/1.0/map.dtd">
<map version="1.0" orientation="isometric" width="25" height="25" tilewidth="64" tileheight="32">
<tileset firstgid="1" name="isometric_grass_and_water" tilewidth="64" tileheight="64">
<tileoffset x="0" y="16"/>
<image source="isometric_grass_and_water.png" width="256" height="384"/>
</tileset>
<layer name="Tile Layer 1" width="25" height="25">
Expand Down
2 changes: 1 addition & 1 deletion examples/perspective_walls.tmx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" width="32" height="32" tilewidth="32" tileheight="32">
<map version="1.0" orientation="orthogonal" width="32" height="32" tilewidth="31" tileheight="31">
<tileset firstgid="1" source="perspective_walls.tsx"/>
<layer name="Walls" width="32" height="32">
<data encoding="base64" compression="zlib">
Expand Down
1 change: 1 addition & 0 deletions examples/perspective_walls.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<tileset name="perspective_walls" tilewidth="64" tileheight="64">
<tileoffset x="-32" y="0"/>
<image source="perspective_walls.png"/>
<tile id="13">
<properties>
Expand Down
18 changes: 12 additions & 6 deletions src/libtiled/isometricrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "mapobject.h"
#include "tile.h"
#include "tilelayer.h"
#include "tileset.h"

#include <cmath>

Expand Down Expand Up @@ -164,10 +165,14 @@ void IsometricRenderer::drawTileLayer(QPainter *painter,
if (rect.isNull())
rect = boundingRect(layer->bounds());

const QSize maxTileSize = layer->maxTileSize();
const int extraWidth = maxTileSize.width() - tileWidth;
const int extraHeight = maxTileSize.height() - tileHeight;
rect.adjust(-extraWidth, 0, 0, extraHeight);
QMargins drawMargins = layer->drawMargins();
drawMargins.setTop(drawMargins.top() - tileHeight);
drawMargins.setRight(drawMargins.right() - tileWidth);

rect.adjust(-drawMargins.right(),
-drawMargins.bottom(),
drawMargins.left(),
drawMargins.top());

// Determine the tile and pixel coordinates to start at
QPointF tilePos = pixelToTileCoords(rect.x(), rect.y());
Expand Down Expand Up @@ -214,13 +219,14 @@ void IsometricRenderer::drawTileLayer(QPainter *painter,
const Cell &cell = layer->cellAt(columnItr);
if (!cell.isEmpty()) {
const QPixmap &img = cell.tile->image();
const QPoint offset = cell.tile->tileset()->tileOffset();

qreal m11 = 1; // Horizontal scaling factor
qreal m12 = 0; // Vertical shearing factor
qreal m21 = 0; // Horizontal shearing factor
qreal m22 = 1; // Vertical scaling factor
qreal dx = x;
qreal dy = y - img.height();
qreal dx = offset.x() + x;
qreal dy = offset.y() + y - img.height();

if (cell.flippedDiagonally) {
// Use shearing to swap the X/Y axis
Expand Down
30 changes: 21 additions & 9 deletions src/libtiled/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ Map::Map(Orientation orientation,
mWidth(width),
mHeight(height),
mTileWidth(tileWidth),
mTileHeight(tileHeight),
mMaxTileSize(tileWidth, tileHeight)
mTileHeight(tileHeight)
{
}

Expand All @@ -53,12 +52,25 @@ Map::~Map()
qDeleteAll(mLayers);
}

void Map::adjustMaxTileSize(const QSize &size)
static QMargins maxMargins(const QMargins &a,
const QMargins &b)
{
if (size.width() > mMaxTileSize.width())
mMaxTileSize.setWidth(size.width());
if (size.height() > mMaxTileSize.height())
mMaxTileSize.setHeight(size.height());
return QMargins(qMax(a.left(), b.left()),
qMax(a.top(), b.top()),
qMax(a.right(), b.right()),
qMax(a.bottom(), b.bottom()));
}

void Map::adjustDrawMargins(const QMargins &margins)
{
// The TileLayer includes the maximum tile size in its draw margins. So
// we need to subtract the tile size of the map, since that part does not
// contribute to additional margin.
mDrawMargins = maxMargins(QMargins(margins.left(),
margins.top() - mTileHeight,
margins.right() - mTileWidth,
margins.bottom()),
mDrawMargins);
}

int Map::tileLayerCount() const
Expand Down Expand Up @@ -105,7 +117,7 @@ void Map::adoptLayer(Layer *layer)
layer->setMap(this);

if (TileLayer *tileLayer = dynamic_cast<TileLayer*>(layer))
adjustMaxTileSize(tileLayer->maxTileSize());
adjustDrawMargins(tileLayer->drawMargins());
}

Layer *Map::takeLayerAt(int index)
Expand Down Expand Up @@ -158,7 +170,7 @@ bool Map::isTilesetUsed(Tileset *tileset) const
Map *Map::clone() const
{
Map *o = new Map(mOrientation, mWidth, mHeight, mTileWidth, mTileHeight);
o->mMaxTileSize = mMaxTileSize;
o->mDrawMargins = mDrawMargins;
foreach (const Layer *layer, mLayers)
o->addLayer(layer->clone());
o->mTilesets = mTilesets;
Expand Down
25 changes: 9 additions & 16 deletions src/libtiled/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "object.h"

#include <QList>
#include <QMargins>
#include <QSize>

namespace Tiled {
Expand Down Expand Up @@ -128,26 +129,18 @@ class TILEDSHARED_EXPORT Map : public Object
int tileHeight() const { return mTileHeight; }

/**
* Returns the maximum tile size used by tile layers of this map.
* @see TileLayer::extraTileSize()
* Adjusts the draw margins to be at least as big as the given margins.
* Called from tile layers when their tiles change.
*/
QSize maxTileSize() const { return mMaxTileSize; }
void adjustDrawMargins(const QMargins &margins);

/**
* Adjusts the maximum tile size to be at least as much as the given
* size. Called from tile layers when their maximum tile size increases.
*/
void adjustMaxTileSize(const QSize &size);

/**
* Convenience method for getting the extra tile size, which is the number
* of pixels that tiles may extend beyond the size of the tile grid.
* Returns the margins that have to be taken into account when figuring
* out which part of the map to repaint after changing some tiles.
*
* @see maxTileSize()
* @see TileLayer::drawMargins
*/
QSize extraTileSize() const
{ return QSize(mMaxTileSize.width() - mTileWidth,
mMaxTileSize.height() - mTileHeight); }
QMargins drawMargins() const { return mDrawMargins; }

/**
* Returns the number of layers of this map.
Expand Down Expand Up @@ -259,7 +252,7 @@ class TILEDSHARED_EXPORT Map : public Object
int mHeight;
int mTileWidth;
int mTileHeight;
QSize mMaxTileSize;
QMargins mDrawMargins;
QList<Layer*> mLayers;
QList<Tileset*> mTilesets;
};
Expand Down
15 changes: 11 additions & 4 deletions src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,14 +277,21 @@ Tileset *MapReaderPrivate::readTileset()
tileSpacing, margin);

while (xml.readNextStartElement()) {
if (xml.name() == "tile")
if (xml.name() == "tile") {
readTilesetTile(tileset);
else if (xml.name() == "properties")
} else if (xml.name() == "tileoffset") {
const QXmlStreamAttributes oa = xml.attributes();
int x = oa.value(QLatin1String("x")).toString().toInt();
int y = oa.value(QLatin1String("y")).toString().toInt();
tileset->setTileOffset(QPoint(x, y));
xml.skipCurrentElement();
} else if (xml.name() == "properties") {
tileset->mergeProperties(readProperties());
else if (xml.name() == "image")
} else if (xml.name() == "image") {
readTilesetImage(tileset);
else
} else {
readUnknownElement();
}
}
}
} else { // External tileset
Expand Down
8 changes: 8 additions & 0 deletions src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,14 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset *tileset,
if (margin != 0)
w.writeAttribute(QLatin1String("margin"), QString::number(margin));

const QPoint offset = tileset->tileOffset();
if (!offset.isNull()) {
w.writeStartElement(QLatin1String("tileoffset"));
w.writeAttribute(QLatin1String("x"), QString::number(offset.x()));
w.writeAttribute(QLatin1String("y"), QString::number(offset.y()));
w.writeEndElement();
}

// Write the tileset properties
writeProperties(w, tileset->properties());

Expand Down
19 changes: 13 additions & 6 deletions src/libtiled/orthogonalrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "mapobject.h"
#include "tile.h"
#include "tilelayer.h"
#include "tileset.h"

#include <cmath>

Expand Down Expand Up @@ -192,10 +193,15 @@ void OrthogonalRenderer::drawTileLayer(QPainter *painter,
int endY = layer->height();

if (!exposed.isNull()) {
const QSize maxTileSize = layer->maxTileSize();
const int extraWidth = maxTileSize.width() - tileWidth;
const int extraHeight = maxTileSize.height() - tileHeight;
QRectF rect = exposed.adjusted(-extraWidth, 0, 0, extraHeight);
QMargins drawMargins = layer->drawMargins();
drawMargins.setTop(drawMargins.top() - tileHeight);
drawMargins.setRight(drawMargins.right() - tileWidth);

QRectF rect = exposed.adjusted(-drawMargins.right(),
-drawMargins.bottom(),
drawMargins.left(),
drawMargins.top());

rect.translate(-layerPos);

startX = qMax((int) rect.x() / tileWidth, 0);
Expand All @@ -213,13 +219,14 @@ void OrthogonalRenderer::drawTileLayer(QPainter *painter,
continue;

const QPixmap &img = cell.tile->image();
const QPoint offset = cell.tile->tileset()->tileOffset();

qreal m11 = 1; // Horizontal scaling factor
qreal m12 = 0; // Vertical shearing factor
qreal m21 = 0; // Horizontal shearing factor
qreal m22 = 1; // Vertical scaling factor
qreal dx = x * tileWidth;
qreal dy = (y + 1) * tileHeight - img.height();
qreal dx = offset.x() + x * tileWidth;
qreal dy = offset.y() + (y + 1) * tileHeight - img.height();

if (cell.flippedDiagonally) {
// Use shearing to swap the X/Y axis
Expand Down
38 changes: 28 additions & 10 deletions src/libtiled/tilelayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ QRegion TileLayer::region() const
return region;
}

static QSize maxSize(const QSize &a,
const QSize &b)
{
return QSize(qMax(a.width(), b.width()),
qMax(a.height(), b.height()));
}

static QMargins maxMargins(const QMargins &a,
const QMargins &b)
{
return QMargins(qMax(a.left(), b.left()),
qMax(a.top(), b.top()),
qMax(a.right(), b.right()),
qMax(a.bottom(), b.bottom()));
}

void TileLayer::setCell(int x, int y, const Cell &cell)
{
Q_ASSERT(contains(x, y));
Expand All @@ -78,16 +94,17 @@ void TileLayer::setCell(int x, int y, const Cell &cell)
if (cell.flippedDiagonally)
std::swap(width, height);

if (width > mMaxTileSize.width()) {
mMaxTileSize.setWidth(width);
if (mMap)
mMap->adjustMaxTileSize(mMaxTileSize);
}
if (height > mMaxTileSize.height()) {
mMaxTileSize.setHeight(height);
if (mMap)
mMap->adjustMaxTileSize(mMaxTileSize);
}
const QPoint offset = cell.tile->tileset()->tileOffset();

mMaxTileSize = maxSize(QSize(width, height), mMaxTileSize);
mOffsetMargins = maxMargins(QMargins(-offset.x(),
-offset.y(),
offset.x(),
offset.y()),
mOffsetMargins);

if (mMap)
mMap->adjustDrawMargins(drawMargins());
}

mGrid[x + y * mWidth] = cell;
Expand Down Expand Up @@ -403,5 +420,6 @@ TileLayer *TileLayer::initializeClone(TileLayer *clone) const
Layer::initializeClone(clone);
clone->mGrid = mGrid;
clone->mMaxTileSize = mMaxTileSize;
clone->mOffsetMargins = mOffsetMargins;
return clone;
}
19 changes: 17 additions & 2 deletions src/libtiled/tilelayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include "layer.h"

#include <QMargins>
#include <QString>
#include <QVector>

Expand Down Expand Up @@ -113,11 +114,24 @@ class TILEDSHARED_EXPORT TileLayer : public Layer
TileLayer(const QString &name, int x, int y, int width, int height);

/**
* Returns the maximum tile size of this layer. Used by the layer
* rendering code to determine the area that needs to be redrawn.
* Returns the maximum tile size of this layer.
*/
QSize maxTileSize() const { return mMaxTileSize; }

/**
* Returns the margins that have to be taken into account while drawing
* this tile layer. The margins depend on the maximum tile size and the
* offset applied to the tiles.
*/
QMargins drawMargins() const
{
return QMargins(mOffsetMargins.left(),
mOffsetMargins.top() + mMaxTileSize.height(),
mOffsetMargins.right() + mMaxTileSize.width(),
mOffsetMargins.bottom());
}


/**
* Returns whether (x, y) is inside this map layer.
*/
Expand Down Expand Up @@ -256,6 +270,7 @@ class TILEDSHARED_EXPORT TileLayer : public Layer

private:
QSize mMaxTileSize;
QMargins mOffsetMargins;
QVector<Cell> mGrid;
};

Expand Down
13 changes: 13 additions & 0 deletions src/libtiled/tileset.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

#include <QColor>
#include <QList>
#include <QPoint>
#include <QString>

class QImage;
Expand Down Expand Up @@ -126,6 +127,17 @@ class TILEDSHARED_EXPORT Tileset : public Object
*/
int margin() const { return mMargin; }

/**
* Returns the offset that is applied when drawing the tiles in this
* tileset.
*/
QPoint tileOffset() const { return mTileOffset; }

/**
* @see tileOffset
*/
void setTileOffset(QPoint offset) { mTileOffset = offset; }

/**
* Returns the tile for the given tile ID.
* The tile ID is local to this tileset, which means the IDs are in range
Expand Down Expand Up @@ -210,6 +222,7 @@ class TILEDSHARED_EXPORT Tileset : public Object
int mTileHeight;
int mTileSpacing;
int mMargin;
QPoint mTileOffset;
int mImageWidth;
int mImageHeight;
int mColumnCount;
Expand Down
Loading

0 comments on commit 694257b

Please sign in to comment.