Search code examples
javalwjglmodelscollisiondetection

Java 3D LWJGL collision


I am making a 3D Java game with the LWJGL library, and I was wondering how to add collision detection, so that the player does not go through models.

I am using OBJ models. Here is the OBJLoader class, which loads the models:

package renderEngine;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import models.RawModel;

import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

public class OBJLoader {


    public static RawModel loadObjModel(String fileName, Loader loader){
        FileReader fr = null;
        try {
            fr = new FileReader(new File("res/"+fileName+".obj"));
        } catch (FileNotFoundException e) {
            System.err.println("Couldn't load file!");
            e.printStackTrace();
        }
        BufferedReader reader = new BufferedReader(fr);
        String line;
        List<Vector3f> vertices = new ArrayList<Vector3f>();
        List<Vector2f> textures = new ArrayList<Vector2f>();
        List<Vector3f> normals = new ArrayList<Vector3f>();
        List<Integer> indices = new ArrayList<Integer>();
        float[] verticesArray = null;
        float[] normalsArray = null;
        float[] textureArray = null;
        int[] indicesArray = null;
        try{
            while(true){
                line = reader.readLine();
                String[] currentLine = line.split(" ");
                if(line.startsWith("v ")){
                    Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]), 
                            Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
                    vertices.add(vertex);
                }else if(line.startsWith("vt ")){
                    Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]), 
                            Float.parseFloat(currentLine[2]));
                    textures.add(texture);
                }else if(line.startsWith("vn ")){
                    Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]), 
                            Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
                    normals.add(normal);
                }else if(line.startsWith("f ")){
                    textureArray = new float[vertices.size() * 2];
                    normalsArray = new float[vertices.size() * 3];
                    break;
                }
            }

            while(line != null){
                if(!line.startsWith("f ")){
                    line = reader.readLine();
                    continue;
                }
                String[] currentLine = line.split(" ");
                String[] vertex1 = currentLine[1].split("/");
                String[] vertex2 = currentLine[2].split("/");
                String[] vertex3 = currentLine[3].split("/");

                processVertex(vertex1, indices, textures, normals, textureArray, normalsArray);
                processVertex(vertex2, indices, textures, normals, textureArray, normalsArray);
                processVertex(vertex3, indices, textures, normals, textureArray, normalsArray);
                line = reader.readLine();
            }

            reader.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        verticesArray = new float[vertices.size()*3];
        indicesArray = new int[indices.size()];


        int vertexPointer = 0;
        for(Vector3f vertex:vertices){
            verticesArray[vertexPointer++] = vertex.x;
            verticesArray[vertexPointer++] = vertex.y;
            verticesArray[vertexPointer++] = vertex.z;
        }
        for(int i=0;i<indices.size(); i++){
            indicesArray[i] = indices.get(i);
        }
        return loader.loadToVAO(verticesArray, textureArray, normalsArray, indicesArray);
    }

    private static void processVertex(String[] vertexData, List<Integer> indices, 
            List<Vector2f> textures, 
            List<Vector3f> normals, float[] textureArray, float[] normalsArray){

        int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
        indices.add(currentVertexPointer);
        Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
        textureArray[currentVertexPointer*2] = currentTex.x;
        textureArray[currentVertexPointer*2+1] = 1 - currentTex.y;
        Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2])-1);
        normalsArray[currentVertexPointer*3] = currentNorm.x;
        normalsArray[currentVertexPointer*3+1] = currentNorm.y;
        normalsArray[currentVertexPointer*3+2] = currentNorm.z;
    }

}

Thanks!


Solution

  • You have a few options here. The easiest is to simply create an axis-aligned bounding box (AABB) around the object; this can be done algorithmically by simply finding the minimum and maximum values for each axis. For some applications this will work fine, but this obviously isn't very precise. It's worth noting, though, that if two AABBs do not intersect, then the objects themselves definitely don't intersect either; you can use this fact as an early-exit for your collision-checking algorithm.

    In addition to bounding boxes, some game engines employ other basic types of bounding volumes, such as bounding spheres. Detecting if a point is in a sphere is as simple as checking if the distance between the center of the sphere and the point is less than or equal to the radius of the sphere. Investigate the bounding volume Wikipedia page for other types. You can often approximate the true boundaries of your object by creating compound bounding volumes -- that is, bounding volumes composed of several simpler volumes, like spheres, boxes, cylinders, etc. This is how the Unity game engine handles collision detection.

    You might also want to investigate 2D collision detection algorithms, like the separating axis theorem example (see further reading section). These algorithms can almost always be scaled to higher dimensions.

    If all this seems too complicated, I'd recommend picking up a prebaked solution, such as Bullet (Java port). Google around a bit; you'll probably find something that fits your use case. Don't feel bad if you feel overwhelmed; collision detection is a complicated subject backed by decades of research.

    Further reading: