Main Menu

jPCT and JBullet Physics

Started by Ben, March 20, 2008, 09:11:39 PM

Previous topic - Next topic

Ben

I am not sure this is the correct section for posting this question, apologies in advance if needs to be moved.

I was wondering if anyone here has used the JBullet Physics engine : http://jbullet.advel.cz/ , and in particular, if they have had any luck using it with jPCT. It (JBullet) seems to be usable in an applet, and I was thinking that something like this combined with a graphics engine like jPCT would be really cool!

Anyone else have thoughts on this, has anyone used the 2 together, is it feasible?

Ben.



EgonOlsen

Quote from: Ben on March 20, 2008, 09:11:39 PM
is it feasible?
I think so. It claims to be independent from the underlying 3D framework, so i don't see a reason why it shouldn't work in combination with jPCT. Maybe some transformations between the coordinate systems of both engines are required, but i think that's the only hurdle.

EgonOlsen

#2
I've ported the basic demo to jPCT by writting my own implementation of jBullet's IGL-interface (which is fine to try things out but not a good idea to  continue development on that base, because it's bound too much to the way how OpenGL works. That's why i'm currently not offering it for download).
The only stage where i had to make a transition was when applying the OpenGL-alike transformation matrix to jPCT created Object3Ds. I had to split it into translation and rotation. The translation (as well as all other position related stuff) requires a small transformation into jPCT's coordinate system by doing x,y,z -> x,-y,-z. The rotational part can be set directly into a jPCT-matrix with setDump().

That was all, the rest was to make a jPCT based application work in the way the demo framework works, which required some ugly hacks. Anyway:



BTW: Fully developed on Asus EEEPC...it's quite convenient when using Netbeans (and impossible when using Eclipse).

Ben

Wow! that was fast, thanks for your responses Egon!

The screen shot looks really impressive, I would love to see it running (the shadows look really nice).

Although, I understand that you don't want to make the ported demo available for download, is there any chance you could make a simple applet that would demonstrate getting jPCT and Jbullet working together. Something really simple, although I realize this is asking a lot, and if you don't have the time, I understand.

When I find some spare time (soon), I would definitely like to play around with jPCT and Jbullet together.

Anyway, thanks for your insights, Egon.

Ben.

EgonOlsen

I would rather post the sources for the port than to dig deeper in jBullet, which is what i would have to do to make it work in an applet of my own. Actually, you just have to learn how jBullet works. The glue between jPCT and jBullet is very thin, and it should be possible to derive that from the sources. I'll post them later tomorrow.

EgonOlsen

#5
And here it comes. But please keep in mind that especially JPCTGL.java does some strange things to make jPCT behave like the state machine that OpenGL is. Normally, one wouldn't write a jPCT based application in that way.

/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* Bullet Continuous Collision Detection and Physics Library
* Copyright (c) 2003-2007 Erwin Coumans  http://continuousphysics.com/Bullet/
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
*    claim that you wrote the original software. If you use this software
*    in a product, an acknowledgment in the product documentation would be
*    appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
*    misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/

package javabullet.demos.basic;

import java.util.ArrayList;
import java.util.List;
import javabullet.collision.broadphase.BroadphaseInterface;
import javabullet.collision.broadphase.SimpleBroadphase;
import javabullet.collision.dispatch.CollisionDispatcher;
import javabullet.collision.dispatch.DefaultCollisionConfiguration;
import javabullet.collision.shapes.BoxShape;
import javabullet.collision.shapes.CollisionShape;
import javabullet.collision.shapes.StaticPlaneShape;
import javabullet.demos.opengl.DemoApplication;
import javabullet.demos.opengl.GLDebugDrawer;
import javabullet.demos.opengl.IGL;
import javabullet.dynamics.DiscreteDynamicsWorld;
import javabullet.dynamics.RigidBody;
import javabullet.dynamics.RigidBodyConstructionInfo;
import javabullet.dynamics.constraintsolver.ConstraintSolver;
import javabullet.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
import javabullet.linearmath.DefaultMotionState;
import javabullet.linearmath.Transform;
import javax.vecmath.Vector3f;
import static javabullet.demos.opengl.IGL.*;
import javabullet.demos.opengl.jpct.*;

/**
* BasicDemo is good starting point for learning the code base and porting.
*
* @author jezek2
*/
public class BasicDemoJPCT extends DemoApplication {

// create 125 (5x5x5) dynamic object
private static final int ARRAY_SIZE_X = 5;
private static final int ARRAY_SIZE_Y = 5;
private static final int ARRAY_SIZE_Z = 5;

// maximum number of objects (and allow user to shoot additional boxes)
private static final int MAX_PROXIES = (ARRAY_SIZE_X*ARRAY_SIZE_Y*ARRAY_SIZE_Z + 1024);

private static final int START_POS_X = -5;
private static final int START_POS_Y = -5;
private static final int START_POS_Z = -3;

// keep the collision shapes, for deletion/cleanup
private List<CollisionShape> collisionShapes = new ArrayList<CollisionShape>();
private BroadphaseInterface overlappingPairCache;
private CollisionDispatcher dispatcher;
private ConstraintSolver solver;
private DefaultCollisionConfiguration collisionConfiguration;

public BasicDemoJPCT(IGL gl) {
super(gl);
}

@Override
public void clientMoveAndDisplay() {
gl.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// simple dynamics world doesn't handle fixed-time-stepping
float ms = clock.getTimeMicroseconds();
clock.reset();

// step the simulation
if (dynamicsWorld != null) {
dynamicsWorld.stepSimulation(ms / 1000000f);
// optional but useful: debug drawing
dynamicsWorld.debugDrawWorld();
}

renderme();

//glFlush();
//glutSwapBuffers();
}

@Override
public void displayCallback() {
renderme();

// optional but useful: debug drawing to detect problems
if (dynamicsWorld != null) {
dynamicsWorld.debugDrawWorld();
}

}

public void initPhysics() {
setCameraDistance(50f);

// collision configuration contains default setup for memory, collision setup
collisionConfiguration = new DefaultCollisionConfiguration();

// use the default collision dispatcher. For parallel processing you can use a diffent dispatcher (see Extras/BulletMultiThreaded)
dispatcher = new CollisionDispatcher(collisionConfiguration);

// the maximum size of the collision world. Make sure objects stay within these boundaries
// TODO: AxisSweep3
// Don't make the world AABB size too large, it will harm simulation quality and performance
Vector3f worldAabbMin = new Vector3f(-10000,-10000,-10000);
Vector3f worldAabbMax = new Vector3f(10000,10000,10000);
//overlappingPairCache = new AxisSweep3(worldAabbMin,worldAabbMax,MAX_PROXIES);
overlappingPairCache = new SimpleBroadphase(MAX_PROXIES);
//overlappingPairCache = new _BrutalforceBroadphase();

// the default constraint solver. For parallel processing you can use a different solver (see Extras/BulletMultiThreaded)
SequentialImpulseConstraintSolver sol = new SequentialImpulseConstraintSolver();
solver = sol;

// TODO: needed for SimpleDynamicsWorld
//sol.setSolverMode(sol.getSolverMode() & ~SolverMode.SOLVER_CACHE_FRIENDLY.getMask());

dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);
//dynamicsWorld = new SimpleDynamicsWorld(dispatcher, overlappingPairCache, solver, collisionConfiguration);

dynamicsWorld.setGravity(new Vector3f(0f, -10f, 0f));

// create a few basic rigid bodies
//CollisionShape groundShape = new BoxShape(new Vector3f(50f, 50f, 50f));
CollisionShape groundShape = new StaticPlaneShape(new Vector3f(0, 1, 0), 50);

collisionShapes.add(groundShape);

Transform groundTransform = new Transform();
groundTransform.setIdentity();
groundTransform.origin.set(0, -56, 0);

// We can also use DemoApplication::localCreateRigidBody, but for clarity it is provided here:
{
float mass = 0f;

// rigidbody is dynamic if and only if mass is non zero, otherwise static
boolean isDynamic = (mass != 0f);

Vector3f localInertia = new Vector3f(0, 0, 0);
if (isDynamic) {
groundShape.calculateLocalInertia(mass, localInertia);
}

// using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
DefaultMotionState myMotionState = new DefaultMotionState(groundTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, groundShape, localInertia);
RigidBody body = new RigidBody(rbInfo);

// add the body to the dynamics world
dynamicsWorld.addRigidBody(body);
}

{
// create a few dynamic rigidbodies
// Re-using the same collision is better for memory usage and performance

CollisionShape colShape = new BoxShape(new Vector3f(1, 1, 1));
//CollisionShape colShape = new SphereShape(1f);
collisionShapes.add(colShape);

// Create Dynamic Objects
Transform startTransform = new Transform();
startTransform.setIdentity();

float mass = 1f;

// rigidbody is dynamic if and only if mass is non zero, otherwise static
boolean isDynamic = (mass != 0f);

Vector3f localInertia = new Vector3f(0, 0, 0);
if (isDynamic) {
colShape.calculateLocalInertia(mass, localInertia);
}

float start_x = START_POS_X - ARRAY_SIZE_X / 2;
float start_y = START_POS_Y;
float start_z = START_POS_Z - ARRAY_SIZE_Z / 2;

for (int k = 0; k < ARRAY_SIZE_Y; k++) {
for (int i = 0; i < ARRAY_SIZE_X; i++) {
for (int j = 0; j < ARRAY_SIZE_Z; j++) {
startTransform.origin.set(
2f * i + start_x,
2f * k + start_y,
2f * j + start_z);

// using motionstate is recommended, it provides interpolation capabilities, and only synchronizes 'active' objects
DefaultMotionState myMotionState = new DefaultMotionState(startTransform);
RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, myMotionState, colShape, localInertia);
RigidBody body = new RigidBody(rbInfo);

dynamicsWorld.addRigidBody(body);
}
}
}
}

clientResetScene();
}

