Screen Shot

Hey all, I am having some trouble with the code below. This code is just to take a screenshot and display it, but when I can get the screen shot working quickly enough I plan to break the image up and ‘shatter’ it like a baseball going through a window. The first and important problem is that it takes too long, in my example it’s taking 1300 ms to complete taking the screenshot (392x360 size GLCanvas), converting it to a 512x512 image so I can use it as a texture. Creating a 256x256 sized image instead takes 800 ms, I think I can be happy if it can be reduced to 200 ms or less but I’m not sure it can be done, even with a 256x256 sized texture. The other problem I’m having is that the resulting image is quite a bit darker than the original scene. I’ve tried shutting off lighting in case the lighting calculations hadn’t been applied to the image before the screen shot or if maybe lighting was being applied twice (once to the original scene and again to the texture when it’s redisplayed) but that didn’t seem to be part of the problem. I did try using the other setRGB method of ImageBuffer to set the pixel information all at once in bulk, but this actually took about 200 ms longer to finish because it required an extra array to be created (pretty sure in C++ I could have just used a byte array and grabbed the alpha channel too and then cast it as an int array to avoid the problem of creating an extra array but I’m pretty sure that even if that worked in java it’d be a pretty bad idea so I’m not trying it). To use the code below, put the following line at the bottom of your drawing code. Since this will overlay the rest of the scene, so you probably will want to shut off drawing the rest of the scene after si has been created. Also note that in the ShatteredImage class in Display it may be necessary to set the vertixes away from the camera more or move the camera back.

Anyway, if anyone can help get this code sped up enough, I will post the final code so anyone can use it (final code being the window shattering effect).

/******** Code to use at end of your drawing ********/
ShatteredImage si = null;
if (si == null) {
Dimension d = glcanvas.getSize();
si = new ShatterImage(R,gl,d.width,d.height);
}
if (si != null) {
si.display(gl,timeDif);
}

/******** Class that takes the screenshot ********/

import java.util.;
import java.awt.image.
;
import java.awt.;
import javax.swing.
;
import net.java.games.jogl.;
import java.nio.
;
import java.awt.color.*;

public class ShatterImage {

  Random R;
  byte[] image;
  
  public ShatterImage(Random R,GL gl,int width,int height) {
        this.R = R;
        System.out.println(System.currentTimeMillis());
        image = new byte[width*height*3];
        //int[] tempimage = new int[width*height];
        gl.glReadPixels(0, 0, width, height, GL.GL_RGB, GL.GL_BYTE, image);
        BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        for (int i = 0; i < height; i++) {
              for (int i2 = 0; i2< width; i2++) {
                    int color = 0;
                    int r = image[(i*width*3)+i2*3];
                    int g = image[((i*width*3)+i2*3)+1];
                    int b = image[((i*width*3)+i2*3)+2];
                    color = r * 0x010000 + g * 0x000100 + b;
                    bi.setRGB(i2,i,color);
                    //tempimage[i*width+i2] = color;
              }
        }
        
        //bi.setRGB(0,0,width,height,tempimage,0,width);
        System.out.println(System.currentTimeMillis());
        Image scale = bi.getScaledInstance(512,512,Image.SCALE_FAST);
        bi = new BufferedImage(512,512,BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = bi.createGraphics();
        g2.drawImage(scale,0,0,null);
        
        ByteBuffer bb = convertImageData(bi);
        System.out.println(System.currentTimeMillis());
        gl.glBindTexture(GL.GL_TEXTURE_2D, 400);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
        gl.glTexImage2D(GL.GL_TEXTURE_2D,0,GL.GL_RGB,512,512,0,GL.GL_RGB,GL.GL_UNSIGNED_BYTE,bb );
        System.out.println(System.currentTimeMillis());
  }
  
private ByteBuffer convertImageData(BufferedImage bufferedImage)  
{ 
              ComponentColorModel glColorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB),
              new int[] {8,8,8,0},false,false,ComponentColorModel.OPAQUE,DataBuffer.TYPE_BYTE);
    ByteBuffer imageBuffer = null; 
    WritableRaster raster;
    BufferedImage texImage;
    
    int texWidth = bufferedImage.getWidth();
    int texHeight = bufferedImage.getHeight();
    
    raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,texWidth,texHeight,3,null);
    texImage = new BufferedImage(glColorModel,raster,false,new Hashtable());
    
    Graphics g = texImage.getGraphics();
    g.drawImage(bufferedImage,0,0,null);
    
    byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData(); 

    imageBuffer = ByteBuffer.allocateDirect(data.length); 
    imageBuffer.order(ByteOrder.nativeOrder()); 
    imageBuffer.put(data, 0, data.length); 

    return imageBuffer; 
} 

  public void display(GL gl,long timeDiff) {
        float halfsize = 5f;
        gl.glBindTexture(GL.GL_TEXTURE_2D,400);
        gl.glBegin(GL.GL_QUADS);
              gl.glNormal3f( 0.0f, 0.0f, 1.0f);
              gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-halfsize, -halfsize,  -0f);
              gl.glTexCoord2f(1.0f, 0.0f); gl.glVertex3f( halfsize, -halfsize,  -0f);
              gl.glTexCoord2f(1.0f, 1.0f); gl.glVertex3f( halfsize,  halfsize,  -0f);
              gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f(-halfsize,  halfsize,  -0f);
        gl.glEnd();
  }

}

From what I see, you’re reading the framebuffer back across to main memory, tinkering with it, then updating a texture with this new data. Which means you’re sending a whacking big set of pixel data across the AGP bus twice, which is probably why its so slow.

Assuming you don’t need to do any complex pixel math before putting it on a texture (which you shouldn’t if you’re just doing a shatter) then try using glTexSubImage2d() instead, assuming the texture isn’t too big all the data need never leave the graphics card.

Thanks, I’ll look into it, I know there is about a bazillion texture commands but I’m still struggling with the standard ones for setting textures. The only thing I’m doing to the image is rescaling it to a proper texture size.

glTexSubImage2D seems to be a way to modify a texture that already exists, such as change one side of the texture to some different image or to add a decal to a texture. Correct me if I’m wrong.

Grabbing the framebuffer is using up about 300 ms time so getting rid of that wouldn’t hurt.

The resize you’re doing with the grabed framebuffer is also not going to help speed, if at all possible you should avoid that. IMHO, the best way would be to rerender your scene to a 512x512 area on the framebuffer (just set the viewport and draw as normal) then you can just copy this straight into a texture with glTexSubImage2D.

If you don’t want to re-render to a smaller, power of two size then you can either use several textures of different sizes and piece them together or use an overly large one and have unused space on the edges.

Or you could use the extensions to allow non-power of two texture sizes. IIRC they have some restrictions (no mipmapping, no tiling) but they shouldn’t matter for this effect.

I just thought about the java-class: java.awt.Robot. It maybe don’t work when using GL?

I don’t think Robot would be suitable for something like this, it shouldn’t be faster… but it looks like an interesting class assuming it would allow a java program to actually remote control another application…

Just noticed after discovering glCopyTexSubImage2D that it was what was recommended earlier, and I read it as glTexSubImage2D, if you were wondering about my retarded response now you know :stuck_out_tongue:

I consider the scaling to be required since I don’t want to force someone to use a specific window size, especially since typical screen resolutions are not power of two compatable. I’m not sure what options java gives in full screen mode which I intend to use as I get things more complete but I have some doubts that I can just go with a perfect texture resolution for a window.

For the viewport change are you suggesting I change the aspect ratio so that my scene would fit in a power of 2 space and then just grab that portion (might be a little tricky but I could do this, though it probably would probably make this effect limitted to my current planned use)? I’m currently going through a bunch of info on viewport translations since I barely skimmed over it the first time I read through it, much less understood it all, and I haven’t touched it since (I use a few pre-set things in init probably from a nehe tutorial and an aspect ratio of 1 and haven’t touched things since they work). Anyway, thanks for the pointers and I’ll update everyone when I have it figured out (or at least working better).

