Blender -> Blitz (source inside)

Blitz3D Forums/Blitz3D Programming/Blender -> Blitz (source inside)

Shagwana(Posted 2003) [#1]
Hi again,

This is my latest on the blender exporter. It comes in two parts; a python script that exports the current
selected mesh from Blender. And a blitz program to load and display that mesh!.
It does't really do much other then import a create a optimized blitz mesh (welding verts where possible).
Support for vertex color and uv mapping is in there and works. Support for vertex normals should also work
(untested). Textures that are used need to be copyed to the location that the blitz program is run from.
I release this in the hope that someone will find it usefull!. My next step for this project should be a .b3d
exporter (with some luck) :-)

The Python script.
#### [ Blender -> bb3d exporter ] ####
"""

   Author : Stephen Greener (aka Shagwana)
  Purpose : Export selected mesh data to a file that can be converted into a .bb3d file


"""
####[ Imports ]####

from Blender import *
import struct


####[ Consts ]####

TRUE=1
FALSE=0


####[ Varibles ]####


SaveFileName="C:\\BlitzExport.dat"    # The file that the data will be saved too


global OutputFile                 # File pointer to the above file while saving

####[ Procedures ]####

# Export this very polygon, and all related information!
def ExportPolygon(f,Obj,ObjData,Face,v1,v2,v3):
  Vert1=Face.v[v1].index       # Face vertex reference information...
  Vert2=Face.v[v2].index
  Vert3=Face.v[v3].index

  #Vertex information
  Coord1=ObjData.verts[Vert1].co     # Coord information of this vertex
  Coord2=ObjData.verts[Vert2].co 
  Coord3=ObjData.verts[Vert3].co 
  Normal1=ObjData.verts[Vert1].no    # Normal information
  Normal2=ObjData.verts[Vert2].no
  Normal3=ObjData.verts[Vert3].no

  #Vertex color information
  if Face.col:					# Color information of the verts
    Color1=[Face.col[v1].r,Face.col[v1].g,Face.col[v1].b,Face.col[v1].a]
    Color2=[Face.col[v2].r,Face.col[v2].g,Face.col[v2].b,Face.col[v2].a]
    Color3=[Face.col[v3].r,Face.col[v3].g,Face.col[v3].b,Face.col[v3].a]
    bHasVC=TRUE
  else:
    Color1,Color2,Color3=[255,255,255,255],[255,255,255,255],[255,255,255,255]
    bHasVC=FALSE

  #UV information
  bHasUV=FALSE
  UV1,UV2,UV3=[0,1],[1,0],[1,1]	# Default values for uv info
  UVName=[]						# Default is no texture!
  if ObjData.hasFaceUV():
    if Face.image:				# Theres a texture to use!
      UVName=Face.image.name	# Grabs the name
      UV1=Face.uv[v1]			# Grab the uv coords!
      UV2=Face.uv[v2]
      UV3=Face.uv[v3]
      bHasUV=TRUE
  
  #Coord information...
  f.write(struct.pack("f",Coord1[0]))  #X,Y,Z coords (reletive to the center)..
  f.write(struct.pack("f",Coord1[1]))
  f.write(struct.pack("f",Coord1[2]))
  f.write(struct.pack("f",Coord2[0]))  #X,Y,Z coords (reletive to the center)..
  f.write(struct.pack("f",Coord2[1]))
  f.write(struct.pack("f",Coord2[2]))
  f.write(struct.pack("f",Coord3[0]))  #X,Y,Z coords (reletive to the center)..
  f.write(struct.pack("f",Coord3[1]))
  f.write(struct.pack("f",Coord3[2]))
  #Normals...
  f.write(struct.pack("f",Normal1[0]))  #X,Y,Z normal 
  f.write(struct.pack("f",Normal1[1]))
  f.write(struct.pack("f",Normal1[2]))
  f.write(struct.pack("f",Normal2[0]))  #X,Y,Z normal 
  f.write(struct.pack("f",Normal2[1]))
  f.write(struct.pack("f",Normal2[2]))
  f.write(struct.pack("f",Normal3[0]))  #X,Y,Z normal 
  f.write(struct.pack("f",Normal3[1]))
  f.write(struct.pack("f",Normal3[2]))  
  #Vertex color
  if bHasVC==TRUE:
    f.write(struct.pack("L",1))         #Yes, there is a set vertex color!
    f.write(struct.pack("f",Color1[0])) #Red 
    f.write(struct.pack("f",Color1[1])) #Green
    f.write(struct.pack("f",Color1[2])) #Blue
    f.write(struct.pack("f",Color1[3])) #Alpha
    f.write(struct.pack("f",Color2[0])) #Red 
    f.write(struct.pack("f",Color2[1])) #Green
    f.write(struct.pack("f",Color2[2])) #Blue
    f.write(struct.pack("f",Color2[3]))
    f.write(struct.pack("f",Color3[0])) #Red 
    f.write(struct.pack("f",Color3[1])) #Green
    f.write(struct.pack("f",Color3[2])) #Blue
    f.write(struct.pack("f",Color3[3]))
  else:
    f.write(struct.pack("L",0))         #No vertex color set, use a default color!
  #Vertex uv texture mapping
  if bHasUV==TRUE:
    f.write(struct.pack("L",1))         #Yes there is UV mapping information
    f.write(UVName+chr(0))              #Name of texture that this polygon uses
    f.write(struct.pack("f",UV1[0]))    #U 
    f.write(struct.pack("f",UV1[1]))    #V 
    f.write(struct.pack("f",UV2[0]))    #U 
    f.write(struct.pack("f",UV2[1]))    #V 
    f.write(struct.pack("f",UV3[0]))    #U 
    f.write(struct.pack("f",UV3[1]))    #V    
  else:
    f.write(struct.pack("L",0))         #No uv mapping information

