Demo3D.java Source code

Java tutorial

Introduction

Here is the source code for Demo3D.java

Source

/* Author Claude G. Schwab 
 * Copyright (c) 2005  University of applied sciences 
 * Biel School of Engineering and Architecture, Switzerland.
 * http://www.hta-bi.bfh.ch
 * All Rights Reserved.
 * Compiled with SDK 1.4.1 and Java3D API Version 1.3
 *
 * This Demo class is a demonstration software (code) 
 * for my introduction to Java3D.
 */

import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.text.NumberFormat;
import java.util.Enumeration;

import javax.media.j3d.Alpha;
import javax.media.j3d.AmbientLight;
import javax.media.j3d.Appearance;
import javax.media.j3d.Background;
import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.Canvas3D;
import javax.media.j3d.ColoringAttributes;
import javax.media.j3d.DirectionalLight;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.Group;
import javax.media.j3d.LineArray;
import javax.media.j3d.Locale;
import javax.media.j3d.Material;
import javax.media.j3d.PhysicalBody;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.PointLight;
import javax.media.j3d.PolygonAttributes;
import javax.media.j3d.QuadArray;
import javax.media.j3d.RotationInterpolator;
import javax.media.j3d.Shape3D;
import javax.media.j3d.Switch;
import javax.media.j3d.Texture;
import javax.media.j3d.TextureAttributes;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.TransparencyAttributes;
import javax.media.j3d.View;
import javax.media.j3d.ViewPlatform;
import javax.media.j3d.VirtualUniverse;
import javax.media.j3d.WakeupOnAWTEvent;
import javax.media.j3d.WakeupOnElapsedFrames;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.vecmath.AxisAngle4f;
import javax.vecmath.Color3f;
import javax.vecmath.Point3d;
import javax.vecmath.Point3f;
import javax.vecmath.TexCoord2f;
import javax.vecmath.Vector3d;
import javax.vecmath.Vector3f;

import com.sun.j3d.utils.behaviors.picking.PickRotateBehavior;
import com.sun.j3d.utils.behaviors.picking.PickTranslateBehavior;
import com.sun.j3d.utils.behaviors.picking.PickZoomBehavior;
import com.sun.j3d.utils.geometry.GeometryInfo;
import com.sun.j3d.utils.geometry.NormalGenerator;
import com.sun.j3d.utils.geometry.Sphere;
import com.sun.j3d.utils.geometry.Stripifier;
import com.sun.j3d.utils.image.TextureLoader;

/**
 * This class is the main class of the program Demo. It creates a Virtual
 * Universe and a Locale and attaches to the Locale: a right-handed 3D
 * coordinate system, a color cube, a tetrahedron, the earth and the ViewBranch.
 * It also overrides the addBranchGraph methode.
 */

public class Demo3D extends JFrame implements Runnable {
    static int screenwidth;

    static int screenheight;

    static Thread fpsThread;

    long sleepDuration = 200; // in msec

    int decimalForAllFps = 1;

    JLabel jLabel;

    // Create a virtual universe.
    VirtualUniverse universe = new VirtualUniverse();

    // A single hi-res. Locale node is created and attached to the
    // virtual universe.
    Locale locale = new Locale(universe);

    Canvas3D canvas3D;

    ViewBranch viewBr;

    /**
     * Constructor that allows to specify the desired initial instances.
     */
    public Demo3D() {
        // Set the best GraphicsConfiguration
        GraphicsConfigTemplate3D template = new GraphicsConfigTemplate3D();

        GraphicsConfiguration graphConf = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice()
                .getBestConfiguration(template);

        canvas3D = new Canvas3D(graphConf); // The used Canvas3D

        // Construction of the main frame.
        setTitle("Demo");

        JPanel jMainPanel = new JPanel(true);
        jMainPanel.setLayout(new BorderLayout(0, 5)); // hor_gap and ver_gap

        JPanel jFpsPanel = new JPanel(true);
        jFpsPanel.setBackground(Color.white);

        jLabel = new JLabel("");
        jLabel.setText("Wait for informations");
        jFpsPanel.add(jLabel);

        jMainPanel.add(canvas3D, BorderLayout.CENTER);

        /*
         * // For the stereo-mode with an "Head Monted Display" (HMD). JPanel
         * jScene_Stereo_Panel = new JPanel(true);
         * jScene_Stereo_Panel.setLayout(new GridLayout(1, 2, 0, 0)); // rows,
         * col, hor_gap and ver_gap jScene_Stereo_Panel.add(canvas3D);
         * jScene_Stereo_Panel.add(canvas3D);
         * jMainPanel.add(jScene_Stereo_Panel, BorderLayout.CENTER);
         */

        jMainPanel.add(jFpsPanel, BorderLayout.SOUTH);

        setContentPane(jMainPanel);

        // The ViewBranch class creates the instances of ViewPlatform, View,
        // etc.
        viewBr = new ViewBranch(canvas3D);

        fpsThread = new Thread(this);

        myScene();
    }

    /**
     * Assembling of all components of the scene.
     */
    public void myScene() {
        // Necessary to use NewTextureLoader in other classes.
        NewTextureLoader.setImageObserver(this); // AWT Component

        // Attach the subgraphs SceneBuilder1, SceneBuilder2, SceneBuilder3
        // and the ViewBranch to the Locale node.
        addBranchGraph(new SceneBuilder1().mySubGraph1());
        addBranchGraph(new SceneBuilder2().mySubGraph2());
        addBranchGraph(new SceneBuilder3(canvas3D).mySubGraph3());
        addBranchGraph(viewBr.myViewBranch());
    }

    /**
     * Allows to attach all subgraphs of the scene and the ViewBranch to the
     * Locale node.
     * 
     * @param javax.media.j3d.BranchGroup
     *            brGr - the root of the subgraph
     */

    public void addBranchGraph(BranchGroup brGr) {
        locale.addBranchGraph(brGr);
    }

    ///////////////////////////// Framemeter /////////////////////////////
    /**
     * This start method allows to start the thread of the framemeter.
     */
    public void start() {
        SwingUtilities.invokeLater(fpsThread);
    }

    /**
     * This run method allows to launch the computation of all frames per second
     * for the framemeter.
     */
    public void run() {
        long lastFrameTime;
        double fps;
        double min = Double.MAX_VALUE;
        double max = Double.MIN_VALUE;
        long count = 0;
        double sum = 0;
        double mean = 0;

        while (true) {
            lastFrameTime = viewBr.view.getLastFrameDuration();

            if (lastFrameTime > 0) {
                fps = 1000 / (double) lastFrameTime;
                count += 1;
                sum += fps;
                mean = sum / count;

                // To format all fps-informations.
                NumberFormat numbForm;
                numbForm = NumberFormat.getInstance();
                numbForm.setMaximumFractionDigits(decimalForAllFps);

                if (min > fps && fps != 0 && count > 4)
                    min = fps;
                if (max < fps)
                    max = fps;

                jLabel.setText("Frames/sec = " + numbForm.format(fps) + "  ;    minFrames/sec = "
                        + numbForm.format(min) + "  ;    maxFrames/sec = " + numbForm.format(max)
                        + "  ;    meanFrames/sec = " + numbForm.format(mean));

                // System.out.println("Frames per second = " + fps);
            }

            try {
                Thread.sleep(sleepDuration);
            } catch (InterruptedException e) {
            }
        }
    }

    ///////////////////////// End of the framemeter /////////////////////////

    /**
     * Main of the Demo program. Take the graphic environment of the
     * workstation.
     */
    public static void main(String args[]) {
        JFrame jFrameDemo = new Demo3D();

        // To be sure to stop the application when the frame is closed.
        WindowListener winListener = new WindowAdapter() {
            public void windowClosing(WindowEvent event) {
                System.exit(0);
            }
        };

        jFrameDemo.addWindowListener(winListener);

        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        screenwidth = (int) screenSize.getWidth();
        screenheight = (int) screenSize.getHeight();
        jFrameDemo.setSize(screenwidth, screenheight);

        // Turn on the visibility of the frame.
        jFrameDemo.setVisible(true);

        fpsThread.start();
    }
}

/*
 * Author Claude G. Schwab Copyright (c) 2002 University of applied sciences
 * Biel School of Engineering and Architecture, Switzerland.
 * http://www.hta-bi.bfh.ch All Rights Reserved. Compiled with SDK 1.4.1 and
 * Java3D API Version 1.3
 * 
 * This ViewBranch class is a demonstration software (code) for my introduction
 * to Java3D.
 */

/**
 * This class creates all necessary objects on the "View Branch" side of the
 * scene graph.
 */

class ViewBranch {
    public BranchGroup vbBrGr;

    private Canvas3D canvas3D;

    private PhysicalBody body;

    private PhysicalEnvironment environment;

    public static View view; // static for the Framemeter

    private ViewPlatform viewPlat;

    private TransformGroup vpTrGrStart, vpTrGrKeys_Transl_Turn, vpTrGrKeys_Rot_Up_Down;

    private Transform3D trStart;

    private BoundingSphere cameraBounds;

    private Camera_Transl_Turn camera_Transl_Turn;

    private Camera_Rot_Up_Down camera_Rot_Up_Down;

    private Aimer aimer;

    /**
     * Constructor that allows to specify the desired Canvas3D.
     * 
     * @param javax.media.j3d.Canvas3D
     *            canv - the Canvas3D being used
     */
    public ViewBranch(Canvas3D canv) // The instance canv of Canvas3D class is
    { // created in the constructor of the Demo class.
        canvas3D = canv;
    }

