Code archives/3D Graphics - Mesh/minib3d - converted terrain functions

This code has been declared by its author to be Public Domain code.

Download source code

minib3d - converted terrain functions by Warner2008
A more B3D like terrain can be found here:
http://www.blitzmax.com/Community/posts.php?topic=85741
I couldn't post it in the archives, because it has a certain license.

Functions:
  LoadTerrain:TTerrain(filename$,RoamMaxHeightError,MaxTileDepth)
  TerrainY#(terrain:TTerrain, x#, z#)

Update: this is a conversion of a Russian tutorial I found online.
It uses ROAM to optimize a terrain. When areas need a bigger precision, more triangles are generated at that location.

You can supply two optional paramers:
1. a cutoff value for terrain steepness
2. a cutoff value for triangle splitting (depth level)

Basically, it starts with two big triangles and subdivides them on areas where that's needed. The first parameter determines where it splits and the second one determines how much levels of splitting is allowed.

I tried to include routines that autodetect the right setting. If you omit the first parameter, the autodetection takes place.

The pixmap is automatically rescaled to 512x512 in the LoadTerrain function. The pixmap has to be square.

In the update I removed any global variables and I made it SuperStrict compatible. It is now also an OOP object.
Example:
	Include "terrain.bmx" 'the below file

	Local terrain:TMesh = LoadTerrain("heightmap.png")
	PositionEntity terrain, -256, 0, -256

terrain.bmx:
Type TTerrain Extends TMesh

	Field HeightMap#[512, 512]

'---------------------------------------------------------------------------------------------------------------
'													Create()
'---------------------------------------------------------------------------------------------------------------
	Method Create:TTerrain()
	
		Local m:TTerrain = New TTerrain
		class$ = "Terrain"
		
		AddParent(Null)
		EntityListAdd(TTerrain.entity_list)
			
		Return Self
	
	End Method
	