public static void main(String[] args)  {
BasicDemoJPCT ccdDemo = new BasicDemoJPCT(JPCT.getGL());
ccdDemo.initPhysics();
ccdDemo.getDynamicsWorld().setDebugDrawer(new GLDebugDrawer(JPCT.getGL()));

JPCT.main(args, 640, 480, "Bullet Physics Demo. http://bullet.sf.net", ccdDemo);
}

}


/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* Bullet Continuous Collision Detection and Physics Library
* Copyright (c) 2003-2007 Erwin Coumans  http://continuousphysics.com/Bullet/
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
*    claim that you wrote the original software. If you use this software
*    in a product, an acknowledgment in the product documentation would be
*    appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
*    misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
package javabullet.demos.opengl.jpct;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.*;
import org.lwjgl.opengl.*;
import javabullet.demos.opengl.*;
import com.threed.jpct.*;

/**
*
* @author EgonOlsen
*/
public class JPCT {

    private static boolean redisplay = false;
    private static JPCTGL gl = new JPCTGL();
    private static FrameBuffer buffer = null;

    public static void postRedisplay() {
        redisplay = true;
    }

    public static IGL getGL() {
        return gl;
    }

    public static int main(String[] args, int width, int height, String title, DemoApplication demoApp) {
        buffer = new FrameBuffer(width, height, FrameBuffer.SAMPLINGMODE_NORMAL);
        Config.glWindowName = title;

        Config.lightMul=3;
        Config.glColorDepth = 16; // Should work on EEEPC

        buffer.disableRenderer(IRenderer.RENDERER_SOFTWARE);
        buffer.enableRenderer(IRenderer.RENDERER_OPENGL);

        gl.setBuffer(buffer);
       
        Keyboard.enableRepeatEvents(true);

        gl.init();

        demoApp.myinit();
        demoApp.reshape(width, height);

        boolean quit = false;

        long lastTime = System.currentTimeMillis();
        int frames = 0;

        while (!Display.isCloseRequested() && !quit) {
            demoApp.moveAndDisplay();
            gl.update();

            while (Keyboard.next()) {
                if (Keyboard.getEventCharacter() != '\0') {
                    demoApp.keyboardCallback(Keyboard.getEventCharacter(), Mouse.getX(), Mouse.getY());
                }

                if (Keyboard.getEventKeyState()) {
                    demoApp.specialKeyboard(Keyboard.getEventKey(), Mouse.getX(), Mouse.getY());
                } else {
                    demoApp.specialKeyboardUp(Keyboard.getEventKey(), Mouse.getX(), Mouse.getY());
                }

                if (Keyboard.getEventKey() == Keyboard.KEY_ESCAPE) {
                    quit = true;
                }
                if (Keyboard.getEventKey() == Keyboard.KEY_Q) {
                    quit = true;
                }
            }

            while (Mouse.next()) {
                if (Mouse.getEventButton() != -1) {
                    int btn = Mouse.getEventButton();
                    if (btn == 1) {
                        btn = 2;
                    } else if (btn == 2) {
                        btn = 1;
                    }
                    demoApp.mouseFunc(btn, Mouse.getEventButtonState() ? 0 : 1, Mouse.getEventX(), Display.getDisplayMode().getHeight() - 1 - Mouse.getEventY());
                }
                demoApp.mouseMotionFunc(Mouse.getEventX(), Display.getDisplayMode().getHeight() - 1 - Mouse.getEventY());
            }

            long time = System.currentTimeMillis();
            if (time - lastTime < 1000) {
                frames++;
            } else {
                Display.setTitle(title + " | FPS: " + frames);
                lastTime = time;
                frames = 0;
            }
        }

        System.exit(0);
        return 0;
    }
}


