import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import com.sun.j3d.utils.universe.*;

public class Test extends JFrame {
        JMenuBar menuBar1 = new JMenuBar();
        JMenu menuFile = new JMenu();
        JMenuItem menuChangeColor = new JMenuItem();
        JMenuItem menuFileExit = new JMenuItem();
        JLabel statusBar = new JLabel();
        BorderLayout borderLayout1 = new BorderLayout();

        static final float AREA_SIZE = (1.0f);
        static final int GEOM_NUM = (100);

        OnCanvas3D      canvas;
        VirtualUniverse universe;
        Locale locale;
        BranchGroup objRoot;
        BranchGroup vpRoot;
        Shape3D shape[] = new Shape3D[GEOM_NUM*GEOM_NUM];
        float[] color = { 0.0f, 1.0f, 0.0f };
        int shape_count=0;
        long change_start = 0;
        long change_end;

        public Test() {
                JPopupMenu.setDefaultLightWeightPopupEnabled(false);
                enableEvents(AWTEvent.WINDOW_EVENT_MASK);
                try     {
                        jbInit();
                }
                catch(Exception e) {
                        e.printStackTrace();
                }

                canvas = new OnCanvas3D ( SimpleUniverse.getPreferredConfiguration() );
                getContentPane().add(canvas,BorderLayout.CENTER);

                createSceneGraph();
        }

        public void createSceneGraph() {
                /**
                 * Create the object root of the branch graph
                 */
                objRoot = new BranchGroup();
                objRoot.setCapability(BranchGroup.ALLOW_DETACH);

                /*
                 * Create geom data (Shape3D)
                 */
                int i,j;
                float pich;
                float base;
                float x, xx, y, yy;
                float[] vertex = new float[12];
                float[] normal = { 0.0f, 0.0f, 1.0f,
                                                                                         0.0f, 0.0f, 1.0f,
                                                                                         0.0f, 0.0f, 1.0f,
                                                                                         0.0f, 0.0f, 1.0f };
                int[] index = { 0, 1, 2,
                                                                                0, 2, 3 };
                pich = AREA_SIZE / GEOM_NUM;

                base = -(AREA_SIZE / 2.0f);
                for(i=0;i<GEOM_NUM;i++){
                        x = base + pich * i;
                        xx = base + pich * (i+1);
                        for(j=0;j<GEOM_NUM;j++){
                                y = base + pich * j;
                                yy = base + pich * (j+1);

                                vertex[0] = x;        vertex[1] = y;        vertex[2] = 0.0f;
                                vertex[3] = xx; vertex[4] = y;      vertex[5] = 0.0f;
                                vertex[6] = xx; vertex[7] = yy; vertex[8] = 0.0f;
                                vertex[9] = x;        vertex[10] = yy; vertex[11] = 0.0f;
                                IndexedTriangleArray geometry =
                                        new IndexedTriangleArray( 4,
                                                GeometryArray.COORDINATES | GeometryArray.NORMALS ,
                                                6);
                                geometry.setCoordinates(0,vertex);
                                geometry.setCoordinateIndices(0, index);
                                geometry.setNormals(0, normal);

                                Material material = new Material();
                                material.setCapability(Material.ALLOW_COMPONENT_READ);
                                material.setCapability(Material.ALLOW_COMPONENT_WRITE);
                                material.setDiffuseColor( new Color3f(color) );
                                material.setShininess(128.0f);

                                Appearance appearance = new Appearance();
                                appearance.setCapability(Appearance.ALLOW_MATERIAL_READ);
                                appearance.setMaterial(material);

                                shape[shape_count] = new Shape3D(geometry,appearance);
                                shape[shape_count].setCapability(Shape3D.ALLOW_APPEARANCE_READ);
                                objRoot.addChild(shape[shape_count++]);
                        }
                }
                DirectionalLight dirLight =new DirectionalLight( new Color3f(1.0f, 1.0f, 1.0f),
                                        new Vector3f(0.0f, -1.0f, -1.0f) );
                dirLight.setInfluencingBounds(new BoundingSphere(new Point3d(0,0,0),((BoundingSphere)objRoot.getBounds()).getRadius()));
                dirLight.setEnable(true);
                objRoot.addChild(dirLight);

                /*
                 * Create Virtual Universe
                 */
                universe = new VirtualUniverse();
                locale = new Locale(universe);

                vpRoot = new BranchGroup();
                vpRoot.setCapability(BranchGroup.ALLOW_DETACH);

                /*
                 * Create Viewing environment
                 */
                ViewPlatform platform = new ViewPlatform();
                View view = new View();
                view.setPhysicalBody(new PhysicalBody());
                view.setPhysicalEnvironment(new PhysicalEnvironment());
                view.setBackClipDistance(10.0) ;
                view.attachViewPlatform(platform);
                view.addCanvas3D(canvas);

                /*
                 * Set viewing matrix and add to viewport root
                 */
                Transform3D trans = new Transform3D();
                trans.setTranslation(new Vector3d(0.0, 0.0, ((BoundingSphere)objRoot.getBounds()).getRadius()*3.0) );
                TransformGroup transGroup = new TransformGroup(trans);
                transGroup.addChild(platform);

                vpRoot.addChild(transGroup);

                /*
                 * Add object root and view to locale
                 */
                locale.addBranchGraph(objRoot);
                locale.addBranchGraph(vpRoot);

                System.out.println("J3D priority ("+ Thread.MIN_PRIORITY + " - " + Thread.MAX_PRIORITY     + ") =" +universe.getJ3DThreadPriority() );
                //universe.setJ3DThreadPriority(3);
        }

        /**
         * Window menu set up
         */
        private void jbInit() throws Exception  {
                this.getContentPane().setLayout(borderLayout1);
                this.setSize(new Dimension(400, 300));
                this.setTitle("Color change test");
                statusBar.setText(" ");
                menuFile.setText("File");
                menuChangeColor.setText("Change color");
                menuChangeColor.addActionListener(new ActionListener()  {

                        public void actionPerformed(ActionEvent e) {
                                changeColor_actionPerformed(e);
                        }
                });
                menuFile.add(menuChangeColor);

                menuFileExit.setText("exit");
                menuFileExit.addActionListener(new ActionListener()     {

                        public void actionPerformed(ActionEvent e) {
                                fileExit_actionPerformed(e);
                        }
                });

                menuFile.add(menuFileExit);
                menuBar1.add(menuFile);
                this.setJMenuBar(menuBar1);
                this.getContentPane().add(statusBar, BorderLayout.SOUTH);
        }

        /**
         * Change color action
         */
        public void changeColor_actionPerformed(ActionEvent e) {
                int i;
                if( color[1] == 1.0f ){
                        color[1] = 0.0f; color[2] = 1.0f;
                }
                else {
                        color[1] = 1.0f; color[2] = 0.0f;
                }
                change_start = System.currentTimeMillis();
                vpRoot.detach();        // for repress render twice
                //scene.detach();
                for(i=0;i<shape_count;i++){
                        shape[i].getAppearance().getMaterial().setDiffuseColor(color[0],color[1],color[2]);
                }
                //locale.addBranchGraph(scene);
                locale.addBranchGraph(vpRoot);
        }


        /**
         * Exit action
         */
        public void fileExit_actionPerformed(ActionEvent e) {
                System.exit(0);
        }

        /**
         * System close
         */
        protected void processWindowEvent(WindowEvent e) {
                super.processWindowEvent(e);
                if(e.getID() == WindowEvent.WINDOW_CLOSING) {
                        fileExit_actionPerformed(null);
                }
        }

        /**
         * Canvas3D
         */
        public class OnCanvas3D extends Canvas3D {
                long    pre_render;
                long    post_render;
                long    post_swap;

                public OnCanvas3D( GraphicsConfiguration gc ) {
                        super(gc);
                }

                public void preRender() {
                        pre_render = System.currentTimeMillis();
                }

                public void postRender() {
                        post_render = System.currentTimeMillis();
                }

                public void postSwap() {
                        post_swap = System.currentTimeMillis();
                        System.out.println("Render msec = ( "+ pre_render +" - "+post_swap+" ) === "+ (post_swap-pre_render) );
                        if( change_start != 0 ){
                                change_end = post_swap;
                                System.out.println("Color change msec = ( "+ change_start +" - "+change_end+" ) === "+ (change_end-change_start) );
                                change_start = 0;
                        }
                }
        }

        /**
         * Main routine
         */
        public static void main(String[] args) {
                try     {
                        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                }
                catch(Exception e) {
                }
                Test frame = new Test();
                frame.setVisible(true);
        }
}