www.jpct.net

jPCT - a 3d engine for Java => Support => Topic started by: paulscode on February 24, 2008, 09:04:22 pm

Title: How to Hardware Mode
Post by: paulscode on February 24, 2008, 09:04:22 pm
I am having a little trouble getting my applet to run in SAMPLINGMODE_HARDWARE_ONLY.
The Sun Java Console shows:
Code: [Select]
Java version is: 1.6.0_03
-> support for BufferedImage
Version helper for 1.2+ initialized!
-> using BufferedImage
Software renderer (OpenGL mode) initialized
java.lang.NoClassDefFoundError: org/lwjgl/opengl/Display
at com.threed.jpct.GLHelper.findMode(Unknown Source)
at com.threed.jpct.GLHelper.init(Unknown Source)
at com.threed.jpct.GLRenderer.init(Unknown Source)
at com.threed.jpct.FrameBuffer.enableRenderer(Unknown Source)
at com.threed.jpct.FrameBuffer.enableRenderer(Unknown Source)
at com.threed.jpct.FrameBuffer.enableRenderer(Unknown Source)
at jPCTGears.init(jPCTGears.java:48)
at sun.applet.AppletPanel.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

Here is the code in question:
Code: [Select]
    // Initialize all components of the applet
    @Override
    public void init()
    {
        // sign the applet up to receive mouse messages:
        addMouseListener( this );
        addMouseMotionListener( this );
       
        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.enableRenderer( IRenderer.RENDERER_OPENGL );
        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );

        // 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, 135.0f, 0 ) );
        blueGear.build();

        // Set up a pivot point for the entire gear assembly:
        //assemblyPivot = new Object3D(1);
        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
    }

The applet only works when I use SAMPLINGMODE_NORMAL and RENDERER_SOFTWARE.  I am sure it is something simple I am doing incorrectly.  I am just having a little trouble figuring out what.
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 24, 2008, 09:14:10 pm
You have to include the lwjgl.jar into your classpath and point the VM to the native part (dll/so/...)  by adding a -Djava.library.path=<your path to the dlls> when starting your application. When using it in an applet, it's a different story. You have to download the native parts in that applet and install them locally (the applet has to be signed to do this). LWJGL (http://lwjgl.org (http://lwjgl.org)) comes with an utility class (http://www.lwjgl.org/javadoc/org/lwjgl/util/applet/package-summary.html (http://www.lwjgl.org/javadoc/org/lwjgl/util/applet/package-summary.html)) to ease this.
Title: Re: How to Hardware Mode
Post by: paulscode on February 25, 2008, 02:23:26 am
I am using the AppletLoader now, but still having some troubles.  It is making it a bit further into the program, but crashing at Paint().  Here is the output from the Java Console:
Code: [Select]
Java version is: 1.6.0_03
-> support for BufferedImage
Version helper for 1.2+ initialized!
-> using BufferedImage
Software renderer (OpenGL mode) initialized
Current mode:640 x 480 x 32 @120Hz
Driver is: ati2dvag/6.14.10.6666 on ATI Technologies Inc. / RADEON XPRESS Series x86/MMX/3DNow!/SSE2
FBO supported and used!
OpenGL renderer initialized (using 4 texture stages)
Software renderer disposed
Loading file from InputStream
File from InputStream loaded...51142 bytes
Processing new material FlatRed!
Processing object from 3DS-file: Gear
Object 'Gear_jPCT0' created using 728 polygons and 364 vertices.
Loading file from InputStream
File from InputStream loaded...29586 bytes
Processing new material FlatGreen!
Processing object from 3DS-file: Gear
Object 'Gear_jPCT1' created using 420 polygons and 210 vertices.
Loading file from InputStream
File from InputStream loaded...33504 bytes
Processing new material FlatBlue!
Processing object from 3DS-file: Gear
Object 'Gear_jPCT2' created using 476 polygons and 238 vertices.
Adding Lightsource: 0
Exception in thread "AWT-EventQueue-2" java.lang.NullPointerException
    at org.lwjgl.opengl.GL11.glClearColor(GL11.java:566)
    at com.threed.jpct.GLRenderer.execute(Unknown Source)
    at com.threed.jpct.FrameBuffer.clearHardware(Unknown Source)
    at com.threed.jpct.FrameBuffer.clear(Unknown Source)
    at com.threed.jpct.FrameBuffer.clear(Unknown Source)
    at jPCTGears.paint(jPCTGears.java:103)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.paintDirtyRegions(Unknown Source)
    at javax.swing.RepaintManager.seqPaintDirtyRegions(Unknown Source)
    at javax.swing.SystemEventQueueUtilities$ComponentWorkRequest.run(Unknown Source)
    at java.awt.event.InvocationEvent.dispatch(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)

