Here is a small example of a Map produced by L3DT (HeightMap + ColorMap) + a Detail Map and finally rendered with my lib in AppGameKit
The lib create an obj file to render the terrain from the heightmap (
because AppGameKit doesn't permit to create vertices and faces on the fly for the moment )
I will clean the code and publish the complete example project here very soon
EDIT : The Example project is now attached on this post and is over commented lol
EDIT2 : i'm so lazy to add vertices normals (important for dynamic lighting) for the moment but calculating them and add them to the OBJ file is easy if you want to try
(or i will add them soon)
EDIT3 : The Zoq2 as ported the code to Tier 2 here : (thanks !)
http://forum.thegamecreators.com/?m=forum_view&t=203224&b=48&msg=2441546#m2441546
The function itself :
Need an UDT before :
Type Vertex
x#
y#
z#
u#
v#
VertexNumber
EndType
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// LOADTERRAIN FUNCTION
////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Syntax : LoadTerrain(heightmapFile$,colorMapFile$,detailMapFile$,detailSX# ,detailSY#,MaxHeight#,StepSize)
//
// heightmapFile$ : The path of the grayscale heightmap image (heightmap)
// colorMapFile$ : The path of the terrain texture image (colormap). (may have a different size)
// detailMapFile$ : The path of the detail texture image (detailmap). Will be used with shader files (vertexDetail.vs & pixelDetail.ps)
// detailSX#, detailSY# : X and Y Scale factors of the detailmap texture to pass to shader.
// MaxHeight# : Scale the height for the terrain (this value will be the maximum height of the terrain (white pixels on heightmap (rgb : 255,255,255))
// StepSize : Must be integer. Defines the resolution of the map.
// If set to 1, the loader will take each pixel into account (so a heightmap of 512x512 will create 512*512 vertices... so a huge mesh). [MAXIMUM MAP RESOLUTION]
// If set to 2, will take 1 pixel of 2 (X,Y) into account (256x256 vertices) ( less vertices and triangles but also less details (but insignificant))
// If set to 3, 1 pixel of 3 ... etc...
Function LoadTerrain(heightmapFile$,colorMapFile$,detailMapFile$,detailSX# ,detailSY#,MaxHeight#,StepSize)
// Load HeightMap Image
HeightMapImage=LoadImage(heightmapFile$)
// Getting the size of HeightMap => Why -2 ? due to zero index and to prevent overflow for last iteration.
x = GetImageWidth(HeightMapImage)-2 : y = GetImageHeight(HeightMapImage)-2
// Dim Vertices Array (from 0)
Dim Vertices[x,y] as Vertex
// Create Memblock from HeightMap image
memBlockID=CreateMemblockFromImage( HeightMapImage )
If GetFileExists("map.obj") then DeleteFile("map.obj") // Just to be sure .... :)
ObjFile=OpenToWrite("map.obj") // Prepare an OBJ File to write into.
VertNumber=1 // In OBJ Files, Vertices index starts at "1"
For ly = 0 To y Step StepSize
// Just a progress indicator ...
If mod(ly,5)=0 : print("Creating Vertex:"+str(ly)+"/"+str(y)) : Render2DFront() : Swap() : EndIf
For lx = 0 To x Step StepSize
offsetPix=12+(((lx)+(ly*(x+2))))*4
r=GetMemblockByte(memBlockID,offsetPix+0)
g=GetMemblockByte(memBlockID,offsetPix+1)
b=GetMemblockByte(memBlockID,offsetPix+2)
a=GetMemblockByte(memBlockID,offsetPix+3) // Not necessary (alpha is always 255 for classic heightmaps .. will just raise the terrain height a little more )
// Calculate height from pixel RGB
h#=((r+g+b)/3.0)/255 // height Normalization : can have a maximum value of 1.0
// Vertex Coords
Vertices[lx,ly].x#=lx
Vertices[lx,ly].y#=h#*MaxHeight# // Apply the MaxHeight# (scaling the height only) parameters
Vertices[lx,ly].z#=y-ly // (easier to make it like that for vertices associations later )
myFloatlx#=lx // Temp variables to cast lx and ly to float to have a working division for UVs
myFloatly#=ly // to have a working division for UVs (just below)
// UVs
Vertices[lx,ly].u#=myFloatlx#/x // UVs values are just Textures coordinates Normalized (0-1).
Vertices[lx,ly].v#=(y-myFloatly#)/y // (must respect the way you have calculated x/z previously :)
Vertices[lx,ly].VertexNumber=VertNumber // Vertices OBJ indexes for faces
// Write the Vertex Coords (Mesh) to Obj File
WriteLine(ObjFile,"v "+str(Vertices[lx,ly].x#)+" "+str(Vertices[lx,ly].y#)+" "+str(Vertices[lx,ly].z#))
// Write the UV Coords (Textures) to Obj File => VT will have the same indexes as Vertices
// (not always true .. association relies in faces (triangles) declarations...
// See OBJ file specifications to understand)
WriteLine(ObjFile,"vt "+str(Vertices[lx,ly].u#)+" "+str(Vertices[lx,ly].v#))
inc VertNumber,1
Next
Next
For ly = StepSize To y-1 Step StepSize
// Just a progress indicator ...
if mod(ly,5)=0 : print("Creating Faces : "+str(ly)+"/"+str(y)) : Render2DFront() : Swap() : EndIf
For lx = StepSize To x-1 Step StepSize
If lx>StepSize-1
// The Complex Part : Get the correct indexes
// to create well formed faces in good order :
// one face (triangle)
v1=Vertices[lx,ly].VertexNumber
v2=Vertices[lx-StepSize,ly].VertexNumber
v3=Vertices[lx,ly-StepSize].VertexNumber
// second face (triangle)
v4=Vertices[lx,ly-StepSize].VertexNumber
v5=Vertices[lx-StepSize,ly].VertexNumber
v6=Vertices[lx-StepSize,ly-StepSize].VertexNumber
// VT indexes are the same as vertices ones.
vt1=v1 : vt2=v2 : vt3=v3 : vt4=v4 : vt5=v5 : vt6=v6
// Write Adjacent Faces to Obj File
WriteLine(ObjFile,"f "+str(v1)+"/"+str(vt1)+" "+str(v2)+"/"+str(vt2)+" "+str(v3)+"/"+str(vt3))
WriteLine(ObjFile,"f "+str(v4)+"/"+str(vt4)+" "+str(v5)+"/"+str(vt5)+" "+str(v6)+"/"+str(vt6))
EndIf
Next
Next
CloseFile(ObjFile)
//// OBJ File creation finished ? Load it.
TerrainID=LoadObject("map.obj")
//////////////// TEXTURES //////////////////////
// If a texture file is specified as parameter
If (colorMapFile$<>"")
colormap=LoadImage(colorMapFile$)
SetObjectImage(TerrainID,colormap,0) // Tex Stage 0
SetImageWrapU(colormap,1)
SetImageWrapV(colormap,1)
EndIf
// If a detailed texture is specified as parameter
If (detailMapFile$<>"")
DetailMap=LoadImage(detailMapFile$)
SetImageWrapU(DetailMap,1)
SetImageWrapV(DetailMap,1)
SetObjectImage(TerrainID,DetailMap,1) // Tex Stage 1 !!
// Loading, configuring and assigning shader :)
DetailMapShader=LoadShader("/media/vertexDetail.vs","/media/pixelDetail.ps")
SetShaderConstantByName( DetailMapShader, "ScaleDetailMap",detailSX# ,detailSY#,0, 0 ) // passed by the main function paramaters to control the appearance
SetObjectShader(TerrainID,DetailMapShader)
EndIf
EndFunction TerrainID