'---------------------------------------------------------------------------------------------------------------
'													MakeTerrainMesh()
'---------------------------------------------------------------------------------------------------------------
	Method MakeTerrainMesh(HmapImage:TPixmap, RoamMaxHeightError#=-1, MaxTileDepth%)
		
		If HmapImage = Null Then Return
			
		'clear array
		For Local i:Int = 0 To 511
		For Local j:Int = 0 To 511
			HeightMap[i,j]=0
		Next
		Next
		
		Local RoamTerrainWidth%=Max(PixmapWidth(HmapImage),PixmapHeight(HmapImage))-1
		Local RoamTerrainHeight#[RoamTerrainWidth+1+1,RoamTerrainWidth+1+1]
	
		Local maxv# =  0
		Local minv# =  256
		For Local PixX%=0 To RoamTerrainWidth
		For Local PixY%=0 To RoamTerrainWidth
			Local Pixel% = 0
			If PixX < PixmapWidth(HmapImage) Then
			If PixY < PixmapHeight(HmapImage) Then
				Pixel=ReadPixel(HmapImage,PixX,PixY)
			End If
			End If
	        RoamTerrainHeight#[PixX,PixY]=(Pixel & $FF)
			If RoamTerrainHeight#[PixX,PixY] < minv Then minv = RoamTerrainHeight#[PixX,PixY]
			If RoamTerrainHeight#[PixX,PixY] > maxv Then maxv = RoamTerrainHeight#[PixX,PixY]
		Next
		Next
		
		Local vwidth# = (maxv - minv)
			
		If vwidth = 0 Then vwidth = 1.0
		For Local PixX%=0 To RoamTerrainWidth
		For Local PixY%=0 To RoamTerrainWidth
			RoamTerrainHeight#[PixX,PixY] = (RoamTerrainHeight#[PixX,PixY] - minv) * 30.0 / vwidth
		Next
		Next
	
		If RoamMaxHeightError = -1 Then
			For Local PixY%=0 To RoamTerrainWidth
			For Local PixX%=1 To RoamTerrainWidth
				Local dd# = (RoamTerrainHeight#[PixX,PixY] - RoamTerrainHeight#[PixX-1,PixY])
				If Abs(dd) > 10 Then RoamTerrainHeight#[PixX,PixY] = RoamTerrainHeight#[PixX-1,PixY] + Sgn(dd)*10
			Next
			Next
			For Local PixX%=0 To RoamTerrainWidth
			For Local PixY%=1 To RoamTerrainWidth
				Local dd# = (RoamTerrainHeight#[PixX,PixY] - RoamTerrainHeight#[PixX,PixY-1])
				If Abs(dd) > 10 Then RoamTerrainHeight#[PixX,PixY] = RoamTerrainHeight#[PixX,PixY-1] + Sgn(dd)*10
			Next
			Next
	
			Local ee# = 0
			For Local PixY%=0 To RoamTerrainWidth
			For Local PixX%=1 To RoamTerrainWidth
				ee :+ Abs(RoamTerrainHeight#[PixX,PixY] - RoamTerrainHeight#[PixX-1,PixY])
			Next
			Next
			For Local PixX%=0 To RoamTerrainWidth
			For Local PixY%=1 To RoamTerrainWidth
				ee :+ Abs(RoamTerrainHeight#[PixX,PixY] - RoamTerrainHeight#[PixX,PixY-1])
			Next
			Next
			RoamMaxHeightError = Sqr(ee) / 264.0
			If RoamMaxHeightError < 0.1 Then RoamMaxHeightError = 0.1
		End If
		
		Local RoamMaxTriangles%=RoamTerrainWidth*RoamTerrainWidth*2-1
		
		Local RoamBaseNeighbor%[RoamMaxTriangles*10+1]
		Local RoamBaseNeighborFlag%[RoamMaxTriangles*10+1]
	
		For Local CurrentNumber%=1 To RoamMaxTriangles
		  FindTriBaseNeighbor(RoamBaseNeighbor, RoamBaseNeighborFlag, CurrentNumber)
		Next
	
		CreateLand(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, ..
					RoamTerrainWidth, RoamMaxHeightError, MaxTileDepth)
		
	End Method

'---------------------------------------------------------------------------------------------------------------
'													CreateLand()
'---------------------------------------------------------------------------------------------------------------
Method CreateLand(RoamTerrainHeight:Float[,], RoamBaseNeighbor:Int[], RoamBaseNeighborFlag:Int[], ..
					RoamTerrainWidth:Int, RoamMaxHeightError#, MaxTileDepth%)

	Local RoamCriticalTriLevel%=RoamTerrainWidth*RoamTerrainWidth-1
	Local MinTileSizeLevel%=2^MaxTileDepth
		
	Local RoamTriangle:TBank[2]
	Local RoamSurface:TSurface = CreateSurface()
	
	RoamTriangle[0]=CreateBank(RoamTerrainWidth*RoamTerrainWidth*2)
	RoamTriangle[1]=CreateBank(RoamTerrainWidth*RoamTerrainWidth*2)
	Local RoamVertex:Int[,] = New Int[RoamTerrainWidth+1,RoamTerrainWidth+1]
	AddVertex RoamSurface,0,0,0
	RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, 0, ..
						RoamTerrainWidth,RoamTerrainWidth,RoamTerrainWidth,RoamTerrainWidth,0,1,0, ..
						RoamCriticalTriLevel, MinTileSizeLevel, RoamMaxHeightError)
	RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
						RoamTerrainWidth,0,0,0,0,RoamTerrainWidth,1,1, RoamCriticalTriLevel, MinTileSizeLevel, ..
						RoamMaxHeightError)
	RoamCreateTriangle(RoamTerrainHeight, RoamTriangle, RoamVertex, RoamSurface, 0,RoamTerrainWidth, ..
						RoamTerrainWidth,RoamTerrainWidth,RoamTerrainWidth,0,1,0,RoamTerrainWidth)
	RoamCreateTriangle(RoamTerrainHeight, RoamTriangle, RoamVertex, RoamSurface, RoamTerrainWidth,0,0,0,0, ..
						RoamTerrainWidth,1,1,RoamTerrainWidth)
	UpdateNormals
	
End Method

'---------------------------------------------------------------------------------------------------------------
'													FindTriBaseNeighbor()
'---------------------------------------------------------------------------------------------------------------
Method FindTriBaseNeighbor(RoamBaseNeighbor:Int[], RoamBaseNeighborFlag:Int[], Number%)
	If RoamBaseNeighbor[number]>0 
		Return 
	EndIf
	If Number<4
        Select Number
                Case 1
                        RoamBaseNeighbor[Number]=1
                        RoamBaseNeighborFlag[Number]=1
                Case 2
                        RoamBaseNeighbor[Number]=0
                        RoamBaseNeighborFlag[Number]=0
                Case 3
                        RoamBaseNeighbor[Number]=0
                        RoamBaseNeighborFlag[Number]=0
        End Select
		Return
	EndIf

	Local Parent%=Number Shr 1
	Local ParentLeftChild%=(Parent Shl 1)
	Local ParentRightChild%=(Parent Shl 1)+1
	
	Local PraParent%=Parent Shr 1
	Local PraParentLeftChild%=(PraParent Shl 1)
	Local PraParentRightChild%=(PraParent Shl 1)+1
	
	Local PraParentRightChildRightChild%
	Local PraParentLeftChildLeftChild%
	
	If Number=ParentLeftChild And Parent=PraParentLeftChild
       PraParentRightChildRightChild=(PraParentRightChild Shl 1)+1
       RoamBaseNeighbor[Number]=PraParentRightChildRightChild
       RoamBaseNeighborFlag[Number]=0
       RoamBaseNeighbor[PraParentRightChildRightChild]=Number
       RoamBaseNeighborFlag[PraParentRightChildRightChild]=0
		Return
	EndIf

	If Number=ParentRightChild And Parent=PraParentRightChild
       PraParentLeftChildLeftChild=(PraParentLeftChild Shl 1)
       RoamBaseNeighbor[Number]=PraParentLeftChildLeftChild
       RoamBaseNeighborFlag[Number]=0
       RoamBaseNeighbor[PraParentLeftChildLeftChild]=Number
       RoamBaseNeighborFlag[PraParentLeftChildLeftChild]=0
		Return
	EndIf

	Local PraParentBaseNeighbor%=RoamBaseNeighbor[PraParent]
	Local PraParentBaseNeighborFlag%=RoamBaseNeighborFlag[PraParent]

	If PraParentBaseNeighbor=0
        RoamBaseNeighbor[Number]=0
        RoamBaseNeighborFlag[Number]=0
        Return
	EndIf

	Local PraParentBaseNeighborRightChild%
	Local PraParentBaseNeighborRightChildLeftChild%
	
	If Number=ParentRightChild And Parent=PraParentLeftChild
	   PraParentBaseNeighborRightChild=(PraParentBaseNeighbor Shl 1)+1
	   PraParentBaseNeighborRightChildLeftChild=(PraParentBaseNeighborRightChild Shl 1)
	   RoamBaseNeighbor[Number]=PraParentBaseNeighborRightChildLeftChild
	   RoamBaseNeighborFlag[Number]=PraParentBaseNeighborFlag
	   RoamBaseNeighbor[PraParentBaseNeighborRightChildLeftChild]=Number
	   RoamBaseNeighborFlag[PraParentBaseNeighborRightChildLeftChild]=PraParentBaseNeighborFlag
	   Return
	EndIf

	Local PraParentBaseNeighborLeftChild%
	Local PraParentBaseNeighborLeftChildRightChild%
	
	If Number=ParentLeftChild And Parent=PraParentRightChild
		PraParentBaseNeighborLeftChild=PraParentBaseNeighbor Shl 1
		PraParentBaseNeighborLeftChildRightChild=(PraParentBaseNeighborLeftChild Shl 1)+1
		RoamBaseNeighbor[Number]=PraParentBaseNeighborLeftChildRightChild
		RoamBaseNeighborFlag[Number]=PraParentBaseNeighborFlag
		RoamBaseNeighbor[PraParentBaseNeighborLeftChildRightChild]=Number
		RoamBaseNeighborFlag[PraParentBaseNeighborLeftChildRightChild]=PraParentBaseNeighborFlag
	EndIf

End Method

'---------------------------------------------------------------------------------------------------------------
'													RoamBreakTriangle()
'---------------------------------------------------------------------------------------------------------------
Method RoamBreakTriangle(RoamTerrainHeight:Float[,], RoamBaseNeighbor:Int[], RoamBaseNeighborFlag:Int[], ..
							 RoamTriangle:TBank[], x0%,z0%,x1%,z1%,x2%,z2%,Number%,Branch%, ..
							 RoamCriticalTriLevel%, MinTileSizeLevel%, RoamMaxHeightError#)

	If Number>RoamCriticalTriLevel
        PokeByte(RoamTriangle[Branch],Number,0)
        Return
	EndIf
	
	Local xC%,zC%
	Local LeftChild%, RightChild%
	
	If number >= BankSize(RoamTriangle[0]) Then 
		Print "out of memory"
		Return
	End If
	
	If Number<MinTileSizeLevel
        xC=(x0+x2)/2
        zC=(z0+z2)/2
        LeftChild=(Number Shl 1)
        RightChild=(Number Shl 1)+1
        PokeByte(RoamTriangle[Branch],Number,1)
        RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
							x1,z1,xC,zC,x0,z0,LeftChild,Branch, RoamCriticalTriLevel, MinTileSizeLevel, ..
							RoamMaxHeightError)
        RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
							x2,z2,xC,zC,x1,z1,RightChild,Branch, RoamCriticalTriLevel, MinTileSizeLevel, ..
							RoamMaxHeightError)
        Return
	EndIf

	If PeekByte(RoamTriangle[Branch],Number)=1
        xC=(x0+x2)/2
        zC=(z0+z2)/2
        LeftChild=(Number Shl 1)
        RightChild=(Number Shl 1)+1
        RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
							x1,z1,xC,zC,x0,z0,LeftChild,Branch, RoamCriticalTriLevel, MinTileSizeLevel, ..
							RoamMaxHeightError)
        RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
							x2,z2,xC,zC,x1,z1,RightChild,Branch, RoamCriticalTriLevel, MinTileSizeLevel, ..
							RoamMaxHeightError)
        Return
	EndIf

	xC=(x0+x2)/2
	zC=(z0+z2)/2
	
	Local DeltaHeight#=Abs(RoamTerrainHeight[xC,zC]-(RoamTerrainHeight[x0,z0]+RoamTerrainHeight[x2,z2])*0.5)

	If (DeltaHeight < RoamMaxHeightError) Then Return

	xC=(x0+x2)/2
	zC=(z0+z2)/2
	LeftChild=(Number Shl 1)
	RightChild=(Number Shl 1)+1
	PokeByte(RoamTriangle[Branch],Number,1)
	RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
	x1,z1,xC,zC,x0,z0,LeftChild,Branch, RoamCriticalTriLevel, MinTileSizeLevel, ..
	RoamMaxHeightError)
	RoamBreakTriangle(RoamTerrainHeight, RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
	x2,z2,xC,zC,x1,z1,RightChild,Branch, RoamCriticalTriLevel, MinTileSizeLevel, ..
	RoamMaxHeightError)
	If RoamBaseNeighbor[Number]>0
	    RoamForceSplitTri(RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, ..
		RoamBaseNeighbor[Number],Branch ~ RoamBaseNeighborFlag[Number])
	EndIf

