Removing leftover DebugLog

Removing leftover DebugLog

Strict

Rem
	bbdoc: Primitive mesh loader for MaxB3D.
End Rem
Module MaxB3D.Primitives
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core

Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateCube:TMesh(parent:TEntity=Null)
	Return _currentworld.AddMesh("//cube",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateCone:TMesh(segments=8,solid=True,parent:TEntity=Null)
	Return _currentworld.AddMesh("//cone("+segments+","+solid+")",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateCylinder:TMesh(segments=8,solid=True,parent:TEntity=Null)
	Return _currentworld.AddMesh("//cylinder("+segments+","+solid+")",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateSphere:TMesh(segments=8,parent:TEntity=Null)
	Return _currentworld.AddMesh("//sphere("+segments+")",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateTorus:TMesh(radius#,width#,segments,sides,parent:TEntity=Null)
	Return _currentworld.AddMesh("//torus("+radius+","+width+","+segments+","+sides+")",parent)
End Function

Type TMeshLoaderPrimitives Extends TMeshLoader
	Method Run(mesh:TMesh,stream:TStream,url:Object)
		Local str$=String(url)
		Local params$[]=str[str.Find("(")+1..str.FindLast(")")].Split(",")

		Local name$ = str[str.Find("//")+2..]
		If str.Find("(") > -1 name = name[..name.Find("(")]

		Select name
		Case "sphere"
			Local segments=Int(params[0])
			If segments<2 Or segments>100 Then Return Null

			'Vertex count
			'((segments*2)*4) '2
			'((segments*2)*6)+((segments*2)*((segments-2)*4)) '>2

			'Triangle count
			'(segments*2)*2
			'(segments*2)*2+(segments*2)*(segments-2)*2

			Local vertexcount=4*segments*(3+((segments-2)*2))
			Local trianglecount=segments*4*(1+(segments-2))
			If segments=2
				vertexcount=segments*8
				trianglecount=segments*4
			EndIf

			Local surface:TSurface=mesh.AddSurface(Null,vertexcount,trianglecount)

			Local div#=Float(360.0/(segments*2))
			Local height#=1.0
			Local upos#=1.0
			Local udiv#=Float(1.0/(segments*2))
			Local vdiv#=Float(1.0/segments)
			Local RotAngle#=90

			If segments=2
				For Local i=1 To (segments*2)
					Local np=(i-1)*4+0,sp=np+1
					surface.SetCoords(np,0.0,height,0.0);surface.SetTexCoords(np,upos#-(udiv#/2.0),0) 'northpole
					surface.SetCoords(sp,0.0,-height,0.0);surface.SetTexCoords(sp,upos#-(udiv#/2.0),1) 'southpole
					Local XPos#=-Cos(RotAngle#)
					Local ZPos#=Sin(RotAngle#)
					Local v0=sp+1
					surface.SetCoords(v0,XPos#,0,ZPos#)
					surface.SetTexCoords(v0,upos#,0.5)
					RotAngle#=RotAngle#+div#
					If RotAngle#>=360.0 Then RotAngle#=RotAngle#-360.0
					XPos#=-Cos(RotAngle#)
					ZPos#=Sin(RotAngle#)
					upos#=upos#-udiv#
					Local v1=v0+1
					surface.SetCoords(v1,XPos#,0,ZPos#)
					surface.SetTexCoords(v1,upos#,0.5)

					surface.SetTriangle((i-1)*2+0,np,v0,v1)
					surface.SetTriangle((i-1)*2+1,v1,v0,sp)
				Next
			Else
				For Local i=1 To (segments*2)
					Local np=(i-1)*6+0,sp=np+1
					surface.SetCoords(np,0.0,height,0.0)
					surface.SetTexCoords(np,upos#-(udiv#/2.0),0)'northpole
					surface.SetCoords(sp,0.0,-height,0.0)
					surface.SetTexCoords(sp,upos#-(udiv#/2.0),1)'southpole

					Local YPos#=Cos(div#)

					Local XPos#=-Cos(RotAngle#)*(Sin(div#))
					Local ZPos#=Sin(RotAngle#)*(Sin(div#))

					Local v0t=sp+1
					surface.SetCoords(v0t,XPos#,YPos#,ZPos#)
					surface.SetTexCoords(v0t,upos#,vdiv#)
					Local v0b=v0t+1
					surface.SetCoords(v0b,XPos#,-YPos#,ZPos#)
					surface.SetTexCoords(v0b,upos#,1-vdiv#)

					RotAngle#=RotAngle#+div#

					XPos#=-Cos(RotAngle#)*(Sin(div#))
					ZPos#=Sin(RotAngle#)*(Sin(div#))

					upos#=upos#-udiv#

					Local v1t=v0b+1
					surface.SetCoords(v1t,XPos#,YPos#,ZPos#)
					surface.SetTexCoords(v1t,upos#,vdiv#)
					Local v1b=v1t+1
					surface.SetCoords(v1b,XPos#,-YPos#,ZPos#)
					surface.SetTexCoords(v1b,upos#,1-vdiv#)

					surface.SetTriangle((i-1)*2+0,np,v0t,v1t)
					surface.SetTriangle((i-1)*2+1,v1b,v0b,sp)
				Next

				upos#=1.0
				RotAngle#=90
				For Local i=1 To (segments*2)

					Local mult#=1
					Local YPos#=Cos(div#*(mult#))
					Local YPos2#=Cos(div#*(mult#+1.0))
					Local Thisvdiv#=vdiv#
					For Local j=1 To (segments-2)
						Local XPos#=-Cos(RotAngle#)*(Sin(div#*(mult#)))
						Local ZPos#=Sin(RotAngle#)*(Sin(div#*(mult#)))

						Local XPos2#=-Cos(RotAngle#)*(Sin(div#*(mult#+1.0)))
						Local ZPos2#=Sin(RotAngle#)*(Sin(div#*(mult#+1.0)))

						Local v0t=(segments*2)*6+((i-1)*(segments-2)*4)+((j-1)*4),v0b=v0t+1
						surface.SetCoords(v0t,XPos#,YPos#,ZPos#)
						surface.SetTexCoords(v0t,upos#,Thisvdiv#,0.0)
						surface.SetCoords(v0b,XPos2#,YPos2#,ZPos2#)
						surface.SetTexCoords(v0b,upos#,Thisvdiv#+vdiv#,0.0)

						Local tempRotAngle#=RotAngle#+div#

						XPos#=-Cos(tempRotAngle#)*(Sin(div#*(mult#)))
						ZPos#=Sin(tempRotAngle#)*(Sin(div#*(mult#)))

						XPos2#=-Cos(tempRotAngle#)*(Sin(div#*(mult#+1.0)))
						ZPos2#=Sin(tempRotAngle#)*(Sin(div#*(mult#+1.0)))

						Local temp_upos#=upos-udiv

						Local v1t=v0b+1,v1b=v1t+1
						surface.SetCoords(v1t,XPos,YPos,ZPos)
						surface.SetTexCoords(v1t,temp_upos,Thisvdiv,0.0)
						surface.SetCoords(v1b,XPos2,YPos2,ZPos2)
						surface.SetTexCoords(v1b,temp_upos,Thisvdiv+vdiv,0.0)

						surface.SetTriangle((segments*2)*2+((i-1)*(segments-2)*2)+((j-1)*2)+0,v1t,v0t,v0b)
						surface.SetTriangle((segments*2)*2+((i-1)*(segments-2)*2)+((j-1)*2)+1,v1b,v1t,v0b)

						Thisvdiv#=Thisvdiv#+vdiv#
						mult#=mult#+1
						YPos#=Cos(div#*(mult#))
						YPos2#=Cos(div#*(mult#+1.0))

					Next
					upos#=upos#-udiv#
					RotAngle#=RotAngle#+div#
				Next
			EndIf

			mesh.UpdateNormals()
			'mesh.ForEachSurfaceDo FlipNormals
			Return True
		Case "cylinder"
			Local ringsegments=0

			Local segments=Int(params[0]),solid=Int(params[1])

			Local tr,tl,br,bl
			Local ts0,ts1,newts
			Local bs0,bs1,newbs
			If segments<3 Or segments>100 Then Return Null
			If ringsegments<0 Or ringsegments>100 Then Return Null

			Local surface:TSurface=mesh.AddSurface(Null,1000,1000)
			Local solidsurface:TSurface
			If solid=True
				solidsurface=mesh.AddSurface(Null,1000,1000)
			EndIf
			Local div#=Float(360.0/(segments))

			Local height#=1.0
			Local ringSegmentHeight#=(height#*2.0)/(ringsegments+1)
			Local upos#=1.0
			Local udiv#=Float(1.0/(segments))
			Local vpos#=1.0
			Local vdiv#=Float(1.0/(ringsegments+1))

			Local SideRotAngle#=90

			Local tRing[segments+1]
			Local bRing[segments+1]

			If solid=True
				Local xpos#=-Cos(SideRotAngle#)
				Local zpos#=Sin(SideRotAngle#)

				ts0=solidsurface.AddVertex(xpos,height,zpos,xpos/2.0+0.5,zpos/2.0+0.5)
				bs0=solidsurface.AddVertex(xpos,-height,zpos,xpos/2.0+0.5,zpos/2.0+0.5)

				solidsurface.SetTexCoords(ts0,xpos/2.0+0.5,zpos/2.0+0.5,0.0)
				solidsurface.SetTexCoords(bs0,xpos/2.0+0.5,zpos/2.0+0.5,0.0)

				SideRotAngle=SideRotAngle+div

				xpos#=-Cos(SideRotAngle#)
				zpos#=Sin(SideRotAngle#)

				ts1=solidsurface.AddVertex(xpos#,height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)
				bs1=solidsurface.AddVertex(xpos#,-height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)

				solidsurface.SetTexCoords(ts1,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)
				solidsurface.SetTexCoords(bs1,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)

				For Local i=1 To (segments-2)
					SideRotAngle#=SideRotAngle#+div#

					xpos#=-Cos(SideRotAngle#)
					zpos#=Sin(SideRotAngle#)

					newts=solidsurface.AddVertex(xpos#,height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)
					newbs=solidsurface.AddVertex(xpos#,-height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)

					solidsurface.SetTexCoords(newts,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)
					solidsurface.SetTexCoords(newbs,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)

					solidsurface.AddTriangle(ts0,ts1,newts)
					solidsurface.AddTriangle(newbs,bs1,bs0)

					If i<(segments-2)
						ts1=newts
						bs1=newbs
					EndIf
				Next
			EndIf

			Local thisHeight#=height#

			SideRotAngle#=90
			Local xpos#=-Cos(SideRotAngle#)
			Local zpos#=Sin(SideRotAngle#)
			Local thisUPos#=upos#
			Local thisVPos#=0
			tRing[0]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
			surface.SetTexCoords(tRing[0],thisUPos#,thisVPos#,0.0)
			For Local i=0 To (segments-1)
				SideRotAngle#=SideRotAngle#+div#
				xpos#=-Cos(SideRotAngle#)
				zpos#=Sin(SideRotAngle#)
				thisUPos#=thisUPos#-udiv#
				tRing[i+1]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
				surface.SetTexCoords(tRing[i+1],thisUPos#,thisVPos#,0.0)
			Next

			For Local ring=0 To ringsegments
				Local thisHeight=thisHeight-ringSegmentHeight#

				SideRotAngle#=90
				xpos#=-Cos(SideRotAngle#)
				zpos#=Sin(SideRotAngle#)
				thisUPos#=upos#
				thisVPos#=thisVPos#+vdiv#
				bRing[0]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
				surface.SetTexCoords(bRing[0],thisUPos#,thisVPos#,0.0)
				For Local i=0 To (segments-1)
					SideRotAngle#=SideRotAngle#+div#
					xpos#=-Cos(SideRotAngle#)
					zpos#=Sin(SideRotAngle#)
					thisUPos#=thisUPos#-udiv#
					bRing[i+1]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
					surface.SetTexCoords(bRing[i+1],thisUPos#,thisVPos#,0.0)
				Next

				For Local v=1 To (segments)
					tl=tRing[v]
					tr=tRing[v-1]
					bl=bRing[v]
					br=bRing[v-1]

					surface.AddTriangle(tl,tr,br)
					surface.AddTriangle(bl,tl,br)
				Next

				For Local v=0 To (segments)
					tRing[v]=bRing[v]
				Next
			Next

			mesh.UpdateNormals()
			Return True
		Case "cone"
			Local segments=Int(params[0]),solid=Int(params[1])

			If segments<3 Or segments>100 Then Return Null

			Local surface:TSurface=mesh.AddSurface(Null,1+segments*2,segments)
			Local bottomsurface:TSurface
			If solid bottomsurface=mesh.AddSurface(Null,1+segments,segments-1)

			Local div#=Float(360.0/(segments))

			Local height#=1.0
			Local upos#=1.0
			Local udiv#=Float(1.0/(segments))
			Local angle#=90

			Local xpos#=-Cos(angle)
			Local zpos#=Sin(angle)

			surface.SetCoords(0,0.0,height,0.0);surface.SetTexCoords(0,upos-(udiv/2.0),0)
			surface.SetCoords(1,xpos,-height,zpos);surface.SetTexCoords(1,upos,1)

			If solid bottomsurface.SetCoords(0,xpos,-height,zpos);bottomsurface.SetTexCoords(0,xpos/2.0+0.5,zpos/2.0+0.5)

			angle:+div

			xpos=-Cos(angle)
			zpos=Sin(angle)

			surface.SetCoords(2,xpos,-height,zpos);surface.SetTexCoords(2,upos-udiv,1)

			If solid bottomsurface.SetCoords(1,xpos,-height,zpos);bottomsurface.SetTexCoords(1,xpos/2.0+0.5,zpos/2.0+0.5)

			surface.SetTriangle(0,2,0,1) 'br,top,bl

			For Local i=1 To segments-1
				Local v=1+(i*2)
				upos:-udiv
				surface.SetCoords(v+0,0.0,height,0.0);surface.SetTexCoords(v+0,upos-(udiv/2.0),0)

				angle:+div

				xpos=-Cos(angle)
				zpos=Sin(angle)

				surface.SetCoords(v+1,xpos,-height,zpos);surface.SetTexCoords(v+1,upos-udiv,1)
				surface.SetTriangle(i,v+1,v+0,v-1)

				If solid=True
					bottomsurface.SetCoords(i+1,xpos,-height,zpos);bottomsurface.SetTexCoords(i+1,xpos/2.0+0.5,zpos/2.0+0.5)
					bottomsurface.SetTriangle(i-1,i+1,i,0)
				EndIf
			Next

			mesh.UpdateNormals()
			Return True
		Case "cube"
			Local surface:TSurface=mesh.AddSurface(Null,24,12)

			For Local i=0 To 3
				surface.SetNormal(i,0,-1,0)
			Next
			surface.SetCoords( 0, 1.0, 1.0,-1.0);surface.SetTexCoords( 0, 0.0, 0.0)
			surface.SetCoords( 1,-1.0, 1.0,-1.0);surface.SetTexCoords( 1, 0.0, 1.0)
			surface.SetCoords( 2,-1.0, 1.0, 1.0);surface.SetTexCoords( 2,-1.0, 1.0)
			surface.SetCoords( 3, 1.0, 1.0, 1.0);surface.SetTexCoords( 3,-1.0, 0.0)
			surface.SetTriangle( 0, 0, 1, 2)
			surface.SetTriangle( 1, 3, 0, 2)

			For Local i=4 To 7
				surface.SetNormal(i,0,1,0)
			Next
			surface.SetCoords( 4, 1.0,-1.0, 1.0);surface.SetTexCoords( 4, 0.0, 0.0)
			surface.SetCoords( 5,-1.0,-1.0, 1.0);surface.SetTexCoords( 5, 0.0, 1.0)
			surface.SetCoords( 6,-1.0,-1.0,-1.0);surface.SetTexCoords( 6,-1.0, 1.0)
			surface.SetCoords( 7, 1.0,-1.0,-1.0);surface.SetTexCoords( 7,-1.0, 0.0)
			surface.SetTriangle( 2, 4, 5, 6)
			surface.SetTriangle( 3, 7, 4, 6)

			For Local i=8 To 11
				surface.SetNormal(i,0,0,-1)
			Next
			surface.SetCoords( 8, 1.0, 1.0, 1.0);surface.SetTexCoords( 8,-1.0, 0.0)
			surface.SetCoords( 9,-1.0, 1.0, 1.0);surface.SetTexCoords( 9, 0.0, 0.0)
			surface.SetCoords(10,-1.0,-1.0, 1.0);surface.SetTexCoords(10, 0.0, 1.0)
			surface.SetCoords(11, 1.0,-1.0, 1.0);surface.SetTexCoords(11,-1.0, 1.0)
			surface.SetTriangle( 4,8 , 9,10)
			surface.SetTriangle( 5,11, 8,10)

			For Local i=12 To 15
				surface.SetNormal(i,0,0,1)
			Next
			surface.SetCoords(12, 1.0,-1.0,-1.0);surface.SetTexCoords(12, 0.0, 1.0)
			surface.SetCoords(13,-1.0,-1.0,-1.0);surface.SetTexCoords(13,-1.0, 1.0)
			surface.SetCoords(14,-1.0, 1.0,-1.0);surface.SetTexCoords(14,-1.0, 0.0)
			surface.SetCoords(15, 1.0, 1.0,-1.0);surface.SetTexCoords(15, 0.0, 0.0)
			surface.SetTriangle( 6,12,13,14)
			surface.SetTriangle( 7,15,12,14)

			For Local i=16 To 19
				surface.SetNormal(i,1,0,0)
			Next
			surface.SetCoords(16,-1.0, 1.0, 1.0);surface.SetTexCoords(16,-1.0, 0.0)
			surface.SetCoords(17,-1.0, 1.0,-1.0);surface.SetTexCoords(17, 0.0, 0.0)
			surface.SetCoords(18,-1.0,-1.0,-1.0);surface.SetTexCoords(18, 0.0, 1.0)
			surface.SetCoords(19,-1.0,-1.0, 1.0);surface.SetTexCoords(19,-1.0, 1.0)
			surface.SetTriangle( 8,16,17,18)
			surface.SetTriangle( 9,19,16,18)

			For Local i=20 To 23
				surface.SetNormal(i,-1,0,0)
			Next
			surface.SetCoords(20, 1.0, 1.0,-1.0);surface.SetTexCoords(20,-1.0, 0.0)
			surface.SetCoords(21, 1.0, 1.0, 1.0);surface.SetTexCoords(21, 0.0, 0.0)
			surface.SetCoords(22, 1.0,-1.0, 1.0);surface.SetTexCoords(22, 0.0, 1.0)
			surface.SetCoords(23, 1.0,-1.0,-1.0);surface.SetTexCoords(23,-1.0, 1.0)
			surface.SetTriangle(10,20,21,22)
			surface.SetTriangle(11,23,20,22)
			Return True
		Case "torus"
			'torrad#,torwidth#,segments,sides
			Local torrad#=Float(params[0]),torwidth#=Float(params[1]),segments=Int(params[2]),sides=Int(params[3])

			Local surface:TSurface=mesh.AddSurface(Null,segments*sides,segments*sides*2)

			Local FATSTEP#=360.0/sides
			Local DEGSTEP#=360.0/segments

			Local radius#=0,x#=0,y#=0,z#=0
			For Local f=0 To sides-1
				Local fat#=FATSTEP*f
				radius = torrad + torwidth*Sin(fat)
				z=torwidth*Cos(fat)
				For Local d=0 To segments-1
					Local deg#=DEGSTEP*f
					x=radius*Cos(deg)
					y=radius*Sin(deg)
					surface.SetCoords f*segments+d,x,y,z
					surface.SetTexCoords f*segments+d,x,y
				Next
			Next

			For Local v=0 To segments*sides-1
				Local v0=v,v1=v+segments,v2=v+1,v3=v+1+segments

				If v1>=segments*sides v1:-(segments*sides)
				If v2>=segments*sides v2:-(segments*sides)
				If v3>=segments*sides v3:-(segments*sides)

				surface.SetTriangle v*2+0,v0,v1,v2
				surface.SetTriangle v*2+1,v1,v3,v2
			Next

		Default
			Return False
		End Select
	End Method

	Method Info$()
		Return "Primitives"
	End Method
	Method ModuleName$()
		Return "primitives"
	End Method

	Function FlipNormals(surface:TSurface)
		For Local v=0 To surface._vertexcnt-1
			Local nx#,ny#,nz#
			surface.GetNormal(v,nx,ny,nz)
			surface.SetNormal(v,-nx,-ny,-nz)
		Next
	End Function
End Type

New TMeshLoaderPrimitives

Strict

Rem
	bbdoc: Primitive mesh loader for MaxB3D.
End Rem
Module MaxB3D.Primitives
ModuleInfo "Author: Kevin Primm"
ModuleInfo "License: MIT"

Import MaxB3D.Core

Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateCube:TMesh(parent:TEntity=Null)
	Return _currentworld.AddMesh("//cube",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateCone:TMesh(segments=8,solid=True,parent:TEntity=Null)
	Return _currentworld.AddMesh("//cone("+segments+","+solid+")",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateCylinder:TMesh(segments=8,solid=True,parent:TEntity=Null)
	Return _currentworld.AddMesh("//cylinder("+segments+","+solid+")",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateSphere:TMesh(segments=8,parent:TEntity=Null)
	Return _currentworld.AddMesh("//sphere("+segments+")",parent)
End Function
Rem
	bbdoc: Needs documentation. #TODO
End Rem
Function CreateTorus:TMesh(radius#,width#,segments,sides,parent:TEntity=Null)
	Return _currentworld.AddMesh("//torus("+radius+","+width+","+segments+","+sides+")",parent)
End Function

Type TMeshLoaderPrimitives Extends TMeshLoader
	Method Run(mesh:TMesh,stream:TStream,url:Object)
		Local str$=String(url)
		Local params$[]=str[str.Find("(")+1..str.FindLast(")")].Split(",")

		Local name$ = str[str.Find("//")+2..]
		If str.Find("(") > -1 name = name[..name.Find("(")]
		DebugLog name
		Select name
		Case "sphere"
			Local segments=Int(params[0])
			If segments<2 Or segments>100 Then Return Null

			'Vertex count
			'((segments*2)*4) '2
			'((segments*2)*6)+((segments*2)*((segments-2)*4)) '>2

			'Triangle count
			'(segments*2)*2
			'(segments*2)*2+(segments*2)*(segments-2)*2

			Local vertexcount=4*segments*(3+((segments-2)*2))
			Local trianglecount=segments*4*(1+(segments-2))
			If segments=2
				vertexcount=segments*8
				trianglecount=segments*4
			EndIf

			Local surface:TSurface=mesh.AddSurface(Null,vertexcount,trianglecount)

			Local div#=Float(360.0/(segments*2))
			Local height#=1.0
			Local upos#=1.0
			Local udiv#=Float(1.0/(segments*2))
			Local vdiv#=Float(1.0/segments)
			Local RotAngle#=90

			If segments=2
				For Local i=1 To (segments*2)
					Local np=(i-1)*4+0,sp=np+1
					surface.SetCoords(np,0.0,height,0.0);surface.SetTexCoords(np,upos#-(udiv#/2.0),0) 'northpole
					surface.SetCoords(sp,0.0,-height,0.0);surface.SetTexCoords(sp,upos#-(udiv#/2.0),1) 'southpole
					Local XPos#=-Cos(RotAngle#)
					Local ZPos#=Sin(RotAngle#)
					Local v0=sp+1
					surface.SetCoords(v0,XPos#,0,ZPos#)
					surface.SetTexCoords(v0,upos#,0.5)
					RotAngle#=RotAngle#+div#
					If RotAngle#>=360.0 Then RotAngle#=RotAngle#-360.0
					XPos#=-Cos(RotAngle#)
					ZPos#=Sin(RotAngle#)
					upos#=upos#-udiv#
					Local v1=v0+1
					surface.SetCoords(v1,XPos#,0,ZPos#)
					surface.SetTexCoords(v1,upos#,0.5)

					surface.SetTriangle((i-1)*2+0,np,v0,v1)
					surface.SetTriangle((i-1)*2+1,v1,v0,sp)
				Next
			Else
				For Local i=1 To (segments*2)
					Local np=(i-1)*6+0,sp=np+1
					surface.SetCoords(np,0.0,height,0.0)
					surface.SetTexCoords(np,upos#-(udiv#/2.0),0)'northpole
					surface.SetCoords(sp,0.0,-height,0.0)
					surface.SetTexCoords(sp,upos#-(udiv#/2.0),1)'southpole

					Local YPos#=Cos(div#)

					Local XPos#=-Cos(RotAngle#)*(Sin(div#))
					Local ZPos#=Sin(RotAngle#)*(Sin(div#))

					Local v0t=sp+1
					surface.SetCoords(v0t,XPos#,YPos#,ZPos#)
					surface.SetTexCoords(v0t,upos#,vdiv#)
					Local v0b=v0t+1
					surface.SetCoords(v0b,XPos#,-YPos#,ZPos#)
					surface.SetTexCoords(v0b,upos#,1-vdiv#)

					RotAngle#=RotAngle#+div#

					XPos#=-Cos(RotAngle#)*(Sin(div#))
					ZPos#=Sin(RotAngle#)*(Sin(div#))

					upos#=upos#-udiv#

					Local v1t=v0b+1
					surface.SetCoords(v1t,XPos#,YPos#,ZPos#)
					surface.SetTexCoords(v1t,upos#,vdiv#)
					Local v1b=v1t+1
					surface.SetCoords(v1b,XPos#,-YPos#,ZPos#)
					surface.SetTexCoords(v1b,upos#,1-vdiv#)

					surface.SetTriangle((i-1)*2+0,np,v0t,v1t)
					surface.SetTriangle((i-1)*2+1,v1b,v0b,sp)
				Next

				upos#=1.0
				RotAngle#=90
				For Local i=1 To (segments*2)

					Local mult#=1
					Local YPos#=Cos(div#*(mult#))
					Local YPos2#=Cos(div#*(mult#+1.0))
					Local Thisvdiv#=vdiv#
					For Local j=1 To (segments-2)
						Local XPos#=-Cos(RotAngle#)*(Sin(div#*(mult#)))
						Local ZPos#=Sin(RotAngle#)*(Sin(div#*(mult#)))

						Local XPos2#=-Cos(RotAngle#)*(Sin(div#*(mult#+1.0)))
						Local ZPos2#=Sin(RotAngle#)*(Sin(div#*(mult#+1.0)))

						Local v0t=(segments*2)*6+((i-1)*(segments-2)*4)+((j-1)*4),v0b=v0t+1
						surface.SetCoords(v0t,XPos#,YPos#,ZPos#)
						surface.SetTexCoords(v0t,upos#,Thisvdiv#,0.0)
						surface.SetCoords(v0b,XPos2#,YPos2#,ZPos2#)
						surface.SetTexCoords(v0b,upos#,Thisvdiv#+vdiv#,0.0)

						Local tempRotAngle#=RotAngle#+div#

						XPos#=-Cos(tempRotAngle#)*(Sin(div#*(mult#)))
						ZPos#=Sin(tempRotAngle#)*(Sin(div#*(mult#)))

						XPos2#=-Cos(tempRotAngle#)*(Sin(div#*(mult#+1.0)))
						ZPos2#=Sin(tempRotAngle#)*(Sin(div#*(mult#+1.0)))

						Local temp_upos#=upos-udiv

						Local v1t=v0b+1,v1b=v1t+1
						surface.SetCoords(v1t,XPos,YPos,ZPos)
						surface.SetTexCoords(v1t,temp_upos,Thisvdiv,0.0)
						surface.SetCoords(v1b,XPos2,YPos2,ZPos2)
						surface.SetTexCoords(v1b,temp_upos,Thisvdiv+vdiv,0.0)

						surface.SetTriangle((segments*2)*2+((i-1)*(segments-2)*2)+((j-1)*2)+0,v1t,v0t,v0b)
						surface.SetTriangle((segments*2)*2+((i-1)*(segments-2)*2)+((j-1)*2)+1,v1b,v1t,v0b)

						Thisvdiv#=Thisvdiv#+vdiv#
						mult#=mult#+1
						YPos#=Cos(div#*(mult#))
						YPos2#=Cos(div#*(mult#+1.0))

					Next
					upos#=upos#-udiv#
					RotAngle#=RotAngle#+div#
				Next
			EndIf

			mesh.UpdateNormals()
			'mesh.ForEachSurfaceDo FlipNormals
			Return True
		Case "cylinder"
			Local ringsegments=0

			Local segments=Int(params[0]),solid=Int(params[1])

			Local tr,tl,br,bl
			Local ts0,ts1,newts
			Local bs0,bs1,newbs
			If segments<3 Or segments>100 Then Return Null
			If ringsegments<0 Or ringsegments>100 Then Return Null

			Local surface:TSurface=mesh.AddSurface(Null,1000,1000)
			Local solidsurface:TSurface
			If solid=True
				solidsurface=mesh.AddSurface(Null,1000,1000)
			EndIf
			Local div#=Float(360.0/(segments))

			Local height#=1.0
			Local ringSegmentHeight#=(height#*2.0)/(ringsegments+1)
			Local upos#=1.0
			Local udiv#=Float(1.0/(segments))
			Local vpos#=1.0
			Local vdiv#=Float(1.0/(ringsegments+1))

			Local SideRotAngle#=90

			Local tRing[segments+1]
			Local bRing[segments+1]

			If solid=True
				Local xpos#=-Cos(SideRotAngle#)
				Local zpos#=Sin(SideRotAngle#)

				ts0=solidsurface.AddVertex(xpos,height,zpos,xpos/2.0+0.5,zpos/2.0+0.5)
				bs0=solidsurface.AddVertex(xpos,-height,zpos,xpos/2.0+0.5,zpos/2.0+0.5)

				solidsurface.SetTexCoords(ts0,xpos/2.0+0.5,zpos/2.0+0.5,0.0)
				solidsurface.SetTexCoords(bs0,xpos/2.0+0.5,zpos/2.0+0.5,0.0)

				SideRotAngle=SideRotAngle+div

				xpos#=-Cos(SideRotAngle#)
				zpos#=Sin(SideRotAngle#)

				ts1=solidsurface.AddVertex(xpos#,height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)
				bs1=solidsurface.AddVertex(xpos#,-height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)

				solidsurface.SetTexCoords(ts1,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)
				solidsurface.SetTexCoords(bs1,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)

				For Local i=1 To (segments-2)
					SideRotAngle#=SideRotAngle#+div#

					xpos#=-Cos(SideRotAngle#)
					zpos#=Sin(SideRotAngle#)

					newts=solidsurface.AddVertex(xpos#,height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)
					newbs=solidsurface.AddVertex(xpos#,-height,zpos#,xpos#/2.0+0.5,zpos#/2.0+0.5)

					solidsurface.SetTexCoords(newts,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)
					solidsurface.SetTexCoords(newbs,xpos#/2.0+0.5,zpos#/2.0+0.5,0.0)

					solidsurface.AddTriangle(ts0,ts1,newts)
					solidsurface.AddTriangle(newbs,bs1,bs0)

					If i<(segments-2)
						ts1=newts
						bs1=newbs
					EndIf
				Next
			EndIf

			Local thisHeight#=height#

			SideRotAngle#=90
			Local xpos#=-Cos(SideRotAngle#)
			Local zpos#=Sin(SideRotAngle#)
			Local thisUPos#=upos#
			Local thisVPos#=0
			tRing[0]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
			surface.SetTexCoords(tRing[0],thisUPos#,thisVPos#,0.0)
			For Local i=0 To (segments-1)
				SideRotAngle#=SideRotAngle#+div#
				xpos#=-Cos(SideRotAngle#)
				zpos#=Sin(SideRotAngle#)
				thisUPos#=thisUPos#-udiv#
				tRing[i+1]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
				surface.SetTexCoords(tRing[i+1],thisUPos#,thisVPos#,0.0)
			Next

			For Local ring=0 To ringsegments
				Local thisHeight=thisHeight-ringSegmentHeight#

				SideRotAngle#=90
				xpos#=-Cos(SideRotAngle#)
				zpos#=Sin(SideRotAngle#)
				thisUPos#=upos#
				thisVPos#=thisVPos#+vdiv#
				bRing[0]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
				surface.SetTexCoords(bRing[0],thisUPos#,thisVPos#,0.0)
				For Local i=0 To (segments-1)
					SideRotAngle#=SideRotAngle#+div#
					xpos#=-Cos(SideRotAngle#)
					zpos#=Sin(SideRotAngle#)
					thisUPos#=thisUPos#-udiv#
					bRing[i+1]=surface.AddVertex(xpos#,thisHeight,zpos#,thisUPos#,thisVPos#)
					surface.SetTexCoords(bRing[i+1],thisUPos#,thisVPos#,0.0)
				Next

				For Local v=1 To (segments)
					tl=tRing[v]
					tr=tRing[v-1]
					bl=bRing[v]
					br=bRing[v-1]

					surface.AddTriangle(tl,tr,br)
					surface.AddTriangle(bl,tl,br)
				Next

				For Local v=0 To (segments)
					tRing[v]=bRing[v]
				Next
			Next

			mesh.UpdateNormals()
			Return True
		Case "cone"
			Local segments=Int(params[0]),solid=Int(params[1])

			If segments<3 Or segments>100 Then Return Null

			Local surface:TSurface=mesh.AddSurface(Null,1+segments*2,segments)
			Local bottomsurface:TSurface
			If solid bottomsurface=mesh.AddSurface(Null,1+segments,segments-1)

			Local div#=Float(360.0/(segments))

			Local height#=1.0
			Local upos#=1.0
			Local udiv#=Float(1.0/(segments))
			Local angle#=90

			Local xpos#=-Cos(angle)
			Local zpos#=Sin(angle)

			surface.SetCoords(0,0.0,height,0.0);surface.SetTexCoords(0,upos-(udiv/2.0),0)
			surface.SetCoords(1,xpos,-height,zpos);surface.SetTexCoords(1,upos,1)

			If solid bottomsurface.SetCoords(0,xpos,-height,zpos);bottomsurface.SetTexCoords(0,xpos/2.0+0.5,zpos/2.0+0.5)

			angle:+div

			xpos=-Cos(angle)
			zpos=Sin(angle)

			surface.SetCoords(2,xpos,-height,zpos);surface.SetTexCoords(2,upos-udiv,1)

			If solid bottomsurface.SetCoords(1,xpos,-height,zpos);bottomsurface.SetTexCoords(1,xpos/2.0+0.5,zpos/2.0+0.5)

			surface.SetTriangle(0,2,0,1) 'br,top,bl

			For Local i=1 To segments-1
				Local v=1+(i*2)
				upos:-udiv
				surface.SetCoords(v+0,0.0,height,0.0);surface.SetTexCoords(v+0,upos-(udiv/2.0),0)

				angle:+div

				xpos=-Cos(angle)
				zpos=Sin(angle)

				surface.SetCoords(v+1,xpos,-height,zpos);surface.SetTexCoords(v+1,upos-udiv,1)
				surface.SetTriangle(i,v+1,v+0,v-1)

				If solid=True
					bottomsurface.SetCoords(i+1,xpos,-height,zpos);bottomsurface.SetTexCoords(i+1,xpos/2.0+0.5,zpos/2.0+0.5)
					bottomsurface.SetTriangle(i-1,i+1,i,0)
				EndIf
			Next

			mesh.UpdateNormals()
			Return True
		Case "cube"
			Local surface:TSurface=mesh.AddSurface(Null,24,12)

			For Local i=0 To 3
				surface.SetNormal(i,0,-1,0)
			Next
			surface.SetCoords( 0, 1.0, 1.0,-1.0);surface.SetTexCoords( 0, 0.0, 0.0)
			surface.SetCoords( 1,-1.0, 1.0,-1.0);surface.SetTexCoords( 1, 0.0, 1.0)
			surface.SetCoords( 2,-1.0, 1.0, 1.0);surface.SetTexCoords( 2,-1.0, 1.0)
			surface.SetCoords( 3, 1.0, 1.0, 1.0);surface.SetTexCoords( 3,-1.0, 0.0)
			surface.SetTriangle( 0, 0, 1, 2)
			surface.SetTriangle( 1, 3, 0, 2)

			For Local i=4 To 7
				surface.SetNormal(i,0,1,0)
			Next
			surface.SetCoords( 4, 1.0,-1.0, 1.0);surface.SetTexCoords( 4, 0.0, 0.0)
			surface.SetCoords( 5,-1.0,-1.0, 1.0);surface.SetTexCoords( 5, 0.0, 1.0)
			surface.SetCoords( 6,-1.0,-1.0,-1.0);surface.SetTexCoords( 6,-1.0, 1.0)
			surface.SetCoords( 7, 1.0,-1.0,-1.0);surface.SetTexCoords( 7,-1.0, 0.0)
			surface.SetTriangle( 2, 4, 5, 6)
			surface.SetTriangle( 3, 7, 4, 6)

			For Local i=8 To 11
				surface.SetNormal(i,0,0,-1)
			Next
			surface.SetCoords( 8, 1.0, 1.0, 1.0);surface.SetTexCoords( 8,-1.0, 0.0)
			surface.SetCoords( 9,-1.0, 1.0, 1.0);surface.SetTexCoords( 9, 0.0, 0.0)
			surface.SetCoords(10,-1.0,-1.0, 1.0);surface.SetTexCoords(10, 0.0, 1.0)
			surface.SetCoords(11, 1.0,-1.0, 1.0);surface.SetTexCoords(11,-1.0, 1.0)
			surface.SetTriangle( 4,8 , 9,10)
			surface.SetTriangle( 5,11, 8,10)

			For Local i=12 To 15
				surface.SetNormal(i,0,0,1)
			Next
			surface.SetCoords(12, 1.0,-1.0,-1.0);surface.SetTexCoords(12, 0.0, 1.0)
			surface.SetCoords(13,-1.0,-1.0,-1.0);surface.SetTexCoords(13,-1.0, 1.0)
			surface.SetCoords(14,-1.0, 1.0,-1.0);surface.SetTexCoords(14,-1.0, 0.0)
			surface.SetCoords(15, 1.0, 1.0,-1.0);surface.SetTexCoords(15, 0.0, 0.0)
			surface.SetTriangle( 6,12,13,14)
			surface.SetTriangle( 7,15,12,14)

			For Local i=16 To 19
				surface.SetNormal(i,1,0,0)
			Next
			surface.SetCoords(16,-1.0, 1.0, 1.0);surface.SetTexCoords(16,-1.0, 0.0)
			surface.SetCoords(17,-1.0, 1.0,-1.0);surface.SetTexCoords(17, 0.0, 0.0)
			surface.SetCoords(18,-1.0,-1.0,-1.0);surface.SetTexCoords(18, 0.0, 1.0)
			surface.SetCoords(19,-1.0,-1.0, 1.0);surface.SetTexCoords(19,-1.0, 1.0)
			surface.SetTriangle( 8,16,17,18)
			surface.SetTriangle( 9,19,16,18)

			For Local i=20 To 23
				surface.SetNormal(i,-1,0,0)
			Next
			surface.SetCoords(20, 1.0, 1.0,-1.0);surface.SetTexCoords(20,-1.0, 0.0)
			surface.SetCoords(21, 1.0, 1.0, 1.0);surface.SetTexCoords(21, 0.0, 0.0)
			surface.SetCoords(22, 1.0,-1.0, 1.0);surface.SetTexCoords(22, 0.0, 1.0)
			surface.SetCoords(23, 1.0,-1.0,-1.0);surface.SetTexCoords(23,-1.0, 1.0)
			surface.SetTriangle(10,20,21,22)
			surface.SetTriangle(11,23,20,22)
			Return True
		Case "torus"
			'torrad#,torwidth#,segments,sides
			Local torrad#=Float(params[0]),torwidth#=Float(params[1]),segments=Int(params[2]),sides=Int(params[3])

			Local surface:TSurface=mesh.AddSurface(Null,segments*sides,segments*sides*2)

			Local FATSTEP#=360.0/sides
			Local DEGSTEP#=360.0/segments

			Local radius#=0,x#=0,y#=0,z#=0
			For Local f=0 To sides-1
				Local fat#=FATSTEP*f
				radius = torrad + torwidth*Sin(fat)
				z=torwidth*Cos(fat)
				For Local d=0 To segments-1
					Local deg#=DEGSTEP*f
					x=radius*Cos(deg)
					y=radius*Sin(deg)
					surface.SetCoords f*segments+d,x,y,z
					surface.SetTexCoords f*segments+d,x,y
				Next
			Next

			For Local v=0 To segments*sides-1
				Local v0=v,v1=v+segments,v2=v+1,v3=v+1+segments

				If v1>=segments*sides v1:-(segments*sides)
				If v2>=segments*sides v2:-(segments*sides)
				If v3>=segments*sides v3:-(segments*sides)

				surface.SetTriangle v*2+0,v0,v1,v2
				surface.SetTriangle v*2+1,v1,v3,v2
			Next

		Default
			Return False
		End Select
	End Method

	Method Info$()
		Return "Primitives"
	End Method
	Method ModuleName$()
		Return "primitives"
	End Method

	Function FlipNormals(surface:TSurface)
		For Local v=0 To surface._vertexcnt-1
			Local nx#,ny#,nz#
			surface.GetNormal(v,nx,ny,nz)
			surface.SetNormal(v,-nx,-ny,-nz)
		Next
	End Function
End Type

New TMeshLoaderPrimitives


Added more stub methods for MonkeyMax.

Added more stub methods for MonkeyMax.

--HG--
extra : convert_revision : svn%3Ae934d3cd-0d57-f34a-94d0-9e8d31ae1c29/trunk%40352

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp

Type gxtkApp
	Field ginput:gxtkInput
	Field gaudio:gxtkAudio
	Field ggraphics:gxtkGraphics

	Field dead:Int=0
	Field suspended:Int=0
	Field vloading:Int=0
	Field maxloading:Int=0
	Field updateRate:Int=0
	Field nextUpdate:Float=0
	Field updatePeriod:Float=0
	Field startMillis:Float=0

	Method New()' gxtkApp()
		app=Self
		ggraphics=New gxtkGraphics
	EndMethod

	Method Setup()
		ginput=New gxtkInput
		gaudio=New gxtkAudio

		bb_input_SetInputDevice(ginput)
		bb_audio_SetAudioDevice(gaudio)

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 )

		InvokeOnCreate()
		InvokeOnRender()
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			Local updates:Int = 0

			Local cont:Int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then cont = 0
				If nextUpdate>BlitzMaxMillisecs() Then cont = 0
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				EndIf
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps
			nextUpdate=BlitzMaxMillisecs() +updatePeriod
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadState:String()
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		var state:String=file.data.state;
'		file.close();
'		if( state ) return state;
		Return ""
	EndMethod

	Method SaveState:Int( state:String )
'		var file:SharedObject=SharedObject.getLocal( "gxtkapp" );
'		file.data.state=state;
'		file.close();
		Return 0
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading
	EndMethod

	Method OnCreate:Int()
		Return 0
	EndMethod

	Method OnUpdate:Int()
		Return 0
	EndMethod

	Method OnSuspend:Int()
		Return 0
	EndMethod

	Method OnResume:Int()
		Return 0
	EndMethod

	Method OnRender:Int()
		Return 0
	EndMethod

	Method OnLoading:Int()
		Return 0
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method DrawPoint:Int(x:Float, y:Float)
		Plot x, y
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Local sx:Float = Sqr( (ix*ix) + (jx*jx) )
		Local sy:Float = Sqr( (iy*iy) + (jy*jy) )
		Local rot:Float = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		SetOrigin( tx, ty )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		Return BRL.PolledInput.KeyDown( key )
	EndMethod

	Method KeyHit:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		Return BRL.PolledInput.KeyHit( key )
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method TouchY:Float( index:Int )
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method AccelX:Int()
		return 0
	EndMethod

	Method AccelY:Int()
		return 0
	EndMethod

	Method AccelZ:Int()
		return 0
	EndMethod

	Method SetKeyboardEnabled:Int( enabled:int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0
	Field channels:gxtkChannel[] = New gxtkChannel[33]

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method ChannelState:int( channel:int )
		Return -1
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method PauseMusic:Int()
		Return 0
	EndMethod

	Method ResumeMusic:Int()
		Return 0
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local sound:TSound = LoadSound("data/"+path)
		If sound Then
			Local gs:gxtkSample = New gxtkSample
			gs.setSound(sound)
			Return gs
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******

' ***** Start mojo.bmax.bmx ******

Global app:gxtkApp;

Type gxtkApp
	Field ginput:gxtkInput;
	Field gaudio:gxtkAudio;
	Field ggraphics:gxtkGraphics;

	Field dead:Int=0;
	Field suspended:Int=0;
	Field vloading:Int=0;
	Field maxloading:Int=0;
	Field updateRate:Int=0;
	Field nextUpdate:Float=0;
	Field updatePeriod:Float=0;
	Field startMillis:Float=0;

	Method New()' gxtkApp()
		app=Self;
		ggraphics=New gxtkGraphics;
	EndMethod

	Method Setup()
		ginput=New gxtkInput;
		gaudio=New gxtkAudio;

		bb_input_SetInputDevice(ginput);
		bb_audio_SetAudioDevice(gaudio);

		startMillis=BlitzMaxMillisecs()

		SetFrameRate( 0 );

		InvokeOnCreate();
		InvokeOnRender();
		Update()
	EndMethod

	Method Update()
		While Not AppTerminate()
			Local updates:Int = 0

			Local cont:Int = 1
			While (cont)
				nextUpdate:+updatePeriod
				InvokeOnUpdate()
				If Not updatePeriod Then cont = 0
				If nextUpdate>BlitzMaxMillisecs() Then cont = 0
				updates:+1
				If updates = 7 Then
					nextUpdate = BlitzMaxMillisecs()
					cont = 0
				EndIf
			Wend
			InvokeOnRender()
		Wend
	EndMethod

	Method InvokeOnCreate()
		If dead Return
		dead = 1
		OnCreate()
		dead = 0
	EndMethod

	Method InvokeOnUpdate()
		If dead Or suspended Or Not updateRate Or vloading Return
		OnUpdate()
	EndMethod

	Method InvokeOnRender()
		If dead Or suspended Return
		ggraphics.BeginRender()
		If vloading
			OnLoading()
		Else
			OnRender()
		EndIf
		ggraphics.EndRender()
	EndMethod

	Method SetFrameRate( fps:Int )
		If fps
			updatePeriod=1000.0/fps;
			nextUpdate=BlitzMaxMillisecs() +updatePeriod;
		Else
			updatePeriod=0
		EndIf
	EndMethod

	Method LoadString:String( path:String )
		path = "data/" + path;
		Return LoadText( path )
	EndMethod

	' ***** GXTK API *****

	Method GraphicsDevice:gxtkGraphics()
		Return ggraphics
	EndMethod

	Method InputDevice:gxtkInput()
		Return ginput
	EndMethod

	Method AudioDevice:gxtkAudio()
		Return gaudio
	EndMethod

	Method SetUpdateRate:Int(hertz:Int)
		updateRate = hertz
		If Not vloading Then SetFrameRate(updateRate)
		Return 0
	EndMethod

	Method MilliSecs:Int()
		Return BlitzMaxMillisecs() - startMillis
	EndMethod

	Method Loading:Int()
		Return vloading;
	EndMethod

	Method OnCreate:Int()
		Return 0;
	EndMethod

	Method OnUpdate:Int()
		Return 0;
	EndMethod

	Method OnSuspend:Int()
		Return 0;
	EndMethod

	Method OnResume:Int()
		Return 0;
	EndMethod

	Method OnRender:Int()
		Return 0;
	EndMethod

	Method OnLoading:Int()
		Return 0;
	EndMethod
EndType

Type gxtkGraphics
	Field gmode:Int = 1

	Method Mode:Int()
		Return gmode
	EndMethod

	Method Cls:Int(r:Int = 0, g:Int = 0, b:Int = 0)
		BlitzMaxCls(r, g, b)
		Return 0
	EndMethod

	Method LoadSurface:gxtkSurface(path:String)
		Local image:TImage = LoadImage("data/"+path)
		If image Then
			Local gs:gxtkSurface = New gxtkSurface
			gs.setImage(image)
			Return gs
		EndIf
		Return Null
	EndMethod

	Method BeginRender()
	EndMethod

	Method EndRender()
		Flip
	EndMethod

	Method SetColor:Int(r:Int, g:Int, b:Int)
		BlitzMaxSetColor(r, g, b)
		Return 0
	EndMethod

	Method SetAlpha:Int(a:Float)
		BlitzMaxSetAlpha(a)
		Return 0
	EndMethod

	Method SetBlend:Int(blend:Int)
		BlitzMaxSetBlend(blend)
		Return 0
	EndMethod

	Method Width:Int()
		Return GraphicsWidth()
	EndMethod

	Method Height:Int()
		Return GraphicsHeight()
	EndMethod

	Method SetScissor:Int(x:Int, y:Int, w:Int, h:Int)
		SetViewport(x, y, w, h) ' NOT TESTED!
		Return 0
	EndMethod

	Method SetMatrix:Int(ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float)
		Local sx:Float = Sqr( (ix*ix) + (jx*jx) )
		Local sy:Float = Sqr( (iy*iy) + (jy*jy) )
		Local rot:Float = -Atan2( jx, ix )
		SetTransform( rot, sx, sy )
		SetOrigin( tx, ty )
		Return 0
	EndMethod

	Method DrawSurface:Int(surface:gxtkSurface,x:Float,y:Float)
		DrawImage(surface.image, x, y, 0)
		Return 0
	EndMethod

	Method DrawSurface2:Int(surface:gxtkSurface,x:Float,y:Float, srcx:Int, srcy:Int, srcw:Int, srch:Int )
		DrawSubImageRect(surface.image, x, y, srcw, srch, srcx, srcy, srcw, srch)
		Return 0
	EndMethod

	Method DrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
		BlitzMaxDrawLine(x1, y1, x2, y2)
		Return 0
	EndMethod

	Method DrawOval:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawOval(x, y, w, h)
		Return 0
	EndMethod

	Method DrawPoly:Int(vertices:Float[])
		BlitzMaxDrawPoly( vertices )
		Return 0
	EndMethod

	Method DrawRect:Int( x:Float, y:Float, w:Float, h:Float )
		BlitzMaxDrawRect(x, y, w, h)
		Return 0
	EndMethod
EndType

Type gxtkInput
	Method MouseX:Float()
		Return BRL.PolledInput.MouseX()
	EndMethod

	Method MouseY:Float()
		Return BRL.PolledInput.MouseY()
	EndMethod

	Method KeyDown:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseDown( key )
		EndIf
		Return BRL.PolledInput.KeyDown( key )
	EndMethod

	Method KeyHit:Int( key:Int )
		If( key >= 1 And key <= 3 )
			Return BRL.PolledInput.MouseHit( key )
		EndIf
		Return BRL.PolledInput.KeyHit( key )
	EndMethod

	Method GetChar:Int()
		Return BRL.PolledInput.GetChar()
	EndMethod

	Method JoyX:Int( index:Int )
		Return Pub.FreeJoy.JoyX( index )
	EndMethod

	Method JoyY:Int( index:Int )
		Return Pub.FreeJoy.JoyY( index )
	EndMethod

	Method JoyZ:Int( index:Int )
		Return Pub.FreeJoy.JoyZ( index )
	EndMethod

	Method TouchX:Float( index:Int )
		Return 0
	EndMethod

	Method TouchY:Float( index:Int )
		Return 0
	EndMethod
EndType

Type gxtkChannel
	Field channel:TChannel	' null then not playing
	Field sample:gxtkSample
	Field loops:int
	'Field transform:SoundTransform=new SoundTransform()
	'Field pausepos:Number
	Field state:int

	Method New()
		channel = New TChannel
	EndMethod
EndType

Type gxtkAudio
	Field amusicState:Int = 0;
	Field channels:gxtkChannel[] = New gxtkChannel[33];

	Method New()
		For local i:Int = 0 To 33 - 1
			channels[i] = New gxtkChannel
		Next
	EndMethod

	Method MusicState:Int()
'		If( musicState = 1 And music.isPlaying() = False )
'			musicState = 0;
'		Endif
		Return amusicState;
	EndMethod

	Method PlayMusic:Int( path:String, flags:Int )
		Return 0
	EndMethod

	Method StopMusic()
	EndMethod

	Method StopChannel( channel:Int )
	EndMethod

	Method PlaySample( sound:gxtkSample, channel:Int, flags:Int )
		Local chan:gxtkChannel = channels[channel]

		'If chan.state <> 0 Then chan.channel.Stop() <-- this crashes after the first play!?

		chan.sample = sound
		'chan.loops = flags ? 0x7fffffff : 0;
		'chan.channel = sample.sound.play( 0,chan.loops,chan.transform );
		'chan.channel = sound
		chan.state=1

		PlaySound( sound.sound, chan.channel )
	EndMethod

	Method SetPan( channel:Int, pan:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetPan(pan)
	EndMethod

	Method SetRate( channel:Int, rate:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetRate(rate)
	EndMethod

	Method SetMusicVolume:Int( volume:Float )
		Return 0
	EndMethod

	Method SetVolume:Int( channel:Int, volume:Float )
		Local chan:gxtkChannel = channels[channel]
		chan.channel.SetVolume(volume)
		Return 0
	EndMethod

	Method LoadSample:gxtkSample( path:String )
		Local sound:TSound = LoadSound("data/"+path)
		If sound Then
			Local gs:gxtkSample = New gxtkSample
			gs.setSound(sound)
			Return gs
		EndIf
		Return Null
	EndMethod
EndType

Type gxtkSample
	Field sound:TSound

	Method setSound(sound:TSound)
		Self.sound = sound
	EndMethod

	Method Discard()
		Self.sound = null
	EndMethod
EndType

Function BlitzMaxDrawPoly:Int(vertices:Float[])
	DrawPoly vertices
	Return 0
EndFunction

Function BlitzMaxDrawRect:Int( x:Float, y:Float, w:Float, h:Float )
	DrawRect x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawOval:Int( x:Float, y:Float, w:Float, h:Float )
	DrawOval x, y, w, h
	Return 0
EndFunction

Function BlitzMaxDrawLine:Int( x1:Float,y1:Float,x2:Float,y2:Float )
	DrawLine x1, y1, x2, y2
	Return 0
EndFunction

Function BlitzMaxMillisecs:Int()
	Return MilliSecs()
EndFunction

Function BlitzMaxSetColor(r:Int, g:Int, b:Int)
	SetColor(r, g, b)
EndFunction

Function BlitzMaxSetBlend(blend:Int)
	Select blend
		Case 0
			SetBlend( ALPHABLEND )
		Case 1
			SetBlend( LIGHTBLEND )
	End Select
EndFunction

Function BlitzMaxSetAlpha(a:Float)
	SetAlpha(a)
EndFunction

Function BlitzMaxCls(r:Int = 0, g:Int = 0, b:Int = 0)
	SetClsColor(r, g, b)
	Cls
EndFunction

Type gxtkSurface
	Field w:Int, h:Int
	Field image:TImage

	Method setImage(image:TImage)
		Self.image = image
	EndMethod

	Method Width:Int()
		Return image.Width
	EndMethod

	Method Height:Int()
		Return image.Height
	EndMethod

	Method Discard()
	EndMethod
EndType

' ***** End mojo.bmax.bmx ******


Added mxVersionObject to represent version numbers

refs gh-1

Rem
	bbdoc: Maximus module base.
	about: An abstract type for the similarities between module scopes and modules.
End Rem
Type mxModuleBase Abstract

	Field m_name:String

'#region Field accessors

	Rem
		bbdoc: Set the base's name.
		returns: Nothing.
	End Rem
	Method SetName(name:String)
		Assert name, "(mxModuleBase.SetName) name cannot be Null"
		m_name = name
	End Method

	Rem
		bbdoc: Get the base's name.
		returns: The base's name.
	End Rem
	Method GetName:String()
		Return m_name
	End Method

'#end region Field accessors

	Rem
		bbdoc: Set a common field from the given variable.
		returns: True if the given variable was handled, or False if it was not.
	End Rem
	Method SetCommonFromVariable:Int(variable:dVariable)
		Return False
	End Method

End Type

Rem
	bbdoc: Maximus module scope.
End Rem
Type mxModuleScope Extends mxModuleBase

	Field m_modules:dObjectMap

	Method New()
		m_modules = New dObjectMap
	End Method

	Rem
		bbdoc: Add the given module to the scope.
		returns: True if the module was added, or False if it was not (the module is Null).
	End Rem
	Method AddModule:Int(modul:mxModule)
		If modul
			m_modules._Insert(modul.GetName(), modul)
			modul.SetParent(Self)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Check if the given module name is found in the scope.
		returns: True if the given module was found in the scope, or False if it was not.
	End Rem
	Method HasModule:Int(modname:String)
		Return m_modules._Contains(modname)
	End Method

	Rem
		bbdoc: Get a module with the given name.
		returns: The module with the given name, or Null if there is no module with the given name.
	End Rem
	Method GetModuleWithName:mxModule(modname:String)
		Return mxModule(m_modules._ObjectWithKey(modname))
	End Method

	Rem
		bbdoc: Load the given dJObject into the scope.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxModuleScope(root:dJObject)
		If root
			SetName(root.GetName())
			For Local variable:dVariable = EachIn root
				If dJObject(variable)
					AddModule(New mxModule.FromJSON(dJObject(variable)))
				'Else
				'	SetCommonFromVariable(variable)
				End If
			Next
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the module enumerator for the scope.
		returns: The module enumerator for the scope.
	End Rem
	Method ModuleEnumerator:TMapEnumerator()
		Return m_modules.ValueEnumerator()
	End Method

End Type

Rem
	bbdoc: Maximus module.
End Rem
Type mxModule Extends mxModuleBase

	Field m_parent:mxModuleScope
	Field m_description:String
	Field m_versions:dObjectMap

	Method New()
		m_versions = New dObjectMap
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the module's parent.
		returns: Nothing.
	End Rem
	Method SetParent(parent:mxModuleScope)
		m_parent = parent
	End Method

	Rem
		bbdoc: Get the module's parent.
		returns: The module's parent.
	End Rem
	Method GetParent:mxModuleScope()
		Return m_parent
	End Method

	Rem
		bbdoc: Get the full name of the module (e.g. "modscope.module").
		returns: The full name of the module.
	End Rem
	Method GetFullName:String()
		Return m_parent.m_name + "." + m_name
	End Method

	Rem
		bbdoc: Set the module's description.
		returns: Nothing.
	End Rem
	Method SetDescription(description:String)
		m_description = description
	End Method

	Rem
		bbdoc: Get the module's description.
		returns: The module's description.
	End Rem
	Method GetDescription:String()
		Return m_description
	End Method

'#end region Field accessors

	Rem
		bbdoc: Get the installed version of the module (if it is installed).
		returns: The version of the module that is installed, or Null if the module is not installed.
	End Rem
	Method GetInstalledVersion:mxModuleVersion()
		Local version:String = mxModUtils.GetInstalledVersionFromVerID(GetFullName())
		If version
			Return New mxModuleVersion.Create(Self, version, Null)
		End If
		Return Null
	End Method

	Rem
		bbdoc: Add the given version to the module.
		returns: True if the version was added, or False if it was not (the version is Null).
	End Rem
	Method AddVersion:Int(version:mxModuleVersion)
		If version
			m_versions._Insert(version.GetName(), version)
			version.SetParent(Self)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Check if there is a version with the given name.
		returns: True if the version was found, or False if it was not.
	End Rem
	Method HasVersion:Int(name:String)
		Return m_versions._Contains(name)
	End Method

	Rem
		bbdoc: Get a version with the given name.
		returns: The version with the given name, or Null if there is no version with the given name.
	End Rem
	Method GetVersionWithName:mxModuleVersion(name:String)
		Return mxModuleVersion(m_versions._ObjectWithKey(name))
	End Method

	Rem
		bbdoc: Get the latest (non-dev if available) version for this module.
		returns: The latest version for this module.
		about: The latest version will be returned, or the dev version if it is the only version.
	End Rem
	Method GetLatestVersion:mxModuleVersion()
		Local hver:mxModuleVersion
		For Local ver:mxModuleVersion = EachIn VersionEnumerator()
			If ver.GetName() <> "dev"
				If Not hver Or ver.Compare(hver) = 1
					hver = ver
				End If
			End If
		Next
		If Not hver
			hver = GetVersionWithName("dev")
		End If
		Return hver
	End Method

	Rem
		bbdoc: Set a common field from the given variable.
		returns: True if the given variable was handled, or False if it was not.
	End Rem
	Method SetCommonFromVariable:Int(variable:dVariable)
		If Super.SetCommonFromVariable(variable)
			Return True
		Else
			Select variable.GetName().ToLower()
				Case "desc"
					SetDescription(dValueVariable(variable).ValueAsString())
				Case "versions"
					For Local jobj:dJObject = EachIn dJObject(variable)
						AddVersion(New mxModuleVersion.FromJSON(jobj))
					Next
				Default
					Return False
			End Select
			Return True
		End If
	End Method

	Rem
		bbdoc: Load the given dJObject into the module.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxModule(root:dJObject)
		If root
			SetName(root.GetName())
			For Local variable:dVariable = EachIn root
				SetCommonFromVariable(variable)
			Next
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the version enumerator for the module.
		returns: The version enumerator for the module.
	End Rem
	Method VersionEnumerator:TMapEnumerator()
		Return m_versions.ValueEnumerator()
	End Method

End Type

Rem
	bbdoc: Maximus module version.
End Rem
Type mxModuleVersion

	Field m_parent:mxModule
	Field m_name:String, m_url:String
	Field m_dependencies:mxModuleDependencies

	Method New()
		m_dependencies = New mxModuleDependencies
	End Method

	Rem
		bbdoc: Create a module version.
		returns: Itself.
	End Rem
	Method Create:mxModuleVersion(parent:mxModule, name:String, url:String)
		SetParent(parent)
		SetName(name)
		SetUrl(url)
		Return Self
	End Method

	Rem
		bbdoc: Clone the version.
		returns: A clone of the version.
		about: If @withdeps is True (default value), the dependencies will be added as well.
	End Rem
	Method Clone:mxModuleVersion(withdeps:Int = True)
		Local v:mxModuleVersion = New mxModuleVersion.Create(m_parent, m_name, m_url)
		For Local dep:mxModuleDependency = EachIn DependencyEnumerator()
			v.m_dependencies.AddDependency(dep)
		Next
		Return v
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the version's parent.
		returns: Nothing.
	End Rem
	Method SetParent(parent:mxModule)
		m_parent = parent
	End Method

	Rem
		bbdoc: Get the version's parent.
		returns: The version's parent.
	End Rem
	Method GetParent:mxModule()
		Return m_parent
	End Method

	Rem
		bbdoc: Set the version's name.
		returns: Nothing.
	End Rem
	Method SetName(name:String)
		Assert name, "(mxModuleVersion.SetName) name cannot be Null!"
		m_name = name
	End Method

	Rem
		bbdoc: Get the version's name.
		returns: The version's name.
	End Rem
	Method GetName:String()
		Return m_name
	End Method

	Rem
		bbdoc: Set the version's url.
		returns: Nothing.
	End Rem
	Method SetUrl(url:String)
		'Assert url, "(mxModuleVersion.SetUrl) url cannot be Null!"
		m_url = url
	End Method

	Rem
		bbdoc: Get the version's url.
		returns: The version's url.
	End Rem
	Method GetUrl:String()
		Return m_url
	End Method

	Rem
		bbdoc: Get the temporary fetch path for the version's source archive.
		returns: The temporary file path for the version's source archive.
	End Rem
	Method GetTemporaryFilePath:String()
		Return "tmp/" + m_parent.GetFullName() + "-" + m_name + ".zip"
	End Method

	Rem
		bbdoc: Set the version's dependencies.
		returns: Nothing.
	End Rem
	Method SetDependencies(dependencies:mxModuleDependencies)
		m_dependencies = dependencies
	End Method

	Rem
		bbdoc: Get the version's dependencies.
		returns: The version's dependencies.
	End Rem
	Method GetDependencies:mxModuleDependencies()
		Return m_dependencies
	End Method

	Rem
		bbdoc: Get the version parts for the version.
		returns: True if the version is development, in which case the parameters are both 0; or False, in which case the parameters are set accordingly.
	End Rem
	Method GetVersionParts:Int(vmajor:String Var, vminor:String Var)
		If m_name <> "dev"
			Local i:Int = m_name.Find(".")
			If i > -1
				vmajor = m_name[..i]
				vminor = m_name[i + 1..]
			Else
				vmajor = m_name ' I'm not sure what other version formats would be used, so I'm just playing a random card here
			End If
			Return False
		End If
		Return True
	End Method

'#end region Field accessors

	Rem
		bbdoc: Set a common field from the given variable.
		returns: True if the given variable was handled, or False if it was not.
	End Rem
	Method SetCommonFromVariable:Int(variable:dVariable)
		Select variable.GetName().ToLower()
			Case "url"
				SetUrl(dValueVariable(variable).ValueAsString())
			Case "deps"
				m_dependencies.FromJSON(dJArray(variable))
			Default
				Return False
		End Select
		Return True
	End Method

	Rem
		bbdoc: Load the given dJObject into the version.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxModuleVersion(root:dJObject)
		If root
			SetName(root.GetName())
			For Local variable:dVariable = EachIn root
				SetCommonFromVariable(variable)
			Next
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the dependency enumerator for the version.
		returns: The dependency enumerator for the version.
	End Rem
	Method DependencyEnumerator:TMapEnumerator()
		Return m_dependencies.DependencyEnumerator()
	End Method

	Rem
		bbdoc: Compare the version with the given version.
		returns: 0 if the two versions are the same (same in version number alone), 1 if this version is greater than the given, or -1 if the given version is greater than this version.
	End Rem
	Method Compare:Int(with:Object)
		Local ver:mxModuleVersion = mxModuleVersion(with)
		If ver
			If m_name = ver.m_name
				Return 0
			Else
				Local smajor:String, sminor:String, wmajor:String, wminor:String
				Local sdev:Int = GetVersionParts(smajor, sminor), wdev:Int = ver.GetVersionParts(wmajor, wminor)
				If (sdev And wdev) Or (smajor = wmajor And sminor = wminor)
					Return 0
				Else If smajor > wmajor
					Return 1
				Else If smajor = wmajor
					If sminor > wminor
						Return 1
					Else
						Return -1
					End If
				Else
					Return -1
				End If
			End If
		End If
		Return Super.Compare(with)
	End Method

	Rem
		bbdoc: Fetch the version's source archive.
		returns: Nothing.
	End Rem
	Method FetchSourceArchive()
		Local file:String = GetTemporaryFilePath()
		logger.LogMessage("fetching: " + GetUrl() + " -> " + file + "    ", False)
		If FileType(file) = FILETYPE_NONE
			Local stream:TStream = WriteFileExplicitly(file)
			If stream
				Local request:TRESTRequest = New TRESTRequest, response:TRESTResponse
				request.SetProgressCallback(_ProgressCallback, New _mxProgressStore)
				request.SetStream(stream)
				Try
					response = request.Call(GetUrl(), ["User-Agent: " + mainapp.m_useragent], "GET")
				Catch e:Object
					stream.Close()
					DeleteFile(file)
					logger.LogMessage("")
					ThrowError(_s("error.fetch.archive", [e.ToString()]))
				End Try
				stream.Close()
				If response.responseCode = 200
					logger.LogMessage(_s("message.fetch.done", [String(response.responseCode)]))
				Else
					DeleteFile(file)
					logger.LogMessage("")
					ThrowError(_s("error.fetch.archive", ["Bad response code: " + String(response.responseCode)]))
				End If
			Else
				ThrowError(_s("error.writeperms", [file]))
			End If
		Else
			logger.LogMessage(_s("message.fetch.alreadyfetched"))
		End If
	End Method

	Rem
		bbdoc: Progress callback for source archive fetching.
		returns: Zero (no error).
	End Rem
	Function _ProgressCallback:Int(data:Object, dltotal:Double, dlnow:Double, ultotal:Double, ulnow:Double)
		Local store:_mxProgressStore = _mxProgressStore(data)
		Local prog:Int = (dlnow / dltotal) * 100
		If prog > store.m_progress + 5
			store.m_progress = prog
			WriteStdOut(".")
		End If
		Return 0
	End Function

End Type

Rem
	bbdoc: A object representing a version, not to be confused with mxModuleVersion
	about: Supported formats are 1, 1.1 and 1.1.1.
	Do note that because the version is represented as a Double there's a change
	the precision of the final number is off.

	1.1.2 Would be converted to 1.001002 but when converted to a Double it results
	in 1.0010019999999999.

	1.1.12 However would be converted to 1.001012 and when converted to a Double it
	results in 1.0010120000000000.

	A version string of 'dev' will be represented as 99999
End Rem
Type mxVersionObject

	Field m_version:Double

	Rem
		bbdoc: Parse a version string
		returns: Itself.
	End Rem
	Method Parse:mxVersionObject(version:String)
		If version = "dev"
			m_version = 99999
			Return Self
		End If

		Local parts:String[] = version.Split(".")
		Local converted:String = parts[0]
		If parts.Length > 1
			converted:+"."
			For Local part:String = EachIn parts[1..]
				While part.Length <> 3
					part = "0" + part
				WEnd
				converted:+part
			Next

			'Make sure we format to x.x.x
			If parts.Length = 2
				converted:+"000"
			End If
		End If

		m_version = converted.ToDouble()
		Return Self
	End Method

	Rem
		bbdoc: Compare version numbers
	End Rem
	Method Compare:Int(withObject:Object)
		Local version:mxVersionObject = mxVersionObject(withObject)
		If version
			If m_version = version.m_version
				Return 0
			Else If m_version > version.m_version
				Return 1
			Else If m_version < version.m_version
				Return - 1
			End If
		End If
		Return Super.Compare(withObject)
	End Method
End Type

Rem
	bbdoc: Maximus metafile handler
End Rem
Type mxMetaFile

	Field m_metafile:String
	Field m_scope:String
	Field m_name:String
	Field m_version:String

	Rem
		bbdoc: Create a metafile handler.
		returns: Itself.
	End Rem
	Method Create:mxMetaFile(metafile:String)
		SetMetaFile(metafile)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the metafile file path.
		returns: Nothing.
	End Rem
	Method SetMetaFile(metafile:String)
		m_metafile = metafile
	End Method

	Rem
		bbdoc: Get the metafile path.
		returns: The metafile path.
	End Rem
	Method GetMetaFile:String()
		Return m_metafile
	End Method

'#end region Field accessors

	Rem
		bbdoc: Load the configuration.
		returns: Nothing.
	End Rem
	Method Load()
		If FileType(GetMetaFile()) = FILETYPE_FILE
			Try
				Local contents:String = LoadText(GetMetaFile()).Trim()
				Local parts:String[] = contents.Split("/")

				If parts.Length <> 2
					Throw _s("error.load.meta.invalid_format") ;
				End If

				Local modinfo:String[] = parts[0].Split(".")
				m_scope = modinfo[0]
				m_name = modinfo[1]
				m_version = parts[1]
			Catch e:Object
				logger.LogError(_s("error.load.metafile.parse", [GetMetaFile()]))
				logger.LogError(_s("error.load.metafile.parse", [e.ToString()]))
			End Try
		Else
			logger.LogWarning(_s("error.load.metafile.notfound", [GetMetaFile()]))
		End If
	End Method

	Rem
		bbdoc: Save the configuration
		returns: Nothing.
	End Rem
	Method Save()
		If m_scope.Length = 0 Or m_name.Length = 0 Or m_version.Length = 0
			ThrowError(_s("error.save.metafile.incomplete", [m_scope, m_name, m_version]))
		End If

		Local stream:TStream = WriteFileExplicitly(GetMetaFile())
		stream.WriteString(m_scope + "." + m_name + "/" + m_version)
		stream.Close
	End Method
End Type

Rem
	bbdoc: Maximus module base.
	about: An abstract type for the similarities between module scopes and modules.
End Rem
Type mxModuleBase Abstract

	Field m_name:String

'#region Field accessors

	Rem
		bbdoc: Set the base's name.
		returns: Nothing.
	End Rem
	Method SetName(name:String)
		Assert name, "(mxModuleBase.SetName) name cannot be Null"
		m_name = name
	End Method

	Rem
		bbdoc: Get the base's name.
		returns: The base's name.
	End Rem
	Method GetName:String()
		Return m_name
	End Method

'#end region Field accessors

	Rem
		bbdoc: Set a common field from the given variable.
		returns: True if the given variable was handled, or False if it was not.
	End Rem
	Method SetCommonFromVariable:Int(variable:dVariable)
		Return False
	End Method

End Type

Rem
	bbdoc: Maximus module scope.
End Rem
Type mxModuleScope Extends mxModuleBase

	Field m_modules:dObjectMap

	Method New()
		m_modules = New dObjectMap
	End Method

	Rem
		bbdoc: Add the given module to the scope.
		returns: True if the module was added, or False if it was not (the module is Null).
	End Rem
	Method AddModule:Int(modul:mxModule)
		If modul
			m_modules._Insert(modul.GetName(), modul)
			modul.SetParent(Self)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Check if the given module name is found in the scope.
		returns: True if the given module was found in the scope, or False if it was not.
	End Rem
	Method HasModule:Int(modname:String)
		Return m_modules._Contains(modname)
	End Method

	Rem
		bbdoc: Get a module with the given name.
		returns: The module with the given name, or Null if there is no module with the given name.
	End Rem
	Method GetModuleWithName:mxModule(modname:String)
		Return mxModule(m_modules._ObjectWithKey(modname))
	End Method

	Rem
		bbdoc: Load the given dJObject into the scope.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxModuleScope(root:dJObject)
		If root
			SetName(root.GetName())
			For Local variable:dVariable = EachIn root
				If dJObject(variable)
					AddModule(New mxModule.FromJSON(dJObject(variable)))
				'Else
				'	SetCommonFromVariable(variable)
				End If
			Next
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the module enumerator for the scope.
		returns: The module enumerator for the scope.
	End Rem
	Method ModuleEnumerator:TMapEnumerator()
		Return m_modules.ValueEnumerator()
	End Method

End Type

Rem
	bbdoc: Maximus module.
End Rem
Type mxModule Extends mxModuleBase

	Field m_parent:mxModuleScope
	Field m_description:String
	Field m_versions:dObjectMap

	Method New()
		m_versions = New dObjectMap
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the module's parent.
		returns: Nothing.
	End Rem
	Method SetParent(parent:mxModuleScope)
		m_parent = parent
	End Method

	Rem
		bbdoc: Get the module's parent.
		returns: The module's parent.
	End Rem
	Method GetParent:mxModuleScope()
		Return m_parent
	End Method

	Rem
		bbdoc: Get the full name of the module (e.g. "modscope.module").
		returns: The full name of the module.
	End Rem
	Method GetFullName:String()
		Return m_parent.m_name + "." + m_name
	End Method

	Rem
		bbdoc: Set the module's description.
		returns: Nothing.
	End Rem
	Method SetDescription(description:String)
		m_description = description
	End Method

	Rem
		bbdoc: Get the module's description.
		returns: The module's description.
	End Rem
	Method GetDescription:String()
		Return m_description
	End Method

'#end region Field accessors

	Rem
		bbdoc: Get the installed version of the module (if it is installed).
		returns: The version of the module that is installed, or Null if the module is not installed.
	End Rem
	Method GetInstalledVersion:mxModuleVersion()
		Local version:String = mxModUtils.GetInstalledVersionFromVerID(GetFullName())
		If version
			Return New mxModuleVersion.Create(Self, version, Null)
		End If
		Return Null
	End Method

	Rem
		bbdoc: Add the given version to the module.
		returns: True if the version was added, or False if it was not (the version is Null).
	End Rem
	Method AddVersion:Int(version:mxModuleVersion)
		If version
			m_versions._Insert(version.GetName(), version)
			version.SetParent(Self)
			Return True
		End If
		Return False
	End Method

	Rem
		bbdoc: Check if there is a version with the given name.
		returns: True if the version was found, or False if it was not.
	End Rem
	Method HasVersion:Int(name:String)
		Return m_versions._Contains(name)
	End Method

	Rem
		bbdoc: Get a version with the given name.
		returns: The version with the given name, or Null if there is no version with the given name.
	End Rem
	Method GetVersionWithName:mxModuleVersion(name:String)
		Return mxModuleVersion(m_versions._ObjectWithKey(name))
	End Method

	Rem
		bbdoc: Get the latest (non-dev if available) version for this module.
		returns: The latest version for this module.
		about: The latest version will be returned, or the dev version if it is the only version.
	End Rem
	Method GetLatestVersion:mxModuleVersion()
		Local hver:mxModuleVersion
		For Local ver:mxModuleVersion = EachIn VersionEnumerator()
			If ver.GetName() <> "dev"
				If Not hver Or ver.Compare(hver) = 1
					hver = ver
				End If
			End If
		Next
		If Not hver
			hver = GetVersionWithName("dev")
		End If
		Return hver
	End Method

	Rem
		bbdoc: Set a common field from the given variable.
		returns: True if the given variable was handled, or False if it was not.
	End Rem
	Method SetCommonFromVariable:Int(variable:dVariable)
		If Super.SetCommonFromVariable(variable)
			Return True
		Else
			Select variable.GetName().ToLower()
				Case "desc"
					SetDescription(dValueVariable(variable).ValueAsString())
				Case "versions"
					For Local jobj:dJObject = EachIn dJObject(variable)
						AddVersion(New mxModuleVersion.FromJSON(jobj))
					Next
				Default
					Return False
			End Select
			Return True
		End If
	End Method

	Rem
		bbdoc: Load the given dJObject into the module.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxModule(root:dJObject)
		If root
			SetName(root.GetName())
			For Local variable:dVariable = EachIn root
				SetCommonFromVariable(variable)
			Next
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the version enumerator for the module.
		returns: The version enumerator for the module.
	End Rem
	Method VersionEnumerator:TMapEnumerator()
		Return m_versions.ValueEnumerator()
	End Method

End Type

Rem
	bbdoc: Maximus module version.
End Rem
Type mxModuleVersion

	Field m_parent:mxModule
	Field m_name:String, m_url:String
	Field m_dependencies:mxModuleDependencies

	Method New()
		m_dependencies = New mxModuleDependencies
	End Method

	Rem
		bbdoc: Create a module version.
		returns: Itself.
	End Rem
	Method Create:mxModuleVersion(parent:mxModule, name:String, url:String)
		SetParent(parent)
		SetName(name)
		SetUrl(url)
		Return Self
	End Method

	Rem
		bbdoc: Clone the version.
		returns: A clone of the version.
		about: If @withdeps is True (default value), the dependencies will be added as well.
	End Rem
	Method Clone:mxModuleVersion(withdeps:Int = True)
		Local v:mxModuleVersion = New mxModuleVersion.Create(m_parent, m_name, m_url)
		For Local dep:mxModuleDependency = EachIn DependencyEnumerator()
			v.m_dependencies.AddDependency(dep)
		Next
		Return v
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the version's parent.
		returns: Nothing.
	End Rem
	Method SetParent(parent:mxModule)
		m_parent = parent
	End Method

	Rem
		bbdoc: Get the version's parent.
		returns: The version's parent.
	End Rem
	Method GetParent:mxModule()
		Return m_parent
	End Method

	Rem
		bbdoc: Set the version's name.
		returns: Nothing.
	End Rem
	Method SetName(name:String)
		Assert name, "(mxModuleVersion.SetName) name cannot be Null!"
		m_name = name
	End Method

	Rem
		bbdoc: Get the version's name.
		returns: The version's name.
	End Rem
	Method GetName:String()
		Return m_name
	End Method

	Rem
		bbdoc: Set the version's url.
		returns: Nothing.
	End Rem
	Method SetUrl(url:String)
		'Assert url, "(mxModuleVersion.SetUrl) url cannot be Null!"
		m_url = url
	End Method

	Rem
		bbdoc: Get the version's url.
		returns: The version's url.
	End Rem
	Method GetUrl:String()
		Return m_url
	End Method

	Rem
		bbdoc: Get the temporary fetch path for the version's source archive.
		returns: The temporary file path for the version's source archive.
	End Rem
	Method GetTemporaryFilePath:String()
		Return "tmp/" + m_parent.GetFullName() + "-" + m_name + ".zip"
	End Method

	Rem
		bbdoc: Set the version's dependencies.
		returns: Nothing.
	End Rem
	Method SetDependencies(dependencies:mxModuleDependencies)
		m_dependencies = dependencies
	End Method

	Rem
		bbdoc: Get the version's dependencies.
		returns: The version's dependencies.
	End Rem
	Method GetDependencies:mxModuleDependencies()
		Return m_dependencies
	End Method

	Rem
		bbdoc: Get the version parts for the version.
		returns: True if the version is development, in which case the parameters are both 0; or False, in which case the parameters are set accordingly.
	End Rem
	Method GetVersionParts:Int(vmajor:String Var, vminor:String Var)
		If m_name <> "dev"
			Local i:Int = m_name.Find(".")
			If i > -1
				vmajor = m_name[..i]
				vminor = m_name[i + 1..]
			Else
				vmajor = m_name ' I'm not sure what other version formats would be used, so I'm just playing a random card here
			End If
			Return False
		End If
		Return True
	End Method

'#end region Field accessors

	Rem
		bbdoc: Set a common field from the given variable.
		returns: True if the given variable was handled, or False if it was not.
	End Rem
	Method SetCommonFromVariable:Int(variable:dVariable)
		Select variable.GetName().ToLower()
			Case "url"
				SetUrl(dValueVariable(variable).ValueAsString())
			Case "deps"
				m_dependencies.FromJSON(dJArray(variable))
			Default
				Return False
		End Select
		Return True
	End Method

	Rem
		bbdoc: Load the given dJObject into the version.
		returns: Itself, or Null if @root is Null.
	End Rem
	Method FromJSON:mxModuleVersion(root:dJObject)
		If root
			SetName(root.GetName())
			For Local variable:dVariable = EachIn root
				SetCommonFromVariable(variable)
			Next
			Return Self
		End If
		Return Null
	End Method

	Rem
		bbdoc: Get the dependency enumerator for the version.
		returns: The dependency enumerator for the version.
	End Rem
	Method DependencyEnumerator:TMapEnumerator()
		Return m_dependencies.DependencyEnumerator()
	End Method

	Rem
		bbdoc: Compare the version with the given version.
		returns: 0 if the two versions are the same (same in version number alone), 1 if this version is greater than the given, or -1 if the given version is greater than this version.
	End Rem
	Method Compare:Int(with:Object)
		Local ver:mxModuleVersion = mxModuleVersion(with)
		If ver
			If m_name = ver.m_name
				Return 0
			Else
				Local smajor:String, sminor:String, wmajor:String, wminor:String
				Local sdev:Int = GetVersionParts(smajor, sminor), wdev:Int = ver.GetVersionParts(wmajor, wminor)
				If (sdev And wdev) Or (smajor = wmajor And sminor = wminor)
					Return 0
				Else If smajor > wmajor
					Return 1
				Else If smajor = wmajor
					If sminor > wminor
						Return 1
					Else
						Return -1
					End If
				Else
					Return -1
				End If
			End If
		End If
		Return Super.Compare(with)
	End Method

	Rem
		bbdoc: Fetch the version's source archive.
		returns: Nothing.
	End Rem
	Method FetchSourceArchive()
		Local file:String = GetTemporaryFilePath()
		logger.LogMessage("fetching: " + GetUrl() + " -> " + file + "    ", False)
		If FileType(file) = FILETYPE_NONE
			Local stream:TStream = WriteFileExplicitly(file)
			If stream
				Local request:TRESTRequest = New TRESTRequest, response:TRESTResponse
				request.SetProgressCallback(_ProgressCallback, New _mxProgressStore)
				request.SetStream(stream)
				Try
					response = request.Call(GetUrl(), ["User-Agent: " + mainapp.m_useragent], "GET")
				Catch e:Object
					stream.Close()
					DeleteFile(file)
					logger.LogMessage("")
					ThrowError(_s("error.fetch.archive", [e.ToString()]))
				End Try
				stream.Close()
				If response.responseCode = 200
					logger.LogMessage(_s("message.fetch.done", [String(response.responseCode)]))
				Else
					DeleteFile(file)
					logger.LogMessage("")
					ThrowError(_s("error.fetch.archive", ["Bad response code: " + String(response.responseCode)]))
				End If
			Else
				ThrowError(_s("error.writeperms", [file]))
			End If
		Else
			logger.LogMessage(_s("message.fetch.alreadyfetched"))
		End If
	End Method

	Rem
		bbdoc: Progress callback for source archive fetching.
		returns: Zero (no error).
	End Rem
	Function _ProgressCallback:Int(data:Object, dltotal:Double, dlnow:Double, ultotal:Double, ulnow:Double)
		Local store:_mxProgressStore = _mxProgressStore(data)
		Local prog:Int = (dlnow / dltotal) * 100
		If prog > store.m_progress + 5
			store.m_progress = prog
			WriteStdOut(".")
		End If
		Return 0
	End Function

End Type

Rem
	bbdoc: Maximus metafile handler
End Rem
Type mxMetaFile

	Field m_metafile:String
	Field m_scope:String
	Field m_name:String
	Field m_version:String

	Rem
		bbdoc: Create a metafile handler.
		returns: Itself.
	End Rem
	Method Create:mxMetaFile(metafile:String)
		SetMetaFile(metafile)
		Return Self
	End Method

'#region Field accessors

	Rem
		bbdoc: Set the metafile file path.
		returns: Nothing.
	End Rem
	Method SetMetaFile(metafile:String)
		m_metafile = metafile
	End Method

	Rem
		bbdoc: Get the metafile path.
		returns: The metafile path.
	End Rem
	Method GetMetaFile:String()
		Return m_metafile
	End Method

'#end region Field accessors

	Rem
		bbdoc: Load the configuration.
		returns: Nothing.
	End Rem
	Method Load()
		If FileType(GetMetaFile()) = FILETYPE_FILE
			Try
				Local contents:String = LoadText(GetMetaFile()).Trim()
				Local parts:String[] = contents.Split("/")

				If parts.Length <> 2
					Throw _s("error.load.meta.invalid_format") ;
				End If

				Local modinfo:String[] = parts[0].Split(".")
				m_scope = modinfo[0]
				m_name = modinfo[1]
				m_version = parts[1]
			Catch e:Object
				logger.LogError(_s("error.load.metafile.parse", [GetMetaFile()]))
				logger.LogError(_s("error.load.metafile.parse", [e.ToString()]))
			End Try
		Else
			logger.LogWarning(_s("error.load.metafile.notfound", [GetMetaFile()]))
		End If
	End Method

	Rem
		bbdoc: Save the configuration
		returns: Nothing.
	End Rem
	Method Save()
		If m_scope.Length = 0 Or m_name.Length = 0 Or m_version.Length = 0
			ThrowError(_s("error.save.metafile.incomplete", [m_scope, m_name, m_version]))
		End If

		Local stream:TStream = WriteFileExplicitly(GetMetaFile())
		stream.WriteString(m_scope + "." + m_name + "/" + m_version)
		stream.Close
	End Method
End Type


Fixed major TSurface refresh bug.

Fixed major TSurface refresh bug.

Strict

Import MaxB3D.Math
Import "brush.bmx"

Const SURFACE_POS = 1
Const SURFACE_NML = 2
Const SURFACE_CLR = 4
Const SURFACE_TEX = 8
Const SURFACE_TRI = 16
Const SURFACE_ALL = SURFACE_POS|SURFACE_NML|SURFACE_CLR|SURFACE_TEX|SURFACE_TRI

Type TSurface
	Field _brush:TBrush=New TBrush

	Field _vertexcnt,_trianglecnt
	Field _vertexpos#[],_vertexnml#[],_vertexclr#[]
	Field _vertextex#[][],_texcoordsize=-1

	Field _triangle[]

	Field _res:TSurfaceRes,_reset=-1

	Field _resetbounds=True,_minx#,_miny#,_minz#,_maxx#,_maxy#,_maxz#
	Field _boundsupdatedmsg:TMaxB3DMsg=New TMaxB3DMsg

	Method Copy:TSurface(data=SURFACE_ALL)
		Local surface:TSurface=New TSurface
		surface._brush.Load(_brush)
		surface._vertexcnt=_vertexcnt
		If data&SURFACE_POS surface._vertexpos=_vertexpos[..]
		If data&SURFACE_NML surface._vertexnml=_vertexnml[..]
		If data&SURFACE_CLR surface._vertexclr=_vertexclr[..]
		If data&SURFACE_TEX
			surface._vertextex = New Float[][_texcoordsize]
			For Local i=0 To _texcoordsize-1
				surface._vertextex[i]=_vertextex[i][..]
			Next
			surface._texcoordsize=_texcoordsize
		EndIf
		If data&SURFACE_TRI surface._trianglecnt=_trianglecnt;surface._triangle=_triangle[..]
		Return surface
	End Method

	Method Resize(vertexcount,trianglecount)
		If vertexcount>-1
			_vertexpos=_vertexpos[..vertexcount*3]
			_vertexnml=_vertexnml[..vertexcount*3]
			_vertexclr=_vertexclr[..vertexcount*4]
			For Local i=_vertexcnt*4 To vertexcount*4-1
				_vertexclr[i]=1.0
			Next
			For Local i=0 To _texcoordsize-1
				_vertextex[i]=_vertextex[i][..vertexcount*2]
			Next
			_vertexcnt=vertexcount
		EndIf

		If trianglecount>-1
			_triangle=_triangle[..trianglecount*3]
			_trianglecnt=trianglecount
		EndIf
	End Method

	Method GetSize(vertices Var,triangles Var)
		vertices=_vertexcnt
		triangles=_trianglecnt
	End Method

	Method AddVertex(x#,y#,z#,u#=0.0,v#=0.0)
		Resize(_vertexcnt+1,-1)
		SetCoords(_vertexcnt-1,x,y,z)
		SetTexCoords(_vertexcnt-1,u,v)
		Return _vertexcnt-1
	End Method

	Method GetCoords(index,x# Var,y# Var,z# Var)
		x=_vertexpos[index*3+0]
		y=_vertexpos[index*3+1]
		z=_vertexpos[index*3+2]
	End Method
	Method SetCoords(index,x#,y#,z#)
		_vertexpos[index*3+0]=x
		_vertexpos[index*3+1]=y
		_vertexpos[index*3+2]=z
		_reset:|1;_resetbounds=True
	End Method

	Method GetNormal(index,nx# Var,ny# Var,nz# Var)
		nx=_vertexnml[index*3+0]*-1
		ny=_vertexnml[index*3+1]*-1
		nz=_vertexnml[index*3+2]*-1
	End Method
	Method SetNormal(index,nx#,ny#,nz#)
		_vertexnml[index*3+0]=nx*-1
		_vertexnml[index*3+1]=ny*-1
		_vertexnml[index*3+2]=nz*-1
		_reset:|2
	End Method

	Method GetColor(index,red Var,green Var,blue Var,alpha# Var)
		red=_vertexclr[index*4+0]*255.0
		green=_vertexclr[index*4+1]*255.0
		blue=_vertexclr[index*4+2]*255.0
		alpha=_vertexclr[index*4+3]
	End Method
	Method SetColor(index,red,green,blue,alpha#)
		_vertexclr[index*4+0]=red/255.0
		_vertexclr[index*4+1]=green/255.0
		_vertexclr[index*4+2]=blue/255.0
		_vertexclr[index*4+3]=alpha
		_reset:|4
	End Method

	Method GetTexCoords(index,u# Var,v# Var,set=0)
		ResizeTexSets set
		u=_vertextex[set][index*2+0]
		v=_vertextex[set][index*2+1]
	End Method
	Method SetTexCoords(index,u#,v#,set=0)
		ResizeTexSets set
		_vertextex[set][index*2+0]=u
		_vertextex[set][index*2+1]=v
		_reset:|Int(2^(4+set))
	End Method

	Method ResizeTexSets(set)
		If set+1>_texcoordsize
			Local size=_vertextex.length
			_vertextex=_vertextex[..set+1]
			For Local i=size To set
				_vertextex[i]=New Float[_vertexcnt*2]
			Next
			_texcoordsize=set+1
		EndIf
	End Method

	Method AddTriangle(v0,v1,v2)
		Resize(-1,_trianglecnt+1)
		SetTriangle _trianglecnt-1,v0,v1,v2
		Return _trianglecnt-1
	End Method

	Method GetTriangle(index,v0 Var,v1 Var,v2 Var)
		v0=_triangle[index*3+0]
		v1=_triangle[index*3+1]
		v2=_triangle[index*3+2]
	End Method
	Method SetTriangle(index,v0,v1,v2)
		_triangle[index*3+0]=v0
		_triangle[index*3+1]=v1
		_triangle[index*3+2]=v2
		_reset:|8
	End Method

	Method Flip()
		For Local t=0 To _trianglecnt-1
			Local v2=_triangle[t*3+2]
			_triangle[t*3+2]=_triangle[t*3+0]
			_triangle[t*3+0]=v2
		Next
		For Local v=0 To _vertexcnt-1
			_vertexnml[(v*3)+0]:*-1
			_vertexnml[(v*3)+1]:*-1
			_vertexnml[(v*3)+2]:*-1
		Next
		_reset:|2|8
	End Method

	Method Transform(matrix:TMatrix)
		For Local i=0 To _vertexcnt-1
			matrix.TransformVec3 _vertexpos[i*3+0],_vertexpos[i*3+1],_vertexpos[i*3+2]
			'matrix.TransformVector _vertexnml[i+0],_vertexnml[i+1],_vertexnml[i+2],w
		Next
		_reset:|1;_resetbounds=True
	End Method

	Method UpdateBounds(force=False)
		If Not _resetbounds And force=False Return
		_minx=999999999;_miny=999999999;_minz=999999999
		_maxx=-999999999;_maxy=-999999999;_maxz=-999999999
		For Local v=0 To _vertexcnt-1
			Local x#,y#,z#
			GetCoords v,x,y,z
			_minx=Min(x,_minx);_maxx=Max(x,_maxx)
			_miny=Min(y,_miny);_maxy=Max(y,_maxy)
			_minz=Min(z,_minz);_maxz=Max(z,_maxz)
		Next
		_boundsupdatedmsg.Run
		_resetbounds=False
	End Method

	Method UpdateNormals(smoothing=True)
		If smoothing
			C_UpdateNormals(_trianglecnt,_vertexcnt,_triangle,_vertexpos,_vertexnml)
		Else
			Local face_normal:TVector[_trianglecnt],vertex_triangles[][_vertexcnt]

			For Local i=0 To _trianglecnt-1
				Local v0,v1,v2
				GetTriangle i,v0,v1,v2

				vertex_triangles[v0]:+[i]
				vertex_triangles[v1]:+[i]
				vertex_triangles[v2]:+[i]

				Local a:TVector=New TVector,b:TVector=New TVector,c:TVector=New TVector
				GetCoords v0,a.x,a.y,a.z
				GetCoords v1,b.x,b.y,b.z
				GetCoords v2,c.x,c.y,c.z

				face_normal[i]=New TVector.FromTriangle(a,b,c)
			Next

			Local normal:TVector=New TVector
			For Local i=0 To _vertexcnt-1
				normal.Create3(0,0,0)
				For Local t=0 To vertex_triangles[i].length-1
					normal=normal.Add(face_normal[vertex_triangles[i][t]])
				Next
				normal.Normalize()
				SetNormal i,-normal.x,-normal.y,-normal.z
			Next
		EndIf
		_reset:|2
	End Method

	Method SetTriangleNormal(index,nx#,ny#,nz#)
		Local v0,v1,v2
		GetTriangle index,v0,v1,v2
		SetNormal v0,nx,ny,nz
		SetNormal v1,nx,ny,nz
		SetNormal v2,nx,ny,nz
	End Method

	Method GetBrush:TBrush()
		Return _brush.Copy()
	End Method
	Method SetBrush(brush:TBrush)
		_brush.Load(brush)
	End Method

	Method CountVertices()
		Return _vertexcnt
	End Method
	Method CountTriangles()
		Return _trianglecnt
	End Method

	Method IsEmpty()
		Return _vertexcnt=0 And _trianglecnt=0
	End Method

	Method HasAlpha()
		Return _brush.HasAlpha()
	End Method
End Type

Type TSurfaceRes Extends TDriverResource
	Field _vertexcnt
	Field _trianglecnt

	Method Copy:TSurfaceRes() Abstract
End Type

Type TMaxB3DMsg
	Field _succ:TMaxB3DMsg
	Field _context:Object
	Field _func(context:Object)

	Method Add(func(context:Object),context:Object)
		Local msg:TMaxB3DMsg=Self
		While msg._succ
			msg=msg._succ
		Wend
		msg._succ=New TMaxB3DMsg
		msg._context=context
		msg._func=func
	End Method

	Method Run()
		Local msg:TMaxB3DMsg=Self
		While msg
			If msg._func msg._func(msg._context)
			msg=msg._succ
		Wend
	End Method
End Type

Strict

Import MaxB3D.Math
Import "brush.bmx"

Const SURFACE_POS = 1
Const SURFACE_NML = 2
Const SURFACE_CLR = 4
Const SURFACE_TEX = 8
Const SURFACE_TRI = 16
Const SURFACE_ALL = SURFACE_POS|SURFACE_NML|SURFACE_CLR|SURFACE_TEX|SURFACE_TRI

Type TSurface
	Field _brush:TBrush=New TBrush

	Field _vertexcnt,_trianglecnt
	Field _vertexpos#[],_vertexnml#[],_vertexclr#[]
	Field _vertextex#[][],_texcoordsize=-1

	Field _triangle[]

	Field _res:TSurfaceRes,_reset=-1

	Field _resetbounds=True,_minx#,_miny#,_minz#,_maxx#,_maxy#,_maxz#
	Field _boundsupdatedmsg:TMaxB3DMsg=New TMaxB3DMsg

	Method Copy:TSurface(data=SURFACE_ALL)
		Local surface:TSurface=New TSurface
		surface._brush.Load(_brush)
		surface._vertexcnt=_vertexcnt
		If data&SURFACE_POS surface._vertexpos=_vertexpos[..]
		If data&SURFACE_NML surface._vertexnml=_vertexnml[..]
		If data&SURFACE_CLR surface._vertexclr=_vertexclr[..]
		If data&SURFACE_TEX
			surface._vertextex = New Float[][_texcoordsize]
			For Local i=0 To _texcoordsize-1
				surface._vertextex[i]=_vertextex[i][..]
			Next
			surface._texcoordsize=_texcoordsize
		EndIf
		If data&SURFACE_TRI surface._trianglecnt=_trianglecnt;surface._triangle=_triangle[..]
		Return surface
	End Method

	Method Resize(vertexcount,trianglecount)
		If vertexcount>-1
			_vertexpos=_vertexpos[..vertexcount*3]
			_vertexnml=_vertexnml[..vertexcount*3]
			_vertexclr=_vertexclr[..vertexcount*4]
			For Local i=_vertexcnt*4 To vertexcount*4-1
				_vertexclr[i]=1.0
			Next
			For Local i=0 To _texcoordsize-1
				_vertextex[i]=_vertextex[i][..vertexcount*2]
			Next
			_vertexcnt=vertexcount
		EndIf

		If trianglecount>-1
			_triangle=_triangle[..trianglecount*3]
			_trianglecnt=trianglecount
		EndIf
	End Method

	Method GetSize(vertices Var,triangles Var)
		vertices=_vertexcnt
		triangles=_trianglecnt
	End Method

	Method AddVertex(x#,y#,z#,u#=0.0,v#=0.0)
		Resize(_vertexcnt+1,-1)
		SetCoords(_vertexcnt-1,x,y,z)
		SetTexCoords(_vertexcnt-1,u,v)
		Return _vertexcnt-1
	End Method

	Method GetCoords(index,x# Var,y# Var,z# Var)
		x=_vertexpos[index*3+0]
		y=_vertexpos[index*3+1]
		z=_vertexpos[index*3+2]
	End Method
	Method SetCoords(index,x#,y#,z#)
		_vertexpos[index*3+0]=x
		_vertexpos[index*3+1]=y
		_vertexpos[index*3+2]=z
		_reset:|1;_resetbounds=True
	End Method

	Method GetNormal(index,nx# Var,ny# Var,nz# Var)
		nx=_vertexnml[index*3+0]*-1
		ny=_vertexnml[index*3+1]*-1
		nz=_vertexnml[index*3+2]*-1
	End Method
	Method SetNormal(index,nx#,ny#,nz#)
		_vertexnml[index*3+0]=nx*-1
		_vertexnml[index*3+1]=ny*-1
		_vertexnml[index*3+2]=nz*-1
		_reset:|2
	End Method

	Method GetColor(index,red Var,green Var,blue Var,alpha# Var)
		red=_vertexclr[index*4+0]*255.0
		green=_vertexclr[index*4+1]*255.0
		blue=_vertexclr[index*4+2]*255.0
		alpha=_vertexclr[index*4+3]
	End Method
	Method SetColor(index,red,green,blue,alpha#)
		_vertexclr[index*4+0]=red/255.0
		_vertexclr[index*4+1]=green/255.0
		_vertexclr[index*4+2]=blue/255.0
		_vertexclr[index*4+3]=alpha
		_reset:|4
	End Method

	Method GetTexCoords(index,u# Var,v# Var,set=0)
		ResizeTexSets set
		u=_vertextex[set][index*2+0]
		v=_vertextex[set][index*2+1]
	End Method
	Method SetTexCoords(index,u#,v#,set=0)
		ResizeTexSets set
		_vertextex[set][index*2+0]=u
		_vertextex[set][index*2+1]=v
		_reset:|Int(2^(4+set))
	End Method

	Method ResizeTexSets(set)
		If set+1>_texcoordsize
			Local size=_vertextex.length
			_vertextex=_vertextex[..set+1]
			For Local i=size To set
				_vertextex[i]=New Float[_vertexcnt*2]
			Next
			_texcoordsize=set+1
		EndIf
	End Method

	Method AddTriangle(v0,v1,v2)
		Resize(-1,_trianglecnt+1)
		SetTriangle _trianglecnt-1,v0,v1,v2
		Return _trianglecnt-1
	End Method

	Method GetTriangle(index,v0 Var,v1 Var,v2 Var)
		v0=_triangle[index*3+0]
		v1=_triangle[index*3+1]
		v2=_triangle[index*3+2]
	End Method
	Method SetTriangle(index,v0,v1,v2)
		_triangle[index*3+0]=v0
		_triangle[index*3+1]=v1
		_triangle[index*3+2]=v2
		_reset:|8
	End Method

	Method Flip()
		For Local t=0 To _trianglecnt-1
			Local v2=_triangle[t*3+2]
			_triangle[t*3+2]=_triangle[t*3+0]
			_triangle[t*3+0]=v2
		Next
		For Local v=0 To _vertexcnt-1
			_vertexnml[(v*3)+0]:*-1
			_vertexnml[(v*3)+1]:*-1
			_vertexnml[(v*3)+2]:*-1
		Next
		_reset:|2|8
	End Method

	Method Transform(matrix:TMatrix)
		For Local i=0 To _vertexcnt-1
			matrix.TransformVec3 _vertexpos[i*3+0],_vertexpos[i*3+1],_vertexpos[i*3+2]
			'matrix.TransformVector _vertexnml[i+0],_vertexnml[i+1],_vertexnml[i+2],w
		Next
	End Method

	Method UpdateBounds(force=False)
		If Not _resetbounds And force=false Return
		_minx=999999999;_miny=999999999;_minz=999999999
		_maxx=-999999999;_maxy=-999999999;_maxz=-999999999
		For Local v=0 To _vertexcnt-1
			Local x#,y#,z#
			GetCoords v,x,y,z
			_minx=Min(x,_minx);_maxx=Max(x,_maxx)
			_miny=Min(y,_miny);_maxy=Max(y,_maxy)
			_minz=Min(z,_minz);_maxz=Max(z,_maxz)
		Next
		_boundsupdatedmsg.Run
		_resetbounds=False
	End Method

	Method UpdateNormals(smoothing=True)
		If smoothing
			C_UpdateNormals(_trianglecnt,_vertexcnt,_triangle,_vertexpos,_vertexnml)
		Else
			Local face_normal:TVector[_trianglecnt],vertex_triangles[][_vertexcnt]

			For Local i=0 To _trianglecnt-1
				Local v0,v1,v2
				GetTriangle i,v0,v1,v2

				vertex_triangles[v0]:+[i]
				vertex_triangles[v1]:+[i]
				vertex_triangles[v2]:+[i]

				Local a:TVector=New TVector,b:TVector=New TVector,c:TVector=New TVector
				GetCoords v0,a.x,a.y,a.z
				GetCoords v1,b.x,b.y,b.z
				GetCoords v2,c.x,c.y,c.z

				face_normal[i]=New TVector.FromTriangle(a,b,c)
			Next

			Local normal:TVector=New TVector
			For Local i=0 To _vertexcnt-1
				normal.Create3(0,0,0)
				For Local t=0 To vertex_triangles[i].length-1
					normal=normal.Add(face_normal[vertex_triangles[i][t]])
				Next
				normal.Normalize()
				SetNormal i,-normal.x,-normal.y,-normal.z
			Next
		EndIf
		_reset:|2
	End Method

	Method SetTriangleNormal(index,nx#,ny#,nz#)
		Local v0,v1,v2
		GetTriangle index,v0,v1,v2
		SetNormal v0,nx,ny,nz
		SetNormal v1,nx,ny,nz
		SetNormal v2,nx,ny,nz
	End Method

	Method GetBrush:TBrush()
		Return _brush.Copy()
	End Method
	Method SetBrush(brush:TBrush)
		_brush.Load(brush)
	End Method

	Method CountVertices()
		Return _vertexcnt
	End Method
	Method CountTriangles()
		Return _trianglecnt
	End Method

	Method IsEmpty()
		Return _vertexcnt=0 And _trianglecnt=0
	End Method

	Method HasAlpha()
		Return _brush.HasAlpha()
	End Method
End Type

Type TSurfaceRes Extends TDriverResource
	Field _vertexcnt
	Field _trianglecnt

	Method Copy:TSurfaceRes() Abstract
End Type

Type TMaxB3DMsg
	Field _succ:TMaxB3DMsg
	Field _context:Object
	Field _func(context:Object)

	Method Add(func(context:Object),context:Object)
		Local msg:TMaxB3DMsg=Self
		While msg._succ
			msg=msg._succ
		Wend
		msg._succ=New TMaxB3DMsg
		msg._context=context
		msg._func=func
	End Method

	Method Run()
		Local msg:TMaxB3DMsg=Self
		While msg
			If msg._func msg._func(msg._context)
			msg=msg._succ
		Wend
	End Method
End Type