Rendering of Heightfields

Thanks to some help from JIB and kelly In This Thread I managed to come up with a really good, general purpose, heightmap rendering node with random heights. Try it and let me know what you think …

Here is the file Grid.py that holds the node creation code :


from direct.gui.OnscreenText import OnscreenText 
from direct.showbase import DirectObject
from direct.showbase.DirectObject import DirectObject
import direct.directbase.DirectStart
from direct.task import Task
import sys
from pandac.PandaModules import *
import random

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

def CreateRandomHeightmapNode(xVertexColCount,yVertexRowCount,verbose) :

	# Define the vetex data format (GVF). 
	gvf = GeomVertexFormat.getV3n3c4t2 ()

	# Create the vetex data container (GVD) using the GVF from above to
	# describe its contents.
	gvd = GeomVertexData('GridVertices',gvf,Geom.UHStatic)

	# Create writers for the GVD, one for each type of data (column) that
	# we are going to store in it.
	gvwV = GeomVertexWriter(gvd, 'vertex')
	gvwT = GeomVertexWriter(gvd, 'texcoord')
	gvwC = GeomVertexWriter(gvd, 'color')
	gvwN = GeomVertexWriter(gvd, 'normal')

	# Use the writers to add vertex data to the GVD.

	xColOffset = float(xVertexColCount-1)*0.5
	yRowOffset = float(yVertexRowCount-1)*0.5

	xColScale = 1.0 / float(xVertexColCount)
	yRowScale = 1.0 / float(yVertexRowCount)

	if verbose :
		print "Rows, Row Offset : ",yVertexRowCount,yRowOffset
		print "Cols, Col Offset : ",xVertexColCount,xColOffset

	# Compute function for computation of texture coordinates. This
	# will map the range (0,yVertexRowCount-1) onto (0,1) and it
	# will map the range (0,xVertexColCount-1) onto (0,1)
	xTexSlope = 1.0 / float(xVertexColCount-1)
	yTexSlope = 1.0 / float(yVertexRowCount-1)

	k = 0
	gRan = random.Random(5033)
	for yRow in range(0, yVertexRowCount): 

		if verbose :
			print "Creating Vertices in Row ",yRow," with ",\
				xVertexColCount," vertices in the column"

		y = yRowScale*(float(yRow) - float(yRowOffset))
		ty = yRow*yTexSlope

		for xCol in range(0, xVertexColCount): 

			z = gRan.random()
			x = xColScale*(float(xCol) - float(xColOffset))
			tx = xCol*xTexSlope

			gvwV.addData3f(x, y, z)
			gvwN.addData3f(0, 0, 1)
			gvwC.addData4f(0, z, z, 1)
			gvwT.addData2f(tx, ty)

			if verbose :
				str = "\tVert Col(%d) : %f,%f,%f" % (k,x, y, z)
				print str
				k = k + 1

	# Create a GeomPrimitive object to use the vertices in the GVD
	# then fill it with our grid data.

	geom = Geom(gvd)

	xTriangleColCount = xVertexColCount - 1
	yTriangleRowCount = yVertexRowCount - 1

	for yRow in range(0, yTriangleRowCount) :

		if verbose :
			print "Creating Triangles in Row ",yRow," with ", \
				xTriangleColCount, " triangle pairs in the column"

		for xCol in range(0, xTriangleColCount):

			tris = GeomTriangles(Geom.UHStatic)

			v0 = yRow*xVertexColCount + xCol
			v1 = v0 + 1
			v2 = v1 + xVertexColCount
			v3 = v2 - 1

			tris.addVertex(v0)
			tris.addVertex(v1)
			tris.addVertex(v2)
			tris.closePrimitive()

			tris.addVertex(v0)
			tris.addVertex(v2)
			tris.addVertex(v3)
			tris.closePrimitive()

			geom.addPrimitive(tris)

			if verbose :
				str = "\tTris : %d,%d,%d   %d,%d,%d" % (v0,v1,v2, v0,v2,v3)
				print str

	node = GeomNode('grid')
	node.addGeom(geom)

	return node

Here is the file GridTest.py that holds the test driver :


from direct.gui.OnscreenText import OnscreenText 
from direct.showbase import DirectObject
from direct.showbase.DirectObject import DirectObject
import direct.directbase.DirectStart
from direct.task import Task
import sys
from pandac.PandaModules import *
from Grid import CreateRandomHeightmapNode

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

class World(DirectObject):
	def __init__(self):
		# Setup events
		self.accept('escape',self.handleEscapeDown)

		self.mTextDisplay = \
			OnscreenText( \
				text = 'Random Heightmap Node Test', \
				pos = (-0.9,0.9), \
				scale = 0.07, \
				fg = (1,1,0,1), \
				mayChange = True
			)

		xyscale = 25.0
		zscale = 2.0

		base.camLens.setFar(9000)
		base.camLens.setFov(55, 75)
		base.cam.setPos(0,-3*xyscale,xyscale)
		base.cam.setHpr(0,-10,0)

		# Create heightmap node

		xVertexColCount = 8
		yVertexRowCount = 4
		verbose = False

		gNode = \
			CreateRandomHeightmapNode( \
				xVertexColCount,yVertexRowCount,verbose \
			)
		self.mGridNodePath = render.attachNewNode(gNode)
		self.mGridNodePath.setScale(xyscale,xyscale,zscale)

		# Setup lighting
		lightAttribute = LightAttrib.makeAllOff()
		dirLight = DirectionalLight('DirLight')
		dirLight.setColor(Vec4(0.6,0.6,0.6,1.0))
		dirLightNP = render.attachNewNode(dirLight.upcastToPandaNode()) # crashes without upcast
		dirLightNP.setPos(Vec3(0.0,-10.0,10.0))
		dirLightNP.setHpr(Vec3(0.0,-26.0,0.0))
		lightAttribute = lightAttribute.addLight(dirLight) # add to attribute
		ambientLight = AmbientLight('ambientLight')
		ambientLight.setColor(Vec4(0.25,0.25,0.25,1.0))
		ambientLightNP = render.attachNewNode(ambientLight.upcastToPandaNode())
		lightAttribute = lightAttribute.addLight(ambientLight)
		render.node().setAttrib(lightAttribute)

	def handleEscapeDown(self):
		sys.exit()

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# 

world = World()

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Run the program

run()
1 Like

i don’t like it.

Hey, that looks pretty good!

Excellent work getting the geometry generation engine to obey you :slight_smile:

As a quick heads-up to anyone testing this engine: the line in Grid.py that says

gRan = random.Random(5033)

provides a static seed to the random number generator, so it always outputs the same sequence of numbers. If you’re curious as to why the terrain looks the same every time, removing the ‘5033’ sequence will use the default random constructor and give you different random numbers each time.

There are a lot of changes that could be made, but each person is going to be looking for something unique from a terrain engine. They can use this template to start building exactly what they need; thanks for sharing it!

Take care,
Mark

LOL! Something tells me you aren’t gonna be around here for long …

discourse.panda3d.org/search.ph … okamosalic