package javabullet.demos.opengl.jpct;

/*
* Java port of Bullet (c) 2008 Martin Dvorak <jezek2@advel.cz>
*
* Bullet Continuous Collision Detection and Physics Library
* Copyright (c) 2003-2007 Erwin Coumans  http://continuousphysics.com/Bullet/
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
*    claim that you wrote the original software. If you use this software
*    in a product, an acknowledgment in the product documentation would be
*    appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
*    misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
import com.threed.jpct.*;
import com.threed.jpct.util.*;
import javabullet.demos.opengl.*;

import java.io.*;
import java.util.*;
import java.util.List;
import java.awt.*;

import org.lwjgl.opengl.*;

/**
*
* @author EgonOlsen
*/
public class JPCTGL implements IGL {

    {
        Config.maxPolysVisible=20000;
        Config.useMultipleThreads=false;
    }
   
    private World world=new World();
    private Object3D plane=Primitives.getPlane(10,6);
    private FrameBuffer buffer=null;
   
    private Texture font;
    private int shadeModel = Object3D.SHADING_GOURAUD;
    private Color clearColor = Color.WHITE;
    private Light light=null;

    private Color color=Color.BLUE;
    private SimpleVector translate=new SimpleVector();
    private Matrix rot=new Matrix();
    private float scale=1f;
    Map<Float, List<Object3D>> cubes=new HashMap<Float, List<Object3D>>();
   
