Jump to content

Code for fast terrain editing


Josh
 Share

Recommended Posts

To edit the terrain heightmap on the GPU, I create a small buffer and copy part of the heightmap to this. Then I enable the terrain editor shader and draw this small buffer back onto the same spot on the heightmap buffer:

Strict

Import leadwerks.ENGINE
Import "terrain.bmx"
Import "Globals.bmx"

Global oldTerrainToolPosition:TVec3=Vec3(-1000)

Global terrainfilter:TShader[10]

Global terrainchanged:Int

Global editterrainaabb:TAABB

TTerrain.highresheightmap=1

Function UpdateTerrain(terrain:TTerrain)
Local x:Int,z:Int,size:Int
If terrainchanged
	'terrain.generatecolormap()
	If terrainchanged=2
		terrain.getheightmap()
		If TerrainSectorAABBUpdate
			size=Int(Sqr(TerrainSectorAABBUpdate.length)+0.5)
			For x=0 To size-1
				For z=0 To size-1
					If TerrainSectorAABBUpdate[x,z]
						terrain.sector[x,z].UpdateAABB()
						TerrainSectorAABBUpdate[x,z]=0
					EndIf
				Next
			Next
		EndIf
		If editterrainaabb
			ForEachEntityInAABBDo(editterrainaabb,callback,terrain,ENTITY_MODEL)
			terrain.quadtree.root.ForEachLeafInAABBDo(editterrainaabb,func,Null)
			terrain.quadtree.root.UpdateAABB()
		EndIf
	EndIf
	editterrainaabb=Null
	terrainchanged=False
EndIf

Function func(leaf:TQuadTreeLeaf,o:Object)
	leaf.UpdateInstanceHeights(terrain)
EndFunction

Function Callback:Int(entity:TEntity,extra:Object)
	If Int(entity.GetKey("aligntoground"))
		entity.AlignToTerrain(TTerrain(extra))
	EndIf
	Return 1
EndFunction

EndFunction

Global TerrainSectorAABBUpdate:Byte[,]


Function AddVegetationInstance(terrain:TTerrain,x:Float,z:Float,layer:Int,density:Float,randrotation:Int,randscale:Float,aligntonormal:Int,tilt:Float)
Local pitch#,yaw#,dist#
Local dir:TVec3
Local roll:Float=0.0
Local mat:TMat4
Local scale:Float
Local alignmat:TMat4

If terrain.quadtree.layers[layer]=Null Return
If terrain.quadtree.layers[layer].model=Null Return
If randrotation
	mat=TMat4.FromYawPitchRoll(Rnd(0.0,360.0),Rnd(-tilt,tilt),Rnd(-tilt,tilt))
Else
	'SeedRnd (mat.ix+mat.iy+mat.iz+mat.jx+mat.jy+mat.jz+mat.kx+mat.ky+mat.kz+mat.tx+mat.ty+mat.tz)*1000.0
	mat=TMat4.FromYawPitchRoll(0.0,Rnd(-tilt,tilt),Rnd(-tilt,tilt))
