No 3D movement tutorials?

Started by appel, October 15, 2006, 16:44:37

Previous topic - Next topic

appel

There are examples out there that show how to display a cube or something, but the image is static. You cannot move the *camera* around to view that cube from another perspective. How do you do that?

ndhb

There are many ways. You can wrap a class around your "Kamera Class" like with GLU.gluLookAt() like I did below:

It's for a specifc kinda of camera, that moves in xz-plane and treats y-axis as up, but maybe its a start.

Hope you solve it, ask if something is unclear.

- Nicolai

---

package dk.nesos.view.camera;

import java.nio.*;

import org.lwjgl.*;
import org.lwjgl.opengl.*;
import org.lwjgl.opengl.glu.*;
import org.lwjgl.util.vector.*;

/**
 * <P>
 * Simulates a simple camera in OpenGL.
 * <P>
 * <B>NOTE: Upwards is defined to be the Vector [0, 1, 0].</B>
 * 
 * @see org.lwjgl.opengl.glu.GLU#gluLookAt(float, float, float, float, float,
 *      float, float, float, float)
 * @author ndhb
 */
public final class Camera {

    private static final int NUMBER_OF_FRUSTUMS = 6;

    /**
     * The UP vector in the world.
     */
    private static final Vector3f UP = new Vector3f(0, 1, 0);

    /**
     * <P>
     * The direction in 3D space of the camera.
     * <P>
     * The camera is always looking in this direction.
     */
    private Vector3f direction;

    /**
     * <P>
     * The 6 frustum planes defined by the view.
     */
    private Frustum[] frustums = new Frustum[NUMBER_OF_FRUSTUMS];

    /**
     * <P>
     * The position in 3D space of the camera.
     * <P>
     * The position of the virtual observer.
     */
    private Vector3f position;

    
    /**
     * <P>
     * Display List name for drawing axis
     */
    private int axisListName;
    
    /**
     * <P>
     * This constructor quickly sets UP a camera at [1, 20, 1] with direction
     * [1, 0, 0].
     * <P>
     * That means the camera is located 20 units UP the Y-axis and looking down
     * the X-axis.
     */
    public Camera() {
        this(new Vector3f(1, 20, 1), new Vector3f(1, 0, 0));
    } // constructor

    /**
     * <P>
     * This constructor sets UP a camera and its direction at the supplied
     * locations in 3D space.
     */
    public Camera(Vector3f location, Vector3f direction) {
        this.position = location;
        this.direction = direction;
        for (int f = 0; f < NUMBER_OF_FRUSTUMS; f++) {
            frustums[f] = new Frustum();
        } // for all frustums
    } // constructor

    /**
     * <P>
     * Retrieves the direction in 3D space of the camera.
     * 
     * @return Vector3f representing the camera direction.
     */
    public Vector3f getDirection() {
        return direction;
    } // method

    /**
     * <P>
     * Retrieves the computed frustums.
     * 
     * @return Returns the frustums
     */
    public Frustum[] getFrustums() {
        return frustums;
    } // method

    // /** TODO ndhb: move code to some class implementing Intersectable
    // interface (BoundingPoint?)
    // * <P>Checks if the specified point is contained in the current frustum.
    // *
    // * @param cx the X-coordinate of the point
    // * @param cy the Y-coordinate of the point
    // * @param cz the Z-coordinate of the point
    // * @return <code>true</code> if the the specified point is in the current
    // frustum, otherwise <code>false</code>.
    // */
    // public boolean pointInFrustum(float cx, float cy, float cz) {
    // for (int p = 0; p < 6; p++) {
    // if( frustum[p][0] * cx + frustum[p][1] * cy + frustum[p][2] * cz +
    // frustum[p][3] <= 0 ) {
    // return false;
    // } // if
    // } // for all planes
    // return true;
    // } // method
    //    

    /**
     * <P>
     * Retrieves the position in 3D space of the camera.
     * 
     * @return Vector3f representing the camera position.
     */
    public Vector3f getPosition() {
        return position;
    } // method

    /**
     * <P>
     * Rotates the direction of the camera downwards.
     * 
     * <P>
     * <B>Note: Specify degrees (instead of radians).</B>
     * 
     * @param degrees
     *            the number of degrees.
     */
    public void lookDown(float degrees) {
        float radians = (float) Math.toRadians(degrees);
        Vector3f axis = Vector3f.cross(direction, UP, null); // the axis to rotate around is perpendular to direction and up
        rotateAroundAxis(-radians, axis);
    } // method