# This will loop through the mesh and export all the polygons (3 vert and 4 verts)
def ExportMesh(Obj,f):
  if Obj:
    f.write(Obj.name+chr(0))    # Save the name of this mesh first!
    iPolyCountPos=f.tell()
    f.write(struct.pack("L",0)) # How many polygons (dummy value)
    ObjData=Obj.getData()
    iPolygonCount=0
    if ObjData:  
      for face in ObjData.faces:
        Size=len(face.v)
        if Size==3:
          ExportPolygon(f,Obj,ObjData,face,0,1,2)
          iPolygonCount=iPolygonCount+1
        elif Size==4:
          ExportPolygon(f,Obj,ObjData,face,0,1,2)
          ExportPolygon(f,Obj,ObjData,face,2,3,0)
          iPolygonCount=iPolygonCount+2
    iCurrentPos=f.tell()
    f.seek(iPolyCountPos)
    f.write(struct.pack("L",iPolygonCount))    # How many polygons
    f.seek(iCurrentPos)     

####[ Main program ]####


print ""
print "Exporter started."

#If the user has selected a object to work on...
if Object.GetSelected()[0]:
  OutputFile=open(SaveFileName,'w+b')
  if OutputFile:
    OutputFile.write(struct.pack(">4s","B2BE"))  #File header 'Blender To Blitz Exporter'
    ExportMesh(Object.GetSelected()[0],OutputFile)
    OutputFile.close()
  
print "Exporter ended."



The Blitz program.

; Coded by Stephen Greener (aka Shagwana)
; Load and display a blender exported mesh
;



;The location of the temp file to load the information from...

Const INPUTFILE$="C:\BlitzExport.dat"   ;The source file to build from


;Shared globals!
Global sMeshName$=""         ;Name od the loaded mesh!
Global iMeshPolys=0

;Imported basic polygons
Type PolygonInfo

  Field fX1#,fY1#,fZ1#     ;Coords
  Field fX2#,fY2#,fZ2#
  Field fX3#,fY3#,fZ3#

  Field fNX1#,fNY1#,fNZ1#  ;Normals [not used yet!]
  Field fNX2#,fNY2#,fNZ2#
  Field fNX3#,fNY3#,fNZ3#

  Field bVCPresent           ;Vertex colors present
  Field fR1#,fG1#,fB1#,fA1#  ;[Alpha not used yet]
  Field fR2#,fG2#,fB2#,fA2#
  Field fR3#,fG3#,fB3#,fA3#

  Field bUVPresent           ;Texture mapping present
  Field sTextureName$
  Field fU1#,fV1#
  Field fU2#,fV2#
  Field fU3#,fV3#

  ;Processing varibles 
  Field tTexture.TextureInfo         ;Only used when
  Field tVert1.MasterVertexInfo      ;When placed into the master vertex list, this reference's them!
  Field tVert2.MasterVertexInfo
  Field tVert3.MasterVertexInfo

  End Type


