rem Most of the code is converted from an old C project.

import "OpenGL.lib"
import "gl_h.lib"

constant:
	rem shaders.
	TD_NONE 0
	TD_FLAT 1
	TD_SMOOTH 2
	MAX_OBJECTS 100

	rem Wallmap.

	rem Diagonal split types.
	TD_SPLIT_0Z_X0   0
	TD_SPLIT_00_XZ   1
	TD_SPLIT_0Z_X0_L 2
	TD_SPLIT_0Z_X0_R 3
	TD_SPLIT_00_XZ_L 4
	TD_SPLIT_00_XZ_R 5

	rem Texture modes.
	TD_MAP 0
	TD_STRETCH 1

	rem Wall, floor and ceil fields.
	TD_Y0 0
	TD_Y1 1
	TD_y2 2
	TD_y3 3
	TD_N0X 4
	TD_N0Y 5
	TD_N0Z 6
	TD_N1X 7
	TD_N1Y 8
	TD_N1Z 9

	UNIT_2X1# 0.70710678	

visible:
	vMeshName
	vObjects?[MAX_OBJECTS]
	
	rem camera.
	vCamX#
	vCamY#
	vCamZ#
	vCamYaw#
	vCamPitch#
	vCamRoll#

	rem light.
	vLightDX#
	vLightDY#
	vLightDZ#

	rem heightmap.
	hm_Map?[][]
	hm_SizeX
	hm_SizeZ
	td_Heightmap
	td_HeightmapTexture

	rem background.
	td_BGSphere
	td_BGTexture
	td_BG = false

	rem Every tile consists of four orthogonal walls, one diagonal,
	rem one floor and one ceiling. Each entity is defined by four
	rem y values.
	td_Wallmap
	td_wmw
	td_wmh
	td_floor#[][][]
	td_ceil#[][][]
	td_wall#[][][][]
	td_tile?[][]
	td_color#[10][3]
	td_WMTextures[]
	td_WMNumTextures

hidden:

rem if not TD_Init("ThreeDee Test", 32, 32, 800, 600, 60.0, 0.1, 16.0, false) then end
rem if not TD_LoadWallmap("assets/wallmap.txt") then end

rem Init map.
procedure TD_InitWallmap(w, h)
	td_wmw = w
	td_wmh = h
	td_floor#[td_wmw][td_wmh][10]
	td_ceil#[td_wmw][td_wmh][10]
	td_wall#[td_wmw][td_wmh][5][4]
	td_tile?[td_wmw][td_wmh]
	for z = 0 to td_wmh - 1
		for x = 0 to td_wmw - 1
			rem Textures.
			td_tile[x][z].f = -1
			td_tile[x][z].c = -1
			td_tile[x][z].ft = -1
			td_tile[x][z].ct = -1
			td_tile[x][z].w0 = -1
			td_tile[x][z].w1 = -1
			td_tile[x][z].w2 = -1
			td_tile[x][z].w3 = -1
			td_tile[x][z].w4 = -1
			td_tile[x][z].wm0 = TD_MAP
			td_tile[x][z].wm1 = TD_MAP
			td_tile[x][z].wm2 = TD_MAP
			td_tile[x][z].wm3 = TD_MAP
			td_tile[x][z].wm4 = TD_MAP
			rem Diagonal info.
			td_tile[x][z].d = 0
			rem Blocked?..
			td_tile[x][z].b = 0
			rem Coordinates.
			for i = 0 to 3; td_floor[x][z][i] = 0.0; td_ceil[x][z][i] = 1.0; next
			td_floor[x][z][4] = 0.0; td_floor[x][z][5] = 1.0; td_floor[x][z][6] = 0.0
			td_floor[x][z][7] = 0.0; td_floor[x][z][8] = 1.0; td_floor[x][z][9] = 0.0
			td_ceil[x][z][4] = 0.0; td_ceil[x][z][5] = -1.0; td_ceil[x][z][6] = 0.0
			td_ceil[x][z][7] = 0.0; td_ceil[x][z][8] = -1.0; td_ceil[x][z][9] = 0.0
		next
	next
	rem Colors.
	for i = 0 to 9; for j = 0 to 2; td_color#[i][j] = 1.0; next; next
endproc