Here is the source code for my applet:
Code: [Select]
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
{
    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;
   
    // Initialize all components of the applet
    @Override
    public void init()
    {
        // sign the applet up to receive mouse messages:
        addMouseListener( this );
        addMouseMotionListener( this );
       
        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.enableRenderer( IRenderer.RENDERER_OPENGL );
        buffer.disableRenderer( IRenderer.RENDERER_SOFTWARE );

        // 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 = new Object3D(1);
        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
    }

    // Draw the scene
    @Override
    public void paint( Graphics g )
    {
        // rotate the gears:
        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();

        buffer.display(g, 0, 0);  // draw the buffer onto the applet frame
       
        repaint( 200, 0, 0, width, height );  // keep the graphics auto-refreshing
    }

    // 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 );
    }
   
    // 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 ) {}
    public void mouseMoved( MouseEvent e ) {}
}

Another issue I had, was that I was forced to put my 3ds files into the JAR, because when using the AppletLoader, it seems like the loader can't find them.

I guess I just need to do some more googling about that AppletLoader - it is certainly not as plug-and-play as it looks.
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 25, 2008, 08:13:48 am
jPCT offers two hardware modes. One renders into a native GL-Window, the other one into an AWT Canvas. To render directly into an applet, you have to use the latter, which can be enabled by using FrameBuffer.enableGLCanvasRenderer(); (it returns the Canvas, so that you can add it to the applet).
I can't comment about the 3ds-jar-problem, because i usually put my resources into jars...it's less troublesome and required for webstart, which is what i'm using most of the time. May be related to the AppletLoader using another class loader or something... ???
Title: Re: How to Hardware Mode
Post by: paulscode on February 26, 2008, 01:11:34 am
I am getting closer - now at least there are no error messages.  Unfortunately, now my applet only draws a light-grey square.  I assume this has something to do with my not using the Canvas correctly.  Here is my code, in case you can see something I've obviously done incorrectly:

Code: [Select]
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
{
    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;
   
    Canvas myCanvas;
   
    // Initialize all components of the applet
    @Override
    public void init()
    {
        // sign the applet up to receive mouse messages:
        addMouseListener( this );
        addMouseMotionListener( this );
       
        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.enableRenderer( IRenderer.RENDERER_OPENGL );
        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
    }

    // Draw the scene
    @Override
    public void paint( Graphics g )
    {
        // rotate the gears:
        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();
       
        buffer.displayGLOnly();
        myCanvas.repaint( 200, 0, 0, width, height );
    }

    // 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 );
    }
   
    // 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 ) {}
    public void mouseMoved( MouseEvent e ) {}
}

BTW, I appreciate the assistance you've been giving me.
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 26, 2008, 08:18:08 am
You mean that the complete canvas is grey? Or are the textures just missing? Anyway, try to remove

Code: [Select]
buffer.enableRenderer( IRenderer.RENDERER_OPENGL );

It's not needed in this case, because you don't want the native renderer anyway (which you are initializing with this call). I'm not sure if this will already fix your problem, but it should be removed.
Title: Re: How to Hardware Mode
Post by: paulscode on February 26, 2008, 01:33:07 pm
Removing that line helped (before the complete canvas was grey).  Now, with that line removed, I am getting a black screen (no textures are being drawn).
Title: Re: How to Hardware Mode
Post by: 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:

Code: [Select]
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
    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
       
        myCanvas.addMouseListener( this );
        myCanvas.addMouseMotionListener( this );
       
        new Thread(this).start();
    }

    // Draw the scene
    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();
       
        buffer.displayGLOnly();
        myCanvas.repaint();
    }
   
    public void destroy() {
    loop=false;
    }
   
    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 );
    }
   
    // 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 ) {}
    public void mouseMoved( MouseEvent e ) {}
}
Title: Re: How to Hardware Mode
Post by: paulscode on February 26, 2008, 10:02:28 pm
Ah, that is a smart way for telling the applet to keep repainting itself.  Unfortunately, there still seems to be another problem - the applet is still showing up black even with these additions.

On another note, about the applet needing to repaint itself.  I had that problem when I started writing my first jPCT applet.  Originally, I had fixed this by sticking:
Code: [Select]
repaint( 200, 0, 0, width, height );
At the bottom of the paint() function.  Supposedly, this tells the applet to repaint itself "within 200 miliseconds" (I arbitrarily chose the number 200), and it works well for me when I am doing the applet in software-rendering mode (see my previous thread "Switching Between Pivot Points").  It is a bit cleaner, but essentially, I think this does the same thing as repainting from the loop ( in your case, you are waiting 10 miliseconds between calls to paint() ).

Unfortunately, neither of these two methods are working for the applet in question - still just getting a black screen, so I am guessing there is another problem in addition to telling the applet to repaint itself.

At least I am one problem closer to the solution  ;D
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 26, 2008, 10:18:48 pm
Hmm...the applet works for me in the AppletViewer with exactly the code that i've posted. I haven't tried it in a browser though.
About repaint(): Personally, i don't like the fact that repaint() doesn't really paint anything. It's a just a suggestion to the underlying graphics subsystem that the component needs repainting. The system decides when...i dislike this when it comes to smooth animation and such stuff.

