2
\$\begingroup\$

I'm trying to implement terrain collision for my height map terrain, and I'm following this. The tutorial is for java but I'm using C++, though the principles are the same so it shouldn't be a problem.

To start off we need a function to get the height of the terrain based on the camera's position. WorldX and WorldZ is the camera's position (x, z) and heights is an 2D-array containing all the heights of the vertices.

float HeightMap::getHeightOfTerrain(float worldX, float worldZ, float heights[][256])
{   
    //Image is (256 x 256)
    float gridLength = 256;
    float terrainLength = 256;

    float terrainX = worldX;
    float terrainZ = worldZ;
    float gridSquareLength = terrainLength / ((float)gridLength - 1);
    int gridX = (int)std::floor(terrainX / gridSquareLength);
    int gridZ = (int)std::floor(terrainZ / gridSquareLength);

    //Check if position is on the terrain
    if (gridX >= gridLength - 1 || gridZ >= gridLength - 1 || gridX < 0 || gridZ < 0)
    {
        return 0;
    }

    //Find out where the player is on the grid square
    float xCoord = std::fmod(terrainX, gridSquareLength) / gridSquareLength;
    float zCoord = std::fmod(terrainZ, gridSquareLength) / gridSquareLength;
    float answer = 0.0;

    //Top triangle of a square else the bottom
    if (xCoord <= (1 - zCoord))
    {
        answer = barryCentric(glm::vec3(0, heights[gridX][gridZ], 0),
        glm::vec3(1, heights[gridX + 1][gridZ], 0), glm::vec3(0, heights[gridX][gridZ + 1], 1),
        glm::vec2(xCoord, zCoord));
    }

    else 
    {
        answer = barryCentric(glm::vec3(1, heights[gridX + 1][gridZ], 0),
        glm::vec3(1, heights[gridX + 1][gridZ + 1], 1), glm::vec3(0, heights[gridX][gridZ + 1], 1),
        glm::vec2(xCoord, zCoord));
    }

    return answer;
} 

To find the height of the triangle the camera is currently standing on we use the baryCentric interpolation function.

float HeightMap::barryCentric(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3, glm::vec2 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;
}

Then we just have to use the height we have calculated to check for collision during the game

float terrainHeight = heightMap.getHeightOfTerrain(camera.Position.x, camera.Position.z, heights);
    if (camera.Position.y < terrainHeight)
    {
        camera.Position.y = terrainHeight;
    };

Now according to the tutorial this should work perfectly fine, but the height is rather off and at some places it doesn't even work. I figured it might have something to do with the translation and scaling part of the terrain

    glm::mat4 model;
    model = glm::translate(model, glm::vec3(0.0f, -0.3f, -15.0f));
    model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));

and that I should multiply the values of the heights array by 0.1, as the scaling does that part for the terrain on the GPU side, but that didn't do the trick.

Note

In the tutorial the first lines in the getHeightOfTerrain function says

float terrainX = worldX - x;
float terrainZ = worldZ - z;

where x and z is the world position of the terrain. This is done to get the player position relative to the terrain's position. I tried with the values from the translation part, but it doensn't work either. I changed these lines because it doesn't seem necessary.

Additional

This is the vector where I store all the vertices in the grid with their respective x,y and z-values.

std::vector<glm::vec3> heightVertices(NumVertices);

To get the 2D-array for the getHeightOfTerrain function, I simply do

for (int i = 0; i < 256; i++)
    {
        for (int j = 0; j < 256; j++)
        {
            heights[i][j] = heightVertices[i].y;
        }
    }

And to get all the vertices for the terrain I use a BMP-file and carry on like below. If there would a problem in the code I assume it has to do with the vertices of the terrain, so I'll just list that part

    FILE *filePtr;
    BITMAPFILEHEADER bitmapFileHeader;  // For file      
    BITMAPINFOHEADER bitmapInfoHeader;  // For image     
    int imageSize, index;
    unsigned char height; // Color values

    filePtr = fopen(filename, "rb");
    if (filePtr == NULL)
    {
        printf("Cannot open file! \n");
        getchar();
        return false;
    }

    fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);
    fread(&bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);

    terrainWidth = bitmapInfoHeader.biWidth;
    terrainHeight = bitmapInfoHeader.biHeight;
    imageSize = terrainWidth * terrainHeight * 3;

    // Initialize the array which stores the image data
    unsigned char* bitmapImage = new unsigned char[imageSize];
    fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);

    // Store image data in bitmapImage
    fread(bitmapImage, 1, imageSize, filePtr);
    fclose(filePtr);

    // Size of the image length and height (vertices of the terrain).
    heightMap = new glm::vec3[terrainWidth * terrainHeight];

    int k = 0;
    float heightFactor = 10.0f;

    // Read the image data into our heightMap array
    for (int j = 0; j < terrainHeight; j++)
    {
        for (int i = 0; i < terrainWidth; i++)
        {
            height = bitmapImage[k];
            index = (terrainHeight * j) + i;

            heightMap[index].x = (float)i;
            heightMap[index].y = (float)height / heightFactor;
            heightMap[index].z = (float)j;

            k += 3;
        }
    }

    delete[] bitmapImage;
    bitmapImage = 0;

    // Create vertices of the grid
    int cols = terrainWidth;
    int rows = terrainHeight;

    // Create the grid
    NumVertices = rows * cols;
    NumFaces = (rows - 1)*(cols - 1) * 2;

    std::vector<glm::vec3> heightVertices(NumVertices);


    for (int i = 0; i < rows; ++i)
    {
        for (int j = 0; j < cols; ++j)
        {
            heightVertices[i*cols + j] = heightMap[i*cols + j];
        }
    }
\$\endgroup\$
0
\$\begingroup\$

You are offsetting the model as well as scaling its x and z coordinates, which I assume is why this is failing for you.

You really aren't providing enough information to help you more than that, since we don't know how your interface between the terrain and the rest of the game looks like.

I'd assume that if you account for the offset and the scaling along the x and z axis it will work unless you have another problem in your code.

\$\endgroup\$
  • \$\begingroup\$ I tried removing the translation and scaling part and then place the camera above the terrain, it works on some places and give weird results on other, so I guess that solution didn't work. Everything worked fine before I added the collision part, so I don't know what could possibly be wrong. It's simply a height map with vertices, uvs and normals defined. I could add the code where I get the values for the array, because I use a vector for the vertices from the beginning (if that would be a problem). \$\endgroup\$ – Riggs Jul 23 '16 at 18:47
  • \$\begingroup\$ The code at the top for finding the height should work if all the surrounding structures are doing their job. \$\endgroup\$ – Nils Ole Timm Jul 24 '16 at 4:49
  • \$\begingroup\$ You should see this as an opportunity to learn how to debug stuff. Sit down and manually calculate the height at some specific point, then compare the results of your steps to the results you get from your algorithm and see where it goes wrong. So that you can trace back to where the original error is. From your edit it seems like you are just dumping extra code without really thinking about what it means. \$\endgroup\$ – Nils Ole Timm Jul 24 '16 at 4:51
  • \$\begingroup\$ Mhm, you're right. I have been sitting with this for a while so I guess I'm pretty tired of it. Thanks @Nils Ole Timm \$\endgroup\$ – Riggs Jul 24 '16 at 7:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.