Search |
||
Introducing the SceneGraph ProjectPosted by hansmuller on January 8, 2008 at 6:50 PM PST
Introducing SceneGraphI haven't written a blog entry since January when I advertised the fledgling Swing Application Framework (JSR-296) project with an uncharacteristically brief item. Work on that kept me busy until summer, when the Java FX juggernaut got underway here at Sun. Since then I've devoted much of my time to leading a project we've called "Scenario" that provides the graphical runtime for Java FX Script. We're now a public project on java.net called scenegraph.dev.java.net . You'll find downloads of the the 0.4.1 version of the Scenario source and binaries and (very) sketchy javadoc on the site. The 0.x version number is intended to convey the fact that the API hasn't stabilized yet. It's sufficient for experimentation and the implementation was robust enough to support a port of the Java FX Script interpreter, which we've showcased with a Scenario version of the FXPad Demo . Obviously, we don't recommend putting anything based on Scenario into production. Not yet. The code is being made available under the GPLv2 license. Passion about open source licenses is not something I possess so I'll leave the shouting about the implications of this choice to others. Suffice it to say that, per my limited understanding of these matters, you're free to share the code, and in the process of developing it further. We're moving our discussions of the API to the newly minted Scene Graph java.net forum . If you're interested in the project's evolution, that would be a good place to start looking. There are quite a few engineers working on Scenario, most of whom have made bigger contributions to the new software than I have, and you'll be hearing from them in their own blogs before too long. For now, what I'd like to do is to provide an introduction to the new Java APIs and just one demo. The team has written a whole raft of demos and we'll be opening up a subproject before too long, that contains the entire demo catalog. DemoAll of the examples that follow are part of a demo, each one occupies a tab. If you press "control-T" after clicking on a demo, you'll get a nice interactive tree view of the scene graph's structure, thanks to Amy Fowler for that! So press the orange button to launch the demo.
Note also: the demo scales the selected example scene to fit.
This is done with a small extension to Basics, Hello World
A scene graph, really a tree for now, is a data structure you
create from leaf nodes that represent visual elements
like 2D graphics and Swing components, filter nodes that
represent visual state, like 2D transforms and composition, and
group nodes that manage a list of children. All nodes
are subclasses of
SGText text = new SGText();
text.setText("Hello World");
JSGPanel panel = new JSGPanel();
panel.setScene(text);
panel.setPreferredSize(new Dimension(640, 480));
// create a JFrame, add the panel to it, etc..
One unusual line from the previous example, from a Swing
programmer's perspective, is that we've explicitly set the
preferred size of the
SGText text = new SGText();
text.setText("Hello World");
text.setFont(new Font("SansSerif", Font.PLAIN, 36));
text.setAntialiasingHint(RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
text.setFillPaint(Color.WHITE);
JSGPanel panel = new JSGPanel();
panel.setBackground(Color.BLACK);
panel.setScene(text);
panel.setPreferredSize(new Dimension(640, 480));
More Hello World: Groups and Shapes
Most of the Scenario classes are simple Java Beans. They provide null (no parameters) constructors and mutable properties. The API is intended to be "minimal" in the sense that it exposes the capabilities of the underlying Java 2D and Swing classes but does attempt to substantially simplify or abstract them. Scenario is intended to serve as the basis for higher level abstractions that require a scene graph, notably Java FX Script. For that reason, you may find programming against the Scenario API directly to be a bit tedious. Creating your own abstractions that simplify creating scene graphs is definitely the order of the day.
To add a border to the simple Hello World scene, we'll write a
method that takes any scene graph node and puts a garish rounded
rectangle border behind it. This example demonstrates using the
SGNode createBorder(SGNode node) {
Rectangle2D nodeR = node.getBounds();
double borderWidth = 10;
double x = nodeR.getX() - borderWidth;
double y = nodeR.getY() - borderWidth;
double w = nodeR.getWidth() + (2 * borderWidth);
double h = nodeR.getHeight() + (2 * borderWidth);
double a = 1.5 * borderWidth;
SGShape border = new SGShape();
border.setShape(new RoundRectangle2D.Double(x, y, w, h, a, a));
border.setFillPaint(new Color(0x660000));
border.setDrawPaint(new Color(0xFFFF33));
border.setDrawStroke(new BasicStroke(borderWidth / 2.0));
border.setMode(SGShape.Mode.STROKE_FILL);
border.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_ON);
SGGroup borderedNode = new SGGroup();
borderedNode.add(border);
borderedNode.add(node);
return borderedNode;
}
Adding a the border doesn't change the code that creates the scene graph very much:
SGText text = new SGText();
text.setText("Hello World");
// same as before ...
panel.setScene(createBorder(text));
Rotating Hello World: Transforms
As you can see in the previous example, the
To rotate a scene graph node around a point, you have to
assemble a chain of three transforms that: translate the node so
that the rotation point is at the origin, rotate the desired
amount, translate the node back to its original location.
Transforms are created with
SGTransform createRotation(SGNode node) {
Rectangle2D nodeR = node.getBounds();
double cx = nodeR.getCenterX();
double cy = nodeR.getCenterY();
SGTransform toOriginT = SGTransform.createTranslation(-cx, -cy, node);
SGTransform.Rotate rotateT = SGTransform.createRotation(0.0, toOriginT);
return SGTransform.createTranslation(cx, cy, rotateT);
}
To use the
SGTransform scene = createRotation(node);
SGTransform.Rotate rotateT = (SGTransform.Rotate)scene.getChild();
rotateT.setRotation(...);
Images and More Layout
Images can be incorporated in a scene graph with the
SGNode createRow(SGNode... nodes) {
double rowHeight = 0.0;
for(SGNode node : nodes) {
rowHeight = Math.max(rowHeight, node.getBounds().getHeight());
}
SGGroup row = new SGGroup();
double x = 0.0;
double gap = 8.0;
for(SGNode node : nodes) {
Rectangle2D nodeR = node.getBounds();
double y = (rowHeight - nodeR.getHeight()) / 2.0;
double dx = x - nodeR.getX();
double dy = y - nodeR.getY();
SGTransform xlate = SGTransform.createTranslation(dx, dy, node);
row.add(xlate);
x += nodeR.getWidth() + gap;
}
return row;
}
The code to create the SGImage node and the overall scene looks like this:
SGNode createEarth() {
BufferedImage image = null;
// ... code to load the image file
SGImage sgImage = new SGImage();
sgImage.setImage(image);
return sgImage;
}
// ...
SGNode row = createRow(createHelloWorldText(), createEarth());
SGNode scene = createBorder(row);
JSGPanel panel = new JSGPanel();
panel.setScene(scene);
Handling Input
All nodes can handle mouse and keyboard events and the support for
doing so is very similar to what AWT/Swing provides. For
example to make a node handle mouse events you add an
In this example, we've added a mouse listener to the
earth node in the scene from the previous example and restored
the support for rotating the scene. Dragging the earth with the
mouse rotates the scene. Here's the code that sets up the scene
and its
SGNode earth = createEarth();
SGNode row = createRow(createHelloWorldText(), earth);
SGTransform scene = createRotation(createBorder(row));
final SGTransform.Rotate rotateT = (SGTransform.Rotate)scene.getChild();
SGMouseListener changeRotation = new SGMouseAdapter() {
@Override public void mouseDragged(MouseEvent e, SGNode node) {
Rectangle2D r = e.getComponent().getBounds();
double x = e.getX() - r.getCenterX();
double y = e.getY() - r.getCenterY();
rotateT.setRotation(Math.atan2(y, x));
}
};
earth.addMouseListener(changeRotation);
JSGPanel panel = new JSGPanel();
panel.setScene(scene);
Although there's a great deal more to say by way of introducing Scenario, this blog entry has grown long enough that I'd better just take the same tack that Chet did , and declare this "Part 1". The code for the examples can be found here: http://weblogs.java.net/blog/hansmuller/archive/Intro.java A easy to build version will appear along with the other Scenario demos, later this month. »
Comments
Comments are listed in date ascending order (oldest first)
Submitted by throwable on Tue, 2008-01-29 10:02.
It is rather difficult to build and manipulate with scene graph using AffineTransform nodes. I think it may be simplified for example by integrating of SGNode with SGTransform allowing user to use simple bean methods like setX(), setY(), rotate(), scale(), etc... The main reason is that every "visual" node must be positioned inside scene and thus will always have corresponding translation node.
Submitted by hansmuller on Wed, 2008-01-09 11:18.
I've removed the alarming stack trace print that occurs when
MasterTimer can't read it's System properties. It was harmless
and unneccessary. I wish we had a better way to test sandboxed
applications.
Submitted by mikeazzi on Wed, 2008-01-09 12:26.
When I resize the frame I notice some lines flashing around the edges, giving it a not so smooth resizing feel. Otherwise it looks great. I am using Windows XP with Update N b08.
Submitted by diverson on Wed, 2008-01-09 14:37.
Thanks for the intro, Hans.
Regarding the rotation example: if the rotation were static (or will be rendered many more times than it will be changed) then optimally you would want to concatenate the three transforms into one transform. Will the scene graph be able to detect this and optimize automatically similar to the way Java2D handles images for you? Or, perhaps more realistically, will there be a method we can call to perform such optimizations? Some scene graphs (Java3D, for example) have this ability to "compile" scene graphs.
Submitted by paksegu on Wed, 2008-01-09 21:10.
Nice, Definately Nice...Sun is definately stepping is its game with this JavaFx thing...No frills just JAVA
Submitted by swv on Thu, 2008-04-10 12:05.
Datapoint-
Ctrl-T worked fine.
mouse didn't respond to mouse at all.
XP64 / 6N / run as webstart
Submitted by mattnathan on Wed, 2008-01-09 08:24.
I get the same exception under linux
Java Web Start 1.6.0_03
Using JRE version 1.6.0_03 Java HotSpot(TM) Server VM and ctrl+t doesn't work for me either. Also the spacing between the characters in rotated text seems to be affected by rounding errors of some kind, this is most noticeable under the mouse tab where the characters seem to 'wobble' when rotated by small values. Other than that I'm looking forward to what comes out of this project.
Submitted by pdoubleya on Wed, 2008-01-09 03:16.
Hi Hans--nice overview, thanks.
I have the same problems with the demo--Ctrl T doesn't work for me. But the panels display correctly.
Also, in the demo code, the first method, createFill(), sets anti-aliasing on but only fills a shape with a black color--what does AA mean in the context of a black fill?
The AA method call makes me think there's room for (maybe the community to offer) some cover methods like "enableAA()" as an alternative to "setAntialiasingHint(RenderingHints.VALUE_TEXT_ANTIALIAS_ON)". The number of method calls involved makes me think an "extension" API designed with fluent interfaces might make this more fun to program with--JMock has been a real inspiration for me as regards fluent interfaces. Those are minor, and something a community member could take on to simplify use of the API.
Looking forward to looking into the framework further, keep up the good writing.
Thanks, Patrick
Submitted by twilightworkshop on Wed, 2008-01-09 08:37.
CTRL T does not work on OSX either.
And I have the same exception
Submitted by quintesse on Wed, 2008-01-09 01:39.
Control-T doesn't seem to do anything? (at least on my system: Windows XP, Java 6 (1.6.0_03))
Submitted by tball on Wed, 2008-01-09 10:10.
If you check the MasterTimer.java source (Joe-Bob says check it out!), you'll find that the exception is a harmless diagnostic. I've asked that it be removed, since everyone (including myself) assume it is a fatal error.
Submitted by hansmuller on Wed, 2008-01-09 10:40.
OK: control-T doesn't work for me either! It does work when I launch
the application locally, but not with the web started version. Will
figure what's wrong, sorry about that.
Submitted by kirillcool on Wed, 2008-01-09 01:38.
java.security.AccessControlException: access denied (java.util.PropertyPermission com.sun.scenario.animationTimeline.nogaps read)
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPermission(Unknown Source)
at java.lang.SecurityManager.checkPropertyAccess(Unknown Source)
at java.lang.System.getProperty(Unknown Source)
at java.lang.Boolean.getBoolean(Unknown Source)
at com.sun.scenario.animation.MasterTimer.(MasterTimer.java:54)
at com.sun.scenario.animation.Timeline.addFrameJob(Timeline.java:52)
at com.sun.scenario.scenegraph.JSGPanelRepainter.(JSGPanelRepainter.java:53)
at com.sun.scenario.scenegraph.JSGPanelRepainter.(JSGPanelRepainter.java:45)
at com.sun.scenario.scenegraph.JSGPanel.markDirty(JSGPanel.java:525)
at com.sun.scenario.scenegraph.JSGPanel.setScene(JSGPanel.java:102)
at demo.intro.Intro$ScalingSGPanel.setScene(Intro.java:366)
at demo.intro.Intro.createMainPanel(Intro.java:336)
at demo.intro.Intro.startup(Intro.java:347)
Submitted by hansmuller on Mon, 2008-01-14 11:48.
The problem with control-T appeared to have been that the demo never
requested focus. That was easily fixed however when I tried the
(supposedly) updated app, no soap. I was somewhat busy with another
project at the time and asked
Igor Kushnirskiy
to take a look at the problem. He pointed out that I hadn't been
updating the right .jar file. He might have also pointed out that I'm
a knucklehead, but tactful man that he is, he let the facts speak for
themselves.
So the good news is that Amy's scene graph viewer now appears on control-T,
if you've clicked on the demo first.
Submitted by hansmuller on Mon, 2008-01-14 11:58.
Posted by: pdoubleya on January 09, 2008 at 02:16 AM
Also, in the demo code, the first method, createFill(), sets
anti-aliasing on but only fills a shape with a black color--what does
AA mean in the context of a black fill?
For the black filled arcs that appear in the "smiley" demo I didn't
describe, the AA flag will cause the value of pixels that only
partially overlap the curve to have an interpolated value that's
relative to the color already there. You can see it if you use a
screen magnifier.
About enabling AA in general: I agree that doing so per node is clunky,
albeit flexible. It would be useful to be able to
enableAA(),
as you say, for a subtree.
Submitted by hansmuller on Mon, 2008-01-14 12:02.
Posted by: diverson on January 09, 2008 at 01:37 PM
Regarding the rotation example: if the rotation were static (or will
be rendered many more times than it will be changed) then optimally
you would want to concatenate the three transforms into one
transform. Will the scene graph be able to detect this and optimize
automatically similar to the way Java2D handles images for you? Or,
perhaps more realistically, will there be a method we can call to
perform such optimizations? Some scene graphs (Java3D, for example)
have this ability to "compile" scene graphs.
I agree: caching the relatively static parts of the scene graph in a
form that's much closer to what's needed for rendering is definitely
the path to better performance. We're working on that.
|
||
|
|