Пример #1
0
bool TmxViewer::viewMap(const QString &fileName)
{
    mScene->clear();
    centerOn(0, 0);

    mRenderer.reset();

    MapReader reader;
    mMap.reset(reader.readMap(fileName));
    if (!mMap) {
        qWarning().noquote() << "Error:" << reader.errorString();
        return false;
    }

    switch (mMap->orientation()) {
    case Map::Isometric:
        mRenderer.reset(new IsometricRenderer(mMap.get()));
        break;
    case Map::Staggered:
        mRenderer.reset(new StaggeredRenderer(mMap.get()));
        break;
    case Map::Hexagonal:
        mRenderer.reset(new HexagonalRenderer(mMap.get()));
        break;
    case Map::Orthogonal:
    default:
        mRenderer.reset(new OrthogonalRenderer(mMap.get()));
        break;
    }

    mScene->addItem(new MapItem(mMap.get(), mRenderer.get()));

    return true;
}
Пример #2
0
SharedTileset readTileset(const QString &fileName, QString *error)
{
    // Try the first registered tileset format that claims to support the file
    if (TilesetFormat *format = findSupportingFormat(fileName)) {
        SharedTileset tileset = format->read(fileName);

        if (error) {
            if (!tileset)
                *error = format->errorString();
            else
                *error = QString();
        }

        if (tileset)
            tileset->setFormat(format);

        return tileset;
    }

    // Fall back to default reader (TSX format)
    MapReader reader;
    SharedTileset tileset = reader.readTileset(fileName);

    if (error) {
        if (!tileset)
            *error = reader.errorString();
        else
            *error = QString();
    }

    return tileset;
}
Пример #3
0
int main(int argc, char *argv[])
{
    QGuiApplication a(argc, argv);

    a.setOrganizationDomain(QLatin1String("mapeditor.org"));
    a.setApplicationName(QLatin1String("TerrainGenerator"));
    a.setApplicationVersion(QLatin1String("1.0"));

    CommandLineOptions options;

    if (!parseCommandLineArguments(options)) {
        // Something went wrong, abort.
        return 1;
    }

    if (options.showVersion)
        showVersion();
    if (options.showHelp)
        showHelp();
    if (options.showVersion || options.showHelp)
        return 0;

    if (options.target.isEmpty()) {
        qWarning() << "Error: No target tileset provided";
        showHelp();
        return 1;
    }
    if (options.sources.isEmpty()) {
        qWarning() << "Error: No source tilesets provided";
        showHelp();
        return 1;
    }

    // Check terrain priorities.
    if (options.terrainPriority.isEmpty()) {
        qWarning("Error: No terrain priorities set (option -p).");
        showHelp();
        return 1;
    }

    MapReader reader;
    SharedTileset targetTileset;
    QList<SharedTileset> sources;

    if (!options.overwrite && QFile::exists(options.target)) {
        targetTileset = reader.readTileset(options.target);
        if (!targetTileset) {
            qCritical("Error reading target tileset:\n%s",
                      qUtf8Printable(reader.errorString()));
        }

        // Remove empty tiles from the end of the tileset
        int nextTileId = targetTileset->nextTileId();
        for (int id = nextTileId - 1; id >= 0; --id) {
            if (Tile *tile = targetTileset->findTile(id)) {
                if (isEmpty(tile->image().toImage())) {
                    targetTileset->deleteTile(id);
                    nextTileId = id;
                    continue;
                }
            }

            break;
        }
        targetTileset->setNextTileId(nextTileId);

        // If the target tileset already exists, it is also a source tileset
        sources.append(targetTileset);
    }

    // Read source tilesets.
    for (const QString &sourceFileName : options.sources) {
        SharedTileset source = reader.readTileset(sourceFileName);
        if (!source) {
            qCritical("Error reading source tileset '%s':\n%s",
                      qUtf8Printable(sourceFileName),
                      qUtf8Printable(reader.errorString()));
        }
        sources.append(source);
    }

    // If the target tileset does not exist yet, create it
    if (!targetTileset) {
        QString name = QFileInfo(options.target).completeBaseName();
        const SharedTileset &firstSource = sources.first();
        int tileWidth = firstSource->tileWidth();
        int tileHeight = firstSource->tileHeight();

        targetTileset = Tileset::create(name, tileWidth, tileHeight);
    }

    // Set up a mapping from terrain to tile, for quick lookup
    QMap<TileTerrainNames, Tile*> terrainToTile;
    for (const SharedTileset &tileset : sources)
        for (Tile *tile : tileset->tiles())
            if (tile->terrain() != 0xFFFFFFFF)
                if (!terrainToTile.contains(TileTerrainNames(tile))) // TODO: Optimize
                    terrainToTile.insert(TileTerrainNames(tile), tile);

    // Set up the list of all terrains, mapped by name.
    QMap<QString, Terrain*> terrains;
    for (const SharedTileset &tileset : sources)
        for (Terrain *terrain : tileset->terrains())
            if (!terrains.contains(terrain->name()))
                terrains.insert(terrain->name(), terrain);

    // Check if there is anything to combine.
    if (options.combineList.size() == 0) {
        qWarning() << "No terrain specified to combine (-c option).";
    } else {
        // Dump the combine lists.
        qWarning() << "Terrains to combine:";
        for (const QStringList &combine : options.combineList) {
            if (combine.isEmpty()) {
                qCritical("Empty combine set");
            }
            qWarning() << combine;

            // Make sure every terrain from this set was defined.
            for (const QString &terrainName : combine)
                if (!terrains.contains(terrainName))
                    qCritical("Terrain %s is in combine list, however it wasn't defined by any tileset.",
                              qUtf8Printable(terrainName));
        }
    }

    // Setup terrain priorities.
    TerrainLessThan lessThan;
    int priority = 0;
    for (const QString &terrainName : options.terrainPriority) {
        lessThan.terrainPriority.insert(terrainName, priority);
        ++priority;
    }

    qDebug() << "Terrains found:" << terrains.keys();

    // Check if all terrains from priority list were found and loaded.
    for (const QString &terrainName : lessThan.terrainPriority.keys())
        if (!terrains.contains(terrainName))
            qWarning() << "Terrain" << terrainName << "from priority list not found.";

    // Add terrain names not specified from command line.
    for (const QString &terrainName : terrains.keys()) {
        if (!lessThan.terrainPriority.contains(terrainName)) {
            qWarning() << "No priority set for" << terrainName;
            lessThan.terrainPriority.insert(terrainName, priority);
            ++priority;
        }
    }

    // Add terrains that are not defined in the target tileset yet
    // TODO: This step should be more configurable
    for (Terrain *terrain : terrains) {
        if (!hasTerrain(*targetTileset, terrain->name())) {
            Tile *terrainTile = terrain->imageTile();
            QPixmap terrainImage = terrainTile->image();

            Tile *newTerrainTile = targetTileset->addTile(terrainImage);
            newTerrainTile->setProperties(terrainTile->properties());

            Terrain *newTerrain =  targetTileset->addTerrain(terrain->name(),
                                                             newTerrainTile->id());

            // WARNING: This assumes the terrain tile has this terrain on all
            // its corners.
            newTerrainTile->setTerrain(makeTerrain(newTerrain->id()));
            terrainToTile.insert(TileTerrainNames(newTerrainTile),
                                 newTerrainTile);
        }
    }

    // Prepare a list of terrain combinations.
    QVector<TileTerrainNames> process;
    for (const QStringList &combine : options.combineList) {
        QList<Terrain*> terrainList;
        // Get the terrains to combine
        for (const QString &terrainName : combine)
            terrainList.append(terrains[terrainName]);

        // Construct a vector with all terrain combinations to process
        for (Terrain *topLeft : terrainList) {
            for (Terrain *topRight : terrainList) {
                for (Terrain *bottomLeft : terrainList) {
                    for (Terrain *bottomRight : terrainList) {
                        process.append(TileTerrainNames(topLeft->name(),
                                                        topRight->name(),
                                                        bottomLeft->name(),
                                                        bottomRight->name()));
                    }
                }
            }
        }
    }

    // Go through each combination of terrains and add the tile to the target
    // tileset if it's not in there yet.
    for (const TileTerrainNames &terrainNames : process) {
        Tile *tile = terrainToTile.value(terrainNames);

        if (tile && tile->tileset() == targetTileset)
            continue;

        QPixmap image;
        Properties properties;

        if (!tile) {
            qWarning() << "Generating" << terrainNames;

            // Start a new image
            QImage tileImage = QImage(targetTileset->tileWidth(),
                                      targetTileset->tileHeight(),
                                      QImage::Format_ARGB32);
            tileImage.fill(Qt::transparent);
            QPainter painter(&tileImage);

            QStringList terrainList = terrainNames.terrainList();
            qSort(terrainList.begin(), terrainList.end(), lessThan);

            // Draw the lowest terrain to avoid pixel gaps
            QString baseTerrain = terrainList.first();
            QPixmap baseImage = terrains[baseTerrain]->imageTile()->image();
            painter.drawPixmap(0, 0, baseImage);

            for (const QString &terrainName : terrainList) {
                TileTerrainNames filtered = terrainNames.filter(terrainName);
                Tile *tile = terrainToTile.value(filtered);
                if (!tile) {
                    qWarning() << "Missing" << filtered;
                    continue;
                }

                painter.drawPixmap(0, 0, tile->image());
                properties.merge(tile->properties());
            }

            image = QPixmap::fromImage(tileImage);
        } else {
            qWarning() << "Copying" << terrainNames << "from"
                       << QFileInfo(tile->tileset()->fileName()).fileName();

            image = tile->image();
            properties = tile->properties();
        }

        Tile *newTile = targetTileset->addTile(image);
        newTile->setTerrain(terrainNames.toTerrain(*targetTileset));
        newTile->setProperties(properties);
        terrainToTile.insert(terrainNames, newTile);
    }

    if (targetTileset->tileCount() == 0)
        qCritical("Target tileset is empty");

    if (options.embedImage) {
        // Make sure there is no source name, this way the image will be saved in the TSX file.
        targetTileset->setImageSource(QString());
    } else {
        // Save the target tileset image as separate file.
        int columns = qMin(options.columns, targetTileset->tileCount());
        int rows = targetTileset->tileCount() / options.columns;
        if (targetTileset->tileCount() % options.columns > 0)
            ++rows;

        qWarning() << "Writing external tileset image.";
        // Save the target tileset image
        QImage image(targetTileset->tileWidth() * columns,
                     targetTileset->tileHeight() * rows,
                     QImage::Format_ARGB32);

        image.fill(Qt::transparent);
        QPainter painter(&image);

        for (Tile *tile : targetTileset->tiles()) {
            int x = (tile->id() % options.columns) * targetTileset->tileWidth();
            int y = (tile->id() / options.columns) * targetTileset->tileHeight();
            painter.drawPixmap(x, y, tile->image());
        }

        QString imageFileName = QFileInfo(options.target).completeBaseName();
        imageFileName += ".png";
        image.save(imageFileName);

        targetTileset->setImageSource(imageFileName);
        targetTileset->setColumnCount(options.columns);
    }

    // Save the target tileset
    MapWriter writer;
    targetTileset->setFileName(QString());
    writer.writeTileset(*targetTileset, options.target);

    return 0;
}
Пример #4
0
int TmxRasterizer::render(const QString &mapFileName,
                          const QString &imageFileName)
{
    MapReader reader;
    std::unique_ptr<Map> map { reader.readMap(mapFileName) };
    if (!map) {
        qWarning("Error while reading \"%s\":\n%s",
                 qUtf8Printable(mapFileName),
                 qUtf8Printable(reader.errorString()));
        return 1;
    }

    std::unique_ptr<MapRenderer> renderer;

    switch (map->orientation()) {
    case Map::Isometric:
        renderer.reset(new IsometricRenderer(map.get()));
        break;
    case Map::Staggered:
        renderer.reset(new StaggeredRenderer(map.get()));
        break;
    case Map::Hexagonal:
        renderer.reset(new HexagonalRenderer(map.get()));
        break;
    case Map::Orthogonal:
    default:
        renderer.reset(new OrthogonalRenderer(map.get()));
        break;
    }

    QRect mapBoundingRect = renderer->mapBoundingRect();
    QSize mapSize = mapBoundingRect.size();
    QPoint mapOffset = mapBoundingRect.topLeft();
    qreal xScale, yScale;

    if (mSize > 0) {
        xScale = (qreal) mSize / mapSize.width();
        yScale = (qreal) mSize / mapSize.height();
        xScale = yScale = qMin(1.0, qMin(xScale, yScale));
    } else if (mTileSize > 0) {
        xScale = (qreal) mTileSize / map->tileWidth();
        yScale = (qreal) mTileSize / map->tileHeight();
    } else {
        xScale = yScale = mScale;
    }

    QMargins margins = map->computeLayerOffsetMargins();
    mapSize.setWidth(mapSize.width() + margins.left() + margins.right());
    mapSize.setHeight(mapSize.height() + margins.top() + margins.bottom());

    mapSize.rwidth() *= xScale;
    mapSize.rheight() *= yScale;

    QImage image(mapSize, QImage::Format_ARGB32);
    image.fill(Qt::transparent);
    QPainter painter(&image);

    painter.setRenderHint(QPainter::Antialiasing, mUseAntiAliasing);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, mSmoothImages);
    painter.setTransform(QTransform::fromScale(xScale, yScale));

    painter.translate(margins.left(), margins.top());
    painter.translate(-mapOffset);

    // Perform a similar rendering than found in exportasimagedialog.cpp
    LayerIterator iterator(map.get());
    while (const Layer *layer = iterator.next()) {
        if (!shouldDrawLayer(layer))
            continue;

        const auto offset = layer->totalOffset();

        painter.setOpacity(layer->effectiveOpacity());
        painter.translate(offset);

        const TileLayer *tileLayer = dynamic_cast<const TileLayer*>(layer);
        const ImageLayer *imageLayer = dynamic_cast<const ImageLayer*>(layer);

        if (tileLayer) {
            renderer->drawTileLayer(&painter, tileLayer);
        } else if (imageLayer) {
            renderer->drawImageLayer(&painter, imageLayer);
        }

        painter.translate(-offset);
    }

    map.reset();

    // Save image
    QImageWriter imageWriter(imageFileName);

    if (!imageWriter.canWrite())
        imageWriter.setFormat("png");

    if (!imageWriter.write(image)) {
        qWarning("Error while writing \"%s\": %s",
                 qUtf8Printable(imageFileName),
                 qUtf8Printable(imageWriter.errorString()));
        return 1;
    }

    return 0;
}