package PCG.HomeWork1;

/* import packages of the practical "computer graphics" */
import PCG.*;

/* import java awt/swing packages */
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
import java.awt.*;

/* import java3d packages */
import javax.media.j3d.*;
import javax.vecmath.*;

/* import java3d utility packages */
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.geometry.*;
import com.sun.j3d.utils.behaviors.mouse.*;
import com.sun.j3d.loaders.Scene;

/* import packages of ncsa-portfolio-loader */
import ncsa.j3d.loaders.*;

/**
 * Überschrift:   1. Aufgabe: Elementare Java3D Programmierung
 * Beschreibung:  Ziel dieser Aufgabe ist es, eine Java3D-Szene aufzubauen.
 *                Dazu sollen unterschiedliche Techniken, die Java3D anbietet,
 *                genutzt werden (z.B. "Level Of Detail", Interpolatoren,
 *                Picking, Loader, ...).
 * Copyright:     Copyright (c) 2001
 * Organisation:
 * @author Fabian Wleklinski (fabian@wleklinski.de)
 * @version 1.0
 */
public class MainFrame extends JFrame {

        /**
         * application-title
         */
        private final static String m_sApplicationTitle = "Praktikum " +
                "Computergrafik - 1. Aufgabe - Lösung von Fabian Wleklinski";

        /**
         * Name of image file for the background texture
         */
        private final static String m_sBackgroundTexture = "aj145.jpg";

        /**
         * Name of image file for the texture of the tetrahedron
         */
        private final static String m_sTetrahedronTexture = "brick001.jpg";

        /**
         * Name of the sound file containing the ambient sound
         */
        private final static String m_sAmbientSound = "file:birds.au";

        /**
         * The <code>BranchGroup</code> that includes <emp>all</emp>
         * scenegraph-objects!
         */
        private BranchGroup m_oBranchGroupScene;

        /**
         * The <code>Background</code> of the scene.
         */
        private Background m_oBackground;

        /**
         * The color of the directional light.
         */
        private Color3f m_oDirectionalLight = new Color3f( 1.0f, 0.2f, 0.1f );

        /**
         * The color of the ambient light.
         */
        private Color3f m_oAmbientLight = new Color3f( 0.5f, 0.3f, 0.1f );

        /**
         * The color of the point light.
         */
        private Color3f m_oPointLight = new Color3f( 1.0f, 1.0f, 1.0f );

        private Box m_oCollisionBox = null;

        /*
        private static final Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
        private static final Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
        private static final Color3f red   = new Color3f(1.0f, 0.0f, 0.0f);
        private static final Color3f blue  = new Color3f(0.0f, 0.0f, 1.0f);
        private static final Color3f darkblue  = new Color3f(0.0f, 0.0f, 0.5f);
        */

        /*
        private JButton btQuit = new JButton();
        */

        /**
         * Constructor.
         */
        public MainFrame() {

                // call inherited constructor (of JFrame)
                super();

                // enableEvents( AWTEvent.WINDOW_EVENT_MASK );

                // set the title of this cool application :)
                this.setTitle( m_sApplicationTitle );

                // get the graphics configuration of the system
                GraphicsConfiguration oConfiguration = SimpleUniverse.getPreferredConfiguration();

                // now go and create a Canvas3D-instance!
                Canvas3D oCanvas = new Canvas3D( oConfiguration );

                getContentPane().setLayout( new BorderLayout() );
                getContentPane().add( oCanvas, BorderLayout.CENTER );

                /*
                btQuit.setText("P A N I C");
                btQuit.addActionListener(
                        new java.awt.event.ActionListener() {
                                public void actionPerformed( java.awt.event.ActionEvent oActionEvent ) {
                                        btQuit_actionPerformed( oActionEvent );
                                }
                        }
                );
                getContentPane().add( btQuit, BorderLayout.NORTH );
                */

                // we have to create a "universe" before we can do any
                // java3d action...
                SimpleUniverse oUniverse = new SimpleUniverse( oCanvas );

                // create the whole scenegraph, and compile it!
                m_oBranchGroupScene = createSceneGraph();

                PickMagnifyBehavior oMagnifyBehavior = new PickMagnifyBehavior(
                        m_oBranchGroupScene, oCanvas, new BoundingSphere(
                                new Point3d( 0.0, 0.0, 0.0 ), 1000.0 ) );
                m_oBranchGroupScene.addChild( oMagnifyBehavior );

                m_oBranchGroupScene.compile();

                // add the compiled scenegraph to our simple universe
                oUniverse.addBranchGraph( m_oBranchGroupScene );

                // set viewing transform
                setViewingTransform( oUniverse );

		/* setup the sound device */
		AudioDevice oAudioDevive =
                        oUniverse.getViewer().createAudioDevice();

                oCanvas.getView().setFrontClipPolicy( View.VIRTUAL_EYE );
                oCanvas.getView().setFrontClipDistance( 14.00 );
        }

