Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / Built-in scrolling and transformations in general
- By erisdiscord Date 2010-04-28 20:59 Edited 2010-06-08 13:52
It would be nice and convenient and probably faster for games that require screen scrolling if Gosu's drawing code did something like this:

glPushMatrix();
glTranslatef(pimpl->scrollX, pimpl->scrollY, 0.0);
/* Gosu's drawing magic here */
glPopMatrix();


The Graphics object (or Window in Ruby) would present functions for getting and setting the X and Y scroll offsets and drawing code built on top of Gosu would not need to concern itself with additional arithmetic to handle screen scrolling. Of course, monkey patching on the Ruby side could do this for me, but it seems like a glTranslatef-based approach would be faster. Comments?

[ I tried implementing it myself, but I can't for the life of me get Gosu to build cleanly on my system. After unpacking Ruby 1.9's source into the appropriate location and running rake mac:gem, it still fails spectacularly during the xcode build. I'll ask for help with this problem later. :) ]

(Took the liberty to rename this topic as it evolved—jlnr)
- By jlnr (dev) Date 2010-04-29 06:46 Edited 2010-04-29 06:50
I whole-heartedly agree. As for how the interface should look like: I don't trust solutions that assign to global state (or window-local state, which is the same thing). Having to remember resetting flags is error-prone. Also, one pair of scrolling variables may work for some games, but is not general, think parallax layers or non-sidescrollers, etc. etc.

I secretly planned to add translate(scroll_x, scroll_y) do ... end, same for rotate, scale and a generic matrix translation.

I am working on making the "building for Ruby 1.9" thing a bit better right now because I can't even get it to build myself anymore. I am stalled on trying to get rvm to install ppc builds. Time spent on Gosu is generally maybe 2% for writing actual code, 98% for plumbing like the rvm stuff. Did I mention Linux compilation in the presence of rvm is broken too? Or Windows compilation with MSVC 2010? etc. etc.

Sorry, I just had to rant because the situation pains me. I really missed this feature myself during the last Ludum Dare compo, and its absence uglifies all my existing games too.
- By BlueScope Date 2010-04-29 08:20
It appears to me as that'd be a break of the base concept though, wouldn't it? Providing only the basic framework is a bit less advanced than providing a built-in translate feature...

Basically, I believe that it wouldn't hurt, however, it's currently neither hard nor cluttery to include scrolling into games...
- By jlnr (dev) Date 2010-04-29 08:40 Edited 2010-04-29 08:42
Well, one could argue that I should only include applying_matrix(...) do ... end, because that would only add one function for a lot of new possibilities. Translate and rotate etc. could easily be built on top of that.

Right now you have to manually do the math for all the scrolling, which is actually *slow*. No framework built on top of Gosu could ever provide this at the same performance as using a matrix internally could. Also, rotations especially are incredibly annoying to do by hand. At least for translations in 90° steps, I already had real-life use cases too :)
- By erisdiscord Date 2010-04-29 12:05
I agree with the global state philosophy in general, but I couldn't think of a clean way to implement it. I haven't delved too far into the code, but it's my understanding that Gosu sorts drawing operations by Z order and draws back to front, correct? I'm not an expert on OpenGL, but I seem to recall that this has to be done for the scene to look right.

Given that, how would the translation effect be pulled off? I suppose you could add a translation parameter to the drawing ops, but then that would add unneeded complexity and you'd still be doing the translation math (albeit hardware accelerated matrix math) for each draw, and then that wouldn't take into account arbitrary translations. Flush the drawing cache after each block? Then you'd have Z ordering issues. Of course, this stuff is why I prefer to use your framework rather than using SDL or OpenGL directly. :D

I'm definitely aware of people's problems building the library. I blame Xcode on the Mac side—it's a decent tool but the level of complexity available for project configurations is problematic. I think I've traced my problem to not having the 10.4 SDK installed, but we'll see!
- By jlnr (dev) Date 2010-04-30 09:04
Well, using applying_matrix do… you would assume that every user only has a small number of matrices. I think I will just have all those ready and for every draw op store the index of the matrix that it needs. Then pop/push when two DrawOps have different transformations applied. For the simplest use case, moving all game content but not the UI on top of it (z-wise), this should work very well.

Re Xcode: Actually no, the most blocking issue for me right now is that Ruby is just horrible to work with from a dev perspective. I actually moved the source files from Ruby 1.9 out of their Makefile hell into an Xcode project because that made it EASIER to just get the damn thing to compile for more than the system's current architecture. From Ruby itself down to Rubygems, nothing is really documented either.
- By erisdiscord Date 2010-04-30 14:51
Ouch, yeah, the GNU build system is a horrible monster that needs to die. We need a new cross-platform build system that isn't and I haven't found one yet. :( I'll spare the rant about where Xcode has burned me because it is an adorable kitten with a nice UI in comparison.

Now that I have the 10.4 SDK, I've traced my build problems to SWIG. Mine is generating different (but still wrong!) code from yours so the patch is failing and I'm getting a bunch of crazy errors (including "void pointer used in arithmetic") where the patch corrections have missed. I can't apply the patch by hand because the code looks so different that I can't really tell where the fixes should go and what I should erase. My SWIG is version 1.3.31 and it's in /usr/bin so I think it came with the system. I don't install things there.
- By jlnr (dev) Date 2010-04-30 17:17
Oh, I always just install swig from hand, because in a move of sudden intelligence, its whole make hell builds into /usr/local by default. If you don't want to install it, just disable that Xcode build step, because I always check in the file generated here anyway.
- By erisdiscord Date 2010-04-30 18:11 Edited 2010-05-01 01:01
Aha, thank you. I will try disablng that step as soon as I get a chance to try it. I'll whine at you some more if I hi another snag. :) I may yet come to fully appreciate Xcode!

UPDATE: Yes, I've managed to get it to build. Had to add /usr/local/include/ruby-1.9.1 and a subdirectory to the include paths and turn off i386 and ppc (since I don't have a universal Ruby 1.9). Builds cleanly now that I've done all that and disabled the SWIG step, though.

