Game Model Format

BlitzMax Forums/BlitzMax Programming/Game Model Format

Here is a Blitz3D-made converter that will save a .gmf file:
file$="test.3ds"

Global binormalarray
Global tangentarray

AppTitle "GMF Converter"
Graphics3D 400,300,0,2

m=LoadAnimMesh(file)
While CountChildren(m)=1
	If EntityClass(m)="Mesh"
		If CountSurfaces(m)>0
			Exit
		EndIf
	EndIf
	m=GetChild(m,1)
Wend

If Not m
	RuntimeError "Failed to load model "+Chr(34)+file+Chr(34)+"."
	End
EndIf

f=WriteFile(StripExt(file)+".gmf")

;WriteString f,"GMFM"
;WriteInt f,1
;WriteInt f,4

BeginChunk f,"GMFM",1,4

WriteInt f,1

SaveEntity(m,f)

WaitKey()

End

Function StripExt$(path$)
	p=FindLast(path,".")
	If p path=Left(path,p-1)
	Return path
End Function

Function FindLast(s$,token$)
	For n=Len(s) To 1 Step -1
		If Mid(s,n,1)=token Return n
	Next
End Function

Function SaveEntity(entity,f)
	
	subnodes=0
	
	size=Len(EntityName(entity))+1+16*4
	
	Select EntityClass(entity)
		Case "Mesh"
			If CountSurfaces(entity)>0
				subnodes=subnodes+1
			EndIf
	End Select	
	
	subnodes=subnodes+CountChildren(entity)
	
	;WriteString f,"NODE"
	;WriteInt f,subnodes
	;WriteInt f,size	
	
	BeginChunk f,"NODE",subnodes,size
	
	WriteString f,EntityName(entity)
	WriteByte f,0
	
	For x=0 To 3
		For y=0 To 3
			WriteFloat f,GetMatElement(entity,x,y)
		Next
	Next
	
	;RuntimeError EntityClass(entity)
	
	Select EntityClass(entity)
		Case "Mesh"
			If CountSurfaces(entity)>0
				WriteMesh entity,f
			EndIf		
	End Select
	
	For c=1 To CountChildren(entity)
		SaveEntity GetChild(entity,c),f
	Next
	
End Function

Function WriteMesh(entity,f)
	
	;RuntimeError  CountSurfaces(entity)
	
	If CountSurfaces(entity)=1
	
		surf=GetSurface(entity,1)
		WriteSurface surf,f
		
	Else
		
		BeginChunk f,"NODE",CountSurfaces(entity),16*4+1
		WriteByte f,0 ;name
		For x=0 To 3
			For y=0 To 3
				WriteFloat f,GetMatElement(entity,x,y)
			Next
		Next
		
		For s=1 To CountSurfaces(entity)
			
			surf=GetSurface(entity,s)
			
			BeginChunk f,"NODE",1,16*4+1
			WriteByte f,0 ;name
			For x=0 To 3
				For y=0 To 3
					WriteFloat f,GetMatElement(entity,x,y)
				Next
			Next
			
			WriteSurface surf,f
				
		Next
		
	EndIf	
	
End Function

Function BeginChunk(f,id$,subnodes,size)
	Print id
	WriteString f,id
	WriteInt f,subnodes
	WriteInt f,size
End Function