    /**
     * <P>
     * Rotates the camera direction in the XZ-plane.
     * 
     * <P>
     * <B>Note: Specify degrees (instead of radians).</B>
     * 
     * @param degrees
     *            the number of degrees.
     */
    public void lookLeft(float degrees) {
        float radians = (float)Math.toRadians(degrees);
        rotateY(radians); // just rotate around the Y-axis
    } // method

    /**
     * <P>
     * Rotates the camera direction in the XZ-plane.
     * 
     * <P>
     * <B>Note: Specify degrees (instead of radians).</B>
     * 
     * @param degrees
     *            the number of degrees.
     */
    public void lookRight(float degrees) {
        float radians = (float)Math.toRadians(degrees);
        rotateY(-radians); // just rotate around the Y-axis
    } // method

    /**
     * <P>
     * Rotates the direction of the camera upwards.
     * 
     * <P>
     * <B>Note: Specify degrees (instead of radians).</B>
     * 
     * @param degrees
     *            the number of degrees.
     */
    public void lookUp(float degrees) {
        float radians = (float)Math.toRadians(degrees);
        Vector3f axis = Vector3f.cross(direction, UP, null); // the axis to rotate around is perpendular to direction and up
        rotateAroundAxis(radians, axis);
    } // method

    /**
     * <P>
     * Moves the position of the camera backwards in the current direction.
     * <P>
     * The Y-coordinate remains unchanged.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveBackwards(float delta) {
        position.x = position.x - (direction.x * delta);
        position.z = position.z - (direction.z * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera backwards and to the left of the current
     * direction.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveBackwardsLeft(float delta) {
        position.x = position.x - (direction.x * delta) + (direction.z * delta);
        position.z = position.z - (direction.z * delta) - (direction.x * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera backwards and to the right of the
     * current direction.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveBackwardsRight(float delta) {
        position.x = position.x - (direction.x * delta) - (direction.z * delta);
        position.z = position.z - (direction.z * delta) + (direction.x * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera downwards in the Y-axis.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveDown(float delta) {
        position.y -= delta;
    } // method

    /**
     * <P>
     * Moves the position of the camera forward in the current direction.
     * <P>
     * The Y-coordinate remains unchanged.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveForwards(float delta) {
        position.x = position.x + (direction.x * delta);
        position.z = position.z + (direction.z * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera forwards and to the left of the current
     * direction.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveForwardsLeft(float delta) {
        position.x = position.x + (direction.x * delta) + (direction.z * delta);
        position.z = position.z + (direction.z * delta) - (direction.x * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera forwards and to the right of the current
     * direction.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveForwardsRight(float delta) {
        position.x = position.x + (direction.x * delta) - (direction.z * delta);
        position.z = position.z + (direction.z * delta) + (direction.x * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera to the left of the current direction.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveLeft(float delta) {
        position.x = position.x + (direction.z * delta);
        position.z = position.z - (direction.x * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera to the right of the current direction.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveRight(float delta) {
        position.x = position.x - (direction.z * delta);
        position.z = position.z + (direction.x * delta);
    } // method

    /**
     * <P>
     * Moves the position of the camera upwards in the Y-axis.
     * 
     * @param delta
     *            the number of units to move.
     */
    public void moveUp(float delta) {
        position.y += delta;
    } // method

    /**
     * <P>
     * Performs an update of the view.
     * <P>
     * After modifying the camera position or direction this method should be
     * called to orient the OpenGL Modelview-matrix.
     */
    public void refresh() {
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();
        // maybe do camera zoom here (with VIEWPORT_MATRIX)?
        GLU.gluLookAt(position.x, position.y, position.z, position.x + direction.x, position.y + direction.y, position.z + direction.z, UP.x, UP.y, UP.z);
        computeFrustums(); // recompute frustums now
    } // method