End Method

'---------------------------------------------------------------------------------------------------------------
'													RoamForceSplitTri()
'---------------------------------------------------------------------------------------------------------------
Method RoamForceSplitTri(RoamBaseNeighbor:Int[], RoamBaseNeighborFlag:Int[], RoamTriangle:TBank[], Number%, ..
							Branch%)
	If PeekByte(RoamTriangle[Branch],Number)=1
        Return
	EndIf
	PokeByte(RoamTriangle[Branch],Number,1)
	If RoamBaseNeighbor[Number]>0
        RoamForceSplitTri(RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, RoamBaseNeighbor[Number], ..
							Branch ~ RoamBaseNeighborFlag[Number])
	EndIf
	Local Parent%=Number Shr 1
	RoamForceSplitTri(RoamBaseNeighbor, RoamBaseNeighborFlag, RoamTriangle, Parent,Branch)
End Method

'---------------------------------------------------------------------------------------------------------------
'													RoamCreateTriangle()
'---------------------------------------------------------------------------------------------------------------
	Method RoamCreateTriangle(RoamTerrainHeight:Float[,], RoamTriangle:TBank[], RoamVertex:Int[,], ..
								RoamSurface:TSurface, x0%,z0%,x1%,z1%,x2%,z2%,..
	Number%,Branch%,RoamTerrainWidth%)
	
		 Local xC%, zC%
		 Local LeftChild%
	  	 Local RightChild%
	
	  	 If Number >= BankSize(RoamTriangle[0]) Then 
			Print "out of memory"
			Return
		 End If
		
		 If PeekByte(RoamTriangle[Branch],Number)=1
	        xC=(x0+x2)/2
	        zC=(z0+z2)/2
	        LeftChild=(Number Shl 1)
	        RightChild=(Number Shl 1)+1
			If RoamSurface.CountVertices() > 16000 Then
				Print "new surface"
				RoamSurface = CreateSurface()
				RoamVertex = New Int[RoamTerrainWidth+1,RoamTerrainWidth+1]
			End If
	        RoamCreateTriangle(RoamTerrainHeight, RoamTriangle, RoamVertex, RoamSurface, x1,z1,xC,zC,x0,z0,..
									LeftChild,Branch,RoamTerrainWidth)
	        RoamCreateTriangle(RoamTerrainHeight, RoamTriangle, RoamVertex, RoamSurface, x2,z2,xC,zC,x1,z1,..
									RightChild,Branch,RoamTerrainWidth)
	        Return
		Else
	
	        If RoamVertex[x0,z0]=0
	                RoamVertex[x0,z0]=AddVertex(RoamSurface,x0,RoamTerrainHeight[x0,z0],z0,x0,z0)
	        EndIf
	
	        If RoamVertex[x1,z1]=0
	                RoamVertex[x1,z1]=AddVertex(RoamSurface,x1,RoamTerrainHeight[x1,z1],z1,x1,z1)
	        EndIf
	
	        If RoamVertex[x2,z2]=0
	                RoamVertex[x2,z2]=AddVertex(RoamSurface,x2,RoamTerrainHeight[x2,z2],z2,x2,z2)
	        EndIf
	
	        AddTriangle (RoamSurface,RoamVertex[x0,z0],RoamVertex[x1,z1],RoamVertex[x2,z2])
			AddHeightTriangle(x0,RoamTerrainHeight[x0,z0],z0, ..
	       					  x1,RoamTerrainHeight[x1,z1],z1, ..
							  x2,RoamTerrainHeight[x2,z2],z2)
		EndIf
	
	End Method