rem Render wall map while editing or building.
procedure TD_RenderWallmap()
	rem Should do one cycle per texture instead to avoid binding.
	for i = 0 to td_WMNumTextures - 1
	_glBindTexture GL_TEXTURE_2D, td_WMTextures[i]
	_glBegin GL_QUADS
	for z = 0 to td_wmh - 1
		for x = 0 to td_wmw - 1
			rem Orthogonal walls.
			if td_tile[x][z].w0 = i
				_glColor3f td_color[0][0], td_color[0][1], td_color[0][2]
				if td_tile[x][z].wm0 = TD_MAP
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][0][0]; _glVertex3f float(x), td_wall[x][z][0][0], float(z)
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][0][1]; _glVertex3f float(x), td_wall[x][z][0][1], float(z) + 1.0
      		_glTexCoord2f 1.0, 1.0 - td_wall[x][z][0][2]; _glVertex3f float(x), td_wall[x][z][0][2], float(z) + 1.0
      		_glTexCoord2f 0.0, 1.0 - td_wall[x][z][0][3]; _glVertex3f float(x), td_wall[x][z][0][3], float(z)
				else
					_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_wall[x][z][0][0], float(z)
					_glTexCoord2f 1.0, 1.0; _glVertex3f float(x), td_wall[x][z][0][1], float(z) + 1.0
      		_glTexCoord2f 1.0, 0.0; _glVertex3f float(x), td_wall[x][z][0][2], float(z) + 1.0
      		_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_wall[x][z][0][3], float(z)
				endif
			endif
			if td_tile[x][z].w1 = i
				_glColor3f td_color[1][0], td_color[1][1], td_color[1][2]
				if td_tile[x][z].wm1 = TD_MAP
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][1][0]; _glVertex3f float(x) + 1.0, td_wall[x][z][1][0], float(z)
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][1][1]; _glVertex3f float(x), td_wall[x][z][1][1], float(z)
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][1][2]; _glVertex3f float(x), td_wall[x][z][1][2], float(z)
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][1][3]; _glVertex3f float(x) + 1.0, td_wall[x][z][1][3], float(z)
				else
					_glTexCoord2f 0.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][1][0], float(z)
					_glTexCoord2f 1.0, 1.0; _glVertex3f float(x), td_wall[x][z][1][1], float(z)
					_glTexCoord2f 1.0, 0.0; _glVertex3f float(x), td_wall[x][z][1][2], float(z)
					_glTexCoord2f 0.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][1][3], float(z)
				endif
			endif
			if td_tile[x][z].w2 = i
				_glColor3f td_color[2][0], td_color[2][1], td_color[2][2]
				if td_tile[x][z].wm2 = TD_MAP
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][2][0]; _glVertex3f float(x) + 1.0, td_wall[x][z][2][0], float(z) + 1.0
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][2][1]; _glVertex3f float(x) + 1.0, td_wall[x][z][2][1], float(z)
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][2][2]; _glVertex3f float(x) + 1.0, td_wall[x][z][2][2], float(z)
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][2][3]; _glVertex3f float(x) + 1.0, td_wall[x][z][2][3], float(z) + 1.0
				else
					_glTexCoord2f 0.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][2][0], float(z) + 1.0
					_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][2][1], float(z)
					_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][2][2], float(z)
					_glTexCoord2f 0.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][2][3], float(z) + 1.0
				endif
			endif
			if td_tile[x][z].w3 = i
				_glColor3f td_color[3][0], td_color[3][1], td_color[3][2]
				if td_tile[x][z].wm3 = TD_MAP
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][3][0]; _glVertex3f float(x), td_wall[x][z][3][0], float(z) + 1.0
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][3][1]; _glVertex3f float(x) + 1.0, td_wall[x][z][3][1], float(z) + 1.0
					_glTexCoord2f 1.0, 1.0 - td_wall[x][z][3][2]; _glVertex3f float(x) + 1.0, td_wall[x][z][3][2], float(z) + 1.0
					_glTexCoord2f 0.0, 1.0 - td_wall[x][z][3][3]; _glVertex3f float(x), td_wall[x][z][3][3], float(z) + 1.0
				else
					_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_wall[x][z][3][0], float(z) + 1.0
					_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][3][1], float(z) + 1.0
					_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][3][2], float(z) + 1.0
					_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_wall[x][z][3][3], float(z) + 1.0
				endif
			endif
			rem Diagonals walls.
			if td_tile[x][z].w4 = i
				if td_tile[x][z].d = 1
					_glColor3f td_color[4][0], td_color[4][1], td_color[4][2]
					if td_tile[x][z].wm4 = TD_MAP
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][0]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][0], float(z)
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][1]; _glVertex3f float(x), td_wall[x][z][4][1], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][2]; _glVertex3f float(x), td_wall[x][z][4][2], float(z) + 1.0
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][3]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][3], float(z)
					else
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][0], float(z)
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x), td_wall[x][z][4][1], float(z) + 1.0
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x), td_wall[x][z][4][2], float(z) + 1.0
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][3], float(z)
					endif
				elseif td_tile[x][z].d = 2
					_glColor3f td_color[5][0], td_color[5][1], td_color[5][2]
					if td_tile[x][z].wm4 = TD_MAP
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][0]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][0], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][1]; _glVertex3f float(x), td_wall[x][z][4][1], float(z)
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][2]; _glVertex3f float(x), td_wall[x][z][4][2], float(z)
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][3]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][3], float(z) + 1.0
					else
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][0], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x), td_wall[x][z][4][1], float(z)
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x), td_wall[x][z][4][2], float(z)
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][3], float(z) + 1.0
					endif
				elseif td_tile[x][z].d = 3
					_glColor3f td_color[6][0], td_color[6][1], td_color[6][2];
					if td_tile[x][z].wm4 = TD_MAP
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][0]; _glVertex3f float(x), td_wall[x][z][4][0], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][1]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][1], float(z)
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][2]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][2], float(z)
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][3]; _glVertex3f float(x), td_wall[x][z][4][3], float(z) + 1.0
					else
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_wall[x][z][4][0], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][1], float(z)
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][2], float(z)
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_wall[x][z][4][3], float(z) + 1.0
					endif
				else
					_glColor3f td_color[7][0], td_color[7][1], td_color[7][2]
					if td_tile[x][z].wm4 = TD_MAP
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][0]; _glVertex3f float(x), td_wall[x][z][4][0], float(z)
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][1]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][1], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0 - td_wall[x][z][4][2]; _glVertex3f float(x) + 1.0, td_wall[x][z][4][2], float(z) + 1.0
						_glTexCoord2f 0.0, 1.0 - td_wall[x][z][4][3]; _glVertex3f float(x), td_wall[x][z][4][3], float(z)
					else
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_wall[x][z][4][0], float(z)
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][1], float(z) + 1.0
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_wall[x][z][4][2], float(z) + 1.0
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_wall[x][z][4][3], float(z)
					endif
				endif
			endif
		next
	next
	_glEnd

	_glBegin GL_TRIANGLES
	for z = 0 to td_wmh - 1
		for x = 0 to td_wmw - 1
			rem Floor.
			if td_tile[x][z].f = i
				_glColor3f td_color[8][0], td_color[8][1], td_color[8][2]
				if td_tile[x][z].ft = 0 or td_tile[x][z].ft = 2 or td_tile[x][z].ft = 3
					if td_tile[x][z].ft <> 3
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_floor[x][z][0], float(z)
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_floor[x][z][3], float(z) + 1.0
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_floor[x][z][1], float(z)
					endif
					if td_tile[x][z].ft <> 2
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_floor[x][z][1], float(z)
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_floor[x][z][3], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_floor[x][z][2], float(z) + 1.0
					endif
				else
					if td_tile[x][z].ft <> 5
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_floor[x][z][0], float(z)
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_floor[x][z][3], float(z) + 1.0
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_floor[x][z][2], float(z) + 1.0
					endif
					if td_tile[x][z].ft <> 4
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_floor[x][z][0], float(z)
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_floor[x][z][2], float(z) + 1.0
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_floor[x][z][1], float(z)
					endif
				endif
			endif
			rem Ceiling.
			if td_tile[x][z].c = i
				_glColor3f td_color[9][0], td_color[9][1], td_color[9][2]
  	    if td_tile[x][z].ct = 0 or td_tile[x][z].ct = 2 or td_tile[x][z].ct = 3
					if td_tile[x][z].ct <> 3
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_ceil[x][z][1], float(z)
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_ceil[x][z][3], float(z) + 1.0
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_ceil[x][z][0], float(z)
					endif
					if td_tile[x][z].ct <> 2
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_ceil[x][z][2], float(z) + 1.0
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_ceil[x][z][3], float(z) + 1.0
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_ceil[x][z][1], float(z)
					endif
				else
					if td_tile[x][z].ct <> 5
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_ceil[x][z][2], float(z) + 1.0
						_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), td_ceil[x][z][3], float(z) + 1.0
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_ceil[x][z][0], float(z)
					endif
					if td_tile[x][z].ct <> 4
						_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, td_ceil[x][z][1], float(z)
						_glTexCoord2f 1.0, 1.0; _glVertex3f float(x) + 1.0, td_ceil[x][z][2], float(z) + 1.0
						_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), td_ceil[x][z][0], float(z)
					endif
				endif
			endif
		next
	next
	_glEnd
	next
endproc

rem Build map, must be called before rendering.
procedure TD_BuildMap()
	for z = 0 to td_wmh - 1
		for x = 0 to td_wmw - 1
			_TD_CalcWallmapNormals x, z
		next
	next 

	td_Wallmap = glGenLists(1)
	_glNewList td_Wallmap, GL_COMPILE
	_glEnable GL_TEXTURE_2D
	_TD_RenderWallmap
	_glEndList
endproc

function get_path$(filename$)
	i = instr(filename, "/")
	if i >= 0
		path$ = left(filename, i + 1)
	else
		path$ = ""
	endif
	return path
endfunc

