#!BPY """ Name: 'Terragen (.ter)' Blender: 244 Group: 'Import' Tooltip: 'Terragen Terrain Import Script, It also imports camera and light positions' """ # # Ter2Blend # # Terragen's terrain file to Blender file converter # # # Copyright (c) 2003 Guy Van Rentergem # Copyright (c) 2008 David Johnson (Building on the great work by Guy) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version.# # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # History # ------- # # 10-jul-00: started coding, must've been drunk :) # 15-jul-00: v0.1 is ready, first tries # 20-jul-00: announcement to the Blender and Terragen community # 24-jul-00: 10 days holiday... # 07-aug-00: v0.2 found that fat bug in the camera code # left targetx, targety, targetz out of the code # inserted instead head, pitch, bank # Started writing a basic tutorial # 08-aug-00: Putting v0.2 and the tutorial on my website # Cleared a bug about the maximum mesh size thanks to Frank # 10-aug-00: started coding v1.0 # 12-aug-00: finished the 100 km Dodentocht at Bornem, Yes on foot! # 16-aug-00: testing and finishing v1.0 # 17-aug-00: announcement to the Blender and Terragen community # 20-aug-00: release of v1.0 on the web # 25-aug-00: Ter2Blend v1.1. Big bug discovered by D. Graham. The camerascript # only worked correct with scripts produced with Terramin. Campath generated scripts # couldn't be read. Thanks Dan! # 29-aug-00: create a ter2blend.txt file containing the settings of the last time # the program was used. Things to be saved: camera settings, .ter file, # sun settings and watersetting # 06-sep-00: facelift of the userinterface # 14-sep-00: added a status bar while calculating the surface mesh # 17-sep-00: The scripts now look for ter2blend.txt and ter2blend.tgs in the directory # where the ter2blend.blend file was started. This allows to work with different # projects placed in different directories. # 18-sep-00: Release of v1.2 on the web # 15-jul-01: Checked script if it works with Blender 2.14 # put in a warning if terrain is to big # changed version number to v2 # 29-jul-01: Some changes in the texts displayed on the GUI. changed version number to v3. # 03-oct-03: ************ Ter2Blend v4 **************** # After a long time again a new release!!! At last Blender has a new and stable # Python API. Many thanks to Ton and all the other fantastic developers of Blender. # Fixed that stupid banking bug!!! # This is worth a new version number of the ter2blend script: v4! # Blender Rules!! # 14-jan-08 Work begins again on the project. Added new License copy right # to the top document, to bring it in to GPL, orginal author credited. License applies # from this version onwards # Things to do # ------------ # # * make it possible to work with terrains bigger than 257 x 257 (even now # there is some clipping to 255 x255). The problem is that this will slow # down the working of Blender considerable and maybe will blow out the memory # out of the computer ;) # * do the dishes, mow the lawn,... # Blender limits # -------------- # Render faces: 1024 k # Render vertices: 1024 k # number of vertices in a mesh: 65535 # # Additional vertices are permitted in EditMode, but # Blender generates a warning and blocks EditMode until # the command "SEPARATE" (PKEY) is used to place some of the # vertices and faces in separate meshes. # # definition of the sun: 1 spotlight (only light that can cast shadows in Blender) # and one hemilight put far away of the terrain # 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 ### MOD by pat ### all changes marked like this one ### ### list of changes: ### import glob commented ### removed vertexlimit ### ################################################################# import Blender import sys import string import struct import os #,glob from os import path,name,sep from math import * from Blender import NMesh from Blender.BGL import * from Blender.Draw import * from Blender import Scene from Blender import Lamp from Blender import Mesh flagstatusbar=0 valbar=0 maxbar=1 drawcount=1 version=Blender.Get('version') filename=Blender.Get('filename') lpath,lname=os.path.split(filename) inf = lpath+r"\ter2blend.txt" try: inffile=open(inf, 'r') except IOError: terfile = Create("") waterlevel = Create(-10.00) sunheading = Create(-60.00) sunalt = Create(25.00) camerax = Create(128.00) cameray = Create(0.00) cameraz = Create(0.00) head = Create(0.00) pitch = Create(0.00) bank = Create(0.00) zoom = Create(1.44) else: line = inffile.readline() L1 = string.split(line,',') terfile=Create(L1[0]) waterlevel=Create(string.atof(L1[1])) sunheading=Create(string.atof(L1[2])) sunalt=Create(string.atof(L1[3])) camerax=Create(string.atof(L1[4])) cameray=Create(string.atof(L1[5])) cameraz=Create(string.atof(L1[6])) head=Create(string.atof(L1[7])) pitch=Create(string.atof(L1[8])) bank=Create(string.atof(L1[9])) zoom=Create(string.atof(L1[10])) inffile.close() ################################################################# def gui(): global terfile global waterlevel global sunheading, sunalt global camerax, cameray, cameraz global head, pitch, bank global flagstatusbar,valbar,maxbar global zoom glClearColor(0.5,0.5,0.5,0.0) glClear(GL_COLOR_BUFFER_BIT) glColor3f(0.0,0.0,0.0) glRectf(0,0,480,480) glColor3f(1.0,1.0,1.0) glRectf(2,2,478,478) glColor3f(0.0,0.0,0.0) glRectf(0,288,480,410) glColor3f(1.0,1.0,1.0) glRectf(2,290,478,408) glColor3f(0.0,0.0,0.0) glRasterPos2i(10,460) Text(" TER2BLEND v5.0.1") glRasterPos2i(10,440) Text("Guy Van Rentergem 2003(c) and David Mark Johnson 2008(c)") glRasterPos2i(10,420) Text(" TERRAIN, import a *.ter file") glColor3f(1.0,0.0,0.0) glRasterPos2i(10,385) Text("USE ONLY TERRAGEN TERRAIN UNITS") glColor3f(0.0,0.0,0.0) #glRasterPos2i(10,360) #Text(" The camera lens must have a value of 22.70") glRasterPos2i(10,330) Text(" After importing the terrain, select it and") glRasterPos2i(10,310) Text(' assign it the "shadow only" material ') glRasterPos2i(10,223) Text("Camera position") glRasterPos2i(10,180) Text("Camera orientation") glRasterPos2i(10,267) Text("Open ter-file, full path name required") if flagstatusbar==1: glColor3f(0.0,0.0,0.0) glRectf(20,310,265,380) glColor3f(0.8,0.8,0.8) glRectf(22,312,263,378) glColor3f(0.0,0.0,0.0) glRasterPos2i(115,350) Text("Working...") glColor3f(0.0,0.0,0.0) glRectf(28,321,257,340) glColor3f(1.0,1.0,0.0) glRectf(30,323,255,338) glColor3f(0.0,0.0,1.0) glRectf(30,323,30+(225.0/maxbar)*valbar,338) terfile = String("Open: ",13,10,244,260,18,terfile.val,100) camerax = Number("X: ",7,10,200,86,18,camerax.val,0.00,258.00) cameray = Number("Y: ",8,97,200,86,18,cameray.val,0.00,258.00) cameraz = Number("Z: ",9,184,200,86,18,cameraz.val,-1000.00,1000.00) head = Number("head: ",10,10,156,86,18,head.val,-360.00,360.00) pitch = Number("pitch: ",11,97,156,86,18,pitch.val,-180.00,180.00) bank = Number("bank: ",12,184,156,86,18,bank.val,-180.00,180.00) waterlevel = Slider("water level: ",4,10,124,260,18,waterlevel.val,-258.0,258.0) sunheading = Slider("sun heading: ",5,10,97,260,18,sunheading.val,-180.0,180.0) sunalt = Slider("sun altitude: ",6,10,70,260,18,sunalt.val,-90.0,90.0) zoom = Slider("Camera Zoom: ",7,10,43,260,18,zoom.val,0.25,256.0) Button("Exit",1,190,10,80,19) Button("Draw",2,10,10,80,19) ################################################################# def event(evt,val): if (evt == ESCKEY and not val): Exit() ################################################################# def bevent(evt): global drawcount,version if (evt == 1): Exit() elif (evt == 2): if version < 200: draw(terfile.val,waterlevel.val,sunheading.val,sunalt.val,camerax.val,cameray.val, cameraz.val,head.val,pitch.val,bank.val) else: if drawcount==1: draw(terfile.val,waterlevel.val,sunheading.val,sunalt.val,camerax.val,cameray.val, cameraz.val,head.val,pitch.val,bank.val) else: drawcount=1 ################################################################# def draw(terfile,waterlevel,sunheading,sunalt,camerax,cameray,cameraz,head,pitch,bank): global drawcount global flagstatusbar drawcount=drawcount+1 flagstatusbar=1 gui() Register(gui,event,bevent) Blender.Redraw() createfile() createnewscene() camera(camerax,cameray,cameraz,head,pitch,bank) terrain(terfile) sun(sunheading,sunalt) water(waterlevel) flagstatusbar=0 Blender.Redraw() ################################################################# def createnewscene(): try: scn = Scene.Get('TerragenScene') except: scn = Scene.New('TerragenScene') else: i = 1 # scn = Scene.New('TerragenScene') scn.makeCurrent() ################################################################# def createfile(): global terfile, inf global waterlevel global sunheading, sunalt global camerax, cameray, cameraz global head, pitch, bank inffile=open(inf, 'w') dummy=terfile.val+','+`waterlevel.val`+','+`sunheading.val`+','+`sunalt.val`+','+`camerax.val`+','+`cameray.val`+','+`cameraz.val`+','+`head.val`+','+`pitch.val`+','+`bank.val`+','+`zoom.val` inffile.write(dummy) inffile.close() ################################################################# def water(waterlevel): sce = Scene.GetCurrent() try: water = Blender.Mesh.Get("TerragenWater") except: water = Mesh.New('TerragenWater') coords = [ [0,0,waterlevel], [size,0,waterlevel], [size,size,waterlevel], [0,size,waterlevel] ] faces = [ 0,1,2,3 ] water.verts.extend(coords) water.faces.extend(faces) scn = Scene.GetCurrent() ob = scn.objects.new(water,'TerragenWater') else: print "Found water" #water = Mesh.New('TerrgenWater') ################################################################# # We need to create the lamps that match the SUN from Terragen. # so that they can actually be used, So these Lamps are now # linked in to the Terragen Scene for use in the blender application # method as recommend in the documents for Blender 2.44 def sun(sunheading,sunalt): sce = Scene.GetCurrent() try: lamp = Blender.Object.Get("TerragenHemi") except: lamp = Lamp.New("Sun") sce.objects.new(lamp,"TerragenHemi") else: print "Found hemi!" try: lp = Blender.Object.Get("TerragenShadow") except: lp = Lamp.New("Spot") sce.objects.new(lp,"TerragenShadow") else: print "Found shadow!" sunheading = sunheading*pi/180 sunalt=sunalt*pi/180 SunLocX = size/2+750*sin(sunheading) SunLocY = size/2+750*cos(sunheading) SunLocZ = 750*sin(sunalt)/cos(sunalt) SunRotX = pi/2-sunalt SunRotY = 0 SunRotZ = pi-sunheading shadow = Blender.Object.Get("TerragenShadow") shadow.LocX = SunLocX shadow.LocY = SunLocY shadow.LocZ = SunLocZ shadow.RotX = SunRotX shadow.RotY = SunRotY shadow.RotZ = SunRotZ shadowdata = shadow.getData() shadowdata.setDist(1.1*sqrt((SunLocZ)**2+562500)) shadowdata.setSpotSize( 2*atan(200/shadowdata.getDist())*180/pi) #save angle hemi = Blender.Object.Get("TerragenHemi") hemi.LocX = SunLocX hemi.LocY = SunLocY hemi.LocZ = SunLocZ hemi.RotX = SunRotX hemi.RotY = SunRotY hemi.RotZ = SunRotZ ################################################################# def camera(camerax,cameray,cameraz,head,pitch,bank): sce = Blender.Scene.Get('TerragenScene') try: camera = Blender.Object.Get('TerragenCamera') except: camera = Blender.Camera.New('persp') camera.name = ("TerragenCamera") camera.setLens(22.70) sce.objects.new(camera,'TerragenCamera') else: print "Found camera!" # Field of view needs to be in degrees so calculate that from the Zoom value we have camera = Blender.Camera.Get('TerragenCamera') print zoom invzoom = 1/(zoom.val) FOV = 2*atan(invzoom) lens = 16/tan(FOV/2) camera.setLens(lens) print lens camera = Blender.Object.Get('TerragenCamera') camera.LocX = camerax camera.LocY = cameray camera.LocZ = cameraz # pitch # ----- # (Blender) 0 is down, 90 is horizontal (rad) # (Terragen) -90 is down, 0 is horizontal (deg) camerax = pi/2 + pi/180*pitch # bank # ---- # (Blender) / 45 (rad) # (Terragen) / 45 (deg) cameray = bank*pi/180 # head # ---- # (Blender) 0 is top, 90 is left, -90 is right # (Terragen) 0 is top, 90 is right, -90 is left cameraz = -head*pi/180 camera.setEuler(camerax,cameray,cameraz) Blender.Redraw() ################################################################# def terrain(terfile): global size,maxbar,valbar sce = Scene.GetCurrent() try: terrianName = Blender.Obeject.Get('TerragenMesh') except: size=0 try: ter=open(terfile, 'rb') except IOError: if terfile=="": print "Terragen ter file does not exist!" Exit() else: print terfile + " does not exist!" Exit() else: if ter.read(8) == 'TERRAGEN': if ter.read(8) == 'TERRAIN ': print 'Terragen terrain file: continue' 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 if xpts==0:xpts=size+1 if ypts==0:ypts=size+1 ### MOD by pat ### commented 2 lines #if (xpts>257 or ypts>257): # print str(xpts)+" X "+str(ypts)+" to big! Terragen terrain must be 257 X 257." maxbar=xpts*ypts*2 stepbar=maxbar/25 valbar=0 terrainName = Mesh.New() vertexlist = [] # Create vertices # --------------- # read them all... for y in xrange(ypts): for x in xrange(xpts): (h,) = struct.unpack('h',ter.read(2)) ### MOD by pat: 1 line commented ### 10 lines unindeted #if x < 254 and y < 254: z = baseheight + h*heightscale/65536.0 vertexlist.append((x,y,z)) valbar=valbar+1 if valbar%stepbar==0: gui() Register(gui,event,bevent) Blender.Redraw() xmax=x+1 ymax=y+1 ### end of unindented block ter.close() terrainName.verts.extend(vertexlist) # Create faces # ------------ print "Building Faces" faces = [] for y in range(0,ymax-1): for x in range (0,xmax-1): a=x+y*(ymax) faces.append(( terrainName.verts[a], terrainName.verts[a+ymax], terrainName.verts[a+ymax+1], terrainName.verts[a+1] )) valbar=valbar+1 if valbar%stepbar==0: gui() Register(gui,event,bevent) Blender.Redraw() terrainName.faces.extend(faces) scn = Scene.GetCurrent() ob = scn.objects.new(terrainName,"TerragenMesh") valbar=0 maxbar=1 drawcount=1 else: print "Found Terrain" Register(gui,event,bevent) #########################END OF CODE##############################