Disappointing Terrain Performance?

Blitz3D SDK Forums/Blitz3D SDK Programming/Disappointing Terrain Performance?

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

I should probably use a chunked mesh terrain anyway. The following BlitzMax code runs at over 1000fps:


SuperStrict

Import blitz3d.blitz3dsdk

bbBeginBlitz3D()

bbGraphics3D 800, 600, 32, 1

bbSetBuffer bbBackBuffer()

Local hmap_url:String = "temp_hmap.png"

bbAmbientLight 255, 255, 255

Local Camera:Int = bbCreateCamera()
bbCameraRange Camera, 0.1, 1000.0
bbCameraClsColor Camera, 64, 164, 196

Local Terrain:TChunkedTerrain = New TChunkedTerrain

Local temp:Float[,] = createSampleHeightMap(256, 256, 1000, 32, False, hmap_url)
Terrain.CreateMeshes(temp, 64)

temp = Null
GCCollect

'Terrain.LoadFromFile(hmap_url, 64)

'Terrain.SetUV 32	'<-- BUGGY

Terrain.TextureFromPixmap(LoadPixmap(hmap_url), 1, 0, 1)
Terrain.Scale 10.0, 100.0, 10.0
Terrain.Position 0.0, 0.0, 0.0

'bbClearTextureFilters
'Terrain.TextureChunk bbLoadTexture("grass.png", 1 + 8), Terrain.GetChunk(0.0, 0.0), 0, 1

bbPositionEntity Camera, Terrain.CentreX(), Terrain.Y, Terrain.CentreZ()

Local MoveSpeed:Float = 0.1

Local old_ms:Int, renders:Int, fps:Int

Local mxs:Float, mys:Float

'bbWireFrame True

While Not bbKeyHit(1) 

		mxs :+ (bbMouseXSpeed() / 3.0)
		mys :+ (bbMouseYSpeed() / 3.0)

		bbRotateEntity Camera, mys, -mxs, 0.0

		bbMoveMouse bbGraphicsWidth() / 2, bbGraphicsHeight() / 2
		
		bbMouseXSpeed()
		bbMouseYSpeed()
		
		If bbKeyDown(200) Or bbKeyDown(17) Or bbMouseDown(2) Then bbMoveEntity Camera, 0.0, 0.0, MoveSpeed

		If bbKeyDown(208) Or bbKeyDown(31) Then bbMoveEntity Camera, 0.0, 0.0, -MoveSpeed

		If bbKeyDown(32) Then bbMoveEntity Camera, MoveSpeed, 0.0, 0.0

		If bbKeyDown(30) Then bbMoveEntity Camera, -MoveSpeed, 0.0, 0.0

		Local camx:Float = bbEntityX(Camera)
		Local camz:Float = bbEntityZ(Camera)
		Local camy:Float = Terrain.MeshY(camx, camz) + 5.0

		bbPositionEntity Camera, camx, camy, camz 

		Terrain.DoVisibleChunks camx, camz

		bbRenderWorld 

		renders :+ 1

		If MilliSecs() -old_ms >= 1000
			old_ms = MilliSecs()
			fps = renders
			renders = 0
		EndIf

		bbText 5, 5, "FPS: " + fps

		bbFlip False

Wend

Terrain.Destroy

bbEndBlitz3D()

End


'********** CHUNKED TERRAIN CLASS **********


Type TChunkedTerrain
		
		
	Field ChunkSize:Int

	Field Chunks:Int[,]
	Field Cols:Int
	Field Rows:Int
	
	Field X:Float
	Field Y:Float
	Field Z:Float
	
	Field XScale:Float
	Field YScale:Float
	Field ZScale:Float

	Field XSize:Float
	Field ZSize:Float


	Method New()
	
		X = 0.0
		Y = 0.0
		Z = 0.0
				
		XScale = 1.0
		YScale = 1.0
		ZScale = 1.0
	
	EndMethod
	
	
	Method Destroy()
	
		For Local col:Int = 0 To Cols - 1
			For Local row:Int = 0 To Rows - 1
				If Chunks[col, row] Then bbFreeEntity Chunks[col, row]
				Chunks[col, row] = Null
			Next
		Next	
	
		Chunks = Null
		
		GCCollect		
	
	EndMethod
	
	
	'Chunk Size should be less than the heightmap size and a power of two (eg, 16, 32, 64, etc)
	Method LoadFromFile:Int(hmap_url:String, chunk_size:Int = 64)
		
		Local pix:TPixmap = LoadPixmap(hmap_url)
		If Not pix Return False

		Local map:Float[,] = New Float[pix.width, pix.height]

		For Local px:Int = 0 To pix.width - 1
			For Local pz:Int = 0 To pix.height - 1
				Local r:Byte = ReadPixel(pix, px, pz)
				map[px, pz] = Float(r) / 255.0
			Next
		Next
		
		CreateMeshes map, chunk_size
		
		Scale XScale, YScale, ZScale
		
		map = Null
		GCCollect
		
		Return True

	EndMethod


	Method CreateMeshes(arr:Float[,] Var, chunk_size:Int = 64)
		
		If Not arr Then Return
		
		Local dims:Int[] = arr.Dimensions()

		Local map_size:Int

		If dims[0] < dims[1] Then map_size = dims[0] Else map_size = dims[1]
		
		Local map:Float[,] = New Float[map_size, map_size]
						
		For Local px:Int = 0 To map_size - 1
			For Local pz:Int = 0 To map_size - 1
				map[px, (map_size - pz - 1)] = arr[px, pz]
			Next
		Next
		
		ChunkSize = chunk_size		
				
		Cols = (map_size / ChunkSize)
		Rows = (map_size / ChunkSize)
		
		Chunks = New Int[Cols, Rows]
				
		For Local col:Int = 0 To Cols - 1
			For Local row:Int = 0 To Rows - 1
		
				Local px:Int = (col * ChunkSize) - col
				Local pz:Int = (row * ChunkSize) - row
				Chunks[col, row] = CreateChunk(map, px, pz, ChunkSize + 1, ChunkSize + 1)
				
				bbEntityPickMode Chunks[col, row], 2

			Next
		Next
		
		Position X, Y, Z
		
		map = Null
		GCCollect
		
		Return

	EndMethod
	
	
	Method CreateChunk:Int(map:Float[,] Var, px:Int, pz:Int, w:Int, h:Int, parent:Int = 0)
	Rem
		* Ported And slightly modified from Blitz3D code:
  		  <a href="http://www.blitzbasic.com/codearcs/codearcs.php?code=1609" target="_blank">http://www.blitzbasic.com/codearcs/codearcs.php?code=1609</a>
	EndRem

		Local mesh:Int = bbCreateMesh(parent)
		Local surf:Int = bbCreateSurface(mesh)

		For Local lz:Int = 0 To h
			For Local lx:Int = 0 To w
				bbAddVertex surf, lx, 0, lz, (1.0 / lx), (1.0 / lz)
			Next
		Next
	
		For Local lz:Int = 1 To (h - 2)
			For Local lx:Int = 1 To (w - 2)
				bbAddTriangle surf, lx + ((w + 1) * lz), lx + ((w + 1) * lz) + (w + 1), (lx + 1) + ((w + 1) * lz)
				bbAddTriangle surf, (lx + 1) + ((w + 1) * lz), lx + ((w + 1) * lz) + (w + 1), (lx + 1)+((w + 1) * lz) + (w + 1)
			Next
		Next

		For Local lx:Int = 1 To (w - 1)
			For Local lz:Int = 1 To (h - 1)
				Local index:Int = lx + ((w + 1) * lz)
				bbVertexCoords surf, index, bbVertexX(surf, index), map[px + lx, pz + lz], bbVertexZ(surf, index)
				bbVertexTexCoords surf, index, lx, -lz 
			Next
		Next

		bbUpdateNormals mesh
		
		Return Mesh
	
	EndMethod
	
	
	'** BUGGY - seams visible between chunks
	Method SetUV(tile_size:Int = 1)
		
		For Local col:Int = 0 To Cols - 1
			For Local row:Int = 0 To Rows - 1
			
				Local surf:Int = bbGetSurface(Chunks[col, row], 1)
				
				For Local lx:Int = 1 To ChunkSize
					For Local lz:Int = 1 To ChunkSize 
						Local index:Int = lx + ((ChunkSize + 2) * lz)
						bbVertexTexCoords surf, index, (lx * tile_size), -(lz * tile_Size)
					Next
				Next
				
			Next
		Next
				
	EndMethod


	Method TextureFromFile:Int(tex_url:String = "", flags:Int = 1, frame:Int = 0, index:Int = 0)
	
		Local tex:TPixmap = LoadPixmap(tex_url)
		
		Return TextureFromPixmap(tex, flags, frame, index)

	EndMethod


	Method TextureFromPixmap:Int(tex:TPixmap, flags:Int = 1, frame:Int = 0, index:Int = 0)

		If Not tex Then Return False
		
		If PixmapFormat(tex) <> PF_RGBA8888 Then tex = ConvertPixmap(tex, PF_RGBA8888)
		
		tex = YFlipPixmap(ResizePixmap(tex, ChunkSize * Cols, ChunkSize * Rows))
		
		For Local col:Int = 0 To Cols - 1
			For Local row:Int = 0 To Rows - 1
		
				Local px:Int = (col * ChunkSize) - col
				Local pz:Int = (row * ChunkSize) - row
		
				Local fname:String = "temp_chunk_tex_" + col + "_" + row + ".png"
				Local pm:TPixmap = PixmapWindow(tex, px, pz, ChunkSize + 1, ChunkSize + 1)
				SavePixmapPNG(YFlipPixmap(pm), fname, 9)

				TextureChunk(bbLoadTexture(fname, flags), Chunks[col, row], frame, index)
				
				DeleteFile fname
				
			Next
		Next
		
		Return True
		
	EndMethod
	

	Method TextureChunk(tex:Int, mesh:Int, frame:Int = 0, index:Int = 0)
	
		If Not tex Then Return

		bbScaleTexture tex, ChunkSize + 1, ChunkSize + 1
		
		bbEntityTexture mesh, tex, frame, index
		
	EndMethod
	

	Method Scale(sx:Float, sy:Float, sz:Float)
	
		XScale = sx
		YScale = sy
		ZScale = sz
		
		For Local c:Int = 0 To Cols - 1
			For Local r:Int = 0 To Rows - 1
				bbScaleEntity Chunks[c, r], XScale, YScale, ZScale
			Next
		Next				
	
		XSize = ChunkSize * Cols * XScale
		ZSize = ChunkSize * Rows * ZScale
			
	EndMethod
	
	
	Method Position(wx:Float, wy:Float, wz:Float)
	
		X = wx
		Y = wy
		Z = wz
		
		For Local col:Int = 0 To Cols - 1
			For Local row:Int = 0 To Rows - 1
				Local px:Int = X + (col * ChunkSize * XScale) - (col * XScale)
				Local pz:Int = Z + (row * ChunkSize * ZScale) - (row * ZScale)
				bbPositionEntity Chunks[col, row], px - XScale, Y, pz - ZScale
			Next
		Next
	
	EndMethod
	

	Method MeshY:Float(wx:Float = 0.0, wz:Float = 0.0)

		If (wx < X) Or (wz < Z) Then Return Y
		If wx > X + (ChunkSize * Cols * XScale) Then Return Y
		If wz > Z + (ChunkSize * Rows * ZScale) Then Return Y

		bbLinePick(wx, Y + YScale, wz, 0.0, -(YScale * 2), 0.0)
		
		Return bbPickedY()

	EndMethod


	Method GetChunk:Int(wx:Float = 0.0, wz:Float = 0.0)

		If (wx < X) Or (wz < Z) Then Return Null
		If wx > X + (ChunkSize * Cols * XScale) Then Return Null
		If wz > Z + (ChunkSize * Rows * ZScale) Then Return Null

		Local col:Int = (wx - X) / (ChunkSize * XScale)
		Local row:Int = (wz - Z) / (ChunkSize * ZScale)

		Return Chunks[col, row]

	EndMethod
	
	
	Method CentreX:Float()
		
		Return X + ((Cols * ChunkSize * XScale) / 2)
		
	EndMethod
	
	
	Method CentreZ:Float()
		
		Return Z + ((Rows * ChunkSize * ZScale) / 2)
		
	EndMethod
	
	
	Method DoVisibleChunks(wx:Float, wz:Float, range:Int = 1)
	
		For Local col:Int = 0 To Cols - 1
			For Local row:Int = 0 To Rows - 1
				bbHideEntity Chunks[col, row]
			Next
		Next
		
		Local current_col:Int = (wx - X) / (ChunkSize * XScale)
		Local start_col:Int = current_col - range
		If start_col < 0 Then start_col = 0
		Local end_col:Int = current_col + range
		If end_col >= Cols Then end_col = Cols - 1
		
		Local current_row:Int = (wz - Z) / (ChunkSize * ZScale)
		Local start_row:Int = current_row - range
		If start_row < 0 Then start_row = 0
		Local end_row:Int = current_row + range
		If end_row >= Rows Then end_row = Rows - 1
			
		For Local col:Int = start_col To end_col
			For Local row:Int = start_row To end_row
				bbShowEntity Chunks[col, row]
			Next
		Next
		
	EndMethod
	
	