    private boolean shadows=true;
    private ShadowHelper sh=null;
    private boolean lines=false;
   
    public void setBuffer(FrameBuffer buffer) {
        this.buffer=buffer;
        if (shadows) {
            Projector proj=new Projector();
            proj.setPosition(1, -30, -1);
            proj.setFOV(1.5f);
            proj.setYFOV(1.5f);
            proj.lookAt(SimpleVector.ORIGIN);
            sh=new ShadowHelper(world,buffer,proj,1024);
            sh.addReceiver(plane);
            sh.setCullingMode(false);
            sh.setAmbientLight(new Color(20,20,20));
        }
    }
       
    public void init() {
        if (shadows) {
           world.setAmbientLight(30, 20, 20);
        } else {
           plane.setAdditionalColor(Color.GREEN);
        }
        TextureManager tm=TextureManager.getInstance();
        Texture t=new Texture("box.jpg");
        tm.addTexture("plane",t);
        plane.setTexture("plane");
        plane.build();
        plane.translate(0,7,0);
        plane.rotateX((float) Math.PI/2f);
        world.addObject(plane);
    }

    public void glLight(int light, int pname, float[] params) {
        if (pname==GL11.GL_AMBIENT) {
            this.light=new Light(world);
            SimpleVector ins=new SimpleVector(params[0]*255,params[1]*255, params[2]*255);
            this.light.setIntensity(ins);
        }
        if (pname==GL11.GL_POSITION) {
            this.light.setPosition(new SimpleVector(params[0],-params[1], -params[2]));
        }
    }