    /**
     * Create the ViewBranch
     * 
     * @return javax.media.j3d.BranchGroup vbBrGr - the root of the ViewBranch
     */
    public BranchGroup myViewBranch() {
        // Create the minimal PhysicalBody and PhysicalEnvironnement
        // instances with default parameters.
        body = new PhysicalBody();
        environment = new PhysicalEnvironment();

        // Create a View instance and attach the Canvas3D, the PhysicalBody
        // and the PhysicalEnvironment to it.
        view = new View();
        view.setFrontClipDistance(0.02); // Default value is 0.1 m
        view.setBackClipDistance(40.0); // Default value is 10 m
        // Rem.: BackClipDistance / FrontClipDistance = 2000 > 1000 but < 3000
        view.addCanvas3D(canvas3D);
        view.setPhysicalBody(body);
        view.setPhysicalEnvironment(environment);
        /*
         * // Choices of the projection type. They are 2 possibilities, namely: //
         * PERSPECTIVE_PROJECTION and PARALLEL_PROJECTION. // Note: the default
         * value is PERSPECTIVE_PROJECTION
         * view.setProjectionPolicy(View.PARALLEL_PROJECTION);
         */
        // Create a ViewPlatform instance and bind it with the View instance.
        viewPlat = new ViewPlatform();
        viewPlat.setActivationRadius(40.0f); // Default value is 62 m
        view.attachViewPlatform(viewPlat);

        // Create the action volume for the camera's navigation.
        cameraBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

        // Create the two necessary TransformGroups for the ViewPlatform's
        // motion (6 translations and 4 rotations).
        vpTrGrKeys_Rot_Up_Down = new TransformGroup();
        vpTrGrKeys_Transl_Turn = new TransformGroup();

        // With the ALLOW_TRANSFORM_READ and ALLOW_TRANSFORM_WRITE
        // capabilities, we allow the modification of the TransformGroup's
        // code by the Behavior's code at run time.
        vpTrGrKeys_Transl_Turn.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        vpTrGrKeys_Transl_Turn.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        vpTrGrKeys_Rot_Up_Down.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        vpTrGrKeys_Rot_Up_Down.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        // Attach the ViewPlatform to the vpTrGrKeys_Rot_Up_Down node.
        vpTrGrKeys_Rot_Up_Down.addChild(viewPlat);

        // Create and attach an aimer to the TransformGroup node
        // vpTrGrKeys_Rot_Up_Down.
        aimer = new Aimer(1.5f);
        vpTrGrKeys_Rot_Up_Down.addChild(aimer.myAimer());

        // View-platform's motion ==> camera's navigation: 6 translations and 4
        // rotations.

        // Create and attach the camera's rotation on the vpTrGrKeys_Rot_Up_Down
        // node.
        camera_Rot_Up_Down = new Camera_Rot_Up_Down(vpTrGrKeys_Rot_Up_Down);
        camera_Rot_Up_Down.setSchedulingBounds(cameraBounds);
        vpTrGrKeys_Rot_Up_Down.addChild(camera_Rot_Up_Down);

        // Create and attach the camera's translation and rotation instances
        // on the vpTrGrKeys_Transl_Turn node.
        camera_Transl_Turn = new Camera_Transl_Turn(vpTrGrKeys_Transl_Turn);
        camera_Transl_Turn.setSchedulingBounds(cameraBounds);
        vpTrGrKeys_Transl_Turn.addChild(camera_Transl_Turn);

        // Attach the vpTrGrKeys_Rot_Up_Down node to the vpTrGrKeys_Transl_Turn
        // node.
        vpTrGrKeys_Transl_Turn.addChild(vpTrGrKeys_Rot_Up_Down);

        // Give the starting position of the ViewPlatform.
        trStart = new Transform3D(); // Identity matrix
        trStart.set(new Vector3f(0.0f, 0.0f, 10.0f)); // Translation of the
        // camera (0,0,10)

        // Create the TransformGroup node for the ViewPlatform's
        // starting position.
        vpTrGrStart = new TransformGroup(trStart);

        // Attach the vpTrGrKeys_Transl_Turn node to the TransformGroup
        // node vpTrGrStart.
        vpTrGrStart.addChild(vpTrGrKeys_Transl_Turn);

        // Add the TransformGroup node vpTrGrStart to the view
        // BranchGroup node vbBrGr.
        vbBrGr = new BranchGroup();
        vbBrGr.addChild(vpTrGrStart);

        // Compile the ViewBranch to optimize the performances.
        vbBrGr.compile();

        // Return the final version of the view branch BranchGroup node vbBrGr.
        return vbBrGr;
    }
}

/**
 * This class serves to put a locale right-handed 3D coordinate system as
 * reference system into the scene. It also creates a background, the
 * illumination of the scene and the borders of the virtual universe.
 */

class SceneBuilder1 {
    public BranchGroup brGr1;

    private CoordSyst coordSyst;

    private BoundingSphere boundsBackGr, boundsGen;

    private Background backGr;

    private NewTextureLoader newTextureLoader;

    private AmbientLight ambientLight;

    private PointLight pointLight;

    private DirectionalLight directionalLight;

    private BordersIn bordersIn;

    private BordersOut bordersOut;

    private static final float dimUniverse = 5.0f; // dimensions of the virtual

    // universe are:

    // dimUniverse x dimUniverse x dimUniverse

    /**
     * Create the subgraph #1
     * 
     * @return javax.media.j3d.BranchGroup brGr1 - the root of the subgraph #1
     */
    public BranchGroup mySubGraph1() {
        // Create the BranchGroup brGr1 of the subgraph #1.
        brGr1 = new BranchGroup();

        // Create and attach the coordinate system to the brGr1.
        coordSyst = new CoordSyst(1.0f, 1.0f, 0.0f, // Color of the x-axis
                0.0f, 0.0f, 1.0f, // Color of the y-axis
                1.0f, 0.0f, 0.0f, // Color of the z-axis
                0.75f); // Lenght of the 3 axes
        brGr1.addChild(coordSyst);

        // Background setting for the scene.
        newTextureLoader = new NewTextureLoader("Images/Ciel_Out.jpg");
        newTextureLoader.setImageObserver(newTextureLoader.getImageObserver());
        backGr = new Background(newTextureLoader.getImage());
        backGr.setImageScaleMode(Background.SCALE_FIT_ALL);
        boundsBackGr = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 1000.0);
        backGr.setApplicationBounds(boundsBackGr);
        brGr1.addChild(backGr);

        // A BoundingSphere instance as general bounding region.
        boundsGen = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

        // Lighting of the scene.

        // Create and attach an ambient light.
        ambientLight = new AmbientLight(true, new Color3f(0.2f, 0.2f, 0.2f));
        ambientLight.setInfluencingBounds(boundsGen);
        brGr1.addChild(ambientLight);

        // Create and attach a point light.
        pointLight = new PointLight(true, new Color3f(1.0f, 1.0f, 0.3f), new Point3f(-100.0f, 0.0f, 100.0f),
                new Point3f(0.0f, 0.05f, 0.1f));
        pointLight.setInfluencingBounds(boundsGen);
        brGr1.addChild(pointLight);

        // Create and attach a directional light.
        directionalLight = new DirectionalLight(true, new Color3f(0.8f, 1.0f, 1.0f),
                new Vector3f(-0.5f, -0.5f, -0.5f));
        directionalLight.setInfluencingBounds(boundsGen);
        brGr1.addChild(directionalLight);

        // Create the borders of the virtual universe for the inside view of the
        // scene.
        bordersIn = new BordersIn(dimUniverse);
        brGr1.addChild(bordersIn.myInternalUniverse());

        // Create the borders of the virtual universe for the outside view of
        // the scene.
        bordersOut = new BordersOut(dimUniverse);
        brGr1.addChild(bordersOut.myExternalUniverse());

        // Compile the subgraph to optimize the performances.
        brGr1.compile();

        // Return the final version of the BranchGroup node brGr1
        return brGr1;
    }
}

/**
 * This class serves to put a colored cube into the scene graph. It also
 * produces a "static rotation" and a "dynamic rotation" of the colored cube.
 */

class SceneBuilder2 {
    public BranchGroup brGr2;

    private BoundingSphere boundsGen;

    private TransformGroup trGr2_1, trGr2_2;

    private CoordSyst coordSyst;

    private ColorCube colorCube;

    private Transform3D trans1, rot1;

    private Alpha rotationAlpha;

    private AxisAngle4f axe_rot;

    private RotationInterpolator rotator;

    /**
     * Create the subgraph #2
     * 
     * @return javax.media.j3d.BranchGroup brGr2 - the root of the subgraph #2
     */
    public BranchGroup mySubGraph2() {
        // Create the BranchGroup node brGr2 of the second subgraph.
        brGr2 = new BranchGroup();

        // A BoundingSphere instance as general bounding region.
        boundsGen = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

        // Create a Transform3D instance rot1 to perform the necessary
        // "static rotation" for the desired cube's position.
        rot1 = new Transform3D();

        // Rotation of Pi/2 - arctan(1/sqrt(2)) = 0.955 rad about the
        // (1,0,-1)-axis passing through the origin.
        axe_rot = new AxisAngle4f(1.0f, 0.0f, -1.0f, 0.955f);
        rot1.setRotation(axe_rot);

        // Create the first TransformGroup node trGr2_1 and attach the
        // "static rotation" rot1 instance to it.
        trGr2_1 = new TransformGroup(rot1);

        // Create and attach a coordinate system to the TransformGroup node
        // trGr2_1 of the subgraph #2, that is to the cube.
        coordSyst = new CoordSyst(1.0f, 1.0f, 0.0f, // Color of the x-axis
                0.0f, 0.0f, 1.0f, // Color of the y-axis
                1.0f, 0.0f, 0.0f, // Color of the z-axis
                0.4f); // Lenght of the 3 axes
        trGr2_1.addChild(coordSyst);

        // Create the ColorCube (Shape3D) and attach it to the
        // TransformGroup node trGr2_1 of the subgraph #2.
        colorCube = new ColorCube(0.5f);
        trGr2_1.addChild(colorCube);

        // Create the second TransformGroup node trGr2_2.
        trGr2_2 = new TransformGroup();

        // With the ALLOW_TRANSFORM_WRITE capability, we allow the
        // modification of the TransformGroup's code by the behavior's
        // code at run time.
        trGr2_2.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        // Attach the first node trGr2_1 to the second node trGr2_2.
        trGr2_2.addChild(trGr2_1);

        // Prepare the RotationInterpolator (Behavior) for the
        // cube's rotation about the y-axis.
        trans1 = new Transform3D();

        // Create the alpha(t) function.
        rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 10000, 0, 0, 0, 0, 0);
        // Create the cube's rotation about the y-axis.
        rotator = new RotationInterpolator(rotationAlpha, trGr2_2, trans1, 0.0f, (float) Math.PI * 2.0f);
        rotator.setSchedulingBounds(boundsGen);