EndIf
'SeedRnd (mat.ix+mat.iy+mat.iz+mat.jx+mat.jy+mat.jz+mat.kx+mat.ky+mat.kz+mat.tx+mat.ty+mat.tz)*1000.0
scale=Rnd(1.0-randscale,1.0+randscale)				
mat.ix:*scale
mat.iy:*scale
mat.iz:*scale
mat.jx:*scale
mat.jy:*scale
mat.jz:*scale
mat.kx:*scale
mat.ky:*scale
mat.kz:*scale
mat.tx=x'x+x0*density+Rnd(-density*0.5,density*0.5)
mat.tz=z'z+z0*density+Rnd(-density*0.5,density*0.5)
mat.ty=terrain.GetElevation(mat.tx,mat.tz)
If aligntonormal	
	dir=terrain.CalcNormal(x,z)
	dist#=Sqr(dir.x#*dir.x#+dir.z#*dir.z#)
	pitch#=ATan2(-dir.y#,dist#)
	yaw#=ATan2(-dir.x#,dir.z#)
	If randrotation
		'SeedRnd (mat.ix+mat.iy+mat.iz+mat.jx+mat.jy+mat.jz+mat.kx+mat.ky+mat.kz+mat.tx+mat.ty+mat.tz)*1000.0
		roll=Rnd(0.0,360.0)
	EndIf
	alignmat=TMat4.FromYawPitchRoll(yaw,pitch,roll)
	alignmat.tx=mat.tx
	alignmat.ty=mat.ty
	alignmat.tz=mat.tz
	mat.ix=-alignmat.ix*scale
	mat.iy=-alignmat.iy*scale
	mat.iz=-alignmat.iz*scale
	mat.jx=alignmat.kx*scale
	mat.jy=alignmat.ky*scale
	mat.jz=alignmat.kz*scale
	mat.kx=alignmat.jx*scale
	mat.ky=alignmat.jy*scale
	mat.kz=alignmat.jz*scale
	If randrotation
		'SeedRnd (mat.ix+mat.iy+mat.iz+mat.jx+mat.jy+mat.jz+mat.kx+mat.ky+mat.kz+mat.tx+mat.ty+mat.tz)*1000.0
		mat=mat.times(TMat4.FromYawPitchRoll(0.0,Rnd(-tilt,tilt),Rnd(-tilt,tilt)))
	EndIf
EndIf
terrain.quadtree.addinstance(mat,layer,density)
EndFunction


Global toolimage:TTexture