    /**
     * <P>
     * Rotates the direction around an arbitrary axis (represented by a vector).
     * 
     * @link http://www.cprogramming.com/tutorial/3d/rotation.html
     * @param radians the amount to rotate
     * @param axis the vector representing the axis.
     */
    public void rotateAroundAxis(float radians, Vector3f axis) {
        float cos = (float) Math.cos(radians);
        float sin = (float) Math.sin(radians);
        float t = 1 - cos;
        if (axis.length() != 0) {
            axis.normalise(); // unit vector
        } // if vector is not zero vector
        Matrix3f rotationMatrix = new Matrix3f();
        rotationMatrix.m00 = t * axis.x * axis.x + cos; // row 0, column 0
        rotationMatrix.m01 = t * axis.x * axis.y + sin * axis.z; // row 0, column 1
        rotationMatrix.m02 = t * axis.x * axis.z - sin * axis.y; // row 0, column 2
        rotationMatrix.m10 = t * axis.x * axis.y - sin * axis.z; // row 1, column 0
        rotationMatrix.m11 = t * axis.y * axis.y + cos; // row 1, column 1
        rotationMatrix.m12 = t * axis.y * axis.z + sin * axis.x; // row 1, column 2
        rotationMatrix.m20 = t * axis.x * axis.y + sin * axis.y; // row 2, column 0
        rotationMatrix.m21 = t * axis.y * axis.z - sin * axis.x; // row 2, column 1
        rotationMatrix.m22 = t * axis.z * axis.z + cos; // row 2, column 2
        Matrix3f.transform(rotationMatrix, direction, direction); // changes direction vector
    } // method

    /**
     * Rotating the view around the X-axis can be achieved by multiplying the
     * matrix:<BR>
     * 
     * <PRE>
     *              [ 1       0        0      ] 
     *   R_x(t) =   [ 0       cos(t)  -sin(t) ]
     *              [ 0       sin(t)  cos(t)  ]
     * </PRE>
     * 
     * <BR>
     * with the direction vector.
     * <P>
     * Note. This method unfolds the matrix multiplication to avoid creating
     * unnecessary objects (eg. Matrix3f).
     * 
     * @param radians the amount to rotate
     */
    public void rotateX(float radians) {
        float cos = (float)Math.cos(radians);
        float sin = (float)Math.sin(radians);
        direction.y = (direction.y * cos) + (direction.z * -sin);
        direction.z = (direction.y * sin) + (direction.z * cos);
    } // method

    /**
     * Rotating the view around the Y-axis can be achieved by multiplying the
     * matrix:<BR>
     * 
     * <PRE>
     *              [ cos(t)  0       sin(t)  ] 
     *   R_y(t) =   [ 0       1       0       ]
     *              [ -sin(t) 0       cos(t)  ]
     * </PRE>
     * 
     * <BR>
     * with the direction vector.
     * <P>
     * Note. This method unfolds the matrix multiplication to avoid creating
     * unnecessary objects (eg. Matrix3f).
     * 
     * @param radians the amount to rotate
     */
    public void rotateY(float radians) {
        float cos = (float)Math.cos(radians);
        float sin = (float)Math.sin(radians);
        direction.x = (direction.x * cos) + (direction.z * sin);
        direction.z = (direction.x * -sin) + (direction.z * cos);
    } // method

    /**
     * Rotating the view around the Z-axis can be achieved by multiplying the
     * matrix:<BR>
     * 
     * <PRE>
     *              [ cos(t)  -sin(t) 0       ] 
     *   R_z(t) =   [ sin(t)  cos(t)  0       ]
     *              [ 0       0       1       ]
     * </PRE>
     * 
     * <BR>
     * with the direction vector.
     * <P>
     * Note. This method unfolds the matrix multiplication to avoid creating
     * unnecessary objects (eg. Matrix3f).
     * 
     * @param radians the amount to rotate
     */
    public void rotateZ(float radians) {
        float cos = (float)Math.cos(radians);
        float sin = (float)Math.sin(radians);
        direction.x = (direction.x * cos) + (direction.y * -sin);
        direction.y = (direction.x * sin) + (direction.y * cos);
    } // method

    /**
     * <P>Sets the camera looking in the specified direction.
     * 
     * @param direction The direction to set.
     */
    public void setDirection(Vector3f direction) {
        this.direction = direction;
    } // method

    /**
     * <P>Sets the camera at the specified position.
     * 
     * @param position The position to set.
     */
    public void setPosition(Vector3f position) {
        this.position = position;
    } // method

