Search code examples
java3dcollision-detectionlwjgl

Java LWJGL - Collision detection on 3D models?


I am on the long road of developing my own little mmorpg game using LWJGL. So far I have imported a bunny into my game and sorta just positioned him above the grass. Like this:

bunny placed above grass

What I want to do is have my objects in the game able to interact with. For example clicking on them would bring up a little menu of options, or make it so when you run into them something happens. For this I need collision detection. I have worked with it before on 2D games which is much simpler.

Here is my Bunny.java class:

public class Bunny {


public static Model m = null;
public static int displayList;

public static void init() {

    displayList = glGenLists(1);
    glNewList(displayList, GL_COMPILE);
    {
        try {
            m = OBJLoader.loadModel(new File("res/models/bunny.obj"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Display.destroy();
            System.exit(1);
        } catch (IOException e) {
            e.printStackTrace();
            Display.destroy();
            System.exit(1);
        }

        glBegin(GL_TRIANGLES);

        for (Face face : m.faces) {
            Vector3f n1 = m.normals.get((int) face.normal.x - 1);
            glNormal3f(n1.x, n1.y, n1.z);
            Vector3f v1 = m.vertices.get((int) face.vertex.x - 1);
            glVertex3f(v1.x, v1.y, v1.z);
            Vector3f n2 = m.normals.get((int) face.normal.y - 1);
            glNormal3f(n2.x, n2.y, n2.z);
            Vector3f v2 = m.vertices.get((int) face.vertex.y - 1);
            glVertex3f(v2.x, v2.y, v2.z);
            Vector3f n3 = m.normals.get((int) face.normal.z - 1);
            glNormal3f(n3.x, n3.y, n3.z);
            Vector3f v3 = m.vertices.get((int) face.vertex.z - 1);
            glVertex3f(v3.x, v3.y, v3.z);
        }
        glEnd();

    }
    glEndList();

}

public static void draw(float x, float y, float z) {
    glPushMatrix();
    glTranslatef(x, y, z);
    glCallList(displayList);
    glPopMatrix();
}

}

And my Main class:

public class Main {
public static float PlayerX = 0;
public static float PlayerY = 0;
public static float PlayerZ = 0;
public static float walkSpeed = 0.016f;
public static float PlayerRot = 0;

private static float[] lightPosition = {-2.19f, 1.36f, 11.45f, 1f};

public float pCenterX = 0;
public float pCenterY = 0;
public float pCenterZ = 0;

public static Model m = null;

public static void main(String[] args) {


    initDisplay();
    Bunny.init();
    gameLoop();
    cleanUp();
}


public static void gameLoop() {
    Texture tex = loadTexture("tex", "png");    
    Texture wood = loadTexture("wood", "png");

    loadLight();

    Camera camera = new Camera(70, (float) Display.getWidth() / (float) Display.getHeight(), 0.3f, 1000);

    while (!Display.isCloseRequested()) {



        if(Keyboard.isKeyDown(Keyboard.KEY_W)) camera.move(walkSpeed, 1);
        if(Keyboard.isKeyDown(Keyboard.KEY_S)) camera.move(-walkSpeed, 1);
        if(Keyboard.isKeyDown(Keyboard.KEY_A)) camera.rotateY(-0.1f);
        if(Keyboard.isKeyDown(Keyboard.KEY_D)) camera.rotateY(0.1f);
        if(Keyboard.isKeyDown(Keyboard.KEY_SPACE)) camera.moveY(0.05f, 1);
        if(Keyboard.isKeyDown(Keyboard.KEY_Z)) camera.moveY(0.05f, 0);


        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


//      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        glTranslatef(0, 2.5f, 0);

        glLoadIdentity();

        //camera.setX(-PlayerX); camera.setY(-PlayerY - 3); camera.setZ(-PlayerZ - 10);

        glLight(GL_LIGHT0, GL_POSITION, asFloatBuffer(new float[] {-2.19f, 1.36f, 11.45f, 1f}));

        camera.useView();

        //Draw.rect(PlayerX, PlayerY, PlayerZ, 0.5f, 0.5f, 0.5f, tex);
        Draw.rect(1, 0, 1, 100, 0.5f, 100, wood);

        Bunny.draw(10, 2.5f, 10);
        Bunny.draw(50, 2.5f, 50);

        Display.update();

    }
}


public static void cleanUp() {
    Display.destroy();
}

public static void initDisplay() {
    try {
        Display.setDisplayMode(new DisplayMode(800, 600));
        Display.create();
    } catch (LWJGLException e) {
        e.printStackTrace();
    }

}


public static Texture loadTexture(String key, String fileType) {

    try {
        return TextureLoader.getTexture(fileType, new FileInputStream(new File("res/img/" + key + "." + fileType)));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;

}

private static FloatBuffer asFloatBuffer(float[] values) {
    FloatBuffer buffer = BufferUtils.createFloatBuffer(values.length);
    buffer.put(values);
    buffer.flip();
    return buffer;
}

public static void loadLight() {

    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightModel(GL_LIGHT_MODEL_AMBIENT, asFloatBuffer(new float[] {0.05f, 0.05f, 0.05f, 1f}));
    glLight(GL_LIGHT0, GL_DIFFUSE, asFloatBuffer(new float[] {1.5f, 1.5f, 1.5f, 1f}));
    //glEnable(GL_CULL_FACE);
    //glCullFace(GL_BACK);
    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT, GL_DIFFUSE);

}
}

How can I get the points of the bunny, and use those in an algorithm to detect when we have collided?


Solution

  • You already showed the code for the vertices in the first snippet. So you have them.

    As for the second part of the question: What you are looking for is collision detection and picking. Covering them in an answer is just too much, so you will have to look them up.

    One way to do this is a scene graph.

    • Each object gets a bounding box that encompasses all of its points
    • the object in linked into a scene graph by connecting it to a node
    • each node gets a bounding box that encompasses all boxes of its children

    This gives you a treelike structure. Now you just check if a collision has happened by testing the bounding boxes of a node against the collision partner. If it hits you check its subnodes and objects for collision. If you have a hit for an object you do another check based on actual model data.

    For normal objects you use their bounding boxes and data for the check. For picking you use a ray fired from the in-world coordinates of your mouse click.

    This is just on way to do this, but there are a lot more shades to the topic. For example OpenGL offers its own picking support.