    public void glEnable(int cap) {
    }

    public void glDisable(int cap) {
    }

    public void glShadeModel(int mode) {
        if (mode == GL11.GL_SMOOTH) {
            shadeModel = Object3D.SHADING_GOURAUD;
        } else {
            shadeModel = Object3D.SHADING_FAKED_FLAT;
        }
    }

    public void glDepthFunc(int func) {
    }

    public void glClearColor(float red, float green, float blue, float alpha) {
        clearColor = new Color(red, green, blue, alpha);
    }

    public void glMatrixMode(int mode) {
    }

    public void glLoadIdentity() {
    }

    public void glFrustum(double left, double right, double bottom, double top, double zNear, double zFar) {
    }

    public void gluLookAt(float eyex, float eyey, float eyez, float centerx, float centery, float centerz, float upx, float upy, float upz) {
       world.getCamera().setPosition(new SimpleVector(eyex, -eyey,-eyez));
       world.getCamera().lookAt(new SimpleVector(centerx,-centery,-centerz));
    }

    public void glViewport(int x, int y, int width, int height) {
    }

    public void glPushMatrix() {
        rot.setIdentity();
        translate.x=0;
        translate.y=0;
        translate.z=0;
    }

    public void glPopMatrix() {
    }

    public void gluOrtho2D(float left, float right, float bottom, float top) {
    }

    public void glScalef(float x, float y, float z) {
        scale=x;
    }

    public void glTranslatef(float x, float y, float z) {
    }

    public void glColor3f(float red, float green, float blue) {
        color=new Color(Math.min(1f,red), Math.min(1f,green), Math.min(1f,blue));
    }

    public void glClear(int mask) {
        buffer.clear(clearColor);
    }

    public void glBegin(int mode) {
          if (mode==GL11.GL_LINES) {
            lines=true;
        } else {
            lines=false;
        }
    }

    public void glEnd() {
    }

    public void glVertex3f(float x, float y, float z) {
    }

    public void glLineWidth(float width) {
    }

    public void glPointSize(float size) {
    }

    public void glNormal3f(float nx, float ny, float nz) {
    }

    public void glMultMatrix(float[] m) {
        translate.x=m[12];
        translate.y=-m[13];
        translate.z=-m[14];
       
        m[12]=0;
        m[13]=0;
        m[14]=0;
       
        rot.setDump(m);
    }

    ////////////////////////////////////////////////////////////////////////////
    public void drawCube(float extent) {
        extent = extent * 0.5f;

        Float key=new Float(extent);
        List<Object3D> cubeLst=cubes.get(key);
        if (cubeLst==null) {
            cubeLst=new ArrayList<Object3D>();
            cubes.put(key, cubeLst);
        }
       
        Object3D next=null;
        for (Object3D obj:cubeLst) {
            if (obj.getVisibility()==Object3D.OBJ_INVISIBLE) {
                next=obj;
                break;
            }
        }
        if (next==null) {
            next=Primitives.getBox(extent, 1);
            cubeLst.add(next);
            world.addObject(next);
            next.rotateY((float) Math.PI/4f);
            next.rotateMesh();
            next.build();
           
            if (shadows) {
                sh.addCaster(next);
                //sh.addReceiver(next);
            }
        }
        next.setVisibility(Object3D.OBJ_VISIBLE);
        next.setAdditionalColor(color);
        next.getTranslationMatrix().setIdentity();
        next.translate(new SimpleVector(translate));
        next.setScale(1);
        next.getRotationMatrix().setTo(rot);
       
        next.setScale(scale);
       
        //System.out.println(next.getTransformedCenter());
       
    }