    /**
     * <P>
     * Returns a string representation of the position and direction.
     * 
     * @see java.lang.Object#toString()
     * @return a String with the position and direction values.
     */
    public String toString() {
        StringBuilder s = new StringBuilder();
        s.append(System.getProperty("line.separator"));
        s.append("Position:  ");
        s.append(position.toString());
        s.append(System.getProperty("line.separator"));
        s.append("Direction: ");
        s.append(direction.toString());
        return s.toString();
    } // method

    /**
     * <P>
     * Computes the frustum planes based on the current ModelView and Projection
     * matrices.
     * <P>
     * Copied from Mark Morley's article on OpenGL.org "Frustum Culling in
     * OpenGL"
     */
    private void computeFrustums() {
        FloatBuffer proj = BufferUtils.createFloatBuffer(16);
        FloatBuffer modl = BufferUtils.createFloatBuffer(16);
        FloatBuffer clip = BufferUtils.createFloatBuffer(16);

        GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, proj); // get the current projection matrix from OpenGL
        GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, modl); // get the current modelview matrixfrom OpenGL

        // combine the two matrices (multiply projection by modelview)
        clip.put(0, modl.get(0) * proj.get(0) + modl.get(1) * proj.get(4) + modl.get(2) * proj.get(8) + modl.get(3) * proj.get(12));
        clip.put(1, modl.get(0) * proj.get(1) + modl.get(1) * proj.get(5) + modl.get(2) * proj.get(9) + modl.get(3) * proj.get(13));
        clip.put(2, modl.get(0) * proj.get(2) + modl.get(1) * proj.get(6) + modl.get(2) * proj.get(10) + modl.get(3) * proj.get(14));
        clip.put(3, modl.get(0) * proj.get(3) + modl.get(1) * proj.get(7) + modl.get(2) * proj.get(11) + modl.get(3) * proj.get(15));

        clip.put(4, modl.get(4) * proj.get(0) + modl.get(5) * proj.get(4) + modl.get(6) * proj.get(8) + modl.get(7) * proj.get(12));
        clip.put(5, modl.get(4) * proj.get(1) + modl.get(5) * proj.get(5) + modl.get(6) * proj.get(9) + modl.get(7) * proj.get(13));
        clip.put(6, modl.get(4) * proj.get(2) + modl.get(5) * proj.get(6) + modl.get(6) * proj.get(10) + modl.get(7) * proj.get(14));
        clip.put(7, modl.get(4) * proj.get(3) + modl.get(5) * proj.get(7) + modl.get(6) * proj.get(11) + modl.get(7) * proj.get(15));

        clip.put(8, modl.get(8) * proj.get(0) + modl.get(9) * proj.get(4) + modl.get(10) * proj.get(8) + modl.get(11) * proj.get(12));
        clip.put(9, modl.get(8) * proj.get(1) + modl.get(9) * proj.get(5) + modl.get(10) * proj.get(9) + modl.get(11) * proj.get(13));
        clip.put(10, modl.get(8) * proj.get(2) + modl.get(9) * proj.get(6) + modl.get(10) * proj.get(10) + modl.get(11) * proj.get(14));
        clip.put(11, modl.get(8) * proj.get(3) + modl.get(9) * proj.get(7) + modl.get(10) * proj.get(11) + modl.get(11) * proj.get(15));

        clip.put(12, modl.get(12) * proj.get(0) + modl.get(13) * proj.get(4) + modl.get(14) * proj.get(8) + modl.get(15) * proj.get(12));
        clip.put(13, modl.get(12) * proj.get(1) + modl.get(13) * proj.get(5) + modl.get(14) * proj.get(9) + modl.get(15) * proj.get(13));
        clip.put(14, modl.get(12) * proj.get(2) + modl.get(13) * proj.get(6) + modl.get(14) * proj.get(10) + modl.get(15) * proj.get(14));
        clip.put(15, modl.get(12) * proj.get(3) + modl.get(13) * proj.get(7) + modl.get(14) * proj.get(11) + modl.get(15) * proj.get(15));

        // extract the numbers for the RIGHT plane
        frustums[0].setA(clip.get(3) - clip.get(0));
        frustums[0].setB(clip.get(7) - clip.get(4));
        frustums[0].setC(clip.get(11) - clip.get(8));
        frustums[0].setD(clip.get(15) - clip.get(12));
        frustums[0].normalise();

        // extract the numbers for the LEFT plane
        frustums[1].setA(clip.get(3) + clip.get(0));
        frustums[1].setB(clip.get(7) + clip.get(4));
        frustums[1].setC(clip.get(11) + clip.get(8));
        frustums[1].setD(clip.get(15) + clip.get(12));
        frustums[1].normalise();

        // extract the numbers for the BOTTOM plane
        frustums[2].setA(clip.get(3) + clip.get(1));
        frustums[2].setB(clip.get(7) + clip.get(5));
        frustums[2].setC(clip.get(11) + clip.get(9));
        frustums[2].setD(clip.get(15) + clip.get(13));
        frustums[2].normalise();

        // extract the numbers for the TOP plane
        frustums[3].setA(clip.get(3) - clip.get(1));
        frustums[3].setB(clip.get(7) - clip.get(5));
        frustums[3].setC(clip.get(11) - clip.get(9));
        frustums[3].setD(clip.get(15) - clip.get(13));
        frustums[3].normalise();

        // extract the numbers for the FAR plane
        frustums[4].setA(clip.get(3) - clip.get(2));
        frustums[4].setB(clip.get(7) - clip.get(6));
        frustums[4].setC(clip.get(11) - clip.get(10));
        frustums[4].setD(clip.get(15) - clip.get(14));
        frustums[4].normalise();

        // extract the numbers for the NEAR plane
        frustums[5].setA(clip.get(3) + clip.get(2));
        frustums[5].setB(clip.get(7) + clip.get(6));
        frustums[5].setC(clip.get(11) + clip.get(10));
        frustums[5].setD(clip.get(15) + clip.get(14));
        frustums[5].normalise();
    } // method

    /**
     * <P>Draws lines along the axis of the current coordinate system.
     *
     */
    public void drawAxis() {
        if (axisListName == 0) { // create display list
            axisListName = GL11.glGenLists(1);
            GL11.glNewList(axisListName, GL11.GL_COMPILE);
                GL11.glEnable(GL11.GL_LINE_STIPPLE);
                GL11.glBegin(GL11.GL_LINES);
                    GL11.glColor3f(1, 0, 0); // color red
                    GL11.glVertex3f(0, 0, 0); //  draw line along x
                    GL11.glVertex3f(100, 0, 0);
                    GL11.glColor3f(0, 1, 0); // color green
                    GL11.glVertex3f(0, 0, 0); // draw line along y
                    GL11.glVertex3f(0, 100, 0);
                    GL11.glColor3f(0, 0, 1); // color blue
                    GL11.glVertex3f(0, 0, 0); // draw line along z
                    GL11.glVertex3f(0, 0, 100);
                GL11.glEnd();
                GL11.glDisable(GL11.GL_LINE_STIPPLE);
            GL11.glEndList();
        } else { // call display list
            GL11.glCallList(axisListName);
        } // if else
    } // method
    
} // class

