Here is a Blitz3D-made converter that will save a .gmf file:
And this will load the file into a node hierarchy and display it:
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()