rem load map from rc editor.
function TD_LoadRCMap(filename$, &plyx#, &plyz#, &plya#, build)
	open file 0, filename$
	if not file(0) then return false
	path$ = get_path(filename)
	td_WMNumTextures = read(0)
	td_WMTextures[td_WMNumTextures]
	for i = 0 to td_WMNumTextures - 1
		rem ... index?
		tmp = read(0); fn$ = path + read$(0)
		td_WMTextures[i] = TD_LoadTexture(fn)
		ck = read(0); r = read(0); g = read(0); b = read(0)
	next
	rem objects?..
	count = read(0)
	for i = 0 to count - 1; for j = 0 to 4; tmp = read(0); next; next
	rem fog.
	fogActive = read(0)
	fogR = read(0); fogG = read(0); fogB = read(0)
	fogZMin# = read#(0); fogZMax# = read#(0)
	if fogActive; _TD_SetFog fogR, fogG, fogB, fogZMin, fogZMax
	else; _TD_DisableFog; endif
	rem map.
	w = read(0); h = read(0)
	_TD_InitWallmap w, h
	rem walls.
	for z = 0 to h - 1; for x = 0 to w - 1
		wall = read(0)
		if wall > 0
			SUB wall 1
			td_tile[x][z].w0 = wall; td_tile[x][z].w1 = wall; td_tile[x][z].w2 = wall; td_tile[x][z].w3 = wall
			for i = 0 to 3
				td_wall[x][z][i][0] = 0.0
				td_wall[x][z][i][1] = 0.0
				td_wall[x][z][i][2] = 1.0
				td_wall[x][z][i][3] = 1.0
			next
			td_tile[x][z].wm0 = TD_STRETCH
			td_tile[x][z].wm1 = TD_STRETCH
			td_tile[x][z].wm2 = TD_STRETCH
			td_tile[x][z].wm3 = TD_STRETCH
			td_tile[x][z].wm4 = TD_STRETCH
		endif
	next; next
	rem floor.
	for z = 0 to h - 1; for x = 0 to w - 1
		floor = read(0)
		if floor > 0; td_tile[x][z].f = floor - 1; td_tile[x][z].ft = 0; endif
	next; next
	rem ceiling.
	for z = 0 to h - 1; for x = 0 to w - 1
		ceil = read(0)
		if ceil > 0; td_tile[x][z].c = ceil - 1; td_tile[x][z].ct = 0; endif
	next; next
	rem unused here.
	for i = 0 to 2; for z = 0 to h - 1; for x = 0 to w - 1; tmp = read(0); next; next; next
	rem player.
	plyx = float(read(0)) + 0.5; plyz = float(read(0)) + 0.5; plya = float(read(0))
	free file 0
	if build then _TD_BuildMap
	return true
endfunc

function TD_LoadWallmap(filename$)
	open file 0, filename
	if not file(0) then return false
	path$ = get_path(filename)
	w = read(0); h = read(0)
	_TD_InitWallmap w, h
	td_WMNumTextures = read(0)
	td_WMTextures[td_WMNumTextures]
	for i = 0 to td_WMNumTextures - 1
		tmp = read(0)
		fn$ = path + read$(0)
		td_WMTextures[i] = TD_LoadTexture(fn)
	next
	for i = 0 to 9
		td_color[i][0] = read#(0)
		td_color[i][1] = read#(0)
		td_color[i][2] = read#(0)
rem		wln "COL", i, " = ", td_color[i][0], " ", td_color[i][1], " ", td_color[i][2]
	next
	tex[5]
	for z = 0 to td_wmh - 1
		for x = 0 to td_wmw - 1
			tmp = read(0)
			if tmp = 1
				td_tile[x][z].b = read(0)
				td_tile[x][z].d = read(0)
				for i = 0 to 4
					tex[i] = read(0)
					td_wall[x][z][i][0] = read#(0)
					td_wall[x][z][i][1] = read#(0)
					td_wall[x][z][i][2] = read#(0)
					td_wall[x][z][i][3] = read#(0)
				next
				
				td_tile[x][z].w0 = tex[0]
				td_tile[x][z].w1 = tex[1]
				td_tile[x][z].w2 = tex[2]
				td_tile[x][z].w3 = tex[3]
				td_tile[x][z].w4 = tex[4]

				td_tile[x][z].ft = read(0)
				td_tile[x][z].f  = read(0)
				td_floor[x][z][0] = read#(0)
				td_floor[x][z][1] = read#(0)
				td_floor[x][z][2] = read#(0)
				td_floor[x][z][3] = read#(0)

				td_tile[x][z].ct = read(0)
				td_tile[x][z].c = read(0) 
				td_ceil[x][z][0] = read#(0)
				td_ceil[x][z][1] = read#(0)
				td_ceil[x][z][2] = read#(0)
				td_ceil[x][z][3] = read#(0)
				
			endif
		next
	next
	free file 0
	proc TD_BuildMap
	return true
endfunc

procedure TD_CalcWallmapNormals(x, z)
	a#[3]; b#[3]
	if td_tile[x][z].ft = 0 or td_tile[x][z].ft = 2 or td_tile[x][z].ft = 3
		a[0] = 0.0; a[1] = td_floor[x][z][3] - td_floor[x][z][0]; a[2] = 1.0
		b[0] = 1.0; b[1] = td_floor[x][z][1] - td_floor[x][z][0]; b[2] = 0.0
		n#[] = crossvp(a, b); _normv n
		td_floor[x][z][4] = n[0]; td_floor[x][z][5] = n[1]; td_floor[x][z][6] = n[2]

		a[0] = 0.0; a[1] = td_floor[x][z][1] - td_floor[x][z][2]; a[2] = -1.0
		b[0] = -1.0; b[1] = td_floor[x][z][3] - td_floor[x][z][2]; b[2] = 0.0
		n#[] = crossvp(a, b); _normv n
		td_floor[x][z][7] = n[0]; td_floor[x][z][8] = n[1]; td_floor[x][z][9] = n[2]
	else
		a[0] = -1.0; a[1] = td_floor[x][z][0] - td_floor[x][z][1]; a[2] = 0.0
		b[0] = 0.0; b[1] = td_floor[x][z][2] - td_floor[x][z][1]; b[2] = 1.0
		n#[] = crossvp(a, b); _normv n
		td_floor[x][z][4] = n[0]; td_floor[x][z][5] = n[1]; td_floor[x][z][6] = n[2]

		a[0] = 1.0; a[1] = td_floor[x][z][2] - td_floor[x][z][3]; a[2] = 0.0
		b[0] = 0.0; b[1] = td_floor[x][z][0] - td_floor[x][z][3]; b[2] = -1.0
		n#[] = crossvp(a, b); _normv n
		td_floor[x][z][7] = n[0]; td_floor[x][z][8] = n[1]; td_floor[x][z][9] = n[2]
	endif
	if td_tile[x][z].ct = 0 or td_tile[x][z].ct = 2 or td_tile[x][z].ct = 3
		a[0] = 0.0; a[1] = td_ceil[x][z][3] - td_ceil[x][z][0]; a[2] = 1.0
		b[0] = 1.0; b[1] = td_ceil[x][z][1] - td_ceil[x][z][0]; b[2] = 0.0
		n#[] = crossvp(a, b); _normv n
		td_ceil[x][z][4] = n[0]; td_ceil[x][z][5] = n[1]; td_ceil[x][z][6] = n[2]

		a[0] = 0.0; a[1] = td_ceil[x][z][1] - td_ceil[x][z][2]; a[2] = -1.0
		b[0] = -1.0; b[1] = td_ceil[x][z][3] - td_ceil[x][z][2]; b[2] = 0.0
		n#[] = crossvp(a, b); _normv n
		td_ceil[x][z][7] = n[0]; td_ceil[x][z][8] = n[1]; td_ceil[x][z][9] = n[2]
	else
		a[0] = -1.0; a[1] = td_ceil[x][z][0] - td_ceil[x][z][1]; a[2] = 0.0
		b[0] = 0.0; b[1] = td_ceil[x][z][2] - td_ceil[x][z][1]; b[2] = 1.0
		n#[] = crossvp(a, b); _normv n
		td_ceil[x][z][4] = n[0]; td_ceil[x][z][5] = n[1]; td_ceil[x][z][6] = n[2]

		a[0] = 1.0; a[1] = td_ceil[x][z][2] - td_ceil[x][z][3]; a[2] = 0.0
		b[0] = 0.0; b[1] = td_ceil[x][z][0] - td_ceil[x][z][3]; b[2] = -1.0
		n#[] = crossvp(a, b); _normv n
		td_ceil[x][z][7] = n[0]; td_ceil[x][z][8] = n[1]; td_ceil[x][z][9] = n[2] 
	endif

endproc

	rem pos#[] = TD_TryMove(vPlayerX, vPlayerY, vPlayerZ, plyDX, plyDY, plyDZ, 0.3, 1.0, 0.2)

procedure TD_Move(&xp#, &yp#, &zp#, dx#, dy#, dz#, distance#, hgt#, ySkip#)
	xNew#; zNew#; yhm#; yFloor#; yCeiling#; ymin#
	floor; ceiling
	norm#[3]; n#[3]
	dir#[3]
	k#
	ix; iz;

  ceilingHit = 0
  floorHit = 0
  wallHit = 0

	if td_Heightmap
		if TD_GetHeightmapHeight(yhm, xp, zp)			
		endif
	endif
	ymin = yhm

	floor = wmFloorHeight(yFloor, xp, zp)
	ceiling = wmCeilingHeight(yCeiling, xp, zp)
	floorHit = -1;
	if floor
		if (yFloor >= yhm or td_Heightmap = 0) and not (ceiling and yCeiling <= yFloor and yp < yCeiling)
			ymin = yFloor
			floorHit = -2;
    endif
	endif
	yp = yp + dy

	if yp < ymin
		yp = ymin
		if ymin = yhm
			floorHit = 1
			_hmNormal norm, xp, zp
			dir[0] = dx
			dir[1] = 0.0
			dir[2] = dz
			k = dotvp(dir, norm)
			n[0] = norm[0]; n[1] = norm[1]; n[2] = norm[2]
			_mulv n, k
			norm[0] = dir[0]; norm[1] = dir[1]; norm[2] = dir[2]
			_subv norm, n
			if norm[1] > 0.0
				dx = norm[0]
				dz = norm[2]
      endif
		else
			floorHit = 2
		endif
  endif

	if ceiling and yp+hgt > yCeiling and yp < yFloor
		yp = yCeiling-hgt
		ceilingHit = 1
  endif

	wallHit = wmTryMove(xNew, zNew, xp, yp, zp, dx, dz, distance, hgt, ySkip)
	xp = xNew
	zp = zNew
endproc

function wmTryMove(&xAp#, &zAp#, xPos#, yPos#, zPos#, dx#, dz#, distance#, hgt#, ySkip#)
	ix; iz
	distInv# = 1.0 - distance
	xNew#; zNew#
	legalXP; legalXN; legalZP; legalZN
	ret

  rem Om inga frndringar sker ...
  xAp = xPos + dx
  zAp = zPos + dz
  
	if dx = 0.0 and dz = 0.0 then return 0
  
	ix = int(xPos); iz = int(zPos)
  xPos = xPos - float(ix); zPos = zPos - float(iz)
  
  xNew = xPos + dx; zNew = zPos + dz
  
	if ix < td_wmw - 1 then legalXP = 1
	if ix > 0 then legalXN = 1
	if iz < td_wmh - 1 then legalZP = 1
  if iz > 0 then legalZN = 1

  rem hrn
  rem Upperright
	if not (inway(ix + 1, iz, 0, xPos, yPos, zPos, hgt, ySkip) or inway(ix, iz + 1, 1, xPos, yPos, zPos, hgt, ySkip))
		if inway(ix+1, iz+1, 0, xPos, yPos, zPos, hgt, ySkip) or inway(ix+1, iz+1, 1, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix][iz+1].d = 1 and inway(ix, iz+1, 4, xPos, yPos, zPos, hgt, ySkip)) or (td_tile[ix+1][iz].d = 1 and inway(ix+1, iz, 4, xPos, yPos, zPos, hgt, ySkip))
			if dummy(1.0, distInv, -UNIT_2X1, UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
				ret = 5
				xAp = xAp + float(ix)
				zAp = zAp + float(iz)
			endif
		endif
  endif

  rem upperleft
  if not (inway(ix-1, iz, 2, xPos, yPos, zPos, hgt, ySkip) or inway(ix, iz+1, 1, xPos, yPos, zPos, hgt, ySkip))
		if (inway(ix-1, iz+1, 2, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix][iz+1].d = 2 and inway(ix, iz+1, 4, xPos, yPos, zPos, hgt, ySkip))) or (inway(ix-1, iz+1, 1, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix-1][iz].d = 2 and inway(ix-1, iz, 4, xPos, yPos, zPos, hgt, ySkip)))
			if dummy(distance, 1.0, -UNIT_2X1, -UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
				ret = 6
    		xAp = xAp + float(ix)
    		zAp = zAp + float(iz)
			endif
		endif
	endif
  
	rem bottomleft 
  if not (inway(ix-1, iz, 2, xPos, yPos, zPos, hgt, ySkip) or inway(ix, iz-1, 3, xPos, yPos, zPos, hgt, ySkip))
  	if (inway(ix-1, iz-1, 2, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix][iz-1].d = 3 and inway(ix, iz-1, 4, xPos, yPos, zPos, hgt, ySkip))) or (inway(ix-1, iz-1, 3, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix-1][iz].d = 3 and inway(ix-1, iz, 4, xPos, yPos, zPos, hgt, ySkip)))
			if dummy(0.0, distance, UNIT_2X1, -UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
				ret = 7
				xAp = xAp + float(ix)
				zAp = zAp + float(iz)
    	endif
		endif
  endif
  
  rem bottomright
  if not (inway(ix+1, iz, 0, xPos, yPos, zPos, hgt, ySkip) or inway(ix, iz-1, 3, xPos, yPos, zPos, hgt, ySkip))
  	if (inway(ix+1, iz-1, 0, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix][iz-1].d = 4 and inway(ix, iz-1, 4, xPos, yPos, zPos, hgt, ySkip))) or (inway(ix+1, iz-1, 3, xPos, yPos, zPos, hgt, ySkip) or (td_tile[ix+1][iz].d = 4 and inway(ix+1, iz, 4, xPos, yPos, zPos, hgt, ySkip)))
			if dummy(distInv, 0.0, UNIT_2X1, UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
				ret = 8
				xAp = xAp + float(ix)
				zAp = zAp + float(iz)
			endif
		endif
	endif
  
  
  rem Sneda vggar inom tile
	if td_tile[ix][iz].d = 1 and inway(ix, iz, 4, xPos, yPos, zPos, hgt, ySkip)
		if dummy(1.0-distance, 0.0, -UNIT_2X1, UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
			ret = 5
			xAp = xAp + float(ix)
			zAp = zAp + float(iz)
		endif
	endif
	if td_tile[ix][iz].d = 2 and inway(ix, iz, 4, xPos, yPos, zPos, hgt, ySkip)
		if dummy(1.0, 1.0-distance, -UNIT_2X1, -UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
			ret = 6
			xAp = xAp + float(ix)
			zAp = zAp + float(iz)
		endif
	endif
	if td_tile[ix][iz].d = 3 and inway(ix, iz, 4, xPos, yPos, zPos, hgt, ySkip)
		if dummy(distance, 1.0, UNIT_2X1, -UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
			ret = 7
			xAp = xAp + float(ix)
			zAp = zAp + float(iz)
		endif
	endif
	if td_tile[ix][iz].d = 4 and inway(ix, iz, 4, xPos, yPos, zPos, hgt, ySkip)
		if dummy(0.0, distance, UNIT_2X1, UNIT_2X1, xPos, zPos, dx, dz, xAp, zAp)
			ret = 8;
			xAp = xAp + float(ix)
			zAp = zAp + float(iz)
    endif
  endif
  
  rem orto
  if dx > 0.0 and xNew > distInv and inway(ix+1, iz, 0, xPos, yPos, zPos, hgt, ySkip)
		xAp = float(ix)+distInv
		ret = 1
	endif
  
	if dx < 0.0 and xNew < distance and inway(ix-1, iz, 2, xPos, yPos, zPos, hgt, ySkip)
		xAp = float(ix)+distance
		ret = 3
	endif
  
	if dz > 0.0 and zNew > distInv and inway(ix, iz+1, 1, xPos, yPos, zPos, hgt, ySkip)
		zAp = float(iz)+distInv
		ret = 2
	endif
  
	if dz < 0.0 and zNew < distance and inway(ix, iz-1, 3, xPos, yPos, zPos, hgt, ySkip)
		zAp = float(iz)+distance
		ret = 4
	endif
  
	rem ???
	ix = int(xAp)
	iz = int(zAp)

	return ret
endfunc

function wmFloorHeight(&yAp#, x#, z#)
	ix = int(x); iz = int(z)
	if ix < 0 or ix >= td_wmw or iz < 0 or iz >= td_wmh then return 0
	if td_tile[ix][iz].f = -1 then return 0
	x = x - float(ix)
	z = z - float(iz)
	if td_tile[ix][iz].ft = 0 or td_tile[ix][iz].ft = 2 or td_tile[ix][iz].ft = 3
		if x < 1.0-z
			if td_tile[ix][iz].ft = 3 then return 0
			yAp = td_floor[ix][iz][0]-(td_floor[ix][iz][4]*x+td_floor[ix][iz][6]*z)/td_floor[ix][iz][5]
		else
			if td_tile[ix][iz].ft = 2 then return 0
			x = x - 1.0
			z = z - 1.0
			yAp = td_floor[ix][iz][2]-(td_floor[ix][iz][7]*x+td_floor[ix][iz][9]*z)/td_floor[ix][iz][8]
		endif
	else
		if x < z
			if td_tile[ix][iz].ft = 5 then return 0
			z = z - 1.0
			yAp = td_floor[ix][iz][3]-(td_floor[ix][iz][7]*x+td_floor[ix][iz][9]*z)/td_floor[ix][iz][8]
		else
			if td_tile[ix][iz].ft = 4 then return 0
			x = x - 1.0
			yAp = td_floor[ix][iz][1]-(td_floor[ix][iz][4]*x+td_floor[ix][iz][6]*z)/td_floor[ix][iz][5]
		endif
	endif
	return 1
endfunc

function wmCeilingHeight(&yAp#, x#, z#)
  ix = int(x); iz = int(z)
	if ix < 0 or ix >= td_wmw or iz < 0 or iz >= td_wmh then return 0
	if td_tile[ix][iz].c = -1 then return 0
	x = x-float(ix)
	z = z-float(iz)
	if td_tile[ix][iz].ct = 0 or td_tile[ix][iz].ct = 2 or td_tile[ix][iz].ct = 3
		if x < 1.0-z
			if td_tile[ix][iz].ct = 3 then return 0
			yAp = td_ceil[ix][iz][0]-(td_ceil[ix][iz][4]*x+td_ceil[ix][iz][6]*z)/td_ceil[ix][iz][5]
		else
			if td_tile[ix][iz].ct = 2 then return 0
			x = x - 1.0
			z = z - 1.0
			yAp = td_ceil[ix][iz][2]-(td_ceil[ix][iz][7]*x+td_ceil[ix][iz][9])/td_ceil[ix][iz][8]
		endif
	else
		if x < z
			if td_tile[ix][iz].ct = 5 then return 0
			z = z - 1.0
			yAp = td_ceil[ix][iz][3]-(td_ceil[ix][iz][7]*x+td_ceil[ix][iz][9]*z)/td_ceil[ix][iz][8]
		else
			if td_tile[ix][iz].ct = 4 then return 0
			x = x - 1.0
			yAp = td_ceil[ix][iz][1]-(td_ceil[ix][iz][4]*x+td_ceil[ix][iz][6]*z)/td_ceil[ix][iz][5]
		endif
	endif
	return 1
endfunc

function inway(x, z, wall, xPos#, yPos#, zPos#, hgt#, ySkip#)
	yb#; yt#

	rem if x < 0 or x >= td_wmw-2 or z < 0 or z >= td_wmh-2 then return 1
	if x < 0 or x >= td_wmw-1 or z < 0 or z >= td_wmh-1 then return 1

	rem Trivialt.
  rem if(wm->tile[x][z] == NULL)
  rem  return(0);

	if td_tile[x][z].b then return 1

	if wall = 0
		if td_tile[x][z].w0 = -1 then return 0
	elseif wall = 1
		if td_tile[x][z].w1 = -1 then return 0
	elseif wall = 2
		if td_tile[x][z].w2 = -1 then return 0
	elseif wall = 3
		if td_tile[x][z].w3 = -1 then return 0
	else
		if td_tile[x][z].w4 = -1 then return 0
	endif

	if td_wall[x][z][wall][0] < td_wall[x][z][wall][1]
		yb = td_wall[x][z][wall][0]
	else
		yb = td_wall[x][z][wall][1]
	endif
  
	if td_wall[x][z][wall][2] > td_wall[x][z][wall][3]
		yt = td_wall[x][z][wall][2]
	else
		yt = td_wall[x][z][wall][3]
	endif
  
	if yPos < yt-ySkip and yPos+hgt > yb
    return 1
	endif
  
  return 0
endfunc

function dummy(Ax#, Az#, ax#, az#, Bx#, Bz#, bx#, bz#, &x#, &z#)
	t#; dx#; dz#
	if bx*az-bz*ax <= 0.0 then return 0
  t = (az*Bx - az*Ax + ax*Az - ax*Bz)/(ax*bz - az*bx)
 	if t < 1.0
		x = Bx+bx*t; z = Bz+bz*t
		bx = (1.0-t)*bx; bz = (1.0-t)*bz
		dx = (bx*ax+bz*az)*ax; dz = (bx*ax+bz*az)*az
		x = x + dx;z = z + dz 
		return 1    
  else
		return 0
  endif
endfunc

rem init.
function TD_Init(title$, x, y, w, h, fov#, zMin#, zMax#, hide)
	vMeshName = 1
	for i = 0 to MAX_OBJECTS - 1
		vObjects[i].used = false
	next
	res = glInit(title, x, y, w, h, hide)
	if res
		_glSet3D fov, zMin, zMax
		return true
	else
		return false
	endif
endfunc

procedure TD_Update()
	_glUpdate
endproc

function TD_Running()
	return glRunning()
endfunc

rem load mesh, return handle.
function TD_LoadMesh(filename$, smooth, reverse)
	return TD_LoadCGM(filename, smooth, reverse)
endfunc

rem free mesh.
procedure TD_FreeMesh(handle)
	rem ???
	rem cge free mesh handle	
endproc

rem ==================================================================
rem objects.
rem ==================================================================

rem create object, return handle.
function TD_CreateObj(meshHandle)
	for i = 0 to MAX_OBJECTS - 1
		if vObjects[i].used = false then break
	next

	if i < MAX_OBJECTS
		vObjects[i].used = true
		vObjects[i].m = meshHandle
		vObjects[i].x# = 0.0
		vObjects[i].y# = 0.0
		vObjects[i].z# = 0.0
		vObjects[i].sx# = 1.0
		vObjects[i].sy# = 1.0
		vObjects[i].sz# = 1.0
		vObjects[i].pivx# = 0.0
		vObjects[i].pivy# = 0.0
		vObjects[i].pivz# = 0.0
		vObjects[i].yaw# = 0.0
		vObjects[i].pitch# = 0.0
		vObjects[i].roll# = 0.0
		vObjects[i].tex = 0
		vObjects[i].lgt = 0
		vObjects[i].shd = TD_NONE
		return i + 1
	else
		return 0
	endif
endfunc

procedure TD_FreeObject(obj)
	obj = obj - 1
	vObjects[obj].used = false
endproc

rem set position.
procedure TD_SetObjPosition(obj, x#, y#, z#)
	obj = obj - 1
	vObjects[obj].x# = x
	vObjects[obj].y# = y
	vObjects[obj].z# = z
endproc

rem set pivot.
procedure TD_SetObjPivot(obj, x#, y#, z#)
	obj = obj - 1
	vObjects[obj].pivx# = x
	vObjects[obj].pivy# = y
	vObjects[obj].pivz# = z
endproc

rem set orientation.
procedure TD_SetObjOrientation(obj, yaw#, pitch#, roll#)
	obj = obj - 1
	vObjects[obj].yaw# = yaw
	vObjects[obj].pitch# = pitch
	vObjects[obj].roll# = roll
endproc

rem set scale.
procedure TD_SetObjScale(obj, x#, y#, z#)
	obj = obj - 1
	vObjects[obj].sx# = x; vObjects[obj].sy# = y; vObjects[obj].sz# = z
endproc

rem set texture.
procedure TD_SetObjTexture(obj, tex)
	obj = obj - 1; vObjects[obj].tex = tex
endproc

procedure TD_SetObjShader(obj, type)
	obj = obj - 1; vObjects[obj].shd = type
endproc

rem set light.
procedure TD_SetObjLightmap(obj, lgt)
	obj = obj - 1; vObjects[obj].lgt = lgt
endproc

rem ==================================================================
rem camera.
rem ==================================================================

rem set position.
procedure TD_SetCamPosition(x#, y#, z#)
	vCamX = x; vCamY = y; vCamZ = z
endproc

rem set orientation.
procedure TD_SetCamOrientation(yaw#, pitch#, roll#)
	vCamYaw = yaw; vCamPitch = pitch; vCamRoll = roll
endproc

rem ==================================================================
rem light.
rem ==================================================================

rem set direction.
procedure TD_SetLightDirection(dx#, dy#, dz#)
	vLightDX = dx; vLightDY = dy; vLightDZ = dz
endproc

rem ==================================================================
rem other.
rem ==================================================================

procedure TD_Render()
	_glClear GL_COLOR_BUFFER_BIT OR GL_DEPTH_BUFFER_BIT
	_glLoadIdentity
	_glRotatef vCamRoll, 0.0, 0.0, 1.0
	_glRotatef vCamPitch, 1.0, 0.0, 0.0
	_glRotatef vCamYaw + 90.0, 0.0, 1.0, 0.0

	if td_BG
		_glDepthMask false
		_glBindTexture GL_TEXTURE_2D, td_BGTexture
		_glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR
		_glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR
		_glPushMatrix
			_glRotatef 180.0, 1.0, 0.0, 0.0
			_glCallList td_BGSphere
		_glPopMatrix
		_glDepthMask true
	endif

	_glTranslatef -vCamX, -vCamY, -vCamZ
	_glEnable GL_TEXTURE_2D

	if td_Heightmap
		_glBindTexture GL_TEXTURE_2D, td_HeightmapTexture
		_glCallList td_Heightmap
	endif
	if td_Wallmap
		_glBindTexture GL_TEXTURE_2D, 0
		_glCallList td_Wallmap
	endif

	for i = 0 to MAX_OBJECTS - 1
		if vObjects[i].used
			_glPushMatrix
			_glTranslatef vObjects[i].x#, vObjects[i].y#, vObjects[i].z#
			_glRotatef vObjects[i].yaw#, 0.0, 1.0, 0.0
			_glRotatef vObjects[i].pitch#, 1.0, 0.0, 0.0
			_glRotatef vObjects[i].roll#, 0.0, 0.0, 1.0
			_glTranslatef -vObjects[i].pivx#, -vObjects[i].pivy#, -vObjects[i].pivz#
			_glScalef vObjects[i].sx#, vObjects[i].sy#, vObjects[i].sz#
			if vObjects[i].tex
				_glBindTexture GL_TEXTURE_2D, vObjects[i].tex
				_glCallList vObjects[i].m
			else
				_glDisable GL_TEXTURE_2D
				_glCallList vObjects[i].m
				_glEnable GL_TEXTURE_2D
			endif
			_glPopMatrix
		endif
	next
	_glRender
endproc

procedure TD_SetBGColor(r, g, b)
	_glClearColor float(r)/255.0, float(g)/255.0, float(b)/255.0, 1.0
endproc

procedure TD_SetFog(r, g, b, zMin#, zMax#)
	_glEnable GL_FOG
	_glFogi GL_FOG_MODE, GL_LINEAR
	_glFogf GL_FOG_START, zMin
	_glFogf GL_FOG_END, zMax
	_glFogfv GL_FOG_COLOR, [float(r)/255.0, float(g)/255.0, float(b)/255.0, 1.0]
endproc

procedure TD_DisableFog()
	_glDisable GL_FOG
endproc

procedure TD_EnableFog()
	_glEnable GL_FOG
endproc

function TD_LoadTexture(filename$)
	load image -1087, filename
	if image(-1087)
		tex = glCreateTexture(-1087, true)
		free image -1087
		return tex
	else
		return 0
	endif
endfunc

function TD_LoadBackground(filename$)
	td_BG = false
	td_BGTexture = TD_LoadTexture(filename)
	if td_BGTexture
		td_BG = true
		if td_BGSphere = 0
			td_BGSphere = TD_LoadCGM("assets/sphere.cgm", true, true)
			if td_BGSphere = 0
				td_BG = false
			endif
		endif
	endif
	return td_BG
endfunc

procedure TD_EnableBackground()
	td_BG = true
endproc

procedure TD_DisableBackground()
	td_BG = false
endproc

rem ==================================================================
rem Load CGM model.
rem ==================================================================
function TD_LoadCGM(filename$, smooth, reverse)
	open file 0, filename$, true
	if not file(0) then return 0
	head[256]
	i
	do
		byte = read8(0)
		head[i] = byte
		i = i + 1
		if i = 3 and not (head[0] = asc("C") and head[1] = asc("G") and head[2] = asc("M"))
			free file 0
			return 0
		endif	
	until byte = 0

	i = 3
	nFlag = false
	NFlag = false
	cFlag = false
	CFlag = false
	tFlag = false
	TFlag = false
	iFlag = false
	while head[i] <> 0
		if head[i] = asc("n")
			nFlag = true
		elseif head[i] = asc("N")
			NFlag = true
		elseif head[i] = asc("c")
			cFlag = true
		elseif head[i] = asc("C")
			CFlag = true
		elseif head[i] = asc("t")
			tFlag = true
		elseif head[i] = asc("T")
			TFlag = true
		elseif head[i] = asc("i")
			iFlag = true
		endif
		i = i + 1
	wend

	vertexCount = read32(0)
	triangleCount = read32(0)
	textureCount = read32(0)
	vertices?[vertexCount]
	triangles?[triangleCount]
	textures[textureCount]

	for i = 0 to vertexCount - 1
		vertices[i].x# = readf(0)
		vertices[i].y# = readf(0)
		vertices[i].z# = readf(0)
		if nFlag
			vertices[i].nx# = readf(0)
			vertices[i].ny# = readf(0)
			vertices[i].nz# = readf(0)
		endif
		if cFlag
			vertices[i].r# = readf(0)
			vertices[i].g# = readf(0)
			vertices[i].b# = readf(0)
		endif
		if iFlag
			vertices[i].i# = readf(0)
		endif
		if tFlag
			vertices[i].u# = readf(0)
			vertices[i].v# = readf(0)
		endif
	next

	for i = 0 to triangleCount - 1
		if reverse
			triangles[i].v2 = read32(0)
			triangles[i].v1 = read32(0)
			triangles[i].v0 = read32(0)
		else
			triangles[i].v0 = read32(0)
			triangles[i].v1 = read32(0)
			triangles[i].v2 = read32(0)
		endif
		if NFlag
			triangles[i].nx# = readf(0)
			triangles[i].ny# = readf(0)
			triangles[i].nz# = readf(0)
		endif
		if CFlag
			triangles[i].r# = readf(0)
			triangles[i].g# = readf(0)
			triangles[i].b# = readf(0)
		endif
		if TFlag
			rem texture index, starting at 1?..
			triangles[i].t = read32(0)
			if reverse
				triangles[i].v2u# = readf(0)
				triangles[i].v2v# = readf(0)
				triangles[i].v1u# = readf(0)
				triangles[i].v1v# = readf(0)
				triangles[i].v0u# = readf(0)
				triangles[i].v0v# = readf(0)
			else
				triangles[i].v0u# = readf(0)
				triangles[i].v0v# = readf(0)
				triangles[i].v1u# = readf(0)
				triangles[i].v1v# = readf(0)
				triangles[i].v2u# = readf(0)
				triangles[i].v2v# = readf(0)
			endif
		else
			triangles[i].t = -1
		endif
	next

	rem Load textures.
	for i = 0 to textureCount - 1
		w = read32(0)
		h = read32(0)
		texels[w*h]
		create image -1087, w, h
		set image -1087
		for j = 0 to w*h - 1
			red = read8(0)
			green = read8(0)
			blue = read8(0)
			set color red, green, blue
			set pixel j%w, j/w
		next
		set image primary
		textures[i] = glCreateTexture(-1087, true)
		free image -1087
	next

	free file 0

	rem Triangle normals.
	if not NFlag
		for i = 0 to triangleCount - 1
			v0 = triangles[i].v0
			v1 = triangles[i].v1
			v2 = triangles[i].v2
			a#[] = [vertices[v1].x# - vertices[v0].x#, vertices[v1].y# - vertices[v0].y#, vertices[v1].z# - vertices[v0].z#]
			b#[] = [vertices[v2].x# - vertices[v0].x#, vertices[v2].y# - vertices[v0].y#, vertices[v2].z# - vertices[v0].z#]
			n#[] = crossvp(a, b)
			_normv n
			triangles[i].nx# = n[0]
			triangles[i].ny# = n[1]
			triangles[i].nz# = n[2]
		next
	endif

	rem Vertex normals.
	if not nFlag
		for i = 0 to vertexCount - 1
			n#[] = [0.0, 0.0, 0.0]
			rem Look for triangles that include this vertex.
			for j = 0 to triangleCount - 1
				if triangles[j].v0 = i or triangles[j].v1 = i or triangles[j].v2 = i
					n[0] = n[0] + triangles[j].nx#
					n[1] = n[1] + triangles[j].ny#
					n[2] = n[2] + triangles[j].nz#
				endif
			next
			_normv n
			vertices[i].nx# = n[0]
			vertices[i].ny# = n[1]
			vertices[i].nz# = n[2]
		next
	endif

	rem Generate mesh.
	m = glGenLists(1)
	wln m
	_glNewList m, GL_COMPILE
			for t = -1 to textureCount - 1
				if t = -1 and textureCount > 0
					_glDisable(GL_TEXTURE_2D)
				elseif t >= 0
					_glBindTexture GL_TEXTURE_2D, textures[t]
				endif
				_glBegin GL_TRIANGLES
				for i = 0 to triangleCount - 1
					if triangles[i].t = t
						if CFlag and t = -1 then _glColor3f triangles[i].r#, triangles[i].g#, triangles[i].b#
						v0 = triangles[i].v0
						v1 = triangles[i].v1
						v2 = triangles[i].v2
						if smooth
							_glNormal3f vertices[v0].nx#, vertices[v0].ny#, vertices[v0].nz#
						else
						 	_glNormal3f triangles[i].nx#, triangles[i].ny#, triangles[i].nz#
						endif
						if TFlag
							_glTexCoord2f triangles[i].v0u#, triangles[i].v0v#
						elseif tFlag
							_glTexCoord2f vertices[v0].u#, vertices[v0].v#
						endif
						if cFlag then _glColor3f vertices[v0].r#, vertices[v0].g#, vertices[v0].b#
						_glVertex3f vertices[v0].x#, vertices[v0].y#, vertices[v0].z#
	
						if smooth then _glNormal3f vertices[v1].nx#, vertices[v1].ny#, vertices[v1].nz#
						if TFlag
							_glTexCoord2f triangles[i].v1u#, triangles[i].v1v#
						elseif tFlag
							_glTexCoord2f vertices[v1].u#, vertices[v1].v#
						endif
						if cFlag then _glColor3f vertices[v1].r#, vertices[v1].g#, vertices[v1].b#
						_glVertex3f vertices[v1].x#, vertices[v1].y#, vertices[v1].z#

						if smooth then _glNormal3f vertices[v2].nx#, vertices[v2].ny#, vertices[v2].nz#
						if TFlag
							_glTexCoord2f triangles[i].v2u#, triangles[i].v2v#
						elseif tFlag
							_glTexCoord2f vertices[v2].u#, vertices[v2].v#
						endif
						if cFlag then _glColor3f vertices[v2].r#, vertices[v2].g#, vertices[v2].b#
						_glVertex3f vertices[v2].x#, vertices[v2].y#, vertices[v2].z#
					endif
				next
				_glEnd
				if t = -1 and textureCount > 0
					_glEnable(GL_TEXTURE_2D)
				endif
			next
	_glEndList
	
	return m
endfunc

function TD_LoadHeightmap(filename$, tex_filename$, smooth)
	if smooth = true; flat = false
	else; flat = true; endif
	load image -1087, filename
	if not image(-1087) then return false

	rem Generate heightmap from image.
	hm_SizeZ = height(-1087)
	hm_SizeX = width(-1087)
	hm_Map[hm_SizeX][hm_SizeZ]
	rem Read heights.
	set image -1087
	for z = 0 to hm_SizeZ - 1
		for x = 0 to hm_SizeX - 1
			hm_Map[x][z].h# = 4.0*float(pixeli(x, z) AND 0xff)/255.0
		next
	next
	set image primary
	free image -1087

	load image -1087, tex_filename
	if image(-1087)
		td_HeightmapTexture = glCreateTexture(-1087, true)
		free image(-1087)
	endif

	rem Calculate distant light vector.
	lightx# = 1.0; lightz# = 1.0; lighty# = 0.5
	k# = 1.0/sqr(lightx*lightx + lightz*lightz + lighty*lighty)
	lightx = lightx*k; lightz = lightz*k; lighty = lighty*k

	rem Calculate two normals and one light (for flat shading) per tile.
	for z = 0 to hm_SizeZ - 2
		for x = 0 to hm_SizeX - 2
			a#[] = [0.0, hm_Map[x][z + 1].h# - hm_Map[x][z].h#, 1.0]
			b#[] = [1.0, hm_Map[x + 1][z + 1].h# - hm_Map[x][z].h#, 1.0]
			c#[] = [1.0, hm_Map[x + 1][z].h# - hm_Map[x][z].h#, 0.0]	
			n#[] = crossvp(a, b); _normv n
			hm_Map[x][z].nLx# = n[0]; hm_Map[x][z].nLy# = n[1]; hm_Map[x][z].nLz# = n[2]
			il# = max#(0.0, n[0]*lightx + n[1]*lighty + n[2]*lightz)
			n#[] = crossvp(b, c); _normv n
			hm_Map[x][z].nRx# = n[0]; hm_Map[x][z].nRy# = n[1]; hm_Map[x][z].nRz# = n[2]
			ir# = max#(0.0, n[0]*lightx + n[1]*lighty + n[2]*lightz)
			hm_Map[x][z].iF# = (il + ir)*0.5
		next
	next

	rem Calculate light for smooth shading.
	for z = 0 to hm_SizeZ - 1
		for x = 0 to hm_SizeX - 1
			n#[3]
			if x > 0 and z > 0
				n[0] = n[0] + hm_Map[x - 1][z - 1].nLx# + hm_Map[x - 1][z - 1].nRx#
				n[1] = n[1] + hm_Map[x - 1][z - 1].nLy# + hm_Map[x - 1][z - 1].nRy#
				n[2] = n[2] + hm_Map[x - 1][z - 1].nLz# + hm_Map[x - 1][z - 1].nRz#
			endif
			if x > 0 and z < hm_SizeZ - 1
				n[0] = n[0] + hm_Map[x - 1][z].nLx# + hm_Map[x - 1][z].nRx#
				n[1] = n[1] + hm_Map[x - 1][z].nLy# + hm_Map[x - 1][z].nRy#
				n[2] = n[2] + hm_Map[x - 1][z].nLz# + hm_Map[x - 1][z].nRz#
			endif
			if z < hm_SizeZ - 1 and x < hm_SizeX - 1	
				n[0] = n[0] + hm_Map[x][z].nLx# + hm_Map[x][z].nRx#
				n[1] = n[1] + hm_Map[x][z].nLy# + hm_Map[x][z].nRy#
				n[2] = n[2] + hm_Map[x][z].nLz# + hm_Map[x][z].nRz#
			endif
			if z > 0 and x < hm_SizeX - 1
				n[0] = n[0] + hm_Map[x][z - 1].nLx# + hm_Map[x][z - 1].nRx#
				n[1] = n[1] + hm_Map[x][z - 1].nLy# + hm_Map[x][z - 1].nRy#
				n[2] = n[2] + hm_Map[x][z - 1].nLz# + hm_Map[x][z - 1].nRz#
			endif
			_normv n
			hm_Map[x][z].i# = max#(0.0, n[0]*lightx + n[1]*lighty + n[2]*lightz)
		next
	next

	rem Build display list.
	td_Heightmap = glGenLists(1)
	_glNewList td_Heightmap, GL_COMPILE
	for z = 0 to hm_SizeZ - 2
		_glBegin GL_TRIANGLE_STRIP
		x = 0
		if flat; _glColor3f hm_Map[x][z].iF#, hm_Map[x][z].iF#, hm_Map[x][z].iF#
		else; _glColor3f hm_Map[x][z].i#, hm_Map[x][z].i#, hm_Map[x][z].i#; endif
		_glTexCoord2f 0.0, 0.0; _glVertex3f float(x), hm_Map[x][z].h#, float(z)
		if not flat then _glColor3f hm_Map[x][z + 1].i#, hm_Map[x][z + 1].i#, hm_Map[x][z + 1].i#
		_glTexCoord2f 0.0, 1.0; _glVertex3f float(x), hm_Map[x][z + 1].h#, float(z) + 1.0
		if not flat then _glColor3f hm_Map[x + 1][z].i#, hm_Map[x + 1][z].i#, hm_Map[x + 1][z].i#
		_glTexCoord2f 1.0, 0.0; _glVertex3f float(x) + 1.0, hm_Map[x + 1][z].h#, float(z)
		for x = 1 to hm_SizeX - 2
			if flat;_glColor3f hm_Map[x - 1][z].iF#, hm_Map[x - 1][z].iF#, hm_Map[x - 1][z].iF#
			else; _glColor3f hm_Map[x][z + 1].i#, hm_Map[x][z + 1].i#, hm_Map[x][z + 1].i#; endif
			_glTexCoord2f float(x), 1.0; _glVertex3f float(x), hm_Map[x][z + 1].h#, float(z) + 1.0
			if flat then; _glColor3f hm_Map[x][z].iF#, hm_Map[x][z].iF#, hm_Map[x][z].iF#
			else; _glColor3f hm_Map[x + 1][z].i#, hm_Map[x + 1][z].i#, hm_Map[x + 1][z].i#; endif
			_glTexCoord2f float(x + 1), 0.0; _glVertex3f float(x) + 1.0, hm_Map[x + 1][z].h#, float(z)
		next
		if flat; _glColor3f hm_Map[x - 1][z].iF#, hm_Map[x - 1][z].iF#, hm_Map[x - 1][z].iF#
		else; _glColor3f hm_Map[x][z + 1].i#, hm_Map[x][z + 1].i#, hm_Map[x][z + 1].i#; endif
		_glTexCoord2f float(x), 1.0; _glVertex3f float(x), hm_Map[x][z + 1].h#, float(z) + 1.0
		_glEnd
	next
	_glEndList

	return true
endfunc


rem Get height
function TD_GetHeightmapHeight(&y#, x#, z#)
	if x < 0.0 or z < 0.0 then return false
	ix = int(x); iz = int(z)
	if ix >= hm_SizeX - 1 or iz >= hm_SizeZ - 1 then return false
	x = x - float(ix); z = z - float(iz)
	if x < z
		y# = hm_Map[ix][iz].h# - (hm_Map[ix][iz].nLx#*x + hm_Map[ix][iz].nLz#*z)/hm_Map[ix][iz].nLy#
	else
		x = x - 1.0; z = z - 1.0
		y# = hm_Map[ix + 1][iz + 1].h# - (hm_Map[ix][iz].nRx#*x + hm_Map[ix][iz].nRz#*z)/hm_Map[ix][iz].nRy#
	endif
	return true
endfunc

procedure hmNormal(&norm#[], x#, z#)
	ix = int(x); iz = int(z); index
	if ix < 0 or ix >= hm_SizeX - 1 or iz < 0 or iz >= hm_SizeZ - 1
		norm[0] = 0.0; norm[1] = 1.0;norm[2] = 0.0
		return
	endif
	x = x-float(ix); z = z-float(iz)
	if x < 1.0-z
		norm[0] = hm_Map[ix][iz].nLx#; norm[1] = hm_Map[ix][iz].nLy#; norm[2] = hm_Map[ix][iz].nLz#
	else
		norm[0] = hm_Map[ix][iz].nRx#; norm[1] = hm_Map[ix][iz].nRy#; norm[2] = hm_Map[ix][iz].nRz#
	endif
endproc

function TD_GetWidth()
	return glWidth()
endfunc

function TD_GetHeight()
	return glHeight()
endfunc

procedure TD_PullCeiling(x, z, h#)
	for i = 0 to 3; td_ceil[x][z][i] = h; next
	for i = 0 to 3
		td_wall[x][z][i][2] = h
		td_wall[x][z][i][3] = h
	next
	td_wall[x - 1][z][2][2] = h
	td_wall[x - 1][z][2][3] = h
	td_wall[x - 1][z][1][3] = h
	td_wall[x - 1][z][3][2] = h
	td_wall[x + 1][z][0][2] = h
	td_wall[x + 1][z][0][3] = h
	td_wall[x + 1][z][1][2] = h
	td_wall[x + 1][z][3][3] = h
	td_wall[x][z - 1][3][2] = h
	td_wall[x][z - 1][3][3] = h
	td_wall[x][z - 1][2][3] = h
	td_wall[x][z - 1][0][2] = h
	td_wall[x][z + 1][1][2] = h
	td_wall[x][z + 1][1][3] = h
	td_wall[x][z + 1][2][2] = h
	td_wall[x][z + 1][0][3] = h
	td_wall[x - 1][z - 1][2][3] = h
	td_wall[x - 1][z - 1][3][2] = h
	td_wall[x - 1][z + 1][1][3] = h
	td_wall[x - 1][z + 1][2][2] = h
	td_wall[x + 1][z - 1][0][2] = h
	td_wall[x + 1][z - 1][3][3] = h
	td_wall[x + 1][z + 1][1][2] = h
	td_wall[x + 1][z + 1][0][3] = h

	td_ceil[x + 1][z][3] = h; td_ceil[x + 1][z][0] = h
	td_ceil[x - 1][z][1] = h; td_ceil[x - 1][z][2] = h
	td_ceil[x][z + 1][0] = h; td_ceil[x][z + 1][1] = h
	td_ceil[x][z - 1][2] = h; td_ceil[x][z - 1][3] = h
	td_ceil[x + 1][z + 1][0] = h
	td_ceil[x + 1][z - 1][3] = h
	td_ceil[x - 1][z + 1][1] = h
	td_ceil[x - 1][z - 1][2] = h
endproc

procedure TD_PullFloor(x, z, h#)
	for i = 0 to 3; td_floor[x][z][i] = h; next
	for i = 0 to 3
		td_wall[x][z][i][1] = h
		td_wall[x][z][i][0] = h
	next
	td_wall[x - 1][z][2][1] = h
	td_wall[x - 1][z][2][0] = h
	td_wall[x - 1][z][1][0] = h
	td_wall[x - 1][z][3][1] = h
	td_wall[x + 1][z][0][1] = h
	td_wall[x + 1][z][0][0] = h
	td_wall[x + 1][z][1][1] = h
	td_wall[x + 1][z][3][0] = h
	td_wall[x][z - 1][3][1] = h
	td_wall[x][z - 1][3][0] = h
	td_wall[x][z - 1][2][0] = h
	td_wall[x][z - 1][0][1] = h
	td_wall[x][z + 1][1][1] = h
	td_wall[x][z + 1][1][0] = h
	td_wall[x][z + 1][2][1] = h
	td_wall[x][z + 1][0][0] = h
	td_wall[x - 1][z - 1][2][0] = h
	td_wall[x - 1][z - 1][3][1] = h
	td_wall[x - 1][z + 1][1][0] = h
	td_wall[x - 1][z + 1][2][1] = h
	td_wall[x + 1][z - 1][0][1] = h
	td_wall[x + 1][z - 1][3][0] = h
	td_wall[x + 1][z + 1][1][1] = h
	td_wall[x + 1][z + 1][0][0] = h

	td_floor[x + 1][z][3] = h; td_floor[x + 1][z][0] = h
	td_floor[x - 1][z][1] = h; td_floor[x - 1][z][2] = h
	td_floor[x][z + 1][0] = h; td_floor[x][z + 1][1] = h
	td_floor[x][z - 1][2] = h; td_floor[x][z - 1][3] = h
	td_floor[x + 1][z + 1][0] = h
	td_floor[x + 1][z - 1][3] = h
	td_floor[x - 1][z + 1][1] = h
	td_floor[x - 1][z - 1][2] = h
endproc

procedure TD_SetWallmapColor(i, r, g, b)
	td_color[i][0] = float(r)/255.0; td_color[i][1] = float(g)/255.0; td_color[i][2] = float(b)/255.0
endproc