appel

THANKS! :)

You dont happen to have the Frustrum class also? =)

ndhb

Frustums are basicly just planar equations for the planes that lie on the visible edges of the camera. Here:

package dk.nesos.view.camera;

/**
 * A Frustum is a plane.
 *
 * @author ndhb
 *
 */
public final class Frustum {
    
    private float a;
    private float b;
    private float c;
    private float d;
    
    public Frustum() {
        // empty - default values works
    } // constructor
    
    /**
     * Constructs the Frustum defined by the equation ax + by + cz + d = 0.
     * 
     * @param a
     * @param b
     * @param c
     * @param d
     */
    public Frustum(float a, float b, float c, float d) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
    } // constructor

    /**
     * @return Returns the a.
     */
    public float getA() {
        return a;
    } // method

    /**
     * @param a The a to set.
     */
    public void setA(float a) {
        this.a = a;
    } // method

    /**
     * @return Returns the b.
     */
    public float getB() {
        return b;
    } // method

    /**
     * @param b The b to set.
     */
    public void setB(float b) {
        this.b = b;
    } // method

    /**
     * @return Returns the c.
     */
    public float getC() {
        return c;
    } // method

    /**
     * @param c The c to set.
     */
    public void setC(float c) {
        this.c = c;
    } // method

    /**
     * @return Returns the d.
     */
    public float getD() {
        return d;
    } // method

    /**
     * @param d The d to set.
     */
    public void setD(float d) {
        this.d = d;
    } // method
    
    /**
     * TODO NDHB: Description
     *
     */
    public void normalise() {
        float t = (float)Math.sqrt(a * a + b * b + c * c);
        a /= t;
        b /= t;
        c /= t;
        d /= t;
    } // method
    
} // class


