How to Hardware Mode

Started by paulscode, February 24, 2008, 09:04:22 PM

Previous topic - Next topic

EgonOlsen

#15
Why should it print out "dispose" if you are actually trying to enable it? I'm not sure about the order in which you enable/disable things, but the software enabling path is definitely missing a disabling of the AWTGLRenderer (just disable it like the "normal" GLRenderer). Maybe that helps...

raft

Quote from: EgonOlsen on February 26, 2008, 05:06:37 PM
The AWTGLRenderer doesn't really draw anything when you are calling draw(), but it enqueues the calls to be executed in the AWT event thread dispatch instead (i.e. in the AWTGLCanvas' paint()-method). Your code works, you just don't get any repaint events for the applet, which is why nothing moves. Maybe it's not possible in all cases to schedule a repaint() from inside paint()...i'm not sure. However, i've modified your applet slightly to start an additional thread that just makes the applet repaint itself, i.e. execute your drawing code.
Personally, i wouldn't do it that way, but make my own thread that does the calculations and leave the applets paint() alone. That way, you'll benefit from a feature of the AWTGLRenderer: It runs multithreaded, i.e. calculations in one thread, drawing in another (in the AWT event dispatch thread in this case). In your code, both operations happen in the AWT EDT. I haven't modified your code to work that way (i.e. move the code from paint into that thread's loop), because it requires some more work: Your mouse listeners are modifying jPCT objects directly, but jPCT isn't thread save, i.e. you shouldn't modify a jPCT object from another thread than the rendering thread. Instead, the listeners should just flag what they want to do and let the calculation thread execute the operations. Anyway, here's the modified example:

i had faced same repaint and multi-thread issue. one point you havent possibly faced yet is, repaint thing works well for simple scenes (where cpu is relatively idle) but begins to behave strange for larger scenes. it skips some frames etc. awt system may skip some repaint requests. indeed this way is almost identical to using a timer. have a look at http://www.jpct.net/forum2/index.php/topic,282.0.html thread for an early discussion of this.

for multi-threading: i also use swing/awt components. instead of passing data from awt thread to rendering thread, i decided to do all my work in awt thread. so my game loop is something like:

while (loop) {
   SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
            updateGame();
            world.renderScene(frameBuffer);
            world.draw(frameBuffer);           
            canvas.paint(someGraphics);   // for awt Canvas
            // component.paintImmediately(component.getBounds()); // this is better for Swing panels
        }
    });   
}


hope this helps,
r a f t

paulscode

Ah, yes, that was a bad oversight on my part.  Adding that disable of the GL renderer did correct the out-of-place "dispose" message I was getting.

But now I have a another problem:

When switching from Hardware to Software, everything is fine.  Java Console shows:

OpenGL (AWTGLCanvas) renderer disposed
Software renderer (OpenGL mode) initialized


But from Software to Hardware I just get:

Software renderer disposed


Basically, it is not initializing the AWTGLRenderer - The Java Console should say something like:

Software renderer disposed
Current mode: (blah, blah)
Driver is: (blah, blah)
FBO supported and used!
OpenGL renderer initialized (using 4 texture stages)


But here is the strange thing:  Mode switching works if I make the following change:

    buffer.enableRenderer( IRenderer.RENDERER_OPENGL );
    // instead of:  myCanvas = buffer.enableGLCanvasRenderer();


Unfortunately, this runs the applet in a poppup window instead of on the canvas.  Noooo!...  :D

Here is that mode-switching code the way it looks now, in case you can see what I am doing wrong:

if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
{
        // Switch to hardware mode
        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
        myCanvas = buffer.enableGLCanvasRenderer();
        add( myCanvas, BorderLayout.CENTER);
        myCanvas.setVisible( true );
        validate();
        // get mouse input picked up by the canvas:
        myCanvas.addMouseListener( this );
        myCanvas.addMouseMotionListener( this );
}
else
{
        // Switch to software mode
        buffer.disableRenderer( IRenderer.RENDERER_OPENGL );
        buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE );
        removeAll();
        validate();
}

paulscode

Quote from: raft on February 27, 2008, 06:46:34 PM
for multi-threading: i also use swing/awt components. instead of passing data from awt thread to rendering thread, i decided to do all my work in awt thread. so my game loop is something like:

