Hi Paul,
I've filed bug 4732956
- Raster z value set incorrectly when setDstOffset() or CLIP_IMAGE mode
is used
for problem (1).
Culling works fine if setDstOffset() and CLIP_IMAGE mode is not used.
For (2),(3) RasterTextLabel is contruct with alwaysOnTop = true
so depth buffer test and depth write is disable.
The menu option "alwaysOnTop" only change the Sphere renderingAttributes
but not the RasterTextLabel (which remain disable depth test and write).
Since rendering order is undefined unless OrderedGroup is used,
the behavior is not unexpected.
For (5) bug 4732965
- Raster CLIP_IMAGE mode cause flickering when part of it is clipped
For (4)
Doc. mention that position (x, y, z) set by user is in object space
(same as other vertex) and transformed to the device coordinates.
(x', y', z') which is used to put the raster. I don't see
any confusion here.
For (6), From Doc.
/**
* Sets the destination pixel offset of the upper-left corner of
* the rendered image relative to the transformed position. This
* pixel offset is added to the transformed raster position prior
* to rendering the image.
*/
The center of Raster, instead of upper left corner, is
shift to right (for positive X) and down (for position Y).
The is the convention use for image since the specification
mention upper left corner is (0, 0). As a result of center
shift right and down, it appears that image move left and up.
Thanks for your bug report.
- Kelvin
--------------------
Java 3D Team
Sun Microsystems Inc.
Paul Pantera wrote:
>
> OK, I've composed a small program demonstrating the problems I'm
> seeing. It has a sphere and a label both at the same position.
>
>> 1) Rasters ignoring front and back clipping planes.
>
>
>
> Zoom way in, zoom way out. The sphere disappears, the label
> never does.
>
>
>> 2) Rasters ignoring setDepthBufferEnable and always doing depth
>> buffer compare.
>
>
>
> This demo isn't showing this. Justin suggested that maybe it's a
> rendering order issue. When alwaysOnTop is true (depth buffering
> is disabled) the raster is always either entirely in front of
> the sphere or entirely behind it. Justin suggested I use
> OrderedGroup to make sure my rasters are rendered last.
>
> OrderedGroup may be the only way to do what I need to do.
> Since I can't control the depth of my rasters, I can use
> OrderedGroup to render my objects in a specific order so
> that objects in front of the scene obscure labels behind
> them. Any comments on this? Is the use of OrderedGroup
> going to slow me down? I guess I'll find out soon enough. . .
>
>> 3) A raster and another Shape transformed by the same transforms
>> end up at different depths.
>
>
> Use the menu to turn off alwaysOnTop. Zoom in and notice how the
> sphere comes right through the label. The label and the sphere
> are at the same position, so the label should always be in the
> center of the sphere. Depending on the position of the sphere,
> the label moves relative to it in Z.
>
>> 4) Documentation unclear about the meaning of a Raster's position.
>> (Perhaps more bugs here too, depending on the meaning.)
>
>
> I'm not setting the raster's position in this example program, for
> simplicity.
>
>> 5) CLIP_IMAGE causes flickering
>
>
> Move the sphere over to the left so the left side of the raster is
> clipped. Now spin the sphere and watch the label flicker.
>
>> 6) Sign problem with setDstOffset
>
>
> This may not be a bug, but you may want to document it because it's
> not clear to me why positive X and Y offsets move the raster up and
> to the left.
>
> -Paul
>
>
> ------------------------------------------------------------------------
>
> /*****************************************************************************
> * J3D.org Copyright (c) 2000
> * Java Source
> *
> * This source is licensed under the GNU LGPL v2.1
> * Please read http://www.gnu.org/copyleft/lgpl.html for more information
> *
> ****************************************************************************/
>
> package org.j3d.geom;
>
> // Standard imports
> import javax.media.j3d.*;
>
> import java.awt.AlphaComposite;
> import java.awt.Color;
> import java.awt.Font;
> import java.awt.FontMetrics;
> import java.awt.Graphics;
> import java.awt.Graphics2D;
> import java.awt.geom.Rectangle2D;
> import java.awt.image.BufferedImage;
>
> import javax.vecmath.Point3f;
>
> // Application specific imports
>
> /**
> * A text label for labelling objects on screen that uses a Java 3D Raster
> * to produce the overlay effect.
> * <p>
> *
> * If the label text is null, then no label will be displayed. All of the
> * setup will be done, but no raster will be created.
> * <p>
> *
> * The text label can come in a number of flavours depending on how you
> * configure it through the constructors. You may build a label that is
> * only static, always fixed size regardless of text length, and/or may
> * be hidden from other objects v always on the top. Once configured to one
> * of these versions, it cannot be changed.
> * <p>
> *
> * If running dynamic text, the internal images will only resize to a
> * larger size. That is, if a new string comes in that is smaller than
> * the original string, the image will stay the larger length than the
> * original. The idea is to reduce the amount of garbage generated. However,
> * in some instances this may not produce acceptable visual behaviour,
> * so the crop() method is introduced that will force the image size to
> * be reduced to the smallest possible size for the next time a string
> * is set. There are also variants on the setText() methods to do this
> * as well with a flag. Cropping is independent of the fixedSize flag but is
> * still subject to the dynamic flag on the constructor.
> *
> * @author Justin Couch
> * @version $Revision: 2.0 $
> */
> public class RasterTextLabel extends Shape3D
> {
> /** Message when the text label is not setup for dynamic changes */
> private static final String CANT_CHANGE_MSG =
> "Attempting to make a change to a label that was not originally " +
> "configured to be dynamic";
>
> /** The clear colour used to clear the background of the image */
> private static final Color CLEAR_COLOR = new Color(0, 0, 0, 0);
>
> /** The inset between the text and the border if one is required. */
> private static final int BORDER_INSETS = 2;
>
> /** The current color of the text */
> private Color textColor;
>
> /** The current color of the border. Null if not in use */
> private Color borderColor;
>
> /** The background color of the image */
> private Color backgroundColor;
>
> /** The font of the label. Null if using system default */
> private Font labelFont;
>
> /** The raster object that we put the information in */
> private Raster raster;
>
> /** The image component that holds the image used by the raster */
> private ImageComponent2D component;
>
> /**
> * Flag to say if the implementation should resize the underlying label
> * for each text update or not.
> */
> private boolean adjustImageSize;
>
> /** Flag indicating if this is a dynamic label */
> private boolean isDynamic;
>
> /** Underlying image used. Only set if this is a dynamic label */
> private BufferedImage textImage;
>
> /** The current image width */
> private int imageWidth;
>
> /** The current image height */
> private int imageHeight;
>
> /**
> * Create a new blank label with no text. It is located at the origin. It
> * is assumed to be dynamic and always on top.
> */
> public RasterTextLabel()
> {
> this(null, null, true, true, 0, 0, 0, null, null);
> }
>
> /**
> * Create a new blank label with the given text located at the origin.
> * If the text color is not specified, white is used and the code assumes
> * static text, it will always be on top.
> *
> * @param label The string to use on the label
> * @param col The text color to be drawn in
> */
> public RasterTextLabel(String label, Color col)
> {
> this(label, col, false, true, 0, 0, 0, null, null);
> }
>
> /**
> * Create a new blank label with the given text located at the origin.
> * If the text color is not specified, white is used and the code assumes
> * static text, it will always be on top.
> *
> * @param label The string to use on the label
> * @param col The text color to be drawn in
> * @param alwaysOnTop true if this should never be obscured by content
> */
> public RasterTextLabel(String label, Color col, boolean alwaysOnTop)
> {
> this(label, col, alwaysOnTop, false, 0, 0, 0, null, null);
> }
>
> /**
> * Create a new blank label with the given text located at the origin.
> * If the text color is not specified, white is used.
> *
> * @param label The string to use on the label
> * @param col The text color to be drawn in
> * @param dynamic True if this will change text over time
> * @param alwaysOnTop true if this should never be obscured by content
> */
> public RasterTextLabel(String label,
> Color col,
> boolean alwaysOnTop,
> boolean dynamic)
> {
> this(label, col, alwaysOnTop, dynamic, 0, 0, 0, null, null);
> }
>
> /**
> * Create a new blank label with the given text located at a specific
> * point in 3D world coordinates. The code assumes a static label.
> *
> * @param label The string to use on the label
> * @param col The text color to be drawn in
> * @param alwaysOnTop true if this should never be obscured by content
> * @param x The x world coordinate to place the label
> * @param y The y world coordinate to place the label
> * @param z The z world coordinate to place the label
> */
> public RasterTextLabel(String label,
> Color col,
> boolean alwaysOnTop,
> float x,
> float y,
> float z)
> {
> this(label, col, alwaysOnTop, false, x, y, z, null, null);
> }
>
> /**
> * Create a new blank label with the given text located at a specific
> * point in 3D world coordinates.
> *
> * @param label The string to use on the label
> * @param col The text color to be drawn in
> * @param x The x world coordinate to place the label
> * @param y The y world coordinate to place the label
> * @param z The z world coordinate to place the label
> * @param alwaysOnTop true if this should never be obscured by content
> * @param dynamic True if this will change text over time
> */
> public RasterTextLabel(String label,
> Color col,
> boolean alwaysOnTop,
> boolean dynamic,
> float x,
> float y,
> float z)
> {
> this(label, col, alwaysOnTop, dynamic, x, y, z, null, null);
> }
>
> /**
> * Create a new blank label with the given text located at a specific
> * point in 3D world coordinates and an option to show a border and
> * selected font. If the border color is specified, it will show a 1
> * pixel wide border in that color. If no font is defined, the system
> * default font will be used.
> *
> * @param label The string to use on the label
> * @param col The text color to be drawn in
> * @param x The x world coordinate to place the label
> * @param y The y world coordinate to place the label
> * @param z The z world coordinate to place the label
> * @param border The color to use for the border or null for none
> * @param font The font to draw the string in or null for default
> * @param dynamic True if this will change text over time
> */
> public RasterTextLabel(String label,
> Color col,
> boolean alwaysOnTop,
> boolean dynamic,
> float x,
> float y,
> float z,
> Color border,
> Font font)
> {
> adjustImageSize = false;
>
> textColor = (col != null) ? col : Color.white;
> borderColor = border;
> labelFont = font;
> isDynamic = dynamic;
>
> Appearance app = new Appearance();
> RenderingAttributes ra = new RenderingAttributes();
> ra.setAlphaTestFunction(RenderingAttributes.GREATER);
>
> if(alwaysOnTop)
> {
> ra.setDepthBufferEnable(false);
> ra.setDepthBufferWriteEnable(false);
> }
>
> app.setRenderingAttributes(ra);
> setAppearance(app);
>
> // create a disposable 1x1 image so that we can fetch the font
> // metrics associated with the font and text label. This will allow
> // us to determine the real image size. This is kludgy, but I can't
> // think of a better way of doing it!
> BufferedImage tmp_img =
> new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
>
> Graphics graphics = tmp_img.getGraphics();
> FontMetrics fm;
>
> if(labelFont == null)
> fm = graphics.getFontMetrics();
> else
> fm = graphics.getFontMetrics(labelFont);
>
> int width = 0;
> int height = 0;
>
> if(label == null)
> {
> // No label? Create an empty (default) raster object then. This
> // should result in nothing being rendered on screen, but leaves
> // us a placeholder for later.
> raster = new Raster();
> raster.setPosition(new Point3f(x, y, z));
> }
> else
> {
> // now we have the metrics, let's work out how big the label is!
> Rectangle2D dimensions = fm.getStringBounds(label, graphics);
>
> graphics.dispose();
> tmp_img.flush();
> tmp_img = null;
>
> width = (int)dimensions.getWidth();
> height = (int)dimensions.getHeight();
> int ascent = fm.getMaxAscent();
>
> if(border != null)
> {
> width += BORDER_INSETS * 2 + 2; // one pixel border * 2
> height += BORDER_INSETS * 2 + 2;
> }
>
> textImage = new BufferedImage(width,
> height,
> BufferedImage.TYPE_INT_ARGB);
>
> graphics = textImage.getGraphics();
>
> renderImage(graphics, label, width, height, ascent);
>
> graphics.dispose();
>
> component = new ImageComponent2D(ImageComponent2D.FORMAT_RGBA,
> textImage);
>
> raster = new Raster(new Point3f(x, y, z),
> Raster.RASTER_COLOR,
> 0,
> 0,
> width,
> height,
> component,
> null);
>
> // clear the reference if not dynamic
> if(!dynamic)
> {
> component = null;
> textImage = null;
> }
> }
>
> if(dynamic)
> {
> component.setCapability(ImageComponent2D.ALLOW_IMAGE_WRITE);
> raster.setCapability(Raster.ALLOW_SIZE_WRITE);
> raster.setCapability(Raster.ALLOW_IMAGE_WRITE);
> raster.clearCapabilityIsFrequent(Raster.ALLOW_IMAGE_WRITE);
> }
>
> setGeometry(raster);
> }
>
> /**
> * Set the label string that is to be rendered. This maintains the
> * current text color. If this was not set up to be a dynamic image, an
> * exception is thrown.
> *
> * @param text The string to be rendered
> * @throws IllegalStateException The label was not set up to be dynamic
> * in the constructor
> */
> public void setText(String text) throws IllegalStateException
> {
> if(!isDynamic)
> throw new IllegalStateException(CANT_CHANGE_MSG);
>
> updateText(text);
> }
>
> /**
> * Set the label string that is to be rendered with the option of croping
> * it to the length of the string. This maintains the current text color.
> * If this was not set up to be a dynamic image, an exception is thrown.
> *
> * @param text The string to be rendered
> * @param crop true to crop the underlying raster
> * @throws IllegalStateException The label was not set up to be dynamic
> * in the constructor
> */
> public void setText(String text, boolean crop) throws IllegalStateException
> {
> if(!isDynamic)
> throw new IllegalStateException(CANT_CHANGE_MSG);
>
> if(crop)
> textImage = null;
>
> updateText(text);
> }
>
> /**
> * Set the label string that is to be rendered and changes the color
> * to the new value. If this was not set up to be a dynamic image, an
> * exception is thrown.
> *
> * @param text The string to be rendered
> * @param col The new color to be used or null for the default (white)
> * @throws IllegalStateException The label was not set up to be dynamic
> * in the constructor
> */
> public void setText(String text, Color col) throws IllegalStateException
> {
> if(!isDynamic)
> throw new IllegalStateException(CANT_CHANGE_MSG);
>
> textColor = (col != null) ? col : Color.white;
>
> updateText(text);
> }
>
> /**
> * Set the label string that is to be rendered and changes the color
> * to the new value. If this was not set up to be a dynamic image, an
> * exception is thrown.
> *
> * @param text The string to be rendered
> * @param col The new color to be used or null for the default (white)
> * @param crop true to crop the underlying raster
> * @throws IllegalStateException The label was not set up to be dynamic
> * in the constructor
> */
> public void setText(String text, Color col, boolean crop)
> throws IllegalStateException
> {
> if(!isDynamic)
> throw new IllegalStateException(CANT_CHANGE_MSG);
>
> if(crop)
> textImage = null;
>
> textColor = (col != null) ? col : Color.white;
>
> updateText(text);
> }
>
>
> /**
> * Set the condition of whether the implementation should resize the
> * canvas after each new label is set or just stick to a fixed size
> * canvas. A fixed size label is useful when you are making fast updates
> * such as a counter. When this is called, the label will not be resized
> * from it's current dimensions. This may be changed dynamically and will
> * only take effect next time a text string is set and the size is based
> * on the biggest image used to date, not on the next string that is
> * set.
> *
> * @param fixed true if the label size should remain fixed
> * @throws IllegalStateException The label was not set up to be dynamic
> * in the constructor
> */
> public void fixSize(boolean fixed)
> throws IllegalStateException
> {
> if(!isDynamic)
> throw new IllegalStateException(CANT_CHANGE_MSG);
>
> adjustImageSize = fixed;
> }
>
> /**
> * Crop the image used for the raster to the length of the string the next
> * time a string is set. This will not crop the current string, only the
> * next one.
> *
> * @throws IllegalStateException The label was not set up to be dynamic
> * in the constructor
> */
> public void crop()
> throws IllegalStateException
> {
> if(!isDynamic)
> throw new IllegalStateException(CANT_CHANGE_MSG);
>
> // easiest way to force a rebuild is to remove the textImage reference
> // completely and treat it like a new image with no text set.
> textImage = null;
> }
>
> /**
> * Update the raster to display the new text.
> *
> * @param label The new text string to draw
> * @param fontChanged true if the font changed
> */
> private void updateText(String label)
> {
> // If we have no text to display, just set the size to display
> // nothing and leave immediately.
> if(label == null)
> {
> raster.setSize(0, 0);
> return;
> }
>
> int width = 0;
> int height = 0;
> FontMetrics metrics = null;
> Graphics graphics = null;
>
> // Work on the null image first because adjustSize may also be set
> // but would crash with a null source image.
> if(textImage == null)
> {
> BufferedImage tmp_img =
> new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
>
> graphics = tmp_img.getGraphics();
>
> if(labelFont == null)
> metrics = graphics.getFontMetrics();
> else
> metrics = graphics.getFontMetrics(labelFont);
>
> Rectangle2D dimensions = metrics.getStringBounds(label, graphics);
>
> graphics.dispose();
> tmp_img.flush();
>
> width = (int)dimensions.getWidth();
> height = (int)dimensions.getHeight();
>
> if(borderColor != null)
> {
> width += BORDER_INSETS * 2 + 2; // one pixel border * 2
> height += BORDER_INSETS * 2 + 2;
> }
>
> textImage = new BufferedImage(width,
> height,
> BufferedImage.TYPE_INT_ARGB);
>
> graphics = textImage.createGraphics();
> renderImage(graphics, label, width, height, metrics.getAscent());
>
> component = new ImageComponent2D(ImageComponent2D.FORMAT_RGBA,
> textImage);
> component.setCapability(ImageComponent2D.ALLOW_IMAGE_WRITE);
>
> raster.setSize(width, height);
> raster.setImage(component);
> }
> else if(adjustImageSize)
> {
> // recalc size and compare against current.
> graphics = textImage.getGraphics();
>
> if(labelFont == null)
> metrics = graphics.getFontMetrics();
> else
> metrics = graphics.getFontMetrics(labelFont);
>
> Rectangle2D dimensions = metrics.getStringBounds(label, graphics);
>
> width = (int)dimensions.getWidth();
> height = (int)dimensions.getHeight();
>
> if(borderColor != null)
> {
> width += BORDER_INSETS * 2 + 2; // one pixel border * 2
> height += BORDER_INSETS * 2 + 2;
> }
>
> // So now we know the required size. Is it bigger than the current
> // image size? Resize if new size if bigger than the old.
> if((width > textImage.getWidth(null)) ||
> (height > textImage.getHeight(null)))
> {
> textImage.flush();
> textImage = new BufferedImage(width,
> height,
> BufferedImage.TYPE_INT_ARGB);
>
> graphics = textImage.createGraphics();
>
> // Probably superfluous, but in for correctness.
> if(labelFont == null)
> metrics = graphics.getFontMetrics();
> else
> metrics = graphics.getFontMetrics(labelFont);
>
> renderImage(graphics, label, width, height, metrics.getAscent());
>
> component = new ImageComponent2D(ImageComponent2D.FORMAT_RGBA,
> textImage);
> component.setCapability(ImageComponent2D.ALLOW_IMAGE_WRITE);
>
> raster.setSize(width, height);
> raster.setImage(component);
> }
> else
> {
> renderImage(graphics, label, width, height, metrics.getAscent());
>
> component.set(textImage);
> raster.setSize(width, height);
> }
> }
> else
> {
> // fixed size
> graphics = textImage.createGraphics();
>
> if(labelFont == null)
> metrics = graphics.getFontMetrics();
> else
> metrics = graphics.getFontMetrics(labelFont);
>
> Rectangle2D dimensions = metrics.getStringBounds(label, graphics);
>
> width = (int)dimensions.getWidth();
> height = (int)dimensions.getHeight();
>
> if(borderColor != null)
> {
> width += BORDER_INSETS * 2 + 2; // one pixel border * 2
> height += BORDER_INSETS * 2 + 2;
> }
>
> renderImage(graphics, label, width, height, metrics.getAscent());
>
> component.set(textImage);
> raster.setSize(width, height);
> }
>
> graphics.dispose();
> }
>
> /**
> * Convenience method to render the image given the font information. When
> * this method exits, it will have changed the global imageWidth and
> * imageHeight variables to the given width and height values.
> *
> * @param graphics The graphics context for textImage
> * @param label The string to render
> * @param width The width of the image drawn to
> * @param height The height of the image drawn to
> * @param ascent The ascent of the font in use
> */
> private void renderImage(Graphics graphics,
> String label,
> int width,
> int height,
> int ascent)
> {
> Graphics2D g = (Graphics2D)graphics;
> g.setComposite(AlphaComposite.Src);
>
> g.setColor(CLEAR_COLOR);
> g.fillRect(0, 0, imageWidth, imageHeight);
>
> if(borderColor != null)
> {
> g.setColor(borderColor);
> g.drawRect(0, 0, width - 1, height - 1);
>
> g.setColor(textColor);
> g.setFont(labelFont);
> g.drawString(label,
> BORDER_INSETS + 1,
> ascent + BORDER_INSETS + 1);
> }
> else
> {
> g.setColor(textColor);
> g.setFont(labelFont);
> g.drawString(label, 0, ascent);
> }
>
> imageWidth = width;
> imageHeight = height;
> }
> }
>
>
> ------------------------------------------------------------------------
>
> import java.awt.*;
> import java.awt.event.*;
> import java.awt.Rectangle;
> import java.util.AbstractList;
> import java.util.ArrayList;
> import java.util.Enumeration;
> import java.util.Iterator;
>
> import javax.media.j3d.*;
> import javax.vecmath.*;
> import javax.swing.*;
>
> import com.sun.j3d.utils.geometry.Sphere;
> import com.sun.j3d.utils.universe.PlatformGeometry;
> import com.sun.j3d.utils.universe.SimpleUniverse;
> import com.sun.j3d.utils.universe.ViewingPlatform;
> import com.sun.j3d.utils.image.TextureLoader;
> import com.sun.j3d.utils.behaviors.vp.OrbitBehavior;
> import com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior;
> import com.sun.j3d.utils.applet.JMainFrame;
> import org.j3d.geom.RasterTextLabel;
>
>
> import org.j3d.geom.*;
>
> public final class RasterBugs extends JApplet {
>
> private static final Geometry sphereGeometry;
>
> private KeyNavigatorBehavior keyBehavior;
>
> private TransformGroup vptg;
>
> private static final float MOVE_BACK = 2.0f;
>
> private BranchGroup objRoot = null;
>
> private static final Color3f BLACK = new Color3f(0f, 0f, 0f);
> private static final Color3f WHITE = new Color3f(1f, 1f, 1f);
> private static final Color3f GREEN = new Color3f(0.5f, 1f, 0.5f);
>
> private SimpleUniverse u;
>
> private PlatformGeometry pg;
>
> private BoundingSphere bounds;
>
> private static final Point3d D_ORIGIN = new Point3d(0, 0, 0);
>
> private Canvas3D c3d;
>
> private boolean alwaysOnTop = true;
>
> private RenderingAttributes ra;
>
> static {
> final int SPHERE_TESSELATION = 20;
>
> Sphere s =
> new Sphere(1.0f, Sphere.GENERATE_NORMALS, SPHERE_TESSELATION, null);
> sphereGeometry = s.getShape().getGeometry();
> } // End of static initializer
>
>
>
> /**
> * Set up keyboard navigation.
> */
> private void addKeyBehavior(BranchGroup b) {
> // Set up keyboard navigation
> keyBehavior = new KeyNavigatorBehavior(
> u.getViewingPlatform().getViewPlatformTransform());
> keyBehavior.setSchedulingBounds(bounds);
> // Add behavior to scenegraph
> b.addChild(keyBehavior);
> } // End of addKeyBehavior
>
>
> /**
> * Initialize and return the Canvas3D we'll be using for rendering.
> */
> public void init() {
>
> bounds = new BoundingSphere(D_ORIGIN, Double.POSITIVE_INFINITY);
>
> // Use utility to get the right graphics destination
> GraphicsConfiguration config =
> SimpleUniverse.getPreferredConfiguration();
>
> // Create a canvas on the device
> c3d = new Canvas3D(config);
>
> Container contentPane = getContentPane();
> contentPane.setLayout(new BorderLayout());
> contentPane.add(c3d, "Center");
> JPopupMenu.setDefaultLightWeightPopupEnabled(false);
> JMenuBar menuBar = new JMenuBar();
> setJMenuBar(menuBar);
> JMenu viewMenu = new JMenu("View");
> menuBar.add(viewMenu);
> JMenuItem inFrontItem = new JMenuItem("not alwaysOnTop");
> viewMenu.add(inFrontItem);
> inFrontItem.addActionListener(
> new ActionListener() {
> public void actionPerformed(ActionEvent e) {
> JMenuItem inFrontItem = (JMenuItem)e.getSource();
> alwaysOnTop = !alwaysOnTop;
> if (alwaysOnTop) {
> inFrontItem.setText("not alwaysOnTop");
> ra.setDepthBufferEnable(false);
> ra.setDepthBufferWriteEnable(false);
> } else {
> inFrontItem.setText("alwaysOnTop");
> ra.setDepthBufferEnable(true);
> ra.setDepthBufferWriteEnable(true);
> }
> }
> }
> );
>
>
> // Use utility to create default universe
> u = new SimpleUniverse(c3d);
>
> // Set the viewpoint back from the origin and point toward the origin
> ViewingPlatform vp = u.getViewingPlatform();
>
>
> vp.setNominalViewingTransform();
> vptg = vp.getViewPlatformTransform();
> Transform3D vpt = new Transform3D();
> vptg.getTransform(vpt);
> Transform3D translate = new Transform3D();
> translate.setTranslation(new Vector3f(0f,0f,MOVE_BACK));
> vpt.mul(translate);
> vptg.setTransform(vpt);
>
> // Allow mouse manipulation
> OrbitBehavior orbit = new OrbitBehavior(c3d, OrbitBehavior.REVERSE_ALL);
>
> // Behavior is only enabled within its bounds
> bounds = new BoundingSphere(D_ORIGIN, 100.0);
> orbit.setSchedulingBounds(bounds);
>
> // Attach behavior to ViewingPlatform
> vp.setViewPlatformBehavior(orbit);
>
> // Will add lights here so they are "headlights"
> // that move with the viewer
> pg = new PlatformGeometry();
>
> // Set up the ambient light
> Color3f ambientColor = new Color3f(0.1f, 0.1f, 0.1f);
> AmbientLight ambientLightNode = new AmbientLight(ambientColor);
> ambientLightNode.setInfluencingBounds(bounds);
> pg.addChild(ambientLightNode);
>
> // Set up the directional lights
> Color3f light1Color = new Color3f(1.0f, 1.0f, 0.9f);
> Vector3f light1Direction = new Vector3f(1.0f, 1.0f, 1.0f);
> Color3f light2Color = new Color3f(1.0f, 1.0f, 1.0f);
> Vector3f light2Direction = new Vector3f(-1.0f, -1.0f, -1.0f);
>
> DirectionalLight light1
> = new DirectionalLight(light1Color, light1Direction);
> light1.setInfluencingBounds(bounds);
> pg.addChild(light1);
>
> DirectionalLight light2
> = new DirectionalLight(light2Color, light2Direction);
> light2.setInfluencingBounds(bounds);
> pg.addChild(light2);
>
> vp.setPlatformGeometry(pg);
>
> objRoot = new BranchGroup();
> objRoot.setCapability(objRoot.ALLOW_DETACH);
> objRoot.setCapability(objRoot.ALLOW_CHILDREN_EXTEND);
> objRoot.setCapability(objRoot.ALLOW_CHILDREN_WRITE);
>
> addKeyBehavior(objRoot);
>
> objRoot.compile();
>
> u.addBranchGraph(objRoot);
>
> addNode();
>
> } // End of init
>
>
> /**
> * Add a node to the graph.
> */
> public void addNode()
> {
> // Each node is connected by a BranchGroup (to allow it to
> // be removed)
> BranchGroup root = new BranchGroup();
>
> // Transform Group to hold position transform
> TransformGroup posTg = new TransformGroup();
> Transform3D pos = new Transform3D();
> Vector3f translate = new Vector3f(0.2f,0f,0f);
> pos.setTranslation(translate);
> posTg.setTransform(pos);
> root.addChild(posTg);
>
> // TransformGroup to hold scale transform
> TransformGroup scaleTg = new TransformGroup();
> Transform3D scale = new Transform3D();
> scale.setScale(0.2);
> scaleTg.setTransform(scale);
> posTg.addChild(scaleTg);
>
> // Set appearance of node
> ra = new RenderingAttributes();
> Material mat = new Material(BLACK, BLACK, GREEN, WHITE, 128.0f);
> Appearance a = new Appearance();
> a.setMaterial(mat);
> a.setRenderingAttributes(ra);
>
> Shape3D shape = new Shape3D(sphereGeometry, a);
>
> scaleTg.addChild(shape);
>
> BranchGroup bg = new BranchGroup();
>
> RasterTextLabel rtl = new RasterTextLabel(
> "RasterTextLabel", Color.WHITE, alwaysOnTop, false, 0f, 0f, 0f);
>
> Raster labelText = (Raster)rtl.getGeometry();
> labelText.setClipMode(Raster.CLIP_IMAGE);
>
> Appearance ap = rtl.getAppearance();
> ra = ap.getRenderingAttributes();
> ra.setCapability(RenderingAttributes.ALLOW_DEPTH_ENABLE_WRITE);
>
> Dimension dim = new Dimension();
> labelText.getSize(dim);
> labelText.setDstOffset(dim.width / 2, dim.height / 2);
> posTg.addChild(rtl);
>
> root.compile();
>
> objRoot.addChild(root);
> } // End of addNode
>
>
> public static void main(String[] args) {
> new JMainFrame(new RasterBugs(args), 500, 500);
> }
>
> public RasterBugs(String[] args)
> {
> }
>
> public RasterBugs()
> {
> }
> } // End of file RenderEngine.java
>
===========================================================================
To unsubscribe, send email to [EMAIL PROTECTED] and include in the body
of the message "signoff JAVA3D-INTEREST". For general help, send email to
[EMAIL PROTECTED] and include in the body of the message "help".