I sent out a program yesterday to demonstrate a null pointer when
detaching and reattaching groups quickly. Well, it responds to keystrokes
and I put a keylistener on the canvas and it works if you click on the
canvas (which I was doing when I was running it since there is a rotate
and room behavior enabled and I liked poking at it) but if you just start
it up and hit keys it ignores you. To fix this I added a keylistener to
the applet as well.

And since I can't avoid playing I also set it so that 9 on the neatest
rotation sets I played with are mapped to the keys qweasdzxc. Also you can
change the distance of rotation using the 8/2 and 4/6 on the numeric
keypad. It looks pretty neat with the number of cubes set really
high. (The number of cubes being the one thing that has to be set at load
time. I like playing but that was too much work for a playtime
project.) =)

So this is the same program but improved a bit.

Will Holcomb

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.utils.image.TextureLoader;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.geometry.*;

public class DetachTest extends JApplet implements KeyListener {
    int speed;
    static float defaultLength = 1;
    static double defaultCenterOffset = 3;
    static int defaultNumSections = 4;  
    static int defaultSpeed = 3000;
    Alpha rotationAlpha;
    BranchGroup[] cubeGroup;
    TransformGroup[] offset;
    RotationInterpolator[] rotator;
    TransformGroup[] initialPosition;
    Group holder;
    
    final int UP = 1;
    final int DOWN = 2;
    int direction = 0;
    int index = 0;
    boolean paused = false;
    Vector3d offsetVector = new Vector3d();

    public DetachTest() {
        this(defaultLength, defaultCenterOffset, defaultNumSections, defaultSpeed);
    }

    public DetachTest(float length) {
        this(length, defaultCenterOffset, defaultNumSections, defaultSpeed);
    }

    public DetachTest(float length, double centerOffset) {
        this(length, centerOffset, defaultNumSections, defaultSpeed);
    }

    public DetachTest(float length, double centerOffset, int numSections) {
        this(length, centerOffset, numSections, defaultSpeed);
    }

    public DetachTest(float length, double centerOffset, int numSections, int speed) {
        this.speed = speed;

        VirtualUniverse universe = new VirtualUniverse();
        Locale locale = new Locale(universe);
        BranchGroup head = new BranchGroup();
        holder = new Group();
        holder.setCapability(Group.ALLOW_CHILDREN_WRITE);
        holder.setCapability(Group.ALLOW_CHILDREN_EXTEND);

        BoundingSphere bounds = new BoundingSphere(new Point3d(), Double.MAX_VALUE);

        Background background = new Background(new Color3f(29f / 255f, 43f / 255f, 
153f / 255f));
        background.setApplicationBounds(bounds);
        head.addChild(background);

        DirectionalLight light = new DirectionalLight
            (new Color3f(100f / 255f, 100f / 255f, 100f / 255f), 
             new Vector3f(0f, 0f, -1f));
        light.setInfluencingBounds(bounds);
        head.addChild(light);

        TransformGroup rotationTransformation = new TransformGroup();
        rotationTransformation.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        rotationTransformation.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        head.addChild(rotationTransformation);
        
        MouseRotate rotateBehavior = new MouseRotate(rotationTransformation);
        rotateBehavior.setSchedulingBounds(bounds);
        rotationTransformation.addChild(rotateBehavior);
        
        numSections = Math.max(numSections, 1);
        offsetVector = new Vector3d(centerOffset, 0, 0);
        Transform3D rotation;
        Transform3D translation = new Transform3D();
        translation.setTranslation(offsetVector);
        initialPosition = new TransformGroup[numSections];
        TransformGroup[] interpolator = new TransformGroup[numSections];
        offset = new TransformGroup[numSections];
        cubeGroup = new BranchGroup[numSections];
        rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
                                        0, 0,
                                        speed, 0, 0,
                                        0, 0, 0);
        Transform3D axis = new Transform3D();
        rotator = new RotationInterpolator[numSections];

        for(index = 0; index < numSections; index++) {
            axis.rotX(index * Math.PI * 2 / numSections);
            rotation = new Transform3D();
            rotation.rotZ(index * Math.PI * 2 / numSections);
            rotation.mul(translation);
            initialPosition[index] = new TransformGroup(rotation);
            initialPosition[index].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
            
initialPosition[index].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
            rotationTransformation.addChild(initialPosition[index]);
            interpolator[index] = new TransformGroup();
            interpolator[index].setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
            interpolator[index].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
            initialPosition[index].addChild(interpolator[index]);
            offset[index] = new TransformGroup(translation);
            offset[index].setCapability(Group.ALLOW_CHILDREN_WRITE);
            offset[index].setCapability(Group.ALLOW_CHILDREN_EXTEND);
            offset[index].setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
            interpolator[index].addChild(offset[index]);
            cubeGroup[index] = new BranchGroup();
            cubeGroup[index].setCapability(BranchGroup.ALLOW_DETACH);
            offset[index].addChild(cubeGroup[index]);
            cubeGroup[index].addChild(new ColorCube(length));
            rotator[index] = new RotationInterpolator(rotationAlpha,
                                                      interpolator[index],
                                                      axis, 0,
                                                      (float)Math.PI * 2f);
            rotator[index].setSchedulingBounds(bounds);
            interpolator[index].addChild(rotator[index]);
        }

