I use tools like fragmotion, Milkshape3d, TrueSpace .... to make and edit 3d model & animations.
And I think b3d format is very inclusive format and well supported by cheap and good utilities.
So it's a good format for 3d games.(Though it's price goes up as it gets popularity...)
I converted my b3d to md2 and then to ddd. My 1.4MB b3d file become 13MB! so huge!~
So I suggest that GLbasic supports a skinning Mesh format and hope you make a exporter for fragmotion or for
other cheap and good tool!
I think Unwrap3d may be a good tool too.
Is the ddd file format described somewhere?
No, I didn't see that but I think ddd doesn't support skinning mesh system.
I can't understand why we should know about ddd's structure.
I tested convert3d with my skinning model and non-skinning hierarchicaly animated 3ds model( only with transform by mesh group).
With Fragmotion, my model can be exported to both md2 and xof file.
md2 have geo-sprite data. xof contains Skinning Mesh data in this case.
3ds is from TheGameCreators. It has animation with mesh groups transform information.
With convert3d;
Test 1: md2 -> ddd :ok. ddd has animation.
Test 2: xof -> ddd:failed. convert3d refused to convert xof to ddd.
Test 3: 3ds ->ddd ok: ddd has animation.
But popular animation system on today is skinning mesh!
So I want Skinning mesh support.
Yes, that's an issue on my TODO list. Next thing to do, propably.
I think about a native B3D loader, since this format seems to be quite reasonable from its specs. The format internally is a total mess, though, that's why I haven't done this earlier.
Quote from: Kitty Hello on 2009-Aug-11
Yes, that's an issue on my TODO list. Next thing to do, propably.
I think about a native B3D loader, since this format seems to be quite reasonable from its specs. The format internally is a total mess, though, that's why I haven't done this earlier.
Yepp, B3D-Format have a realy smal filesize, and have bones!
Is there a reason for not implementing COLLADA for example? It's industry standard, open-source file format.
For those who dislike having their resources in open format file, it can be encrypted, or whatever.
Blender is about to get full Collada suppor, next-gen Lightwave is transferring from its proprietary format to Collada as its main file format, 3dsMax and Maya already support it, most other game engines on the market also support it, etc.
B3D is just another closed format which will reach its limit eventually, probably already did. And support for B3D on non-Windows platforms is doubtful, to say the least.
B3D is not so closed format. B3D's spec is officially opened at http://www.blitzbasic.com/sdkspecs/sdkspecs/b3dfile_specs.txt (http://www.blitzbasic.com/sdkspecs/sdkspecs/b3dfile_specs.txt)
and extended format is allowed. As a result extended format exist here http://www.onigirl.com/pipeline/ (http://www.onigirl.com/pipeline/)
B3D is supported by irricht engine, gile, pace maker, ultimated unwrap3d, fragmotion, lightray3d, milkshape3d, etc.
It was designed originally for blitz3d compiler(DX7). I think it make sense for handheld devices.
I would love to see the B3D format supported. I have no artistic talent, and the models I bought a few years back are all in B3D format. I have no idea in how to get them converted to something GLBasic can use.
QuoteB3D is not so closed format. B3D's spec is officially opened at http://www.blitzbasic.com/sdkspecs/sdkspecs/b3dfile_specs.txt
That was for the original release. The B3D format "may" have changed a little bit since then.
Right. The B3D format is quite nice, since many programs support it. but the documentation is horrible to zero.
Is this page any help?
http://www.geocities.com/drago_blitz/B3dFormat.html (http://www.geocities.com/drago_blitz/B3dFormat.html)
Very nice. What is this:
float rotation[4] (http://www.geocities.com/drago_blitz/B3dFormat.html#KEYS)
I don't understand what these 4 floats are.
From that page (and I don't claim to know what this means, I just know how to use "find" and "copy/paste" :P )
QuoteQuaternions are used to specify general orientations. The first value is the quaternion 'w' value, the next 3 are the quaternion 'vector'. A 'null' rotation should be specified as 1,0,0,0.
From later in the file, the rotation values seem to be specified in radians.
Does that help?
From a quick search of the blitz forums... (again, might not be exactly what you're after but it seems relevant), here's two b3d load functions.
(Website http://www.blitzbasic.com/Community/posts.php?topic=42211#474790 (http://www.blitzbasic.com/Community/posts.php?topic=42211#474790))
; ID: 524
; Author: halo
; Date: 2002-12-07 15:52:04
; Title: LoadB3D() (updated)
; Description: Load a .b3d file, with access to all the texture data, and some other options.
;LoadB3D( file$, [parent], [texturedir$], [flags] )
;file$ - file to load
;parent (optional) - mesh parent.
;texturedir$ (optional) - directory to load textures from.
;flags (optional) - none yet.
;No support for animations or bones.
;Hierarchies now supported.
Function LoadB3D(file$,parent,texturedir$="",flags=0)
f=ReadFile(file)
If Not f Return
tag$=ReadStringN(f,3)
chunksize=ReadInt(f)
endchunkpos=chunksize+FilePos(f)
texbank=CreateBank()
brushbank=CreateBank()
vertbank=CreateBank()
trisbank=CreateBank()
If tag="BB3D" Return loadchunk("BB3D",f,endchunkpos,parent,texbank,brushbank,vertbank,trisbank,texturedir$)
For n=0 To (BankSize(texbank)-1)/4
texture=PeekInt(texbank,n*4)
If texture FreeTexture texture
Next
For n=0 To (BankSize(brushbank)-1)/4
brush=PeekInt(brushbank,n*4)
If brush FreeBrush brush
Next
FreeBank texbank
FreeBank brushbank
FreeBank vertbank
FreeBank trisbank
End Function
Function loadchunk(tag$,f,endchunkpos,parent=0,texbank,brushbank,vertbank,trisbank,texturedir$)
DebugLog tag
Select tag
Case "BB3D"
version=ReadInt(f)
DebugLog " version: "+version
Case "TEXS"
name$=ReadStringN(f)
name=findfile(name,texturedir$)
flags=ReadInt(f)
blend=ReadInt(f)
x#=ReadFloat(f)
y#=ReadFloat(f)
scalex#=ReadFloat(f)
scaley#=ReadFloat(f)
rotation#=ReadFloat(f)
texture=LoadTexture(name,flags)
If texture
If 65536 And flags TextureCoords texture,1
TextureBlend texture,blend
PositionTexture texture,x,y
ScaleTexture texture,scalex,scaley
RotateTexture texture,rotation
EndIf
ResizeBank texbank,BankSize(texbank)+4
PokeInt(texbank,BankSize(texbank)-4,texture)
DebugLog " name: "+name
DebugLog " flags: "+flags
DebugLog " blend: "+blend
DebugLog " position: "+x+", "+y
DebugLog " scale: "+scalex+", "+scaley
DebugLog " rotation: "+rotation
Case "BRUS"
textures=ReadInt(f)
name$=ReadStringN(f)
red=ReadFloat(f)*255
green=ReadFloat(f)*255
blue=ReadFloat(f)*255
alpha#=ReadFloat(f)
shininess#=ReadFloat(f)
blend=ReadInt(f)
fx=ReadInt(f)
brush=CreateBrush()
BrushColor brush,red,green,blue
BrushAlpha brush,alpha
BrushShininess brush,shininess
BrushBlend brush,blend
BrushFX brush,fx
For n=1 To textures
textureindex=ReadInt(f)
If textureindex*4+4<=BankSize(texbank)
texture=PeekInt(texbank,textureindex*4)
Else
RuntimeError "Texture does not exist."
EndIf
If texture
BrushTexture brush,texture,0,n-1
EndIf
Next
ResizeBank brushbank,BankSize(brushbank)+4
PokeInt brushbank,BankSize(brushbank)-4,brush
;DebugLog " name: "+name
;DebugLog " color: "+red+", "+green+", "+blue
;DebugLog " alpha: "+alpha
;DebugLog " shininess: "+shininess
;DebugLog " blend: "+blend
;DebugLog " fx: "+fx
Case "NODE"
lastmeshendchunkpos=endchunkpos
name$=ReadStringN(f)
DebugLog name
x#=ReadFloat(f)
y#=ReadFloat(f)
z#=ReadFloat(f)
width#=ReadFloat(f)
height#=ReadFloat(f)
depth#=ReadFloat(f)
w#=ReadFloat(f)
pitch#=ReadFloat(f)
yaw#=ReadFloat(f)
roll#=ReadFloat(f)
;DebugLog " name: "+name
;DebugLog " position: "+x+", "+y+", "+z
;DebugLog " scale: "+width+", "+height+", "+depth
;DebugLog " rotation: "+pitch+", "+yaw+", "+roll+", "+w
;If FilePos(f)=endchunkpos
mesh=CreateMesh(parent);this is more compatible than inserting a pivot.
parent=mesh
NameEntity mesh,name
PositionEntity mesh,x,y,z
ScaleEntity mesh,width,height,depth
RotateEntity mesh,pitch,yaw,roll;how do b3d rotations work?
; EndIf
Case "MESH"
brush=ReadInt(f)
;mesh=CreateMesh(parent)
;parent=mesh
;NameEntity mesh,name
;PositionEntity mesh,x#,y#,z#
;ScaleEntity mesh,width,height,depth
;RotateEntity mesh,pitch,yaw,roll;how do b3d rotations work?
Case "VRTS"
flags=ReadInt(f)
texcoordsets=ReadInt(f)
texcoords=ReadInt(f)
;DebugLog " flags: "+flags
;DebugLog " texturecoord sets: "+texcoordsets
;DebugLog " texturecoords:"+texcoords
ResizeBank vertbank,0
While FilePos(f)<endchunkpos
x#=ReadFloat(f)
y#=ReadFloat(f)
z#=ReadFloat(f)
;TFormPoint x,y,z,0,parent
;x=TFormedX()
;y=TFormedY()
;z=TFormedZ()
If texcoords>0
u0#=ReadFloat(f)
If texcoords>1
v0#=ReadFloat(f)
If texcoords>2
w0#=ReadFloat(f)
EndIf
EndIf
EndIf
If texcoordsets>1
If texcoords>0
u1#=ReadFloat(f)
If texcoords>1
v1#=ReadFloat(f)
If texcoords>2
w1#=ReadFloat(f)
EndIf
EndIf
EndIf
EndIf
;DebugLog " position: "+x+", "+y+", "+z
;DebugLog " texturecoords:"+u0+", "+v0+", "+w0
;DebugLog " texturecoords:"+u1+", "+v1+", "+w1
;DebugLog ""
ResizeBank vertbank,BankSize(vertbank)+4*10
PokeFloat vertbank,BankSize(vertbank)-4*10,x
PokeFloat vertbank,BankSize(vertbank)-4*9,y
PokeFloat vertbank,BankSize(vertbank)-4*8,z
PokeFloat vertbank,BankSize(vertbank)-4*7,u0
PokeFloat vertbank,BankSize(vertbank)-4*6,v0
PokeFloat vertbank,BankSize(vertbank)-4*5,w0
PokeFloat vertbank,BankSize(vertbank)-4*4,u1
PokeFloat vertbank,BankSize(vertbank)-4*3,v1
PokeFloat vertbank,BankSize(vertbank)-4*2,w1
PokeInt vertbank,BankSize(vertbank)-4*1,0
Wend
Case "TRIS"
brush=ReadInt(f)
;DebugLog " brush: "+brush
ResizeBank trisbank,0
For n=1 To (BankSize(vertbank))/(4*10)
PokeInt vertbank,36+(n-1)*(4*10),0
Next
While FilePos(f)<endchunkpos
a=ReadInt(f)
b=ReadInt(f)
c=ReadInt(f)
If a*(4*10)<BankSize(vertbank) PokeByte vertbank,36+a*(4*10),1 Else RuntimeError "Vertex does not exist."
If b*(4*10)<BankSize(vertbank) PokeByte vertbank,36+b*(4*10),1 Else RuntimeError "Vertex does not exist."
If c*(4*10)<BankSize(vertbank) PokeByte vertbank,36+c*(4*10),1 Else RuntimeError "Vertex does not exist."
;DebugLog " vertices: "+a+", "+b+", "+c
ResizeBank trisbank,BankSize(trisbank)+12
PokeInt trisbank,BankSize(trisbank)-12,a
PokeInt trisbank,BankSize(trisbank)-8,b
PokeInt trisbank,BankSize(trisbank)-4,c
triscount=triscount+1
Wend
If triscount
surf=CreateSurface(parent)
If brush>-1
If brush*4+4<=BankSize(brushbank)
PaintSurface surf,PeekInt(brushbank,brush*4)
Else
RuntimeError "Brush does not exist."
EndIf
EndIf
EndIf
vertcount=0
For n=1 To (BankSize(vertbank))/(4*10)
If PeekInt(vertbank,36+(n-1)*(4*10))
x#=PeekFloat(vertbank,(n-1)*(4*10))
y#=PeekFloat(vertbank,4+(n-1)*(4*10))
z#=PeekFloat(vertbank,8+(n-1)*(4*10))
u0#=PeekFloat(vertbank,12+(n-1)*(4*10))
v0#=PeekFloat(vertbank,16+(n-1)*(4*10))
w0#=PeekFloat(vertbank,20+(n-1)*(4*10))
u1#=PeekFloat(vertbank,24+(n-1)*(4*10))
v1#=PeekFloat(vertbank,28+(n-1)*(4*10))
w1#=PeekFloat(vertbank,32+(n-1)*(4*10))
AddVertex surf,x,y,z,u0,v0,w0
VertexTexCoords surf,CountVertices(surf)-1,u1,v1,w1,1
vertcount=vertcount+1
PokeInt vertbank,36+(n-1)*(4*10),vertcount
EndIf
Next
For n=1 To (BankSize(trisbank))/12
a=PeekInt(trisbank,(n-1)*12)
b=PeekInt(trisbank,4+(n-1)*12)
c=PeekInt(trisbank,8+(n-1)*12)
For i=1 To (BankSize(vertbank))/(4*10)
If i-1=a a=PeekInt(vertbank,36+(i-1)*(4*10))-1
If i-1=b b=PeekInt(vertbank,36+(i-1)*(4*10))-1
If i-1=c c=PeekInt(vertbank,36+(i-1)*(4*10))-1
Next
AddTriangle surf,a,b,c
Next
UpdateNormals parent
Default
SeekFile f,endchunkpos
End Select
While FilePos(f)<endchunkpos
tag$=ReadStringN(f,3)
chunksize=ReadInt(f)
childendchunkpos=chunksize+FilePos(f)
m=loadchunk(tag,f,childendchunkpos,parent,texbank,brushbank,vertbank,trisbank,texturedir$)
If Not parent parent=m
Wend
Return parent
End Function
Function ReadStringN$(f,maxlength=0)
Repeat
ch=ReadByte(f)
If ch=0 Return t$
If maxlength
If Len(t$)=maxlength Return t$+Chr(ch)
EndIf
t$=t$+Chr$(ch)
Forever
End Function
Function countfiles(dirname$)
dir=ReadDir(dirname)
If Not dir RuntimeError dirname
n=-1
Repeat
n=n+1
Until NextFile(dir)=""
CloseDir dir
Return n
End Function
Function findfile$(file$,root$)
If FileType(root+file)=1 Return root+file
d=ReadDir(root)
For n=1 To countfiles(root)
tfile$=NextFile(d)
If FileType(root+tfile)=2
If tfile<>"." And tfile<>".."
test$=findfile(file,root+tfile+"\")
If test<>"" Return test
EndIf
EndIf
Next
CloseDir d
End Function
and
; ID: 866
; Author: jfk
; Date: 2003-12-24 01:23:50
; Title: SaveB3D
; Description: Save multisurface textured Mesh as .B3D
Graphics3D 640,480,32,2
SetBuffer BackBuffer()
meshname$="testsurf.3ds" ; Mesh to save... (one that is using textures)
; BTW: you should run this from inside the folder where the Mesh resides, unless you edit the
; Part labeled by "<<<<<<<<<<<<"
Include "b3dfile.bb"
; this file can be found here: <a href="http://www.blitzbasic.com/sdkspecs/sdkspecs/b3dfile_utils.zip" target="_blank">http://www.blitzbasic.com/sdkspecs/sdkspecs/b3dfile_utils.zip</a>
cam=CreateCamera()
TranslateEntity cam,0,0,-10
mesh=LoadMesh(meshname$)
Global c_surfs=CountSurfaces(mesh)
Print "Mesh "+meshname$+" has "+ c_surfs+" Surfaces, using the following textures:"
Dim c_surf(c_surfs)
Dim c_brush(c_surfs)
Dim c_tex(c_surfs)
Dim c_tex_name$(c_surfs)
; track down used textures (thanks Mark!)
For i=1 To c_surfs
c_surf(i)= GetSurface(mesh,i)
c_brush(i)=GetSurfaceBrush( c_surf(i) )
c_tex(i)=GetBrushTexture( c_brush(i) )
c_tex_name$(i)=Lower$(TextureName$( c_tex(i))) ; Full (!) Texture Path
curdir$=Lower$(CurrentDir$())
c_tex_name$(i)= Replace$(c_tex_name$(i),curdir$,"") ;<<<<<<<<<<<<<<<<<<<
Print c_tex_name$(i)
If c_tex_name$(i)="" Then Print "Error: Surface No."+i+" has no Texture"
If FileType(c_tex_name$(i))<>1 Then Print "Warning: Surface No."+i+" uses nonexistant Texture ("+c_tex_name$(i)+")."
Next
Print "Press any key to save this Mesh as TEMP.B3D"
WaitKey()
; end
WriteBB3D( "temp.b3d",mesh )
For i=1 To c_surfs
FreeBrush c_brush(i); release memory
FreeTexture c_tex(i)
Next
; test if it worked...
FreeEntity mesh
mesh2=LoadMesh("temp.b3d")
While Not KeyDown(1)
TurnEntity mesh2,1,2,3
RenderWorld()
Text 0,0,"TEMP.B3D"
Flip
Wend
End
Function WriteBB3D( f_name$,mesh )
file=WriteFile( f_name$ )
b3dSetFile( file )
b3dBeginChunk( "BB3D" )
b3dWriteInt( 1 ) ;version
b3dBeginChunk( "TEXS" ) ; list all textures used by the mesh
For i=1 To c_surfs
b3dWriteString( c_tex_name$(i) ) ;texture file
b3dWriteInt( 1 ) ;flags
b3dWriteInt( 2 ) ;blend
b3dWriteFloat( 0 ) ;x in tex 0 (hu?)
b3dWriteFloat( 0 ) ;y in tex 0
b3dWriteFloat( 1 ) ;x scale 1
b3dWriteFloat( 1 ) ;y scale 1
b3dWriteFloat( 0 ) ;rotation 0
Next
b3dEndChunk() ;end of TEXS chunk
For i=1 To c_surfs
b3dBeginChunk( "BRUS" ) ; describe all brushes used by the mesh
b3dWriteInt( 1 ) ;number of textures per brush ; (eg 2 with lightmap)
b3dWriteString( "brush"+(i-1) ) ;brushname
b3dWriteFloat( 1 ) ;red
b3dWriteFloat( 1 ) ;green
b3dWriteFloat( 1 ) ;blue
b3dWriteFloat( 1 ) ;alpha
b3dWriteFloat( 0 ) ;shininess
b3dWriteInt( 1 ) ;blendmode
b3dWriteInt( 0 ) ;FX
b3dWriteInt( i-1 ) ;used texture index
; b3dWriteInt( ? ) ;additional texture index (eg lightmap), but here we only use 1 (see above)
b3dEndChunk() ;end of BRUS chunk
Next
b3dBeginChunk( "NODE" )
b3dWriteString( "entity_name_here!" )
b3dWriteFloat( 0 ) ;x_pos
b3dWriteFloat( 0 ) ;y_pos
b3dWriteFloat( 0 ) ;z_pos
b3dWriteFloat( 1 ) ;x_scale
b3dWriteFloat( 1 ) ;y_scale
b3dWriteFloat( 1 ) ;z_scale
b3dWriteFloat( 1 ) ;rot_w
b3dWriteFloat( 0 ) ;rot_x
b3dWriteFloat( 0 ) ;rot_y
b3dWriteFloat( 0 ) ;rot_z
WriteMESH( mesh )
b3dEndChunk() ;end of NODE chunk
b3dEndChunk() ;end of BB3D chunk
CloseFile file
End Function
Function WriteMESH( mesh )
n_surfs=CountSurfaces( mesh )
b3dBeginChunk( "MESH" )
b3dWriteInt( -1 ) ;no 'entity' brush -1
b3dBeginChunk( "VRTS" )
b3dWriteInt( 0 ) ;flags - 0=no normal/color
b3dWriteInt( 1 ) ;number of tex_coord sets (eg: 2 with lightmap)
b3dWriteInt( 2 ) ;coords per set (u,v,w?) 2 with uv, 3 with uvw
For k=1 To n_surfs
surf=GetSurface( mesh,k )
n_verts=CountVertices( surf )-1
For j=0 To n_verts
b3dWriteFloat( VertexX( surf,j ) )
b3dWriteFloat( VertexY( surf,j ) )
b3dWriteFloat( VertexZ( surf,j ) )
b3dWriteFloat( VertexU#( surf,j,0 ) )
b3dWriteFloat( VertexV#( surf,j,0 ) )
; b3dWriteFloat( VertexW#( surf,j,0 ) )
;; b3dWriteFloat( VertexU#( surf,j,1 ) ) ; lightmap uv
;; b3dWriteFloat( VertexV#( surf,j,1 ) ) ; lightmap uv
; b3dWriteFloat( VertexW#( surf,j,1 ) )
Next
Next
b3dEndChunk() ;end of VRTS chunk
first_vert=0
For k=1 To n_surfs
surf=GetSurface( mesh,k )
n_tris=CountTriangles( surf )-1
b3dBeginChunk( "TRIS" )
b3dWriteInt( k-1 ) ;brush for these triangles (surf -1 !!!)
For j=0 To n_tris
b3dWriteInt( first_vert+TriangleVertex( surf,j,0 ) )
b3dWriteInt( first_vert+TriangleVertex( surf,j,1 ) )
b3dWriteInt( first_vert+TriangleVertex( surf,j,2 ) )
Next
b3dEndChunk() ;end of TRIS chunk
first_vert=first_vert+CountVertices( surf )
Next
b3dEndChunk() ;end of MESH chunk
End Function
;-------------------------------------------------------------------------------------------------
From that code it appears to be rotate w,x,y,z - though what the heck the w dimension is I don't know... If I find anything that might explain it I'll post it here.
A bit more digging seems to have gotten an answer - Kitty Hello, let me know if this isn't sufficient information.
http://www.blitzbasic.com/Community/posts.php?topic=62584#699300 (http://www.blitzbasic.com/Community/posts.php?topic=62584#699300)
Quotewhat is the fourth rotation for the bones ?
The rotation has 4 values because they are stored as Quaternions not Euler angles. Quaternions represent an arbitrary rotation around a fixed axis and do not suffer the gimbal-lock problem of eulers. It is also very easy to interpolate or "slerp" between Quat values which makes it ideal for animation keys.
It seems like Mark was making things more difficult than they need to be when he designed the B3D format?
It's a total mess. But here's the solution to the quaternion thing:
Q54. How do I convert a quaternion to a rotation matrix?
--------------------------------------------------------
Assuming that a quaternion has been created in the form:
Q = |X Y Z W|
Then the quaternion can then be converted into a 4x4 rotation
matrix using the following expression (Warning: you might have to
transpose this matrix if you (do not) follow the OpenGL order!):
¦ 2 2 ¦
¦ 1 - (2Y + 2Z ) 2XY + 2ZW 2XZ - 2YW ¦
¦ ¦
¦ 2 2 ¦
M = ¦ 2XY - 2ZW 1 - (2X + 2Z ) 2YZ + 2XW ¦
¦ ¦
¦ 2 2 ¦
¦ 2XZ + 2YW 2YZ - 2XW 1 - (2X + 2Y ) ¦
¦ ¦
If a 4x4 matrix is required, then the bottom row and right-most column
may be added.
The matrix may be generated using the following expression:
xx = X * X;
xy = X * Y;
xz = X * Z;
xw = X * W;
yy = Y * Y;
yz = Y * Z;
yw = Y * W;
zz = Z * Z;
zw = Z * W;
mat[0] = 1 - 2 * ( yy + zz );
mat[1] = 2 * ( xy - zw );
mat[2] = 2 * ( xz + yw );
mat[4] = 2 * ( xy + zw );
mat[5] = 1 - 2 * ( xx + zz );
mat[6] = 2 * ( yz - xw );
mat[8] = 2 * ( xz - yw );
mat[9] = 2 * ( yz + xw );
mat[10] = 1 - 2 * ( xx + yy );
mat[3] = mat[7] = mat[11] = mat[12] = mat[13] = mat[14] = 0;
mat[15] = 1;
The resulting matrix uses the following positions:
¦ mat[0] mat[4] mat[ 8] mat[12] ¦
M = ¦ mat[1] mat[5] mat[ 9] mat[13] ¦
¦ mat[2] mat[6] mat[10] mat[14] ¦
¦ mat[3] mat[7] mat[11] mat[15] ¦
OK, might be worth a try now.
:grin: nice!
On the Blitz site miniB3D is free to download and open source and contains up to date loading routines for the B3D format.
yepp... maybe i give it a try :good: