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

import java.awt.event.*;
import java.util.Enumeration;

import javax.media.j3d.ImageComponent2D;
import java.awt.image.BufferedImage;

public class SpinningCube extends Applet implements Runnable
{

        private Thread                          m_Thread;
        private boolean                         m_bRunning = false;
        private boolean                         m_bAllLoaded = false;
	
        private Canvas3D                        m_Canvas;
        private Image                           m_offImage;
        private Graphics                        m_offGraphics;
	
        private int                                     m_iFrame = 0;

        public BranchGroup createSceneGraph()
        {	
                BranchGroup objRoot = new BranchGroup();

                TransformGroup objTransform = new TransformGroup();
                objTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
                objRoot.addChild(objTransform);

                objTransform.addChild(new ColorCube(0.4));
        
                Alpha alpha = new Alpha(-1, 10000);
                alpha.setStartTime(System.currentTimeMillis());
                RotationInterpolator objRotate = new RotationInterpolator(alpha, objTransform);
                objRotate.setSchedulingBounds(new BoundingSphere());
        objRoot.addChild(objRotate);

                objRoot.compile();

                return objRoot;
        }

        public void init()
        {
                setLayout(null);
        	
                GraphicsConfiguration gc = SimpleUniverse.getPreferredConfiguration();
                m_Canvas = new Canvas3D(gc, true);
                m_Canvas.setBounds(0, 0, 256, 256);
                m_Canvas.getScreen3D().setSize(256, 256);
                m_Canvas.getScreen3D().setPhysicalScreenWidth(0.0254/90.0 * 256);
                m_Canvas.getScreen3D().setPhysicalScreenHeight(0.0254/90.0 * 256);

                BufferedImage image = new BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB);
                ImageComponent2D buffer = new ImageComponent2D(ImageComponent2D.FORMAT_RGB, image);
                m_Canvas.setOffScreenBuffer(buffer);

                BranchGroup scene = createSceneGraph();
                SimpleUniverse simpleU = new SimpleUniverse(m_Canvas);
                simpleU.getViewingPlatform().setNominalViewingTransform();
                simpleU.addBranchGraph(scene);
        	
                m_offImage = createImage(256, 256);
                m_offGraphics = m_offImage.getGraphics();
        	
                m_Canvas.startRenderer();
                m_bAllLoaded = true;
        }
	
        public void start ()
        {
                if (m_Thread == null)
                {
                        m_Thread = new Thread(this);
                }
                	
                m_bRunning = true;
                m_Thread.start();
        }
	
        public void stop ()
        {
                //      The new preferred way to kill a thread is to maintain a boolean indicating
                //      if the thread is live.  We will check this boolean in the run method.  If
                //      it becomes false, the run method will exit and the thread will be destroyed
                //      automatically.
                if (m_Thread != null)
                {
                        m_bRunning = false;
                }
        }
	
        public void run ()
        {
                while (m_bRunning)
                {
                        try
                        {
                                Thread.sleep(500);
                        }
                        catch (InterruptedException e)
                        {
                                stop();
                        }
                	
                        if (m_bAllLoaded)
                        {
                                m_iFrame = ++m_iFrame % 30;
                                m_Canvas.renderOffScreenBuffer();
                                m_Canvas.waitForOffScreenRendering();
                                repaint();
                        }
                }
        }
	
        public void update(Graphics g)
        {
                if (m_bAllLoaded)
                {                       	
                        ImageComponent2D buffer = m_Canvas.getOffScreenBuffer();
                        m_offGraphics.drawImage(buffer.getImage(), 0, 0, this);

                        m_offGraphics.setColor(new Color(255, 0, 0));
                        m_offGraphics.drawString("Frame: " + m_iFrame, 10, 10);
                	
                        g.drawImage(m_offImage, 0, 0, this);
                }
        }
	
        public static void main (String[] args)
        {
                MainFrame frame = new MainFrame(new SpinningCube(), 256, 256);
        }
}
