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()