Here is the code from HeightMapMesh3D that builds the MeshGeometry3D:
Code:
_xCount = _heightData.GetUpperBound(0) + 1; // NOTE: This is not length but max element index (so lenght is GetUpperBound() + 1)
_yCount = _heightData.GetUpperBound(1) + 1;
double x_step = _size.X / ((double)(_xCount - 1));
double z_step = _size.Z / ((double)(_yCount - 1));
Point3DCollection positions = new Point3DCollection(_xCount * _yCount);
PointCollection textureCoordinates = new PointCollection(_xCount * _yCount);
Int32Collection triangleIndices = new Int32Collection((_xCount - 1) * (_yCount - 1) * 6);
double x_pos = _centerPosition.X - (_size.X / 2.0);
double yScale = _size.Y;
double yOffset = _centerPosition.Y; // if one value is 0, then it is positioned at the _centerPosition.Y (positive values are above center; negative values below center)
for (int x = 0; x < _xCount; x++)
{
double z_pos = _centerPosition.Z - (_size.Z / 2.0);
for (int y = 0; y < _yCount; y++)
{
positions.Add(new Point3D(x_pos, _heightData[x,y] * yScale + yOffset, z_pos));
z_pos += z_step;
}
x_pos += x_step;
}
double xCount1 = (double)(_xCount - 1);
double yCount1 = (double)(_yCount - 1);
for (int x = 0; x < _xCount; x++)
{
double xTexture = ((double)x) / xCount1;
for (int y = 0; y < _yCount; y++)
{
textureCoordinates.Add(new Point(xTexture, (double)y / yCount1));
}
}
for (int x = 0; x < _xCount - 1; x++)
{
for (int y = 0; y < _yCount - 1; y++)
{
if ((x + y) % 2 == 1)
{
triangleIndices.Add(y + (x * _yCount));
triangleIndices.Add((y + 1) + x * _yCount);
triangleIndices.Add((y) + ((x + 1) * _yCount));
triangleIndices.Add(y + ((x + 1) * _yCount));
triangleIndices.Add((y + 1) + ((x) * _yCount));
triangleIndices.Add((y + 1) + ((x + 1) * _yCount));
}
else
{
triangleIndices.Add(y + (x * _yCount));
triangleIndices.Add((y + 1) + x * _yCount);
triangleIndices.Add((y + 1) + ((x + 1) * _yCount));
triangleIndices.Add(y + (x * _yCount));
triangleIndices.Add((y + 1) + ((x + 1) * _yCount));
triangleIndices.Add(y + ((x + 1) * _yCount));
}
}
}
_geometry = new MeshGeometry3D();
_geometry.Positions = positions;
_geometry.TextureCoordinates = textureCoordinates;
_geometry.TriangleIndices = triangleIndices;
As you see the last pair of for loops build the triangles (2 for each quad of the grid).
In your case you would need to update the code in those 2 for loops to check the positions that form the quad. If all 4 positions are valid, the use the existing code. If all 4 are NaN, just skip the quad. If you have 3 valid positions, then you can define only that triangle.
If you would also like to define the edge to somehow "complete" the height map when it "touches" the NaN area, you may try to do something like this:
- Update all "edge positions" y values from Nan to double.MinValue (in positions collection) (or something similar but different from NaN) - edge positions are positions that are neighbors to a valid position (when checking 4 positions inside a for loop). Then form the triangles as with the MinValue positions would be valid. You will also need to adjust checking for valid values - MinValue should be considered as NaN - so you would not expand the edge - in case you have 2 MinValues and 2 NaN you should not create any triangle. Then after the 2 for loops add another for loop that will go through all positions and set double.MinValue to a value that you would like to have as a min value - for example 0.
I cannot say if WPF 3D will be able to render your height map with sufficient performance. You should try it. If this will not work well, then just add DXEngine to the scene and the DirectX 11 rendering engine should render it smoothly as butter.