'---------------------------------------------------------------------------------------------------------------
'												AddHeightTriangle()
'---------------------------------------------------------------------------------------------------------------
	Method AddHeightTriangle(x1#,y1#,z1#,x2#,y2#,z2#,x3#,y3#,z3#)
			
		If z3 < z1 Then
			Swap_Ter(x1,x3)
			Swap_Ter(y1,y3)
			Swap_Ter(z1,z3)
		End If
		If z2 < z1 Then
			Swap_Ter(x1,x2)
			Swap_Ter(y1,y2)
			Swap_Ter(z1,z2)
		End If
		If z3 < z2 Then
			Swap_Ter(x2,x3)
			Swap_Ter(y2,y3)
			Swap_Ter(z2,z3)
		End If
		
		Local dx12# = (x2 - x1) / (z2 - z1)
		Local dx13# = (x3 - x1) / (z3 - z1)
		Local dx23# = (x3 - x2) / (z3 - z2)
		
		Local ax# = x1
		Local bx# = x1
		
		Local x#, z#
	
		Local vv1# = PointDistToLine_Ter(x1, z1, x2, z2, x3, z3)
		Local vv2# = PointDistToLine_Ter(x2, z2, x1, z1, x3, z3)
		Local vv3# = PointDistToLine_Ter(x3, z3, x1, z1, x2, z2)
		
		For z = z1 To z3
			If z = z2 Then ax = x2
			
			Local s1#,s2#
			If ax < bx Then
				s1 = ax s2 = bx
			Else
				s1 = bx s2 = ax
			End If
			
			For x = s1 To s2
				Local v1# = PointDistToLine_Ter(x, z, x2,z2,x3,z3) / vv1
				Local v2# = PointDistToLine_Ter(x, z, x1,z1,x3,z3) / vv2
				Local v3# = PointDistToLine_Ter(x, z, x1,z1,x2,z2) / vv3
				Local h# = (v1 * y1) + (v2 * y2) + (v3 * y3)			
				SetHeightmapPixel(x, h, z)
			Next
			
			If z < z2 Then 
				ax = ax + dx12
			Else
				ax = ax + dx23
			End If
			bx = bx + dx13
		Next
			
	End Method

'---------------------------------------------------------------------------------------------------------------
'												Swap()
'---------------------------------------------------------------------------------------------------------------
	Method Swap_Ter(a# Var, b# Var)
		Local c# = a
		a = b
		b = c
	End Method

'---------------------------------------------------------------------------------------------------------------
'												SetHeightmapPixel()
'---------------------------------------------------------------------------------------------------------------
	Method SetHeightMapPixel(x#,y#,z#)
			
		x# =Floor(x)
		z# =Floor(z)
		If x < 0 Then Return
		If z < 0 Then Return
		If x > 511 Then Return
		If z > 511 Then Return
		Heightmap[x, z] = y
		
	End Method
	
	Method ReadHeightMapHeight#(x#,z#)
	
		Local fx% = Floor(x)
		Local fz% = Floor(z)
		If (fx = x) And (fz = z) Then Return ReadHeightMapPixel(x, z)
		If (fx <> x) And (fz <> z) Then
			'get integer position
			Local a:Int = Floor(x)
			Local b:Int = Floor(z)
		
			'get fractional part (range 0..1)
			Local c1# = x - Floor(x)
		  	Local d1# = z - Floor(z)
			'and invert it
			Local c2# = 1 - c1
			Local d2# = 1 - d1
			
			'get four points around position
			Local v00# = ReadHeightMapPixel(a + 0, b + 0)
			Local v10# = ReadHeightMapPixel(a + 1, b + 0)
			Local v01# = ReadHeightMapPixel(a + 0, b + 1)
			Local v11# = ReadHeightMapPixel(a + 1, b + 1)
			'interpolate
			Local v0# = (c2 * v00) + (c1 * v10)
			Local v1# = (c2 * v01) + (c1 * v11)	
			Local v# = (d2 * v0) + (d1 * v1)
			Return v
		End If
		
		If (fx <> x) Then
			'get integer position
			Local a:Int = Floor(x)
		
			'get fractional part (range 0..1)
			Local c1# = x - Floor(x)
			'and invert it
			Local c2# = 1 - c1
			
			'get two points around position
			Local v00# = ReadHeightMapPixel(a + 0, z)
			Local v10# = ReadHeightMapPixel(a + 1, z)
			'interpolate
			Local v0# = (c2 * v00) + (c1 * v10)
			Return v0
		End If
		
		If (fz <> z) Then
			'get integer position
			Local b:Int = Floor(z)
		
			'get fractional part (range 0..1)
		  	Local d1# = z - Floor(z)
			'and invert it
			Local d2# = 1 - d1
			
			'get two points around position
			Local v01# = ReadHeightMapPixel(x, b + 0)
			Local v11# = ReadHeightMapPixel(x, b + 1)
			'interpolate
			Local v1# = (d2 * v01) + (d1 * v11)	
			Return v1
		End If
		
	End Method

'---------------------------------------------------------------------------------------------------------------
'												ReadHeightmapPixel()
'---------------------------------------------------------------------------------------------------------------
	Method ReadHeightMapPixel#(x#,z#)
	
		x# =Floor(x)
		z# =Floor(z)
		If x < 0 Then Return 0
		If z < 0 Then Return 0
		If x > 511 Then Return 0
		If z > 511 Then Return 0
		Return Heightmap[x, z]
		
	End Method

'---------------------------------------------------------------------------------------------------------------
'												PointToPointDist()
'---------------------------------------------------------------------------------------------------------------
'get distance between two points
	Function PointToPointDist_Ter#(x1#,y1#,x2#,y2#)
		
		Local dx# = x1-x2
		Local dy# = y1-y2
		
		Return Sqr(dx*dx + dy*dy)
		
	End Function

'---------------------------------------------------------------------------------------------------------------
'												MinDistPointLine()
'---------------------------------------------------------------------------------------------------------------
	'find distance point->line (thanks tomtoad)
	Function PointDistToLine_Ter#(px#,py#,x1#,y1#,x2#,y2#)
	
		If x1 = x2 And y1 = y2 Then Return PointToPointDist_Ter(px,py,x1,y1)
		
		Local sx# = x2-x1
		Local sy# = y2-y1
		
		Local q# = ((px-x1) * (x2-x1) + (py - y1) * (y2-y1)) / (sx*sx + sy*sy)
		
		If q < 0.0 Then q = 0.0
		If q > 1.0 Then q = 1.0
		
		Return PointToPointDist_Ter(px,py,(1-q)*x1+q*x2,(1-q)*y1 + q*y2)
	
	End Function
 
'---------------------------------------------------------------------------------------------------------------
'												GetHeight()
'---------------------------------------------------------------------------------------------------------------
	Method GetHeight#(x#, z#)
	
		TFormPoint x, 0, z, Null, Self
		Local y# = ReadHeightmapHeight(TFormedX(), TFormedZ())
		TFormPoint TFormedX(), y, TFormedZ(), Self, Null
		Return TFormedY()
		
	End Method

End Type

'---------------------------------------------------------------------------------------------------------------
'													LoadTerrain()
'---------------------------------------------------------------------------------------------------------------
'usage: 
'		Local terr:TTerrain = LoadTerrain("heightmap.bmp")
'
'		RoamMaxHeightError  = cutoff value terrain steepness
'		MaxTileDepth		= cutoff depth for splitting triangles
'
Function LoadTerrain:TTerrain(filename$, RoamMaxHeightError#=-1, MaxTileDepth%=8)

	Local HmapImage:TPixmap

	If FileType(filename$) = 1 Then
		HmapImage = LoadPixmap(filename$)
		If HmapImage <> Null Then HmapImage = ResizePixmap(HmapImage, 512, 512)
	End If
	
	Local m:TTerrain = New TTerrain.Create()
	If HmapImage <> Null Then m.MakeTerrainMesh(HmapImage,RoamMaxHeightError,MaxTileDepth)

	Return m

End Function

'---------------------------------------------------------------------------------------------------------------
'													TerrainY()
'---------------------------------------------------------------------------------------------------------------
'usage:
'
'		Print TerrainY(terr, 256, 0, 256)
'
'Terrain extends TMesh, but don't use Mesh deform commands (PositionMesh)
'Else it screws up the heightmap reading
'
Function TerrainY#(t:TTerrain, x#, z#)

	If t = Null Then Return 0
	Return t.GetHeight(x, z)

End Function

'Heightmap is scaled to 512x512. I needed that for myself. Change it in LoadTerrain
'but also, change the HeightMap array size, and the array clearing routine in MakeTerrainMesh
'Not that input heightmap should be square. Not sure if it has to be a power of two
'
'The height is rescaled to a range 30. If you want to change that in MakeTerrainMesh.
'The automatic adjustment is based on that.
'So along, also change
'			RoamMaxHeightError = Sqr(ee) / 264.0
'

Comments

impixi2008
Interesting discovery. Thanks.


plash2008
Very interesting (russian blitzers!??), I was just looking at terrain rendering and ROAM on wikipedia.

EDIT: yikes! it uses 65k memory.


Damien Sturdy2008

EDIT: yikes! it uses 65k memory.



Is that bad? 65k? Thats tiny!


plash2008
65k - as in 65 thousand, not kilobytes. If thats what you got before, it's still a lot to me.


Wings2008
Thats not much.

My engine use 200 mb only on th black and white JPG file. 5000x5000 :D


Warner2009
Updated.


Code Archives Forum