#!BPY

"""
Name: 'Terragen (.ter)'
Blender: 248
Group: 'Wizards'
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 <http://www.gnu.org/licenses/>.
#

# 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##############################