        trGr2_2.addChild(rotator);

        brGr2.addChild(trGr2_2);

        // Compile the subgraph to optimize the performances.
        brGr2.compile();

        // Return the final version of the BranchGroup node brGr2
        return brGr2;
    }
}

/**
 * This class serves to attache both the BranchGraph31 and BranchGraph32 to the
 * Locale and to pick the tetrahedron.
 */

/*
 * Note: It is not always necessary to use "detach" and "add" to add/remove a
 * subgraph from a scene graph. In many cases the using of the setEnable()
 * method, to turn a subgraph on and off, is adequate.
 */

class SceneBuilder3 {
    public BranchGroup brGr3;

    private AddDetachEarthBehavior addDetachEarthBehavior;

    private BoundingSphere boundsGen, pickBounds;

    private Canvas3D canvas3D; // needed 3 times for the Picking of the

    // tetrahedron

    private PickRotateBehavior pickRotBehavior;

    private PickZoomBehavior pickZoomBehavior;

    private PickTranslateBehavior pickTransBehavior;

    private SceneBuilder31 sceneBuilder31;

    private SceneBuilder32 sceneBuilder32;

    private Tetrahedron tetrahedron;

    /**
     * Constructor that allows to specify the desired Canvas3D.
     * 
     * @param javax.media.j3d.Canvas3D
     *            canv - the active Canvas3D
     */
    public SceneBuilder3(Canvas3D canv) {
        canvas3D = canv;
    }

    /**
     * Create the subgraph #3
     * 
     * @return javax.media.j3d.BranchGroup brGr3 - the root of the subgraph #3
     */
    public BranchGroup mySubGraph3() {
        // Create the BranchGroup node brGr3, in other words the root of
        // the subgraph31 and subgraph32.
        brGr3 = new BranchGroup();

        // To allow the detach/add process of the subgraph 32 from the
        // BranchGroup node brGr3.
        brGr3.setCapability(Group.ALLOW_CHILDREN_READ);
        brGr3.setCapability(Group.ALLOW_CHILDREN_WRITE);
        brGr3.setCapability(Group.ALLOW_CHILDREN_EXTEND);

        // A BoundingSphere instance as picking bound region.
        pickBounds = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 5.0);

        // A BoundingSphere instance as general bounding region.
        boundsGen = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

        // Create and attach the subgraph31 with the tetrahedron
        // to the BranchGroup node brGr3.
        tetrahedron = new Tetrahedron(1.0f);
        sceneBuilder31 = new SceneBuilder31(tetrahedron.myTetrahedron());
        brGr3.addChild(sceneBuilder31.mySubGraph31());

        // Picking of the tetrahedron
        // Note:It's the instruction:
        //      trGr31.setCapability(TransformGroup.ENABLE_PICK_REPORTING)
        //      in the class SceneBuilder31 that determines if the
        //      tetrahedron is pickable or not.

        // Pick and translate the tetrahedron parallel to the z-axis if the
        // mouse pointer is over it.
        pickZoomBehavior = new PickZoomBehavior(brGr3, canvas3D, pickBounds);
        // pickZoomBehavior.setEnable(ctrlDown);
        brGr3.addChild(pickZoomBehavior);

        // Pick and translate the tetrahedron in the (x-y)-plane if the
        // mouse pointer is over it.
        pickTransBehavior = new PickTranslateBehavior(brGr3, canvas3D, pickBounds);
        // pickTransBehavior.setEnable(ctrlDown);
        brGr3.addChild(pickTransBehavior);

        // Pick and rotate the tetrahedron if the mouse pointer is over it.
        pickRotBehavior = new PickRotateBehavior(brGr3, canvas3D, pickBounds);
        // pickRotBehavior.setEnable(ctrlDown);
        brGr3.addChild(pickRotBehavior);

        // Create the subgraph32 ===> the earth in double rotation.
        sceneBuilder32 = new SceneBuilder32();
        brGr3.addChild(sceneBuilder32.mySubGraph32());

        // Create an instance of the AddDetachEarthBehavior class to
        // allow the detach/add process of the subgraph32.
        addDetachEarthBehavior = new AddDetachEarthBehavior(this, sceneBuilder32);
        addDetachEarthBehavior.setSchedulingBounds(boundsGen);
        brGr3.addChild(addDetachEarthBehavior);

        // Compile the subgraph to optimize the performances.
        brGr3.compile();

        // Return the final version of the BranchGroup node brGr3
        return brGr3;
    }

    /**
     * This method is called up in the DetachEathBehavior class to add a new
     * representation of the earth.
     */
    public void addEarth() {
        brGr3.addChild(sceneBuilder32.mySubGraph32());
    }
}

/**
 * This class serves to put the objet (a tetrahedron) into the scene. It also
 * produces a "static translation" of the tetrahedron and allows its picking.
 */

class SceneBuilder31 {
    public TransformGroup trGr31;

    private Shape3D myObject;

    private Transform3D transl;

    private Vector3d vectransl; // translation

    /**
     * Constructor that allows to specify the desired object.
     * 
     * @param javax.media.j3d.Shape3D
     *            objet - the Shape3D instance which will be attached to the
     *            subgraph #31
     */
    public SceneBuilder31(Shape3D object) {
        myObject = object;
    }

    /**
     * Create the subgraph #31 and prepare the TransformGroup node trGr31 for
     * the tetrahedron's picking.
     * 
     * @return javax.media.j3d.TransformGroup trGr31_2 - the root of the
     *         subgraph #31
     */
    public TransformGroup mySubGraph31() {
        // Create a Transform3D node to execute the desired "static translation"
        // of the tetrahedron ===> start position.
        transl = new Transform3D();
        vectransl = new Vector3d(0.0, -2.0, 0.0); // translation
        transl.set(vectransl);

        // Create the TransformGroup node trGr31, attach into it the "static
        // translation" instance and prepare it for the picking.
        trGr31 = new TransformGroup(transl);
        trGr31.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
        trGr31.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
        trGr31.setCapability(TransformGroup.ENABLE_PICK_REPORTING);

        // Attach myObject (Shape3D leaf) to the TransformGroup node trGr31.
        trGr31.addChild(myObject);

        // Return the final version of the TransformGroup node trGr31.
        return trGr31;
    }
}

/**
 * This class serves to put the rendering of five different earth's
 * representations (each with a right-handed 3D coordinate system) into the
 * scene graph. It also produces a "static translation" and a double "dynamic
 * rotation" of each earth's representations and allows to detach and then add
 * again this subgraph32 from to the entire scene graph.
 */

/*
 * Note: It is not always necessary to use "detach" and "add" to detach/add a
 * subgraph from a scene graph. In many cases the using of the setEnable()
 * method, to turn a subgraph on and off, is adequate.
 */

class SceneBuilder32 {
    public BranchGroup brGr32;

    private TransformGroup trGr32_1, trGr32_2, trGr32_3;

    private int thisEarth;

    private BoundingSphere boundsGen;

    private CoordSyst coordSyst;

    private SwitchBehavior switchBehavior;

    private Switch switchEarths; // the Switch for the 5 different earth's

    // representations

    private Transform3D transl;

    private Vector3d vectTransl; // translation

    private Alpha rotationAlpha_1, rotationAlpha_2;

    private RotationInterpolator rotator_1, rotator_2;

    // The five different earth's representations
    private Earth earth_Points, earth_Lines, earth_Polygons, earth_Gouraud, earth_Texture;

