SwingLWJGL JPanel Code Drop

Started by gregorypierce, January 23, 2006, 04:52:38

Previous topic - Next topic

gregorypierce

This is a code drop taken from the sojourner mobile code archives and as such I have only had a chance to strip it down to the point where it compiles and runs. Doing this to get it into LWJGL1.0. All SOJO asks for is a credit.


public class OpenGLJPanelTest extends JFrame
{
    public static Logger logger = Logger.getLogger("OpenGLJPanelTest");

    public OpenGLJPanelTest()
    {
        OpenGLTool panel = new OpenGLTool();

        getContentPane().add( panel );
        panel.setPreferredSize( new Dimension( 640, 480 ) );
    }

    public static final void main(String[] args )
    {
        BasicConfigurator.configure();

        try
        {
            OpenGLJPanelTest test = new OpenGLJPanelTest();

            test.pack();
            test.setVisible( true );

            test.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        }
        catch( Exception e )
        {
            logger.error(e);
        }

    }
}


import org.lwjgl.Sys;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.*;
import org.apache.log4j.Logger;
import org.jdom.Element;

import javax.swing.Timer;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import java.awt.Graphics;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ByteOrder;

import com.sojournermobile.studio.component.itempanel.ItemPanelWidgetToolIF;


public class OpenGLTool extends JPanel implements ActionListener, ItemPanelWidgetToolIF
{
    public static Logger logger = Logger.getLogger("OpenGLTool");

    static
    {
        Sys.initialize();
    }

    private Timer                       animationTimer = new Timer(10, this); // the timer that controls animation

    private Pbuffer                     pBuffer;  // the native opengl pBuffer that we render the scene into

    private BufferedImage               renderedImage;    // the image that will be rendered to the swing component
    private int[]                       imageData; // the int[] that represents the backing store of our BufferedImage

    private IntBuffer                   intBuffer; // the int buffer that will host the captured opengl data
    private boolean                     isInitialized = false;


    private int                         width; // stored width for our component
    private int                         height; // stored height for our component
    private int                         bitDepth = 32;
    private int                         refreshRate = 60;
    private int                         alphaBits;
    private int                         stencilBits;
    private int                         depthBufferBits;
    private int                         sampleBits;

    private float                       rotation = 0.1f;
    private int                         frameCount = 0;
    private long                        startTime;


    /**
     * Creates a JPanel that can be rendered to by LWJGL. It sets itself to opaque
     * by default so that Swing knows that we will render our entire bounds and therefore
     * doesn't need to do any checks
     */
    public OpenGLTool()
    {
        // this component is responsible for painting its entire bounds
        //
        setOpaque( true );
    }

    public int getBitDepth()
    {
        return bitDepth;
    }

    public void setBitDepth(int bitDepth)
    {
        this.bitDepth = bitDepth;
    }

    public int getAlphaBits()
    {
        return alphaBits;
    }

    public void setAlphaBits(int alphaBits)
    {
        this.alphaBits = alphaBits;
    }

    public int getStencilBits()
    {
        return stencilBits;
    }

    public void setStencilBits(int stencilBits)
    {
        this.stencilBits = stencilBits;
    }

    public int getDepthBufferBits()
    {
        return depthBufferBits;
    }

    public void setDepthBufferBits(int depthBufferBits)
    {
        this.depthBufferBits = depthBufferBits;
    }

    public int getSampleBits()
    {
        return sampleBits;
    }

    public void setSampleBits(int sampleBits)
    {
        this.sampleBits = sampleBits;
    }

    /**

     */
    public void initComponent( int bpp, int freq ) throws Exception
    {
        logger.debug("Creating headless window " + width +"/" + height + "/" + bitDepth);

        DisplayMode[] modes = Display.getAvailableDisplayModes();
        DisplayMode mode = null;
        
        // Make sure that we find the mode that uses our current monitor freq.

        for (int i = 0; i < modes.length; i++) 
        {
            if (modes[i].getWidth() == width && modes[i].getHeight() == height
                    && modes[i].getBitsPerPixel() == bpp
                    && (freq == 0 || modes[i].getFrequency() == freq)) 
            {
                mode = modes[i];
            }
        }
        
        PixelFormat format = new PixelFormat(bitDepth, alphaBits, depthBufferBits, stencilBits, sampleBits);

        try
        {
            Display.setDisplayMode(mode);

            // create a PbUffer that will store the contents of our renderings
            //
            pBuffer = new Pbuffer(width, height, format, null, null);
            pBuffer.makeCurrent();
        }
        catch( Exception e )
        {
            logger.error("Unable to create headless display", e );
        }
    }

