Skip to content

Commit

Permalink
Allow setting up the orientation and grid size of a tileset
Browse files Browse the repository at this point in the history
This orientation (currently just isometric or orthogonal) allows the
collision editor and terrain overlays to take into account isometric
tiles and transform the rendering and mouse input accordingly.

Useful for when the collision shape should lie "on the ground" and to
better see which corner is meant regarding the terrain information.

Closes #757 - Collision Objects are not projected while in Isometric Mode
Closes #419 - isometric terrain overlays
  • Loading branch information
bjorn committed Jan 9, 2017
1 parent dfd7834 commit 68997ee
Show file tree
Hide file tree
Showing 15 changed files with 381 additions and 62 deletions.
7 changes: 4 additions & 3 deletions examples/isometric_grass_and_water.tmx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="isometric" renderorder="right-down" width="25" height="25" tilewidth="64" tileheight="32">
<tileset firstgid="1" name="isometric_grass_and_water" tilewidth="64" tileheight="64">
<map version="1.0" orientation="isometric" renderorder="right-down" width="25" height="25" tilewidth="64" tileheight="32" nextobjectid="1">
<tileset firstgid="1" name="isometric_grass_and_water" tilewidth="64" tileheight="64" tilecount="24" columns="4">
<tileoffset x="0" y="16"/>
<grid orientation="isometric" width="64" height="32"/>
<image source="isometric_grass_and_water.png" width="256" height="384"/>
<terraintypes>
<terrain name="Grass" tile="0"/>
Expand Down Expand Up @@ -37,4 +38,4 @@
eJx1lttywjAMROVgyqVtAoFC/v9L68xoh5PFPGhIYktrrVYyS0QszZ7Nvpvd0n7y24L1Q7MhrTSreN/le821HZ7lv9qYa6sdE0cYs/kX7PXYwtfaevYp7WDrd+SnHByjYr/npP1zZ4/elcuM71rjeckdc5KNHX75fMwc9s2uzb6AsYstJzwrv5/Tz89SLIZy8v203llV8xl7yMU+462/v81OqA114/UhrzUxRqwprnh6ZGzp2PNQfPqRu/X9hnMV8F/xLg1L42erDf2oaa2RI2qPtbgbhmw2H69nMUxx/gVccXdC3AW/o/HV60vW59Lhu8arDxmfGIPFUV1qbLVQEIs4PlOeHQxqVjmzr5mLYsmf+5Qj5yM1r3Ne4p1D5VcMh3qWZibLx2fYkBhPYOv81I9wbrGd45zFU7zrndpwDjkHXXfej9zHc3EG+D3AWcCZMJif7hTnVxr6i9edtoBDz8N7kxqbY6sN9gJnsnqIOqCme7Un76579sIV8dccHvHqZefH76BP9wjzkVapM2rL+5/8cR6QS9eh8p2AT12y5oO9+7yh5hzLZypnHX29/pzB9PE7bOg8Mza5KvGu4R7mp/89zqvr7x+TnxEn
</data>
</layer>
</map>
</map>
22 changes: 22 additions & 0 deletions src/libtiled/mapreader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class MapReaderPrivate

SharedTileset readTileset();
void readTilesetTile(Tileset &tileset);
void readTilesetGrid(Tileset &tileset);
void readTilesetImage(Tileset &tileset);
void readTilesetTerrainTypes(Tileset &tileset);
ImageReference readImage();
Expand Down Expand Up @@ -333,6 +334,8 @@ SharedTileset MapReaderPrivate::readTileset()
int y = oa.value(QLatin1String("y")).toInt();
tileset->setTileOffset(QPoint(x, y));
xml.skipCurrentElement();
} else if (xml.name() == QLatin1String("grid")) {
readTilesetGrid(*tileset);
} else if (xml.name() == QLatin1String("properties")) {
tileset->mergeProperties(readProperties());
} else if (xml.name() == QLatin1String("image")) {
Expand Down Expand Up @@ -448,6 +451,25 @@ void MapReaderPrivate::readTilesetTile(Tileset &tileset)
}
}

void MapReaderPrivate::readTilesetGrid(Tileset &tileset)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("grid"));

const QXmlStreamAttributes atts = xml.attributes();

const QString orientation = atts.value(QLatin1String("orientation")).toString();
const int width = atts.value(QLatin1String("width")).toInt();
const int height = atts.value(QLatin1String("height")).toInt();

tileset.setOrientation(Tileset::orientationFromString(orientation));

const QSize gridSize(width, height);
if (!gridSize.isEmpty())
tileset.setGridSize(gridSize);

xml.skipCurrentElement();
}

