Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports Microsoft.DirectX
Imports Microsoft.DirectX.Direct3D
Public Class Terrain
Private device As Device
Private decl As VertexDeclaration
Private vb As VertexBuffer
Private ib As IndexBuffer
Private numvertices As Integer, numindices As Integer, numtriangles As Integer
Private heights(,) As Single
Private dx As Single, dy As Single
Private Created As Boolean = False
Private numWidth As Integer, numHeight As Integer
Public Shared size As Single = 150.0F
Private normallist As Vector3()
Public renderNormals As Boolean = False
'public unsafe Visor( Device d, float Min, float Max)
Public Sub New(ByVal d As Device, ByVal Min As Single, ByVal Max As Single)
device = d
' Load the heightmap
Dim b As Bitmap = New Bitmap("..\..\heightmap.bmp")
' Store the number of pixels
Dim numWidth As Integer = b.Width
Dim numHeight As Integer = b.Height
' Create an array of floats
ReDim heights(numWidth, numHeight)
' Lock the data from the bitmap so it can be accessed unsafely
Dim data As BitmapData
data = b.LockBits(New Rectangle(0, 0, numWidth, numHeight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb)
' Assigh the address of the first pixel to the pointer
Dim i, j As Integer
Dim p As IntPtr
p = data.Scan0
Dim Arr(0) As Byte
Dim lowest As Integer = 255
Dim highest As Integer
For i = 0 To numWidth - 1
For j = 0 To numHeight - 1
System.Runtime.InteropServices.Marshal.Copy(p, Arr, 0, 1)
If Arr(0) < lowest Then
lowest = Arr(0)
End If
If Arr(0) > highest Then
highest = Arr(0)
End If
p = New IntPtr(p.ToInt32 + 3)
Next
Next
p = data.Scan0
For i = 0 To numWidth - 1
For j = 0 To numHeight - 1
System.Runtime.InteropServices.Marshal.Copy(p, Arr, 0, 1)
heights(i, j) = CType((Arr(0) - lowest) / ((highest - lowest)) * (Max - Min) + Min, Single)
p = New IntPtr(p.ToInt32 + 3)
Next
Next
b.UnlockBits(data)
numvertices = numWidth * numHeight
numindices = 6 * (numWidth - 1) * (numHeight - 1)
numtriangles = 2 * (numWidth - 1) * (numHeight - 1)
' Create on array of vertices
Dim verts() As Vertex
ReDim verts(numvertices)
' Create on array of indices
Dim ind() As Integer
ReDim ind(numindices)
Dim n, x As Integer
dx = size / numWidth
dy = size / numWidth
For i = 0 To numHeight - 1
For j = 0 To numWidth - 1
' Calculate the blend value by dividing the height by the maximal height
Dim Blend As Single = (heights(j, i) - Min) / (Max - Min)
' Normals will be calculated after this so assign a upward normal for now
verts(i * numWidth + j) = New Vertex(New Vector3(j * dx - size / 2.0F, _
heights(j, i), _
i * dy - size / 2.0F), _
New Vector3(0, 1, 0), _
CType(j / numWidth * size / 16.0F, Single), _
CType(i / numHeight * size / 16.0F, Single), _
1 - Blend, Blend, 0, 0, True)
Next
Next
Dim x2, Normal, z As Vector3
For i = 1 To numHeight - 2
For j = 1 To numWidth - 2
' Calculate the real normals by using the cross product of the vertex' neighbours
x2 = Vector3.Subtract(verts(i * numWidth + j + 1).Position, verts(i * numWidth + j - 1).Position)
z = Vector3.Subtract(verts((i + 1) * numWidth + j).Position, verts((i - 1) * numWidth + j).Position)
Normal = Vector3.Cross(z, x2)
Normal.Normalize()
verts(i * numWidth + j).Normal = Normal
Next
Next
' Create on array of Vector3's for each vertex storing the position and the normal added to the position so the normals can be rendered
ReDim normallist(numvertices * 2)
For i = 0 To numvertices - 1
normallist(2 * i) = verts(i).Position
normallist(2 * i + 1) = Vector3.Add(verts(i).Position, Vector3.Multiply(verts(i).Normal, 3))
Next
' Fill the array of indices
For i = 0 To numWidth - 2
For j = 0 To numHeight - 2
x = i * numWidth + j
n += 1
ind(n) = x
ind(n) = x + 1
ind(n) = x
ind(n) = x + numWidth + 1
ind(n) = x
ind(n) = x
ind(n) = x
ind(n) = x + numWidth
ind(n) = x
ind(n) = x + numWidth + 1
Next
Next
' Create the vertex- and indexbuffer and set their data
vb = New VertexBuffer(GetType(Vertex), numvertices, device, Usage.None, Vertex.Format, Pool.Default)
vb.SetData(verts, 0, 0)
ib = New IndexBuffer(GetType(Integer), numindices, device, 0, 0)
ib.SetData(ind, 0, 0)
' Create the vertexdeclaration
Dim v() As VertexElement = {New VertexElement(0, 0, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Position, 0), _
New VertexElement(0, 12, DeclarationType.Float3, DeclarationMethod.Default, DeclarationUsage.Normal, 0), _
New VertexElement(0, 24, DeclarationType.Float2, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 0), _
New VertexElement(0, 32, DeclarationType.Float4, DeclarationMethod.Default, DeclarationUsage.TextureCoordinate, 1), _
VertexElement.VertexDeclarationEnd}
decl = New VertexDeclaration(device, v)
End Sub
Public Sub Draw()
' Set the VertexDeclaration, StreamSource and Indices
device.VertexDeclaration = decl
device.SetStreamSource(0, vb, 0)
device.Indices = ib
' Draw the terrain
device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, numvertices, 0, numtriangles)
If Not renderNormals Then
Return
End If
' Render the normal list
Using l As New Line(device)
l.Antialias = True
l.Width = 2
l.Begin()
For i As Integer = 0 To normallist.Length - 2 Step 2
l.DrawTransform(New Vector3() {normallist(i), normallist(i + 1)}, Form1.viewMatrix * Form1.projMatrix, Color.Red)
Next
l.End()
End Using
End Sub
End Class