Function WriteSurface(surf,f)
	
	matname$=""
	
	brus=GetSurfaceBrush(surf)
	If brus
		tex=GetBrushTexture(brus,0)
		If tex
			matname=TextureName(tex)
		EndIf
	EndIf
	
	BeginChunk f,"MESH",6,Len(matname)+1
	WriteString f,matname
	WriteByte f,0
	;Print matname
	
	CalculateTBN(surf)
	
	;Position
	BeginChunk f,"VRTS",0,CountVertices(surf)*12+16
	WriteInt f,CountVertices(surf) ;num vertices
	WriteInt f,1 ;position
	WriteInt f,4 ;float
	WriteInt f,3 ;elements
	For v=0 To CountVertices(surf)-1
		WriteFloat f,VertexX(surf,v)
		WriteFloat f,VertexY(surf,v)
		WriteFloat f,VertexZ(surf,v)
	Next
	
	;Normal
	BeginChunk f,"VRTS",0,CountVertices(surf)*12+16
	WriteInt f,CountVertices(surf) ;num vertices
	WriteInt f,2 ;normal
	WriteInt f,4 ;float
	WriteInt f,3 ;elements
	For v=0 To CountVertices(surf)-1
		WriteFloat f,VertexNX(surf,v)
		WriteFloat f,VertexNY(surf,v)
		WriteFloat f,VertexNZ(surf,v)
	Next
	
	;TexCoords
	BeginChunk f,"VRTS",0,CountVertices(surf)*8+16
	WriteInt f,CountVertices(surf) ;num vertices
	WriteInt f,3 ;texcoords
	WriteInt f,4 ;float
	WriteInt f,2 ;elements
	For v=0 To CountVertices(surf)-1
		WriteFloat f,VertexU(surf,v)
		WriteFloat f,VertexV(surf,v)
	Next

	;Binormal
	BeginChunk f,"VRTS",0,CountVertices(surf)*12+16
	WriteInt f,CountVertices(surf) ;num vertices
	WriteInt f,4 ;binormal
	WriteInt f,4 ;float
	WriteInt f,3 ;elements
	For v=0 To CountVertices(surf)-1
		WriteFloat f,PeekFloat(binormalarray,v*12+0)
		WriteFloat f,PeekFloat(binormalarray,v*12+4)
		WriteFloat f,PeekFloat(binormalarray,v*12+8)
	Next

	;Tangent
	BeginChunk f,"VRTS",0,CountVertices(surf)*12+16
	WriteInt f,CountVertices(surf) ;num vertices
	WriteInt f,4 ;tangent
	WriteInt f,4 ;float
	WriteInt f,3 ;elements
	For v=0 To CountVertices(surf)-1
		WriteFloat f,PeekFloat(tangentarray,v*12+0)
		WriteFloat f,PeekFloat(tangentarray,v*12+4)
		WriteFloat f,PeekFloat(tangentarray,v*12+8)
	Next

	;Indices
	BeginChunk f,"TRIS",0,CountTriangles(surf)*6+8	
	WriteInt f,CountTriangles(surf)*3 ;num indices
	WriteInt f,2 ;word
	For t=0 To CountTriangles(surf)-1
		WriteShort f,TriangleVertex(surf,t,0)
		WriteShort f,TriangleVertex(surf,t,1)
		WriteShort f,TriangleVertex(surf,t,2)
	Next	
	
End Function

Function WriteString(f,s$)
	For n=1 To Len(s)
		WriteByte f,Asc(Mid(s,n))
	Next
End Function

