/*
 * File: MultipleSetTextureBug.java
 * Author: Marty Vona (vona@jpl.nasa.gov)
 * Created: 1/14/02
 *
 * Description: Demonstrates application hang bug with calling setTexture() on
 * many different Appearances, without using TextureUnitState.  Also
 * demonstrates a won't render before canvas resize bug.
 *
 * Required files: apimage.jpg, bg.jpg from the Java3D demo/java3d/images
 * directory (must be placed in current directory)
 *
 * Tested on two platforms:
 *
 * Sun J2SDK1.4b3 + Blackdown Java3D 1.3b1 on
 *   Linux 2.4.9-12 + XFree86 4.0.3 + NVidia 2313 + NVidia GeForce2 MXR
 *
 * Sun J2SDK1.4b3 + Sun Java3D 1.3b1 on
 *   Windows 2000p2 + NVidia GeForce2 MXR
 *
 * NVidia "TwinView" enabled in both cases.
 *
 * Bug 1: 25 rotating tetrahedron Shape3Ds are created, each with its own
 * Appearance.  Two Textures are created and are assigned alternately to the
 * tetrahedrons in checkerboard fashion.  Every 500ms one of the Appearances is
 * toggled to the other texture.  After a short time (varies, usually less than
 * 30 seconds) the entire application hangs.
 *
 * Bug 2: Upon startup, nothing is rendered until the application frame is
 * resized by the user.
 *
 * Adapted from: AppearanceTest.java 1.26 01/06/20 16:17:56
 *
 * Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 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.
 *
 * You acknowledge that Software is not designed,licensed or intended
 * for use in the design, construction, operation or maintenance of
 * any nuclear facility.  */

import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import com.sun.j3d.utils.applet.MainFrame;
import com.sun.j3d.utils.universe.*;
import com.sun.j3d.utils.image.TextureLoader;
import javax.media.j3d.*;
import javax.vecmath.*;

public class MultipleSetTextureBug extends Applet {

  public static final int ROWS = 5;
  public static final int COLS = 5;

  public static final int DELAY = 500;

  private Texture texture0;
  private Texture texture1;

  private Appearance[][] app = new Appearance[ROWS][COLS];
  
  private java.net.URL tex1Image = null;
  private java.net.URL tex0Image = null;
  
  private SimpleUniverse u = null;
  
  private BranchGroup createSceneGraph() {

    // Create the root of the branch graph
    BranchGroup objRoot = new BranchGroup();
    
    // Create a bounds for the background and lights
    BoundingSphere bounds =
	    new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
   
    // load textures
    texture0 = (new TextureLoader(tex0Image, this)).getTexture();
    texture1 = (new TextureLoader(tex1Image, this)).getTexture();

    // Set up the global lights
    Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f);
    Vector3f lDir1  = new Vector3f(-1.0f, -1.0f, -1.0f);
    Color3f alColor = new Color3f(0.2f, 0.2f, 0.2f);
    
    AmbientLight aLgt = new AmbientLight(alColor);
    aLgt.setInfluencingBounds(bounds);
    DirectionalLight lgt1 = new DirectionalLight(lColor1, lDir1);
    lgt1.setInfluencingBounds(bounds);
    objRoot.addChild(aLgt);
    objRoot.addChild(lgt1);
    
    // Create a bunch of objects with a behavior and add them
    // into the scene graph.
    
    int row, col;

    for (row = 0; row < ROWS; row++)
      for (col = 0; col < COLS; col++) {

        app[row][col] = new Appearance();
        app[row][col].setCapability(Appearance.ALLOW_TEXTURE_READ);
        app[row][col].setCapability(Appearance.ALLOW_TEXTURE_WRITE);
        
      }
   
    double vspacing = 1.0 / (ROWS-1);
    double hspacing = 1.0 / (COLS-1);
    double scale = Math.min(hspacing, vspacing)*0.5;

    for (int i = 0; i < ROWS; i++) {
      double ypos = (double)(i - 1) * vspacing;
      for (int j = 0; j < COLS; j++) {
        double xpos = (double)(j - 1) * hspacing;
        objRoot.addChild(createObject(app[i][j], scale,  xpos, ypos));
      }
    }
    
    // Let Java 3D perform optimizations on this scene graph.
    objRoot.compile();
    
