Code archives/3D Graphics - Mesh/Texture Splatting on Meshterrain

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

Download source code

Texture Splatting on Meshterrain by Krischan2009
This function creates a mesh terrain layer using a heightmap, a colormap and a alphamap for transparency (up to 128x128 vertices/pixel). All black pixels (0,0,0) of the alphamap are ignored. The trick is to create a full nontransparent base layer with FX2 and add the transparent FX2+32 mesh layers on top of it which results in nice faded transitions between two textures.

Here is a running demo showing the effect. With Keys 1-5 you can show/hide the base layer and the 4 transparent layers. Moving with arrows and Mouse, LMB/RMB = 2x faster, SPACE = Wireframe:



Download

Code of the demo (all images are 128x128 pixel except the textures):
Type triangle
	
	Field v%[2]
	Field alpha#
	
End Type

Global alphavertex#[65536]


; create Meshterrain
Function LoadMeshTerrain(hmap%,vscale#=16.0,cmap%,cscale#=1.0,amap%,r1%=False,g1%=False,b1%=False,base%=False)
	
	Local rgb%,r%,g%,b%,a#
	Local h#,x%,y%,vertex%
	Local vx#,vz#,u#,v#
	Local v0%,v1%,v2%,v3%
	
	; Heightmap
	If hmap Then
		Local hbuf%=ImageBuffer(hmap)
		Local size%=ImageWidth(hmap)-1
		Local verts%=size+1
	EndIf
	
	; Colormap
	If cmap Then 
		Local cbuf%=ImageBuffer(cmap)
		Local cwidth%=ImageWidth(cmap)
		Local cfactor#=cwidth*1.0/verts
	EndIf
	
	; Alphamap
	If amap Then Local abuf%=ImageBuffer(amap)
	
	; create the Mesh
	Local mesh%=CreateMesh()
	Local surf%=CreateSurface(mesh)
	
	; lock buffers
	LockBuffer hbuf
	If cmap Then LockBuffer cbuf
	If amap Then LockBuffer abuf
	
	For y=0 To size
		
		For x=0 To size
			
			; read height (only red channel) and normalize it
			h=Normalize((ReadPixelFast(x,size-y,hbuf) And $ff0000)/$10000,0,255,0.0,vscale)
			
			; read vertexcolor from colormap (R,G,B)
			If cmap Then
				rgb=ReadPixelFast(x*cfactor,(size-y)*cfactor,cbuf)
				r=Int(((rgb And $ff0000)/$10000)*cscale)
				g=Int(((rgb And $ff00)/$100)*cscale)
				b=Int((rgb And $ff)*cscale)
			Else
				r=128
				g=128
				b=128
			EndIf
			
			; read alpha value from alphamap (only red channel)
			If amap Then
				a=Normalize((ReadPixelFast(x,size-y,abuf) And $ff0000)/$10000,0,255,0,1)
			Else
				a=1.0
			EndIf
			
			; use forced colors if used
			If r1 Then r=r1
			If g1 Then g=g1
			If b1 Then b=b1
			
			; calculate vertex coordinates / texture coordinates
			vx=x-(size/2.0)
			vz=y-(size/2.0)
			u=x*1.0/size
			v=(size-y)*1.0/size
			
			; place vertex
			vertex=AddVertex(surf,vx,h,vz,u,v)
			
			; set vertex color and texture coordinates
			VertexColor surf,vertex,r,g,b,a
			
			; build alpha blitzarray for alpha check later
			alphavertex[vertex]=a
			
			; set triangles
			If y<size And x<size Then
				
				v0=x+((size+1)*y)
				v1=x+((size+1)*y)+(size+1)
				v2=(x+1)+((size+1)*y)
				v3=(x+1)+((size+1)*y)+(size+1)
				
				; add first triangle
				t.triangle = New triangle
				t\v[0]=v0
				t\v[1]=v1
				t\v[2]=v2
				
				; add second triangle
				t.triangle = New triangle
				t\v[0]=v2
				t\v[1]=v1
				t\v[2]=v3
				
			EndIf
			
		Next
		
	Next
	
	; flag all 100% transparent triangles (just add transparency values, higher than 0 = not transparent :-)
	For t.triangle = Each triangle
		
		t\alpha=alphavertex[t\v[0]]+alphavertex[t\v[1]]+alphavertex[t\v[2]]
		
	Next
	
	; create triangles
	For t.triangle = Each triangle
		
		; base layer = draw ALL triangles
		If base=True Then
			
			AddTriangle surf,t\v[0],t\v[1],t\v[2]
			
		Else
			
			; if they are not transparent
			If t\alpha>0 Then
				
				AddTriangle surf,t\v[0],t\v[1],t\v[2]
				
			EndIf
			
		EndIf
		
		; delete triangle from to do list
		Delete t.triangle
		
	Next
	
	; unlock buffers
	If amap Then UnlockBuffer abuf
	If cmap Then UnlockBuffer cbuf
	UnlockBuffer hbuf
	
	UpdateNormals mesh
	
	Return mesh
	
End Function


; Normalize a value
Function Normalize#(value#=128.0,value_min#=0.0,value_max#=255.0,norm_min#=0.0,norm_max#=1.0)
	
	Return ((value-value_min)/(value_max-value_min))*(norm_max-norm_min)+norm_min
	
End Function

Comments

RifRaf2009
Brilliant, thanks for sharing.


BlitzSupport2009
Nice, works well here.


Beaker2009
My Samsung NC10 has a problem. At certain angles and distances the grass layer pops in and out of existence showing the background color.


Krischan2009
Beaker, try it this way: I revised it and it works with only one mesh now, gaining 10% speed. Just replace the texture_splatting.bb with this one and run demo2.bb (or download the whole package again).

Download Singlemesh-Version

demo2.bb


texture_splatting.bb



Beaker2009
Unfortunately, still the same problem.


_PJ_2009
This is really very useful, thanks Krishan!

Compared to many other examples of multi-textured terrains this is the easiest to use, whilst still giving great results!


Krischan2009
Beaker: I dunno, perhaps the Intel GMA950 doesn't allow meshes displayed in this way - you could experiment with the order the meshes/surfaces are drawn. Here on my nVidia 9600M GT it works fine.

To top it - I experimented with normal maps to dramatically increase the detail level of the poor 128x128 vertex resolution and here is my solution, again texture splatted. I got it to work only using two meshes - one multitextured and one with the normalmap put on top of it. Lighting is a combination of vertex light and dot3 lighting.

You can change the sunlight angle (Y axis, 0...360°) with the Keys Q/W and the sun inclination angle with A/S (-90 to 90°). With the keys 1/2 you can show/hide the multitextured mesh and the normal map mesh to see the difference!

Beware: i am using a very hires 2048x2048 normal map texture here, so resize it if it doesn't work!

Download



demo_normals.bb (the include is the same file like in my single-mesh-version)



RifRaf2009
I like the method, but is this texturesplatting? since you are still using vertex alpha for the transparency and not really a blendmode of textures.


Code Archives Forum