        public static void main(String[] args) {
                HomeWork1 homeWork11 = new HomeWork1();
        }

        /**
         * Sets the background image of the scene accordingly to the
         * current frame-size (pixels).
         */
        private void setSceneBackgroundImage( BranchGroup oBranchGroup ) {
                // load texture for background
                com.sun.j3d.utils.image.TextureLoader oTextureLoader =
                        new com.sun.j3d.utils.image.TextureLoader(
                        m_sBackgroundTexture, this );

                // add a background to the scene
                if (m_oBackground == null) {
                        m_oBackground = new Background( oTextureLoader.getImage() );
                        m_oBackground.setCapability( Background.ALLOW_IMAGE_WRITE );
                        m_oBackground.setApplicationBounds( new BoundingSphere(
                                new Point3d( 0.0, 0.0, 0.0 ), 1000.0 ) );

                        oBranchGroup.addChild( m_oBackground );
                } else {
                }
                if ((getWidth() > 0) && (getHeight() > 0)) {
                        m_oBackground.setImage( oTextureLoader.getScaledImage(
                                getWidth(), getHeight() ) );
                }
        }

        private void setViewingTransform( SimpleUniverse oUniverse ) {

                // This will move the ViewPlatform back a bit and ensure, that
                // the scene's objects can be viewed.
                // oUniverse.getViewingPlatform().setNominalViewingTransform();

                Transform3D oTransform3D = new Transform3D();

                oUniverse.getViewingPlatform().getMultiTransformGroup().getTransformGroup(0).getTransform( oTransform3D );

                oTransform3D.set( new Vector3d( 0, 0, 17 ) );

                oUniverse.getViewingPlatform().getMultiTransformGroup().getTransformGroup(0).setTransform( oTransform3D );
        }

        private void btQuit_actionPerformed( java.awt.event.ActionEvent oActionEvent ) {
                System.exit( 0 );
        }

        /**
         * Overridden, so we can resize our background bitmap if component
         * is resized.
         */
        protected void processComponentEvent( java.awt.event.ComponentEvent oComponentEvent ) {
                if (oComponentEvent.getID() == java.awt.event.ComponentEvent.COMPONENT_RESIZED ) {
                        setSceneBackgroundImage( m_oBranchGroupScene );
                }
        }

        /**
         * Overridden so we can exit when window is closed
         */
        protected void processWindowEvent( java.awt.event.WindowEvent oWindowEvent ) {
                super.processWindowEvent( oWindowEvent );
                if (oWindowEvent.getID() == java.awt.event.WindowEvent.WINDOW_CLOSING) {
                        System.exit( 0 );
                }
        }