    /*
    private static final Cylinder quadObj = new Cylinder();
    private static final Sphere sphere = new Sphere();

    private static class SphereKey {

        public float radius;

        public SphereKey() {
        }

        public SphereKey(SphereKey key) {
            radius = key.radius;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null || !(obj instanceof SphereKey)) {
                return false;
            }
            SphereKey other = (SphereKey) obj;
            return radius == other.radius;
        }

        @Override
        public int hashCode() {
            return Float.floatToIntBits(radius);
        }
    }
    private static Map<SphereKey, Integer> sphereDisplayLists = new HashMap<SphereKey, Integer>();
    private static SphereKey sphereKey = new SphereKey();

    public void drawSphere(float radius, int slices, int stacks) {
        sphereKey.radius = radius;
        Integer glList = sphereDisplayLists.get(sphereKey);
        if (glList == null) {
            glList = glGenLists(1);
            glNewList(glList, GL_COMPILE);
            sphere.draw(radius, 8, 8);
            glEndList();
            sphereDisplayLists.put(new SphereKey(sphereKey), glList);
        }

        glCallList(glList);
    }

    ////////////////////////////////////////////////////////////////////////////
    private static class CylinderKey {

        public float radius;
        public float halfHeight;

        public CylinderKey() {
        }

        public CylinderKey(CylinderKey key) {
            radius = key.radius;
            halfHeight = key.halfHeight;
        }

        public void set(float radius, float halfHeight) {
            this.radius = radius;
            this.halfHeight = halfHeight;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null || !(obj instanceof CylinderKey)) {
                return false;
            }
            CylinderKey other = (CylinderKey) obj;
            if (radius != other.radius) {
                return false;
            }
            if (halfHeight != other.halfHeight) {
                return false;
            }
            return true;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 23 * hash + Float.floatToIntBits(radius);
            hash = 23 * hash + Float.floatToIntBits(halfHeight);
            return hash;
        }
    }
    private static Map<CylinderKey, Integer> cylinderDisplayLists = new HashMap<CylinderKey, Integer>();
    private static CylinderKey cylinderKey = new CylinderKey();

    public void drawCylinder(float radius, float halfHeight, int upAxis) {
        glPushMatrix();
        switch (upAxis) {
            case 0:
                glRotatef(-90f, 0.0f, 1.0f, 0.0f);
                glTranslatef(0.0f, 0.0f, -halfHeight);
                break;
            case 1:
                glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
                glTranslatef(0.0f, 0.0f, -halfHeight);
                break;
            case 2:
                glTranslatef(0.0f, 0.0f, -halfHeight);
                break;
            default: {
                assert (false);
            }
        }

        // The gluCylinder subroutine draws a cylinder that is oriented along the z axis.
        // The base of the cylinder is placed at z = 0; the top of the cylinder is placed at z=height.
        // Like a sphere, the cylinder is subdivided around the z axis into slices and along the z axis into stacks.

        cylinderKey.set(radius, halfHeight);
        Integer glList = cylinderDisplayLists.get(cylinderKey);
        if (glList == null) {
            glList = glGenLists(1);
            glNewList(glList, GL_COMPILE);

            quadObj.setDrawStyle(GLU_FILL);
            quadObj.setNormals(GLU_SMOOTH);

            quadObj.draw(radius, radius, 2f * halfHeight, 15, 10);

            glEndList();
            cylinderDisplayLists.put(new CylinderKey(cylinderKey), glList);
        }

        glCallList(glList);

        glPopMatrix();
    }
*/

     public void drawCylinder(float radius, float halfHeight, int upAxis) {
     }
   
     public void drawSphere(float radius, int slices, int stacks) {
     }
     
    ////////////////////////////////////////////////////////////////////////////
    public void drawString(CharSequence s, int x, int y, float red, float green, float blue) {
        if (font != null) {
            //
        }
    }
   
    public void update() {
        plane.setVisibility(Object3D.OBJ_VISIBLE);
        if (!shadows) {
            buffer.clear(clearColor);
            world.renderScene(buffer);
            if (lines) {
                world.drawWireframe(buffer, Color.WHITE);
            } else {
                world.draw(buffer);
            }
            buffer.update();
            buffer.displayGLOnly();
        } else {
            sh.updateShadowMap();
            buffer.clear(clearColor);
            sh.drawScene();
            buffer.update();
            buffer.displayGLOnly();
        }
       
        for (Enumeration e=world.getObjects(); e.hasMoreElements();) {
            Object3D obj=(Object3D)e.nextElement();
            obj.setVisibility(false);
        }
    }
}


Ben

Thanks for making this available Egon, I am sure this will be helpful.

:D