As for me, I have been using Homebrew for a package manager lately and it seems to keep out of my business pretty well. Linking to system libraries where available (unlike, say, MacPorts) is a kindness unheard of in the world of package management.
- By jlnr (dev) Date 2010-05-02 08:07
I think I just finished the RubyGosu App.app template and RubyGosu itself.

If you want to try building it from source, you can get the correct Ruby 1.9 installed by extracting the source of 1.9.2pre1 into mac/Ruby/source and then running "rake ruby19:build" from the root. After that, the Xcode projects should just pick up the libs and includes from that. It will not pollute your system.
- By erisdiscord Date 2010-06-08 08:44
I dug into Gosu's source again and added a rudimentary sort of transformations system to the C++ side. Graphics has two new functions: beginTransform(Gosu::Transform) and endTransform. The Transform class basically wraps an arbitrary OpenGL matrix and provides helper methods to generate rotation, translation and scaling in two dimensions.

As it's implemented, DrawOpQueue maintains a list of transformations and each DrawOp gets a snapshot of that list when it goes into the queue. It's copy-heavy and does glPushMatrix and glPopMatrix for each transformation, so it could probably be optimised a lot. I probably should also have named the class Matrix and made it act like one.

I have made no attempt at Ruby bindings. Or rather, I attempted and got frustrated with Swig and gave up. :)

Here's a patch that I made against the GitHub repository at revision 7ab7a0cc7a1442a88c259448c491d5ba2c738545. I did this all while I should have been sleeping (it's 4:43 here now), so please go easy on me!

Oh yeah, and the patch doesn't include changes to the Xcode project file or anything else really. I'll get on that after I've caught up on lost sleep.
Attachment: gosu-transform.diff - Gosu transformations patch (4k)
- By jlnr (dev) Date 2010-06-08 13:40 Edited 2010-06-08 13:50
Awesome—I just came back from traveling and that is the next feature I wanted to tackle! I'll put a deadline on it for this weekend.

Not sure about using a dedicated class vs. using an array, somehow I am afraid of introducing a Matrix class. Sounds like a gazillion operators and design decisions to waste time on to make it perfect… (You forgot the Transform class anyway ;) )

Question: Does anyone know a vector/matrix math library for Ruby? As long as Gosu is easy to use with popular libraries, I'm for using arrays :)
Question 2: transform(foo) do…end? Also, helper methods that return matrices or directly have rotate(a) do…end (same for scale, translate) as shortcuts? I think rotate(45) { level.draw } looks pretty sweet in terms of readability.
- By erisdiscord Date 2010-06-08 15:50 Edited 2010-06-08 16:06
Oh crap, see, I forgot to even add it to the git repository. Here it is, but it's nothing spectacular. I resorted to using friend class ugliness to restrict the begin and end methods from the user. I can't say C++ is my strong point. :)

As for Ruby, I'm pretty sure there are Matrix and Vector classes in the standard library, and both of them can be had by a simple require 'matrix'. Ruby matrices are row-major and OpenGL matrices are column-major, so this will have to be accounted for either in your code or ours.

Meanwhile, If you're going to offer dedicated rotate, translate &c operations on the Ruby side, you should still offer a generic transform method that accepts arbitrary transformation matrices also; I'm sure someone will find something awesome to do with that. :)
Attachment: Transform.hpp - Gosu/Transform.hpp (1k)
Attachment: Transform-1.cpp - GosuImpl/Graphics/Transform.cpp (1k)
- By jlnr (dev) Date 2010-06-10 04:59 Edited 2010-06-19 14:04
I just committed it. I replaced the Transform class by a typedef to boost::array<16, double>. That makes it pretty clear that everyone looking for matrix math should consult another library for that :)

In Ruby, it works as

class Window
    # Rotates everything drawn in the block around (0, 0).
    def rotate(angle, around_x = 0, around_y = 0, &drawing_code); end
   
    # Scales everything drawn in the block by a factor.
    def scale(factor, &drawing_code); end
   
    # Scales everything drawn in the block by a factor for each dimension.
    def scale(factor_x, factor_y, &drawing_code); end
   
    # Moves everything drawn in the block by an offset in each dimension.
    def translate(x, y, &drawing_code); end
   
    # Applies a free-form matrix rotation to everything drawn in the block.
    def transform(m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12, m13, m14, m15, &drawing_code); end
end


In C++, there is a pushTransform/popTransform method pair in Window and three helper functions Gosu::scale, Gosu::translate and Gosu::rotate, which return a Gosu::Transform that can be passed.

I also changed the way it works internally, now Gosu remembers all matrices that have been used during rendering for each DrawOp and only pushes/pops it when necessary. That should actually make Gosu *faster* in the long run because I scale stuff by hand in a number of places, that can be done by a matrix (OpenGL) now. I hope that's going to be noticeable on the iPhone.

Thanks again. :)

Edit, future note: These will be free functions in module Gosu rather than Window methods later, when the dependency on the Window has been moved to Gosu's internals. Backwards compatibility will be there for a long time though.
- By erisdiscord Date 2010-06-10 17:14
Julian, you, sir, are a god among men. This will make my life a lot easier when I want to make composite objects too. Thanks for adding it. :)
Up Topic Gosu / Gosu Exchange / Built-in scrolling and transformations in general

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill