Проблемы со столкновением с землей

Я пытаюсь реализовать столкновение с землей для своей карты высот и следую этому. Учебник предназначен для java, но я использую C++, хотя принципы те же, так что это не должно быть проблемой.

Для начала нам нужна функция для получения высоты ландшафта в зависимости от положения камеры. WorldX и WorldZ — это положение камеры (x, z), а heights — это 2D-массив, содержащий все высоты вершин.

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;
} 

Чтобы найти высоту треугольника, на котором в данный момент стоит камера, мы используем функцию интерполяции baryCentric.

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;
}

Затем нам просто нужно использовать вычисленную нами высоту для проверки столкновения во время игры.

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

Теперь, согласно учебнику, это должно работать отлично, но высота довольно неправильная, а в некоторых местах это даже не работает. Я подумал, что это может быть как-то связано с переводом и масштабированием части ландшафта.

    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));

и что я должен умножить значения массива высот на 0,1, так как масштабирование делает эту часть для ландшафта на стороне графического процессора, но это не помогло.

Примечание

В учебнике первые строки в функции getHeightOfTerrain говорят

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

где x и z - мировое положение местности. Это делается для того, чтобы получить положение игрока относительно положения местности. Я пытался со значениями из части перевода, но это тоже не работает. Я изменил эти строки, потому что это не кажется необходимым.


person Riggs    schedule 23.07.2016    source источник


Ответы (1)


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

Эти линии на самом деле очень необходимы, если только ваш ландшафт всегда не находится в начале координат.

Ваш ресурс кода (учебник) предполагает, что вы не масштабировали и не поворачивали ландшафт каким-либо образом. Переменные x и z представляют собой координаты местности по оси XZ, которые учитывают случаи перемещения местности.

В идеале вы должны преобразовать вектор мирового положения из мирового пространства в пространство объектов (используя обратную матрицу model, которую вы используете для ландшафта), что-то вроде

vec3 localPosition = inverse(model) * vec4(worldPosition, 1)

А затем используйте localPosition.x и localPosition.z вместо terrainX и terrainZ.

person EvilTak    schedule 24.07.2016
comment
Понятно, теперь я не знаю, звучит ли это глупо, но как я могу найти вектор Worldposition, так как у меня есть только вектор перевода, который смещает локальное положение в пространстве, но я не знаю локальное положение в пространстве, потому что оно отличается для каждой вершины, а графический процессор смещает каждую вершину индивидуально с помощью этого вектора перевода? Если вы понимаете, что я имею в виду? @Злой Так - person Riggs; 24.07.2016
comment
Вам нужна только позиция в мировом пространстве нижнего левого угла (при условии, что оси X, Y и Z указывают вправо, вверх и вперед соответственно) местности. - person EvilTak; 24.07.2016
comment
Что можно найти, проверив первый индекс вектора со всеми вершинами и затем переведя его: (0,0, 0,89, 0,0) + (0,0, -0,3, -15,0) = (0,0, 0,59, -15,0)? (Если я не буду масштабировать) @Evil Tak - person Riggs; 24.07.2016
comment
да. Не переводите его, а преобразуйте (умножьте) на матрицу model. Вы также должны хранить ландшафт как объект (точно так же, как вы/учебник делаете с камерой) с общедоступными свойствами для таких вещей, как положение, вращение и масштаб. Когда вы визуализируете ландшафт, убедитесь, что нижний левый угол визуализируется в нужном месте. Нижний левый угол должен быть центром ландшафта. Вы также можете заставить его работать, сохраняя положение в мировом пространстве в качестве центра ландшафта, но для этого вам потребуется немного изменить код проверки диапазона. - person EvilTak; 24.07.2016
comment
Оно работает! Спасибо за помощь. Я отмечу ваш пост как ответ. @Злой Так - person Riggs; 24.07.2016