
// Standard imports
import java.awt.*;
import java.awt.event.*;

import java.io.File;
import java.net.MalformedURLException;

import java.net.URL;

import javax.vecmath.Matrix4f;
import javax.vecmath.Vector3f;

import net.java.games.jogl.GLCapabilities;

// Application Specific imports
import org.j3d.aviatrix3d.*;

import org.j3d.aviatrix3d.pipeline.NullCullStage;
import org.j3d.aviatrix3d.pipeline.NullSortStage;
import org.j3d.aviatrix3d.pipeline.SimpleTransparencySortStage;
import org.j3d.aviatrix3d.pipeline.DefaultRenderPipeline;
import org.j3d.aviatrix3d.pipeline.SingleThreadRenderManager;
import org.j3d.aviatrix3d.surface.SimpleAWTSurface;

/**
 * Example application that demonstrates using transparent material nodes with Aviatrix.
 *
 * @author Alan Hudson
 * @version $Revision: 1.2 $
 */
public class MaterialDemo extends Frame
    implements WindowListener, Runnable
{
    /** Manager for the scene graph handling */
    private SingleThreadRenderManager sceneManager;

    /** Our drawing surface */
    private DrawableSurface surface;

    public MaterialDemo()
    {
        super("Basic Aviatrix Demo");

        setLayout(new BorderLayout());
        addWindowListener(this);

        setupAviatrix();
        setupSceneGraph();

        setSize(600, 600);
        setLocation(40, 40);

        // Need to set visible first before starting the rendering thread due
        // to a bug in JOGL. See JOGL Issue #54 for more information on this.
        // http://jogl.dev.java.net
        setVisible(true);

        Runtime system_runtime = Runtime.getRuntime();
        system_runtime.addShutdownHook(new Thread(this));
    }

    /**
     * Setup the avaiatrix pipeline here
     */
    private void setupAviatrix()
    {
        // Assemble a simple single-threaded pipeline.
        GLCapabilities caps = new GLCapabilities();
        caps.setDoubleBuffered(true);
        caps.setHardwareAccelerated(true);

        CullStage culler = new NullCullStage();
        culler.setOffscreenCheckEnabled(false);

//        SortStage sorter = new NullSortStage();
        SortStage sorter = new SimpleTransparencySortStage();
        surface = new SimpleAWTSurface(caps);
        DefaultRenderPipeline pipeline = new DefaultRenderPipeline();

        pipeline.setCuller(culler);
        pipeline.setSorter(sorter);
        pipeline.setDrawableSurface(surface);

        // Render manager
        sceneManager = new SingleThreadRenderManager();
        sceneManager.addPipeline(pipeline);
        sceneManager.setDrawableSurface(surface);
        sceneManager.setMinimumFrameInterval(100);

        // Before putting the pipeline into run mode, put the canvas on
        // screen first.
        Component comp = (Component)surface.getSurfaceObject();
        add(comp, BorderLayout.CENTER);
    }

    /**
     * Setup the basic scene which consists of a quad and a viewpoint
     */
    private void setupSceneGraph()
    {
        // View group

        Viewpoint vp = new Viewpoint();

        Vector3f trans = new Vector3f(0, 0, 1);

        Matrix4f mat = new Matrix4f();
        mat.setIdentity();
        mat.setTranslation(trans);

        TransformGroup tx = new TransformGroup();
        tx.addChild(vp);
        tx.setTransform(mat);

        Group scene_root = new Group();
        scene_root.addChild(tx);

        // Flat panel that has the viewable object as the demo
        float[] coord = { 0, 0, -1, 0.25f, 0, -1, 0, 0.25f, -1 };
        float[] normal = { 0, 0, 1, 0, 0, 1, 0, 0, 1 };

        TriangleArray geom = new TriangleArray();
        geom.setValidVertexCount(3);
        geom.setVertices(TriangleArray.COORDINATE_3, coord);
        geom.setNormals(normal);

        Material material = new Material();
        material.setDiffuseColor(new float[] { 0, 0, 1 });
        material.setEmissiveColor(new float[] { 0, 0, 1 });
        material.setSpecularColor(new float[] { 1, 1, 1 });
        material.setTransparency(0.5f);

        Appearance app = new Appearance();
        app.setMaterial(material);

        Shape3D shape = new Shape3D();
        shape.setGeometry(geom);
        shape.setAppearance(app);

        TransformGroup tg = new TransformGroup();
        Matrix4f transform = new Matrix4f();
        transform.setIdentity();
        transform.setTranslation(new Vector3f(0.15f, 0, -1));
        tg.setTransform(transform);

        Shape3D backShape = new Shape3D();
        Material material2 = new Material();
        material2.setDiffuseColor(new float[] { 1, 0, 0 });
        material2.setEmissiveColor(new float[] { 1, 0, 0 });
        material2.setSpecularColor(new float[] { 1, 1, 1 });

        Appearance app2 = new Appearance();
        app2.setMaterial(material2);
        backShape.setGeometry(geom);
        backShape.setAppearance(app2);
        tg.addChild(backShape);

        scene_root.addChild(tg);
        scene_root.addChild(shape);

        Scene scene = new Scene();
        scene.setRenderedGeometry(scene_root);
        scene.setActiveView(vp);

        sceneManager.setScene(scene);
    }

    //---------------------------------------------------------------
    // Methods defined by Runnable
    //---------------------------------------------------------------

    /**
     * Run method for the shutdown hook. This is to deal with someone using
     * ctrl-C to kill the application. Makes sure that all the resources
     * are cleaned up properly.
     */
    public void run()
    {
        shutdownApp();
    }

    //---------------------------------------------------------------
    // Methods defined by WindowListener
    //---------------------------------------------------------------

    /**
     * Ignored
     */
    public void windowActivated(WindowEvent evt)
    {
    }

    /**
     * Ignored
     */
    public void windowClosed(WindowEvent evt)
    {
    }

    /**
     * Exit the application
     *
     * @param evt The event that caused this method to be called.
     */
    public void windowClosing(WindowEvent evt)
    {
        shutdownApp();
        System.exit(0);
    }

    /**
     * Ignored
     */
    public void windowDeactivated(WindowEvent evt)
    {
    }

    /**
     * Ignored
     */
    public void windowDeiconified(WindowEvent evt)
    {
    }

    /**
     * Ignored
     */
    public void windowIconified(WindowEvent evt)
    {
    }

    /**
     * When the window is opened, start everything up.
     */
    public void windowOpened(WindowEvent evt)
    {
        sceneManager.setEnabled(true);
    }

    //---------------------------------------------------------------
    // Local methods
    //---------------------------------------------------------------

    /**
     * Close down the application safely by destroying all the resources
     * currently in use.
     */
    private void shutdownApp()
    {
        sceneManager.setEnabled(false);
        surface.dispose();
    }

    public static void main(String[] args)
    {
        MaterialDemo demo = new MaterialDemo();
        demo.setVisible(true);
    }
}