EndType


'******************************


Function createSampleHeightMap:Float[,](width:Int = 128, height:Int = 128, numhills:Int = 1000, maxhsize:Int = 32, island:Int = True, fname:String = "")

	'Generate

	Local Map:Float[width, height]

    SeedRnd(MilliSecs())
  
	If island
		numhills :* 0.2
		maxhsize :* 0.5
	EndIf

    For Local n:Int = 1 To numhills

		Local radius:Int = Rand(0, maxhsize)
		Local centrex:Int = Rand(0, Width - 1)
		Local centrez:Int = Rand(0, Height - 1)
		
		If island
			Local theta:Int = Rand(0, 359)
			Local distx:Int = Rand(0, (Width * 0.5) - radius)
			Local distz:Int = Rand(0, (Height * 0.5) - radius)
	  		centrex = (Width * 0.5) + (Cos(theta) * distx)
	  		centrez = (Height * 0.5) + (Sin(theta) * distz)
		EndIf

    	Local startx:Int = centrex - radius
      	If startx < 0 startx = 0

      	Local endx:Int = centrex + radius
	  	If endx > Width endx = Width

      	Local startz:Int = centrez - radius
      	If startz < 0 startz = 0

      	Local endz:Int = centrez + radius
      	If endz > Height endz = Height

      	For Local x:Int = startx To (endx - 1)
       		For Local z:Int = startz To (endz - 1)
	   			Local y:Float = (radius * radius) - (((x - centrex) * (x - centrex)) + ((z - centrez) * (z - centrez)))
	    		If y > 0.0 Map[x, z] :+ y
	    	Next
	  	Next
    
    Next

	'Normalise
	
	Local minv:Float = 10^38, maxv:Float = 10^-38
  
  	For Local x:Int = 0 To (Width - 1)
    	For Local z:Int = 0 To (Height - 1)
 	 		If Map[x, z] < minv minv = Map[x, z] Else If Map[x, z] > maxv maxv = Map[x, z]
     	Next
   	Next

   	For Local x:Int = 0 To (Width - 1)
     	For Local z:Int = 0 To (Height - 1)
			Map[x, z] = (Map[x, z] - minv) / (maxv - minv)
		Next
  	Next

	'Convert to byte Array
	
	Local arr:Byte[,] = New Byte[width, height]

  	For Local x:Int = 0 To (Width - 1)
    	For Local z:Int = 0 To (Height - 1)

			Local v:Int = Floor(Map[x, z] * 255)

			arr[x, z] = Byte(v)

		Next
	Next
		
	'Render to pixmap and save to file
	
	If fname
	
		Local pm:TPixmap = CreatePixmap(width, height, PF_RGB888)

    	For Local x:Int = 0 To (Width - 1)
    		For Local z:Int = 0 To (Height - 1)
    			WritePixel(pm, x, z, arr[x, z] Shl 16 | arr[x, z] Shl 8 | arr[x, z])
	    	Next
    	Next

		SavePixmapPNG(pm, fname, 9)	
	
	EndIf
	
	Return Map
	
End Function




It’s an updated version of code I wrote for MiniB3d earlier in the year. I just haven’t figured out how to eliminate the seams between chunks when tiling textures.