;The merged master vertex list for this mesh
Type MasterVertexInfo

  ;This vertex information
  Field fXCoord#,fYCoord#,fZCoord#       ;Coords
  Field fXNormal#,fYNormal#,fZNormal#    ;Normals
  Field bVCPresent,fRed#,fGreen#,fBlue#  ;Vertex colors
  Field bUVPresent,fU#,fV#               ;Texture mapping
  Field tTexture.TextureInfo             ;The surface used

  ;Used to show this vertex's 
  Field iIndex

  End Type


;Shared texture id's - to make brushes out of
Type TextureInfo
  Field sTextureName$     ;Name of this texture
  Field pSurface          ;Pointer to this ones surface
  End Type


tPolygon.PolygonInfo = Null
tVertex.MasterVertexInfo = Null
tTexture.TextureInfo = Null


;This will place the texture name into the list
Function PlaceTexture.TextureInfo(sTextName$="")
  ;See if this texture matches an existing texture
  For tTexture.TextureInfo= Each TextureInfo
    If tTexture\sTextureName$=sTextName$
      ;Matches
      Return tTexture.TextureInfo
      EndIf
    Next
  ;If here then need to append this texture
  tTexture.TextureInfo= New TextureInfo
  tTexture\sTextureName$=sTextName$
  tTexture\pSurface=0   ;Build these in a moment
  Return tTexture.TextureInfo
  End Function

;Scan all polygons, return a surface that its going to be placed into
Function BuildMasterTextureList(pMesh)
  ;Scan all textures - make them 
  For tPolygon.PolygonInfo = Each PolygonInfo
    If tPolygon\bUVPresent=True
      ;Texture present
      tPolygon\tTexture.TextureInfo=PlaceTexture(tPolygon\sTextureName$)
      Else
      ;No texture, vc mesh!
      tPolygon\tTexture.TextureInfo=PlaceTexture()
      EndIf
    Next 
  End Function


;This will load textures and create the surfaces required
Function BuildSurfaces(pMesh)
  For tTexture.TextureInfo= Each TextureInfo
    If tTexture\sTextureName$<>""
      ;Textures present on this surface
      pBrush=LoadBrush(tTexture\sTextureName$,49)
      tTexture\pSurface=CreateSurface(pMesh,pBrush)
      FreeBrush pBrush
      Else
      ;This is a non textured surface
      tTexture\pSurface=CreateSurface(pMesh)
      EndIf
    Next
  End Function