    /**
     * Create the subgraph #32
     * 
     * @return javax.media.j3d.TransformGroup trGr32_3 - the root of the
     *         subgraph #32
     */
    public BranchGroup mySubGraph32() {
        // A BoundingSphere instance as general bounding region.
        boundsGen = new BoundingSphere(new Point3d(0.0, 0.0, 0.0), 100.0);

        // Create the first TransformGroup node trGr32_1 to:
        // 1) attach the Switch node with the five different earth's
        //    representations to the subgraph32
        // 2) attach a coordinate system to each earth's representation
        // 3) rotate each earth about its own y-axis.
        trGr32_1 = new TransformGroup();

        // With the ALLOW_TRANSFORM_WRITE capability, we allow the
        // modification of the TransformGroup's code by the behavior's
        // code at run time.
        trGr32_1.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        // SwitchBehavior is the class which controls the fonctioning of
        // the switchEarths node.

        switchBehavior = new SwitchBehavior(this);
        switchBehavior.setSchedulingBounds(boundsGen);
        trGr32_1.addChild(switchBehavior);

        // The Switch which allows the rendering of the five different
        // earth's representations.
        switchEarths = new Switch();
        // With the ALLOW_TRANSFORM_WRITE, ALLOW_SWITCH_WRITE and
        // ALLOW_CHILDREN_READ
        // capabilities we allow to get or set new capabilities.
        switchEarths.setCapability(Switch.ALLOW_SWITCH_READ);
        switchEarths.setCapability(Switch.ALLOW_SWITCH_WRITE);
        switchEarths.setCapability(Switch.ALLOW_CHILDREN_READ);

        // Attach the different earth's representations to the Switch node.
        // Increasing
        earth_Points = new Earth("points", 0.4f);
        switchEarths.addChild(earth_Points.myEarth()); // # 0

        earth_Lines = new Earth("lines", 0.4f);
        switchEarths.addChild(earth_Lines.myEarth()); // # 1

        earth_Polygons = new Earth("polygons", 0.4f);
        switchEarths.addChild(earth_Polygons.myEarth()); // # 2

        earth_Gouraud = new Earth("gouraud", 0.4f);
        switchEarths.addChild(earth_Gouraud.myEarth()); // # 3

        earth_Texture = new Earth("texture", 0.4f);
        switchEarths.addChild(earth_Texture.myEarth()); // # 4

        // Decreasing
        switchEarths.addChild(earth_Texture.myEarth()); // # 4
        switchEarths.addChild(earth_Gouraud.myEarth()); // # 3
        switchEarths.addChild(earth_Polygons.myEarth()); // # 2
        switchEarths.addChild(earth_Lines.myEarth()); // # 1
        switchEarths.addChild(earth_Points.myEarth()); // # 0

        // Attach the Switch node with the five different earth's
        // representations to the TransformGroup node trGr32_1.
        trGr32_1.addChild(switchEarths);

        // Create and attach a coordinate system to the TransformGroup node
        // trGr32_1, that is to each earth's representation.
        coordSyst = new CoordSyst(1.0f, 1.0f, 0.0f, // Color of the x-axis
                0.0f, 0.0f, 1.0f, // Color of the y-axis
                1.0f, 0.0f, 0.0f, // Color of the z-axis
                0.6f); // Lenght of the 3 axes
        trGr32_1.addChild(coordSyst);

        // Create the alpha(t) function for the earth's rotation about
        // its own y-axis.
        rotationAlpha_1 = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 10000, 0, 0, 0, 0, 0);
        // Create the earth's rotation about its own y-axis.
        rotator_1 = new RotationInterpolator(rotationAlpha_1, trGr32_1, new Transform3D(), 0.0f,
                (float) Math.PI * 2.0f);
        rotator_1.setSchedulingBounds(boundsGen);
        trGr32_1.addChild(rotator_1);

        // Create a Transform3D instance to execute the desired "static
        // translation" of the earth, that is the rotation radius around
        // the sun.
        transl = new Transform3D();
        vectTransl = new Vector3d(2.5, 0.0, 0.0);
        transl.set(vectTransl);

        // Create the second TransformGroup node trGr32_2 and attach the
        // "static translation" transl to it.
        trGr32_2 = new TransformGroup(transl);

        // Attach the trGr32_1 node to the trGr32_2 node.
        trGr32_2.addChild(trGr32_1);

        // Create the third TransformGroup node trGr32_3 for the earth's
        // rotation around the sun.
        trGr32_3 = new TransformGroup();

        // With the ALLOW_TRANSFORM_WRITE capability, we allow the
        // modification of the TransformGroup's code by the behavior's
        // code at run time.
        trGr32_3.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);

        // Attach the trGr32_2 node to the trGr32_3 node.
        trGr32_3.addChild(trGr32_2);

        // Create the alpha(t) function for the earth's rotation around the sun.
        rotationAlpha_2 = new Alpha(-1, Alpha.INCREASING_ENABLE, 0, 0, 20000, 0, 0, 0, 0, 0);

        // To restart correctly the rotation of the earth around the
        // sun after a detach/add process of the subgraph32 from the
        // BranchGroup node brGr3.
        rotationAlpha_2.setStartTime(System.currentTimeMillis());

        // Create the earth's rotation around the sun.
        rotator_2 = new RotationInterpolator(rotationAlpha_2, trGr32_3, new Transform3D(), 0.0f,
                (float) Math.PI * 2.0f);
        rotator_2.setSchedulingBounds(boundsGen);
        trGr32_3.addChild(rotator_2);

        // To allow the detaching of this subgraph32 from the
        // BranchGroup node brGr3.
        brGr32 = new BranchGroup();
        brGr32.setCapability(BranchGroup.ALLOW_DETACH);
        brGr32.addChild(trGr32_3);

        // Return the final version of the BranchGroup node brGr32.
        return brGr32;
    }

    /**
     * This method is called up in the SwitchBehavior class and gets the new
     * earth's representation which has to be drawn.
     * 
     * @param thisEarth -
     *            the new earth's representation to draw.
     */
    public void setNewEarth(int thisEarth) {
        switchEarths.setWhichChild(thisEarth);
    }

    // This method is called up in the DetachEathBehavior class to
    // detach the momentary representation of the earth.
    public void detachEarth() {
        brGr32.detach();
    }
}
/*
 * @(#)NewTextureLoades.java 1.0 99/10/21
 * 
 * Copyright (c) 1996-1999 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 * 
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.
 * 
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear facility.
 * Licensee represents and warrants that it will not use or redistribute the
 * Software for such purposes.
 */

/**
 * A texture loading utility that doesn't require an image observer for
 * constructing objects. This class extends the TextureLoader class of the
 * com.sun.j3d.utils.image package.
 *  
 */

class NewTextureLoader extends TextureLoader {
    static java.awt.Component observer;

    /**
     * Specify an object to server as the image observer. Use this method once
     * before constructing any texture loaders.
     * 
     * @param imageObserver
     *            the object to be used in subsequent NewTextureLoader
     *            constuctions
     */
    public static void setImageObserver(java.awt.Component imageObserver) {
        observer = imageObserver;
    }

    /**
     * Retreve the object used as the image observer for NewTextureLoader
     * objects. Use this method when the image observer is needed.
     * 
     * @return the object used in as the image observer in subsequent
     *         NewTextureLoader constuctions
     */
    public static java.awt.Component getImageObserver() {
        return observer;
    }

