|
|
||
Chet Haase's BlogBeen There, Scene ThatPosted by chet on January 04, 2008 at 02:27 PM | Comments (9)(This is Part 1 of a two-part blog. It's been broken in two for no particularly good reason other than it was getting a bit long for a single entry and I always like a bit of suspense and tension in my technical articles - don't you? . Look for Part 2 in the next few days) You may have already heard about the Scene Graph project that we released last month on java.net. In case you haven't heard about it yet, here's some information about it: We released the project last month on java.net. Since the team was pretty busy at the time (I was at JavaPolis talking about the project, among other things, and the team was cranking away on the actual code), we haven't really gotten around to blogging about it yet to tell people more about the project (hey, the code's out there - does anyone need anything more?). Fortunately for us, Josh seems to blog in his sleep, so there's at least been some information floating out there in the blogosphere. But maybe now that the library is available, we should actually talk about it to help everyone understand what it is, how it works, and what you can do with it. Hans Muller is working an intro/overview on the subject. Chris Campbell has a blog in the works on scene graph image effects. And I was tasked with (surprise, surprise) a discussion of animation. TimingFramework++I think that the best way to describe the animation engine in the scene graph project is that it is like the next major version of the Timing Framework. Maybe it's because I'm a graphics geek, but I always find it easier to understand concepts through pictures. So here's a technical diagram illustrating how the scene graph animation engine relates to the Timing Framework. It's a bit technical, but hopefully you'll get the point.
The similarlity between the two libraries is not, obviously, a coincidence. We started with the 1.0 version of the Timing Framework and changed it to suit our needs for the scene graph, where "suit our needs" means that we refactored the API to improve upon various things and added functionality that has so far been lacking in the Timing Framework. Rather than explain how the Timing Framework works, I'd encourage you to check out the project, the docs linked on the project site, the demos for that project, and the copious other amounts of information on the library (including demos, chapters in the Filthy Rich Clients book, and so on). I'll assume that anyone reading past here has some passing familiarity with the Timing Framework. I'll step through the major categories of differences between the TimingFramework and this new animation library, to give a sense of what's new in the scene graph. I'll show some sample code along the way, although I'd encourage you to check out the Nodes example on the scene graph site to see animation usage in action. Refactoring: What's Changed?Clip is the new AnimatorAfter living for way too long with the bureaucratically dull name
Besides, Clip has a handful of factory methods for the common cases:
create(int duration, double repeatCount, Object target,
String property, T... keyValues)
create(int duration, double repeatCount, Property property, T... keyValues)
create(int duration, double repeatCount, TimingTarget target)
create(int duration, Object target, String property,
T... keyValues)
create(int duration, Property property, T... keyValues)
create(int duration, TimingTarget target)
(The Property object will be explained later, but think of it as a replacement for the previous PropertySetter object in the Timing Framework). Like Animator, Clips begin running when the start() method is called (although Clip also has more involved and powerful scheduling capabilities, discussed later): // Fade in myObject over a half second
Clip clip = Clip.create(500, myObject, "opacity", 0f, 1f);
clip.start();
PropertySetter is now BeanPropertyThe old way of having the animation engine automatically set object/property values was through the PropertySetter class, which was constructed with a given object and property-name pair. We felt that this was a bit too constrictive in a world where there might be other ways that one might want to set values on objects; what if someone has an object without JavaBean-like getter/setters on it? So we defined a new interface, public interface Property<T> {
public <T> getValue();
public void setValue(T value);
}
Then we refactored the old public class BeanProperty<T> implements Property<T> {
public BeanProperty(Object object, String propertyName);
public <T> getValue();
public void setValue(T value);
}
Also, note that we separated the functionality of setting values on a Property object from animating those values. These concepts overlapped in the previous PropertySetter object, which was an implementation of TimingTarget and handled both animating the value in question as well as setting it on the specified object. Now, the new KeyFrames object handles animating a property, and the resulting value is then sent into the appropriate Property object. InterpolatorsOne of my favorite refactorings from the TimingFramework was the change that Chris
Campbell made to interpolators. The base interface, public interface Interpolator {
public float interpolate(float fraction);
}
The change is that there used to be several implementation classes of
Interpolator ( public class Interpolators {
static Interpolator getLinearInstance();
static Interpolator getDiscreteInstance();
static Interpolator getSplineInstance(float x1, float y1, float x2, float y2);
static Interpolator getEasingInstance();
static Interpolator getEasingInstance(float acceleration, float deceleration);
}
The linear, discrete, and spline interpolators are virtually the same as
before, in both construction and
operation. But there is now an additional "easing" variety that takes the place
of the Also, note that Clip uses an easing interpolator by default (with the default factors of .2f/.2f), as opposed to the old linearly-interpolated Animator object. Linear interpolation makes more sense as a default from an analytical standpoint (it seems less arbitrary than some particular choice of easing factors), but frankly most of the animations that you'll create should be non-linearly interpolated, which means that you would either not get the behavior you should if you used the linear default or (as in all of my animation demos) you'd have to keep writing the same boilerplate code to set the easing factors to get the behavior right. KeyFramesThe previous structure of
Now there is a KeyFrames implement the TimingTarget interface, and are used as targets of an animation to animate a value and then set it on the Property object with which they were constructed. Note that KeyFrames objects are created either with an explicit Property object or with the object/property pair that is used to construct a BeanProperty object internally: static KeyFrames<T> create(Object target, String propertyName, KeyFrame<T>... keyFrames);
static KeyFrames<T> create(Object target, String propertyName, T... keyValues);
static KeyFrames<T> create(Property<T> property, KeyFrame<T>... keyFrames);
static KeyFrames<T> create(Property<T> property, T... keyValues);
Evaluator is now ComposerThe system in TimingFramework to handle interpolation between values of various
types uses the As in the TimingFramework, most types that you would care about animating are already handled by the system. But if you do need to animate a variable of a type that is unknown to the system, then you need to create your own Composer subclass and register it with the system. It will then be used whenever the system encounters that type and looks for an appropriate Composer. Instead of putting sample code for a here, I'll just defer to the JavaDocs and the source code, which do a good job of showing what any new subclass would need to do in order to work within this system. But assuming you've defined some custom Composer, MytypeComposer, registration is easy: Composer.register(new MytypeComposer());
TimingTrigger is replaced by Clip dependency schedulingOne piece of functionality that is completely gone is the old
addBeginAnimation(Animation anim);
addBeginAnimation(Animation anim, long offset);
addEndAnimation(Animation anim);
addEndAnimation(Animation anim, long offset);
(where Animation is the superclass of both Clip and the as-yet-undiscussed Timeline class). So, for example, you can have clipB start 100 ms after clipA ends with the following call: clipA.addEndAnimation(clipB, 100); Minor ChangesThere are various minor changes to the system that you will notice as you try it
out. It's not worth going through all of these (because it'd take me a long
time to go over the API to catch all of these changes, for one thing), but they
should be fairly self-explanatory when you encounter them. For example, the old
(That's it for this entry - check back next week for the gripping conclusion to this discussion, as we go over some of the new bits in the scene graph animation library that are not now part of the Timing Framework) Bookmark blog post: CommentsComments are listed in date ascending order (oldest first) | Post Comment
| ||
|
|