Function EditTerrain(terrain:TTerrain,x:Float,z:Float,outerradius:Float,innerradius:Float,editmode:Int=0,strength:Float=1.0,channel:Int=0,flattenheight:Float,toolfloor#=0.0,toolceiling#=1.0)

Global toolimagebuffer:TBuffer
Global channelmask:Float[4]

Local x0:Float,z0:Float,x1:Float,z1:Float
Local w:Int,h:Int
Local tw:Int,th:Int
Local cx:Float,cy:Float
Local terrainsize:Float
Local buffer:TBuffer
Local format:Int
Local terrbuffer:TBuffer
Local texture:TTexture
Local constraints:Float[2]
Local i:Int
Local scale:Float
Local ix:Float=x
Local iz:Float=z

scenechanged=1

If editmode=4
	Local gx0:Int,gy0:Int
	Local gx1:Int,gy1:Int

	x=Round(x/terrain.scale.x+0.5)+terrain.resolution/2-1
	z=Round(z/terrain.scale.z+0.5)+terrain.resolution/2-1	

	gx0=x-outerradius
	gx1=x+outerradius
	gy0=z-outerradius
	gy1=z+outerradius

	gx0=Max(0,gx0)
	gx0=Min(gx0,terrain.resolution-1)
	gy0=Max(0,gy0)
	gy0=Min(gy0,terrain.resolution-1)
	gx1=Max(0,gx1)
	gx1=Min(gx1,terrain.resolution-1)
	gy1=Max(0,gy1)
	gy1=Min(gy1,terrain.resolution-1)

	w=(gx1-gx0)/2
	For x0=gx0 To gx1
		For z0=gy0 To gy1
			If strength>0
				terrain.SetTileVisibility(x0,z0,0,0)
			Else
				terrain.SetTileVisibility(x0,z0,1,0)
			EndIf
		Next
	Next
	terrain.UpdateVisibilityTexture(gx0,gy0,gx1-gx0+1,gy1-gy0+1)
	Return
EndIf

If editmode=5

	If Not CurrentVegetationLayer Return

	'If Not terrain.quadtree.layers[TerrainVegetationLayer] Return
	'If Not terrain.quadtree.layers[TerrainVegetationLayer].model Return
	'If SelectedGadgetItem(Gadget_VegetationList)=-1 Return
	If strength>0.0

		'Add instances
		Local tilt:Float=CurrentVegetationLayer.randomtilt'VegetationTilt[TerrainVegetationLayer]
		Local density:Float=CurrentVegetationLayer.density'VegetationDensity[TerrainVegetationLayer]'Float(GadgetText(Gadget_VegetationDensity))
		For x0=-outerradius/density To outerradius/density
			For z0=-outerradius/density To outerradius/density
				If Sqr(x0*density*x0*density+z0*density*z0*density)>outerradius Continue
				'SeedRnd x+x0*density+z+z0*density
				cx=x+x0*density+Rnd(-density*0.5,density*0.5)
				cy=z+z0*density+Rnd(-density*0.5,density*0.5)		
				AddVegetationInstance(terrain,cx,cy,CurrentVegetationLayer.index,CurrentVegetationLayer.density,CurrentVegetationLayer.randomrotation,CurrentVegetationLayer.randomscale,CurrentVegetationLayer.aligntoterrain,CurrentVegetationLayer.randomtilt)
			Next
			GCCollect()
		Next

	Else

		'Remove instances
		Local aabb:TAABB=New TAABB
		aabb.x0=x-outerradius
		aabb.x1=x+outerradius
		aabb.y0=-infinity
		aabb.y1=infinity
		aabb.z0=z-outerradius
		aabb.z1=z+outerradius
		aabb.update()
		terrain.quadtree.root.ForEachLeafInAABBDo(aabb,func,vec3(x,outerradius,z))

		Function func(leaf:TQuadTreeLeaf,o:Object)
			Local n:Int
			Local dx:Float,dz:Float
			Local t:TVec3
			Local count:Int

			t=TVec3(o)
			count=leaf.instance_count[CurrentVegetationLayer.index]
			n=0
			If count
				Repeat
					dx=Abs(leaf.instances[CurrentVegetationLayer.index][n*16+12]-t.x)
					dz=Abs(leaf.instances[CurrentVegetationLayer.index][n*16+14]-t.z)
					If Sqr(dx*dx+dz*dz)<t.y
						leaf.DeleteInstance(n,CurrentVegetationLayer.index)
						n:-1
						count:-1
					EndIf
					If n>=count-1 Exit
					If count<1 Exit
					n:+1
				Forever
			EndIf
		EndFunction

	EndIf
	Return
EndIf

If editmode=1
	terrainchanged=1
Else
	terrainchanged=2
EndIf

If Not TerrainSectorAABBUpdate Or TerrainSectorAABBUpdate.length<>terrain.sectors*terrain.sectors
	TerrainSectorAABBUpdate=New Byte[terrain.sectors,terrain.sectors]
EndIf

If editmode<>1
	terrbuffer=terrain.heightbuffer
	texture=terrain.heightmap
Else
	terrbuffer=terrain.alphabuffer
	texture=terrain.alphatexture
EndIf

x0=Floor((0.5+(x-outerradius+0.5*terrain.scale.x)/(Float(terrain.resolution)*terrain.meterspertile))*(terrain.resolution-1))
x1=Ceil((0.5+(x+outerradius+0.5*terrain.scale.x)/(Float(terrain.resolution)*terrain.meterspertile))*(terrain.resolution-1))
z0=Floor((0.5+(z-outerradius+0.5*terrain.scale.x)/(Float(terrain.resolution)*terrain.meterspertile))*(terrain.resolution-1))
z1=Ceil((0.5+(z+outerradius+0.5*terrain.scale.x)/(Float(terrain.resolution)*terrain.meterspertile))*(terrain.resolution-1))

'Figure out which sectors are affected and need their AABBs updated (skip for paint edit mode)
If editmode<>1 And editmode<>4 And editmode<>5
	Local sx0:Int,sx1:Int,sz0:Int,sz1:Int
	sx0=Floor(x0/Float(terrain.sectorresolution))
	sx1=Ceil(x1/Float(terrain.sectorresolution))-1
	sz0=Floor(z0/Float(terrain.sectorresolution))
	sz1=Ceil(z1/Float(terrain.sectorresolution))-1
	If sx0<0 sx0=0
	If sz0<0 sz0=0
	If sx1>terrain.sectors-1 sx1=terrain.sectors-1
	If sz1>terrain.sectors-1 sz1=terrain.sectors-1
	For x=sx0 To sx1
		For z=sz0 To sz1
			TerrainSectorAABBUpdate[x,z]=1
		Next
	Next
EndIf

'Create small image
If editmode=1
	format=TEXTURE_RGBA8
Else
	If GetGraphicsVendor()=VENDOR_NVIDIA
		If GetShaderModel()=4
			format=TEXTURE_FLOAT32
		Else
			format=TEXTURE_FLOAT
			Print 1
		EndIf
	Else
		format=TEXTURE_FLOAT32
	EndIf
EndIf
w=x1-x0+1+1+1
h=z1-z0+1+1+1
tw=Pow2(w,1)
th=Pow2(h,1)
If tw<2 tw=2
If th<2 th=2	
'If tw<64 tw=64
'If th<64 th=64	
If toolimage
	If toolimage.width()<>tw Or toolimage.height()<>th
		toolimage=Null
	EndIf
EndIf
If toolimage
	If TextureFormat(toolimage)<>format toolimage=Null
EndIf
If Not toolimage
	toolimage=CreateTexture(tw,th,format)
	'If TextureFormat(toolimage)<>format Notify 1
	toolimagebuffer=CreateBuffer(tw,th,BUFFER_COLOR)
	toolimage.setfilter(TEXFILTER_PIXEL)
	toolimage.bind()
	SetColorBuffer(toolimagebuffer,toolimage)		
EndIf

If editmode=1
	Select channel
		Case 0
			If strength>0.0
				channelmask[0]=-1.0
				channelmask[1]=-1.0
				channelmask[2]=-1.0
				channelmask[3]=-1.0
			Else
				channelmask[0]=0.0
				channelmask[1]=0.0
				channelmask[2]=0.0
				channelmask[3]=0.0
			EndIf
		Case 1
			If strength>0.0
				channelmask[0]=1.0
				channelmask[1]=-1.0
				channelmask[2]=-1.0
				channelmask[3]=-1.0
			Else
				channelmask[0]=1.0
				channelmask[1]=0.0
				channelmask[2]=0.0
				channelmask[3]=0.0
			EndIf
		Case 2
			If strength>0.0
				channelmask[0]=0.0
				channelmask[1]=1.0
				channelmask[2]=-1.0
				channelmask[3]=-1.0
			Else
				channelmask[0]=0.0
				channelmask[1]=1.0
				channelmask[2]=0.0
				channelmask[3]=0.0
			EndIf
		Case 3
			If strength>0.0
				channelmask[0]=0.0
				channelmask[1]=0.0
				channelmask[2]=1.0
				channelmask[3]=-1.0
			Else
				channelmask[0]=0.0
				channelmask[1]=0.0
				channelmask[2]=1.0
				channelmask[3]=0.0
			EndIf
		Case 4
			If strength>0.0
				channelmask[0]=0.0
				channelmask[1]=0.0
				channelmask[2]=0.0
				channelmask[3]=1.0
			Else
				channelmask[0]=0.0
				channelmask[1]=0.0
				channelmask[2]=0.0
				channelmask[3]=1.0
			EndIf
	EndSelect
Else
	channelmask[0]=1.0
	channelmask[1]=1.0
	channelmask[2]=1.0
	channelmask[3]=1.0

	If Not editterrainaabb
		editterrainaabb=New TAABB
		editterrainaabb.x0=-infinity
		editterrainaabb.y0=-infinity
		editterrainaabb.z0=-infinity
		editterrainaabb.x1=infinity
		editterrainaabb.y1=infinity
		editterrainaabb.z1=infinity
	EndIf
	editterrainaabb.x0=Min(editterrainaabb.x0,x-outerradius)
	editterrainaabb.z0=Min(editterrainaabb.z0,z-outerradius)
	editterrainaabb.x1=Max(editterrainaabb.x1,x+outerradius)
	editterrainaabb.z1=Max(editterrainaabb.z1,z+outerradius)		
EndIf

'Disable heightmap linear filter
glActiveTextureARB GL_TEXTURE15
glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST
glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST
glActiveTextureARB GL_TEXTURE0

'Draw heightmap onto small image
SetBuffer(toolimagebuffer)
SetShader Null
'Global Texture_Contour:TTexture
'If Not Texture_Contour Texture_Contour=LoadTexture("abstract::crysisocean_DISP.dds")
'BindTexture Texture_Contour,1
SetColor vec4(1)
FlipImage(texture,-x0+1.0,-z0+1.0)
SetColor vec4(1)
SetBuffer(BackBuffer())

terrainsize=(Float(terrain.resolution)*Terrain.meterspertile)
ix:-terrain.scale.x*0.5
iz:-1
cx=((ix/terrain.scale.x+0.5*Float(terrain.resolution))/Float(terrain.resolution))+0.5/Float(terrain.resolution)
cy=((iz/terrain.scale.z+0.5*Float(terrain.resolution))/Float(terrain.resolution))+0.5/Float(terrain.resolution)

'Load terrain edit shader
If Not terrainfilter[editmode]
	terrainfilter[editmode]=LoadShader("abstract::generic.vert","abstract::terraintool.frag","#define TOOLMODE "+editmode+"~n")
EndIf

'Set shader uniforms
toolceiling=Min(1.0,toolceiling)
toolfloor=Max(0.0,toolfloor)
constraints[0]=toolfloor'0'Float(SliderValue(Gadget_TerrainToolFloor)-1)/29.0
constraints[1]=toolceiling'1'Float(SliderValue(Gadget_TerrainToolCeiling)-1)/29.0
If strength>0.0 constraints[0]=0.0
If strength<0.0 constraints[1]=1.0
If editmode>1
	constraints[0]=0.0
	constraints[1]=1.0	
EndIf
SetShaderVec2 terrainfilter[editmode],"toolposition",[cx,cy]
SetShaderVec2 terrainfilter[editmode],"toolradius",[outerradius/terrain.size,innerradius/terrain.size]
SetShaderFloat terrainfilter[editmode],"toolstrength",strength
SetShaderFloat terrainfilter[editmode],"flattenheight",flattenheight
SetShaderVec2 terrainfilter[editmode],"heightmapsize",[Float(terrain.resolution),Float(terrain.resolution)]
SetShaderVec2 terrainfilter[editmode],"imagesize",[Float(tw),Float(th)]
SetShaderVec4 terrainfilter[editmode],"channelmask",channelmask
SetShaderVec2 terrainfilter[editmode],"constraints",constraints

'Draw the small image onto the heightmap
buffer=GetBuffer()
SetBuffer(terrbuffer)
SetShader terrainfilter[editmode]
glCheckError()
flipImage(toolimage,x0-1,z0-1)
glCheckError()
SetBuffer buffer
SetShader(Null)

'Update the normals of the edited area
If editmode<>1
	terrain.UpdateNormals(x0-1,z0-1,w,h)
EndIf

'Restore heightmap linear filter
glActiveTextureARB GL_TEXTURE15
glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR
glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR
glActiveTextureARB GL_TEXTURE0

terrain.generatecolormap()
EndFunction


Function FlipImage(image:TTexture,x,y)
image.bind(0)
glcolor4f 1,1,1,1
glEnable image.target()
gldisable GL_CULL_FACE
DrawRect x,BufferHeight(GetBuffer())-y,image.width(),-image.height()
glDisable image.target()

Function DrawRect(x,y,width,height)
	glBegin GL_QUADS
	glTexCoord2f 0.0,0.0
	glVertex2i x,y
	glTexCoord2f 0.0,1.0
	glVertex2i x,y+height
	glTexCoord2f 1.0,1.0
	glVertex2i x+width,y+height
	glTexCoord2f 1.0,0.0
	glVertex2i x+width,y
	glEnd
EndFunction	

EndFunction

My job is to make tools you love, with the features you want, and performance you can't live without.

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...