Ethan Ma
Ethan Ma

Reputation: 89

How to calculate if 3D model is intersecting terrain surface LWJGL

I am currently working on a project using 3d simplex noise and the marching cubes algorithm in order to procedurally generated terrain. I am trying to implement collision detection between the player object and terrain mesh but I have no clue how to start. I have read some articles and posts about using JBullet and other libraries but they do not support complex meshes such as the ones generated by simplex noise. In order to simplify things for myself I decided to make it so that the player can only move in the direction I point meaning that I would only need to check if a singular point on the player is intersecting with the terrain. Are there any methods to implement such a process? (edit: I've already looked into barycentric coordinates but I have no idea how to implement into the game)

Current Player Code

package Entities;

import org.lwjgl.glfw.GLFW;

import Engine.Input;
import Maths.Vector3f;
import Models.TexturedModel;

public class Player extends Entity {
    
    public float xspeed = 0,zspeed = 0, yspeed = 0; 
    public Vector3f mousePos;
    public float yrotation = 0, zrotation = 0;
    public float maxYRotation = 75f;
    
    private double lastMousePosX = 600 , newMousePosX;
    private double lastMousePosY = 500 , newMousePosY;
    private float speed = 3;
    
    public Player(TexturedModel model, Vector3f position, float rotX, float rotY, float rotZ, float scale) {
        super(model, position, rotX, rotY, rotZ, scale);
    }
    public void move(){
        checkInput();
        System.out.println("x ="+this.position.x+" y ="+this.position.y+" z ="+this.position.z);
        checkCollision();
    }
    public boolean checkCollision(){
        if(terrain != null){
            for(int i = 0; i<terrain.getVertices().length; i+=9){
                Vector3f vertex1 = new Vector3f(terrain.getVertices()[i],terrain.getVertices()[i+1],terrain.getVertices()[i+2]);
                Vector3f vertex2 = new Vector3f(terrain.getVertices()[i+3],terrain.getVertices()[i+4],terrain.getVertices()[i+5]);
                Vector3f vertex3 = new Vector3f(terrain.getVertices()[i+6],terrain.getVertices()[i+7],terrain.getVertices()[i+8]);
                
                //Check if point p is interseting triangle (vertex1, vertex2, vertex3)
                if(someCalculationFunction(position, vertex1, vertex2, vertex3){
                    return true;
                }
            }
        }
        return false;
    }

    public void checkInput(){
        newMousePosX = Input.getMouseX();
        newMousePosY = Input.getMouseY();
        
        float dx = (float)(newMousePosX-lastMousePosX)*0.07f;
        float dy = (float)(newMousePosY-lastMousePosY)*0.07f;
        
        if(!Input.isMouseDown(GLFW.GLFW_MOUSE_BUTTON_1)){

            this.rotY -= dx/2;
            this.rotX -= dy*0.8f;
        
        }
        
        if(Math.abs(rotX) > 50){
            this.rotX = Math.abs(rotX)/rotX*50;
        }
        
        if(this.rotY<0){
            this.rotY = 360;
        }
        
        float horizontalDistance = speed*(float)(Math.cos(Math.toRadians(rotX)));
        float verticleDistance = speed*(float)(Math.sin(Math.toRadians(rotX)));

        if(Input.isKeyDown(GLFW.GLFW_KEY_W)){
            this.position.x += horizontalDistance*Math.sin(Math.toRadians(-rotY));
            this.position.z -= horizontalDistance*Math.cos(Math.toRadians(-rotY));
            this.position.y += verticleDistance;
        }else if(Input.isKeyDown(GLFW.GLFW_KEY_S)){
            this.position.x -= horizontalDistance*Math.sin(Math.toRadians(-rotY));
            this.position.z += horizontalDistance*Math.cos(Math.toRadians(-rotY));
            this.position.y -= verticleDistance;    
        }
        
        lastMousePosX = newMousePosX;
        lastMousePosY = newMousePosY;       
        
    }

}

Upvotes: 0

Views: 101

Answers (1)

Ryan
Ryan

Reputation: 1974

I'm not positive if I understood the question right, but this answer will address the problem of ensuring the players height is that of the terrain that it is standing on

With barycentric coordinates you can calculate what the players height is supposed to be by using the heights of the three vertices that make up that triangle:

public static float baryCentric(Vector3f p1, Vector3f p2, Vector3f p3, Vector2f pos) {
    float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
    float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
    float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
    float l3 = 1.0f - l1 - l2;
    return l1 * p1.y + l2 * p2.y + l3 * p3.y;
}

In order to get these three points you can make a calculation using the world coordinates of your player:

    //Assuming the world is constructed of equal height and width sized triangles
    float gridSquareSize = SIZE_OF_TERRAIN_MESH / NUM_TRIANGLES_PER_ROW;
    float xCoord = worldX % gridSquareSize / gridSquareSize;
    float zCoord = worldZ % gridSquareSize / gridSquareSize;

With the xCoord and zCoord you can determine the 3 points that you need to use for your baryCentric calculation

Upvotes: 1

Related Questions