Mega Code Archive

 
Categories / Java / 3D Graphics
 

Texture By Reference

/*  * @(#)TextureByReference.java 1.14 02/10/21 14:36:22  *   * Copyright (c) 1996-2002 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.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.GraphicsConfiguration; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Transparency; import java.awt.color.ColorSpace; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.image.BufferedImage; import java.awt.image.ColorModel; import java.awt.image.ComponentColorModel; import java.awt.image.DataBuffer; import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; import java.util.Enumeration; import javax.media.j3d.Alpha; import javax.media.j3d.AmbientLight; import javax.media.j3d.Appearance; import javax.media.j3d.Behavior; import javax.media.j3d.BoundingSphere; import javax.media.j3d.BranchGroup; import javax.media.j3d.Canvas3D; import javax.media.j3d.DirectionalLight; import javax.media.j3d.ImageComponent; import javax.media.j3d.ImageComponent2D; import javax.media.j3d.Material; import javax.media.j3d.RotationInterpolator; import javax.media.j3d.Shape3D; import javax.media.j3d.Texture; import javax.media.j3d.Texture2D; import javax.media.j3d.TextureAttributes; import javax.media.j3d.Transform3D; import javax.media.j3d.TransformGroup; import javax.media.j3d.TriangleArray; import javax.media.j3d.WakeupCriterion; import javax.media.j3d.WakeupOnElapsedFrames; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.vecmath.Color3f; import javax.vecmath.Point2f; import javax.vecmath.Point3d; import javax.vecmath.Point3f; import javax.vecmath.TexCoord2f; import javax.vecmath.Vector3f; import com.sun.j3d.utils.applet.MainFrame; import com.sun.j3d.utils.image.TextureLoader; import com.sun.j3d.utils.universe.SimpleUniverse; 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;   private SimpleUniverse universe = null;   // 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 = { "animation1.gif",       "animation2.gif", "animation3.gif",       "animation4.gif", "animation5.gif",       "animation6.gif", "animation7.gif",       "animation8.gif", "animation9.gif",       "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();     universe = new SimpleUniverse(canvas);     universe.getViewingPlatform().setNominalViewingTransform();     universe.addBranchGraph(scene);     // create the gui     JPanel gui = buildGui();     this.add("South", gui);   }   public void destroy() {     universe.cleanup();   }   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);   } } class AnimateTexturesBehavior extends Behavior {   // what image are we on   private int current;   private int max;   // the images   private ImageComponent2D[] images;   // the target   private Texture2D texture;   private Appearance appearance;   // the wakeup criterion   private WakeupCriterion wakeupC;   // are the images flipped?   private boolean flip;   // need the current type because by copy changes all images   // to TYPE_INT_ARGB   private int currentType;   // for custom types   public static final int TYPE_CUSTOM_RGBA = 0x01;   public static final int TYPE_CUSTOM_RGB = 0x02;   private int customType;   // create a new AnimateTextureBehavior   // initialize the images   public AnimateTexturesBehavior(Texture2D texP, java.net.URL[] fnames,       Appearance appP, TextureByReference applet) {     int size = fnames.length;     images = new ImageComponent2D[size];     BufferedImage bImage;     TextureLoader loader;     for (int i = 0; i < size; i++) {       loader = new TextureLoader(fnames[i], TextureLoader.BY_REFERENCE           | TextureLoader.Y_UP, applet);       images[i] = loader.getImage();       bImage = images[i].getImage();       // convert the image to TYPE_4BYTE_ABGR       currentType = BufferedImage.TYPE_4BYTE_ABGR;       bImage = ImageOps.convertImage(bImage, currentType);       // flip the image       flip = true;       ImageOps.flipImage(bImage);       // set the image on the ImageComponent to the new one       images[i].set(bImage);       images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);       images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);     }     texture = texP;     current = 0;     max = size;     wakeupC = new WakeupOnElapsedFrames(20);     appearance = appP;   }   // initialize to the first image   public void initialize() {     texture.setImage(0, images[current]);     if (current < max - 1)       current++;     else       current = 0;     wakeupOn(wakeupC);   }   // procesStimulus changes the ImageComponent of the texture   public void processStimulus(Enumeration criteria) {     //    ImageOps.printType(images[current].getImage());     texture.setImage(0, images[current]);     appearance.setTexture(texture);     if (current < max - 1)       current++;     else       current = 0;     wakeupOn(wakeupC);   }   // flip the image -- useful depending on yUp   public void setFlipImages(boolean b) {     // double check that flipping is necessary     if (b != flip) {       BufferedImage bImage;       // these are the same for all images so get info once       int format = images[0].getFormat();       boolean byRef = images[0].isByReference();       boolean yUp = images[0].isYUp();       // flip all the images       // have to new ImageComponents because can't set the image at       // runtime       for (int i = 0; i < images.length; i++) {         bImage = images[i].getImage();         ImageOps.flipImage(bImage);         // if we are byRef and the bImage type does not match         // currentType         // we need to convert it. If we are not byRef we will         // save converting until it is changed to byRef         if (byRef && bImage.getType() != currentType) {           if (currentType != BufferedImage.TYPE_CUSTOM) {             bImage = ImageOps.convertImage(bImage, currentType);           } else if (customType == this.TYPE_CUSTOM_RGBA) {             bImage = ImageOps.convertToCustomRGBA(bImage);           } else {             bImage = ImageOps.convertToCustomRGB(bImage);           }         }         images[i] = new ImageComponent2D(format, bImage, byRef, yUp);         images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);         images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);       }       // set flip to new value       flip = b;     }   }   // create new ImageComponents with yUp set to the parameter. yUp on   // an ImageComponent cannot be changed at runtim   public void setYUp(boolean b) {     // double check that changing yUp is necessary     if (b != images[0].isYUp()) {       // these are the same for all images so get info once       int format = images[0].getFormat();       boolean byRef = images[0].isByReference();       // reset yUp on all the images -- have to new ImageComponents       // because       // cannot change the value at runtime       for (int i = 0; i < images.length; i++) {         // if we are byRef and the bImage type does not match         // currentType         // we need to convert it. If we are not byRef we will         // save converting until it is changed to byRef         BufferedImage bImage = images[i].getImage();         if (byRef && bImage.getType() != currentType) {           //    bImage = ImageOps.convertImage(bImage, currentType);           if (currentType != BufferedImage.TYPE_CUSTOM) {             bImage = ImageOps.convertImage(bImage, currentType);           } else if (customType == this.TYPE_CUSTOM_RGBA) {             bImage = ImageOps.convertToCustomRGBA(bImage);           } else {             bImage = ImageOps.convertToCustomRGB(bImage);           }         }         images[i] = new ImageComponent2D(format, bImage, byRef, b);         images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);         images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);       }     }   }   // create new ImageComponents with ByReference set by parameter.   // by reference cannot be changed on an image component at runtime   public void setByReference(boolean b) {     // double check that changing is necessary     if (b != images[0].isByReference()) {       // these are the same for all images so get info once       int format = images[0].getFormat();       boolean yUp = images[0].isYUp();       // reset yUp on all the images       // have to new ImageComponents because cannot set value       for (int i = 0; i < images.length; i++) {         // if the bImage type does not match currentType and we are         // setting         // to byRef we need to convert it         BufferedImage bImage = images[i].getImage();         if (bImage.getType() != currentType && b) {           //    bImage = ImageOps.convertImage(bImage, currentType);           if (currentType != BufferedImage.TYPE_CUSTOM) {             bImage = ImageOps.convertImage(bImage, currentType);           } else if (customType == this.TYPE_CUSTOM_RGBA) {             bImage = ImageOps.convertToCustomRGBA(bImage);           } else {             bImage = ImageOps.convertToCustomRGB(bImage);           }         }         images[i] = new ImageComponent2D(format, bImage, b, yUp);         images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);         images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);       }     }   }   // make a new wakeup criterion object based on the new delay time   public void setFrameDelay(int delay) {     wakeupC = new WakeupOnElapsedFrames(delay);   }   //change the type of image   public void setImageType(int newType) {     currentType = newType;     // only need to change the images if we are byRef otherwise will change     // them when we chnage to byRef     if (images[0].isByReference() == true) {       // this information is the same for all       int format = images[0].getFormat();       boolean yUp = images[0].isYUp();       boolean byRef = true;       for (int i = 0; i < images.length; i++) {         BufferedImage bImage = images[i].getImage();         bImage = ImageOps.convertImage(bImage, currentType);         images[i] = new ImageComponent2D(format, bImage, byRef, yUp);         images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);         images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);       }     }   }   public void setImageTypeCustomRGBA() {     currentType = BufferedImage.TYPE_CUSTOM;     customType = this.TYPE_CUSTOM_RGBA;     // only need to change images if we are byRef otherwise will change     // them when we change to byRef     if (images[0].isByReference()) {       // this information is the same for all       int format = images[0].getFormat();       boolean yUp = images[0].isYUp();       boolean byRef = true;       for (int i = 0; i < images.length; i++) {         BufferedImage bImage = images[i].getImage();         bImage = ImageOps.convertToCustomRGBA(bImage);         images[i] = new ImageComponent2D(format, bImage, byRef, yUp);         images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);         images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);       }     }   }   public void setImageTypeCustomRGB() {     currentType = BufferedImage.TYPE_CUSTOM;     customType = this.TYPE_CUSTOM_RGB;     // only need to change images if we are byRef otherwise will change     // them when we change to byRef     if (images[0].isByReference()) {       // this information is the same for all       int format = images[0].getFormat();       boolean yUp = images[0].isYUp();       boolean byRef = true;       for (int i = 0; i < images.length; i++) {         BufferedImage bImage = images[i].getImage();         bImage = ImageOps.convertToCustomRGB(bImage);         images[i] = new ImageComponent2D(format, bImage, byRef, yUp);         images[i].setCapability(ImageComponent.ALLOW_IMAGE_READ);         images[i].setCapability(ImageComponent.ALLOW_FORMAT_READ);       }     }   } } 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 Point2f texCoord[] = { new Point2f(-0.25f, 0.0f),       new Point2f(1.25f, 0.0f), new Point2f(0.5f, 2.0f), };   private TriangleArray geometryByRef;   private TriangleArray geometryByCopy;   // for geometry by reference   private Point3f[] verticesArray = new Point3f[12];   private TexCoord2f[] textureCoordsArray = new TexCoord2f[12];   private Vector3f[] normalsArray = new Vector3f[12];   // default to geometry by copy   public Tetrahedron() {     this(false);   }   // creates a tetrahedron with geometry by reference or by copy depending on   // the byRef parameter   public Tetrahedron(boolean byRef) {     if (byRef) {       createGeometryByRef();       this.setGeometry(geometryByRef);     } else {       createGeometryByCopy();       this.setGeometry(geometryByCopy);     }     this.setCapability(Shape3D.ALLOW_GEOMETRY_READ);     this.setCapability(Shape3D.ALLOW_GEOMETRY_WRITE);     setAppearance(new Appearance());   }   // create the geometry by reference and   // store it in the geometryByRef variable   public void createGeometryByRef() {     //    System.out.println("createGeometryByRef");     geometryByRef = new TriangleArray(12, TriangleArray.COORDINATES         | TriangleArray.NORMALS | TriangleArray.TEXTURE_COORDINATE_2         | TriangleArray.BY_REFERENCE);     int i;     // the coordinates     for (i = 0; i < 12; i++) {       verticesArray[i] = new Point3f(verts[i]);     }     geometryByRef.setCoordRef3f(verticesArray);     //    System.out.println("coordinates set");     //    Point3f[] temp1 = geometryByRef.getCoordRef3f();     //    for (i = 0; i < 12; i++) {     //       System.out.println(temp1[i]);     //    }     // the texture coordinates     for (i = 0; i < 12; i++) {       textureCoordsArray[i] = new TexCoord2f(texCoord[i % 3]);     }     geometryByRef.setTexCoordRef2f(0, textureCoordsArray);     //    System.out.println("texture coords set");     //    TexCoord2f[] temp2 = geometryByRef.getTexCoordRef2f(0);     //    for (i = 0; i < 12; i++) {     //      System.out.println(temp2[i]);     //    }     // the normals     Vector3f normal = new Vector3f();     Vector3f v1 = new Vector3f();     Vector3f v2 = new Vector3f();     Point3f[] pts = new Point3f[3];     for (int face = 0; face < 4; face++) {       pts[0] = new Point3f(verts[face * 3]);       pts[1] = new Point3f(verts[face * 3 + 1]);       pts[2] = new Point3f(verts[face * 3 + 2]);       v1.sub(pts[1], pts[0]);       v2.sub(pts[2], pts[0]);       normal.cross(v1, v2);       normal.normalize();       for (i = 0; i < 3; i++) {         normalsArray[face * 3 + i] = new Vector3f(normal);       }     }     geometryByRef.setNormalRef3f(normalsArray);     //    System.out.println("normals set");     //    Vector3f[] temp3 = geometryByRef.getNormalRef3f();     //    for (i = 0; i < 12; i++) {     //      System.out.println(temp3[i]);     //    }   }   // create the geometry by copy and store it in the geometryByCopy variable   public void createGeometryByCopy() {     int i;     geometryByCopy = new TriangleArray(12, TriangleArray.COORDINATES         | TriangleArray.NORMALS | TriangleArray.TEXTURE_COORDINATE_2);     geometryByCopy.setCoordinates(0, verts);     for (i = 0; i < 12; i++) {       geometryByCopy.setTextureCoordinate(0, i, new TexCoord2f(           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++) {       geometryByCopy.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++) {         geometryByCopy.setNormal((face * 3 + i), normal);       }     }   }   // set the geometry to geometryByRef or geometryByCopy depending on the   // parameter. Create geometryByRef or geometryByCopy if necessary   public void setByReference(boolean b) {     //    System.out.println("Tetrahedron.setByReference " + b);     // by reference is true     if (b) {       // if there is no geometryByRef, create it       if (geometryByRef == null) {         createGeometryByRef();       }       // set the geometry       this.setGeometry(geometryByRef);     }     // by reference is false     else {       // if there is no geometryByCopy, create it       if (geometryByCopy == null) {         createGeometryByCopy();       }       // set the geometry       this.setGeometry(geometryByCopy);     }   } } //some useful, static image operations class ImageOps {   // flip the image   public static void flipImage(BufferedImage bImage) {     int width = bImage.getWidth();     int height = bImage.getHeight();     int[] rgbArray = new int[width * height];     bImage.getRGB(0, 0, width, height, rgbArray, 0, width);     int[] tempArray = new int[width * height];     int y2 = 0;     for (int y = height - 1; y >= 0; y--) {       for (int x = 0; x < width; x++) {         tempArray[y2 * width + x] = rgbArray[y * width + x];       }       y2++;     }     bImage.setRGB(0, 0, width, height, tempArray, 0, width);   }   // convert the image to a specified BufferedImage type and return it   public static BufferedImage convertImage(BufferedImage bImage, int type) {     int width = bImage.getWidth();     int height = bImage.getHeight();     BufferedImage newImage = new BufferedImage(width, height, type);     int[] rgbArray = new int[width * height];     bImage.getRGB(0, 0, width, height, rgbArray, 0, width);     newImage.setRGB(0, 0, width, height, rgbArray, 0, width);     return newImage;   }   // print out some of the types of BufferedImages   static void printType(BufferedImage bImage) {     int type = bImage.getType();     if (type == BufferedImage.TYPE_4BYTE_ABGR) {       System.out.println("TYPE_4BYTE_ABGR");     } else if (type == BufferedImage.TYPE_INT_ARGB) {       System.out.println("TYPE_INT_ARGB");     } else if (type == BufferedImage.TYPE_3BYTE_BGR) {       System.out.println("TYPE_3BYTE_BGR");     } else if (type == BufferedImage.TYPE_CUSTOM) {       System.out.println("TYPE_CUSTOM");     } else       System.out.println(type);   }   public static BufferedImage convertToCustomRGBA(BufferedImage bImage) {     if (bImage.getType() != BufferedImage.TYPE_INT_ARGB) {       ImageOps.convertImage(bImage, BufferedImage.TYPE_INT_ARGB);     }     int width = bImage.getWidth();     int height = bImage.getHeight();     ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);     int[] nBits = { 8, 8, 8, 8 };     ColorModel cm = new ComponentColorModel(cs, nBits, true, false,         Transparency.OPAQUE, 0);     int[] bandOffset = { 0, 1, 2, 3 };     WritableRaster newRaster = Raster.createInterleavedRaster(         DataBuffer.TYPE_BYTE, width, height, width * 4, 4, bandOffset,         null);     byte[] byteData = ((DataBufferByte) newRaster.getDataBuffer())         .getData();     Raster origRaster = bImage.getData();     int[] pixel = new int[4];     int k = 0;     for (int j = 0; j < height; j++) {       for (int i = 0; i < width; i++) {         pixel = origRaster.getPixel(i, j, pixel);         byteData[k++] = (byte) (pixel[0]);         byteData[k++] = (byte) (pixel[1]);         byteData[k++] = (byte) (pixel[2]);         byteData[k++] = (byte) (pixel[3]);       }     }     BufferedImage newImage = new BufferedImage(cm, newRaster, false, null);     //  if (newImage.getType() == BufferedImage.TYPE_CUSTOM) {     //    System.out.println("Type is custom");     //  }     return newImage;   }   public static BufferedImage convertToCustomRGB(BufferedImage bImage) {     if (bImage.getType() != BufferedImage.TYPE_INT_ARGB) {       ImageOps.convertImage(bImage, BufferedImage.TYPE_INT_ARGB);     }     int width = bImage.getWidth();     int height = bImage.getHeight();     ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);     int[] nBits = { 8, 8, 8 };     ColorModel cm = new ComponentColorModel(cs, nBits, false, false,         Transparency.OPAQUE, 0);     int[] bandOffset = { 0, 1, 2 };     WritableRaster newRaster = Raster.createInterleavedRaster(         DataBuffer.TYPE_BYTE, width, height, width * 3, 3, bandOffset,         null);     byte[] byteData = ((DataBufferByte) newRaster.getDataBuffer())         .getData();     Raster origRaster = bImage.getData();     int[] pixel = new int[4];     int k = 0;     for (int j = 0; j < height; j++) {       for (int i = 0; i < width; i++) {         pixel = origRaster.getPixel(i, j, pixel);         byteData[k++] = (byte) (pixel[0]);         byteData[k++] = (byte) (pixel[1]);         byteData[k++] = (byte) (pixel[2]);       }     }     BufferedImage newImage = new BufferedImage(cm, newRaster, false, null);     //  if (newImage.getType() == BufferedImage.TYPE_CUSTOM) {     //    System.out.println("Type is custom");     //  }     return newImage;   } }