while (loop) {
   SwingUtilities.invokeAndWait(new Runnable() {
        public void run() {
            updateGame();
            world.renderScene(frameBuffer);
            world.draw(frameBuffer);           
            canvas.paint(someGraphics);   // for awt Canvas
            // component.paintImmediately(component.getBounds()); // this is better for Swing panels
        }
    });   
}


hope this helps,
r a f t

Thanks for the post, raft.  This is one thing I want to get a better understanding of.  I hate sounding like such a noob, but I have had zero experience using multiple threads.  Is there any chance I could get you to post the code for an entire JApplet that uses your above loop? (something simple - doesn't have to do anything, just initialize a world and get into the game loop.)  Thanks for your advice on this topic!

raft

sure, here it is: http://www.aptalkarga.com/download/SwApplet.java

the loop part is almost the same as i use in karga. this way you are free to do any jPCT things in awt thread but only in awt thread. if in any other thread you need to do such things, just wrap them in a:

SwingUtilities.invokeAndWait(new Runnable() {
    public void run() {
        // do jPCT things here
    }
});


the granularity and aggregatedTime things are very similar to Egon's tick timer in jPCT examples. my sample limits fps to 1000/granularity, if you dont want to limit fps, dont Thread.sleep after scene is rendered.

hope this helps,
r a f t

paulscode

Very awesome, raft.  Thanks!

paulscode

I still can't seem to figure out how to dynamically switch from software mode back to hardware mode by using

enableGLCanvasRenderer();

instead of

enableRenderer( IRenderer.RENDERER_OPENGL );

Which renders onto that darn pop-up instead of the applet frame.  When I try to use enableGLCanvasRender, it causes the applet to no longer refresh (and the JAVA Console doesn't show it initializing the AWTGLRenderer).  Does anyone have any ideas about what I need to change?  Here is the complete code for my applet, as it looks now:


import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.io.InputStream;

import javax.swing.JApplet;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Lights;
import com.threed.jpct.Loader;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

import java.lang.System;

public class jPCTGears extends JApplet implements MouseListener, MouseMotionListener, Runnable
{
    private Object3D redGear, greenGear, blueGear, assemblyPivot;
    private FrameBuffer buffer = null;
    private World world = null;
    private Camera camera = null;
    private int width = 640;
    private int height = 480;
    private float gear_rotation = 0.02f;
    private int prevMouseX, prevMouseY;
    private boolean loop=true;
   
    Canvas myCanvas;
   
    // Initialize all components of the applet
    @Override
    public void init()
    {
        // sign the applet up to receive mouse messages:
        world = new World();  // create a new world
       
        World.setDefaultThread( Thread.currentThread() );

        // create a new buffer to draw on:
        buffer = new FrameBuffer( width, height, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY );
        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
       
        myCanvas = buffer.enableGLCanvasRenderer();
        add( myCanvas, BorderLayout.CENTER);
        myCanvas.setVisible( true );
       
        // load some 3D objects and make sure they have the correct orientation:
        redGear = loadMeshFile( "RedGear.3ds" );
        redGear.rotateY( (float)Math.PI / 2.0f );
        redGear.rotateMesh();
        redGear.setRotationMatrix( new Matrix() );
        redGear.setOrigin( new SimpleVector( 0, 0, 0 ) );
        redGear.build();
        greenGear = loadMeshFile( "GreenGear.3ds" );
        greenGear.rotateY( (float)Math.PI / 2.0f );
        greenGear.rotateZ( 0.35f );
        greenGear.rotateMesh();
        greenGear.setRotationMatrix( new Matrix() );
        greenGear.setOrigin( new SimpleVector( -145.0f, 0, 0 ) );
        greenGear.build();
        blueGear = loadMeshFile( "BlueGear.3ds" );
        blueGear.rotateY( (float)Math.PI / 2.0f );
        //blueGear.rotateZ( 0.40f );
        blueGear.rotateMesh();
        blueGear.setRotationMatrix( new Matrix() );
        blueGear.setOrigin( new SimpleVector( 0, -140.0f, 0 ) );
        blueGear.build();

        // Set up a pivot point for the entire gear assembly:
        assemblyPivot = Object3D.createDummyObj();
        assemblyPivot.setOrigin( new SimpleVector( 0, 0, 0 ) );
        // Make the gears children to assemblyPivot.
        // Translations and rotations to assemblyPivot
        // will affect the entire gear assembly:
        assemblyPivot.addChild( redGear );
        assemblyPivot.addChild( greenGear );
        assemblyPivot.addChild( blueGear );
               
        // add the objects our world:
        world.addObject( redGear );
        world.addObject( greenGear );
        world.addObject( blueGear );
        world.buildAllObjects();
       
        lookAt( redGear );  // make sure the camera is facing towards the object
        letThereBeLight();  // create light sources for the scene
       
        // receive mouse input from the main applet:
        addMouseListener( this );
        addMouseMotionListener( this );

        // also get mouse input picked up by the canvas:
        myCanvas.addMouseListener( this );
        myCanvas.addMouseMotionListener( this );
       
        new Thread(this).start();
    }

    // Draw the scene
    @Override
    public void paint( Graphics g )
    {
    redGear.rotateAxis( redGear.getZAxis(), -gear_rotation );
        greenGear.rotateAxis( greenGear.getZAxis(), 2.0f * gear_rotation );
        blueGear.rotateAxis( blueGear.getZAxis(), 2.0f * gear_rotation );
       
        buffer.clear();   // erase the previous frame

        // render the world onto the buffer:
        world.renderScene( buffer );
        world.draw( buffer );
        buffer.update();
       
        if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
        {
            buffer.display( g, 0, 0);  // Paint this frame onto the applet
        }
        else
        {
            buffer.displayGLOnly();
            myCanvas.repaint();    // Paint the canvas onto the applet
        }
    }
   
    @Override
    public void destroy()
    {
    loop=false;
    }
   
    @Override
    public void run()
    {
        while (loop)
        {
            this.repaint();
            try
            {
                Thread.sleep(10);
            }
            catch(Exception e)
            {
                //Don't care...
            }
        }
    }

    // Load a 3Ds file, and return its Object3D handle
    public Object3D loadMeshFile( String filename )
    {
        Object3D newObject;
        //Object3D[] objs = Loader.load3DS( myURL, "models" + "/" + filename, 1.0f );
        Object3D[] objs = Loader.load3DS( getResource( filename ), 1.0f );

        if( objs.length==1 )
        {
            // The object loaded fine, just need to initialize it
            newObject=objs[0];
            newObject.setCulling( Object3D.CULLING_DISABLED );
            newObject.build();
        }
        else
        {
            // Didn't load anything, or loaded
            //     more than 1 object (not supposed to happen)
            System.out.println( "Unknown file format: " + filename );
            newObject = null;
        }

        return newObject;
    }
   
    private InputStream getResource( String resourceName )
    {
        return getClass().getClassLoader().getResourceAsStream( resourceName );
    }
    // point the camera toward the given object
    private void lookAt( Object3D obj )
    {
        camera = world.getCamera();  // grab a handle to the camera
        camera.setPosition( 0, 0, 500 );  // set its *relative* position
        camera.lookAt( obj.getTransformedCenter() );  // look toward the object
    }

    // create light sources for the scene
    private void letThereBeLight()
    {
        world.getLights().setOverbrightLighting (
            Lights.OVERBRIGHT_LIGHTING_DISABLED );
        world.getLights().setRGBScale( Lights.RGB_SCALE_2X );

        // Set the overall brightness of the world:
        world.setAmbientLight( 50, 50, 50 );
//        world.setAmbientLight( 150, 150, 150 );

        // Create a main light-source:
        world.addLight( new SimpleVector( 50, -50, 300 ), 20, 20, 20 );
    }
   
    // Dragging the mouse should rotate the entire gear assembly
    public void mouseDragged( MouseEvent e )
    {
        // get the mouse's coordinates:
        int x = e.getX();
        int y = e.getY();
        Dimension size = e.getComponent().getSize();

        // Calculate the angles to rotate the assembly:
        float thetaY = (float)Math.PI * ( (float)(x-prevMouseX)/(float)size.width );
        float thetaX = (float)Math.PI * ( (float)(prevMouseY-y)/(float)size.height );
       
        // Apply the rotations to the gear assembly:
        assemblyPivot.rotateAxis( assemblyPivot.getXAxis(), thetaX );
        assemblyPivot.rotateAxis( assemblyPivot.getYAxis(), thetaY );

        // Keep track of the mouse location:
        prevMouseX = x;
        prevMouseY = y;
    }
    public void mousePressed( MouseEvent e )
    {
        // Start keeping track of the mouse location now.
        // This prevent the gear assembly from jerking to the new angle
        // whenever mouseDragged first gets called:
        prevMouseX = e.getX();
        prevMouseY = e.getY();
    }
    public void mouseReleased( MouseEvent e ){}
    public void mouseEntered( MouseEvent e ) {}
    public void mouseExited( MouseEvent e ) {}
    public void mouseClicked( MouseEvent e )
    {
        if( (e.getModifiers() & e.BUTTON3_MASK) != 0 )
        {
            if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
            {
                // Switch to hardware mode
                buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
                //     Java Console does not show AWTGLCanvas render initializing:
                myCanvas = buffer.enableGLCanvasRenderer();
                //     The following causes to render in a pop-up window:
                //     buffer.enableRenderer( IRenderer.RENDERER_OPENGL );
                add( myCanvas, BorderLayout.CENTER);
                myCanvas.setVisible( true );
                validate();
                // get mouse input picked up by the canvas:
                myCanvas.addMouseListener( this );
                myCanvas.addMouseMotionListener( this );
            }
            else
            {
                // Switch to software mode
                buffer.disableRenderer( IRenderer.RENDERER_OPENGL );
                buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE );
                removeAll();
                validate();
            }
        }
    }
    public void mouseMoved( MouseEvent e ) {}
}

EgonOlsen

The paintGL-method of jPCT AWTGLRenderer is obviously never called, so no initialization happens, therefore no message appears. However, the rest of your program is still running. I'm not sure what the reason for this is. To me, it looks like as if the canvas isn't really added to the applet or it remains invisible or something. It could also be a bug in jPCT, but i can't find one ATM. Have to tried this outside an applet in a normal application? I'll try to investigate this further tomorrow.

paulscode

Ah, that's a good idea.  I will try it out in an application and see if it works there.  I'll also do a little more research on dynamically adding canvases, validate(), etc. to see if maybe I am doing something wrong on that part.  Yes, you are right - it definitely does look like it would if the canvas were either not added or not visible.  Thanks!

EgonOlsen

Quote from: paulscode on February 29, 2008, 02:42:29 AM
I will try it out in an application and see if it works there.
Please do that. I can't find anything wrong on my side...that doesn't mean that there is nothing, but if it works in an application, it's most likely an applet problem. If it doesn't work there either, we have an easier to debug test case.

paulscode

#25
Ok, I tried it in an application, and am having the same problem - no painting when switching back to hardware mode.  Here is the code I am using for the application:


import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.event.KeyEvent;

import java.io.InputStream;

import javax.swing.JFrame;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Lights;
import com.threed.jpct.Loader;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;
import com.threed.jpct.util.KeyMapper;
import com.threed.jpct.util.KeyState;

import java.lang.System;

public class jPCTGearsApplication extends JFrame
{
    private int width = 640;
    private int height = 480;
    private int titleBarHeight;
    private int leftBorderWidth;
   
    private Object3D redGear, greenGear, blueGear, assemblyPivot;
    private float gear_rotation = 0.02f;
    private boolean rotateLeft = false;
    private boolean rotateRight = false;
   
    private FrameBuffer buffer = null;
    private World world = null;
    private Camera camera = null;
    private KeyMapper keyMapper;
    private Graphics g = null;
    private Canvas myCanvas;
       
    private boolean exit = false;   

    public static void main( String[] args )
    {
        jPCTGearsApplication h = new jPCTGearsApplication();
    }

    // Draw the scene
    @Override
    public void paint( Graphics g )
    {
    redGear.rotateAxis( redGear.getZAxis(), -gear_rotation );
        greenGear.rotateAxis( greenGear.getZAxis(), 2.0f * gear_rotation );
        blueGear.rotateAxis( blueGear.getZAxis(), 2.0f * gear_rotation );
       
        buffer.clear();   // erase the previous frame

        // render the world onto the buffer:
        world.renderScene( buffer );
        world.draw( buffer );
        buffer.update();
       
        if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
        {
            buffer.display( g, leftBorderWidth, titleBarHeight );  // Paint this frame
        }
        else
        {
            buffer.displayGLOnly();
            myCanvas.repaint();    // Paint the canvas
        }
    }

    private void mainLoop()
    {
        while( !exit )
        {
            poll();
            rotateAssembly();
            this.repaint();
            try
            {
                Thread.sleep( 10 );
            }
            catch( Exception e ){}  //Don't care...
        }
        System.exit(0);
    }

    public jPCTGearsApplication()
    {
        initFrame();
        init3D();
        mainLoop();
    }

    private void initFrame()
    {
        setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        setTitle( "Gears Demo in jPCT!" );
        pack();
        Insets insets = getInsets();
        titleBarHeight = insets.top;
        leftBorderWidth = insets.left;
        setSize( width + leftBorderWidth + insets.right, height + titleBarHeight
            + insets.bottom );
        setResizable( false );
        setLocationRelativeTo( null );
        setVisible( true );
        g = getGraphics();
    }

    private void init3D()
    {
        world = new World();  // create a new world
       
        World.setDefaultThread( Thread.currentThread() );

        // create a new buffer to draw on:
        buffer = new FrameBuffer( width, height, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY );
        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
       
        myCanvas = buffer.enableGLCanvasRenderer();
        add( myCanvas, BorderLayout.CENTER);
        myCanvas.setVisible( true );
        keyMapper = new KeyMapper( myCanvas );
       
        // load some 3D objects and make sure they have the correct orientation:
        redGear = loadMeshFile( "RedGear.3ds" );
        redGear.rotateY( (float)Math.PI / 2.0f );
        redGear.rotateMesh();
        redGear.setRotationMatrix( new Matrix() );
        redGear.setOrigin( new SimpleVector( 0, 0, 0 ) );
        redGear.build();
        greenGear = loadMeshFile( "GreenGear.3ds" );
        greenGear.rotateY( (float)Math.PI / 2.0f );
        greenGear.rotateZ( 0.35f );
        greenGear.rotateMesh();
        greenGear.setRotationMatrix( new Matrix() );
        greenGear.setOrigin( new SimpleVector( -145.0f, 0, 0 ) );
        greenGear.build();
        blueGear = loadMeshFile( "BlueGear.3ds" );
        blueGear.rotateY( (float)Math.PI / 2.0f );
        //blueGear.rotateZ( 0.40f );
        blueGear.rotateMesh();
        blueGear.setRotationMatrix( new Matrix() );
        blueGear.setOrigin( new SimpleVector( 0, -140.0f, 0 ) );
        blueGear.build();

        // Set up a pivot point for the entire gear assembly:
        assemblyPivot = Object3D.createDummyObj();
        assemblyPivot.setOrigin( new SimpleVector( 0, 0, 0 ) );
        // Make the gears children to assemblyPivot.
        // Translations and rotations to assemblyPivot
        // will affect the entire gear assembly:
        assemblyPivot.addChild( redGear );
        assemblyPivot.addChild( greenGear );
        assemblyPivot.addChild( blueGear );
               
        // add the objects our world:
        world.addObject( redGear );
        world.addObject( greenGear );
        world.addObject( blueGear );
        world.buildAllObjects();
       
        lookAt( redGear );  // make sure the camera is facing towards the object
        letThereBeLight();  // create light sources for the scene               
    }

    // Load a 3Ds file, and return its Object3D handle
    public Object3D loadMeshFile( String filename )
    {
        Object3D newObject;
        //Object3D[] objs = Loader.load3DS( myURL, "models" + "/" + filename, 1.0f );
        Object3D[] objs = Loader.load3DS( getResource( filename ), 1.0f );

        if( objs.length==1 )
        {
            // The object loaded fine, just need to initialize it
            newObject=objs[0];
            newObject.setCulling( Object3D.CULLING_DISABLED );
            newObject.build();
        }
        else
        {
            // Didn't load anything, or loaded
            //     more than 1 object (not supposed to happen)
            System.out.println( "Unknown file format: " + filename );
            newObject = null;
        }

        return newObject;
    }
   
    private InputStream getResource( String resourceName )
    {
        return getClass().getClassLoader().getResourceAsStream( resourceName );
    }
    // point the camera toward the given object
    private void lookAt( Object3D obj )
    {
        camera = world.getCamera();  // grab a handle to the camera
        camera.setPosition( 0, 0, 500 );  // set its *relative* position
        camera.lookAt( obj.getTransformedCenter() );  // look toward the object
    }

    // create light sources for the scene
    private void letThereBeLight()
    {
        world.getLights().setOverbrightLighting (
            Lights.OVERBRIGHT_LIGHTING_DISABLED );
        world.getLights().setRGBScale( Lights.RGB_SCALE_2X );

        // Set the overall brightness of the world:
        world.setAmbientLight( 50, 50, 50 );

        // Create a main light-source:
        world.addLight( new SimpleVector( 50, -50, 300 ), 20, 20, 20 );
    }
   
    private void rotateAssembly()
    {
        float thetaX = 0;
        float thetaY = 0;
        float speed = 0.02f;
       
        if( rotateLeft )
        {
            thetaX = speed;
            thetaY = speed;
        }
        if( rotateRight )
        {
            thetaX = -speed;
            thetaY = -speed;
        }
        // Apply the rotations to the gear assembly:
        assemblyPivot.rotateAxis( assemblyPivot.getXAxis(), thetaX );
        assemblyPivot.rotateAxis( assemblyPivot.getYAxis(), thetaY );
    }

    // Use the KeyMapper to poll the keyboard
    private void poll()
    {
        KeyState state = null;
        do
        {
            state = keyMapper.poll();
            if( state != KeyState.NONE )
            {
                keyAffected( state );
            }
        } while( state != KeyState.NONE );
    }

    private void keyAffected( KeyState state )
    {
        int code = state.getKeyCode();
        boolean event = state.getState();

        switch( code )
        {
            case( KeyEvent.VK_ESCAPE ):
            {
                exit = event;
                break;
            }
            case( KeyEvent.VK_LEFT ):
            {
                rotateLeft = !rotateLeft;
                break;
            }
            case( KeyEvent.VK_RIGHT ):
            {
                rotateRight = !rotateRight;
                break;
            }           
            case( KeyEvent.VK_SPACE ):
            {
                if( event )
                {
                    if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
                    {
                        // Switch to hardware mode
                        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
                        myCanvas = buffer.enableGLCanvasRenderer();
                        add( myCanvas, BorderLayout.CENTER);
                        validate();
                        myCanvas.setVisible( true );
                        // get keyboard input from the canvas:
                        keyMapper = new KeyMapper( myCanvas );
                    }
                    else
                    {
                        // Switch to software mode
                        buffer.disableRenderer( IRenderer.RENDERER_OPENGL );
                        buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE );
                        removeAll();
                        validate();
                        // get keyboard input from the "this"
                        keyMapper = new KeyMapper( this );
                    }
                }
                break;
            }           
        }
    }
}


