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?
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.
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.
Another program takes this mesh and creates a height map from it with exactly the same dimensions as the height map for the terrain.
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.
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