This is a simple terrain generator that uses a greyscale image to create your terrain.
Here's an image that shows a greyscale image and the 3D result.

Now the reason I call this a basic generator is because it lacks a few key features.
The main missing features are LOD and color heightmaps.
Purebasic does not have mesh LOD generation.
So, the workarounds are to create a mesh file and build them yourself or use a program that will do it for you.
As for the color heightmaps (which allow smoother height transitions) I might implement them at a later date.
EDIT #2:
I made a few changes within the generator. I added UV texture repeating (no more material scaling required) and cleaned the code up a little bit.
I also added an alpha blending example to go along with the hlsl and original example. Here's a picture showing what alpha blending can do to the terrain.


It does require a material script in order to do alpha blending, but that's not a very big deal because it's needed anyways if you use shaders on your terrain.
I plan on adding terrain LOD in the next update. That will allow much larger terrains to be generated.
Here's the download link with all three examples.
Terrain Generator.zip
Here's the updated the source code for the plain terrain generator here. If you want the images you'll have to get them from the download.
Code: Select all
Enumeration
#Window
#Font
#HelpSprite
#Texture
#Material
#Light
#Camera
#Terrain
#HeightMap
EndEnumeration
Declare TG_UpdateSprite()
Declare TG_GenerateTerrain(TilesX, TilesZ, TerrainHeight, TerrainSizeX, TerrainSizeZ, TextureRepeatU, TextureRepeatV, HeightMapHandle)
If InitEngine3D(#PB_Engine3D_DebugLog) = 0
MessageRequester("ERROR", "Failed to initialize the 3D engine.", #PB_MessageRequester_Ok)
End
EndIf
If InitSprite() = 0
MessageRequester("ERROR", "Failed to initialize the sprite environment.", #PB_MessageRequester_Ok)
End
EndIf
If InitKeyboard() = 0
MessageRequester("ERROR", "Failed to initialize the keyboard environment.", #PB_MessageRequester_Ok)
End
EndIf
If InitMouse() = 0
MessageRequester("ERROR", "Failed to initialize the mouse environment.", #PB_MessageRequester_Ok)
End
EndIf
UsePNGImageDecoder()
UseJPEGImageDecoder()
WindowW = 1280
WindowH = 720
Define.f MouseX, MouseY, KeyX, KeyY
Global.i RenderMode, Help
LoadFont(#Font, "Courier New", 11, #PB_Font_Bold)
If OpenWindow(#Window, 0, 0, WindowW, WindowH, "Terrain Generator", #PB_Window_ScreenCentered)
;AntialiasingMode(#PB_AntialiasingMode_x6)
If OpenWindowedScreen(WindowID(#Window), 0, 0, WindowW, WindowH, 0, 0, 0)
Add3DArchive("Programs\", #PB_3DArchive_FileSystem)
Add3DArchive("Scripts\", #PB_3DArchive_FileSystem)
Add3DArchive("Textures\", #PB_3DArchive_FileSystem)
Parse3DScripts()
CreateSprite(#HelpSprite, 300, 145)
;# Texture that will cover the terrain.
LoadTexture(#Texture, "grass.jpg")
CreateMaterial(#Material, TextureID(#Texture))
;# Greyscale image that will be used for generating the terrain.
LoadImage(#HeightMap, "Textures\HeightMap00.png")
;## A FEW NOTES ##
;# Stay below 510 tiles total or you will exceed the vertex limit.
;# TerrainHeight can have a positive or negative value.
MeshHandle = TG_GenerateTerrain(255, 255, 5, 50, 50, 100, 100, #HeightMap)
TestHandle = IsMesh(MeshHandle)
If TestHandle <> 0
CreateEntity(#Terrain, MeshID(MeshHandle), MaterialID(#Material))
EndIf
CreateLight (#Light, RGB(0,0,0), 0, 500, 1000)
SetLightColor(#Light, #PB_Light_DiffuseColor, RGB(255,255,255))
AmbientColor(RGB(50,50,50))
CreateCamera (#Camera, 0, 0, 100, 100)
MoveCamera (#Camera, -20, 20, 20, #PB_Absolute)
CameraLookAt (#Camera, 0, 00, 0)
CameraBackColor(#Camera, RGB(80,80,80))
;# WARNING!! If you use stencil shadow types (#PB_Shadow_Modulative or #PB_Shadow_Additive)
;and you have over 360 tiles in your terrain the program may crash as you've exceeded the 16 bit index.
;In this case I personally wouldn't use stencil shadows. As they're not meant to be
;used in complex scenes.
;If you really want to use stencil shadows. I highly suggest you keep the terrain simple.
;WorldShadows(#PB_Shadow_Modulative, 100, RGB(150, 150, 150))
Repeat
Event = WindowEvent()
If ExamineMouse()
MouseX = -MouseDeltaX() / 10
MouseY = -MouseDeltaY() / 10
EndIf
If ExamineKeyboard()
If KeyboardPushed(#PB_Key_A)
KeyX = -0.2
ElseIf KeyboardPushed(#PB_Key_D)
KeyX = 0.2
Else
KeyX = 0
EndIf
If KeyboardPushed(#PB_Key_W)
KeyY = -0.2
ElseIf KeyboardPushed(#PB_Key_S)
KeyY = 0.2
Else
KeyY = 0
EndIf
If KeyboardReleased(#PB_Key_R)
If RenderMode = 0
CameraRenderMode(#Camera, #PB_Camera_Wireframe)
RenderMode = 1
Else
CameraRenderMode(#Camera, #PB_Camera_Textured)
RenderMode = 0
EndIf
EndIf
If KeyboardReleased(#PB_Key_H)
If Help = 0
Help = 1
Else
Help = 0
EndIf
EndIf
EndIf
RotateCamera(#Camera, MouseY, MouseX, 0, #PB_Relative)
MoveCamera (#Camera, KeyX, 0, KeyY)
RenderWorld()
If Help = 0
TG_UpdateSprite()
DisplaySprite(#HelpSprite, 0, 0)
EndIf
FlipBuffers()
Until KeyboardPushed(#PB_Key_Escape)
EndIf
EndIf
End
Procedure TG_UpdateSprite()
Define.i FPS, Triangles, Batches
FPS = Engine3DStatus(#PB_Engine3D_CurrentFPS)
Triangles = Engine3DStatus(#PB_Engine3D_NbRenderedTriangles)
Batches = Engine3DStatus(#PB_Engine3D_NbRenderedBatches)
StartDrawing(SpriteOutput(#HelpSprite))
DrawingMode(#PB_2DDrawing_Default)
Box(0, 0, SpriteWidth(#HelpSprite), SpriteHeight(#HelpSprite), RGB(20,20,20))
DrawingFont(FontID(#Font))
DrawingMode(#PB_2DDrawing_Transparent)
DrawText(5, 5, "FPS : " + Str(FPS), RGB(255,0,0))
DrawText(5, 25, "Triangles : " + Str(Triangles), RGB(0,255,0))
DrawText(5, 45, "Batches : " + Str(Batches), RGB(0,128,255))
DrawText(5, 65, "Press W,A,S,D to Move Camera.", RGB(255,255,255))
DrawText(5, 85, "Move Mouse to Rotate Camera.", RGB(255,255,255))
If RenderMode = 0
DrawText(5, 105, "Press R : Wireframe Off", RGB(255,128,0))
Else
DrawText(5, 105, "Press R : Wireframe On", RGB(255,128,0))
EndIf
If Help = 0
DrawText(5, 125, "Press H : Help On", RGB(255,255,0))
Else
DrawText(5, 125, "Press H : Help Off", RGB(255,255,0))
EndIf
StopDrawing()
EndProcedure
Procedure TG_GenerateTerrain(TilesX, TilesZ, TerrainHeight, TerrainSizeX, TerrainSizeZ, TextureRepeatU, TextureRepeatV, HeightMapHandle)
;# First check and see if all parameters have valid values.
;# TerrainHeight can have any value positive or negative. So, we will not be testing for it.
If TilesX <= 0
MessageRequester("ERROR", "TilesX must be greater than 0.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
If TilesZ <= 0
MessageRequester("ERROR", "TilesZ must be greater than 0.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
If TerrainSizeX <= 0
MessageRequester("ERROR", "TerrainSizeX must be greater than 0.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
If TerrainSizeZ <= 0
MessageRequester("ERROR", "TerrainSizeZ must be greater than 0.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
If TextureRepeatU <= 0
MessageRequester("ERROR", "TextureRepeatU must be greater than 0.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
If TextureRepeatV <= 0
MessageRequester("ERROR", "TextureRepeatV must be greater than 0.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
If IsImage(HeightMapHandle) = 0
MessageRequester("ERROR", "Failed to find HeightMap.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
;# Test to make sure terrain will not exceed the 16 bit index limitations (65,536 vertices).
;# There are ways around this limitation, but that's for another day.
Define.i TotalVertices
TotalVertices = (TilesX + 1) * (TilesZ + 1)
If TotalVertices > 65536
MessageRequester("ERROR", "Terrain has too many tiles. Vertex limit has been exceeded.", #PB_MessageRequester_Ok)
ProcedureReturn #False
EndIf
;# Generating Vertex Data
Dim Vertices.d(TotalVertices, 2)
Define.d TileSizeX, TileSizeZ
TileSizeX = TerrainSizeX / TilesX
TileSizeZ = TerrainSizeZ / TilesZ
Define.d TileLocX, TileLocY, TileLocZ
TileLocX = (TerrainSizeX * 0.5) * -1
TileLocZ = (TerrainSizeZ * 0.5) * -1
StartDrawing(ImageOutput(HeightMapHandle))
Define.i ImgWidth, ImgHeight
ImgWidth = ImageWidth (HeightMapHandle) - 1
ImgHeight = ImageHeight(HeightMapHandle) - 1
Define.d ImgXRatio, ImgZRatio
ImgXRatio = ImgWidth / TerrainSizeX
ImgZRatio = ImgHeight / TerrainSizeZ
Define.d HeightRatio
HeightRatio = TerrainHeight / 255
Define.i PixelX, PixelY
Define.i VCTR, CTRX, CTRZ
Define.i Color, ColorValue
For CTRZ = 0 To TilesZ
For CTRX = 0 To TilesX
PixelX = ((TerrainSizeX * 0.5) + TileLocX) * ImgXRatio
PixelY = ((TerrainSizeZ * 0.5) + TileLocZ) * ImgZRatio
Color = Point(PixelX, PixelY)
ColorValue = Red(Color)
TileLocY = ColorValue * HeightRatio
Vertices(VCTR, 0) = TileLocX
Vertices(VCTR, 1) = TileLocY
Vertices(VCTR, 2) = TileLocZ
TileLocX = TileLocX + TileSizeX
VCTR + 1
Next
TileLocX = (TerrainSizeX * 0.5) * -1
TileLocZ = TileLocZ + TileSizeZ
Next
StopDrawing()
;# Generating Face Data
Define.i TotalFaces
TotalFaces = (TilesX * TilesZ) * 2
Dim Faces(TotalFaces, 2)
Define.i FCTR, FaceCTR
For CTRZ = 0 To TilesZ - 1
For CTRX = 0 To TilesX - 1
Faces(FCTR, 0) = FaceCTR
Faces(FCTR, 1) = FaceCTR + 1 + TilesX
Faces(FCTR, 2) = FaceCTR + 1
Faces(FCTR + 1, 0) = FaceCTR + 1
Faces(FCTR + 1, 1) = FaceCTR + 1 + TilesX
Faces(FCTR + 1, 2) = FaceCTR + 2 + TilesX
FCTR + 2
FaceCTR + 1
Next
FaceCTR + 1
Next
;# Now we can create our terrain
Define.i UCounter
Define.d U, V, Uoffset, Voffset
Uoffset = TextureRepeatU * (1 / TilesX)
Voffset = TextureRepeatV * (1 / TilesZ)
Handle = CreateMesh(#PB_Any, #PB_Mesh_TriangleList, #PB_Mesh_Static)
For CTR = 0 To TotalVertices -1
MeshVertexPosition(Vertices(CTR, 0), Vertices(CTR, 1), Vertices(CTR, 2))
MeshVertexNormal(0, 0, 0)
MeshVertexTextureCoordinate(U, V)
U = U + Uoffset
UCounter + 1
If UCounter > TilesX
UCounter = 0
U = 0
V = V + Voffset
EndIf
Next
For CTR = 0 To TotalFaces
MeshFace(Faces(CTR, 0), Faces(CTR, 1), Faces(CTR, 2))
Next
FinishMesh(#True)
;# We normalize the mesh because I haven't spent the time to manually calculate the vertex normals.
;I might add this process later on, but for now I'll keep it this way.
NormalizeMesh(Handle)
FreeArray(Vertices())
FreeArray(Faces())
ProcedureReturn Handle
EndProcedure