Code archives/3D Graphics - Misc/YAL improvments

This code has been declared by its author to be Public Domain code.

Download source code

YAL improvments by Olive2002
Lights now have types, POINT_LIGHT (normal lights), and a new one : SUN_LIGHT
;
;	YAL - Yet Another Lightmapper
;	Version: 1.2
;
;	Please post any code improvement into blitz basic main site www.blitzbasic.co.nz, and include your data into history
;	Thanks to David Dawkins (startfox) and elias_t, that produced the base code for this file.
;
;	References:
;		http://members.net-tech.com.au/alaneb/lightmapping_tutorial.html  	(lightmap tutorial)
;		http://polygone.flipcode.com/tut_lightmap.htm						(lightmap tutorial)
;		http://www.blackpawn.com/texts/lightmaps/default.html				(lightmap packing)
;	
;
;
;	To Do:
;		- Terrain lightmap precision  (seems that the shadows are one or two lumels offset from the correct position)
;		- An weld(mesh) function that re-weld the vertices that have the same values
;		- Other light types, such as directional and spot
;
;
;	History:
;		1.0	(28/11/2002)	- Initial version (marcelo@greenlandstudios.com)
;		1.1 (30/11/2002)	- Generate surfaces with more than one triangle (marcelo)
;							  Per light attenuation and brightness (marcelo)
;							  Functions to apply, save and load the lightmap (marcelo)
;							  Lightmap sharing (starfox)

;Modifications made by O.Arguimbau : 
;
;		1.2 (9/12/2002)		- Bug correction in surface detection
;							- Lights now have types, POINT_LIGHT (normal lights), and a new one : SUN_LIGHT (a directional light without distances considerations)
;							- A little bug correction in loadlightmap
;							- Modification of the exemple code to show lightmapterrain in action
;							- Added the LightMapParams function to modify the light parameters (maybe usefull)
;
;I recommend to calculate objects lightmapping BEFORE adding a Sun_Light because it's VERY slow : Createlmlights(point_lights).....  LightmapMeshes... CreateLmlight(sun_light) Lightmapterrain
;but if you have a powerfull PC...you can try it.
;PS : I choose to upgrade the version to 1.2, maybe the original author have better ideas ??? So Marcelo, it's up to you.


; Call example 
LMExample()

Const FLT_MAX = 65535
Const SUN_LIGHT = 0
Const POINT_LIGHT = 1

; Set to True to draw the triangle edges on the texture
Const LM_DRAWTRIS = False

; Max polys per surface
Const LM_SURFTRIS = 256

; Num verts per poly
Const LM_VERTS = 2

; Angle between normals tolerance
Const LM_NORMAL_EPSILON# = 1.0

; Vertex distance tolerance
Const LM_VERTPOS_EPSILON# = 0.01

; Mapping plane
Const LMPLANE_XY = 0
Const LMPLANE_XZ = 1
Const LMPLANE_YZ = 2

; Mininum texture size
Const LM_MINTEXSIZE = 2

; Types
Type LMTriangle

	; Vertex info
	Field X#[LM_VERTS], Y#[LM_VERTS], Z#[LM_VERTS]
	Field U#[LM_VERTS], V#[LM_VERTS]
	Field VertIndex[LM_VERTS]
	
	; Normal
	Field NX#, NY#, NZ#

	; True if the triangle is part of a surface
	Field Linked
	
	; Original surface pointer
	Field Surf
End Type

Type LMSurface
	; Triangle list
	Field Tris.LMTriangle[LM_SURFTRIS]
	Field NTris%

	; Plane
	Field NX#, NY#, NZ#
	Field Plane%
	
	; UV Bound Box
	Field UMin#, UMax#, UDelta#
	Field VMin#, VMax#, VDelta#
	
	; UV to worldspace transformations
	Field UEdgeX#, UEdgeY#, UEdgeZ#
	Field VEdgeX#, VEdgeY#, VEdgeZ#
	Field OriginX#, OriginY#, OriginZ#
	
	; Misc
	Field Image
	Field ImageSize
End Type

; Wrapper to sort the surfaces
Type LMSortedSurface
	Field Surf.LMSurface
End Type

; Node for the packer
Type LMImgNode
	Field Child.LMImgNode[1]
	Field Surf.LMSurface
	
	Field X1%, Y1%
	Field X2%, Y2%
End Type

; Global parameters
Type LMParams
	Field AmbR, AmbG, AmbB
End Type

; Light
Type LMLight
	Field X#, Y#, Z#
	Field R#, G#, B#
	
	Field Range#
	Field Att#[2]
	Field Bright#
	Field TypeLight
End Type

; Store global parameters
Global g_LMParams.LMParams = Null


;	*****************
;	
;	Public functions
;	
;	*****************

; Create and setup global parameters
; AmbR, AmbG, AmbB is the ambient light color
Function BeginLightMap(AmbR = 0, AmbG = 0, AmbB = 0)
	g_LMParams = New LMParams
	
	g_LMParams\AmbR = AmbR
	g_LMParams\AmbG = AmbG
	g_LMParams\AmbB = AmbB
End Function

Function LightMapParams(AmbR = 0, AmbG = 0, AmbB = 0)
	g_LMParams\AmbR = AmbR
	g_LMParams\AmbG = AmbG
	g_LMParams\AmbB = AmbB
End Function

; Free parameters and stuff
Function EndLightMap()
	If g_LMParams <> Null
		; Delete all lights
		For Light.LMLight = Each LMLight
			Delete Light
		Next
		
		Delete g_LMParams
		g_LMParams = Null
	EndIf
End Function

; Create a new Light for lightmapping, only point lights until now
; x, y, z - world space coordinates
; r, g, b - Red, Green and Blue amounts (0..255)
; range  - Maximum distance that the light will affect
;   (only clamps the distance, If you want a falloff effect use the attenuation coefficients)
;
; bright - Light brightness
;
; att0, att1, att2 - Coefficients for light attenuation (control the falloff curve) 
;   lumel attenuation# = 1.0 / (att0 + (att1 * dist) + (att2 * dist^2)
;   where dist is the distance from light source to lumel