    // constructors without an image observer argument
    /**
     * Constructs a NewTextureLoader object loading the specified iamge in
     * default (RGBA) format. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param image
     *            the image object to load
     */
    public NewTextureLoader(java.awt.Image image) {
        super(image, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified image and
     * option flags in the default (RGBA) format. The an image observer must be
     * set using the setImageObserver() method before using this constructor.
     * 
     * @param image
     *            the image object to load
     * @param flags
     *            the flags to use in construction (e.g. generate mipmap)
     */
    public NewTextureLoader(java.awt.Image image, int flags) {
        super(image, flags, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified file using the
     * specified format. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param image
     *            the image object to load
     * @param format
     *            specificaiton of which channels to use (e.g. RGB)
     */
    public NewTextureLoader(java.awt.Image image, java.lang.String format) {
        super(image, format, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified file with
     * specified format and flags. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param image
     *            the image object to load
     * @param format
     *            specificaiton of which channels to use (e.g. RGB)
     * @param flags
     *            the flags to use in construction (e.g. generate mipmap)
     */
    public NewTextureLoader(java.awt.Image image, java.lang.String format, int flags) {
        super(image, format, flags, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified file using the
     * default format (RGBA). The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param fname
     *            the name of the file to load
     */
    public NewTextureLoader(java.lang.String fname) {
        super(fname, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified file with the
     * specified flags. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param fname
     *            the name of the file to load
     * @param flags
     *            the flags to use in construction (e.g. generate mipmap)
     */
    public NewTextureLoader(java.lang.String fname, int flags) {
        super(fname, flags, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified file using the
     * specified format. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param fname
     *            the name of the file to load
     * @param format
     *            specificaiton of which channels to use (e.g. RGB)
     */
    public NewTextureLoader(java.lang.String fname, java.lang.String format) {
        super(fname, format, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified file using the
     * specified format and flags. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param fname
     *            the name of the file to load
     * @param format
     *            specificaiton of which channels to use (e.g. RGB)
     * @param flags
     *            the flags to use in construction (e.g. generate mipmap)
     */
    public NewTextureLoader(java.lang.String fname, java.lang.String format, int flags) {
        super(fname, format, flags, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified URL using the
     * default format. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param url
     *            specifies the URL of the image to load
     */
    public NewTextureLoader(java.net.URL url) {
        super(url, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified URL using the
     * specified flags. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param url
     *            specifies the URL of the image to load
     * @param flags
     *            the flags to use in construction (e.g. generate mipmap)
     */
    public NewTextureLoader(java.net.URL url, int flags) {
        super(url, flags, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified URL using the
     * specified format. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param url
     *            specifies the URL of the image to load
     * @param format
     *            specificaiton of which channels to use (e.g. RGB)
     */
    public NewTextureLoader(java.net.URL url, java.lang.String format) {
        super(url, format, observer);
    }

    /**
     * Constructs a NewTextureLoader object loading the specified URL using the
     * specified format and flags. The an image observer must be set using the
     * setImageObserver() method before using this constructor.
     * 
     * @param url
     *            specifies the URL of the image to load
     * @param format
     *            specificaiton of which channels to use (e.g. RGB)
     * @param flags
     *            the flags to use in construction (e.g. generate mipmap)
     */
    public NewTextureLoader(java.net.URL url, java.lang.String format, int flags) {
        super(url, format, flags, observer);
    }

} // end of TexturedPlane class

/**
 * This class is a keyboard behavior to control the partly rotation (rotate up
 * and rotate down) of the camera.
 */

class Camera_Rot_Up_Down extends Behavior {
    // The TransformGroup node to modify by the keyboard interaction.
    private TransformGroup target_trGr;

    // Wake up event when a key is pressed.
    private WakeupOnAWTEvent wakeUp = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);

    // The key event
    private KeyEvent keyEvent;

    // The angle to turn when the Home key or End key is pressed.
    private float angle = (float) Math.PI / 36;

    private Transform3D myKeyNavTransf3D = new Transform3D();

    private Transform3D rotation = new Transform3D();

    /**
     * Constructor that allows to specify the desired target transform group.
     * 
     * @param javax.media.j3d.TransformGroup
     *            target - the target transform group
     */
    public Camera_Rot_Up_Down(TransformGroup target) {
        target_trGr = target;
    }

    /**
     * Override Behavior's initialize method to setup wakeup criteria.
     */
    public void initialize() {
        wakeupOn(wakeUp);
    }

    /**
     * Override Behavior's stimulus method to handle the event. This method is
     * called when a key on the keyboard has been pressed and operates on the
     * specified transform group to move the camera.
     * 
     * @param Enumeration
     *            criteria - all pressed keys in a list. This will be passed by
     *            the system.
     */
    public void processStimulus(Enumeration criteria) {
        WakeupOnAWTEvent eventToWakeUp;
        AWTEvent[] events;

        if (criteria.hasMoreElements()) {
            // Decode the wakeup criteria
            eventToWakeUp = (WakeupOnAWTEvent) criteria.nextElement();
            events = eventToWakeUp.getAWTEvent();
            keyEvent = (KeyEvent) events[0];
            int keyCode = keyEvent.getKeyCode();

            // Perform our processing

            // Get the initial transformation from target and put it
            // into myKeyNavTransf3D
            target_trGr.getTransform(myKeyNavTransf3D);

            // Not any of the 2 rotations don't act simultaneously.
            switch (keyCode) {
            case KeyEvent.VK_HOME: // Home - rotate up
                rotation.rotX(angle);
                break;

            case KeyEvent.VK_END: // End - rotate down
                rotation.rotX(-angle);
                break;

            default:
                rotation.rotX(0.0f);
            }

            myKeyNavTransf3D.mul(rotation);

            // Return the final transformation myKeyNavTransf3D to target
            target_trGr.setTransform(myKeyNavTransf3D);
        }

        // Set wakeup criteria for next time.
        wakeupOn(wakeUp);
    }
}

/**
 * This class creates an aimer in the ViewBranch.
 */

class Aimer extends Shape3D {
    private float scale_XYZ;

    private LineArray aimer;

    private float scaledExtremites[];

    /**
     * Constructor that allows to specify the desired initial aimer's
     * dimensions.
     * 
     * @param type
     *            float s_XYZ - the scale factor to adjust the aimer's
     *            dimensions
     */
    public Aimer(float s_XYZ) {
        scale_XYZ = s_XYZ;
    }

    /**
     * Construct an aimer.
     * 
     * @return javax.media.j3d.Shape3D myAimer - the constructed aimer.
     */
    public Shape3D myAimer() {
        // Construction of the aimer (LineArray).
        aimer = new LineArray(20, LineArray.COORDINATES | LineArray.COLOR_3);

        // Scalling of the vertices of the aimer using scale_XYZ.
        scaledExtremites = new float[extremites.length];
        for (int i = 0; i < extremites.length; i++)
            scaledExtremites[i] = extremites[i] * scale_XYZ;

        aimer.setCoordinates(0, scaledExtremites);
        aimer.setColors(0, color);

        this.setGeometry(aimer);

        return this;
    }

    // Aimer's geometry
    private static final float extremites[] = {
            // top-front
            0.075f, 0.05f, -1.0f, -0.075f, 0.05f, -1.0f,

            // left-front
            -0.075f, 0.05f, -1.0f, -0.075f, -0.05f, -1.0f,

            // bottom-front
            -0.075f, -0.05f, -1.0f, 0.075f, -0.05f, -1.0f,

            // right-front
            0.075f, -0.05f, -1.0f, 0.075f, 0.05f, -1.0f,

            // top-back
            0.04f, 0.025f, -1.0f, -0.04f, 0.025f, -1.0f,

            // left-back
            -0.04f, 0.025f, -1.0f, -0.04f, -0.025f, -1.0f,

            // bottom-back
            -0.04f, -0.025f, -1.0f, 0.04f, -0.025f, -1.0f,

            // right-back
            0.04f, -0.025f, -1.0f, 0.04f, 0.025f, -1.0f,

            // cross
            -0.04f, 0.025f, -1.0f, 0.04f, -0.025f, -1.0f, -0.04f, -0.025f, -1.0f, 0.04f, 0.025f, -1.0f };

    // Colors of the aimer (each vertex in aimer is red).
    float color[] = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // front-frame
            1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
            0.0f,

            1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // back-frame
            1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
            0.0f,

            1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // cross
            1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
}

/**
 * This class creates a simple cube with a given texture to simulate the inside
 * of the virtual universe.
 */

class BordersIn extends Shape3D {
    private QuadArray cube;

    private float scale_XYZ;

    private NewTextureLoader newTextureLoader;

    private Texture texture;

    private TextureAttributes textAttr;

    /**
     * Constructor that allows to specify the desired scale factor of the
     * virtual universe.
     * 
     * @param type
     *            float s_XYZ - the scale factor to adjust the borders of the
     *            virtual universe
     */
    public BordersIn(float s_XYZ) {
        scale_XYZ = s_XYZ;
    }

    /**
     * Construction of the desired borders of the virtual universe (cube).
     * 
     * @return javax.media.j3d.Shape3D myUniverse - the constructed borders of
     *         the virtual universe
     */
    public Shape3D myInternalUniverse() {
        cube = new QuadArray(cubeFaces.length, QuadArray.COORDINATES | QuadArray.TEXTURE_COORDINATE_2);

        ////////////////////// Geometric part ///////////////////////////

        // Scaling of the faces.
        for (int i = 0; i < cubeFaces.length; i++)
            cubeFaces[i].scale(scale_XYZ);

        cube.setCoordinates(0, cubeFaces);

        for (int i = 0; i < cubeFaces.length; i++) {
            // With i mod 4 ==> 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 for
            // the 4 vertices of the 6 faces, thus each vertex has
            // a point in the texture space. In this case, each cube's
            // face has the same texture coordinates.
            cube.setTextureCoordinate(0, i, textCoord[i % 4]);
        }

        // The geometry is passed to the instance this of the cube.
        this.setGeometry(cube);

        ////////////////////// Appearance part ///////////////////////////

        Appearance appearance = new Appearance();

        // This code block is only necessary to insure, in all cases, the
        // correct
        // rendering of the 6 faces of the cube (bug in Java3D version 1.2.0 !).
        // Set up the polygon's rendering-mode
        PolygonAttributes polygonAttributes = new PolygonAttributes();
        polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL);
        appearance.setPolygonAttributes(polygonAttributes);

        // Loading the texture for the 6 cube's faces.
        newTextureLoader = new NewTextureLoader("Images/Galaxies.gif");
        newTextureLoader.setImageObserver(newTextureLoader.getImageObserver());
        texture = newTextureLoader.getTexture();
        appearance.setTexture(texture);

        // Application modes of the texture
        textAttr = new TextureAttributes();
        textAttr.setTextureMode(TextureAttributes.MODULATE); // there still are:
        // BLEND, COMBINE,
        // DECAL, and REPLACE
        appearance.setTextureAttributes(textAttr);

        // The appearance is passed to the instance this of the cube.
        this.setAppearance(appearance);

        return this;
    }

    // The 8 vertices p1, p2, ..., p8 of the cube.
    private static final Point3f p1 = new Point3f(1.0f, 1.0f, 1.0f);

    private static final Point3f p2 = new Point3f(1.0f, -1.0f, 1.0f);

    private static final Point3f p3 = new Point3f(-1.0f, -1.0f, 1.0f);

    private static final Point3f p4 = new Point3f(-1.0f, 1.0f, 1.0f);

    private static final Point3f p5 = new Point3f(-1.0f, 1.0f, -1.0f);

    private static final Point3f p6 = new Point3f(-1.0f, -1.0f, -1.0f);

    private static final Point3f p7 = new Point3f(1.0f, -1.0f, -1.0f);

    private static final Point3f p8 = new Point3f(1.0f, 1.0f, -1.0f);

    // The 6 faces of the cube.
    private static final Point3f cubeFaces[] = { // internal front face
            p5, p6, p7, p8,

            // internal right face
            p1, p8, p7, p2,

            // internal back face
            p1, p2, p3, p4,

            // internal left face
            p4, p3, p6, p5,

            // internal top face
            p1, p4, p5, p8,

            // internal bottom face
            p3, p2, p7, p6 };

    // Coordinates in the texture space. Each cube's face has the
    // same texture coordinates.
    private TexCoord2f textCoord[] = { new TexCoord2f(0.0f, 0.0f), new TexCoord2f(0.0f, 1.0f),
            new TexCoord2f(1.0f, 1.0f), new TexCoord2f(1.0f, 0.0f) };
}

/**
 * This class creates a simple cube with a given texture to simulate the outside
 * of the virtual universe.
 */

class BordersOut extends Shape3D {
    private QuadArray cube;

    private float scale_XYZ;

    private NewTextureLoader newTextureLoader;

    private Texture texture;

    private TextureAttributes textAttr;

    /**
     * Constructor that allows to specify the desired scale factor of the
     * virtual universe.
     * 
     * @param type
     *            float s_XYZ - the scale factor to adjust the borders of the
     *            virtual universe
     */
    public BordersOut(float s_XYZ) {
        scale_XYZ = s_XYZ;
    }

    /**
     * Construction of the desired borders of the virtual universe (cube).
     * 
     * @return javax.media.j3d.Shape3D myUniverse - the constructed borders of
     *         the virtual universe
     */
    public Shape3D myExternalUniverse() {
        cube = new QuadArray(cubeFaces.length, QuadArray.COORDINATES | QuadArray.TEXTURE_COORDINATE_2);

        ////////////////////// Geometric part ///////////////////////////

        // Scaling of the faces.
        for (int i = 0; i < cubeFaces.length; i++)
            cubeFaces[i].scale(scale_XYZ);

        cube.setCoordinates(0, cubeFaces);

        for (int i = 0; i < cubeFaces.length; i++) {
            // With i mod 4 ==> 0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3 for
            // the 4 vertices of the 6 faces, thus each vertex has
            // a point in the texture space. In this case, each cube's
            // face has the same texture coordinates.
            cube.setTextureCoordinate(0, i, textCoord[i % 4]);
        }

        // The geometry is passed to the instance this of the cube.
        this.setGeometry(cube);

        ////////////////////// Appearance part ///////////////////////////

        Appearance appearance = new Appearance();

        // This code block is only necessary to insure, in all cases, the
        // correct
        // rendering of the 6 faces of the cube (bug in Java3D version 1.2.0 !).
        // Set up the polygon's rendering-mode
        PolygonAttributes polygonAttributes = new PolygonAttributes();
        polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL);
        appearance.setPolygonAttributes(polygonAttributes);

        // Loading the texture for the 6 cube's faces.
        newTextureLoader = new NewTextureLoader("Images/Ciel_Outside.jpg");
        newTextureLoader.setImageObserver(newTextureLoader.getImageObserver());
        texture = newTextureLoader.getTexture();
        appearance.setTexture(texture);

        // Application modes of the texture
        textAttr = new TextureAttributes();
        textAttr.setTextureMode(TextureAttributes.MODULATE); // there still are:
        // BLEND, COMBINE,
        // DECAL, and REPLACE
        appearance.setTextureAttributes(textAttr);

        // The appearance is passed to the instance this of the cube.
        this.setAppearance(appearance);

        return this;
    }

    // The 8 vertices p1, p2, ..., p8 of the cube.
    private static final Point3f p1 = new Point3f(1.0f, 1.0f, 1.0f);

    private static final Point3f p2 = new Point3f(1.0f, -1.0f, 1.0f);

    private static final Point3f p3 = new Point3f(-1.0f, -1.0f, 1.0f);

    private static final Point3f p4 = new Point3f(-1.0f, 1.0f, 1.0f);

    private static final Point3f p5 = new Point3f(-1.0f, 1.0f, -1.0f);

    private static final Point3f p6 = new Point3f(-1.0f, -1.0f, -1.0f);

    private static final Point3f p7 = new Point3f(1.0f, -1.0f, -1.0f);

    private static final Point3f p8 = new Point3f(1.0f, 1.0f, -1.0f);

    // The 6 faces of the cube.
    private static final Point3f cubeFaces[] = { // external front face
            p5, p8, p7, p6,

            // external right face
            p8, p1, p2, p7,

            // external back face
            p1, p4, p3, p2,

            // external left face
            p4, p5, p6, p3,

            // external top face
            p8, p5, p4, p1,

            // external bottom face
            p2, p3, p6, p7 };

    // Coordinates in the texture space. Each cube's face has the
    // same texture coordinates.
    private TexCoord2f textCoord[] = { new TexCoord2f(1.0f, 1.0f), new TexCoord2f(0.0f, 1.0f),
            new TexCoord2f(0.0f, 0.0f), new TexCoord2f(1.0f, 0.0f) };
}

/**
 * This class is a switch behavior to control the rendering of the five
 * different earth's representations.
 */

class SwitchBehavior extends Behavior {
    // The Alpha class which gives the alpha values to command the Switch node.
    private Alpha switchAlpha;

    private float switchAlphaValue;

    // The class which contains the Switch node for the rendering of
    // the 5 different earth's representations.
    private SceneBuilder32 sceneBuilder32 = null;

    // The earth which will be rendered.
    private int thisEarth = 0;

    // Wakeup event after each frame.
    private WakeupOnElapsedFrames wakeUp = new WakeupOnElapsedFrames(0);

    /**
     * Constructor that allows to specify the reference of the SceneBuilder32's
     * instance.
     * 
     * @param sceneBuilder32 -
     *            the SceneBuilder32 instance
     */
    public SwitchBehavior(SceneBuilder32 sceneBuilder32) {
        super();

        // Create the alpha(t) function to automaticaly switch between the
        // five different earth's representations.
        switchAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE | Alpha.DECREASING_ENABLE, 0, 0, 10000, 0, 0, 10000, 0,
                0);

        // Get the SceneBuilder32 reference
        this.sceneBuilder32 = sceneBuilder32;
    }

    /**
     * Override Behavior's initialize method to setup wakeup criteria.
     */
    public void initialize() {
        wakeupOn(wakeUp);
    }

    /**
     * Override Behavior's stimulus method to handle the event. This method is
     * called up when the define number of frames is draw.
     * 
     * @param criteria -
     *            the wake-up criteria
     */
    public void processStimulus(Enumeration criteria) {
        switchAlphaValue = switchAlpha.value();

        if (switchAlphaValue <= 0.15f)
            thisEarth = 0;
        else if (0.15f < switchAlphaValue && switchAlphaValue <= 0.4f)
            thisEarth = 1;
        else if (0.4f < switchAlphaValue && switchAlphaValue <= 0.6f)
            thisEarth = 2;
        else if (0.6f < switchAlphaValue && switchAlphaValue <= 0.8f)
            thisEarth = 3;
        else if (0.8f < switchAlphaValue)
            thisEarth = 4;

        sceneBuilder32.setNewEarth(thisEarth);

        // Set wakeup criteria for next time.
        wakeupOn(wakeUp);
    }
}

/**
 * This class creates a simple colored cube.
 */

class ColorCube extends Shape3D {
    private QuadArray cube;

    // The constant colors of each cube's face
    private static final Color3f red = new Color3f(1.0f, 0.0f, 0.0f);

    private static final Color3f green = new Color3f(0.0f, 1.0f, 0.0f);

    private static final Color3f blue = new Color3f(0.0f, 0.0f, 1.0f);

    private static final Color3f yellow = new Color3f(1.0f, 1.0f, 0.0f);

    private static final Color3f magenta = new Color3f(1.0f, 0.0f, 1.0f);

    private static final Color3f cyan = new Color3f(0.0f, 1.0f, 1.0f);

    /**
     * Constructor that allows to specify the desired scale factor for the
     * colored cube.
     * 
     * @param type
     *            float s_XYZ - the scale factor to adjust the edges's length of
     *            the colored cube
     */
    public ColorCube(float scale_XYZ) { // 24
        cube = new QuadArray(cubeFaces.length, QuadArray.COORDINATES | QuadArray.COLOR_3);

        // Scaling of vertices
        for (int i = 0; i < cubeFaces.length; i++)
            cubeFaces[i].scale(scale_XYZ);

        cube.setCoordinates(0, cubeFaces);
        cube.setColors(0, colorsFaces);

        this.setGeometry(cube);
    }

    // The 8 vertices p1, ..., p8 of the cube.
    private static final Point3f p1 = new Point3f(1.0f, 1.0f, 1.0f);

    private static final Point3f p2 = new Point3f(-1.0f, 1.0f, 1.0f);

    private static final Point3f p3 = new Point3f(-1.0f, -1.0f, 1.0f);

    private static final Point3f p4 = new Point3f(1.0f, -1.0f, 1.0f);

    private static final Point3f p5 = new Point3f(1.0f, -1.0f, -1.0f);

    private static final Point3f p6 = new Point3f(1.0f, 1.0f, -1.0f);

    private static final Point3f p7 = new Point3f(-1.0f, 1.0f, -1.0f);

    private static final Point3f p8 = new Point3f(-1.0f, -1.0f, -1.0f);

    // The 6 faces of the cube.
    private static final Point3f cubeFaces[] = { // front face
            p1, p2, p3, p4,
            // right face
            p1, p4, p5, p6,
            // back face
            p8, p7, p6, p5,
            // left face
            p2, p7, p8, p3,
            // top face
            p1, p6, p7, p2,
            // bottom face
            p5, p4, p3, p8 };

    // The constant colors for the 6 faces.
    private static final Color3f[] colorsFaces = { // front face
            red, red, red, red,
            // back face
            green, green, green, green,
            // right face
            blue, blue, blue, blue,
            // left face
            yellow, yellow, yellow, yellow,
            // top face
            magenta, magenta, magenta, magenta,
            // bottom face
            cyan, cyan, cyan, cyan };
}

/**
 * This class is used to detach then add again the subgraph 32 from the scene
 * graph.
 */

/*
 * Note: It is not always necessary to use "detach" and "add" to add/remove a
 * subgraph from a scene graph. In many cases the using of the setEnable()
 * method, to turn a subgraph on and off, is adequate.
 */

class AddDetachEarthBehavior extends Behavior {
    // The classe which contains the addEarth method.
    private SceneBuilder3 sceneBuilder3;

    // The classe which contains the detachEarth method.
    private SceneBuilder32 sceneBuilder32;

    // To control the detach / add sequence.
    private boolean validation = false;

    // Wake up event when a key is pressed.
    private WakeupOnAWTEvent wakeUp = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);

    // The key event
    private KeyEvent keyEvent;

    /**
     * Constructor that allows to specify the references of the SceneBuilder3's
     * and SceneBuilder32's instances.
     * 
     * @param sceneBuilder3 -
     *            the SceneBuilder3 instance
     * @param sceneBuilder32 -
     *            the SceneBuilder32 instance
     */
    public AddDetachEarthBehavior(SceneBuilder3 sceneBuilder3, SceneBuilder32 sceneBuilder32) {
        super();

        // Get the SceneBuilder3's and SceneBuilder32's references
        this.sceneBuilder3 = sceneBuilder3;
        this.sceneBuilder32 = sceneBuilder32;
    }

    /**
     * Override Behavior's initialize method to setup wakeup criteria.
     */
    public void initialize() {
        wakeupOn(wakeUp);
    }

    /**
     * Override Behavior's stimulus method to handle the event. This method is
     * called when a key on the keyboard has been pressed and operates on the
     * specified transform group to move the camera.
     * 
     * @param Enumeration
     *            criteria - all pressed keys in a list. This will be passed by
     *            the system.
     */
    public void processStimulus(Enumeration criteria) {
        WakeupOnAWTEvent eventToWakeUp;
        AWTEvent[] events;

        if (criteria.hasMoreElements()) {
            // Decode the wakeup criteria
            eventToWakeUp = (WakeupOnAWTEvent) criteria.nextElement();
            events = eventToWakeUp.getAWTEvent();
            keyEvent = (KeyEvent) events[0];
            int keyCode = keyEvent.getKeyCode();

            // Perform our processing
            switch (keyCode) {
            case KeyEvent.VK_A:
                if (validation) {
                    sceneBuilder3.addEarth();
                    System.out.println("===>    Add Earth");
                    validation = false;
                }
                break;

            case KeyEvent.VK_D:
                if (!validation) {
                    sceneBuilder32.detachEarth();
                    System.out.println("===>    Detach Earth");
                    validation = true;
                }
                break;

            default:
            }
        }

        // Set wakeup criteria for next time.
        wakeupOn(wakeUp);
    }
}

/**
 * This class is a keyboard behavior to control the translation and the partly
 * rotation (turn left and turn right) of the camera.
 * 
 * After a translation or rotation performed by using this class, the
 * observation direction of the virtual camera will always stay parallel to the
 * coordinate system's (x-y)-plane of the parent node.
 * 
 * In the case of this Demo application, it will be the coordinate system
 * associated with the Locale node.
 */

class Camera_Transl_Turn extends Behavior {

    // The TransformGroup node to modify by the keyboard interaction.
    private TransformGroup target_trGr;

    // Wake up event when a key is pressed.
    private WakeupOnAWTEvent wakeUp = new WakeupOnAWTEvent(KeyEvent.KEY_PRESSED);

    // The key event
    private KeyEvent keyEvent;

    // The angle to turn when a Direction Key and the Shift key are pressed.
    private float angle = (float) Math.PI / 36;

    // The step size to move when a Direction Key or the PageDown or
    // PageUp key is pressed.
    private float step = 1f;

    private Transform3D myKeyNavTransf3D = new Transform3D();

    private Vector3f translation = new Vector3f();

    private Vector3f dtrans = new Vector3f();

    private Vector3f dtrans0 = new Vector3f();

    private Transform3D trans_rot = new Transform3D();

    /**
     * Constructor that allows to specify the desired target transform group.
     * 
     * @param javax.media.j3d.TransformGroup
     *            target - the target transform group
     */
    public Camera_Transl_Turn(TransformGroup target) {
        target_trGr = target;
    }

    /**
     * Override Behavior's initialize method to setup wakeup criteria.
     */
    public void initialize() {
        wakeupOn(wakeUp);
    }

    /**
     * Override Behavior's stimulus method to handle the event. This method is
     * called when a key on the keyboard has been pressed and operates on the
     * specified transform group to move the camera.
     * 
     * @param Enumeration
     *            criteria - all pressed keys in a list. This will be passed by
     *            the system.
     */
    public void processStimulus(Enumeration criteria) {
        WakeupOnAWTEvent eventToWakeUp;
        AWTEvent[] events;

        if (criteria.hasMoreElements()) {
            // Decode the wakeup criteria
            eventToWakeUp = (WakeupOnAWTEvent) criteria.nextElement();
            events = eventToWakeUp.getAWTEvent();
            keyEvent = (KeyEvent) events[0];
            int keyCode = keyEvent.getKeyCode();

            // Perform our processing

            // Get the initial transformation from target and put it into
            // myKeyNavTransf3D
            target_trGr.getTransform(myKeyNavTransf3D);

            // Set the translational components of myKeyNavTransf3D in
            // translation
            myKeyNavTransf3D.get(translation);

            // Not any of the 8 motions (6 translations and 2 rotations)
            // don't act simultaneously.
            switch (keyCode) {
            case KeyEvent.VK_UP: // Up Arrow - to move forward
                trans_rot.set(new Vector3f(0.0f, 0.0f, -step));
                break;

            case KeyEvent.VK_DOWN: // Down Arrow - to move backwards
                trans_rot.set(new Vector3f(0.0f, 0.0f, step));
                break;

            case KeyEvent.VK_LEFT: // Left Arrow -to turn left or move left
                if (keyEvent.isShiftDown())
                    trans_rot.set(new Vector3f(-step, 0.0f, 0.0f));
                else
                    trans_rot.rotY(angle);
                break;

            case KeyEvent.VK_RIGHT: // Right Arrow - to turn right or move right
                if (keyEvent.isShiftDown())
                    trans_rot.set(new Vector3f(step, 0.0f, 0.0f));
                else
                    trans_rot.rotY(-angle);
                break;

            case KeyEvent.VK_PAGE_DOWN: // Page Down - to move down
                trans_rot.set(new Vector3f(0.0f, -step, 0.0f));
                break;

            case KeyEvent.VK_PAGE_UP: // Page Up - to move up
                trans_rot.set(new Vector3f(0.0f, step, 0.0f));
                break;

            default:
                trans_rot.set(new Vector3f(0.0f, 0.0f, 0.0f));
            }

            myKeyNavTransf3D.mul(trans_rot);

            // Return the final transformation myKeyNavTransf3D to target
            target_trGr.setTransform(myKeyNavTransf3D);
        }

        // Set wakeup criteria for next time.
        wakeupOn(wakeUp);
    }
}

/**
 * This class creates the Earth by using the Sphere class and a given texture
 * (Earth.jpg).
 */

class Earth {
    public Sphere earth;

    private String renderingType;

    private float scale_XYZ;

    private Color3f diffAmb, reflDiff, reflSpec, emittedLight;

    private float c; //  shininess;

    private Appearance appearance;

    private Material material;

    private ColoringAttributes coloringAttributes;

    private PolygonAttributes polygonAttributes;

    private NewTextureLoader newTextureLoader;

    private Texture texture;

    private TextureAttributes textAttr;

    /**
     * Constructor that allows to specify the desired rendering's mode and the
     * desired scale factor.
     * 
     * @param java.lang.String
     *            nom - the desired type of earth's rendering,
     * @param type
     *            float s_XYZ - the scale factor to adjust the earth's radius
     */
    public Earth(String name, float s_XYZ) {
        renderingType = name;
        scale_XYZ = s_XYZ;
    }

    /**
     * This methode serves to construct the earth.
     * 
     * @return com.sun.j3d.utils.geometry.Sphere earth - the constructed earth
     */
    public Sphere myEarth() {
        // Optical properties of the earth.

        // Ambient-diffuse-reflection coefficient
        diffAmb = new Color3f(1.0f, 1.0f, 1.0f);
        // Diffuse-reflection coefficient
        reflDiff = new Color3f(1.0f, 1.0f, 1.0f);
        // Specular-reflection coefficient (reflectance function)
        reflSpec = new Color3f(0.0f, 0.0f, 0.1f);
        // c = shininess: cos^c in the specular reflection
        c = 1;
        // Emitted light
        emittedLight = new Color3f(0.0f, 0.0f, 0.0f);

        appearance = new Appearance();

        // Create the material and set up the optical properties.
        material = new Material(diffAmb, emittedLight, reflDiff, reflSpec, c);
        appearance.setMaterial(material);

        // Set up the polygon's rendering-mode (with the polygonAttributes) and
        // the shading-mode (with the coloringAttributes).
        polygonAttributes = new PolygonAttributes();
        coloringAttributes = new ColoringAttributes();

        // Points
        if (renderingType.compareTo("points") == 0) {
            polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_POINT);
        }

        /* Lines*/
        else if (renderingType.compareTo("lines") == 0) {
            polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_LINE);
        }

        /* Polygons */
        else if (renderingType.compareTo("polygons") == 0) {
            /* is the default value*/
            polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL);
            coloringAttributes.setShadeModel(ColoringAttributes.SHADE_FLAT);
        }

        /* Gouraud */
        else if (renderingType.compareTo("gouraud") == 0) {
            polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL); /* is the default value*/
            coloringAttributes.setShadeModel(ColoringAttributes.SHADE_GOURAUD); /* is the default value*/
        }

        else if (renderingType.compareTo("texture") == 0) {
            polygonAttributes.setPolygonMode(PolygonAttributes.POLYGON_FILL); /* is the default value*/
            coloringAttributes.setShadeModel(ColoringAttributes.SHADE_GOURAUD); /* is the default value*/

            /* Loading of the texture*/
            newTextureLoader = new NewTextureLoader("Images/Earth.jpg");
            newTextureLoader.setImageObserver(newTextureLoader.getImageObserver());
            texture = newTextureLoader.getTexture();

            appearance.setTexture(texture);

            /* Application mode of the texture */
            textAttr = new TextureAttributes();
            textAttr.setTextureMode(TextureAttributes.REPLACE); /* there still are:
                                                                BLEND, COMBINE,
                                                                DECAL, and MODULATE*/
            appearance.setTextureAttributes(textAttr);
        }

        appearance.setPolygonAttributes(polygonAttributes);
        appearance.setColoringAttributes(coloringAttributes);

        /* Construction of the earth with all its features.*/
        earth = new Sphere(scale_XYZ, Sphere.GENERATE_NORMALS | Sphere.GENERATE_TEXTURE_COORDS, 10, appearance);
        return earth;
    }
}

/**
 * This class creates a tetrahedron with a given texture (Claude.jpg).
 */

class Tetrahedron extends Shape3D {
    private float scale_XYZ;

    private Point3f vertices[];

    private int lengthVertices = 4, lengthTetraFaceIndices = 12; // 4 faces x 3
    // vertices

    private int tetraFaceIndices[], textCoordIndices[];

    private GeometryInfo tetra_GeometryInfo;

    private NormalGenerator normalGenerator;

    private Stripifier stripifier;

    private GeometryArray tetra_GeometryArray;

    private TexCoord2f textCoord2f[]; // for triangles

    private boolean crAngle;

    private Appearance appearance;

    private Color3f diffAmb, reflDiff, reflSpec, emittedLight;

    private Material material;

    private TransparencyAttributes trAttr;

    private NewTextureLoader newTextureLoader;

    private Texture texture;

    private TextureAttributes textAttr;

    /**
     * Constructor that allows to specify the desired scale factor.
     * 
     * @param type
     *            float s_XYZ - the scale factor to adjust the edges's length of
     *            the tetrahedron
     */
    public Tetrahedron(float s_XYZ) {
        scale_XYZ = s_XYZ;
    }

    /**
     * Construction of the desired tetrahedron.
     * 
     * @return javax.media.j3d.Shape3D myTetrahedron - the constructed
     *         tetrahedron
     */
    public Shape3D myTetrahedron() {

        ////////////////////// Geometric part ///////////////////////////

        // The 4 vertices p0, p1, p2 and p3 of the tetrahedron.
        vertices = new Point3f[lengthVertices]; // 4
        vertices[0] = new Point3f(0.0f, 0.0f, 0.0f);
        vertices[1] = new Point3f(1.0f, 0.0f, 0.0f);
        vertices[2] = new Point3f(0.0f, 1.0f, 0.0f);
        vertices[3] = new Point3f(0.0f, 0.0f, 1.0f);

        // Scaling of vertices
        for (int i = 0; i < lengthVertices; i++)
            // lengthVertices = 4
            vertices[i].scale(scale_XYZ);

        // Set the face's indices for the tetrahedron (referenced to the array
        // of vertices
        // by setCoordinates(vertices) and
        // setCoordinateIndices(tetraFaceIndices)).
        tetraFaceIndices = new int[lengthTetraFaceIndices]; // 12
        // From the camera in the view coordinate system
        // bottom
        tetraFaceIndices[0] = 0;
        tetraFaceIndices[1] = 1;
        tetraFaceIndices[2] = 3;
        // back-left face
        tetraFaceIndices[3] = 0;
        tetraFaceIndices[4] = 3;
        tetraFaceIndices[5] = 2;
        // back face
        tetraFaceIndices[6] = 0;
        tetraFaceIndices[7] = 2;
        tetraFaceIndices[8] = 1;
        // front face
        tetraFaceIndices[9] = 1;
        tetraFaceIndices[10] = 2;
        tetraFaceIndices[11] = 3;

        // Create the GeometryInfo instance and set the vertices
        tetra_GeometryInfo = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
        tetra_GeometryInfo.setCoordinates(vertices);
        tetra_GeometryInfo.setCoordinateIndices(tetraFaceIndices);

        //      triangulator = new Triangulator(); // only for polygons:
        // POLYGON_ARRAY
        //      triangulator.triangulate(tetra_GeometryInfo); // and with: int
        // stripCounts[]
        //           gi.setStripCounts(...)
        //           int contourCounts[]

        // Set the parameters (1 texture with dimension 2) for the texture's
        // coordinates
        tetra_GeometryInfo.setTextureCoordinateParams(1, 2);

        //    case #1: each face of the tetrahedron has the same texture portion.

        // The coordinates of the 3 points in the 2D texture space.
        textCoord2f = new TexCoord2f[3];
        textCoord2f[0] = new TexCoord2f(0.0f, 0.2f);
        textCoord2f[1] = new TexCoord2f(0.5f, 1.0f);
        textCoord2f[2] = new TexCoord2f(1.0f, 0.5f);

        // Set the texture coordinate's indices (referenced to the array of 2D
        // points
        // in the texture space by setTextureCoordinates(0, textCoord2f) and
        // setTextureCoordinateIndices(0, textCoordIndices)).
        textCoordIndices = new int[lengthTetraFaceIndices]; // 12

        // From the camera in the view coordinate system (inverse of
        // tetraFaceIndices !!!)
        // front face
        textCoordIndices[0] = 0;
        textCoordIndices[1] = 1;
        textCoordIndices[2] = 2;
        // back face
        textCoordIndices[3] = 0;
        textCoordIndices[4] = 1;
        textCoordIndices[5] = 2;
        // back-left face
        textCoordIndices[6] = 2;
        textCoordIndices[7] = 0;
        textCoordIndices[8] = 1;
        // bottom
        textCoordIndices[9] = 0;
        textCoordIndices[10] = 1;
        textCoordIndices[11] = 2;

        /*
         * // case #2: each face of the tetrahedron has a different part of the
         * texture.
         *  // The coordinates of the 4 points in the 2D texture space.
         * textCoord2f = new TexCoord2f[4]; textCoord2f[0] = new
         * TexCoord2f(0.0f, 0.5f); textCoord2f[1] = new TexCoord2f(1.0f, 0.5f);
         * textCoord2f[2] = new TexCoord2f(0.6f, 0.7f); textCoord2f[3] = new
         * TexCoord2f(0.6f, 0.3f);
         * 
         *  // Set the texture coordinate's indices (referenced to the array of
         * 2D points // in the texture space by setTextureCoordinates(0,
         * textCoord2f) and // setTextureCoordinateIndices(0,
         * textCoordIndices)). textCoordIndices = new
         * int[lengthTetraFaceIndices]; // 12
         *  // From the camera in the view coordinate system (inverse of
         * tetraFaceIndices !!!) // front face textCoordIndices[0] = 3;
         * textCoordIndices[1] = 2; textCoordIndices[2] = 0; // back face
         * textCoordIndices[3] = 1; textCoordIndices[4] = 2; textCoordIndices[5] =
         * 3; // back-left face textCoordIndices[6] = 1; textCoordIndices[7] =
         * 0; textCoordIndices[8] = 2; // bottom textCoordIndices[9] = 1;
         * textCoordIndices[10]= 3; textCoordIndices[11]= 0;
         */
        // just one set
        tetra_GeometryInfo.setTextureCoordinates(0, textCoord2f);
        // just one set
        tetra_GeometryInfo.setTextureCoordinateIndices(0, textCoordIndices);

        normalGenerator = new NormalGenerator();
        normalGenerator.generateNormals(tetra_GeometryInfo);

        if (crAngle)
            normalGenerator.setCreaseAngle(0.0f); // with 0 radian ===> creased

        stripifier = new Stripifier();
        stripifier.stripify(tetra_GeometryInfo);

        tetra_GeometryArray = tetra_GeometryInfo.getGeometryArray();

        // The geonometry is passed to the instance this of the tetrahedron.
        this.setGeometry(tetra_GeometryArray);

        ////////////////////// Appearance part ///////////////////////////

        appearance = new Appearance();

        // Optical properties of the tetrahedron.

        // Ambient-diffuse-reflection coefficient
        diffAmb = new Color3f(1.0f, 0.5f, 1.0f);
        // Diffuse-reflection coefficient
        reflDiff = new Color3f(1.0f, 0.5f, 1.0f);
        // Specular-reflection coefficient (reflectance function)
        reflSpec = new Color3f(1.0f, 0.5f, 1.0f);
        // c = shininess: cos^c in the specular reflection
        float c = 15;
        // Emitted light
        emittedLight = new Color3f(0.0f, 0.0f, 0.0f);

        material = new Material(diffAmb, emittedLight, reflDiff, reflSpec, c);
        appearance.setMaterial(material);

        // This instance acts only on the tetrahedron and not on its texture.
        trAttr = new TransparencyAttributes(TransparencyAttributes.NICEST, 0.0f);
        // 0.0 = fully opaque
        // 1.0 = fully transparent
        appearance.setTransparencyAttributes(trAttr);

        // Loading the texture
        newTextureLoader = new NewTextureLoader("Images/Claude.jpg");
        newTextureLoader.setImageObserver(newTextureLoader.getImageObserver());

        texture = newTextureLoader.getTexture();

        appearance.setTexture(texture);

        // Application mode of the texture
        textAttr = new TextureAttributes();
        textAttr.setTextureMode(TextureAttributes.MODULATE); // there still are:
        // BLEND, COMBINE,
        // DECAL, and REPLACE
        appearance.setTextureAttributes(textAttr);

        // The appearance is passed to the instance this of the tetrahedron.
        this.setAppearance(appearance);

        return this;
    }
}

/**
 * This class creates a right-handed 3D coordinate system.
 */

class CoordSyst extends Shape3D {
    private float rX, gX, bX, rY, gY, bY, rZ, gZ, bZ, scale_XYZ;

    private LineArray axes;

    private float scaledExtremites[];

    /**
     * Constructor that allows to specify the desired initial colors and the
     * length of the axes.
     * 
     * @param type
     *            float rougeX, vertX, bleuX, rougeY, vertY, bleuY, rougeZ,
     *            vertZ, bleuZ - the colors of the axes
     * @param type
     *            float s_XYZ - the scale factor to adjust the axes's length
     */
    public CoordSyst(float rougeX, float vertX, float bleuX, float rougeY, float vertY, float bleuY, float rougeZ,
            float vertZ, float bleuZ, float s_XYZ) {
        rX = rougeX;
        rY = rougeY;
        rZ = rougeZ;
        gX = vertX;
        gY = vertY;
        gZ = vertZ;
        bX = bleuX;
        bY = bleuY;
        bZ = bleuZ;
        scale_XYZ = s_XYZ;

        // Colors of the three axes.
        float color[] = { rX, gX, bX, rX, gX, bX, // the x axis
                rY, gY, bY, rY, gY, bY, // the y axis
                rZ, gZ, bZ, rZ, gZ, bZ }; // the z axis

        // Construction of the axes (LineArray).
        axes = new LineArray(6, LineArray.COORDINATES | LineArray.COLOR_3);

        // Scalling of the vertices of the 3 axes using scale_XYZ.
        scaledExtremites = new float[extremites.length];
        for (int i = 0; i < extremites.length; i++)
            scaledExtremites[i] = extremites[i] * scale_XYZ;

        axes.setCoordinates(0, scaledExtremites);
        axes.setColors(0, color);

        this.setGeometry(axes);
    }

    // Definition of the geometry of the three axes.
    private static final float extremites[] = {
            // x-axis
            0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,

            // y-axis
            0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,

            // z-axis
            0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f };
}