        /**
         * Creates the scenegraph. That means, this method will add all the
         * required java3d-objects to the scene. It creates an instance of
         * <code>BranchGroup</code>, throws all the sceneobjects into it,
         * and returns this <code>BranchGroup</code>.
         * @return      An instance of <code>BranchGroup</code>, that includes
         *              all the java3d-objects of the scene.
         */
        private BranchGroup createSceneGraph() {

                // this is the BranchGroup to be returned!
                BranchGroup oBranchGroup = new BranchGroup();

                // add a background image
                setSceneBackgroundImage( oBranchGroup );

                // add a transform group, which will contain all scenegraph-
                // objects that shell be affected by the camera-movement.
                TransformGroup oTransformGroup = new TransformGroup();
                oBranchGroup.addChild( oTransformGroup );
                oTransformGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
                oTransformGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );

                // add behaviours for navigating the virtual camera trough
                // the scene...
                addBehaviors( oTransformGroup );

                /* Fabian Wleklinski: Würfel erzeugen */
                addBox( oTransformGroup );

                /* Fabian Wleklinski: Den Würfel erzeugen, der mit dem Tetraeder
                kollidieren soll */
                addCollisionBox( oTransformGroup );

                /* Fabian Wleklinski: Kugel erzeugen */
                addSphere( oTransformGroup );

                /* Fabian Wleklinski: Tetraeder erzeugen */
                addTetrahedron( oTransformGroup );

                /* Fabian Wleklinski: Punktförmige Lichtquelle definieren */
                addPointLight( oTransformGroup );

                /* Fabian Wleklinski: Add a directional light */
                addDirectionalLight( oTransformGroup );

                /* Fabian Wleklinski: Add an ambient light */
                addAmbientLight( oTransformGroup );

                /* Fabian Wleklinski: Add a further scenegraph-object */
                addFileObject1( oTransformGroup );

                /* Fabian Wleklinski: Add a further scenegraph-object */
                addFileObject2( oTransformGroup );

                /* Add an ambient sound */
                addAmbientSound( oTransformGroup );

