diff --git a/.gitignore b/.gitignore index d4ed9dd45c..7b030950ef 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # General build artifacts default/ +build/ # clangd cache .cache/ diff --git a/docs/reference/json-map-format.rst b/docs/reference/json-map-format.rst index b4cd7faf86..73d8b4db38 100644 --- a/docs/reference/json-map-format.rst +++ b/docs/reference/json-map-format.rst @@ -216,6 +216,7 @@ Object polyline, array, "Array of :ref:`Points `, in case the object is a polyline" properties, array, "Array of :ref:`Properties `" rotation, double, "Angle in degrees clockwise" + opacity, double, "The opacity of the object as a value from 0 to 1. Defaults to 1." template, string, "Reference to a template file, in case object is a :doc:`template instance `" text, :ref:`json-object-text`, "Only used for text objects" type, string, "The class of the object (was saved as ``class`` in 1.9, optional)" @@ -241,6 +242,7 @@ Object Example "value":12 }], "rotation":0, + "opacity":1, "type":"npc", "visible":true, "width":0, @@ -259,6 +261,7 @@ Ellipse Example "id":13, "name":"", "rotation":0, + "opacity":1, "type":"", "visible":true, "width":248, @@ -276,6 +279,7 @@ Rectangle Example "id":14, "name":"", "rotation":0, + "opacity":1, "type":"", "visible":true, "width":368, @@ -294,6 +298,7 @@ Point Example "name":"", "point":true, "rotation":0, + "opacity":1, "type":"", "visible":true, "width":0, @@ -332,6 +337,7 @@ Polygon Example "y":-288 }], "rotation":0, + "opacity":1, "type":"", "visible":true, "width":0, @@ -374,6 +380,7 @@ Polyline Example "y":0 }], "rotation":0, + "opacity":1, "type":"", "visible":true, "width":0, @@ -396,6 +403,7 @@ Text Example "wrap":true }, "rotation":0, + "opacity":1, "type":"", "visible":true, "width":248, diff --git a/docs/reference/tmx-map-format.rst b/docs/reference/tmx-map-format.rst index 1df38f5e57..49a5a96b48 100644 --- a/docs/reference/tmx-map-format.rst +++ b/docs/reference/tmx-map-format.rst @@ -549,6 +549,8 @@ Can contain any number: :ref:`tmx-object` - **height:** The height of the object in pixels. (defaults to 0) - **rotation:** The rotation of the object in degrees clockwise around (x, y). (defaults to 0) +- **opacity:** The opacity of the object as a value from 0 to 1. (defaults + to 1) - **gid:** A reference to a tile. (optional) - **visible:** Whether the object is shown (1) or hidden (0). (defaults to 1) diff --git a/src/libtiled/mapobject.cpp b/src/libtiled/mapobject.cpp index a517801a98..58595702eb 100644 --- a/src/libtiled/mapobject.cpp +++ b/src/libtiled/mapobject.cpp @@ -312,6 +312,7 @@ QVariant MapObject::mapObjectProperty(Property property) const case PositionProperty: return mPos; case SizeProperty: return mSize; case RotationProperty: return mRotation; + case OpacityProperty: return mOpacity; case CellProperty: Q_ASSERT(false); break; case ShapeProperty: return mShape; case TemplateProperty: Q_ASSERT(false); break; @@ -334,6 +335,7 @@ void MapObject::setMapObjectProperty(Property property, const QVariant &value) case PositionProperty: setPosition(value.toPointF()); break; case SizeProperty: setSize(value.toSizeF()); break; case RotationProperty: setRotation(value.toReal()); break; + case OpacityProperty: setOpacity(value.toReal()); break; case CellProperty: Q_ASSERT(false); break; case ShapeProperty: setShape(value.value()); break; case TemplateProperty: Q_ASSERT(false); break; @@ -374,6 +376,7 @@ MapObject *MapObject::clone() const o->setShape(mShape); o->setCell(mCell); o->setRotation(mRotation); + o->setOpacity(mOpacity); o->setVisible(mVisible); o->setChangedProperties(mChangedProperties); o->setObjectTemplate(mObjectTemplate); @@ -389,6 +392,7 @@ void MapObject::copyPropertiesFrom(const MapObject *object) setShape(object->shape()); setCell(object->cell()); setRotation(object->rotation()); + setOpacity(object->opacity()); setVisible(object->isVisible()); setProperties(object->properties()); setChangedProperties(object->changedProperties()); @@ -428,6 +432,9 @@ void MapObject::syncWithTemplate() if (!propertyChanged(MapObject::RotationProperty)) setRotation(base->rotation()); + if (!propertyChanged(MapObject::OpacityProperty)) + setOpacity(base->opacity()); + if (!propertyChanged(MapObject::VisibleProperty)) setVisible(base->isVisible()); } diff --git a/src/libtiled/mapobject.h b/src/libtiled/mapobject.h index 4e8ae8ab27..bd24ae362c 100644 --- a/src/libtiled/mapobject.h +++ b/src/libtiled/mapobject.h @@ -117,10 +117,11 @@ class TILEDSHARED_EXPORT MapObject : public Object PositionProperty = 1 << 7, SizeProperty = 1 << 8, RotationProperty = 1 << 9, - CellProperty = 1 << 10, - ShapeProperty = 1 << 11, - TemplateProperty = 1 << 12, - CustomProperties = 1 << 13, + OpacityProperty = 1 << 10, + CellProperty = 1 << 11, + ShapeProperty = 1 << 12, + TemplateProperty = 1 << 13, + CustomProperties = 1 << 14, AllProperties = 0xFF }; @@ -200,6 +201,9 @@ class TILEDSHARED_EXPORT MapObject : public Object qreal rotation() const; void setRotation(qreal rotation); + qreal opacity() const; + void setOpacity(qreal opacity); + Alignment alignment(const Map *map = nullptr) const; bool isVisible() const; @@ -247,6 +251,7 @@ class TILEDSHARED_EXPORT MapObject : public Object const ObjectTemplate *mObjectTemplate = nullptr; ObjectGroup *mObjectGroup = nullptr; qreal mRotation = 0.0; + qreal mOpacity = 1.0; bool mVisible = true; bool mTemplateBase = false; ChangedProperties mChangedProperties; @@ -479,6 +484,18 @@ inline qreal MapObject::rotation() const inline void MapObject::setRotation(qreal rotation) { mRotation = rotation; } +/** + * Returns the opacity of the object. + */ +inline qreal MapObject::opacity() const +{ return mOpacity; } + +/** + * Sets the opacity of the object. + */ +inline void MapObject::setOpacity(qreal opacity) +{ mOpacity = opacity; } + inline bool MapObject::isVisible() const { return mVisible; } diff --git a/src/libtiled/mapreader.cpp b/src/libtiled/mapreader.cpp index 820eed8b43..c5454cc11b 100644 --- a/src/libtiled/mapreader.cpp +++ b/src/libtiled/mapreader.cpp @@ -1222,6 +1222,12 @@ std::unique_ptr MapReaderPrivate::readObject() object->setPropertyChanged(MapObject::RotationProperty); } + const qreal opacity = atts.value(QLatin1String("opacity")).toDouble(&ok); + if (ok) { + object->setOpacity(opacity); + object->setPropertyChanged(MapObject::OpacityProperty); + } + if (gid) { object->setCell(cellForGid(gid)); object->setPropertyChanged(MapObject::CellProperty); diff --git a/src/libtiled/maptovariantconverter.cpp b/src/libtiled/maptovariantconverter.cpp index 93e7fb882f..32cde229b4 100644 --- a/src/libtiled/maptovariantconverter.cpp +++ b/src/libtiled/maptovariantconverter.cpp @@ -586,6 +586,9 @@ QVariant MapToVariantConverter::toVariant(const MapObject &object) const if (notTemplateInstance || object.propertyChanged(MapObject::RotationProperty)) objectVariant[QStringLiteral("rotation")] = object.rotation(); + if (notTemplateInstance || object.propertyChanged(MapObject::OpacityProperty)) + objectVariant[QStringLiteral("opacity")] = object.opacity(); + if (notTemplateInstance || object.propertyChanged(MapObject::VisibleProperty)) objectVariant[QStringLiteral("visible")] = object.isVisible(); diff --git a/src/libtiled/mapwriter.cpp b/src/libtiled/mapwriter.cpp index d6c4f3265d..265d708c13 100644 --- a/src/libtiled/mapwriter.cpp +++ b/src/libtiled/mapwriter.cpp @@ -756,6 +756,10 @@ void MapWriterPrivate::writeObject(QXmlStreamWriter &w, if (shouldWrite(rotation != 0.0, isTemplateInstance, mapObject.propertyChanged(MapObject::RotationProperty))) w.writeAttribute(QStringLiteral("rotation"), QString::number(rotation)); + const qreal opacity = mapObject.opacity(); + if (shouldWrite(opacity != 1.0, isTemplateInstance, mapObject.propertyChanged(MapObject::OpacityProperty))) + w.writeAttribute(QStringLiteral("opacity"), QString::number(opacity)); + if (shouldWrite(!mapObject.isVisible(), isTemplateInstance, mapObject.propertyChanged(MapObject::VisibleProperty))) w.writeAttribute(QStringLiteral("visible"), QLatin1String(mapObject.isVisible() ? "1" : "0")); diff --git a/src/libtiled/minimaprenderer.cpp b/src/libtiled/minimaprenderer.cpp index de7e125578..b3bfadff64 100644 --- a/src/libtiled/minimaprenderer.cpp +++ b/src/libtiled/minimaprenderer.cpp @@ -203,6 +203,8 @@ void MiniMapRenderer::renderToImage(QImage &image, RenderFlags renderFlags) cons painter.translate(-origin); } + painter.setOpacity(object->opacity()); + mRenderer->drawMapObject(&painter, object, object->effectiveColors()); if (object->rotation() != qreal(0)) diff --git a/src/libtiled/varianttomapconverter.cpp b/src/libtiled/varianttomapconverter.cpp index 2c2b9bf3b5..fdb5a2c75a 100644 --- a/src/libtiled/varianttomapconverter.cpp +++ b/src/libtiled/varianttomapconverter.cpp @@ -722,6 +722,7 @@ std::unique_ptr VariantToMapConverter::toMapObject(const QVariantMap const qreal width = variantMap[QStringLiteral("width")].toReal(); const qreal height = variantMap[QStringLiteral("height")].toReal(); const qreal rotation = variantMap[QStringLiteral("rotation")].toReal(); + const qreal opacity = variantMap[QStringLiteral("opacity")].toReal(); QString className = variantMap[QStringLiteral("class")].toString(); if (className.isEmpty()) // fallback for compatibility @@ -738,6 +739,11 @@ std::unique_ptr VariantToMapConverter::toMapObject(const QVariantMap object->setPropertyChanged(MapObject::RotationProperty); } + if (variantMap.contains(QLatin1String("opacity"))) { + object->setOpacity(opacity); + object->setPropertyChanged(MapObject::OpacityProperty); + } + if (!templateVariant.isNull()) { // This object is a template instance QString templateFileName = resolvePath(mDir, templateVariant); auto objectTemplate = TemplateManager::instance()->loadObjectTemplate(templateFileName); diff --git a/src/plugins/lua/luaplugin.cpp b/src/plugins/lua/luaplugin.cpp index e8d53b2d37..36493cc773 100644 --- a/src/plugins/lua/luaplugin.cpp +++ b/src/plugins/lua/luaplugin.cpp @@ -730,6 +730,7 @@ void LuaWriter::writeMapObject(const Tiled::MapObject *mapObject) mWriter.writeKeyAndValue("width", mapObject->width()); mWriter.writeKeyAndValue("height", mapObject->height()); mWriter.writeKeyAndValue("rotation", mapObject->rotation()); + mWriter.writeKeyAndValue("opacity", mapObject->opacity()); if (!mapObject->cell().isEmpty()) mWriter.writeKeyAndValue("gid", mGidMapper.cellToGid(mapObject->cell())); diff --git a/src/plugins/python/pythonbind.cpp b/src/plugins/python/pythonbind.cpp index 18d5f5cb3d..8613c0637c 100644 --- a/src/plugins/python/pythonbind.cpp +++ b/src/plugins/python/pythonbind.cpp @@ -6985,6 +6985,18 @@ _wrap_PyTiledMapObject_objectGroup(PyTiledMapObject *self, PyObject *PYBINDGEN_U } +PyObject * +_wrap_PyTiledMapObject_opacity(PyTiledMapObject *self, PyObject *PYBINDGEN_UNUSED(_args), PyObject *PYBINDGEN_UNUSED(_kwargs)) +{ + PyObject *py_retval; + double retval; + + retval = self->obj->opacity(); + py_retval = Py_BuildValue((char *) "d", retval); + return py_retval; +} + + PyObject * _wrap_PyTiledMapObject_rotation(PyTiledMapObject *self, PyObject *PYBINDGEN_UNUSED(_args), PyObject *PYBINDGEN_UNUSED(_kwargs)) { @@ -7049,6 +7061,23 @@ _wrap_PyTiledMapObject_setName(PyTiledMapObject *self, PyObject *args, PyObject } +PyObject * +_wrap_PyTiledMapObject_setOpacity(PyTiledMapObject *self, PyObject *args, PyObject *kwargs) +{ + PyObject *py_retval; + double opacity; + const char *keywords[] = {"opacity", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "d", (char **) keywords, &opacity)) { + return NULL; + } + self->obj->setOpacity(opacity); + Py_INCREF(Py_None); + py_retval = Py_None; + return py_retval; +} + + PyObject * _wrap_PyTiledMapObject_setPosition(PyTiledMapObject *self, PyObject *args, PyObject *kwargs) { @@ -7271,10 +7300,12 @@ static PyMethodDef PyTiledMapObject_methods[] = { {(char *) "isVisible", (PyCFunction) _wrap_PyTiledMapObject_isVisible, METH_NOARGS, "isVisible()\n\n" }, {(char *) "name", (PyCFunction) _wrap_PyTiledMapObject_name, METH_NOARGS, "name()\n\n" }, {(char *) "objectGroup", (PyCFunction) _wrap_PyTiledMapObject_objectGroup, METH_NOARGS, "objectGroup()\n\n" }, + {(char *) "opacity", (PyCFunction) _wrap_PyTiledMapObject_opacity, METH_NOARGS, "opacity()\n\n" }, {(char *) "rotation", (PyCFunction) _wrap_PyTiledMapObject_rotation, METH_NOARGS, "rotation()\n\n" }, {(char *) "setCell", (PyCFunction) _wrap_PyTiledMapObject_setCell, METH_KEYWORDS|METH_VARARGS, "setCell(c)\n\ntype: c: Tiled::Cell const" }, {(char *) "setHeight", (PyCFunction) _wrap_PyTiledMapObject_setHeight, METH_KEYWORDS|METH_VARARGS, "setHeight(h)\n\ntype: h: double" }, {(char *) "setName", (PyCFunction) _wrap_PyTiledMapObject_setName, METH_KEYWORDS|METH_VARARGS, "setName(n)\n\ntype: n: QString" }, + {(char *) "setOpacity", (PyCFunction) _wrap_PyTiledMapObject_setOpacity, METH_KEYWORDS|METH_VARARGS, "setOpacity(opacity)\n\ntype: opacity: double" }, {(char *) "setPosition", (PyCFunction) _wrap_PyTiledMapObject_setPosition, METH_KEYWORDS|METH_VARARGS, "setPosition(pos)\n\ntype: pos: QPointF" }, {(char *) "setRotation", (PyCFunction) _wrap_PyTiledMapObject_setRotation, METH_KEYWORDS|METH_VARARGS, "setRotation(r)\n\ntype: r: double" }, {(char *) "setShape", (PyCFunction) _wrap_PyTiledMapObject_setShape, METH_KEYWORDS|METH_VARARGS, "setShape(s)\n\ntype: s: Tiled::MapObject::Shape" }, diff --git a/src/plugins/python/tiledbinding.py b/src/plugins/python/tiledbinding.py index 2901f270aa..084e11a065 100755 --- a/src/plugins/python/tiledbinding.py +++ b/src/plugins/python/tiledbinding.py @@ -280,6 +280,8 @@ def _decorate(obj, *args, **kwargs): cls_mapobject.add_method('objectGroup', retval('Tiled::ObjectGroup*',reference_existing_object=True), []) cls_mapobject.add_method('rotation', 'double', []) cls_mapobject.add_method('setRotation', None, [('double','r')]) +cls_mapobject.add_method('opacity', 'double', []) +cls_mapobject.add_method('setOpacity', None, [('double','opacity')]) cls_mapobject.add_method('isVisible', 'bool', []) cls_mapobject.add_method('setVisible', None, [('bool','v')]) cls_mapobject.add_method('name', 'QString', []) diff --git a/src/tiled/editablemapobject.cpp b/src/tiled/editablemapobject.cpp index 23e67ec53f..537432176f 100644 --- a/src/tiled/editablemapobject.cpp +++ b/src/tiled/editablemapobject.cpp @@ -190,6 +190,11 @@ void EditableMapObject::setRotation(qreal rotation) setMapObjectProperty(MapObject::RotationProperty, rotation); } +void EditableMapObject::setOpacity(qreal opacity) +{ + setMapObjectProperty(MapObject::OpacityProperty, opacity); +} + void EditableMapObject::setVisible(bool visible) { setMapObjectProperty(MapObject::VisibleProperty, visible); diff --git a/src/tiled/editablemapobject.h b/src/tiled/editablemapobject.h index a43b971f1f..68c2e5a302 100644 --- a/src/tiled/editablemapobject.h +++ b/src/tiled/editablemapobject.h @@ -63,6 +63,7 @@ class EditableMapObject : public EditableObject Q_PROPERTY(qreal height READ height WRITE setHeight) Q_PROPERTY(QSizeF size READ size WRITE setSize) Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) Q_PROPERTY(bool visible READ isVisible WRITE setVisible) Q_PROPERTY(QJSValue polygon READ polygon WRITE setPolygon) Q_PROPERTY(QString text READ text WRITE setText) @@ -114,6 +115,7 @@ class EditableMapObject : public EditableObject qreal height() const; QSizeF size() const; qreal rotation() const; + qreal opacity() const; bool isVisible() const; QJSValue polygon() const; QString text() const; @@ -149,6 +151,7 @@ public slots: void setHeight(qreal height); void setSize(QSizeF size); void setRotation(qreal rotation); + void setOpacity(qreal opacity); void setVisible(bool visible); void setPolygon(QJSValue polygon); void setPolygon(const QPolygonF &polygon); @@ -219,6 +222,11 @@ inline qreal EditableMapObject::rotation() const return mapObject()->rotation(); } +inline qreal EditableMapObject::opacity() const +{ + return mapObject()->opacity(); +} + inline bool EditableMapObject::isVisible() const { return mapObject()->isVisible(); diff --git a/src/tiled/mapobjectitem.cpp b/src/tiled/mapobjectitem.cpp index 70c679fecb..f44a1e6352 100644 --- a/src/tiled/mapobjectitem.cpp +++ b/src/tiled/mapobjectitem.cpp @@ -89,6 +89,7 @@ void MapObjectItem::syncWithMapObject() setPos(pixelPos); setRotation(mObject->rotation()); + setOpacity(mObject->opacity()); if (mBoundingRect != bounds) { // Notify the graphics scene about the geometry change in advance diff --git a/src/tiled/propertybrowser.cpp b/src/tiled/propertybrowser.cpp index 511a33eee1..bc511dc122 100644 --- a/src/tiled/propertybrowser.cpp +++ b/src/tiled/propertybrowser.cpp @@ -839,6 +839,11 @@ void PropertyBrowser::addMapObjectProperties() bool isPoint = mapObject->shape() == MapObject::Point; addProperty(RotationProperty, QMetaType::Double, tr("Rotation"), groupProperty)->setEnabled(!isPoint); + QtVariantProperty *opacityProperty = addProperty(OpacityProperty, QMetaType::Double, tr("Opacity"), groupProperty); + opacityProperty->setAttribute(QLatin1String("minimum"), 0.0); + opacityProperty->setAttribute(QLatin1String("maximum"), 1.0); + opacityProperty->setAttribute(QLatin1String("singleStep"), 0.1); + if (mMapObjectFlags & ObjectHasTile) { QtVariantProperty *flippingProperty = addProperty(FlippingProperty, VariantPropertyManager::flagTypeId(), @@ -1296,6 +1301,12 @@ QUndoCommand *PropertyBrowser::applyMapObjectValueTo(PropertyId id, const QVaria val.toDouble()); } break; + case OpacityProperty:{ + command = new ChangeMapObject(mDocument, mapObject, + MapObject::OpacityProperty, + val.toReal()); + break; + } case FlippingProperty: { const int flippingFlags = val.toInt(); @@ -1905,6 +1916,7 @@ void PropertyBrowser::updateProperties() } mIdToProperty[RotationProperty]->setValue(mapObject->rotation()); + mIdToProperty[OpacityProperty]->setValue(mapObject->opacity()); if (flags & ObjectHasTile) { int flippingFlags = 0; diff --git a/src/tmxrasterizer/tmxrasterizer.cpp b/src/tmxrasterizer/tmxrasterizer.cpp index 05b081fca6..eff40eee06 100644 --- a/src/tmxrasterizer/tmxrasterizer.cpp +++ b/src/tmxrasterizer/tmxrasterizer.cpp @@ -85,6 +85,8 @@ void TmxRasterizer::drawMapLayers(const MapRenderer &renderer, painter.translate(-origin); } + painter.setOpacity(object->opacity()); + renderer.drawMapObject(&painter, object, object->effectiveColors()); if (object->rotation() != qreal(0)) diff --git a/src/tmxviewer/tmxviewer.cpp b/src/tmxviewer/tmxviewer.cpp index e00ea748ef..4a741fc83f 100644 --- a/src/tmxviewer/tmxviewer.cpp +++ b/src/tmxviewer/tmxviewer.cpp @@ -65,6 +65,7 @@ class MapObjectItem : public QGraphicsItem setPos(pixelPos); setRotation(mapObject->rotation()); + setOpacity(mapObject->opacity()); } QRectF boundingRect() const override