Function CreateLMLight.LMLight(x#, y#, z#, r#, g#, b#, range#, bright# = 10.0, att0# = 0, att1# = 1, att2# = 0, typelight = POINT_LIGHT)
	l.LMLight = New LMLight
	l\X = x : l\Y = y : l\Z = z
	l\R = r : l\G = g : l\B =b
	l\Range = range
	
	l\Bright = bright
	l\TypeLight = typelight
	
	l\Att[0] = att0
	l\Att[1] = att1
	l\Att[2] = att2
	
	Return l
End Function


; Apply an lightmap created with LightMapMesh or LightMapTerrain
Function ApplyLightMap(mesh, tex, layer = 1)
	If Not tex
		Return False
	EndIf

	EntityFX(mesh, 1)
	EntityTexture(mesh, tex, 0, layer)
	FreeTexture(tex)
	
	Return True
End Function

; Save to a bmp file and a luv file the information about a lightmapped entity
Function SaveLightMap(mesh, tex, imgfile$, luvfile$)
	If Not tex
		Return False
	EndIf

	SaveBuffer(TextureBuffer(tex), imgfile$)
	CreateLUVs(mesh, luvfile$, 1) 
End Function

; Load an image file and the luv file into the entity 
Function LoadLightMap(mesh, imgfile$, luvfile$, layer = 1)
	Unweld(mesh)

	If FileType(luvfile$)
		LoadLUVs(mesh, luvfile$) 
	EndIf

	tex = LoadTexture(imgfile$) 
	If tex
		EntityFX(mesh, 1)
		TextureCoords(tex, 1) 
		EntityTexture(mesh, tex, 0, layer)
		FreeTexture tex
	EndIf
End Function


; Assigns a 2nd channel planar mapping coordinates to the mesh and returns a packed texture that can be applied for lightmapping
;
; NOTES: 
;
; - The world objects must have EntityPickMode() set to produce shadows
; - The mesh is changed in the process (unwelded)
; - Lumel is the equivalent of an texel, but for lightmaps
; - lumelsize# is the size of the lumel in the world units to control the resolution of the lightmap
;		Example: If you use the metric system, a 0.2 lumelsize will create a lumel at each 20 centimeters
; - maxmapsize : maximum texture size that the lightmapper can pack (only used if needed)
; - blurradius : blur the resul image by this radius			
;
Function LightMapMesh(mesh, lumelsize# = 0.5, maxmapsize = 1024, blurradius = 1)

	SetBuffer(BackBuffer())
	Cls
	sMsg$="Creating lightmap object "+EntityName(mesh)+"..."
	font=LoadFont("Arial",20,True)
	SetFont font
	Color 250,250,150
	Text GraphicsWidth()/2,(GraphicsHeight()/2),sMsg$,True,True
	;progress bar
	progW=400
	progX=(GraphicsWidth()/2)-(progW/2)
	progY=(GraphicsHeight()/2)+50
	progH=20
	Color 0,0,200
	Rect progX-4,progY-4,progW+8,progH+8
	Flip

	UnWeld(mesh)

	NbSurf = CountSurfaces(mesh)
	; Run thru all surfaces & triangles storing the info into LMTriangle
	For surfcount = 1 To NbSurf

		surf = GetSurface(mesh, surfcount)
		For tricount = 0 To CountTriangles(surf) - 1

			Tri.LMTriangle = New LMTriangle

			For i = 0 To LM_VERTS
				vertn = TriangleVertex(surf, tricount, i)
				TFormPoint(VertexX(surf, vertn), VertexY(surf, vertn), VertexZ(surf, vertn), mesh, 0)
				Tri\X[i] = TFormedX() : Tri\Y[i] = TFormedY() : Tri\Z[i] = TFormedZ()
				
				Tri\VertIndex[i] = vertn
			Next
		
			Tri\Surf = Surf
	
			GetTriangleNormal(Tri\X[0], Tri\Y[0], Tri\Z[0], Tri\X[1], Tri\Y[1], Tri\Z[1], Tri\X[2], Tri\Y[2], Tri\Z[2])
			Tri\NX = TriangleNormalX() : Tri\NY = TriangleNormalY() : Tri\NZ = TriangleNormalZ()
		Next	
	Next

	; Create the surfaces
	SurfaceCount = 0
	While True
		LMSurf.LMSurface = New LMSurface
		
		SurfaceCount = SurfaceCount + 1
		
		; Find the first unlinked triangle
		For Tri.LMTriangle = Each LMTriangle
			If Not Tri\Linked
				Exit
			EndIf
		Next
		
		; No more unlinked tris
		If Tri = Null 
			Exit
		EndIf
		
		Tri\Linked = True
		LMSurf\Tris[LMSurf\NTris] = Tri
		LMSurf\NTris = LMSurf\NTris + 1
				
		; Search for adjacent tri's with the same caracteristics and append to list

		; Three waves to assure that all the poly's will be get
		For Wave = 1 To 3
			For STri.LMTriangle = Each LMTriangle
				If Not STri\Linked
	
					; Compare the triangle normal 
;Bug ???
;					Ang# = Abs((STri\NX * Tri\NX) + (STri\NY * Tri\NY) + (STri\NZ * Tri\NZ))

					Ang# = ((STri\NX * Tri\NX) + (STri\NY * Tri\NY) + (STri\NZ * Tri\NZ))
					If ACos(Ang) <= LM_NORMAL_EPSILON
						
						NSharedVerts = 0
						
						; Check if it shares vertices with one of the current surface triangles
						For i = 0 To LMSurf\NTris-1
							VTri.LMTriangle = LMSurf\Tris[i]
							
							For j = 0 To LM_VERTS
								For k = 0 To LM_VERTS
									DX# = STri\X[j] - VTri\X[k]
									DY# = STri\Y[j] - VTri\Y[k]
									DZ# = STri\Z[j] - VTri\Z[k]
								
									Dist# = Sqr(DX*DX + DY*DY + DZ*DZ)
								
									If Dist <= LM_VERTPOS_EPSILON
										NSharedVerts = NSharedVerts + 1
										Exit
									EndIf
								Next
							Next
						Next
						
						If NSharedVerts > 0
							STri\Linked = True		
							LMSurf\Tris[LMSurf\NTris] = STri
							LMSurf\NTris = LMSurf\NTris + 1
					
							If LMSurf\NTris > LM_SURFTRIS
								Exit
							EndIf
						EndIf
					EndIf
				EndIf
			Next
			
			If LMSurf\NTris > LM_SURFTRIS
				Exit
			EndIf
		Next
		
		LMSetupSurface(LMSurf, lumelsize)
	Wend
	
	CurrentCount = 0
	For LMSurf.LMSurface = Each LMSurface
	
		CurrentCount = CurrentCount + 1

		If KeyHit(1) Then End
		
		; Create the light texture
		LMLightSurface(LMSurf, lumelsize)
		
		; Blur resulting image
		If blurradius > 0
			LMBlurImage(LMSurf\Image, blurradius)
		EndIf
		
		;Progression
		SetBuffer BackBuffer()
		Color 0,0,200
		Cls
		Rect progX-4,progY-4,progW+8,progH+8
		
		Color 255,0,0
		Rect progX,progY,progW/Float(SurfaceCount)*Float(CurrentCount),progH
		
		Color 250,250,150
		Text GraphicsWidth()/2,(GraphicsHeight()/2),sMsg$,True,True
		
		Flip
		
	Next
			
	; First sort it by image size, larger images enter first
	For LMSurf.LMSurface = Each LMSurface
		
		; Search for a lower image size
		For SLMSurf.LMSortedSurface = Each LMSortedSurface
			If SLMSurf\Surf\ImageSize <= LMSurf\ImageSize
				Exit
			EndIf
		Next
		
		NLMSurf.LMSortedSurface = New LMSortedSurface
		NLMSurf\Surf = LMSurf
		
		If SLMSurf <> Null
			Insert NLMSurf Before SLMSurf
		EndIf
	Next
	
	; Get the mininum map size possible
	lmapsize% = LMPacker_FitTexSize(maxmapsize)
	
	; Pack into a big texture
	Tex = LMPacker_Pack(lmapsize%)
	
	; Free temporary stuff
	For LMSurf.LMSurface = Each LMSurface
		FreeImage(LMSurf\Image)
		Delete LMSurf
	Next
	
	Delete Each LMSortedSurface
	Delete Each LMTriangle
	
	Return Tex
End Function

;
;	Same as the lightmapmesh, but for terrains. detail% is the texture map size
;
Function LightMapTerrain(terrain, detail% = 0, blurradius% = 1)

	SetBuffer(BackBuffer())
	Cls
	sMsg$="Creating terrain lightmap..."
	font=LoadFont("Arial",20,True)
	SetFont font
	Color 250,250,150
	Text GraphicsWidth()/2,(GraphicsHeight()/2),sMsg$+" 1",True,True
	;progress bar
	progW=400
	progX=(GraphicsWidth()/2)-(progW/2)
	progY=(GraphicsHeight()/2)+50
	progH=20
	Color 0,0,200
	Rect progX-4,progY-4,progW+8,progH+8
	Flip

	TSize# = TerrainSize(terrain)

	If detail = 0
		detail = TSize
	EndIf
	
	; Get the entity scale
	vx# = GetMatElement(terrain, 0, 0)
	vy# = GetMatElement(terrain, 0, 1)
	vz# = GetMatElement(terrain, 0, 2)
	XScale# = Sqr(vx*vx + vy*vy + vz*vz)
	vx# = GetMatElement(terrain, 1, 0)
	vy# = GetMatElement(terrain, 1, 1)
	vz# = GetMatElement(terrain, 1, 2)
	YScale# = Sqr(vx*vx + vy*vy + vz*vz)
	vx# = GetMatElement(terrain, 2, 0)
	vy# = GetMatElement(terrain, 2, 1)
	vz# = GetMatElement(terrain, 2, 2)
	ZScale# = Sqr(vx*vx + vy*vy + vz*vz)
	
	; Relation between detail and texture size
	Scale# = 1
	If detail < TSize
		Scale# = Float(detail)/Float(TSize)
	EndIf

	LMSize = detail
	Img = CreateImage(LMSize, LMSize)

	ImgBuf = ImageBuffer(Img)
	SetBuffer(ImgBuf)
	
	; Set the ambient light
	ClsColor(g_LMParams\AmbR, g_LMParams\AmbG, g_LMParams\AmbB)
	Cls()
	ClsColor(0, 0, 0)
	
	LockBuffer(ImgBuf)
	
	LightPivot = CreatePivot()

	LumelPivot = CreatePivot()
	EntityPickMode(LumelPivot, 1)
	EntityRadius(LumelPivot, 0.625)
	
	xpos# = EntityX(terrain) : ypos# = EntityY(terrain) : zpos# = EntityZ(terrain)
	
	cptlight = 0
	For Light.LMLight = Each LMLight
	
		cptlight = cptlight + 1
		
		PositionEntity(LightPivot, Light\X, Light\Y, Light\Z)
		
		;Added by O.Arguimbau : Determination of the light influence (aproximation) with a simple vertical projection (NEED OPTIMISATION/CORRECTION ?)
		If (Light\TypeLight = POINT_LIGHT)
			zmin = (LMSize-1)-Ceil(((Light\Z+Light\Range)-zpos)/zscale) - 2
			zmax = (LMSize-1)-Floor(((Light\Z-Light\Range)-zpos)/zscale) + 2
	
			xmin = Floor(((Light\X-Light\Range)-xpos)/xscale) - 2
			xmax = Ceil(((Light\X+Light\Range)-xpos)/xscale) + 2
	
			If zmin < 0 Then zmin = 0
			If zmax > LMSize-1 Then zmax = LMSize-1
					
			If xmin < 0 Then xmin = 0
			If xmax > LMSize-1 Then xmax = LMSize-1
		Else
			xmin = 0 : zmin = 0
			xmax = LMSize-1 : zmax = LMSize-1
		EndIf

		For z% = zmin To zmax
			If KeyHit(1) Then End
			For x% = xmin To xmax

				zp% = TSize - z
				y# = TerrainHeight(terrain, x+1, zp)
				
				LumX# = (xpos + Float(x)  * XScale) / Scale
				LumY# = (ypos + Float(y)  * YScale) / Scale
				LumZ# = (zpos + Float(zp) * ZScale) / Scale

				PositionEntity(LumelPivot, LumX, LumY, LumZ)
				Dist# = EntityDistance(LightPivot, LumelPivot)
				
				Select Light\TypeLight
					;Added by O.Arguimbau : Sun light is a directionnal light, all rays are parallels
					Case SUN_LIGHT
						;Check for visibility
						xi# = EntityX(LumelPivot) : yi# = EntityY(LumelPivot) : zi# = EntityZ(LumelPivot)
						PositionEntity LightPivot, Light\X + xi, Light\Y + yi, Light\Z + zi
								
						If EntityVisible(LumelPivot,LightPivot)
                            Intensity# = Light\Bright
		
							ARGB = ReadPixelFast(x, z) And $FFFFFF
							R = (ARGB Shr 16 And %11111111)
							G = (ARGB Shr 8 And %11111111)
							B = (ARGB And %11111111)
							
							R = R + (Light\R * Intensity)
							G = G + (Light\G * Intensity)
							B = B + (Light\B * Intensity)
							
							If R > 255 Then R = 255
							If G > 255 Then G = 255
							If B > 255 Then B = 255
							
							RGB = B Or (G Shl 8) Or (R Shl 16)
							WritePixelFast(x, z, RGB)
						EndIf
					
					Case POINT_LIGHT
						; If this light can light this lumel		
						If (Dist <= Light\Range) And (Dist > 0)
							LMLightProcess(x, z, Light, LumX, LumY, LumZ, Dist, 1.0, LumelPivot)
						EndIf ; Dist < Light\Range
				End Select								
			Next ; x
			;Progression
			UnlockBuffer(ImgBuf)
			
			SetBuffer(BackBuffer())
			ClsColor 0,0,0:Cls
			Color 0,0,200
			Rect progX-4,progY-4,progW+8,progH+8
			
			Color 255,0,0
			Rect progX,progY,progW/Float(zmax)*Float(z+1),progH
			
			Color 250,250,150
			Text GraphicsWidth()/2,(GraphicsHeight()/2),sMsg$ + " " + cptLight,True,True
			
			Flip
			
			SetBuffer (ImgBuf)
			LockBuffer(ImgBuf)		
		Next ; z
	Next
	
	UnlockBuffer(ImgBuf)
	
	; Blur resulting image
	If blurradius > 0
		LMBlurImage(Img, blurradius)
	EndIf
	
	Tex = CreateTexture(LMSize, LMSize, 512)
	CopyRect(0, 0, LMSize, LMSize, 0, 0, ImageBuffer(Img), TextureBuffer(Tex))
	
	TextureCoords(Tex, 1)
	ScaleTexture(Tex, TSize, TSize)

	FreeImage(Img)
	
	SetBuffer(BackBuffer())
	
	FreeEntity(LightPivot)
	FreeEntity(LumelPivot)
	
	Return Tex
End Function






;	******************
;
;	Private functions
;
;   ******************



;  Lightmap packing functions
Function LMPacker_Pack(lmapsize)
	Tex = CreateTexture(lmapsize, lmapsize, 512)
	SetBuffer(TextureBuffer(Tex))	
	
	LMRoot.LMImgNode = New LMImgNode
	LMRoot\X1 = 0 : LMRoot\Y1 = 0
	LMRoot\X2 = lmapsize : LMRoot\Y2 = lmapsize
	LMRoot\Surf = Null
	
	SurfCnt = 0
	
	For SLMSurf.LMSortedSurface = Each LMSortedSurface
		
		; Insert in the best location
		Img.LMImgNode = LMPacker_Insert(LMRoot, SLMSurf\Surf)
		
		If Img <> Null
		
			LMSurf.LMSurface = Img\Surf
			
			IW = ImageWidth(LMSurf\Image)
			IH = ImageHeight(LMSurf\Image)
			
			CopyRect(0, 0, IW, IH, Img\X1, Img\Y1, ImageBuffer(LMSurf\Image), TextureBuffer(Tex))
			
			; Scale the original UV's to the new position and scale
			DX# = Float(Img\X1) / Float(lmapsize)
			DY# = Float(Img\Y1) / Float(lmapsize)
			
			ScaleU# = Float(IW) / Float(lmapsize)
			ScaleV# = Float(IH) / Float(lmapsize)
			
			For i = 0 To LMSurf\NTris-1
				For j = 0 To LM_VERTS
					LMSurf\Tris[i]\U[j] = (LMSurf\Tris[i]\U[j] * ScaleU) + DX
					LMSurf\Tris[i]\V[j] = (LMSurf\Tris[i]\V[j] * ScaleV) + DY
					
					VertexTexCoords(LMSurf\Tris[i]\Surf, LMSurf\Tris[i]\VertIndex[j], LMSurf\Tris[i]\U[j], LMSurf\Tris[i]\V[j], 0, 1)
				Next
			Next
			
			; Draw debug stuff if needed
			If LM_DRAWTRIS
				; Triangles
				Color(255, 255, 255)
				For i = 0 To LMSurf\NTris-1
					x1% = LMSurf\Tris[i]\U[0] * Float(lmapsize)
					y1% = LMSurf\Tris[i]\V[0] * Float(lmapsize)
					x2% = LMSurf\Tris[i]\U[1] * Float(lmapsize)
					y2% = LMSurf\Tris[i]\V[1] * Float(lmapsize)
					Line(x1, y1, x2, y2)
					x1% = LMSurf\Tris[i]\U[1] * Float(lmapsize)
					y1% = LMSurf\Tris[i]\V[1] * Float(lmapsize)
					x2% = LMSurf\Tris[i]\U[2] * Float(lmapsize)
					y2% = LMSurf\Tris[i]\V[2] * Float(lmapsize)
					Line(x1, y1, x2, y2)
					x1% = LMSurf\Tris[i]\U[2] * Float(lmapsize)
					y1% = LMSurf\Tris[i]\V[2] * Float(lmapsize)
					x2% = LMSurf\Tris[i]\U[0] * Float(lmapsize)
					y2% = LMSurf\Tris[i]\V[0] * Float(lmapsize)
					Line(x1, y1, x2, y2)
				Next
			EndIf
			
			SurfCnt = SurfCnt + 1
		Else
			DebugLog("Lightmap doesn't fit into the maxmapsize, increase the lumelsize or increase the maxmapsize")
			Exit
		EndIf
	Next
	
	TextureCoords(Tex, 1)
	
	SetBuffer(BackBuffer())

	For LMNode.LMImgNode = Each LMImgNode
		Delete LMNode
	Next
	
	Return Tex
End Function

;
;  Find of the minimum texture size up to maxmapsize% that will fit all the lightmap images
;
Function LMPacker_FitTexSize%(maxmapsize%)
	lmapsize = LM_MINTEXSIZE
	
	While lmapsize <= maxmapsize
		LMRoot.LMImgNode = New LMImgNode
		LMRoot\X1 = 0 : LMRoot\Y1 = 0
		LMRoot\X2 = lmapsize : LMRoot\Y2 = lmapsize
		LMRoot\Surf = Null
		
		bFit = True
		
		For SLMSurf.LMSortedSurface = Each LMSortedSurface
			Img.LMImgNode = LMPacker_Insert(LMRoot, SLMSurf\Surf)
			
			If Img = Null
				bFit = False
				Exit
			EndIf
		Next
		
		For LMNode.LMImgNode = Each LMImgNode
			Delete LMNode
		Next
		
		If bFit
			Return lmapsize
		EndIf
		
		lmapsize = lmapsize * 2
	Wend
	
	Return maxmapsize
End Function


;
;  Recursive function to pack the lightmaps
;
Function LMPacker_Insert.LMImgNode(Node.LMImgNode, LMSurf.LMSurface)
	; We are not in a leaf
	If (Node\Child[0] <> Null) And (Node\Child[1] <> Null)
		
		; Try first child
		NewNode.LMImgNode = LMPacker_Insert(Node\Child[0], LMSurf)
		If NewNode <> Null Return NewNode
		
		; No room, use the second
		Return LMPacker_Insert(Node\Child[1], LMSurf)
	Else
		; Already have a lightmap here
		If Node\Surf <> Null 
		
			; If the lightmap is the same image use it
			If LMImageAlike(Node\Surf\Image, LMSurf\Image)
				Node\Surf = LMSurf
				Return Node
			Else
				Return Null
			EndIf
		EndIf 
		
		IW% = ImageWidth(LMSurf\Image)
		IH% = ImageHeight(LMSurf\Image)
		
		NW% = Node\X2 - Node\X1
		NH% = Node\Y2 - Node\Y1
		
		; Check if image doesn't fit this node
		If (IW > NW) Or (IH > NH)
			Return Null
		EndIf

		; If it fits perfectly
		If (IW = NW) And (IH = NH)
			Node\Surf = LMSurf
			Return Node
		EndIf
	
		; We need to spit the node
		Node\Child[0] = New LMImgNode
		Node\Child[1] = New LMImgNode
		
		DW% = NW - IW
		DH% = NH - IH
		
		; Choose the best axis to split
		If DW > DH
			Node\Child[0]\X1 = Node\X1
			Node\Child[0]\Y1 = Node\Y1
			Node\Child[0]\X2 = Node\X1 + IW
			Node\Child[0]\Y2 = Node\Y2

			Node\Child[1]\X1 = Node\X1 + IW
			Node\Child[1]\Y1 = Node\Y1
			Node\Child[1]\X2 = Node\X2
			Node\Child[1]\Y2 = Node\Y2
		Else
			Node\Child[0]\X1 = Node\X1
			Node\Child[0]\Y1 = Node\Y1
			Node\Child[0]\X2 = Node\X2
			Node\Child[0]\Y2 = Node\Y1 + IH

			Node\Child[1]\X1 = Node\X1
			Node\Child[1]\Y1 = Node\Y1 + IH
			Node\Child[1]\X2 = Node\X2
			Node\Child[1]\Y2 = Node\Y2
		EndIf
		
		Return LMPacker_Insert(Node\Child[0], LMSurf)
	EndIf
End Function

Function LMImageAlike(img1, img2)
	;Check if imagess are congruent
	width1 = ImageWidth(img1)
	width2 = ImageWidth(img2)
	If width1 <> width2 Then Return False
	
	height1 = ImageHeight(img1)
	height2 = ImageHeight(img2)
	If height1 <> height2 Then Return 0
	
	LockBuffer(ImageBuffer(img1))
	LockBuffer(ImageBuffer(img2))
	
	For y = 0 To height1-1
		For x = 0 To width1-1
			rgb1 = ReadPixelFast(x, y, ImageBuffer(img1))
			rgb2 = ReadPixelFast(x, y, ImageBuffer(img2))
			If rgb1 <> rgb2
				UnlockBuffer(ImageBuffer(img1))
				UnlockBuffer(ImageBuffer(img2))
				Return 0
			EndIf
		Next
	Next
	UnlockBuffer(ImageBuffer(img1))
	UnlockBuffer(ImageBuffer(img2))

	Return True
End Function


;
;	Setup the surface
;
Function LMSetupSurface.LMSurface(LMSurf.LMSurface, lumelsize#)

	; Get the averaged normal
	NX# = 0 : NY# = 0 : NZ# = 0
	For i = 0 To LMSurf\NTris-1
		GetTriangleNormal(LMSurf\Tris[i]\X[0], LMSurf\Tris[i]\Y[0], LMSurf\Tris[i]\Z[0], LMSurf\Tris[i]\X[1], LMSurf\Tris[i]\Y[1], LMSurf\Tris[i]\Z[1], LMSurf\Tris[i]\X[2], LMSurf\Tris[i]\Y[2], LMSurf\Tris[i]\Z[2])
	
		NX = NX + TriangleNormalX()
		NY = NY + TriangleNormalY()
		NZ = NZ + TriangleNormalZ()
	Next
	
	LMSurf\NX = NX / Float(LMSurf\NTris)
	LMSurf\NY = NY / Float(LMSurf\NTris)
	LMSurf\NZ = NZ / Float(LMSurf\NTris)
	
	; Find out the best plane to map on (which have the largest normal)
	NX# = Abs(LMSurf\NX) :	NY# = Abs(LMSurf\NY) :	NZ# = Abs(LMSurf\NZ)
	
	If (NZ > NX) And (NZ > NY)
		LMSurf\Plane = LMPLANE_XY
	Else If (NY > NX) And (NY > NZ)
		LMSurf\Plane = LMPLANE_XZ
	Else
		LMSurf\Plane = LMPLANE_YZ
	EndIf

	Select LMSurf\Plane
		Case LMPLANE_XY
			For i = 0 To LMSurf\NTris-1
				For j = 0 To LM_VERTS
					LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\X[j]
					LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\Y[j]
				Next
			Next
	
		Case LMPLANE_XZ
			For i = 0 To LMSurf\NTris-1
				For j = 0 To LM_VERTS
					LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\X[j]
					LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\Z[j]
				Next
			Next
	
		Case LMPLANE_YZ
			For i = 0 To LMSurf\NTris-1
				For j = 0 To LM_VERTS
					LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\Y[j]
					LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\Z[j]
				Next
			Next
	End Select 
	
	; Measure the UV bound box
	LMSurf\UMin = LMSurf\Tris[0]\U[0] : LMSurf\UMax = LMSurf\Tris[0]\U[0]
	LMSurf\VMin = LMSurf\Tris[0]\V[0] : LMSurf\VMax = LMSurf\Tris[0]\V[0]
	
	For i = 0 To LMSurf\NTris-1
		For j = 0 To LM_VERTS
			If LMSurf\Tris[i]\U[j] < LMSurf\UMin Then LMSurf\UMin = LMSurf\Tris[i]\U[j]
			If LMSurf\Tris[i]\U[j] > LMSurf\UMax Then LMSurf\UMax = LMSurf\Tris[i]\U[j]
	
			If LMSurf\Tris[i]\V[j] < LMSurf\VMin Then LMSurf\VMin = LMSurf\Tris[i]\V[j]
			If LMSurf\Tris[i]\V[j] > LMSurf\VMax Then LMSurf\VMax = LMSurf\Tris[i]\V[j]
		Next
	Next
	
	; Reduce black borders
	DT# = lumelsize
	LMSurf\UMax = LMSurf\UMax + DT
	LMSurf\VMax = LMSurf\VMax + DT
	LMSurf\UMin = LMSurf\UMin - DT
	LMSurf\VMin = LMSurf\VMin - DT
	
	; Bound Box size
	LMSurf\UDelta = LMSurf\UMax - LMSurf\UMin
	LMSurf\VDelta = LMSurf\VMax - LMSurf\VMin
	
	; Normalize the UV's, making it range from 0.0 to 1.0
	For i = 0 To LMSurf\NTris-1
		For j = 0 To LM_VERTS
			; Translate it to the origin
			LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\U[j] - LMSurf\UMin
			LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\V[j] - LMSurf\VMin
		
			; Normalize
			LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\U[j] / LMSurf\UDelta
			LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\V[j] / LMSurf\VDelta
		Next
	Next
	
	;
	; Calculate the UV space to world space equations
	;
	
	; Distance of the plane
	Dist# = -(LMSurf\NX * LMSurf\Tris[0]\X[0] + LMSurf\NY * LMSurf\Tris[0]\Y[0] + LMSurf\NZ * LMSurf\Tris[0]\Z[0])
	
	Local UVX#, UVY#, UVZ#
	Local V1X#, V1Y#, V1Z#
	Local V2X#, V2Y#, V2Z#
	
	; Messy stuff based on the plane equation:  Ax + By + Cz + D = 0
	Select LMSurf\Plane

		Case LMPLANE_XY
		Z# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NY * LMSurf\VMin + Dist) / LMSurf\NZ
		UVX# = LMSurf\UMin : UVY# = LMSurf\VMin : UVZ# = Z
		
		Z# = -(LMSurf\NX * LMSurf\UMax + LMSurf\NY * LMSurf\VMin + Dist) / LMSurf\NZ
		V1X# = LMSurf\UMax : V1Y# = LMSurf\VMin : V1Z# = Z
	
		Z# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NY * LMSurf\VMax + Dist) / LMSurf\NZ
		V2X# = LMSurf\UMin : V2Y# = LMSurf\VMax : V2Z# = Z

		Case LMPLANE_XZ
		Y# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NY
		UVX# = LMSurf\UMin : UVY# = Y : UVZ# = LMSurf\VMin
		
		Y# = -(LMSurf\NX * LMSurf\UMax + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NY
		V1X# = LMSurf\UMax : V1Y# = Y : V1Z# = LMSurf\VMin
	
		Y# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NZ * LMSurf\VMax + Dist) / LMSurf\NY
		V2X# = LMSurf\UMin : V2Y# = Y : V2Z# = LMSurf\VMax

	
		Case LMPLANE_YZ
		X# = -(LMSurf\NY * LMSurf\UMin + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NX
		UVX# = X : UVY# = LMSurf\UMin : UVZ# = LMSurf\VMin
		
		X# = -(LMSurf\NY * LMSurf\UMax + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NX
		V1X# = X : V1Y# = LMSurf\UMax : V1Z# = LMSurf\VMin
	
		X# = -(LMSurf\NY * LMSurf\UMin + LMSurf\NZ * LMSurf\VMax + Dist) / LMSurf\NX
		V2X# = X : V2Y# = LMSurf\UMin : V2Z# = LMSurf\VMax
	
	End Select

	LMSurf\UEdgeX = V1X - UVX : LMSurf\UEdgeY = V1Y - UVY : LMSurf\UEdgeZ = V1Z - UVZ
	LMSurf\VEdgeX = V2X - UVX : LMSurf\VEdgeY = V2Y - UVY : LMSurf\VEdgeZ = V2Z - UVZ
	
	LMSurf\OriginX = UVX# : LMSurf\OriginY = UVY# : LMSurf\OriginZ = UVZ#
	Return LMSurf
End Function

;
;	Create the lightmap texture
;
Function LMLightSurface(LMSurf.LMSurface, lumelsize#)
	; Create image size based on the lumel density
	LMSizeX% = (LMSurf\UDelta / lumelsize)
	LMSizeY% = (LMSurf\VDelta / lumelsize)
	
	; Mininum texture size
	If LMSizeX < LM_MINTEXSIZE Then LMSizeX = LM_MINTEXSIZE
	If LMSizeY < LM_MINTEXSIZE Then LMSizeY = LM_MINTEXSIZE

	LMSurf\Image = CreateImage(LMSizeX, LMSizeY)
 	LMSurf\ImageSize = LMSizeX * LMSizeY
	
	ImgBuf = ImageBuffer(LMSurf\Image)
	SetBuffer(ImgBuf)
	
	; Set the ambient light
	ClsColor(g_LMParams\AmbR, g_LMParams\AmbG, g_LMParams\AmbB)
	Cls()
	ClsColor(0, 0, 0)
	
	LockBuffer(ImgBuf)
	
	LightPivot = CreatePivot()

	LumelPivot = CreatePivot()
	EntityPickMode(LumelPivot, 1)
	EntityRadius(LumelPivot, 0.625)	; Found by trial and error
	
	For Light.LMLight = Each LMLight
	
		PositionEntity(LightPivot, Light\X, Light\Y, Light\Z)
		
		For y% = 0 To LMSizeY-1
			For x% = 0 To LMSizeX-1
				
				; Find the UV
				u# = Float(x) / Float(LMSizeX)
				v# = Float(y) / Float(LMSizeY)
				
				; Transform to world coordinates
				N_UEdgeX# = LMSurf\UEdgeX * u#  :  N_UEdgeY# = LMSurf\UEdgeY * u#  :  N_UEdgeZ# = LMSurf\UEdgeZ * u#
				N_VEdgeX# = LMSurf\VEdgeX * v#  :  N_VEdgeY# = LMSurf\VEdgeY * v#  :  N_VEdgeZ# = LMSurf\VEdgeZ * v#
				
				LumX# = (LMSurf\OriginX + N_UEdgeX + N_VEdgeX)
				LumY# = (LMSurf\OriginY + N_UEdgeY + N_VEdgeY)
				LumZ# = (LMSurf\OriginZ + N_UEdgeZ + N_VEdgeZ)
				
				PositionEntity(LumelPivot, LumX, LumY, LumZ)
				Dist# = EntityDistance(LightPivot, LumelPivot)
				
				Select Light\TypeLight
				
					Case SUN_LIGHT

						; Normal vector between lumel and light
						NX# = (-Light\X);/Dist			;Sun_light is not affected by distance, maybe i can add a directionnal light that is influenced by distance...
						NY# = (-Light\Y);/Dist			;Directionnal_light type maybe :)
						NZ# = (-Light\Z);/Dist
	
						; Dot product to find the cosine angle between the surface normal and incident light normal
						CosAngle# = (NX * LMSurf\NX) + (NY * LMSurf\NY) + (NZ * LMSurf\NZ)
						
						; Poly face front of the light
						If CosAngle > 0
						
							NHits = 0

							; Center pick
							dx# = - Light\X
							dy# = - Light\Y
							dz# = - Light\Z
							If LinePick(Light\X+LumX, Light\Y+LumY, Light\Z+LumZ, dx*FLT_MAX, dy*FLT_MAX, dz*FLT_MAX, 0) = LumelPivot
								NHits = NHits + 1
							EndIf
						
							If NHits > 0
								;
								; Add the incident light the pixel
								;
								; Lambert + Attenuation + Shadow 
								Intensity# = (Light\Bright * CosAngle)
								
								If Intensity < 0.0 Then Intensity = 0.0
								If Intensity > 1.0 Then Intensity = 1.0
								
								ARGB = ReadPixelFast(x, y) And $FFFFFF
								R = (ARGB Shr 16 And %11111111)
								G = (ARGB Shr 8 And %11111111)
								B = (ARGB And %11111111)
								
								R = R + (Light\R * Intensity)
								G = G + (Light\G * Intensity)
								B = B + (Light\B * Intensity)
								
								If R > 255 Then R = 255
								If G > 255 Then G = 255
								If B > 255 Then B = 255
								
								RGB = B Or (G Shl 8) Or (R Shl 16)
								WritePixelFast(x, y, RGB)
	
							EndIf	; Visible
	
						EndIf ; CosAngle > 0
					
					Case POINT_LIGHT								
						; If this light can light this lumel		
						If (Dist <= Light\Range) And (Dist > 0)
							
							; Normal vector between lumel and light
							NX# = (LumX-Light\X) / Dist
							NY# = (LumY-Light\Y) / Dist
							NZ# = (LumZ-Light\Z) / Dist
		
							; Dot product to find the cosine angle between the surface normal and incident light normal
							CosAngle# = (NX * LMSurf\NX) + (NY * LMSurf\NY) + (NZ * LMSurf\NZ)
							
							; Poly face front of the light
							If CosAngle > 0
								LMLightProcess(x, y, Light, LumX, LumY, LumZ, Dist, CosAngle, LumelPivot)
							EndIf 
							
						EndIf ; Dist < Light\Range
					End Select
			Next ; x
		Next ; y

	Next ;Light
	
	UnlockBuffer(ImgBuf)

	SetBuffer(BackBuffer())
	
	FreeEntity(LightPivot)
	FreeEntity(LumelPivot)
End Function


Function LMLightProcess(x%, y%, Light.LMLight, LumX#, LumY#, LumZ#, Dist#, CosAngle#, LumelPivot)
	NHits = 0
	
	; Center pick
	dx# = LumX - Light\X
	dy# = LumY - Light\Y
	dz# = LumZ - Light\Z
	If LinePick(Light\X, Light\Y, Light\Z, dx, dy, dz, 0) = LumelPivot
		NHits = NHits + 1
	EndIf

	If NHits > 0
		;
		; Add the incident light the pixel
		;

		; Measure attenuation
		Att# = 1 / (Light\Att[0] + (Light\Att[1] * Dist) + (Light\Att[2] * Dist * Dist))
				
		; Lambert + attenuation
		Intensity# = (Light\Bright * CosAngle) * Att
		
		If Intensity < 0.0 Then Intensity = 0.0
		If Intensity > 1.0 Then Intensity = 1.0
		
		ARGB = ReadPixelFast(x, y) And $FFFFFF
		R = (ARGB Shr 16 And %11111111)
		G = (ARGB Shr 8 And %11111111)
		B = (ARGB And %11111111)
		
		R = R + (Light\R * Intensity)
		G = G + (Light\G * Intensity)
		B = B + (Light\B * Intensity)
		
		If R > 255 Then R = 255
		If G > 255 Then G = 255
		If B > 255 Then B = 255
		
		RGB = B Or (G Shl 8) Or (R Shl 16)
		WritePixelFast(x, y, RGB)

	EndIf	; Visible
End Function

;
;	Blur an image using radius
;
Function LMBlurImage(Image, radius = 1)
	TmpImg = CopyImage(Image)
	
	TmpBuf = ImageBuffer(TmpImg)
	ImgBuf = ImageBuffer(Image)
	
	LockBuffer(ImgBuf)
	LockBuffer(TmpBuf)
	
	W% = ImageWidth(Image)
	H% = ImageHeight(Image)

	; Go thru all the pixels
	For y% = 0 To H-1
		For x% = 0 To W-1
		
			; Measure the box to get the pixel samples from
			ix1 = x - radius
			iy1 = y - radius
			ix2 = x + radius
			iy2 = y + radius
			
			; Prevent it going out of bound
			If ix1 < 0 Then ix1 = 0
			If iy1 < 0 Then iy1 = 0
			If ix2 > W-1 Then ix2 = W-1
			If iy2 > H-1 Then iy2 = H-1
			
			r = 0 : g = 0 : b = 0
			num = 0
			
			; Run thru all the sampled box
			For y2% = iy1 To iy2
				For x2% = ix1 To ix2
					
					; Sum the sampled pixel 
					argb = ReadPixelFast(x2, y2, TmpBuf) And $FFFFFF
					ar = (argb Shr 16 And %11111111)
					ag = (argb Shr 8 And %11111111)
					ab = (argb And %11111111)
					
					r = r + ar
					g = g + ag
					b = b + ab
					
					num = num + 1
				Next	
			Next
			
			; Get the medium value
			r = r / num
			g = g / num
			b = b / num
			
			; Clamp
			If r > 255 Then r = 255
			If g > 255 Then g = 255
			If b > 255 Then b = 255

			rgb = b Or (g Shl 8) Or (r Shl 16)
			WritePixelFast(x, y, rgb, ImgBuf)

		Next
	Next
	
	UnlockBuffer(TmpBuf)
	UnlockBuffer(ImgBuf)
	
	FreeImage(TmpBuf)
End Function


;
; Helper functions
;

Global g_TriNormalX#, g_TriNormalY#, g_TriNormalZ#

Function GetTriangleNormal(x1#, y1#, z1#, x2#, y2#, z2#, x3#, y3#, z3#)
  	ux# = x1# - x2#
   	uy# = y1# - y2#
  	uz# = z1# - z2#
    vx# = x3# - x2#
    vy# = y3# - y2#
   	vz# = z3# - z2#

	nx# = (uy# * vz#) - (vy# * uz#)
	ny# = (uz# * vx#) - (vz# * ux#)
	nz# = (ux# * vy#) - (vx# * uy#)

	; Normalize it
	NormLen# = Sqr((nx*nx) + (ny*ny) + (nz*nz))
	If NormLen > 0
		nx = nx/NormLen : ny = ny/NormLen: nz = nz/NormLen
	Else
		nx = 0 : ny = 0 : nz = 1
	EndIf

	g_TriNormalX = nx
	g_TriNormalY = ny
	g_TriNormalZ = nz
End Function 

Function TriangleNormalX#()
	Return g_TriNormalX
End Function

Function TriangleNormalY#()
	Return g_TriNormalY
End Function

Function TriangleNormalZ#()
	Return g_TriNormalZ
End Function



;
;	Starfox's functions (unmodified)
;

Type tris
	Field x#[3],y#[3],z#[3]
	Field surf,index,mesh
	Field ver[3],u#[3],v#[3]
	Field tex,size#
End Type

Function CreateLUVs(mesh,filename$,coordset=1)
	file = WriteFile(filename)
	For surfcount = 1 To CountSurfaces(mesh)
		surf = GetSurface(mesh,surfcount)
		For vercount = 0 To CountVertices(surf)-1
			WriteFloat(file,VertexU(surf,vercount,coordset))
			WriteFloat(file,VertexV(surf,vercount,coordset))
		Next
	Next
	CloseFile file
End Function

Function LoadLUVs(mesh,filename$,coordset=1)
	file = ReadFile(filename)
	For surfcount = 1 To CountSurfaces(mesh)
		surf = GetSurface(mesh,surfcount)
		For vercount = 0 To CountVertices(surf)-1
			u# = ReadFloat(file)
			v# = ReadFloat(file)
			VertexTexCoords surf,vercount,u,v,0,coordset
		Next
	Next
	CloseFile file
End Function

Function Unweld(mesh)
	;Unweld a mesh, retaining all of its textures coords and textures
	For surfcount = 1 To CountSurfaces(mesh)
		surf = GetSurface(mesh,surfcount)
		For tricount = 0 To CountTriangles(surf)-1
		t.tris = New tris
		t\surf = surf : t\mesh = mesh
		t\index = tricount
		in = TriangleVertex(t\surf,t\index,0)
		t\x[1] = VertexX(surf,in) : t\y[1] = VertexY(surf,in)
		t\z[1] = VertexZ(surf,in) : t\ver[1] = in
		t\u[1] = VertexU(surf,in) : t\v[1] = VertexV(surf,in)
		in = TriangleVertex(t\surf,t\index,1)
		t\x[2] = VertexX(surf,in) : t\y[2] = VertexY(surf,in)
		t\z[2] = VertexZ(surf,in) : t\ver[2] = in
		t\u[2] = VertexU(surf,in) : t\v[2] = VertexV(surf,in)
		in = TriangleVertex(t\surf,t\index,2)
		t\x[3] = VertexX(surf,in) : t\y[3] = VertexY(surf,in)
		t\z[3] = VertexZ(surf,in) : t\ver[3] = in
		t\u[3] = VertexU(surf,in) : t\v[3] = VertexV(surf,in)
		Next
		ClearSurface(surf,True,True)
		For t.tris = Each tris
		t\ver[1] = AddVertex(t\surf,t\x[1],t\y[1],t\z[1],t\u[1],t\v[1])
		t\ver[2] = AddVertex(t\surf,t\x[2],t\y[2],t\z[2],t\u[2],t\v[2])
		t\ver[3] = AddVertex(t\surf,t\x[3],t\y[3],t\z[3],t\u[3],t\v[3])
		AddTriangle(t\surf,t\ver[1],t\ver[2],t\ver[3])
		Delete t
		Next
	Next
	UpdateNormals mesh
	Return mesh
End Function



;
;	Example function
;
;	Hold 2th mouse button do turn the cam
;	Use the arrrows to move
; 	Click on a object to lightmap it
;	Press F2 to load the saved lightmaps

Function LMExample()
	Graphics3D(640, 480)
	SetBuffer(BackBuffer())
	
	; Create some stuff in the world
	camera = CreateCamera()
	PositionEntity(camera, 17, 18, 18)

	terr=CreateTerrain(512) : PositionEntity terr, -512,0,-512: ScaleEntity terr, 4, 1, 4 : EntityColor terr, 128, 64 , 0
	
	sun=CreateSphere(): PositionEntity sun, -50,60,0
	
	cube1 = CreateCube() : EntityColor cube1, 64,20,20
	PositionMesh(cube1, -2, 1.0, 0)
	ScaleEntity(cube1, 20, 5, 20)
	EntityPickMode(cube1, 2)
	NameEntity(cube1, "cube1")
	
	PointEntity(camera, cube1) ; Look at the cube
	
	cube2 = CreateCube() : EntityColor cube2, 20,64,20
	PositionMesh(cube2, 0, 1.0, 0) 
	ScaleEntity(cube2, 2, 2, 2)
	EntityPickMode(cube2, 2)
	NameEntity(cube2, "cube2")
	
	AmbientLight(50, 50, 50)
	light = CreateLight(1)
	RotateEntity(light, 45, 30, 0)
	
	; Timing control
	OldTime% = MilliSecs()

	While Not KeyHit(1)
		
		; Time elapsed between last frame
		Time% = MilliSecs()
		DeltaTime# = Float(Time - OldTime) / 1000   ; in seconds
		OldTime% = Time
	
		; Camera movement
		CamSpd# = 10 * DeltaTime
		MoveEntity(camera, Float(KeyDown(205) - KeyDown(203)) * CamSpd, 0, Float(KeyDown(200) - KeyDown(208)) * CamSpd)

		If MouseDown(2)
			TurnSpeed# = 0.8
			TurnEntity(camera, Float(MouseYSpeed())  * TurnSpeed#, 0, 0, False)
			TurnEntity(camera, 0, -Float(MouseXSpeed()) * TurnSpeed#, 0, True)
		Else
			MouseXSpeed() : MouseYSpeed()
		EndIf

		; Lightmap the picked entity
		If MouseHit(1)
			ent = CameraPick(camera, MouseX(), MouseY())
			If ent
				
				BeginLightMap(40, 40, 40)
				
				CreateLMLight( -50, 60, 0, 255, 255, 10, 1, 1,0,1,0,SUN_LIGHT)
				CreateLMLight(  8, 3,  3, 255, 255, 219, 40, 2,0,1,0,POINT_LIGHT)

				EntityPickMode(terr, 2)
				tex=lightmapterrain(terr)
				ApplyLightmap(terr, tex)
				EntityPickMode(terr, 0)
			
				tex = LightMapMesh(ent, 0.5)
				If tex
					SaveLightMap(ent, tex, EntityName(ent) + "_lm.bmp", EntityName(ent) + ".luv")
					ApplyLightMap(ent, tex)
				EndIf
				
				EndLightMap()
				
			EndIf
		EndIf		
		
		If KeyHit(60) ;  F2 key
			ent = CameraPick(camera, MouseX(), MouseY())
			If ent
				LoadLightMap(ent, EntityName(ent) + "_lm.bmp", EntityName(ent) + ".luv")
			EndIf
		EndIf

		UpdateWorld()

		RenderWorld()
		Flip()	
		
	Wend
	
	EndGraphics()
End Function

Comments

None.

Code Archives Forum