    return objRoot;
  }
  
  private Group createObject(Appearance app, double scale,
                             double xpos, double ypos) {
    
    // Create a transform group node to scale and position the object.
    Transform3D t = new Transform3D();
    t.set(scale, new Vector3d(xpos, ypos, 0.0));
    TransformGroup objTrans = new TransformGroup(t);
    
    // Create a second transform group node and initialize it to the
    // identity.  Enable the TRANSFORM_WRITE capability so that
    // our behavior code can modify it at runtime.
    TransformGroup spinTg = new TransformGroup();
    spinTg.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    
    // Create a simple shape leaf node and set the appearance
    Shape3D shape = new Tetrahedron();
    shape.setAppearance(app);
    
    // add it to the scene graph.
    spinTg.addChild(shape);
    
    // Create a new Behavior object that will perform the desired
    // operation on the specified transform object and add it into
    // the scene graph.
    Transform3D yAxis = new Transform3D();
    Alpha rotationAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
                                    0, 0,
                                    5000, 0, 0,
                                    0, 0, 0);
    
    RotationInterpolator rotator =
	    new RotationInterpolator(rotationAlpha, spinTg, yAxis,
                               0.0f, (float) Math.PI*2.0f);
    
    BoundingSphere bounds =
	    new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);
    
    rotator.setSchedulingBounds(bounds);
    
    // Add the behavior and the transform group to the object
    objTrans.addChild(rotator);
    objTrans.addChild(spinTg);
    
    return objTrans;
  }
  
  
  public MultipleSetTextureBug() {
  }
  
  public MultipleSetTextureBug(java.net.URL texture0url,
                               java.net.URL texture1url) {
    tex0Image = texture0url;
    tex1Image = texture1url;
  }
  
  public void init() {
    if (tex0Image == null) {
	    // the path to the image for an applet
	    try {
        tex0Image = new java.net.URL(getCodeBase().toString() +
                                   "bg.jpg");
	    }
	    catch (java.net.MalformedURLException ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
	    }
    }
    
    if (tex1Image == null) {
	    // the path to the image for an applet
	    try {
        tex1Image = new java.net.URL(getCodeBase().toString() +
                                    "apimage.jpg");
	    }
	    catch (java.net.MalformedURLException ex) {
        System.out.println(ex.getMessage());
        System.exit(1);
	    }
    }
    setLayout(new BorderLayout());
    GraphicsConfiguration config =
      SimpleUniverse.getPreferredConfiguration();
    
    Canvas3D c = new Canvas3D(config);
    add("Center", c);
    
    // Create a simple scene and attach it to the virtual universe
    BranchGroup scene = createSceneGraph();
    u = new SimpleUniverse(c);
    
    // This will move the ViewPlatform back a bit so the
    // objects in the scene can be viewed.
    u.getViewingPlatform().setNominalViewingTransform();
    
    u.addBranchGraph(scene);
   
    //ok now switch the textures periodically

    boolean useTexture1 = false;

    for (int iteration = 0; ; iteration++) {

      for (int i = 0; i < ROWS; i++)
        for (int j = 0; j < COLS; j++) {
          
          if (iteration == 0) {

            app[i][j].setTexture((useTexture1) ? texture1 : texture0);
          
            useTexture1 = !useTexture1;

          } else {

            if (app[i][j].getTexture() == texture0)
              app[i][j].setTexture(texture1);
            else
              app[i][j].setTexture(texture0);

            try {
              
              (Thread.currentThread()).sleep(DELAY);
              
            } catch (InterruptedException e) {
              System.exit(-1);
            }
          }
        }
    }
  }
  
  public void destroy() {
    u.removeAllLocales();
  }
  
  
  //
  // The following allows MultipleSetTextureBug to be run as an application
  // as well as an applet
  //
  public static void main(String[] args) {
    // the path to the image file for an application
    java.net.URL texture0url = null;
    java.net.URL texture1url = null;
    try {
      texture0url = new java.net.URL("file:bg.jpg");
      texture1url = new java.net.URL("file:apimage.jpg");
    }
    catch (java.net.MalformedURLException ex) {
	    System.out.println(ex.getMessage());
	    System.exit(1);
    }
    new MainFrame(new MultipleSetTextureBug(texture0url, texture1url),
                  COLS*200+100,
                  ROWS*200+100);
  }
}

/*
 *	@(#)Tetrahedron.java 1.12 01/06/20 16:17:56
 *
 * Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * 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.
 *
 * You acknowledge that Software is not designed,licensed or intended
 * for use in the design, construction, operation or maintenance of
 * any nuclear facility.
 */

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

class Tetrahedron extends Shape3D {

  private static final float sqrt3 = (float) Math.sqrt(3.0);
  private static final float sqrt3_3 = sqrt3 / 3.0f;
  private static final float sqrt24_3 = (float) Math.sqrt(24.0) / 3.0f;
  
  private static final float ycenter = 0.5f * sqrt24_3;
  private static final float zcenter = -sqrt3_3;
  
  private static final Point3f p1 = new Point3f(-1.0f, -ycenter, -zcenter);
  private static final Point3f p2 = new Point3f(1.0f, -ycenter, -zcenter);
  private static final Point3f p3 =
  new Point3f(0.0f, -ycenter, -sqrt3 - zcenter);
  private static final Point3f p4 =
  new Point3f(0.0f, sqrt24_3 - ycenter, 0.0f);
  
  private static final Point3f[] verts = {
    p1, p2, p4,	// front face
    p1, p4, p3,	// left, back face
    p2, p3, p4,	// right, back face
    p1, p3, p2,	// bottom face
  };
  
  private TexCoord2f texCoord[] = {
    new TexCoord2f(0.0f, 0.0f),
    new TexCoord2f(1.0f, 0.0f),
    new TexCoord2f(0.5f, sqrt3 / 2.0f),
  };
  
  public Tetrahedron() {
    int i;
    
    TriangleArray tetra =
      new TriangleArray(12, TriangleArray.COORDINATES |
                        TriangleArray.NORMALS |
                        TriangleArray.TEXTURE_COORDINATE_2);
    
    tetra.setCoordinates(0, verts);
    for (i = 0; i < 12; i++) {
      tetra.setTextureCoordinate(0, i, texCoord[i%3]);
    }
    
    int face;
    Vector3f normal = new Vector3f();
    Vector3f v1 = new Vector3f();
    Vector3f v2 = new Vector3f();
    Point3f [] pts = new Point3f[3];
    for (i = 0; i < 3; i++) pts[i] = new Point3f();
    
    for (face = 0; face < 4; face++) {
      tetra.getCoordinates(face*3, pts);
      v1.sub(pts[1], pts[0]);
      v2.sub(pts[2], pts[0]);
      normal.cross(v1, v2);
      normal.normalize();
      for (i = 0; i < 3; i++) {
        tetra.setNormal((face * 3 + i), normal);
      }
    }
    this.setGeometry(tetra);
    this.setAppearance(new Appearance());
  }
}
