Hi all,
This class read a TERRAGEN (.ter) file and converts it to a GeomNode
that can be displayed. Furthermore, you will be able to save it to a BAM
file. The code is based on the TERRAGEN Blender import.
This is the Terrain class:
#################################################################
# #
# Ter2Blend v4 TERRAIN #
# #
# Terragen's terrain file to Blender file converter #
# #
# copyright (c) 2003 Guy Van Rentergem #
# #
# email: guy.van.rentergem@skynet.be #
# #
# #
# Script only works with Blender 2.28c or later and #
# a Python 2.2.2 #
# #
# Terragen stuff
# --------------
#
# standard terragen terrains can have the following sizes:
# 129 * 129 = 16641 vertices
# 257 * 257 = 66049
# 513 * 513 = 263169
# 1025 * 1025 = 1050625
# 2049 * 2049 = 4198401
# 4097 * 4097 = 16785409 (will blow the memory out of your computer!)
#
# Due to memory and speed considerations let's use only one mesh and
# keep the maximum number of vertices to 65535 (Blender limit)
# So to keep things simple we will only allow terrains up 257 * 257
# This means terrains of 129 * 129 and 257 * 257 (this one already needs some clipping...)
# Later we will do some coding so the mesh can be divided in pieces and all
# kind of formats will be allowed, but then a powerfull machine is needed.
#
# default settings in Terragen: sun heading -60 degrees (from north)
# sun altitude 25 degrees from groundlevel
#
# in Terragen the (0,0) is located in the left bottom corner
#
# important: USE ONLY TERRAIN UNITS FROM TERRAGEN
#
# Blender camera lens 22.70 seems to fit the standard lens of Terragen
# Terragen script file
# --------------------
#
# script commands usefull for Ter2Blend:
# InitAnim BaseName, StartFrame - only use for StartFrame, the first frame to rendered
# CamPos x,y,z - sets the camera's position in Terrain Units
# TarPos x,y,z - sets the target's position in Terrain Units
# CamH head - sets the camera's heading in degrees
# CamP pitch - sets the camera's pitch in degrees
# CamB bank - sets the camera's bank in degrees
# SunDir head, alt - sets the heading and altitude of the sun in degrees (for later...)
# FRend - renders a frame
#
# Forbidden script commands in Ter2Blend:
# Zoom z - sets the Zoom value
# Exp e - sets the Exposure value
#
# Useless script commands in Ter2Blend:
# CloudPos x,y
# CloudVel Speed, Heading
# Import Panda3D modules
from pandac.PandaModules import *
import struct
"""
This class imports a terrain from the TERRAGEN tool. File with
extension '.ter'
This code is based on the blender import for TERRAGEN terrain files
"""
class Terrain:
def __init__(self):
self.terrainGN = None
def load(self, iFileName):
"""This method reads a terrain file and returns a Panda node
"""
if iFileName == "":
print "Empty file name provided"
return None
# Open the file
try:
ter = open(iFileName, 'rb')
except IOError:
print "Unable to open %s file" % (iFileName)
return None
# Process the file
# Read the header
if ter.read(8) == 'TERRAGEN':
if ter.read(8) == 'TERRAIN ':
print 'Terragen terrain file: continue'
else:
print "TERRAIN keyword not found"
return None
else:
print "TERRAGEN keyword not found"
return None
keys=['SIZE','XPTS','YPTS','SCAL','CRAD','CRVM','ALTW']
totest = ter.read(4)
while 1:
if totest in keys:
if totest == 'SIZE':
(size,) = struct.unpack('h',ter.read(2))
garbage = ter.read(2)
if totest == 'XPTS':
(xpts,) = struct.unpack('h',ter.read(2))
garbage = ter.read(2)
if totest == 'YPTS':
(ypts,) = struct.unpack('h',ter.read(2))
garbage = ter.read(2)
if totest == 'SCAL':
(scalx,) = struct.unpack('f',ter.read(4))
(scaly,) = struct.unpack('f',ter.read(4))
(scalz,) = struct.unpack('f',ter.read(4))
if totest == 'CRAD':
(crad,) = struct.unpack('f',ter.read(4))
if totest == 'CRVM':
(crvm,) = struct.unpack('H',ter.read(2))
garbage = ter.read(2)
if totest == 'ALTW':
(heightscale,) = struct.unpack('h',ter.read(2))
(baseheight,) = struct.unpack('h',ter.read(2))
break
totest = ter.read(4)
else:
break
# print "xpts = %d ypts = %d" % (xpts, ypts)
if xpts==0:
xpts = size+1
if ypts==0:
ypts = size+1
# Print some debug information
print "size = %d" % (size)
print "xpts = %d" % (xpts)
print "ypts = %d" % (ypts)
print "scalx = %d" % (scalx)
print "scaly = %d" % (scaly)
print "scalz = %d" % (scalz)
print "crad = %d" % (crad)
print "crvm = %d" % (crvm)
print "heightscale = %d" % (heightscale)
print "baseheight = %d" % (baseheight)
# Read all the points
points = []
# Create node
#step 1) create GeomVertexData and add vertex information
format=GeomVertexFormat.getV3()
vdata=GeomVertexData("vertices", format, Geom.UHStatic)
vertexWriter=GeomVertexWriter(vdata, "vertex")
# read them all...
for y in range(0,ypts):
for x in range(0,xpts):
(h,) = struct.unpack('h',ter.read(2))
z = 0.0
z = baseheight + h*heightscale/65536.0
#print "x = %f y = %f z = %f" % (x, y, z)
vertexWriter.addData3f(x*scalx, y*scaly, z*scalz)
xmax=x+1
ymax=y+1
ter.close()
# Create triangles
# ----------------
#step 2) make primitives and assign vertices to them
tris=GeomTriangles(Geom.UHStatic)
for y in range(0,ymax-1):
for x in range (0,xmax-1):
a=x+y*(ymax)
## f=NMesh.Face()
## f.v.append(terrainName.verts[a])
## f.v.append(terrainName.verts[a+ymax])
## f.v.append(terrainName.verts[a+ymax+1])
## f.v.append(terrainName.verts[a+1])
## terrainName.faces.append(f)
# TERRAGEN defines squares, so, we are going to divide them
# into two triangles
# First triangle
tris.addVertex(a)
tris.addVertex(a + ymax)
tris.addVertex(a + ymax + 1)
#indicates that we have finished adding vertices for the first triangle.
tris.closePrimitive()
# Second triangle
tris.addVertex(a + ymax + 1)
tris.addVertex(a + 1)
tris.addVertex(a)
#indicates that we have finished adding vertices for the first triangle.
tris.closePrimitive()
#step 3) make a Geom object to hold the primitives
terrainGeom = Geom(vdata)
terrainGeom.addPrimitive(tris)
#now put squareGeom in a GeomNode. You can now position your geometry
# in the scene graph.
self.terrainGN=GeomNode("terrain")
self.terrainGN.addGeom(terrainGeom)
return self.terrainGN
def getGeomNode(self):
return self.terrainGN
def writeToFile(self, iFileName):
if iFileName == "":
print "Empty file name provided"
return
if self.terrainGN != None:
# Convert the node from GeomNode to NodePath
np = NodePath(self.terrainGN)
# Write to a bam file
np.writeBamFile(iFileName)
return
And, this is a small example of usage:
import direct.directbase.DirectStart
# Import Panda3D modules
from pandac.PandaModules import *
from terrain import Terrain
tn = Terrain()
newNode = tn.load("prueba.ter")
base.camera.setPos(100, -30, 50)
render.attachNewNode(newNode)
tn.writeToFile("prueba.bam")
run()
I hope it will be useful.
Regards.
Alberto
[/code]