|
|
||
Hans Muller's BlogJanuary 2008 ArchivesIntroducing the SceneGraph ProjectPosted by hansmuller on January 08, 2008 at 06:50 PM | Permalink | Comments (16)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. | ||
|
|