As you can see, in this case, I am using keyboard inputs instead of mouse input, and only listening to keyboard input from either the canvas (when in hardware mode) or the main JFrame (when in software mode).  The interesting thing about that is: whenever the application tries to switch back to hardware mode and has the problem, it stops getting keyboard inputs.  That is exactly what I would expect in a case where the canvas were either not added or not visible.  That's a little more confirmation of what we suspected earlier.

[UPDATE]
I have been doing some various tests, and found that adding the following into paint():

myCanvas.getGraphics().fillRoundRect( 0, 0, 100, 100, 10, 10 );

Causes a "null pointer exception".  I thought this might be important.

[UPDATE #2]
Important breakthrough:  In both places in my code, I tried replacing:

myCanvas = buffer.enableGLCanvasRenderer();

with:

buffer.enableGLCanvasRenderer();
myCanvas = new Canvas();       

And I'm drawing a rectangle in paint().  The canvas is drawing the rectangle fine the first time, but when I right-click (remove the canvas), then right-click again (add a new canvas), it does not refresh.  Then, when paint() tries to access myCanvas.getGraphics(), it throws the "null pointer exception".

This means there is not a bug with jPCT.  As you suspected, I am somehow adding the canvas incorrectly.  I just can't seem to figure out what I am doing wrong, lol.  I'll try digging around on some different forums to see if anyone else has had a similar problem dynamically adding and deleting canvases before.  I really appreciate the help!

paulscode

YES!  I figured it out!

I just have to use:

this.remove( myCanvas );


instead of:

this.removeAll();


Apparently removeAll() prevents you from ever adding anything to the applet again later.  Switching between hardware mode and software mode works beautifully!

Here is the code for the working applet, for anyone else who wants to do something similar.  I think being able to dynamically switch between software and hardware mode could be a really useful feature:

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import java.io.InputStream;

import javax.swing.JApplet;

import com.threed.jpct.Camera;
import com.threed.jpct.FrameBuffer;
import com.threed.jpct.IRenderer;
import com.threed.jpct.Lights;
import com.threed.jpct.Loader;
import com.threed.jpct.Matrix;
import com.threed.jpct.Object3D;
import com.threed.jpct.SimpleVector;
import com.threed.jpct.World;

import java.lang.System;

public class jPCTGears extends JApplet implements MouseListener, MouseMotionListener, Runnable
{
    private Object3D redGear, greenGear, blueGear, assemblyPivot;
    private FrameBuffer buffer = null;
    private World world = null;
    private Camera camera = null;
    private int width = 640;
    private int height = 480;
    private float gear_rotation = 0.02f;
    private int prevMouseX, prevMouseY;
    private boolean loop=true;
   
    Canvas myCanvas;
   
    // Initialize all components of the applet
    @Override
    public void init()
    {
        // sign the applet up to receive mouse messages:
        world = new World();  // create a new world
       
        World.setDefaultThread( Thread.currentThread() );

        // create a new buffer to draw on:
        buffer = new FrameBuffer( width, height, FrameBuffer.SAMPLINGMODE_HARDWARE_ONLY );
        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
       
        myCanvas = buffer.enableGLCanvasRenderer();
       
        add( myCanvas, BorderLayout.CENTER);
        myCanvas.setVisible( true );
       
        // load some 3D objects and make sure they have the correct orientation:
        redGear = loadMeshFile( "RedGear.3ds" );
        redGear.rotateY( (float)Math.PI / 2.0f );
        redGear.rotateMesh();
        redGear.setRotationMatrix( new Matrix() );
        redGear.setOrigin( new SimpleVector( 0, 0, 0 ) );
        redGear.build();
        greenGear = loadMeshFile( "GreenGear.3ds" );
        greenGear.rotateY( (float)Math.PI / 2.0f );
        greenGear.rotateZ( 0.35f );
        greenGear.rotateMesh();
        greenGear.setRotationMatrix( new Matrix() );
        greenGear.setOrigin( new SimpleVector( -145.0f, 0, 0 ) );
        greenGear.build();
        blueGear = loadMeshFile( "BlueGear.3ds" );
        blueGear.rotateY( (float)Math.PI / 2.0f );
        //blueGear.rotateZ( 0.40f );
        blueGear.rotateMesh();
        blueGear.setRotationMatrix( new Matrix() );
        blueGear.setOrigin( new SimpleVector( 0, -140.0f, 0 ) );
        blueGear.build();

        // Set up a pivot point for the entire gear assembly:
        assemblyPivot = Object3D.createDummyObj();
        assemblyPivot.setOrigin( new SimpleVector( 0, 0, 0 ) );
        // Make the gears children to assemblyPivot.
        // Translations and rotations to assemblyPivot
        // will affect the entire gear assembly:
        assemblyPivot.addChild( redGear );
        assemblyPivot.addChild( greenGear );
        assemblyPivot.addChild( blueGear );
               
        // add the objects our world:
        world.addObject( redGear );
        world.addObject( greenGear );
        world.addObject( blueGear );
        world.buildAllObjects();
       
        lookAt( redGear );  // make sure the camera is facing towards the object
        letThereBeLight();  // create light sources for the scene
       
        // receive mouse input from the main applet:
        addMouseListener( this );
        addMouseMotionListener( this );

        // also get mouse input picked up by the canvas:
        myCanvas.addMouseListener( this );
        myCanvas.addMouseMotionListener( this );
       
        new Thread(this).start();
    }

    // Draw the scene
    @Override
    public void paint( Graphics g )
    {
    redGear.rotateAxis( redGear.getZAxis(), -gear_rotation );
        greenGear.rotateAxis( greenGear.getZAxis(), 2.0f * gear_rotation );
        blueGear.rotateAxis( blueGear.getZAxis(), 2.0f * gear_rotation );
       
        buffer.clear();   // erase the previous frame

        // render the world onto the buffer:
        world.renderScene( buffer );
        world.draw( buffer );
        buffer.update();
       
        if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
        {
            buffer.display( g, 0, 0);  // Paint this frame onto the applet (software mode)
        }
        else
        {
            buffer.displayGLOnly();
            myCanvas.repaint();    // Paint the canvas onto the applet (hardware mode)
        }
    }
   
    @Override
    public void destroy()
    {
    loop=false;
    }
   
    @Override
    public void run()
    {
        while (loop)
        {
            this.repaint();
            try
            {
                Thread.sleep(10);
            }
            catch(Exception e)
            {
                //Don't care...
            }
        }
    }

    // Load a 3Ds file, and return its Object3D handle
    public Object3D loadMeshFile( String filename )
    {
        Object3D newObject;
        //Object3D[] objs = Loader.load3DS( myURL, "models" + "/" + filename, 1.0f );
        Object3D[] objs = Loader.load3DS( getResource( filename ), 1.0f );

        if( objs.length==1 )
        {
            // The object loaded fine, just need to initialize it
            newObject=objs[0];
            newObject.setCulling( Object3D.CULLING_DISABLED );
            newObject.build();
        }
        else
        {
            // Didn't load anything, or loaded
            //     more than 1 object (not supposed to happen)
            System.out.println( "Unknown file format: " + filename );
            newObject = null;
        }

        return newObject;
    }
   
    private InputStream getResource( String resourceName )
    {
        return getClass().getClassLoader().getResourceAsStream( resourceName );
    }
    // point the camera toward the given object
    private void lookAt( Object3D obj )
    {
        camera = world.getCamera();  // grab a handle to the camera
        camera.setPosition( 0, 0, 500 );  // set its *relative* position
        camera.lookAt( obj.getTransformedCenter() );  // look toward the object
    }

    // create light sources for the scene
    private void letThereBeLight()
    {
        world.getLights().setOverbrightLighting (
            Lights.OVERBRIGHT_LIGHTING_DISABLED );
        world.getLights().setRGBScale( Lights.RGB_SCALE_2X );

        // Set the overall brightness of the world:
        world.setAmbientLight( 50, 50, 50 );

        // Create a main light-source:
        world.addLight( new SimpleVector( 50, -50, 300 ), 20, 20, 20 );
    }
   
    public void mouseEntered( MouseEvent e ) {}
    public void mouseExited( MouseEvent e ) {}
    public void mouseMoved( MouseEvent e ) {}
    public void mouseReleased( MouseEvent e ){}
    public void mouseClicked( MouseEvent e )
    {
        if( (e.getModifiers() & e.BUTTON3_MASK) != 0 )
        {
            if( buffer.usesRenderer( IRenderer.RENDERER_SOFTWARE ) )
            {
                // Switch to hardware mode
                buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );
                myCanvas = buffer.enableGLCanvasRenderer();
               
                this.add( myCanvas, BorderLayout.CENTER);
                this.validate();
                myCanvas.setVisible( true );
                myCanvas.validate();
                // get mouse input picked up by the canvas:
                myCanvas.addMouseListener( this );
                myCanvas.addMouseMotionListener( this );
            }
            else
            {
                // Switch to software mode
                buffer.disableRenderer( IRenderer.RENDERER_OPENGL );
                buffer.enableRenderer(IRenderer.RENDERER_SOFTWARE );
                this.remove( myCanvas );
                this.validate();
            }
        }
    }
    // Dragging the mouse should rotate the entire gear assembly
    public void mouseDragged( MouseEvent e )
    {
        // get the mouse's coordinates:
        int x = e.getX();
        int y = e.getY();
        Dimension size = e.getComponent().getSize();

        // Calculate the angles to rotate the assembly:
        float thetaY = (float)Math.PI * ( (float)(x-prevMouseX)/(float)size.width );
        float thetaX = (float)Math.PI * ( (float)(prevMouseY-y)/(float)size.height );
       
        // Apply the rotations to the gear assembly:
        assemblyPivot.rotateAxis( assemblyPivot.getXAxis(), thetaX );
        assemblyPivot.rotateAxis( assemblyPivot.getYAxis(), thetaY );

        // Keep track of the mouse location:
        prevMouseX = x;
        prevMouseY = y;
    }
    public void mousePressed( MouseEvent e )
    {
        // Start keeping track of the mouse location now.
        // This prevent the gear assembly from jerking to the new angle
        // whenever mouseDragged first gets called:
        prevMouseX = e.getX();
        prevMouseY = e.getY();
    }
}

EgonOlsen

Strange...maybe removeAll() removes too much from the applet!? Anyway, great that you found the problem.

fireside

Does this code go with the lwjgl applet loader then? 
click here->Fireside 7 Games<-

paulscode

Quote from: fireside on March 02, 2008, 05:46:09 PM
Does this code go with the lwjgl applet loader then? 

Yes it does.  The code for switching modes also works if you are using an application, in which case you have to include the lwjgl.jar into your classpath and point the VM to the native dll's when starting your application.