HoloLens Terrain Generation Demo Part 13 – Setting the Size of Our Height Map

I’ve been putting off writing this post for a while now. The fixes to get the size working were really quite simple, so I don’t expect this is even a long post. I just haven’t felt like writing it.

So what exactly was wrong with setting the size of the terrain to match the surface?

The most obvious problem with the terrain in this image is where it goes wonky on the right side. It looks broken up in this image. Clearly, I must have made a mistake somewhere in the code setting up the triangles.
If we look at the code setting up our triangle indices, we see this:

for (auto i = 0u; i < h - 1; ++i) {
	for (auto j = 0u; j < w - 1; ++j) {
		auto index = j + (i * h);

		terrainIndices.push_back(index);
		terrainIndices.push_back(index + h + 1);
		terrainIndices.push_back(index + h);

		terrainIndices.push_back(index);
		terrainIndices.push_back(index + 1);
		terrainIndices.push_back(index + h + 1);
	}
}

It took me a couple of minutes to realize my mistake. In the inner for loop, where I use h as an offset into the array, I meant to use w. The reason for this offset is because we are storing our height map as a 1-dimensional array and need to convert to 2 dimensions, which means offsetting by the number of elements in a row (assuming we store the values sequentially by row). Unfortunately, h is the number of elements in a column, so we wind up accessing the wrong element when h and w differ.
Surprisingly, this seems to have been the only place I made this mistake. Other methods using the same logic were correctly using w. So it was a localized and easily corrected problem.

However, this did not fully fix the terrain.

In this image, I disabled all of the background imagery in order to hopefully make it easier to see the problem with the terrain. You should be able to see triangles along the left edge of the terrain crossing back to the right edge. This image is with a terrain with different height and width values.
To see if the issue only exists when the height and width are different, I set the dimensions to be the same again.

height = width = 0.5


height = width = 2.0


As you can see, with small values for the height and width, everything looks fine; but if we use a larger value, the terrain clearly isn’t square and, although it is difficult to see in this image, appears to have the same problem as when we use differing height and width values.

This turned out to be a problem with setting the resolution of the height map to be greater than one. My goal with height map resolution was to have a system where we could increase the triangle density. A resolution of 1 means that for every square centimeter we would have a 1×1 square, or 2 triangles. If we moved to a resolution of 2, we would have a 2×2 grid of squares resulting in 8 triangles. To confirm that our resolution code had something to do with the terrain mesh breaking, I set the resolution to 1 (I had previously been using 4 in my testing).

height and width are different.


That appears to confirm my suspicions. If I set resolution to 1, the terrain mesh generates perfectly fine, even with differing height and width values. But why? I implemented the resolution as a simple multiplier to the height and width of the height map. I couldn’t see any place where I had forgotten to add this multiplier in.

Admittedly, I didn’t bother trying to fix this. While looking something up, I found this article that talks about issues to be aware of with the HoloLens. It turns out that the HoloLens is optimized to display holograms at a distance of about 2 meters. Any closer, and viewing the holograms can cause discomfort. Microsoft’s own docs confirm this, although they do say that a static hologram like our terrain can be viewed relatively comfortably at as close as 50 centimeters. Even at this distance, however, I don’t think raising the resolution of the terrain would actually add any noticeable detail to the geometry. So my solution to resolution breaking the terrain wound up simply being to remove the resolution variable. Not the best fix, I know, but what is the point of having a feature that reduces performance without improving visuals?

So, with that issue ‘fixed’, we should be good on sizing our terrain to match the surface, right?

oops!


Oops indeed. Not to worry though. This is an easy one. To get the dimension of the surface plane, I created a method to return those dimensions.

float2 SurfacePlaneRenderer::GetDimensions() {
	auto plane = m_planeList[m_intersectedPlane];

	auto extents = plane.bounds.Extents;
	
	return float2(extents.x, extents.y);
}

The problem is that the Extents member of the plane bounds actually returns the distance from the center of the plane and not the actual dimensions. All I need to do to get the actual dimensions is double both the x and y components.

float2 SurfacePlaneRenderer::GetDimensions() {
	auto plane = m_planeList[m_intersectedPlane];

	auto extents = plane.bounds.Extents;
	
	return float2(extents.x * 2.0f, extents.y * 2.0f);
}

And with that final change, our terrain is now not only anchored and oriented to the selected surface, but it is also sized to match the dimensions of the plane.

I did try messing around with various variables in the Plane Finding algorithm to try and fix the plane of the bed so that it better matches, but none of the changes made any difference. Given that most surfaces in the default test levels work fine, I suspect the big problem here is simply the spatial mesh. It is a bit of a mess, after all. I don’t think I will be looking at simplifying the mesh in this project, but it would probably be a good idea for a future project.

So that’s everything for this post. Next time, I plan on fixing the intersection code for the terrain to work properly with the changes of the last two posts.

For the latest version of the code, see GitHub.
Traagen