/*
 *      @(#)TextureByReference.java 1.7 00/03/31 14:31:12
 *
 * Copyright (c) 1996-2000 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.
 */

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 javax.media.j3d.*;
import javax.vecmath.*;
import java.awt.image.*;
import java.awt.color.ColorSpace;
import com.sun.j3d.utils.image.TextureLoader;
import javax.swing.*;
import javax.swing.event.*;


public class TextureByReference extends Applet 
implements ItemListener, ActionListener, ChangeListener {

  // need reference to animation behavior
  private AnimateTexturesBehavior animate;

  // need reference to tetrahedron
  private Tetrahedron tetra;
  
  // the gui buttons
   private JCheckBox flipB;
  private JRadioButton texByRef;
  private JRadioButton texByCopy;
  private JRadioButton geomByRef;
  private JRadioButton geomByCopy;
  private JRadioButton img4ByteABGR;
  private JRadioButton img3ByteBGR;
  private JRadioButton imgIntARGB;
  private JRadioButton imgCustomRGBA;
  private JRadioButton imgCustomRGB;
  private JRadioButton yUp;
  private JRadioButton yDown;
  private JButton animationB;
  private JSlider frameDelay;

  // image files used for the Texture animation for the applet,
  // or if no parameters are passed in for the application
  public static final String[] defaultFiles = {
    "../images/animation1.gif",
    "../images/animation2.gif",
    "../images/animation3.gif",
    "../images/animation4.gif",
    "../images/animation5.gif",
    "../images/animation6.gif",
    "../images/animation7.gif",
    "../images/animation8.gif",
    "../images/animation9.gif",
    "../images/animation10.gif"};

  private java.net.URL[] urls = null;

  
  public TextureByReference() {
  }

   public TextureByReference(java.net.URL[] fnamesP) {
     urls = fnamesP;
   }

  public void init() {
    if (urls == null) {
      urls = new java.net.URL[defaultFiles.length];
      for (int i = 0; i < defaultFiles.length; i++) {
	try {
	  urls[i] = new java.net.URL(getCodeBase().toString() + 
				       defaultFiles[i]);
	}
	catch (java.net.MalformedURLException ex) {
	  System.out.println(ex.getMessage());
	  System.exit(1);
	}
      }
    }
    setLayout(new BorderLayout());
    GraphicsConfiguration config = SimpleUniverse.getPreferredConfiguration();

    Canvas3D canvas = new Canvas3D(config);

    add("Center", canvas);

    // create a simple scene graph and attach it to a simple universe
    BranchGroup scene = createSceneGraph();
    SimpleUniverse universe = new SimpleUniverse(canvas);
    universe.getViewingPlatform().setNominalViewingTransform();
    universe.addBranchGraph(scene);

    // create the gui
    JPanel gui = buildGui();
    
    this.add("South", gui);
  }    

  public JPanel buildGui() {
    flipB = new JCheckBox("flip image", true);
    flipB.addItemListener(this);
    javax.swing.Box flipBox = new javax.swing.Box(BoxLayout.Y_AXIS);
    flipBox.add(flipB);
    Component strut1 = flipBox.createVerticalStrut(flipB.getPreferredSize().height);
    Component strut2 = flipBox.createVerticalStrut(flipB.getPreferredSize().height);
    Component strut3 = flipBox.createVerticalStrut(flipB.getPreferredSize().height);
    Component strut4 = flipBox.createVerticalStrut(flipB.getPreferredSize().height);
    Component strut5 = flipBox.createVerticalStrut(flipB.getPreferredSize().height);
    flipBox.add(strut1);
    flipBox.add(strut2);
    flipBox.add(strut3);
    flipBox.add(strut4);
    flipBox.add(strut5);

    yUp = new JRadioButton("y up");
    yUp.addActionListener(this);
    yUp.setSelected(true);
    yDown = new JRadioButton("y down");
    yDown.addActionListener(this);
    ButtonGroup yGroup = new ButtonGroup();
    yGroup.add(yUp);
    yGroup.add(yDown);
    JLabel yLabel = new JLabel("Image Orientation:");
    javax.swing.Box yBox = new javax.swing.Box(BoxLayout.Y_AXIS);
    yBox.add(yLabel);
    yBox.add(yUp);
    yBox.add(yDown);
    strut1 = yBox.createVerticalStrut(yUp.getPreferredSize().height);
    strut2 = yBox.createVerticalStrut(yUp.getPreferredSize().height);
    strut3 = yBox.createVerticalStrut(yUp.getPreferredSize().height);
    yBox.add(strut1);
    yBox.add(strut2);
    yBox.add(strut3);

    texByRef = new JRadioButton("by reference");
    texByRef.addActionListener(this);
    texByRef.setSelected(true);
    texByCopy = new JRadioButton("by copy");
    texByCopy.addActionListener(this);
    ButtonGroup texGroup = new ButtonGroup();
    texGroup.add(texByRef);
    texGroup.add(texByCopy);
    JLabel texLabel = new JLabel("Texture:*");
    javax.swing.Box texBox = new javax.swing.Box(BoxLayout.Y_AXIS);
    texBox.add(texLabel);
    texBox.add(texByRef);
    texBox.add(texByCopy);
    strut1 = texBox.createVerticalStrut(texByRef.getPreferredSize().height);
    strut2 = texBox.createVerticalStrut(texByRef.getPreferredSize().height);
    strut3 = texBox.createVerticalStrut(texByRef.getPreferredSize().height);
    texBox.add(strut1);
    texBox.add(strut2);
    texBox.add(strut3);

    geomByRef = new JRadioButton("by reference");
    geomByRef.addActionListener(this);
    geomByRef.setSelected(true);
    geomByCopy = new JRadioButton("by copy");
    geomByCopy.addActionListener(this);
    ButtonGroup geomGroup = new ButtonGroup();
    geomGroup.add(geomByRef);
    geomGroup.add(geomByCopy);
    JLabel geomLabel = new JLabel("Geometry:");
    javax.swing.Box geomBox = new javax.swing.Box(BoxLayout.Y_AXIS);
    geomBox.add(geomLabel);
    geomBox.add(geomByRef);
    geomBox.add(geomByCopy);
    strut1 = geomBox.createVerticalStrut(geomByRef.getPreferredSize().height);
    strut2 = geomBox.createVerticalStrut(geomByRef.getPreferredSize().height);
    strut3 = geomBox.createVerticalStrut(geomByRef.getPreferredSize().height);
    geomBox.add(strut1);
    geomBox.add(strut2);
    geomBox.add(strut3);
    
    img4ByteABGR = new JRadioButton("TYPE_4BYTE_ABGR");
    img4ByteABGR.addActionListener(this);
    img4ByteABGR.setSelected(true);
    img3ByteBGR = new JRadioButton("TYPE_3BYTE_BGR");
    img3ByteBGR.addActionListener(this);
    imgIntARGB = new JRadioButton("TYPE_INT_ARGB");
    imgIntARGB.addActionListener(this);
    imgCustomRGBA = new JRadioButton("TYPE_CUSTOM RGBA");
    imgCustomRGBA.addActionListener(this);
    imgCustomRGB = new JRadioButton("TYPE_CUSTOM RGB");
    imgCustomRGB.addActionListener(this);
    ButtonGroup imgGroup = new ButtonGroup();
    imgGroup.add(img4ByteABGR);
    imgGroup.add(img3ByteBGR);
    imgGroup.add(imgIntARGB);
    imgGroup.add(imgCustomRGBA);
    imgGroup.add(imgCustomRGB);
    JLabel imgLabel = new JLabel("Image Type:*");
    javax.swing.Box imgBox = new javax.swing.Box(BoxLayout.Y_AXIS);
    imgBox.add(imgLabel);
    imgBox.add(img4ByteABGR);
    imgBox.add(img3ByteBGR);
    imgBox.add(imgIntARGB);
    imgBox.add(imgCustomRGBA);
    imgBox.add(imgCustomRGB);

    javax.swing.Box topBox = new javax.swing.Box(BoxLayout.X_AXIS);
    topBox.add(flipBox);
    topBox.add(texBox);
    topBox.add(geomBox);
    topBox.add(yBox);
    Component strut = topBox.createRigidArea(new Dimension(10, 10));
    topBox.add(strut);
    topBox.add(imgBox);

    frameDelay = new JSlider(0, 50, 0);
    frameDelay.addChangeListener(this);
    frameDelay.setSnapToTicks(true);
    frameDelay.setPaintTicks(true);
    frameDelay.setPaintLabels(true);
    frameDelay.setMajorTickSpacing(10);
    frameDelay.setMinorTickSpacing(1);
    frameDelay.setValue(20);
    JLabel delayL = new JLabel("frame delay");
    javax.swing.Box delayBox = new javax.swing.Box(BoxLayout.X_AXIS);
    delayBox.add(delayL);
    delayBox.add(frameDelay);
    
    animationB = new JButton(" stop animation ");
    animationB.addActionListener(this);

    JLabel texInfo1 = new JLabel("*To use ImageComponent by reference feature, use TYPE_4BYTE_ABGR on Solaris");
    JLabel texInfo2 = new JLabel("and TYPE_3BYTE_BGR on Windows");

    JPanel buttonP = new JPanel();
    GridBagLayout gridbag = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();
    buttonP.setLayout(gridbag);
    c.anchor = GridBagConstraints.CENTER;
    c.gridwidth = GridBagConstraints.REMAINDER;
    gridbag.setConstraints(topBox, c);
    buttonP.add(topBox);
    gridbag.setConstraints(delayBox, c);
    buttonP.add(delayBox);
    gridbag.setConstraints(animationB, c);
    buttonP.add(animationB);
    gridbag.setConstraints(texInfo1, c);
    buttonP.add(texInfo1);
    gridbag.setConstraints(texInfo2, c);
    buttonP.add(texInfo2);

    return buttonP;

  }

  public BranchGroup createSceneGraph() {

    // create the root of the branch group
    BranchGroup objRoot = new BranchGroup();

    // create the transform group node and initialize it
    // enable the TRANSFORM_WRITE capability so that it can be modified
    // at runtime.  Add it to the root of the subgraph
    Transform3D rotate = new Transform3D();
    TransformGroup objTrans = new TransformGroup(rotate);
    objTrans.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
    objRoot.addChild(objTrans);

    // bounds
    BoundingSphere bounds =
      new BoundingSphere(new Point3d(0.0,0.0,0.0), 100.0);

    // set up some light
    Color3f lColor1 = new Color3f(0.7f, 0.7f, 0.7f);
    Vector3f lDir1  = new Vector3f(-1.0f, -0.5f, -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);
    

    Appearance appearance = new Appearance();

    // enable the TEXTURE_WRITE so we can modify it at runtime
    appearance.setCapability(Appearance.ALLOW_TEXTURE_WRITE);

    // load the first texture
    TextureLoader loader = new TextureLoader(urls[0],
					     TextureLoader.BY_REFERENCE |
 					     TextureLoader.Y_UP,
 					     this);
    // get the texture from the loader
    Texture2D tex = (Texture2D)loader.getTexture();
    
    // get the BufferedImage to convert to TYPE_4BYTE_ABGR and flip
    // get the ImageComponent because we need it anyway
    ImageComponent2D imageComp = (ImageComponent2D)tex.getImage(0);
    BufferedImage bImage = imageComp.getImage();
    // convert the image
    bImage = ImageOps.convertImage(bImage, BufferedImage.TYPE_4BYTE_ABGR);
    // flip the image
    ImageOps.flipImage(bImage);
    imageComp.set(bImage);

    tex.setCapability(Texture.ALLOW_IMAGE_WRITE);
    tex.setBoundaryModeS(Texture.CLAMP);
    tex.setBoundaryModeT(Texture.CLAMP);
    tex.setBoundaryColor(1.0f, 1.0f, 1.0f, 1.0f);
   
    // set the image of the texture
    tex.setImage(0, imageComp);

    // set the texture on the appearance
    appearance.setTexture(tex);

    // set texture attributes
    TextureAttributes texAttr = new TextureAttributes();
    texAttr.setTextureMode(TextureAttributes.MODULATE);
    appearance.setTextureAttributes(texAttr);

    // set material properties
    Color3f black = new Color3f(0.0f, 0.0f, 0.0f);
    Color3f white = new Color3f(1.0f, 1.0f, 1.0f);
    appearance.setMaterial(new Material(white, black, white, black, 1.0f));

    // create a scale transform
    Transform3D scale = new Transform3D();
    scale.set(.6);
    TransformGroup objScale = new TransformGroup(scale);
    objTrans.addChild(objScale);

    tetra = new Tetrahedron(true);
    tetra.setAppearance(appearance);
    objScale.addChild(tetra);

    // create the behavior
    animate = new AnimateTexturesBehavior(tex, 
					  urls,
					  appearance,
					  this);
    animate.setSchedulingBounds(bounds);

    objTrans.addChild(animate);

    // add a rotation behavior so we can see all sides of the tetrahedron
      Transform3D yAxis = new Transform3D();
  	Alpha rotorAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
  				     0, 0,
  				     4000, 0, 0,
  				     0, 0, 0);
  	RotationInterpolator rotator =
  	    new RotationInterpolator(rotorAlpha,
  				     objTrans,
  				     yAxis,
  				     0.0f, (float) Math.PI*2.0f);
  	rotator.setSchedulingBounds(bounds);
	objTrans.addChild(rotator);       


    // have java3d perform optimizations on this scene graph
    objRoot.compile();

    return objRoot;
  }

  // callback for the animation button and delay text field
  public void actionPerformed(ActionEvent e) {
    Object o = e.getSource();

    // for the animation button
    if (o == animationB) {
      if (animate.getEnable()) {
	animate.setEnable(false);
	animationB.setText("start animation");
      }
      else {
	animate.setEnable(true);
	animationB.setText(" stop animation ");
      }
    }

    // for the texByRef button
    else if (o == texByRef && texByRef.isSelected()) {
      animate.setByReference(true);
    }
    // texByCopy button
    else if (o == texByCopy && texByCopy.isSelected()) {
      animate.setByReference(false);
    }
    // yUp button
    else if (o == yUp && yUp.isSelected()) {
      animate.setYUp(true);
    }
    // ydown button
    else if (o == yDown && yDown.isSelected()) {
      animate.setYUp(false);
    }
    //geomByRef button
    else if (o == geomByRef) {
      tetra.setByReference(true);
    }
    // geomByCopy button
    else if (o == geomByCopy) {
      tetra.setByReference(false);
    }
    // TYPE_INT_ARGB
    else if (o == imgIntARGB) {
      animate.setImageType(BufferedImage.TYPE_INT_ARGB);
    }
    // TYPE_4BYTE_ABGR
    else if (o == img4ByteABGR) {
      animate.setImageType(BufferedImage.TYPE_4BYTE_ABGR);
    }
    // TYPE_3BYTE_BGR
    else if (o == img3ByteBGR) {
      animate.setImageType(BufferedImage.TYPE_3BYTE_BGR);
    }
    // TYPE_CUSTOM RGBA
    else if (o == imgCustomRGBA) {
      animate.setImageTypeCustomRGBA();
    }
    // TYPE_CUSTOM RGB
    else if (o == imgCustomRGB) {
      animate.setImageTypeCustomRGB();
    }
  }

  // callback for the checkboxes
  public void itemStateChanged(ItemEvent e) {
    Object o = e.getSource();
    // for the flip checkbox
    if (o == flipB) {
      if (e.getStateChange() == ItemEvent.DESELECTED) {
	animate.setFlipImages(false);
      }
      else animate.setFlipImages(true);
    }
  }

  // callback for the slider
  public void stateChanged(ChangeEvent e) {
    Object o = e.getSource();
    // for the frame delay
    if (o == frameDelay) {
      animate.setFrameDelay(frameDelay.getValue());
    }
  }

  // allows TextureByReference to be run as an application as well as an applet
  public static void main(String[] args) {
    java.net.URL fnames[] = null;
    if (args.length > 1) {
      fnames = new java.net.URL[args.length];
      for (int i = 0; i < args.length; i++) {
	try {
	  fnames[i] = new java.net.URL("file:" + args[i]);
	}
	catch (java.net.MalformedURLException ex) {
	  System.out.println(ex.getMessage());
	}
      }
    }
    else {
      fnames = new java.net.URL[TextureByReference.defaultFiles.length];
      for (int i = 0; i < TextureByReference.defaultFiles.length; i++) {
	try {
	  fnames[i] = new java.net.URL("file:" +
				     TextureByReference.defaultFiles[i]);
	}
	catch (java.net.MalformedURLException ex) {
	  System.out.println(ex.getMessage());
	  System.exit(1);
	}
      }
    }
    new MainFrame((new TextureByReference(fnames)), 650, 750);    
  }
}






