Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Transformations and clipping
- - By Maytsh Date 2010-10-12 15:11
Firstly: In theory, the transformations translate, scale and rotate would be enough to place an image at any position you'd like, right? I'm thinking about whether having

  at (20, 50) $ rotate 50 $ scale 2 $ drawImageCentered bla

wouldn't actually be a good idea. I wouldn't even have to use the transformation support, as my Haskell layer could translate most of it into "normal" drawRot calls behind the scenes. In case the transformation gets too complex, I can always fallback.

Secondly: There's an idea I had while implementing Cptn Ruby in Haskell: For the tile drawing optimization, I would have to pass in the camera position and everything. Wouldn't it be nicer if I could just ask Gosu for the bounding box that applies to all current drawing calls?

That would have to keep track of:
* The window size
* If present, user-defined clippers
* and all kinds of transformations that might be active at that point

The last point could obviously be a bit non-trivial, as Gosu would have to invert the matrices active at that point.

And lastly: Are transformations and clipping interacting correctly anyway? Would

  scale 2 $ clip (0,0) (50, 50) $ scale 2 $ drawImage sky

Give me a 25x25 part of the "sky" image, zoomed up 4 times to (100, 100)?
Parent - - By RavensKrag Date 2010-10-12 17:23
I don't know much about Haskell (well, nothing at all actually...) but if you use Chipmunk for collision detection and physics, as I am doing in my game, you can get a bounding box which you can use to figure out which objects need to be drawn.  I'm using ruby, but I believe that there is a haskell version of Chipmunk as well.  However, this is not really the core of Gosu, so it might be best not to have it in the Cptn Ruby demo.
Parent - By Maytsh Date 2010-10-13 11:42 Edited 2010-10-13 11:46
That's not really what I meant. Let's say I have some complicated top-level transformation. Worst case: The character drank a potion of whiskey, and from personal experience the game programmer decides to represent this by randomly translating, scaling and rotating the whole view around.

So now I'm in the bottom-most drawing call and am asking the questions: What do I need to draw? The call should not need to care about what the upper layers are doing with the transformation matrices. It shouldn't need to keep some Chipmunk sensor up-to-date and synchronized. It should just be able to ask Gosu: "What's the box I need to fill at this point?". That's the idea.
Parent - By Spooner Date 2010-10-13 11:51
As to your last question, yes you are interpreting the interactions correctly (25x25 of the sky zoomed to 100x100).
Parent - - By jlnr (dev) Date 2010-10-14 08:28
I have thought about getting the current bounding box, but the box can be rotated so I thought I'd leave this up to the actual games for now. ;)

I think the whole thing needs further practical evaluation. So far, it would not really save me much or any code. :( This would be mostly for clipping objects and maps, right?
Parent - By Maytsh Date 2010-10-14 16:25
Well, yes. Which is an important enough issue once your game world is big enough, I'd guess? You could also try to use it for a level-of-detail-type estimation.

Rotation could probably get away with pretty conservative estimates - the "fine" check is better left to the graphics card or the driver. The only problematic scenario would be many stacked rotations, which doesn't strike me as a very typical scenario.
Parent - - By Maytsh Date 2010-10-20 15:27
Hm, nobody wants to comment on the first suggestion? I must say I am rather tempted by this. Would greatly prefer it to implementing factorX/factorY.

In the meantime, I have now a rather naive implementation of the clipping area calculation, and it seems to work pretty well without additional headache. The headache only started when I decided to build that into Captain Gosu and played for some time with a rotating yet efficiently drawn playing field %(
Parent - - By jlnr (dev) Date 2010-10-20 21:15
Well there are threads on the board already about changing the Ruby/C++ syntax to img.scale(2).draw(x, y, z) already, so I think it's a good idea. Can't comment on the exakt Haskell syntax though :)
Parent - By Maytsh Date 2010-10-21 12:36
Okay, thank you, that's all I need :)

I actually just figured out that I can capture the "scale relative to point x" and "rotate around point y" stuff with a simple modifier like so:

  rotate 90 around (5,5) $ do ...

Which I think is neat enough to have it in the interface. I was stuck at that detail for some time.

[And just because I can't resist being pedagogical here: around is Haskell syntax for "operator-like" function application, so the above is equivalent to around (rotate 90) (5,5) (do ...). Type signature:

around :: (GosuIO a -> GosuIO a) -> (Double, Double) -> (GosuIO a -> GosuIO a)

which essentially means "takes a function from a Gosu draw action to a Gosu draw action (a transformation), a point and then maps a Gosu draw action to a Gosu draw action (= again, a transformation)". The actual implementation is then:

around trans (x,y) = translate (x, y) . trans . translate (-x, -y)

Which says that, unsurprisingly, the whole thing is just a combination (= function composition, operator ".") of transformations.]
Parent - - By Maytsh Date 2010-11-06 14:44

>  I wouldn't even have to use the transformation support, as my Haskell layer could translate most of it into "normal" drawRot calls behind the scenes.


Hm, would that even be worth it? Is there some kind of data on what the performance difference is of using transformations versus bare draw calls?
Parent - - By jlnr (dev) Date 2010-11-06 15:01
No hard data, but at least it's not the way Gosu is meant to be used, so I can't take it into account when optimizing. On the current iOS port, you would definitely ruin rendering performance. :)
Parent - - By Maytsh Date 2010-11-10 12:34
Seems it ruins rendering performance here as well. At least if I haven't somehow managed to kill it otherwise. 16 ms per draw for the captain example is unacceptable, that's for sure.

