Google Summer of Code 2022 - The ENIGMA Team

Week 4: Fixing tile transformations and adding compressed data support

Contributor: Kartik Shrivastava
Project: Add Tiled compatibility to/from Enigma
Guided by: bjorn, fundies and Josh

Highlights:
  1. Added rotation, alpha(tint), horizontal/vertical flip support and fixed tile scaling #82cc729
  2. Modified Room.tiles paint code to support updated transformations #39cee30
  3. Added support to load TMX files with base64 encoded zlib compressed layer data #21c2c6b



A side note

Let's keep it no visible code log this time

Details

Getting transformations work right

Adding rotation support was the most straightforward one, I just added a rotation option to Room.Tile proto message. There is an attribute with the same name "rotation" already present in TMX Object so rest of the conversion has been taken care by PackRes method of TMX loader.

Adding opacity which can also create a tile tint effect, is not as straightforward as rotation. Reason being "opacity" attribute in TMX is stored in "objectgroup" node which applies to all of the child "object" nodes, and as objectgroup does not correspond to Room.tile (object does), we cannot rely on PackRes of TMX loader to load the opacity into Room.tile message. So I had to explicitly fetch set the opacity in LoadObjects method.

Tiled tiles support several types of flips and rotations based on the type of map orientation (orthogonal, isometric, staggered, hexagonal). The details of these flips and rotations are stored in globalId of the tile which can be retrieved either by gid attribute of "object" node (in case of objects) or by the binary string obtained after decompressing data from layer data node (in case of zlib compression). Checkout Tiled "Global Tile Id" documentation for more details. The highest 32-bits of each gid contains the flip flags and the types of flips/rotations it supports are:

Scale of the object based tiles of Tiled are represented in terms of their width and height, so there was nothing much to do in the TMX loader code. But to get the identical scaling I updated paint code of tiles present in room editor. Details of which are discussed in next section.



Modifying paint code to actually reflect changes in Room editor

So far we have retrieved all the required information correctly (hopefully) in protocol buffers. But why nothing looks different in Room editor? I'm glad you asked :P

Its because paint code does not have suitable implementation to modify tiles according to the added details. So the task is to update the paint code without breaking the look of other importers (like gmx). And indeed there will be a breaking change if not handled correctly.

It's the way source tile rect is transformed to destination tile rect before drawing, and this is where we have to plug the changes of Tiled tile's width/height (or scale). The trick to render tiles from both gmx and tmx correctly is to use "use_as_tileset" flag of background. When the flag is set to true, we will set width/height of "src" QRectF as individual tile width/height and when its false we will set it as whole background image width/height. Fix applies to "src" rect, this is important because we want to accurately focus the starting rect so that whatever width/height is saved in Room.tile (width/height of "dest" rect) handles the rest of scaling properly.

After fixing source-destination rect scaling. Adding rotation and scale (not same as of rect scale) is the matter of correct-ordered-transformation of tile in world coordinates (Qt painter doesn't seem to work in local coordinates). To set alpha I used "painter.setOpacity()" method.



Loading Tiled tiles from base64 encoded zlib compressed bytes

Adding this support wasn't as intimidating as the title sounds like. Thanks to already available implementation to decode base64 strings and also to uncompress the zlib data stream. Just required some grep commands to find the implementation and refactoring them to the common place.

I found base64 helper functions in "SHELL" dir (enigma's engine) which is not accessible from libEGM dir (where importers/exporters live). So my mentor fundies came to the rescue, and suggested to put it in "shared" dir so that it can be used from anywhere (including libEGM). Did that and worked like charm.

I found zlib encoder decoders in "gmk.cpp" which is a .gmk file importer, but the implementation was hidden. So that was separated into a brand new "libEGM/General" dir. Did that worked like charm too.

Plugged the data string from "layer/data" node of TMX file into base64 decoder and then into zlib decoder. Got a big string back which contains gids of all tiles in 4 bytes chunk. Interpreted that correctly and there is the support to compressed data. (P.S.: TBH wasn't that simple, those single liners in the implementation wasn't came into my mind instantly. So yeah ;)

So, that's how my 4th GSoC week went, coming week will be more focused on refactoring and figuring out the support to missing functionalities :)