You can use the frustums to perform visible frustum culling, that is, avoid rendering those objects that are not visible from the Camera. The general idea here, is to surround complex objects with a simple object. That simple object, is called a Bounding Volume and can be a box, sphere or something else (combination of boxes and/or spheres). Here's an example for a Bounding Sphere:

package dk.isle.utils;

import org.lwjgl.opengl.*;
import org.lwjgl.opengl.glu.*;
import org.lwjgl.util.vector.*;

/**
 * A BoundingSphere used for frustum culling (and perhaps collision detection).
 *
 * @author ndhb
 *
 */
public final class BoundingSphere implements Renderable {

    private Vector3f position;
    private float radius;
    private Sphere sphere;
    
    /**
     * Constructs a BoundingSphere at [0, 0, 0] with a radius of 1. 
     */
    public BoundingSphere() {
        this(new Vector3f(0, 0, 0), 1);
    } // constructor
    
    /**
     * Constructs a BoundingSphere at the specified 3D coordinates and radius.
     * 
     * @param cx coordinate
     * @param cy coordinate
     * @param cz coordinate
     * @param radius as a float value.
     */
    public BoundingSphere(float x, float y, float z, float radius) {
        this(new Vector3f(x, y, z), radius);
    } // constructor
    
    /**
     * Constructs a BoundingSphere at the specified 3D position and radius.
     * 
     * @param position in the 3D space.
     * @param radius as a float value.
     */
    public BoundingSphere(Vector3f location, float radius) {
        this.position = location;
        this.radius = radius;
        sphere = new Sphere(); // prepare object for drawing bounding volume
    } // constructor
    
    public Vector3f getPosition() {
        return position;
    } // method
    
    public void setPosition(Vector3f location) {
        this.position = location;
    } // method
    
    public float getRadius() {
        return radius;
    } // method

    public void setRadius(float radius) {
        this.radius = radius;
    } // method
    
    /**
     * <P>Checks if this sphere intersects the frustums.
     * 
     * <P>The sphere has a distance from each frustum. If the distance is positive it's in
     * front of the frustum and thus visible.
     * 
     * @param frustums
     * @return <code>true</code> if the sphere intersects the frustums, otherwise <code>false</code>.
     */
    public byte intersects(Frustum[] frustums) {
        int frustumsIntersecting = 0;
        float d = 0; 
        for (int f = 0; f < frustums.length; f++) {
            d = frustums[f].getA() * position.getX() + frustums[f].getB() * position.getY() + frustums[f].getC() * position.getZ() + frustums[f].getD();
            if (d <= -radius) {
                return NOT_VISIBLE;
            } // if
            if (d > radius) {
                frustumsIntersecting++; 
            } // if
        } // for all planes
       return (frustumsIntersecting == frustums.length ? VISIBLE : PARTIALLY_VISIBLE);
    } // method

    public void renderGL() {
        GL11.glColor4f(1f, 1f, 1f, 0.25f);
        GL11.glPushMatrix();
        GL11.glTranslatef(position.getX(), position.getY(), position.getZ());
        sphere.draw(radius, 8, 8); // ndhb - warning hard coded constant
        GL11.glPopMatrix();
    } // method
    
    public void initGL() {
        // empty
    } // method
    
    public void cleanupGL() {
        // empty
    } // method    
    
    public String toString() {
        return "BoundingSphere: " + position.toString() + ", radius: " + radius; 
    } // method
    
} // class


You dont need the Renderable interface, it just specifies that something Renderable, has methods initGL, renderGL and cleanupGL.

Usage is similar to:
if (myComplexObject.getBoundingSphere().intersects(myCamera.getFrustums()) != NOT_VISIBLE) {
  myComplexObject.draw()
}


... and so on.


ndhb

I just remembered to post, if someone has used this Camera class make sure you move this code:

FloatBuffer proj = BufferUtils.createFloatBuffer(16);
FloatBuffer modl = BufferUtils.createFloatBuffer(16);
FloatBuffer clip = BufferUtils.createFloatBuffer(16);


... outside the computeFrustum method (make them class members). It's a gigantic memory leak if Camera.refresh() is called frequently ;)