Hm, emulation leaves me with quite a number of tricky design decisions.
Parent - - By Maytsh Date 2010-11-11 03:08
And it seems it really is pushTransform/popTransform that makes the difference. If I call it a few thousand times per frame, things slow to a crawl. I guess I will now use the ImageData::draw interface, which is equally powerful and probably a lot faster.

My only problem is that there seems to be no equivalent version for text. Currently thinking of just implementing my own version. Any thoughts?
Parent - - By jlnr (dev) Date 2010-11-11 11:05
By text you mean Font? You really need transforms for that, but are there usually many Font calls per frame? I'd say it's <10 in the common case.
Parent - - By Maytsh Date 2010-11-11 12:10
Well, yes, right now you do. But there isn't really a good reason for it. And I wouldn't really like to have two transformation mechanism implementations side-by-side... Using the "corner" style interface, I can pack all information into a single coordinate transformation function that gets changed around by the transformation functions. Now I would have to additionally keep a list of transforms I would have to apply "just in case" I have to draw text.

Plus I'm currently thinking about going a step further and implement an "align" transform (with "center = align 0.5 0.5") which automatically determines the bounding box of the drawn stuff and aligns it as specified. That would not only make the current "drawCentered" call unnecessary, but also a good number of possible parameters to the text drawing functions.

And as I have to calculate bounding boxes for this anyway, having a quick "draw this text into the space given by these corners" would be really convenient.
Parent - - By jlnr (dev) Date 2010-11-12 00:33
I think then a reimplementation of Font would be the fastest path for you. If vertex arrays worked, you could just do (in Ruby notation):

record { font.draw(…) }.draw_as_quad(x1, y1, c1, … )

This would just put all the font coordinates into a coordinate array, wrap it into an ImageData and then follow the same quad logic that you are using with other ImageDatas. Except you'd probably calculate the resulting matrix, transform it into four coordinates and the ImageData impl would transform the coordinates back into a matrix, but still pretty close. :)
Parent - - By Maytsh Date 2010-11-12 17:43 Edited 2010-11-12 17:49
Hm, at what point would it get re-transformed into a matrix? I was under the impression that the quad was actually pretty close to what GL gets in the end.

And I remembered that with all the flexible coordinate transformation, I still have to make sure the clippings work correctly. Actually not convinced yet Gosu even does the right thing there from looking at the code. Will clipping, then rotating, then clipping again work correctly, for instance?
Parent - By Maytsh Date 2010-11-14 23:30
And sweet irony, I guess I'm now using matrixes in my code again - more efficient than functions. I *still* had to do some pretty hardcore investigation to get performance back on track. But at least now the performance of the captain example isn't completely embarassing anymore.
Up Topic Gosu / Gosu Exchange / Transformations and clipping

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill