/*
 *      @(#)PureImmediateTex.java 1.16 01/01/11 07:38:31
 *
 * Copyright (c) 1996-2001 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.
 *
 * Modified by Yuri Nikishkov
 * Demonstrates immediate mode bugs:
 *
 */

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GraphicsConfiguration;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.geometry.ColorCube;
import com.sun.j3d.utils.universe.*;
import javax.media.j3d.*;
import javax.vecmath.*;

import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.Font;

/**
 * Pure immediate mode example program.  In pure immediate mode, the
 * renderer must be stopped on the Canvas being rendered into. In our
 * example, this is done immediately after the canvas is created. A
 * separate thread is started up to do the immediate mode rendering.
 */
public class PureImmediateTex extends Applet implements Runnable {

        private static int mode = 0;
        private static int geom = 0;

    private MyCanvas3D canvas;
    private GraphicsContext3D gc = null;
    private Geometry cube = null;
    private Transform3D cmt = new Transform3D();

    // One rotation (2*PI radians) every 6 seconds
    private Alpha rotAlpha = new Alpha(-1, 6000);

    private SimpleUniverse u = null;

        private class TexColorCube extends Shape3D {
                private final float[] cubeVerts = {
                 1.0f, -1.0f,  1.0f,    1.0f,  1.0f,  1.0f,     -1.0f,  1.0f,  1.0f,   -1.0f, -1.0f,  1.0f,            // front face
                -1.0f, -1.0f, -1.0f,   -1.0f,  1.0f, -1.0f,      1.0f,  1.0f, -1.0f,    1.0f, -1.0f, -1.0f,             // back face
                 1.0f, -1.0f, -1.0f,    1.0f,  1.0f, -1.0f,      1.0f,  1.0f,  1.0f,    1.0f, -1.0f,  1.0f,             // right face
                -1.0f, -1.0f,  1.0f,   -1.0f,  1.0f,  1.0f,     -1.0f,  1.0f, -1.0f,   -1.0f, -1.0f, -1.0f,            // left face
                 1.0f,  1.0f,  1.0f,    1.0f,  1.0f, -1.0f,     -1.0f,  1.0f, -1.0f,   -1.0f,  1.0f,  1.0f,            // top face
                -1.0f, -1.0f,  1.0f,   -1.0f, -1.0f, -1.0f,      1.0f, -1.0f, -1.0f,    1.0f, -1.0f,  1.0f,             // bottom face
                };
                private final float[] deskVerts = {
                        -1f, -1f, 0f,  1f, -1f, 0f, -1f, 1f, 0f,  1f, 1f, 0f,
                         1f, -1f, 0f, -1f, -1f, 0f,  1f, 1f, 0f, -1f, 1f, 0f,
                };
                private final int[][] texC = {{0,1},{1,0},{1,2},{2,1},{0,2},{0,2}};
                public static final int CUBE = 1;
                public static final int DESK = 2;
                public static final int GENERATE = 4;   // works only for desk!

                public TexColorCube(float scale, int mode) {
                        // Geometry
                        int vert = 0;
                        GeometryArray cube = null;
                        int format = GeometryArray.COORDINATES;
                        if ((mode & GENERATE)==0)
                                format |= GeometryArray.TEXTURE_COORDINATE_2;
                        if ((mode & CUBE)!=0) {
                                vert = 24;
                                cube = new QuadArray(vert, format);
                        } else if ((mode & DESK)!=0) {
                                int[] stripVertexCounts = {4, 4};
                                vert = 8;
                                cube = new TriangleStripArray(vert, format, stripVertexCounts);
                        }
                        // Coordinates
                        float scaledVerts[] = new float[vert*3];
                        for (int i = 0; i < vert*3; i++) {
                                if ((mode & CUBE)!=0)
                                        scaledVerts[i] = cubeVerts[i] * scale;
                                else if ((mode & DESK)!=0)
                                        scaledVerts[i] = deskVerts[i] * scale;
                        }
                        cube.setCoordinates(0, scaledVerts);
                        // Texture coordinates
                        if ((mode & GENERATE)==0) {
                                float texCoords[] = new float[vert*2];
                                for (int i = 0; i < vert; i++) {
                                        if ((mode & CUBE)!=0) {
                                                texCoords[i*2  ] = (cubeVerts[i*3+texC[i/4][0]] == -1f) ? 0f : 1f;
                                                texCoords[i*2+1] = (cubeVerts[i*3+texC[i/4][1]] == -1f) ? 1f : 0f;
                                        }  else if ((mode & DESK)!=0) {
                                                texCoords[i*2  ] = (deskVerts[i*3  ] == -1f) ? 0f : 1f;
                                                texCoords[i*2+1] = (deskVerts[i*3+1] == -1f) ? 1f : 0f;
                                        }
                                }
                                cube.setTextureCoordinates(0, 0, texCoords);
                        }
                        this.setGeometry(cube);
                        // Image
                        int texWidth = 128, texHeight = 128;
                        BufferedImage bImage = new BufferedImage(texWidth, texHeight, BufferedImage.TYPE_INT_RGB);
                        Graphics2D offscreenGraphics = bImage.createGraphics();
                        offscreenGraphics.setColor(java.awt.Color.yellow);
                        offscreenGraphics.fillRect(0, 0, texWidth-1, texHeight-1);
                        offscreenGraphics.setFont(new java.awt.Font("SansSerif",Font.PLAIN,16));
                        offscreenGraphics.setColor(java.awt.Color.red);
                        offscreenGraphics.drawString("Testing, line 01", 10, 50);
                        offscreenGraphics.drawString("Testing, line 02", 10, 90);
                        // Texture
                        ImageComponent2D imageComponent = new ImageComponent2D(ImageComponent.FORMAT_RGB, bImage, true, true);
                        Texture2D t2d = new Texture2D(Texture2D.BASE_LEVEL,   Texture.RGB, texWidth, texHeight);
                        t2d.setBoundaryModeS(t2d.WRAP);
                        t2d.setBoundaryModeT(t2d.WRAP);
                        t2d.setMinFilter(t2d.BASE_LEVEL_LINEAR);
                        t2d.setMagFilter(t2d.BASE_LEVEL_LINEAR);
                        t2d.setImage(0, imageComponent);
                        t2d.setEnable(true);
                        // Appearance
                        Appearance texApp = new Appearance();
                        texApp.setTexture(t2d);
                        if ((mode & GENERATE)!=0) {
                                Vector4f coefS = new Vector4f(0.5f/scale, 0f, 0f, 0.5f);
                                Vector4f coefT = new Vector4f(0f, -0.5f/scale, 0f, 0.5f);
                                texApp.setTexCoordGeneration(new TexCoordGeneration(TexCoordGeneration.OBJECT_LINEAR,
                                        TexCoordGeneration.TEXTURE_COORDINATE_2, coefS, coefT));
                        }
                        this.setAppearance(texApp);
                }

        }

