Merge: gamnit: control drawing order
authorJean Privat <jean@pryen.org>
Mon, 17 Jul 2017 13:53:30 +0000 (09:53 -0400)
committerJean Privat <jean@pryen.org>
Mon, 17 Jul 2017 13:53:30 +0000 (09:53 -0400)
When two sprites are overlapped, a non-opaque pixel drawn before the sprite behind it may be blended with the wrong color. The only solution is to control the draw order to ensure that sprites in the back are drawn first. However, this is hard to automate when grouping sprites in buffers to optimize the drawing of a very large number of sprites (as gamnit does). It requires to break off groups in layers and this may be very costly if the sprites move on the Z axis.

The solution it to let the programmer choose a draw order only when needed. This works pretty well in 2D games where some objects stay on the same relative depth layer.

> Example artifacts on the left, and the fixed result (with a `sprite.draw_order = 1`) on the right:
>
> ![bug1](https://user-images.githubusercontent.com/208057/28196073-3e0ec6fc-681c-11e7-98d0-019e25a7f9c3.png) ![fixed1](https://user-images.githubusercontent.com/208057/28196072-3dfe075e-681c-11e7-9f4d-8d08a45b9661.png)

This PR introduces the attribute `Sprite::draw_order` that can be set to a higher value (default at 0) for the sprite to be drawn later. In general, sprites with a non-opaque `texture` and sprites closer to the camera should have a higher value to be drawn last.

Sprites sharing a `draw_order` are drawn in the same pass. The sprite to sprite draw order is undefined and may change when adding and removing sprites, or changing their attributes. A future improvement could sort sprites in the same group automatically if it becomes an issue.

Note that changing the `draw_order` may have a negative performance impact if there are many different `draw_order` values across many sprites. This may break the grouping optimization by creating too many small groups.

---

I plan on adding the draw order to `gamnit::depth` actors too. This will be useful for games than mix 2D with 3D. However, I would have to apply a different strategy for pure 3D games, implementing two main passes, one for opaque objects and one for non-opaque objects.

Pull-Request: #2526
Reviewed-by: Jean Privat <jean@pryen.org>


Trivial merge