Ack, I really did mean to say glCopyTexSubImage, I don’t have any docs handy here so I couldn’t check it was the right one :-[

For the viewport, all you should have to do is use glViewport to set it to render to a 512x512 area and then just draw as normal. Aspect ratio and everything else should just work fine without any changes. It’ll give you a little bluring due to the lower res but its the best you can do to avoid the manual resize.

Yeah, I figured out the viewport bit, and after some testing it looks like it’ll work pretty good, it’s easily fast enough (I knew there had to be a faster way than what I was doing originally) and I don’t think the blurring will be a big issue, I’m going with the first power of two less than or equal to the screen resolution (limitted to 512 since it’s a fairly safe texture size I think).

The bad news is the viewport has to be changed before anything is drawn to it. I was hoping to just change the viewport, grab the image and change it back, but instead I’ll have to change the viewport before I start drawing and then grab the image. This isn’t a major issue since after I grab the image I’ll just be placing that up over the scene itself but it will make for some spegetti code mess to actually use the class (translation: might not be very helpful to anyone else when I post my final code).

I built my class to handle things, and while it’s not perfect, it does the job. I had to get a little messy because the viewport needed to be changed before the scene was drawn (and the scene had to be drawn in a viewport that was compatible with texture sizes before I could snatch it), it works good in my real app.

The problem comes in when I create a demonstration application that I can post here (the viewport made things a little messy so it’d be almost useless to post just the code that does the work). In my demonstration I don’t do anything fancy, enable the standard stuff like depth buffer and such and texturing. For some reason in this new app the texture created from the screen only has blue and black colors, the screen displays plenty of colors without any problems, but for some reason it doesn’t get grabbed in the image right. Neither program makes any changes to pixel reading/writing functions so this seems strange to me. The demo program doesn’t use lighting or blending but otherwise uses the same settings.

During testing of this second demo app I’m seeing some other things that don’t work quite as planned that hopefully I’ll have ironed out tomorrow so I can post the code in all it’s glory (glory = 0; glory–:wink:

I’ve given up on figuring out why the texture only seems to include blue in the demonstration application, as it displays as intended in the real application (hopefully it’s not a sign of some other problem like forgetting to call glEnd() somewhere or something along those lines. The following code is for reference and probably not suitable for anyone in any way, but I said I’d post it so here it is (oh yeah, and the effect is still far from perfect, I may update later after more time has been placed into what I don’t like, there is no simulation of gravity or any other physics other than the point where ‘something’ hits has the most movement).

/* Main.java /
import net.java.games.jogl.
;

import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Main {
public static void main( String[] args ) {
try {
Frame testFrame = new Frame(“Shatter Image Demo”);
testFrame.setSize( 400, 400 );

              GLCapabilities glCaps = new GLCapabilities();
              glCaps.setRedBits(8);
              glCaps.setBlueBits(8);
              glCaps.setGreenBits(8);
              glCaps.setAlphaBits(8);
              
              GLCanvas canvas = GLDrawableFactory.getFactory().createGLCanvas( glCaps );
              
              testFrame.add( canvas );
              RenderingCanvas rendr = new RenderingCanvas(canvas);
              canvas.addGLEventListener(rendr);
              canvas.addKeyListener(rendr);
              canvas.addMouseMotionListener(rendr);
              canvas.addMouseListener(rendr);
              
              final Animator animator = new Animator( canvas);
              testFrame.addWindowListener(new WindowAdapter() {
                    public void windowClosing(WindowEvent e) {
                          animator.stop();
                          System.exit(0);
                    }
              });
              testFrame.show();
              animator.start();
        } catch( Exception e ) {
              e.printStackTrace();
        }
  }

}

/* Point.java */
public class Point {
float x = 0;
float y = 0;
public Point (float x,float y) {
this.x = x;
this.y = y;
}
}

/* RenderingCanvas.java /
import net.java.games.jogl.
;
import net.java.games.jogl.util.;
import java.awt.
;
import java.awt.event.*;
import java.util.Random;

public class RenderingCanvas implements GLEventListener, KeyListener, MouseListener, MouseMotionListener
{
GLCanvas canvas = null;

  //gl coordinate size of our scene
  float MIN_BOUNDS = -10f;
  float MAX_BOUNDS = 10f;
  
  boolean changeviewport = false;
  
  float br = 0.8f;
  float bg = 0.8f;
  float bb = 1f;
  
  ShatterImage si;
  
  //Mouse location, also used for shatter point
  float x = 0;
  float y = 0;
  
  Random R = new Random();
  
  long lastTime = System.currentTimeMillis();
  
  public RenderingCanvas(GLCanvas canvas) {
        this.canvas = canvas;
  }
  
  public void      init(GLDrawable      drawable)      {      
        GL gl = drawable.getGL();      
        GLU glu = drawable.getGLU();      
        
        gl.glEnable( GL.GL_TEXTURE_2D);
        gl.glShadeModel( GL.GL_SMOOTH );      
        gl.glClearColor(0.0f, 0.0f, 0.0f, 1f);            gl.glClearDepth(1.0f);                                                gl.glEnable(GL.GL_DEPTH_TEST);                                          gl.glDepthFunc(GL.GL_LEQUAL);                                          gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);      
        gl.glEnable(GL.GL_CULL_FACE);
  }      
  
  //      Key      Listener events
  public void      keyTyped(KeyEvent      e) {
        char c = e.getKeyChar();
  }
  
  public void mouseClicked(MouseEvent e) {
  }
  public void mouseEntered(MouseEvent e) {
  }
  public void mouseExited(MouseEvent e) {
  }
  public void mousePressed(MouseEvent e) {
  }
  public void mouseReleased(MouseEvent e) {
        changeviewport = true;
  }
  
  public void mouseMoved(MouseEvent e) {
        Rectangle bounds = canvas.getBounds();
        float incX = ((float)MAX_BOUNDS * 2) / (float)(bounds.width - bounds.x);
        float incY = ((float)MAX_BOUNDS * 2) / (float)(bounds.height - bounds.y);
        x = (float)(e.getX() * incX) + MIN_BOUNDS;
        y = (float)((e.getY() * incY) + MIN_BOUNDS) * -1;
  }
  
  public void mouseDragged(MouseEvent e) {
        mouseMoved(e);//same behavrior for moving and dragging
  }
  
  public void      keyPressed(KeyEvent      e){
  }

  public void      keyReleased(KeyEvent e){
  }
              
  public void display(GLDrawable drawable) {
        try {
              GL gl = drawable.getGL();
              GLU glu = drawable.getGLU();
              GLUT glut = new GLUT();
              long time = System.currentTimeMillis();
              long timeDif = time - lastTime;
              lastTime = time;
              
              boolean viewportchanged = false;
              if (changeviewport) {
                    Dimension d = canvas.getSize();
                    int twidth = ShatterImage.get2Fold(d.width);
                    int theight = ShatterImage.get2Fold(d.height);
                    gl.glViewport(0,0,twidth,theight);
                    viewportchanged = true;
              }
              gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT );      
              gl.glLoadIdentity();
              gl.glTranslatef(0f,0f,-24f);
              gl.glBindTexture(GL.GL_TEXTURE_2D,0);
              //Draw our scene here
              //Draw in a background mostly for filler
              gl.glBegin(GL.GL_QUADS);
              gl.glColor3f(br,bg,bb);
              gl.glVertex3f(MIN_BOUNDS,MIN_BOUNDS,0f);
              gl.glVertex3f(MAX_BOUNDS,MIN_BOUNDS,0f);
              gl.glVertex3f(MAX_BOUNDS,MAX_BOUNDS,0f);
              gl.glVertex3f(MIN_BOUNDS,MAX_BOUNDS,0f);
              gl.glEnd();
              
              
              gl.glBegin(GL.GL_TRIANGLES);
              
              gl.glColor3f(1f,1f,0f);
              gl.glVertex3f(-4f,-4f,0.1f);
              gl.glVertex3f(4f,-4f,0.1f);
              gl.glVertex3f(0f,4f,0.1f);
              
              gl.glColor3f(1f,0f,0f);
              gl.glVertex3f(-3f,-3f,0.1f);
              gl.glVertex3f(3f,-3f,0.1f);
              gl.glVertex3f(0f,3f,0.1f);
              
              gl.glColor3f(0f,1f,0f);
              gl.glVertex3f(-2f,-2f,0.1f);
              gl.glVertex3f(2f,-2f,0.1f);
              gl.glVertex3f(0f,2f,0.1f);
              
              gl.glColor3f(0f,0f,1f);
              gl.glVertex3f(-1f,-1f,0.1f);
              gl.glVertex3f(1f,-1f,0.1f);
              gl.glVertex3f(0f,1f,0.1f);
              gl.glEnd();
              //If viewport has changed, we grab our screenshot here.
              if (viewportchanged) {
                    Dimension d = canvas.getSize();
                    si = new ShatterImage(gl,d.width,d.height,MAX_BOUNDS,new Point(x,y),20);
                    //New background color just so that shattered fragments are distinguishable
                    br = R.nextFloat()%1f;//%1f is probably redundant but didn't want to check
                    bg = R.nextFloat()%1f;
                    bb = R.nextFloat()%1f;
              }
              if (si != null) {
                    si.display(gl,timeDif);
              }
              if (viewportchanged) {
                    changeviewport = false;
              }
        } catch (Throwable t) {
              //This is probably bad code, but I use it so that I can see the exception before
              //JOGL gets hit with it as JOGL will not report the exception (typically it will
              //say something about not being able to remove GL context).
              t.printStackTrace();
        }
  }
              
  public void reshape(GLDrawable gLDrawable, int x, int y, int width, int height) {
        GL gl      =      gLDrawable.getGL();
        GLU      glu      =      gLDrawable.getGLU();
        float h = 1f;
        gl.glViewport(0, 0,      width, height);       //      don't      need to      call this      according      to jogl      docs
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluPerspective(45.0f,h,0.01f,40.0);
        gl.glMatrixMode(GL.GL_MODELVIEW);
        gl.glLoadIdentity();
        
  }

  public void      displayChanged(GLDrawable      drawable,      boolean      modeChanged, boolean deviceChanged)      
  {      
  }      

}

/* ShatterImage.java */
//For this class to work properly, an aspect ratio of 1 may be required, and
//the viewport needs to be set before creating this class, and before drawing
//the scene this class will use as a texture, this class will return the scene to
//the width and height provided in the constructor. Lighting effects can
//be an issue, most likely requires that you set your ambient lighting to full
//when the screenshot is being displayed.

import java.util.;
import net.java.games.jogl.
;

public class ShatterImage {
//Vector to hold an array of screen fragments called ShatterFragment
Vector shatterFragmentList = new Vector();

  //halfsize represents half the size of the screen in opengl coordinates, and is used
  //to determine the size of geometry so that it fits the screen using an aspect ratio of 1.
  //Using an aspect ratio other than 1, should work but I haven't tested it.
  float halfsize;
  
  public ShatterImage(GL gl,int width,int height,float halfsize,Point ShatterPoint,int slices) {
        //Calculate new viewport size. The viewport is changed in RenderCanvas already,
        //it had to be set before the rest of the scene was drawn which makes this
        //class very dependant.
        this.halfsize = halfsize;
        int twidth = get2Fold(width);
        int theight = get2Fold(height);
        
        //Grab a copy of the scene...
        gl.glBindTexture(GL.GL_TEXTURE_2D, 400);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
        gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
        gl.glCopyTexImage2D(GL.GL_TEXTURE_2D,0,GL.GL_RGBA,0,0,twidth,theight,0);
        
        //Set the viewport back to the original size
        gl.glViewport(0,0,width,height);
        
        //increment is how large each square chunk of the screen is
        //texMod is used as a multiplier to setup the correct texture coordinates
        //for each screen fragment.
        float increment = (halfsize * 2f) / slices;
        float texMod = 1f / (halfsize * 2f);
        
        //This is the actual geometry for each square chunk of the screen.
        //I used square screen chunks for simplicity (6 triangles in a hex formation would
        //have probably been a lot more realistic).
        //Each square is broken down into 4 triangles, because quads would look stupid.
        //(It takes special glass to break into quads, and this glass isn't THAT special).
        Point[] gbottom = new Point[3];//Bottom triangle
        Point[] gright = new Point[3];//Right triangle
        Point[] gtop = new Point[3];//Top triangle
        Point[] gleft = new Point[3];//Left triangle
        //inrement is added to the points below so that 0,0 local coordinates will be center
        //add increment to the y
        gbottom[0] = new Point(-increment*.5f,-increment*.25f);
        gbottom[1] = new Point(increment*.5f,-increment*.25f);
        gbottom[2] = new Point(0f,increment*.25f);
        //subtract increment from the x
        gright[0] = new Point(increment*.25f,-increment*.5f);
        gright[1] = new Point(increment*.25f,increment*.5f);
        gright[2] = new Point(-increment*.25f,0f);
        //subtract increment from the y
        gtop[0] = new Point(increment*.5f,increment*.25f);
        gtop[1] = new Point(-increment*.5f,increment*.25f);
        gtop[2] = new Point(0f,-increment*.25f);
        //add increment to the x
        gleft[0] = new Point(-increment*.25f,increment*.5f);
        gleft[1] = new Point(-increment*.25f,-increment*.5f);
        gleft[2] = new Point(increment*.25f,0f);
        float x;
        float y;
        //These two for loops calculate the texture coordinates, set the x and y location
        //of each fragment, and calculate the shatter point.
        //ShatterPoint represents the location where whatever broke the glass
        //hit at, and anything in that area will move more rapidly
        for (int i = 0; i < slices; i++) {//x
              for (int i2 = 0; i2 < slices; i2++) {//y
                    
                    Point vertices[] = new Point[5];//All possible vertices
                    //lower left, lower right, upper right, upper left, center point
                    vertices[0] = new Point(((i*increment))*texMod,((i2*increment))*texMod);
                    vertices[1] = new Point((((i+1)*increment))*texMod,((i2*increment))*texMod);
                    vertices[2] = new Point((((i+1)*increment))*texMod,(((i2+1)*increment))*texMod);
                    vertices[3] = new Point((((i)*increment))*texMod,(((i2+1)*increment))*texMod);
                    //Fourth vertice uses the average of high and low x vertices and high and low y vertices
                    vertices[4] = new Point((vertices[0].x+vertices[1].x)*.5f,(vertices[0].y+vertices[3].y)*.5f);
                    
                    
                    Point[] tvertices = new Point[3];//Actual texture coords
                    tvertices[0] = vertices[0];
                    tvertices[1] = vertices[1];
                    tvertices[2] = vertices[4];
                    x = -halfsize+(i*increment)+(increment*.5f);
                    y = -halfsize+(i2*increment)+(increment*.25f);//.25
                    Point sp = new Point((x-ShatterPoint.x)/halfsize,(y-ShatterPoint.y)/halfsize);//Based on ShatterPoint and
                    
                    ShatterFragment sf = new ShatterFragment(x,y,gbottom,tvertices,sp);//bottom
                    shatterFragmentList.addElement(sf);
                    tvertices = new Point[3];
                    tvertices[0] = vertices[1];
                    tvertices[1] = vertices[2];
                    tvertices[2] = vertices[4];
                    x = -halfsize+(i*increment)+(increment*.75f);//.75
                    y = -halfsize+(i2*increment)+(increment*.5f);
                    sp = new Point((x-ShatterPoint.x)/halfsize,(y-ShatterPoint.y)/halfsize);//Based on ShatterPoint and
                    
                    sf = new ShatterFragment(x,y,gright,tvertices,sp);//right
                    shatterFragmentList.addElement(sf);
                    tvertices = new Point[3];
                    tvertices[0] = vertices[2];
                    tvertices[1] = vertices[3];
                    tvertices[2] = vertices[4];
                    x = -halfsize+(i*increment)+(increment*.5f);
                    y = -halfsize+(i2*increment)+(increment*.75f);//.75
                    sp = new Point((x-ShatterPoint.x)/halfsize,(y-ShatterPoint.y)/halfsize);//Based on ShatterPoint and
                    
                    sf = new ShatterFragment(x,y,gtop,tvertices,sp);//top
                    shatterFragmentList.addElement(sf);
                    tvertices = new Point[3];
                    tvertices[0] = vertices[3];
                    tvertices[1] = vertices[0];
                    tvertices[2] = vertices[4];
                    x = -halfsize+(i*increment)+(increment*.25f);//.25
                    y = -halfsize+(i2*increment)+(increment*.5f);
                    sp = new Point((x-ShatterPoint.x)/halfsize,(y-ShatterPoint.y)/halfsize);//Based on ShatterPoint and
                    
                    sf = new ShatterFragment(x,y,gleft,tvertices,sp);//left
                    shatterFragmentList.addElement(sf);
              }
        }
  }
  
  //Function for finding the highest power of 2 that is lessthan or equal fold
  //Limitted to 512 since this should be safe on most current hardware.
  public static int get2Fold(int fold) {
        fold *= .5f;
        int ret = 2;
        while (ret < fold) {
              ret *= 2;
        }
        if (ret > 512) {
              ret = 512;
        }
        return ret;
  }
  
  //Just loops through the Vector shatterFragmentList and calls the display method
  //to display the fragment. No attempt is made to clean up the fragments as they move
  //out of view. shatterFragmentList.removeElementAt(i) can be used to remove an element
  //once it is out of view.
  public void display(GL gl,long timeDiff) {
        gl.glBindTexture(GL.GL_TEXTURE_2D,400);
        ShatterFragment sf;
        for (int i = shatterFragmentList.size()-1; i >= 0; i--) {
              sf = (ShatterFragment)shatterFragmentList.elementAt(i);
              sf.display(gl,timeDiff);
        }
  }

}

/* ShatterFragment.java */

import net.java.games.jogl.*;

public class ShatterFragment {
Point[] vertices;//points in this triangle
Point ShatterPoint;//vector indicating distance from shatter point
Point[] texCoords;//texture coordinates
float x;
float y;
float z;
float rotation;//Just serves as a multiplier.
float length = 0;

  public ShatterFragment(float x,float y,Point[] vertices,Point[] texCoords,Point ShatterPoint) {
        this.x = x;
        this.y = y;
        this.vertices = vertices;
        this.texCoords = texCoords;
        this.ShatterPoint = ShatterPoint;
        
        //Get the length of the ShatterPoint Point, this length will be used to determine
        //how fast movement and rotation occurs. ShatterImage insures that ShatterPoint contains
        //values between -1 and 1.0 so the maximum length is 1.41...
        if (ShatterPoint.x != 0) {
              this.length = (float)Math.sqrt((ShatterPoint.x*ShatterPoint.x)+(ShatterPoint.y*ShatterPoint.y));
        } else {
              if (ShatterPoint.y != 0) {
                    length = ShatterPoint.y;
              }
        }
        //We convert ShatterPoint to a unit vector, which will give the movement based on ShatterPoint
        //vector a rounded look
        toUnitVector(ShatterPoint);
  }
  
  public void toUnitVector(Point p) {
        float length = (float)Math.sqrt((p.x*p.x)+(p.y*p.y));
        if (p.x != 0) {
              p.x /= length;
        }
        if (p.y != 0) {
              p.y /= length;
        }
  }
  
  public void display(GL gl,long timediff) {
        //xMod and yMod just equal ShatterPoint, they serve no purpose other
        //than being shotter than typing the Object.x variable and I was too lazy
        //to remove them once they weren't necessary.
        float xMod=ShatterPoint.x;
        float yMod=ShatterPoint.y;
        
        //Update position of the x y and z variables.
        //We use 2.83f-length because we want things closest to the center to have
        //the highest movement and 1.41... is the highest length can be.
        //The result is squared to make the difference in speed more extreme.
        x += (2.83f-length)*(2.83f-length)*xMod*0.003*(float)timediff;
        y += (2.83f-length)*(2.83f-length)*yMod*0.003*(float)timediff;
        z += (2.83f-length)*(2.83f-length)*0.001*(float)timediff;
        //rotation just serves as a counter that is multiplied below.
        rotation += (float)timediff;
        
        gl.glPushMatrix();
        gl.glTranslatef(x,y,z);
        gl.glRotatef(rotation*(2.83f-length)*(2.83f-length)*0.1f,-xMod,yMod,0f);
        
        gl.glBegin(GL.GL_TRIANGLES);
        //front (The triangle built in ShatterImage)
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,0f);
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,0f);
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,0f);
        
        //back (The back and all sides are placed in to make the sharts look semi-realistic
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,-0.1f);
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,-0.1f);
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,-0.1f);
        
        //Sides, so that the triangle fragments appear to have 'width'
        //The texture coordinates are intentionally set so that they just have the edge color of
        //the front and back peices.
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,0f);
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,-0.1f);
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,-0.1f);
        
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,-0.1f);
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,0f);
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,0f);
        
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,0f);
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,-0.1f);
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,-0.1f);
        
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,-0.1f);
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,0f);
        gl.glTexCoord2f(texCoords[1].x, texCoords[1].y); gl.glVertex3f(vertices[1].x,vertices[1].y,0f);
        
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,0f);
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,-0.1f);
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,-0.1f);
        
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,-0.1f);
        gl.glTexCoord2f(texCoords[0].x, texCoords[0].y); gl.glVertex3f(vertices[0].x,vertices[0].y,0f);
        gl.glTexCoord2f(texCoords[2].x, texCoords[2].y); gl.glVertex3f(vertices[2].x,vertices[2].y,0f);
        gl.glEnd();
        
        gl.glPopMatrix();
  }

}

In retrospect I should have just zipped the files up and linked to the zip but I hate when I click on a link to a zip from a forum like this one and get a file not found…