Tweening and Interpolating OpenGL Objects with Sch

posted in Programmology
Published January 29, 2010
Advertisement
Posted from http://jlongster.com/blog/2010/01/29/tweening/

--



I want to share something I just finished implementing for my 3d
graphics framework in Scheme which I am building my iPhone game
off of. Keep in mind that I am not intentionally building a
separate 3d graphics framework as a general platform, but I am
building only what is needed to get my game out the door, so it is
rough and the API is awkward and not well thought out.

Given my custom scene objects which abstract away OpenGL
rendering, I built a system for interpolating these objects'
properties (such as scale and color) over time, or "tweening." This turned out to be
incredibly useful. Here's the story.

(Note: I'm not extremely confident I'm using the word "tween"
correctly, but if not, I'm re-defining it.)

What's the problem?



The problem was that I needed a system for presenting the title
screen, level titles, and a 2d overlay for my 3d iPhone game.
These 2d elements should smoothly fade in and out of the screen
over time. I don't need a full GUI toolkit, but I'm more
interested in smooth animations when levels change, text appears,
etc.

This turned out to be a huge bear in my old system: because of the
primitive nature of my Scheme graphics libraries, I had to
manually do all the fading every frame for every object. Horrible.

Establishing the Tween



I started re-hauling parts of my system. It started small, only
supporting alpha fading of 2d objects. However, as I worked
on more and more, I realized all properties should be tweenable.

So I set out to implement tweening for any object properties, such
as position, rotation, scale, color, and anything else. And why
not let 3d objects be tweened as well? No reason not to!

It turns out that this is a good way to specify simplistic
behaviors of objects. I'm sure anyone experienced in developing
games is used to this. I've seen various forms of tweening in game
frameworks, especially particle systems. I'm not sure if I've seen
it expressed this way though. Is there a formal or established
name for this, and any examples of how most engines do this?

Once the system was in place, I implemented many types of tweens
such as "linear," "quadratic", "cubic", and "bounce", each
defining a different interpolation function based on time. More
details about these types, including graphics of each function,
can be seen in
this documentation.

Examples



Here are some examples from my tweening system. I will show you
code and a video of what it does. I will not go into details about
the code because it isn't public yet, and it's really rough, but
hopefully it will help expose what I'm trying to achieve.

Types of Tweens



Here I show you all the types of tweens my system currently
supports. A quick list would include linear, quadratic, cubic,
bouncing, and all the variations of applying them at the end or
the start of the interpolation.

        (begin          (define (tween-type type)            (scene-list-clear!)            (scene-list-add             (make-tween                           (make-2d-object               2d-perspective               position: (make-vec3d .1 (- .5 .025) 0.)               scale: (make-vec3d .05 (/ .05 1.5) 1.)               color: (make-vec4d 1. 1. 1. 1.))                             position: (make-vec3d .65 (- .5 .025) 0.)              color: (make-vec4d (+ (* (random-real) .5) .5)                                 (+ (* (random-real) .5) .5)                                 (+ (* (random-real) .5) .5)                                 (+ (* (random-real) .5) .5))              scale: (make-vec3d .2 (/ .2 1.5) 1.)              length: .7              type: type)))                  (tween-type 'linear))


">


Queued Tweens



You can queue up tweens as well so that one fires off as another one ends.

        (begin          (scene-list-clear!)                  (define tx (image-opengl-load "survived.png"))                  (let ((obj (make-2d-object                      2d-ratio-perspective                      position: (make-vec3d (- .5 .025) .1 0.)                      scale: (make-vec3d .05 .05 1.)                      color: (make-vec4d 1. 1. 1. 1.)                      rotation: (make-vec4d 0. 0. 1. 0.)                      texture: tx                      )))            (scene-list-add             (make-tween              obj              position: (make-vec3d (- .5 .025) .75 0.)              scale: (make-vec3d .4 .4 1.)              color: (make-vec4d 0. 1. 0. .5)              rotation: (make-vec4d 0. 0. 1. 90.)                      type: 'ease-out-bounce              length: 1.5                      on-finished:              (lambda ()                (scene-list-add                 (make-tween                  obj                  type: 'ease-inout-cubic                  position: (make-vec3d .8 .1 0.)                  scale: (make-vec3d .1 .1 1.)                  color: (make-vec4d 1. .5 0. 1.)                  length: 1.))                #f)))))    


">


3d Tween



There's no reason you can't apply tweens to a 3d object as well.

        (begin          (scene-list-clear!)                    (let ((obj (make-mesh-object                      3d-perspective                      mesh: sheep-mesh                      position: (make-vec3d 0. 0. 10.)                      rotation: (make-vec4d 0. 0. 1. 0.)                      scale: (make-vec3d 2. 2. 2.))))            (scene-list-add             (make-tween              obj              scale: (make-vec3d 6. 6. 6.)              rotation: (make-vec4d 0. 0. 1. 90.)              length: 1.              type: 'ease-out-cubic              on-finished:              (lambda ()                (thread-sleep! 1.)                (scene-list-add                 (make-tween                  obj                  scale: (make-vec3d .7 .7 .7)                  type: 'ease-inout-cubic                  on-finished:                  (lambda ()                    (scene-list-add                     (make-tween                      obj                      position: (make-vec3d 0. 5. 10.)                      type: 'ease-out-bounce                      on-finished:                      (lambda ()                        (scene-list-add                         (make-tween                          obj                          type: 'ease-out-bounce                          position: (make-vec3d 0. -5. 10.)))                        #f)))                    #f)))                #f)))))    


">


Improvements



I'm still wrapping my head around how to implement a full motion
system with this. As of now, if you specify a cubic tween between
point (x, y) to (x1, y1), it will perform a cubic interpolation on
both the x and y as a function of time. The result is that the
object moves in a straight line from point A to point B, but the
speed of the movement is cubic.

For example, here's a cubic curve:



Even though it is a curve, if I apply cubic interpolation to
positions, which are 3-dimensional vectors with an x, y, and z
property, the object moves between point A and point B in a
straight line, with cubic-varying speed. If we tracked the
movement, it would look like this:



What if I wanted the object to curve when moving from point A to
point B? Surely I can express that with interpolations? The
problem lies in the fact that I've bound my interpolations of
multi-dimensional vectors to be constant across each dimension. I
would need to support different kinds of interpolations across
each x, y, and z axis. This sounds really neat, and I will have to
extend my system to support this later.

Now that I think about it, I think the special case of
interpolating movement should be handled with
[splines](http://en.wikipedia.org/wiki/Spline_\(mathematics\)). In fact,
it's probably downright inappropriate for me to interpolate
position this way. Splines are on my todo list for research.

Conclusion



Having this simplistic tweening system will make it easy for me to
apply smooth animations consistently across my iPhone game. It's
feels a lot more polished when I use this to fade in items and
slide them off the screen. I wouldn't really have thought of this
unless I forced myself to build a real game.

Building a game from scratch has forced me to learn
all kinds of things, and I find this experience invaluable. I'm
not building something simply because the idea of it sounds cool,
but I *have* to build my framework with certain features so that
my game will not only *run* but look good too. Having this dictate
my development forces me to drastically alter my ideals quickly as
I learn how games are laid out.

Scheme is a huge support in this kind of simplistic agile
development. Between its dynamic type system and functional
simplicity, I can constantly re-shape huge parts of the system as
if I'm a sculpter removing and adding huge chunks of clay. On top
of that, The Lisp methodology of interactive/iterative development
allows me to add details extremely quickly. The turn around time
for feeling reward from intense coding sessions is *really* fast
and extremely motivating!
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement
Advertisement