;Place a single vertex into the list (returns the verex pointer)
Function PlaceVertex.MasterVertexInfo(fX#,fY#,fZ#,fNX#,fNY#,fNZ#,bVC,fR#,fG#,fB#,bUV,tTexture.TextureInfo,fU#,fV#)

  For tVertex.MasterVertexInfo = Each MasterVertexInfo
    ;Compare this vertex - if match, return its iID (postion)
    If (tVertex\fXCoord#=fX#) And (tVertex\fYCoord#=fY#) And (tVertex\fZCoord#=fZ#) And (tVertex\fXNormal#=fNX#) And (tVertex\fYNormal#=fNY#) And (tVertex\fZNormal#=fNZ#)
      ;Coords match

      bMatch=True 

      If bVC=True
        If (tVertex\fRed#=fR#) And (tVertex\fGreen#=fG#) And (tVertex\fBlue#=fB#)
          bMatch=True
          Else
          bMatch=False
          EndIf
        EndIf
      If bMatch=True
        If tVertex\bVCPresent<>bVC
          ;One has the other dont
          bMatch=False
          EndIf
        EndIf

      If bMatch=True
        If bUV=True 
            If (tVertex\tTexture\sTextureName$=tTexture\sTextureName$) And (tVertex\fU#=fU#) And (tVertex\fV#=fV#)
            bMatch=True
            Else
            bMatch=False
            EndIf
          EndIf
        EndIf
      If bMatch=True
        If tVertex\bUVPresent<>bUV
          bMatch=False
          EndIf
        EndIf

      If bMatch=True 
        ;These verts match
        Return tVertex.MasterVertexInfo  
        EndIf

      EndIf
    Next

  ;This appends a new vertex to the list
  tVertex.MasterVertexInfo = New MasterVertexInfo
  tVertex\fXCoord#=fX# 
  tVertex\fYCoord#=fY#
  tVertex\fZCoord#=fZ#
  tVertex\bVCPresent=bVC
  tVertex\fRed#=fR#
  tVertex\fGreen#=fG#
  tVertex\fBlue#=fB#
  tVertex\bUVPresent=bUV
  tVertex\fU#=fU#
  tVertex\fV#=fV#
  tVertex\fXNormal#=fNX#   ;Normals
  tVertex\fYNormal#=fNY#
  tVertex\fZNormal#=fNZ#
  tVertex\tTexture.TextureInfo=tTexture.TextureInfo

  Return tVertex.MasterVertexInfo 
  End Function


;Scan the polygons, and make them refernce a single vertex list!
Function BuildMasterVertexList()
  For tPolygon.PolygonInfo = Each PolygonInfo
    ;Merge these verts into the list    
    tPolygon\tVert1.MasterVertexInfo=PlaceVertex(tPolygon\fX1#,tPolygon\fY1#,tPolygon\fZ1#,tPolygon\fNX1#,tPolygon\fNY1#,tPolygon\fNZ1#,tPolygon\bVCPresent,tPolygon\fR1#,tPolygon\fG1#,tPolygon\fB1#,tPolygon\bUVPresent,tPolygon\tTexture.TextureInfo,tPolygon\fU1#,tPolygon\fV1#)
    tPolygon\tVert2.MasterVertexInfo=PlaceVertex(tPolygon\fX2#,tPolygon\fY2#,tPolygon\fZ2#,tPolygon\fNX2#,tPolygon\fNY2#,tPolygon\fNZ2#,tPolygon\bVCPresent,tPolygon\fR2#,tPolygon\fG2#,tPolygon\fB2#,tPolygon\bUVPresent,tPolygon\tTexture.TextureInfo,tPolygon\fU2#,tPolygon\fV2#)
    tPolygon\tVert3.MasterVertexInfo=PlaceVertex(tPolygon\fX3#,tPolygon\fY3#,tPolygon\fZ3#,tPolygon\fNX3#,tPolygon\fNY3#,tPolygon\fNZ3#,tPolygon\bVCPresent,tPolygon\fR3#,tPolygon\fG3#,tPolygon\fB3#,tPolygon\bUVPresent,tPolygon\tTexture.TextureInfo,tPolygon\fU3#,tPolygon\fV3#)
    Next
  End Function 


;This will place the vers in the correct surfaces!
Function MakeObj(pMesh)
  ;Place verts first
  For tVertex.MasterVertexInfo = Each MasterVertexInfo
    tVertex\iIndex=AddVertex(tVertex\tTexture\pSurface,tVertex\fXCoord#,tVertex\fYCoord#,tVertex\fZCoord#,tVertex\fU#,tVertex\fV#)
    VertexNormal tVertex\tTexture\pSurface,tVertex\iIndex,tVertex\fXNormal#,tVertex\fYNormal#,tVertex\fZNormal#
    If tVertex\bVCPresent=True
      VertexColor tVertex\tTexture\pSurface,tVertex\iIndex,tVertex\fRed#,tVertex\fGreen#,tVertex\fBlue#
      EndIf
    Next

  ;Now add in the triangles
  For tPolygon.PolygonInfo = Each PolygonInfo
    AddTriangle tPolygon\tTexture\pSurface,tPolygon\tVert1\iIndex,tPolygon\tVert2\iIndex,tPolygon\tVert3\iIndex
    Next
  End Function


;This will import the selected file, and return a optimized blitz mesh (some calculations are done)
Function ImportMesh(BlenderFilename$)
  pMesh=0   

  If LoadPolys(BlenderFilename$)=True
    pMesh=CreateMesh()

    ;Polygons loaded into a temp format
    BuildMasterTextureList(pMesh)
    ;<sort textures into order>
    BuildSurfaces(pMesh)
    BuildMasterVertexList()
    MakeObj(pMesh)
    EntityFX pMesh,2
    ScaleMesh pMesh,0.2,0.2,0.2
    RotateMesh pMesh,-135,0,0

    EndIf

  Return pMesh
  End Function


;Read from stream null terminated string....
Function ReadNullTerminatedString$(pF)
  Msg$=""
  Repeat
    b=ReadByte(pF)
    If b<>0 
      Msg$=Msg$+Chr(b)
      EndIf
    Until b=0
  Return Msg$
  End Function

;Read from stream 4byte string (file headers)
Function Read4CharString$(pF)
  Msg$=Chr(ReadByte(pF))+Chr(ReadByte(pF))+Chr(ReadByte(pF))+Chr(ReadByte(pF))
  Return Msg$
  End Function


;This imports the needed information that was exported from blender
Function LoadPolys(Filename$)
  bLoaded=False  
  pF=ReadFile(Filename$)
  If pF<>0
    If Read4CharString$(pF)="B2BE"    ;Blender 2 Blitz Exporter!
      sMeshName$=ReadNullTerminatedString$(pF)    ;Name of the mesh
      bDone=False
      iMeshPolys=ReadInt(pF)    ;Number of polygons in this file!
      For p=0 To iMeshPolys
        bLoaded=True           
        ;Read this polygon in from the file
        tPolygon.PolygonInfo = New PolygonInfo
        ;Load coords for the polys verts
        tPolygon\fX1#=ReadFloat(pF)
        tPolygon\fY1#=ReadFloat(pF)
        tPolygon\fZ1#=ReadFloat(pF)
        tPolygon\fX2#=ReadFloat(pF)
        tPolygon\fY2#=ReadFloat(pF)
        tPolygon\fZ2#=ReadFloat(pF)
        tPolygon\fX3#=ReadFloat(pF)
        tPolygon\fY3#=ReadFloat(pF)
        tPolygon\fZ3#=ReadFloat(pF)
        ;Load the normals, per poly verts
        tPolygon\fNX1#=ReadFloat(pF)
        tPolygon\fNY1#=ReadFloat(pF)
        tPolygon\fNZ1#=ReadFloat(pF)
        tPolygon\fNX2#=ReadFloat(pF)
        tPolygon\fNY2#=ReadFloat(pF)
        tPolygon\fNZ2#=ReadFloat(pF)
        tPolygon\fNX3#=ReadFloat(pF)
        tPolygon\fNY3#=ReadFloat(pF)
        tPolygon\fNZ3#=ReadFloat(pF)
        ;Load in the vertex colors
        If ReadInt(pF)=1
          tPolygon\bVCPresent=True
          tPolygon\fR1#=ReadFloat(pF)
          tPolygon\fG1#=ReadFloat(pF)
          tPolygon\fB1#=ReadFloat(pF)
          tPolygon\fA1#=ReadFloat(pF)
          tPolygon\fR2#=ReadFloat(pF)
          tPolygon\fG2#=ReadFloat(pF)
          tPolygon\fB2#=ReadFloat(pF)
          tPolygon\fA2#=ReadFloat(pF)
          tPolygon\fR3#=ReadFloat(pF)
          tPolygon\fG3#=ReadFloat(pF)
          tPolygon\fB3#=ReadFloat(pF)
          tPolygon\fA3#=ReadFloat(pF)
          Else
          tPolygon\bVCPresent=False
          EndIf
        ;Load in texture mapping
        If ReadInt(pF)=1
          tPolygon\bUVPresent=True
          tPolygon\sTextureName$=ReadNullTerminatedString$(pF)
          tPolygon\fU1#=ReadFloat(pF)
          tPolygon\fV1#=ReadFloat(pF)
          tPolygon\fU2#=ReadFloat(pF)
          tPolygon\fV2#=ReadFloat(pF)
          tPolygon\fU3#=ReadFloat(pF)
          tPolygon\fV3#=ReadFloat(pF)
          Else
          tPolygon\bUVPresent=False
          tPolygon\sTextureName$=""
          EndIf
        Next
      EndIf
    CloseFile(pF)
    EndIf
  Return bLoaded
  End Function


;Count all verts in a given mesh
Function CountVertsInMesh(pMesh)
  S=CountSurfaces(pMesh)
  T=0
  For n=1 To S
    T=T+CountVertices(GetSurface(pMesh,n))
    Next
  Return T
  End Function 


;The test application...
Graphics3D 800,600,32,0
SetBuffer BackBuffer()
pCamera=CreateCamera()            ;Our eye
pLight=CreateLight()              ;A light!

pObj=ImportMesh(INPUTFILE$)       ;Load the mesh in
If pObj<>0 Then PositionEntity pObj,0,0,5

;Temp
fXView#=0.0
fYView#=0.0
fZView#=0.0

CameraClsColor pCamera,60,70,90
While Not KeyDown( 1 )
  ;Simple viewing of the created mesh using the mouse

  If KeyDown(75) ;num_4
    fXView#=fXView#-5    
    If fXView#<-50 Then fXView#=-50
    EndIf

  If KeyDown(77) ;num_6
    fXView#=fXView#+5    
    If fXView#>50 Then fXView#=50
    EndIf

  If KeyDown(72) ;num_8
    fYView#=fYView#-5    
    If fYView#<-50 Then fYView#=-50
    EndIf

  If KeyDown(80) ;num_2
    fYView#=fYView#+5    
    If fYView#>50 Then fYView#=50
    EndIf

  fXView#=fXView#*0.9
  fYView#=fYView#*0.9
  fZView#=fZView#*0.9
  If pObj<>0 Then TurnEntity pObj,0.01*fYView#,0.01*fXView#,0.01*fZView#

  ;Update the display
  RenderWorld

  Color 0,0,255
  Text 0,0," Mesh: "+sMeshName$+"   Polys in file: "+iMeshPolys
  Text 0,16," Tris: "+TrisRendered()+"  Verts:"+CountVertsInMesh(pObj)

  n=0
  For tTexture.TextureInfo= Each TextureInfo
    n=n+1
    If tTexture\sTextureName$=""
      Text 100,((16*n)+32),n+" -> [vertex color surface]"
      Else
      Text 100,((16*n)+32),n+" -> "+tTexture\sTextureName$
      EndIf
    Next

  Flip
  Cls

  Wend
End




elias_t(Posted 2003) [#2]
Cool :)

thx. I wil give it a try ;


Litobyte(Posted 2003) [#3]
Hey Cool!
Amazing! I dream to do in Blender Editor all the things, and have then a full ODE (dynamic) scene :)))

Remember to consider to add (in a future) such an exporting option! (maybe in format of .bb code)

cya!


Rob(Posted 2003) [#4]
*bump*

If you have a working export path from blender to blitz, why not give mark a shout, to host it on the sdk page...


JoeGr(Posted 2003) [#5]
I am in your debt sir.


Kid Tripod(Posted 2003) [#6]
Good stuff, I've been using the .X exporter up to now.

I don't suppose theres any chance of getting this to work with vertex animations/armatures is there?


Shagwana(Posted 2003) [#7]
If you have a working export path from blender to blitz

Its not ready yet, i hope to have a b3d version working one day - then, that day will be the day :)

I don't suppose theres any chance of getting this to work with vertex animations/armatures is there?

Problem with that is that Blender in its current state dose't expose the bones data to python - so theres no way o export it. I will look into it further, hopefully there is a way around it :)


Kid Tripod(Posted 2003) [#8]
>> Problem with that is that Blender in its current state dose't expose the bones data to python - so theres no way o export it. I will look into it further, hopefully there is a way around it :)

Thats a bit fatal. A way round it (nasty way) could be to expect the user to create a set of empties, where an empty exists for each bone. Then read the orientations etc of those.

I could of course try adding a .B3D export directly into the source now . . .


Shagwana(Posted 2003) [#9]
Update time to this old topic...

www.Blender.org has just released a new version of Blender (v2.28+) that gives scripters access to bone data!! (with other new features for pyhton too). So When I get back from work later today, Its gona be time to look at the Blender exporter again :).