    /**
     * override update to avoid clearing
     */
    public void update(Graphics g)
    {
        paint(g);
    }

    public void start()
    {
        logger.debug("Starting animation timer");
        animationTimer.start();
        startTime = System.currentTimeMillis();
    }

    public void stop()
    {
        logger.debug("Stopping animation timer");
        animationTimer.stop();
    }

    /**
     * Instruct the component to paint its entire bounds
     *
     * @param e
     */
    public void actionPerformed(ActionEvent e)
    {
        paintImmediately(0, 0, getWidth(), getHeight());
    }

    public void paintComponent( Graphics g )
    {
        if ( ! isInitialized )
        {
            width = getWidth();
            height = getHeight();

            try
            {
                initComponent( bitDepth, refreshRate );
            }
            catch (Exception e)
            {
                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
            }

            // create a buffered image that is of the size of this component
            // we will render directly into this buffered image in Swing
            // fashion so we can render our interface in Swing properly and get the
            // image data that is backing this buffered image so we can write directly
            // to the internal data structure with no unnecessary copy
            //
            renderedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            imageData = ((DataBufferInt)renderedImage.getRaster().getDataBuffer()).getData();

            // create an int buffer to store the captured data from OpenGL
            //
            intBuffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();

            // we're initialized
            //
            isInitialized = true;

            //start the animation thread
            //
            start();
        }
        else
        {
            // do render in opengl
            //
            render();

            // make sure we're done drawing before capturing the frame
            //
            GL11.glFlush();

            // capture the result
            //
            grabGLFrame();

            // draw the result to this component
            //
            g.drawImage(renderedImage, 0, 0, null);

        }

        frameCount ++;

        if ( frameCount % 100 == 0 )
        {
            long currentTime = System.currentTimeMillis();
            long secondTime = ( currentTime - startTime ) / 1000;

            float frameRate = frameCount / secondTime;

            logger.debug("Another 100 frames " + frameRate );

        }
    }


    /**
     * Captures the currently rendered frame to a buffered image so it can be rendered
     * to a Swing component without any repaint issues. Most assuredly will be slower
     * than direct rendering, but will be compliant with Swing
     */
    private void grabGLFrame()
    {
        GL11.glReadPixels(0, 0, width, height, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, intBuffer);

        intBuffer.clear();

        // dump the captured data into the buffered image
        //
        for (int x = height; --x >= 0; )
        {
           intBuffer.get(imageData, x * width, width);
        }

        intBuffer.flip();
    }

        /**
     * <code>getValidDisplayMode</code> returns a <code>DisplayMode</code>
     * object that has the requested width, height and color depth. If there is
     * no mode that supports a requested resolution, null is returned.
     */
    private DisplayMode getValidDisplayMode(int width, int height, int bpp, int freq)
    {
        // get all the modes, and find one that matches our width, height, bpp.
        DisplayMode[] modes;
        try
        {
            modes = Display.getAvailableDisplayModes();
        } catch (LWJGLException e) {
            e.printStackTrace();
            return null;
        }
        // Make sure that we find the mode that uses our current monitor freq.

        for (int i = 0; i < modes.length; i++) {
            if (modes[i].getWidth() == width && modes[i].getHeight() == height
                    && modes[i].getBitsPerPixel() == bpp
                    && (freq == 0 || modes[i].getFrequency() == freq)) {

                return modes[i];
            }
        }

        // none found
        return null;
    }

    private void render()
    {
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

        GL11.glRotatef(rotation,0.0f,1.0f,0.0f);				// Rotate The Triangle On The Y axis ( NEW )
        GL11.glBegin(GL11.GL_TRIANGLES);						// Drawing Using Triangles
        GL11.glVertex3f( 0.0f, 1.0f, 0.0f);				// Top
        GL11.glVertex3f(-1.0f,-1.0f, 0.0f);				// Bottom Left
        GL11.glVertex3f( 1.0f,-1.0f, 0.0f);				// Bottom Right
        GL11.glEnd();

        rotation += 0.2f;
    }

    public void setData(Element metadata) throws Exception
    {
        //To change body of implemented methods use File | Settings | File Templates.
    }
}
[/code]