                return oBranchGroup;
        }

        private void addAmbientSound( TransformGroup oTransformGroup ) {

                MediaContainer oMediaContainer = new MediaContainer(
                        m_sAmbientSound );
                BackgroundSound oBackgroundSound = new BackgroundSound( oMediaContainer,
                        0.25f );
                oBackgroundSound.setEnable( true );
                oBackgroundSound.setSchedulingBounds( new BoundingSphere(
                        new Point3d( 0.0, 0.0, 0.0 ), 100.0 ) );

                oBackgroundSound.setLoop( -1 );

                oTransformGroup.addChild( oBackgroundSound );
        }

        private void addFileObject1( TransformGroup oTransformGroup ) {
                Transform3D oTransform3D = new Transform3D();
                oTransform3D.setTranslation( new Vector3d( 3.5, 5, -10.0 ) );
                TransformGroup oTransformGroup1 = new TransformGroup( oTransform3D );

                oTransform3D = new Transform3D();
                oTransform3D.setRotation( new AxisAngle4d( 0, 1.0, 0.0,
                        1.55*Math.PI/2) );
                TransformGroup oTransformGroup2 = new TransformGroup( oTransform3D );

                oTransform3D = new Transform3D();
                oTransform3D.setRotation( new AxisAngle4d( -1.0, 0.0, 0.0,
                        1.4*Math.PI/2) );
                TransformGroup oTransformGroup3 = new TransformGroup( oTransform3D );

                ModelLoader loader = new ModelLoader();
                Scene oScene = null;
                try {
                        oScene = loader.load( "eagle.3ds" );
                } catch ( java.io.FileNotFoundException eFileNotFound ) {
                        System.out.println( "Datei nicht gefunden!" );
                }
                if (oScene != null) {
                        addTranslatedNode( oTransformGroup3,
                                oScene.getSceneGroup(), 0, 0, 0, true );
                        oTransformGroup2.addChild( oTransformGroup3 );
                        oTransformGroup1.addChild( oTransformGroup2 );
                        oTransformGroup.addChild( oTransformGroup1 );
                }
        }

        private void addFileObject2( TransformGroup oTransformGroup ) {
                Transform3D oTransform3D = new Transform3D();
                oTransform3D.setTranslation( new Vector3d( -4.0, 0, 2 ) );
                TransformGroup oTransformGroup1 = new TransformGroup( oTransform3D );

                oTransform3D = new Transform3D();
                oTransform3D.setRotation( new AxisAngle4d( -1.0, 0.0, 0,
                        1*Math.PI/2) );
                TransformGroup oTransformGroup2 = new TransformGroup( oTransform3D );

                oTransform3D = new Transform3D();
                oTransform3D.setScale( 0.005d );
                TransformGroup oTransformGroup3 = new TransformGroup( oTransform3D );

                ModelLoader loader = new ModelLoader();
                Scene oScene = null;
                try {
                        oScene = loader.load( "bird.3ds" );
                } catch ( java.io.FileNotFoundException eFileNotFound ) {
                        System.out.println( "Datei nicht gefunden!" );
                }
                if (oScene != null) {
                        addTranslatedNode( oTransformGroup3,
                                oScene.getSceneGroup(), 0, 0, 0, true );
                        oTransformGroup2.addChild( oTransformGroup3 );
                        oTransformGroup1.addChild( oTransformGroup2 );
                        oTransformGroup.addChild( oTransformGroup1 );
                }
        }

        private void addBehaviors( TransformGroup oTransformGroup ) {
                // add behaviours for navigating the virtual camera trough
                // the scene...
                BoundingSphere oBoundingSphere = new BoundingSphere(
                        new Point3d( 0.0, 0.0, 0.0 ), 100.0 );
                // ...a zoom-behaviour...
                MouseBehavior oMouseBehaviorZoom = new MouseZoom();
                oMouseBehaviorZoom.setTransformGroup( oTransformGroup );
                oMouseBehaviorZoom.setSchedulingBounds( oBoundingSphere );
                oTransformGroup.addChild( oMouseBehaviorZoom );
                // ...a rotation-behaviour...
                MouseBehavior oMouseBehaviorRotate = new MouseRotate();
                oMouseBehaviorRotate.setTransformGroup( oTransformGroup );
                oMouseBehaviorRotate.setSchedulingBounds( oBoundingSphere );
                oTransformGroup.addChild( oMouseBehaviorRotate );
                // ...and a translation-behaviour
                MouseBehavior oMouseBehaviorTranslate = new MouseTranslate();
                oMouseBehaviorTranslate.setTransformGroup( oTransformGroup );
                oMouseBehaviorTranslate.setSchedulingBounds( oBoundingSphere );
                oTransformGroup.addChild( oMouseBehaviorTranslate );
        }

        private void addTetrahedron( Group oGroup ) {

                // create an appearance for the tetrahedron
                Appearance oAppearance = new Appearance();

                // load texture for the tetrahedron
                com.sun.j3d.utils.image.TextureLoader oTextureLoader =
                        new com.sun.j3d.utils.image.TextureLoader(
                        m_sTetrahedronTexture, this );
                oAppearance.setTexture( oTextureLoader.getTexture() );

                TextureAttributes oTextureAttributes = new TextureAttributes();
                oTextureAttributes.setTextureMode( TextureAttributes.MODULATE );
                oAppearance.setTextureAttributes( oTextureAttributes );

                oAppearance.setMaterial( new Material( new Color3f(0.2f,.2f,.2f),
                        new Color3f(0.3f,.3f,.3f), new Color3f(1.0f,1.0f,1.0f),
                        new Color3f(), 1.0f ) );

                /* Fabian Wleklinski: create path interpolator */
                Alpha oAlpha = new Alpha( -1, 0, 0, 5000, 0, 0);

                /* Fabian Wleklinski: create translation node for the rotator.
                If we wouldn't apply a translation node, the rotator would
                return the spotlight round and round around it's main axis -
                which wouldn't be very sensefull. */
                Transform3D oTransform3D = new Transform3D();
                oTransform3D.setTranslation( new Vector3f( 8.0f, 0.0f, 0.0f ) );
                TransformGroup oTransformGroup = new TransformGroup( oTransform3D );
                oTransformGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );

                /* Fabian Wleklinski: create an interpolator */
                // float[] afNodes = {0.0f,1.0f};
                Point3f[] aoPoints = {
                        new Point3f( 0, 0, 0 ),
                        new Point3f( 0, 0, -20 )
                };
                PositionPathInterpolator oPositionPathInterpolator =
                        new PositionPathInterpolator( oAlpha, oTransformGroup,
                        oTransform3D, new float[] {0.0f,1.0f}, aoPoints );

                oPositionPathInterpolator.setSchedulingBounds(
                        new BoundingSphere( new Point3d( 0.0, 0.0, 0.0 ),
                        100.0 ) );

                oTransformGroup.addChild( oPositionPathInterpolator );

                // create a level-of-detail-behaviour, and add it into a
                // switch-node:
                DistanceLOD oDistanceLOD = new DistanceLOD(
                        new float[] { 35.0f } );
                oDistanceLOD.setSchedulingBounds( new BoundingSphere(
                        new Point3d(), 1000.0 ) );
                Switch oSwitch = new Switch();
                oSwitch.setCapability( Switch.ALLOW_SWITCH_WRITE );
                oDistanceLOD.addSwitch( oSwitch );

                // create the tetrahedron
                Tetrahedron oTetrahedron = new Tetrahedron( 5.0f, oAppearance );

                // create the tetrahedron "dummy" (a box!)
                Box oBox = new Box( 1.0f, 1.4f, 1.0f,
                        Box.GENERATE_TEXTURE_COORDS + Box.GENERATE_NORMALS,
                        oAppearance );
                TransformGroup oBoxTransformGroup = new TransformGroup();
                Transform3D oBoxTransform3D = new Transform3D();
                oBoxTransform3D.setRotation( new AxisAngle4d( 0.7, 1.0, 0.0,
                        Math.PI/4 ) );
                oBoxTransform3D.setTranslation( new Vector3d( 0.0, -0.25, 0.0 )
                        );
                oBoxTransformGroup.setTransform( oBoxTransform3D );
                oBoxTransformGroup.addChild( oBox );

                /* add the tetrahedron and it's clone into the switch */
                oSwitch.addChild( oTetrahedron );
                oSwitch.addChild( oBoxTransformGroup );

                // add switch and level-of-detail-behavior to our group
                oTransformGroup.addChild( oSwitch );
                oTransformGroup.addChild( oDistanceLOD );

                DetectCollisionBehavior oDetectCollisionBehavior = new DetectCollisionBehavior(
                        oTransformGroup, m_oCollisionBox, new BoundingSphere(
                        new Point3d( 0.0, 0.0, 0.0 ), 1000.0 ),
                        "file:crash1.au" );
                oTransformGroup.addChild( oDetectCollisionBehavior );

                addTranslatedNode( oGroup, oTransformGroup, 0.0, 0.0, 0.0, true );
        }

        private void addSphere( Group oGroup ) {
                Appearance oAppearance = new Appearance();
                oAppearance.setColoringAttributes( new ColoringAttributes(
                        0, 1.0f, 0, ColoringAttributes.FASTEST ) );

                Color3f objColor = new Color3f( 0.7f, 0.8f, 1.0f );
                oAppearance.setTransparencyAttributes(
                        new TransparencyAttributes(
                        TransparencyAttributes.FASTEST, 0.2f ) );
                oAppearance.setMaterial( new Material( new Color3f(.0f,.4f,.0f),
                        new Color3f(.0f,.2f,.0f), new Color3f(0.8f,0.8f,0.8f),
                        new Color3f(.8f,.8f,.8f), 128.0f ) );

                addTranslatedNode( oGroup, new Sphere( 1.0f,
                        Sphere.GENERATE_NORMALS | Sphere.GENERATE_TEXTURE_COORDS,
                        20, oAppearance ), +5.0, 0, 0, true );
        }

        private void addBox( Group oGroup ) {
                /* Fabian Wleklinski: Würfel erzeugen */
                Appearance oAppearance = new Appearance();
                oAppearance.setColoringAttributes( new ColoringAttributes(
                        1.0f, 0, 0, ColoringAttributes.FASTEST ) );

                oAppearance.setTransparencyAttributes(
                        new TransparencyAttributes(
                        TransparencyAttributes.NICEST, 0.2f ) );

                oAppearance.setMaterial( new Material( new Color3f(.2f,.2f,.2f),
                        new Color3f(.6f,.0f,.0f), new Color3f(0.5f,0.5f,0.5f),
                        new Color3f(.8f,.8f,.8f), 64.0f ) );

                addTranslatedNode( oGroup, new Box( 0.5f, 0.5f, 0.5f,
                        oAppearance ), -5.0, 0, 0, true );
        }

        private void addCollisionBox( Group oGroup ) {
                /* Fabian Wleklinski: Würfel erzeugen */
                Appearance oAppearance = new Appearance();
                oAppearance.setColoringAttributes( new ColoringAttributes(
                        0.0f, 0, 1.0f, ColoringAttributes.FASTEST ) );

                oAppearance.setTransparencyAttributes(
                        new TransparencyAttributes(
                        TransparencyAttributes.NICEST, 0.2f ) );

                oAppearance.setMaterial( new Material( new Color3f(.2f,.2f,.2f),
                        new Color3f(.6f,.0f,.0f), new Color3f(0.5f,0.5f,0.5f),
                        new Color3f(.8f,.8f,.8f), 64.0f ) );

                m_oCollisionBox = new Box( 0.5f, 0.5f, 0.5f, oAppearance );
                m_oCollisionBox.setCapability( Box.ENABLE_COLLISION_REPORTING );

                addTranslatedNode( oGroup, m_oCollisionBox, 0, 0, -10, true );
        }

        private void addPointLight( Group oGroup ) {
                TransformGroup oRotatingGroup = new TransformGroup();
                oRotatingGroup.setCapability(
                        TransformGroup.ALLOW_TRANSFORM_WRITE );

                Point3f oPoint3f = new Point3f(0.0f, 0.0f, 0.0f);
                Point3f atten = new Point3f(0.5f, 0.0f, 0.0f);
                PointLight oPointLight = new PointLight( m_oPointLight,
                        oPoint3f, atten );

                oPointLight.setInfluencingBounds( new BoundingSphere(
                        new Point3d( 0.0, 0.0, 0.0 ), 100.0 ) );

                addNode( oRotatingGroup, oPointLight );

                /* Fabian Wleklinski: create rotator */
                Alpha oAlpha = new Alpha( -1, 0, 0, 5000, 0, 0);

                /* Fabian Wleklinski: create translation node for the rotator.
                If we wouldn't apply a translation node, the rotator would
                return the spotlight round and round around it's main axis -
                which wouldn't be very sensefull. */
                Transform3D oTransform3D = new Transform3D ();
                oTransform3D.setTranslation( new Vector3f( 0.0f, -10.0f, 0.0f ) );
                oTransform3D.setRotation( new AxisAngle4d( 0.0, 0.0, 1.0,
                        Math.PI/2 ) );

                /* Fabian Wleklinski: create a rotation interpolator */
                RotationInterpolator oRotationInterpolator =
                        new RotationInterpolator( oAlpha, oRotatingGroup,
                        oTransform3D, 0, (float) Math.PI * 2.0f );

                oRotationInterpolator.setSchedulingBounds(
                        new BoundingSphere( new Point3d( 0.0, 0.0, 0.0 ),
                        100.0 ) );

                oRotatingGroup.addChild( oRotationInterpolator );

                addTranslatedNode( oGroup, oRotatingGroup, 0.0f, 10.0f, 0.0f, false );
        }

        private void addDirectionalLight( Group oGroup ) {
                Vector3f oVector3fDirection = new Vector3f( -1.0f, 0.0f, -1.0f );

                DirectionalLight oDirectionalLight = new DirectionalLight(
                        m_oDirectionalLight, oVector3fDirection );

                oDirectionalLight.setInfluencingBounds( new BoundingSphere(
                        new Point3d( 0.0, 0.0, 0.0 ), 100.0 ) );

                addNode( oGroup, oDirectionalLight );
        }

        private void addAmbientLight( Group oGroup ) {
                AmbientLight oAmbientLight = new AmbientLight(
                        m_oAmbientLight );

                oAmbientLight.setInfluencingBounds( new BoundingSphere(
                        new Point3d( 0.0, 0.0, 0.0 ), 100.0 ) );

                addNode( oGroup, oAmbientLight );
        }

        private static void addTranslatedNode( Group oGroup, Node oNode,
                double fX, double fY, double fZ, boolean bCanHaveFocus ) {
                if ((fX != 0) || (fY != 0) || (fZ != 0) || bCanHaveFocus ) {
                        Transform3D oTransform3D = new Transform3D();
                        oTransform3D.set( new Vector3d( fX, fY, fZ ) );
                        TransformGroup oTransformGroup = new TransformGroup( oTransform3D );
                        oTransformGroup.setCapability( TransformGroup.ENABLE_PICK_REPORTING );
                        oTransformGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_READ );
                        oTransformGroup.setCapability( TransformGroup.ALLOW_TRANSFORM_WRITE );

                        oGroup.addChild( oTransformGroup );
                        addNode( oTransformGroup, oNode );
                } else {
                        addNode( oGroup, oNode );
                }
        }

        private static void addNode( Group oGroup, Node oNode ) {

                /* Fabian Wleklinski: Der übergebenen Gruppe das übergebene
                Objekt hinzufügen */
                oGroup.addChild(
                        oNode
                );

                /* Fabian Wleklinski: Wenn die übergebene Gruppe eine
                Transformationsgruppe ist, deren Translation lesen, und als
                Beschreibung des Objektes hinzufügen */

                if ((oGroup instanceof TransformGroup) && (false)) {

                        /* Fabian Wleklinski: Zugriff auf die Translation der
                        Transformationsgruppe */

                        Transform3D oTransform3D = new Transform3D();
                        ((TransformGroup) oGroup).getTransform( oTransform3D );
                        Vector3d oVector3d = new Vector3d();
                        oTransform3D.get( oVector3d );

                        /* Fabian Wleklinski: Der Transformationsgruppe eine
                        neue Transformationsgruppe hinzufügen, in die später
                        der Beschreibungstext gesetzt wird */

                        Transform3D oTransform3DCaption = new Transform3D();
                        oTransform3DCaption.set( new Vector3d( -0.5, 0, 2.0 ) );
                        TransformGroup oTransformGroupCaption = new TransformGroup( oTransform3DCaption );
                        oGroup.addChild( oTransformGroupCaption );

                        /* Fabian Wleklinski: Nun den Beschreibungstext
                        hinzufügen */

                        oTransformGroupCaption.addChild(
                                new com.sun.j3d.utils.geometry.Text2D(
                                        "(" + oVector3d.x + "," + oVector3d.y + "," + oVector3d.z + ")",
                                        new Color3f( 1.0f, 1.0f, 1.0f ),
                                        "Arial",
                                        72,
                                        0
                                )
                        );
                }
        }
}