        /**
         * Just to inform about rendering.
         */
        private class MyCanvas3D extends Canvas3D {
                public MyCanvas3D(GraphicsConfiguration config) {
                        super(config);
                }
                public void postSwap() {
                        System.out.println("postSwap");
                }
        }

    //
    // Renders a single frame by clearing the canvas, drawing the
    // geometry, and swapping the draw and display buffer.
    //
    public void render() {
                if (gc == null) {
                        // Set up Graphics context
                        gc = canvas.getGraphicsContext3D();

                        // Set up geometry
                        TexColorCube texCube = null;
                        switch (geom) {
                        default:
                        case 0: texCube = new TexColorCube(0.4f, TexColorCube.CUBE); break;
                        case 1: texCube = new TexColorCube(0.4f, TexColorCube.DESK); break;
                        case 2: texCube = new TexColorCube(0.4f, TexColorCube.DESK | TexColorCube.GENERATE); break;
                        }
                        gc.setAppearance(texCube.getAppearance());
                        cube = texCube.getGeometry();
                }

                // Compute angle of rotation based on alpha value
                double angle = rotAlpha.value() * 2.0*Math.PI;
                cmt.rotY(angle);

                // Render the geometry for this frame
                gc.clear();
                gc.setModelTransform(cmt);
                gc.draw(cube);
                canvas.swap();
    }

    //
    // Run method for our immediate mode rendering thread.
    //
    public void run() {
                if (mode == 2) {
                        // Sleep a few seconds to ensure retained rendering
                        try { Thread.sleep(5000); } catch(Exception e) {}
                }
                if (mode > 0) {
                        // Start immediate mode here, after something is displayed in retained mode
                        canvas.stopRenderer();
                }
                System.out.println("PureImmediateTex.run: starting main loop");
                while (true) {
                        render();
                        Thread.yield();
                }
    }

    public PureImmediateTex() {
    }

    //
    // init: create the canvas, stop the renderer,
    // create the universe, and start the drawing thread.
    //
    public void init() {
//              mode = 3;
                setLayout(new BorderLayout());
        GraphicsConfiguration config =  SimpleUniverse.getPreferredConfiguration();
        canvas = new MyCanvas3D(config);

                if (mode == 0) {
                canvas.stopRenderer();
                } else {
                        canvas.setBackground(java.awt.Color.white);
                }

                add("Center", canvas);
            // Create the universe and viewing branch
                u = new SimpleUniverse(canvas);
        // This will move the ViewPlatform back a bit so the
        // objects in the scene can be viewed.
        u.getViewingPlatform().setNominalViewingTransform();
                // Start a new thread that will continuously render
            new Thread(this).start();
    }

    public void destroy() {
                u.removeAllLocales();
    }

    //
    // The following allows PureImmediateTex to be run as an application
    // as well as an applet
    //
    public static void main(String[] args) {
                System.out.println("Parameter 1:");
                System.out.println(" 0 - immediate mode (no bugs)");
                System.out.println(" 1 - mixed mode, but no render (no bugs)");
                System.out.println(" 2 - mixed mode, sleep till render (bugs)");
                System.out.println("Parameter 2:");
                System.out.println(" 0 - cube, texture coordinates in geometry (NVIDIA bug)");
                System.out.println(" 1 - desk, texture coordinates in geometry (NVIDIA bug)");
                System.out.println(" 2 - desk, automatic texture generation (ATI bug)");
                if (args.length > 1) {
                        mode = Integer.parseInt(args[0]);
                        geom = Integer.parseInt(args[1]);
                }
                System.out.println("Selected rendering mode " + mode + ", geometry mode " + geom);
                new MainFrame(new PureImmediateTex(), 250, 250);
    }
}