void MapReaderPrivate::readTilesetImage(Tileset &tileset)
{
Q_ASSERT(xml.isStartElement() && xml.name() == QLatin1String("image"));
Expand Down
54 changes: 31 additions & 23 deletions src/libtiled/maptovariantconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ QVariant MapToVariantConverter::toVariant(const Map *map, const QDir &mapDir)

unsigned firstGid = 1;
for (const SharedTileset &tileset : map->tilesets()) {
tilesetVariants << toVariant(tileset.data(), firstGid);
tilesetVariants << toVariant(*tileset, firstGid);
mGidMapper.insert(firstGid, tileset.data());
firstGid += tileset->nextTileId();
}
Expand Down Expand Up @@ -106,18 +106,18 @@ QVariant MapToVariantConverter::toVariant(const Tileset &tileset,
const QDir &directory)
{
mMapDir = directory;
return toVariant(&tileset, 0);
return toVariant(tileset, 0);
}

QVariant MapToVariantConverter::toVariant(const Tileset *tileset,
QVariant MapToVariantConverter::toVariant(const Tileset &tileset,
int firstGid) const
{
QVariantMap tilesetVariant;

if (firstGid > 0)
tilesetVariant[QLatin1String("firstgid")] = firstGid;

const QString &fileName = tileset->fileName();
const QString &fileName = tileset.fileName();
if (!fileName.isEmpty()) {
QString source = mMapDir.relativeFilePath(fileName);
tilesetVariant[QLatin1String("source")] = source;
Expand All @@ -130,49 +130,57 @@ QVariant MapToVariantConverter::toVariant(const Tileset *tileset,
if (firstGid == 0)
tilesetVariant[QLatin1String("type")] = QLatin1String("tileset");

tilesetVariant[QLatin1String("name")] = tileset->name();
tilesetVariant[QLatin1String("tilewidth")] = tileset->tileWidth();
tilesetVariant[QLatin1String("tileheight")] = tileset->tileHeight();
tilesetVariant[QLatin1String("spacing")] = tileset->tileSpacing();
tilesetVariant[QLatin1String("margin")] = tileset->margin();
tilesetVariant[QLatin1String("tilecount")] = tileset->tileCount();
tilesetVariant[QLatin1String("columns")] = tileset->columnCount();
tilesetVariant[QLatin1String("name")] = tileset.name();
tilesetVariant[QLatin1String("tilewidth")] = tileset.tileWidth();
tilesetVariant[QLatin1String("tileheight")] = tileset.tileHeight();
tilesetVariant[QLatin1String("spacing")] = tileset.tileSpacing();
tilesetVariant[QLatin1String("margin")] = tileset.margin();
tilesetVariant[QLatin1String("tilecount")] = tileset.tileCount();
tilesetVariant[QLatin1String("columns")] = tileset.columnCount();

const QColor bgColor = tileset->backgroundColor();
const QColor bgColor = tileset.backgroundColor();
if (bgColor.isValid())
tilesetVariant[QLatin1String("backgroundcolor")] = colorToString(bgColor);

addProperties(tilesetVariant, tileset->properties());
addProperties(tilesetVariant, tileset.properties());

const QPoint offset = tileset->tileOffset();
const QPoint offset = tileset.tileOffset();
if (!offset.isNull()) {
QVariantMap tileOffset;
tileOffset[QLatin1String("x")] = offset.x();
tileOffset[QLatin1String("y")] = offset.y();
tilesetVariant[QLatin1String("tileoffset")] = tileOffset;
}

if (tileset.orientation() != Tileset::Orthogonal || tileset.gridSize() != tileset.tileSize()) {
QVariantMap grid;
grid[QLatin1String("orientation")] = Tileset::orientationToString(tileset.orientation());
grid[QLatin1String("width")] = tileset.gridSize().width();
grid[QLatin1String("height")] = tileset.gridSize().height();
tilesetVariant[QLatin1String("grid")] = grid;
}

// Write the image element
const QString &imageSource = tileset->imageSource();
const QString &imageSource = tileset.imageSource();
if (!imageSource.isEmpty()) {
const QString rel = mMapDir.relativeFilePath(tileset->imageSource());
const QString rel = mMapDir.relativeFilePath(tileset.imageSource());

tilesetVariant[QLatin1String("image")] = rel;

const QColor transColor = tileset->transparentColor();
const QColor transColor = tileset.transparentColor();
if (transColor.isValid())
tilesetVariant[QLatin1String("transparentcolor")] = transColor.name();

tilesetVariant[QLatin1String("imagewidth")] = tileset->imageWidth();
tilesetVariant[QLatin1String("imageheight")] = tileset->imageHeight();
tilesetVariant[QLatin1String("imagewidth")] = tileset.imageWidth();
tilesetVariant[QLatin1String("imageheight")] = tileset.imageHeight();
}

// Write the properties, terrain, external image, object group and
// animation for those tiles that have them.
QVariantMap tilePropertiesVariant;
QVariantMap tilePropertyTypesVariant;
QVariantMap tilesVariant;
for (const Tile *tile : tileset->tiles()) {
for (const Tile *tile : tileset.tiles()) {
const Properties properties = tile->properties();
if (!properties.isEmpty()) {
tilePropertiesVariant[QString::number(tile->id())] = toVariant(properties);
Expand Down Expand Up @@ -215,10 +223,10 @@ QVariant MapToVariantConverter::toVariant(const Tileset *tileset,
tilesetVariant[QLatin1String("tiles")] = tilesVariant;

// Write terrains
if (tileset->terrainCount() > 0) {
if (tileset.terrainCount() > 0) {
QVariantList terrainsVariant;
for (int i = 0; i < tileset->terrainCount(); ++i) {
Terrain *terrain = tileset->terrain(i);
for (int i = 0; i < tileset.terrainCount(); ++i) {
Terrain *terrain = tileset.terrain(i);
const Properties &properties = terrain->properties();
QVariantMap terrainVariant;
terrainVariant[QLatin1String("name")] = terrain->name();
Expand Down
2 changes: 1 addition & 1 deletion src/libtiled/maptovariantconverter.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class TILEDSHARED_EXPORT MapToVariantConverter
QVariant toVariant(const Tileset &tileset, const QDir &directory);

private:
QVariant toVariant(const Tileset *tileset, int firstGid) const;
QVariant toVariant(const Tileset &tileset, int firstGid) const;
QVariant toVariant(const Properties &properties) const;
QVariant propertyTypesToVariant(const Properties &properties) const;
QVariant toVariant(const TileLayer *tileLayer,
Expand Down
8 changes: 8 additions & 0 deletions src/libtiled/mapwriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ void MapWriterPrivate::writeTileset(QXmlStreamWriter &w, const Tileset &tileset,
w.writeEndElement();
}

if (tileset.orientation() != Tileset::Orthogonal || tileset.gridSize() != tileset.tileSize()) {
w.writeStartElement(QLatin1String("grid"));
w.writeAttribute(QLatin1String("orientation"), Tileset::orientationToString(tileset.orientation()));
w.writeAttribute(QLatin1String("width"), QString::number(tileset.gridSize().width()));
w.writeAttribute(QLatin1String("height"), QString::number(tileset.gridSize().height()));
w.writeEndElement();
}

// Write the tileset properties
writeProperties(w, tileset.properties());

Expand Down
28 changes: 28 additions & 0 deletions src/libtiled/tileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Tileset::Tileset(QString name, int tileWidth, int tileHeight,
mTileHeight(tileHeight),
mTileSpacing(tileSpacing),
mMargin(margin),
mOrientation(Orthogonal),
mGridSize(tileWidth, tileHeight),
mColumnCount(0),
mExpectedColumnCount(0),
mExpectedRowCount(0),
Expand Down Expand Up @@ -591,6 +593,8 @@ void Tileset::swap(Tileset &other)
std::swap(mTileSpacing, other.mTileSpacing);
std::swap(mMargin, other.mMargin);
std::swap(mTileOffset, other.mTileOffset);
std::swap(mOrientation, other.mOrientation);
std::swap(mGridSize, other.mGridSize);
std::swap(mColumnCount, other.mColumnCount);
std::swap(mExpectedColumnCount, other.mExpectedColumnCount);
std::swap(mExpectedRowCount, other.mExpectedRowCount);
Expand Down Expand Up @@ -622,6 +626,8 @@ SharedTileset Tileset::clone() const
// mFileName stays empty
c->mImageReference = mImageReference;
c->mTileOffset = mTileOffset;
c->mOrientation = mOrientation;
c->mGridSize = mGridSize;
c->mColumnCount = mColumnCount;
c->mExpectedColumnCount = mExpectedColumnCount;
c->mExpectedRowCount = mExpectedRowCount;
Expand Down Expand Up @@ -664,3 +670,25 @@ void Tileset::updateTileSize()
mTileWidth = maxWidth;
mTileHeight = maxHeight;
}


QString Tileset::orientationToString(Tileset::Orientation orientation)
{
switch (orientation) {
default:
case Tileset::Orthogonal:
return QLatin1String("orthogonal");
break;
case Tileset::Isometric:
return QLatin1String("isometric");
break;
}
}

Tileset::Orientation Tileset::orientationFromString(const QString &string)
{
Orientation orientation = Orthogonal;
if (string == QLatin1String("isometric"))
orientation = Isometric;
return orientation;
}
68 changes: 68 additions & 0 deletions src/libtiled/tileset.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ typedef QSharedPointer<Tileset> SharedTileset;
class TILEDSHARED_EXPORT Tileset : public Object
{
public:
/**
* The orientation of the tileset determines the projection used in the
* TileCollisionEditor and for the terrain information overlay of the
* TilesetView.
*/
enum Orientation {
Orthogonal,
Isometric,
};

/**
* Creates a new tileset with the given parameters. Using this function
* makes sure the internal weak pointer is initialized, which enables the
Expand Down Expand Up @@ -117,6 +127,12 @@ class TILEDSHARED_EXPORT Tileset : public Object
QPoint tileOffset() const;
void setTileOffset(QPoint offset);

Orientation orientation() const;
void setOrientation(Orientation orientation);

QSize gridSize() const;
void setGridSize(QSize gridSize);

const QMap<int, Tile*> &tiles() const;
inline Tile *findTile(int id) const;
Tile *tileAt(int id) const { return findTile(id); } // provided for Python
Expand Down Expand Up @@ -189,6 +205,23 @@ class TILEDSHARED_EXPORT Tileset : public Object

SharedTileset clone() const;

/**
* Helper function that converts the tileset orientation to a string value.
* Useful for map writers.
*
* @return The tileset orientation as a lowercase string.
*/
static QString orientationToString(Orientation);

/**
* Helper function that converts a string to a tileset orientation enumerator.
* Useful for map readers.
*
* @return The tileset orientation matching the given string, or
* Tileset::Orthogonal if the string is unrecognized.
*/
static Orientation orientationFromString(const QString &);

private:
void updateTileSize();
void recalculateTerrainDistances();
Expand All @@ -201,6 +234,8 @@ class TILEDSHARED_EXPORT Tileset : public Object
int mTileSpacing;
int mMargin;
QPoint mTileOffset;
Orientation mOrientation;
QSize mGridSize;
int mColumnCount;
int mExpectedColumnCount;
int mExpectedRowCount;
Expand Down Expand Up @@ -313,6 +348,39 @@ inline void Tileset::setTileOffset(QPoint offset)
mTileOffset = offset;
}

/**
* Returns the orientation of the tiles in this tileset.
*/
inline Tileset::Orientation Tileset::orientation() const
{
return mOrientation;
}

/**
* @see orientation
*/
inline void Tileset::setOrientation(Orientation orientation)
{
mOrientation = orientation;
}

/**
* Returns the grid size that is used when the tileset has Isometric
* orientation.
*/
inline QSize Tileset::gridSize() const
{
return mGridSize;
}

/**
* @see gridSize
*/
inline void Tileset::setGridSize(QSize gridSize)
{
mGridSize = gridSize;
}

/**
* Returns a const reference to the tiles in this tileset.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/libtiled/varianttomapconverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ SharedTileset VariantToMapConverter::toTileset(const QVariant &variant)
const int spacing = variantMap[QLatin1String("spacing")].toInt();
const int margin = variantMap[QLatin1String("margin")].toInt();
const QVariantMap tileOffset = variantMap[QLatin1String("tileoffset")].toMap();
const QVariantMap grid = variantMap[QLatin1String("grid")].toMap();
const int tileOffsetX = tileOffset[QLatin1String("x")].toInt();
const int tileOffsetY = tileOffset[QLatin1String("y")].toInt();
const int columns = tileOffset[QLatin1String("columns")].toInt();
Expand All @@ -206,6 +207,16 @@ SharedTileset VariantToMapConverter::toTileset(const QVariant &variant)
tileset->setTileOffset(QPoint(tileOffsetX, tileOffsetY));
tileset->setColumnCount(columns);

if (!grid.isEmpty()) {
const QString orientation = grid[QLatin1String("orientation")].toString();
const QSize gridSize(grid[QLatin1String("width")].toInt(),
grid[QLatin1String("height")].toInt());

tileset->setOrientation(Tileset::orientationFromString(orientation));
if (!gridSize.isEmpty())
tileset->setGridSize(gridSize);
}

if (!bgColor.isEmpty() && QColor::isValidColor(bgColor))
tileset->setBackgroundColor(QColor(bgColor));

Expand Down
7 changes: 7 additions & 0 deletions src/plugins/lua/luaplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,13 @@ void LuaPlugin::writeTileset(LuaTableWriter &writer, const Tileset *tileset,
writer.writeKeyAndValue("y", offset.y());
writer.writeEndTable();

const QSize gridSize = tileset->gridSize();
writer.writeStartTable("grid");
writer.writeKeyAndValue("orientation", Tileset::orientationToString(tileset->orientation()));
writer.writeKeyAndValue("width", gridSize.width());
writer.writeKeyAndValue("height", gridSize.height());
writer.writeEndTable();

writeProperties(writer, tileset->properties());

writer.writeStartTable("terrains");
Expand Down
Loading

0 comments on commit 68997ee

Please sign in to comment.