        index = numSections - 1;
        direction = DOWN;
        
        Canvas3D canvas = new Canvas3D(SimpleUniverse.getPreferredConfiguration());
        View view = new View();
        view.setPhysicalBody(new PhysicalBody());
        view.setPhysicalEnvironment(new PhysicalEnvironment());
        view.setBackClipDistance(5000);
        view.addCanvas3D(canvas);
        ViewPlatform viewPlatform = new ViewPlatform();
        view.attachViewPlatform(viewPlatform);

        translation.setTranslation(new Vector3d(0, 0, (length + centerOffset) * 7));
        TransformGroup viewTransformation = new TransformGroup(translation);
        viewTransformation.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        viewTransformation.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        head.addChild(viewTransformation);
        viewTransformation.addChild(viewPlatform);
        
        MouseZoom zoomBehavior = new MouseZoom(viewTransformation);
        zoomBehavior.setSchedulingBounds(bounds);
        viewTransformation.addChild(zoomBehavior);

        getContentPane().add(canvas);
        canvas.addKeyListener(this);
        addKeyListener(this);

        head.compile();
        locale.addBranchGraph(head);
    }

    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_UP) {
            speed -= 10;
            rotationAlpha.setIncreasingAlphaDuration(speed);
        } else if(e.getKeyCode() == KeyEvent.VK_DOWN) {
            speed += 10;
            rotationAlpha.setIncreasingAlphaDuration(speed);
        } else if(e.getKeyCode() == KeyEvent.VK_PAGE_UP) {
            speed -= 500;
            rotationAlpha.setIncreasingAlphaDuration(speed);
        } else if(e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
            speed += 500;
            rotationAlpha.setIncreasingAlphaDuration(speed);
        } else if(e.getKeyCode() == KeyEvent.VK_P) {
            paused = !paused;
            if(paused) {
                rotationAlpha.setIncreasingAlphaDuration(0);
            } else {
                rotationAlpha.setIncreasingAlphaDuration(speed);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_Q) {
            Transform3D axis = new Transform3D();
            for(int i = 0; i < rotator.length; i++) {
                if(i % 2 == 0) {
                    axis.rotX(Math.PI / 2);
                } else {
                    axis.rotX(-Math.PI / 2);
                }
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_W) {
            Transform3D axis = new Transform3D();
            for(int i = 0; i < rotator.length; i++) {
                axis.rotX(i * Math.PI * 2 / rotator.length);
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_E) {
            Transform3D axis = new Transform3D();
            for(int i = 0; i < rotator.length; i++) {
                if(i < rotator.length / 2) {
                    axis.rotX(i * Math.PI * 2 / rotator.length);
                } else {
                    axis.rotX(-i * Math.PI * 2 / rotator.length);
                }
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_A) {
            Transform3D axis = new Transform3D();
            axis.rotX(Math.PI / 2);
            for(int i = 0; i < rotator.length; i++) {
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_S) {
            Transform3D axis = new Transform3D();
            axis.rotY(Math.PI / 2);
            for(int i = 0; i < rotator.length; i++) {
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_D) {
            Transform3D axis = new Transform3D();
            axis.rotZ(Math.PI / 2);
            for(int i = 0; i < rotator.length; i++) {
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_Z) {
            Transform3D axis = new Transform3D();
            for(int i = 0; i < rotator.length; i++) {
                axis.setEuler(new Vector3d(Math.random() * Math.PI * 2,
                                           Math.random() * Math.PI * 2,
                                           Math.random() * Math.PI * 2));
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_X) {
            Transform3D axis = new Transform3D();
            for(int i = 0; i < rotator.length; i++) {
                axis.setEuler(new Vector3d(i * Math.PI * 2 / rotator.length,
                                           i * Math.PI * 2 / rotator.length,
                                           i * Math.PI * 2 / rotator.length));
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_C) {
            Transform3D axis = new Transform3D();
            for(int i = 0; i < rotator.length; i++) {
                axis.rotZ(i * Math.PI * 2 / rotator.length);
                rotator[i].setAxisOfRotation(axis);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_NUMPAD8) {
            Transform3D translation = new Transform3D();
            Transform3D position = new Transform3D();
            offsetVector.x += .5;
            translation.setTranslation(offsetVector);
            for(int i = 0; i < offset.length; i++) {
                initialPosition[i].getTransform(position);
                position.setTranslation(new Vector3d());
                position.mul(translation);
                initialPosition[i].setTransform(position);
                offset[i].setTransform(translation);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_NUMPAD2) {
            Transform3D translation = new Transform3D();
            Transform3D position = new Transform3D();
            offsetVector.x -= .5;
            translation.setTranslation(offsetVector);
            for(int i = 0; i < offset.length; i++) {
                initialPosition[i].getTransform(position);
                position.setTranslation(new Vector3d());
                position.mul(translation);
                initialPosition[i].setTransform(position);
                offset[i].setTransform(translation);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_NUMPAD6) {
            Transform3D translation = new Transform3D();
            Transform3D position = new Transform3D();
            offsetVector.y += .5;
            translation.setTranslation(offsetVector);
            for(int i = 0; i < offset.length; i++) {
                initialPosition[i].getTransform(position);
                position.setTranslation(new Vector3d());
                position.mul(translation);
                initialPosition[i].setTransform(position);
                offset[i].setTransform(translation);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_NUMPAD4) {
            Transform3D translation = new Transform3D();
            Transform3D position = new Transform3D();
            offsetVector.y -= .5;
            translation.setTranslation(offsetVector);
            for(int i = 0; i < offset.length; i++) {
                initialPosition[i].getTransform(position);
                position.setTranslation(new Vector3d());
                position.mul(translation);
                initialPosition[i].setTransform(position);
                offset[i].setTransform(translation);
            }
        } else if(e.getKeyCode() == KeyEvent.VK_R) {
            if(index < 0) {
                index = 0;
                direction = UP;
            } else if(index >= cubeGroup.length) {
                index = cubeGroup.length - 1;
                direction = DOWN;
            }

            if(direction == DOWN) {
                holder.moveTo(cubeGroup[index]);
                index--;
            } else if(direction == UP) {
                offset[index].moveTo(cubeGroup[index]);
                index++;
            }
            return;
        }
    }

    public void keyReleased(KeyEvent e) {}
    public void keyTyped(KeyEvent e) {}

    public static void main(String[] args) {
        float length = DetachTest.defaultLength;
        double offset = DetachTest.defaultCenterOffset;
        int numSections  = DetachTest.defaultNumSections;
        int speed  = DetachTest.defaultSpeed;
        boolean error = false;

        for(int i = 0; i < args.length; i++) {
            if(args[i].length() > 2) {
                if(args[i].toLowerCase().startsWith("-l")) {
                    try {
                        length = Float.parseFloat(args[i].substring(2));
                    } catch(NumberFormatException e) {
                        System.err.println("\"" + args[i].substring(2) + "\" is not a 
valid float.");
                        error = true;
                    }
                } else if(args[i].toLowerCase().startsWith("-o")) {
                    try {
                        offset = Double.parseDouble(args[i].substring(2));
                    } catch(NumberFormatException e) {
                        System.err.println("\"" + args[i].substring(2) + "\" is not a 
valid double.");
                        error = true;
                    }
                } else if(args[i].toLowerCase().startsWith("-n")) {
                    try {
                        numSections = Integer.parseInt(args[i].substring(2));
                    } catch(NumberFormatException e) {
                        System.err.println("\"" + args[i].substring(2) + "\" is not a 
valid integer.");
                        error = true;
                    }
                } else if(args[i].toLowerCase().startsWith("-s")) {
                    try {
                        speed = Integer.parseInt(args[i].substring(2));
                    } catch(NumberFormatException e) {
                        System.err.println("\"" + args[i].substring(2) + "\" is not a 
valid integer.");
                        error = true;
                    }
                } else {
                    System.err.println("Unknown option \"" + args[i].substring(0, 2) + 
"\"");
                    error = true;
                }
            } else {
                if(args[i].toLowerCase().startsWith("-h")) {
                    error = true;
                } else {
                    System.err.println("Unknown option \"" + args[i] + "\"");
                    error = true;
                }
            }
        }
        if(error) {
            System.out.println("Usage:");
            System.out.println("  DetachTest -l(float) -h(double) -n(integer) 
-s(integer) -h");
            System.out.println("   -l => length => Sets the size of the color cubes");
            System.out.println("   -o => offset => Sets rotation distance");
            System.out.println("   -n => number => Sets the number of cubes");
            System.out.println("   -s => speed => Sets the delay in milliseconds");
            System.out.println("   -h => help => Prints this message");
            System.out.println("");
            System.out.println(" Once the program is started pressing the up and down 
keys will");
            System.out.println("  increase the delay by 10 millisecond and pgup and 
pgdown will");
            System.out.println("  increase it by 500. Also the p key will toggle it 
between its");
            System.out.println("  current state and 0. Some setting like -n50 -o0 are 
pretty.");
            System.out.println("  (Well, if your machine can get better than .3fps.) 
=)");
            System.out.println("");
            System.out.println(" The purpose of this program is to illustrate some 
bugs in the java");
            System.out.println("  3d libraries. Whenever the r key is pressed one of 
the cubes will");
            System.out.println("  be removed from the scene graph and attached to a 
group not in the");
            System.out.println("  scene using the Group.moveTo(BranchGroup) method. In 
the 1.1");
            System.out.println("  implementation the cubes do not disappear when 
removed, or at least");
            System.out.println("  not until they are begun to be put back in. In the 
1.2 implementation");
            System.out.println("  they disappear, but if you add and remove them too 
quickly you get");
            System.out.println("  a null pointer exception and rendering stops.");
        } else {
            new MainFrame(new DetachTest(length, offset, numSections, speed), 400, 
300);
        }
    }
}

Reply via email to