Function CalculateTBN(surf)
	vertexupdated=CreateBank(CountVertices(surf))
	binormalarray=CreateBank(CountVertices(surf)*12)
	tangentarray=CreateBank(CountVertices(surf)*12)
	
	For t=0 To CountTriangles(surf)-1
		a=TriangleVertex(surf,t,0)
		b=TriangleVertex(surf,t,1)
		c=TriangleVertex(surf,t,2)
		
		If PeekByte(vertexupdated,a)=0 Or PeekByte(vertexupdated,b)=0 Or PeekByte(vertexupdated,c)=0
			
			v1x#=VertexX(surf,a)
			v1y#=VertexY(surf,a)
			v1z#=VertexZ(surf,a)
			v1u#=VertexU(surf,a)
			v1v#=VertexV(surf,a)
			v2x#=VertexX(surf,b)
			v2y#=VertexY(surf,b)
			v2z#=VertexZ(surf,b)
			v2u#=VertexU(surf,b)
			v2v#=VertexV(surf,b)
			v3x#=VertexX(surf,c)
			v3y#=VertexY(surf,c)
			v3z#=VertexZ(surf,c)
			v3u#=VertexU(surf,c)
			v3v#=VertexV(surf,c)
	        
	        x1#=v2x-v1x
	        x2#=v3x-v1x
	        y1#=v2y-v1y
	        y2#=v3y-v1y
	   		z1#=v2z-v1z
	        z2#=v3z-v1z
	       	s1#=v2u-v1u
	        s2#=v3u-v1u
	        t1#=v2v-v1v
	        t2#=v3v-v1v
				
			r#=1.0/(s1*t2-s2*t1)
			If r<>0.0
				
				sx#=(t2*x1-t1*x2)*Sgn(r)
				sy#=(t2*y1-t1*y2)*Sgn(r)
	            sz#=(t2*z1-t1*z2)*Sgn(r)
				tx#=(s1*x2-s2*x1)*Sgn(r)
				ty#=(s1*y2-s2*y1)*Sgn(r)
				tz#=(s1*z2-s2*z1)*Sgn(r)
	
				If (Abs(sx)<0.0001 And Abs(sy)<0.0001 And Abs(sz)<0.0001)=True Or (Abs(tx)<0.0001 And Abs(ty)<0.0001 And Abs(tz)<0.0001)=False
					
					m#=Sqr(sx*sx+sy*sy+sz*sz)
					sx=sx/m
					sy=sy/m
					sz=sz/m
					
					If PeekByte(vertexupdated,a)=0
						PokeByte vertexupdated,a,1
						PokeFloat binormalarray,a*12+0,sx
						PokeFloat binormalarray,a*12+4,sy
						PokeFloat binormalarray,a*12+8,sz
						PokeFloat tangentarray,a*12+0,tx
						PokeFloat tangentarray,a*12+4,ty
						PokeFloat tangentarray,a*12+8,tz
					EndIf
					
					If PeekByte(vertexupdated,b)=0
						PokeByte vertexupdated,b,1
						PokeFloat binormalarray,b*12+0,sx
						PokeFloat binormalarray,b*12+4,sy
						PokeFloat binormalarray,b*12+8,sz
						PokeFloat tangentarray,b*12+0,tx
						PokeFloat tangentarray,b*12+4,ty
						PokeFloat tangentarray,b*12+8,tz
					EndIf
					
					If PeekByte(vertexupdated,c)=0
						PokeByte vertexupdated,c,1
						PokeFloat binormalarray,c*12+0,sx
						PokeFloat binormalarray,c*12+4,sy
						PokeFloat binormalarray,c*12+8,sz
						PokeFloat tangentarray,c*12+0,tx
						PokeFloat tangentarray,c*12+4,ty
						PokeFloat tangentarray,c*12+8,tz
					EndIf
					
				EndIf
			EndIf
		EndIf
	Next
End Function


And this will load the file into a node hierarchy and display it:
Type TGMFNode
	Field name:String
	Field size:Int
	Field data:TStream
	Field subnodes:TGMFNode[]
	
	Function Read:TGMFNode(stream:TStream,parent:TGMFNode=Null)
		Local node:TGMFNode
		Local subnodes:Int
		Local n
		Local bank:TBank
		If stream.Eof()
			'Notify "Unexpected end of stream."
			LOAD_GMF_ERROR=1
			Return
		EndIf
		node=New TGMFNode
		node.name=stream.ReadString(4)

		subnode_count=stream.ReadInt()
		If subnode_count
			Local subnodes_:TGMFNode[subnode_count]
			node.subnodes=subnodes_
		EndIf
		node.size=stream.ReadInt()
		bank=CreateBank(node.size)
		stream.readbytes bank.buf(),node.size
		node.data=CreateBankStream(bank)		
		For n=0 To subnode_count-1
			node.subnodes[n]=Read(stream,node)
		Next
		Return node
	EndFunction
	
	Method Write(stream:TStream)
		stream.WriteString name
		stream.WriteInt subnodes.length
		stream.WriteInt data.size()
		CopyStream data,stream,data.size()
	EndMethod
	
	Method Debug(tab$="")
		Local n
		Print tab+name
		Print tab+size
		tab:+"	"
		If subnodes
			For n=0 To subnodes.length-1
				subnodes[n].debug(tab)
			Next
		EndIf
	EndMethod
	
EndType

Private
Global LOAD_GMF_ERROR
Public

Function LoadGMF:TGMFNode(url:Object)
	Local stream:TStream
	Local root:TGMFNode
	LOAD_GMF_ERROR=0
	stream=ReadStream(url)
	If Not stream Return
	root=TGMFNode.Read(stream)
	stream.close()
	If LOAD_GMF_ERROR root=Null
	Return root
EndFunction

LoadGMF("test.gmf").debug()