As proof:
(http://www.jpct.net/pix/gears.png)

Title: Re: How to Hardware Mode
Post by: paulscode on February 26, 2008, 10:54:49 pm
Wow.  Here is something odd for you:  I thought the problem might be related to there not being enough light, so I vamped up the ambient light to 150.  It worked then (Insanely bright, of course).  I then tried slowly adjusting the ambient light down to a good level, and reached 50 ( where i started ??? ) and now the thing works.  Darndest thing I ever saw.
Title: Re: How to Hardware Mode
Post by: paulscode on February 26, 2008, 10:59:26 pm
ROFL  -- Only me.  I am at a complete loss.  Well, anyway, I guess it works now...

Thanks for you help!
Title: Re: How to Hardware Mode
Post by: paulscode on February 27, 2008, 12:08:38 am
I hate to beat a dead horse, but I tend to be really thorough when learning a new concept.  So now I know how to run in either software mode or hardware mode from within an applet.  Now my ultimate goal with this concept is the ability to switch between the two modes dynamically.  Here are the changes I made to my code:

In init(), (so I still get mouse input when the canvas gets deleted):
Code: [Select]
        // 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 );       

In paint():
Code: [Select]
        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
        }

And finally, in mouseClicked() (The "dynamic switching" code):
Code: [Select]
    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();
                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.enableRenderer(IRenderer.RENDERER_SOFTWARE );
                removeAll();
                validate();
            }
        }
    }

The idea here is to switch dynamically between modes when the right mouse-button is clicked.  This code works when going from hardware mode to software mode, but not the other way around.  I assume my problem has something to do with the threads, perhaps?
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 27, 2008, 08:08:18 am
Maybe...what exactly is the problem you are getting?
Title: Re: How to Hardware Mode
Post by: paulscode on February 27, 2008, 01:30:28 pm
The applet freezes (no refreshing).  The JAVA Console says "OpenGL (AWTGLCanvas) renderer disposed" immediately before the crash.  No error messages after that, though.
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 27, 2008, 05:51:47 pm
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...
Title: Re: How to Hardware Mode
Post by: raft on February 27, 2008, 06:46:34 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:

Code: [Select]
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
Title: Re: How to Hardware Mode
Post by: paulscode on February 27, 2008, 11:36:30 pm
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:
Code: [Select]
OpenGL (AWTGLCanvas) renderer disposed
Software renderer (OpenGL mode) initialized

But from Software to Hardware I just get:
Code: [Select]
Software renderer disposed

Basically, it is not initializing the AWTGLRenderer - The Java Console should say something like:
Code: [Select]
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:
Code: [Select]
    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:
Code: [Select]
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();
}
Title: Re: How to Hardware Mode
Post by: paulscode on February 27, 2008, 11:46:29 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:

Code: [Select]
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!
Title: Re: How to Hardware Mode
Post by: raft on February 28, 2008, 01:25:02 am
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:

Code: [Select]
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
Title: Re: How to Hardware Mode
Post by: paulscode on February 28, 2008, 02:41:48 am
Very awesome, raft.  Thanks!
Title: Re: How to Hardware Mode
Post by: paulscode on February 28, 2008, 11:33:06 pm
I still can't seem to figure out how to dynamically switch from software mode back to hardware mode by using
Code: [Select]
enableGLCanvasRenderer();
instead of
Code: [Select]
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:

Code: [Select]
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 ) {}
}
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 29, 2008, 12:14:42 am
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.
Title: Re: How to Hardware Mode
Post by: paulscode on February 29, 2008, 02:42:29 am
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!
Title: Re: How to Hardware Mode
Post by: EgonOlsen on February 29, 2008, 05:17:39 pm
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.
Title: Re: How to Hardware Mode
Post by: paulscode on March 01, 2008, 01:41:08 am
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:

Code: [Select]
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():
Code: [Select]
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:
Code: [Select]
myCanvas = buffer.enableGLCanvasRenderer();
with:
Code: [Select]
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!
Title: Re: How to Hardware Mode
Post by: paulscode on March 01, 2008, 03:26:01 am
YES!  I figured it out!

I just have to use:
Code: [Select]
this.remove( myCanvas );

instead of:
Code: [Select]
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:
Code: [Select]
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();
    }
}
Title: Re: How to Hardware Mode
Post by: EgonOlsen on March 01, 2008, 10:41:14 am
Strange...maybe removeAll() removes too much from the applet!? Anyway, great that you found the problem.
Title: Re: How to Hardware Mode
Post by: fireside on March 02, 2008, 05:46:09 pm
Does this code go with the lwjgl applet loader then? 
Title: Re: How to Hardware Mode
Post by: paulscode on March 03, 2008, 01:46:07 am
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.