Multiple height levels

posted in trill41 for project ABx
Published November 21, 2020
Advertisement

In games with a physics engine, there is a constant force pulling an Actor down until it collides with the terrain or some other object (e.g. stairs or a bridge) -- the gravity. That's how actors are kept on the ground in these games.

ABx does not have a physics engine and therefore there is no force pulling actors down to the ground. Instead it uses height maps to determine the Y value for each possible X,Z values. Height maps are simple arrays of floats (the height value) with a size (width and height). This works great for simple terrains, but what if there are buildings an actor can step on, like a bridge?

Source scene

ABx uses now two height maps (in principle it could use more, but two are sufficient for now), one for the terrain and the second for static game objects.

Terrain

The lower level is still generated from an PNG image. The result is an array of floats with the size of image width times image height.

Height map PNG image

Static objects

The second level is generated from the scenes static objects, like buildings. A program extracts these objects from the scene and creates a 3D mesh from it.

Obstacle mesh

Another program takes this mesh and creates a height map from it with exactly the same dimensions as the height map for the terrain.

Obstacle height map

The challenge here was to create two compatible height maps, with exactly the same dimensions and scaling from totally different sources, one PNG (which may be scaled, rotated and translated) image, and a 3D mesh in world coordinates (which is already transformed). These two different sources must be squashed into two one dimensional float arrays with exactly the same number of elements, the height values.

The combined height map can be imagined like the image bellow. The red layer is the terrain (the PNG image) and the green layer are the buildings.

Merged height maps

Get the actual height value

To get the actual height value, I use a relatively naive approach, but it works for now:

float Terrain::GetHeight(const Math::Vector3& world) const
{
    if (!heightMap_)
        return 0.0f;
    if (matrixDirty_)
    {
        const Math::Matrix4 matrix = transformation_.GetMatrix();
        heightMap_->SetMatrix(matrix);
        if (heightMap2_)
            heightMap2_->SetMatrix(matrix);
        matrixDirty_ = false;
    }

    float result = heightMap_->GetHeight(world);
    if (!heightMap2_)
        return result;

    float result2 = heightMap2_->GetHeight(world);
    // Layer2 must be above layer1
    if (Math::IsNegInfinite(result2) || result2 < result)
        return result;

    // If the difference is smaller than the height of the character it can't be the lower height
    float diff12 = result2 - result;
    if (diff12 < 1.7f)
        return result2;

    // Otherwise use the closer value to the current height
    if (fabs(world.y_ - result) < fabs(world.y_ - result2))
        return result;
    return result2;
}

Demonstration

There is a short video demonstrating this: https://devtube.dev-wiki.de/videos/watch/7ccce3db-08e7-4d8b-8ade-399ce5d7d4ac

Previous Entry New navigation mesh
Next Entry Animations
0 likes 0 comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement

Latest Entries

Advertisement