Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # -*- coding: utf-8 -*-
- ###########################################################################
- # Glest Model / Texture / UV / Animation Importer and Exporter
- # for the Game Glest that u can find http://www.glest.org
- # 2011/05/25: v0.1 alpha1
- # created by William Zheng for Blender 2.57(loveheaven_zhengwei@hotmail.com)
- #"""
- #Here an explanation of the V4 Format found at www.glest.org
- #================================
- #1. DATA TYPES
- #================================
- #G3D files use the following data types:
- #uint8: 8 bit unsigned integer
- #uint16: 16 bit unsigned integer
- #uint32: 32 bit unsigned integer
- #float32: 32 bit floating point
- #================================
- #2. OVERALL STRUCTURE
- #================================
- #- File header
- #- Model header
- #- Mesh header
- #- Texture names
- #- Mesh data
- #================================
- #2. FILE HEADER
- #================================
- #Code:
- #struct FileHeader{
- # uint8 id[3];
- # uint8 version;
- #};
- #This header is shared among all the versions of G3D, it identifies this file as a G3D model and provides information of the version.
- #id: must be "G3D"
- #version: must be 4, in binary (not '4')
- #================================
- #3. MODEL HEADER
- #================================
- #Code:
- #struct ModelHeader{
- # uint16 meshCount;
- # uint8 type;
- #};
- #meshCount: number of meshes in this model
- #type: must be 0
- #================================
- #4. MESH HEADER
- #================================
- #There is a mesh header for each mesh, there must be "meshCount" headers in a file but they are not consecutive, texture names and mesh data are stored in between.
- #Code:
- #struct MeshHeader{
- # uint8 name[64];
- # uint32 frameCount;
- # uint32 vertexCount;
- # uint32 indexCount;
- # float32 diffuseColor[3];
- # float32 specularColor[3];
- # float32 specularPower;
- # float32 opacity;
- # uint32 properties;
- # uint32 textures;
- #};
- #name: name of the mesh
- #frameCount: number of keyframes in this mesh
- #vertexCount: number of vertices in each frame
- #indexCount: number of indices in this mesh (the number of triangles is indexCount/3)
- #diffuseColor: RGB diffuse color
- #specularColor: RGB specular color (currently unused)
- #specularPower: specular power (currently unused)
- ##properties: property flags
- #Code:
- #enum MeshPropertyFlag{
- # mpfTwoSided= 1,
- # mpfCustomColor= 2,
- #};
- #mpfTwoSided: meshes in this mesh are rendered by both sides, if this flag is not present only "counter clockwise" faces are rendered
- #mpfCustomColor: alpha in this model is replaced by a custom color, usually the player color
- #textures: texture flags, only 0x1 is currently used, indicating that there is a diffuse texture in this mesh.
- #================================
- #4. TEXTURE NAMES
- #================================
- #A list of uint8[64] texture name values. One for each texture in the mesh. If there are no textures in the mesh no texture names are present. In practice since Glest only uses 1 texture for each mesh the number of texture names should be 0 or 1.
- #================================
- #5. MESH DATA
- #================================
- #After each mesh header and texture names the mesh data is placed.
- #vertices: frameCount * vertexCount * 3, float32 values representing the x, y, z vertex coords for all frames
- #normals: frameCount * vertexCount * 3, float32 values representing the x, y, z normal coords for all frames
- #texture coords: vertexCount * 2, float32 values representing the s, t tex coords for all frames (only present if the mesh has 1 texture at least)
- #indices: indexCount, uint32 values representing the indices
- ###########################################################################
- bl_info = {
- "name": "G3D Mesh Importer",
- "author": "William Zheng (corrected by MrPostiga)",
- "version": (0, 1, 1),
- "blender": (2, 5, 9),
- "api": 36079,
- "location": "File > Import > Glest 3D File (.g3d)",
- "description": "Import .g3d file and create a mesh",
- "warning": "",
- "wiki_url": "",
- "tracker_url": "",
- "category": "Import-Export"}
- ###########################################################################
- # Importing Structures needed (must later verify if i need them really all)
- ###########################################################################
- import bpy
- from bpy.props import StringProperty
- from bpy_extras.image_utils import load_image
- from bpy_extras.io_utils import ImportHelper, ExportHelper
- import sys, struct, string, types
- from types import *
- import os
- from os import path
- from os.path import dirname
- ###########################################################################
- # Variables that are better Global to handle
- ###########################################################################
- imported = [] #List of all imported Objects
- toexport = [] #List of Objects to export (actually only meshes)
- sceneID = None #Points to the active Blender Scene
- def unpack_list(list_of_tuples):
- l = []
- for t in list_of_tuples:
- l.extend(t)
- return l
- ###########################################################################
- # Declaring Structures of G3D Format
- ###########################################################################
- class G3DHeader: #Read first 4 Bytes of file should be G3D + Versionnumber
- binary_format = "<3cB"
- def __init__(self, fileID):
- temp = fileID.read(struct.calcsize(self.binary_format))
- data = struct.unpack(self.binary_format,temp)
- self.id = str(data[0]+data[1]+data[2], "utf-8")
- self.version = data[3]
- class G3DModelHeaderv3: #Read Modelheader in V3 there is only the number of Meshes in file
- binary_format = "<I"
- def __init__(self, fileID):
- temp = fileID.read(struct.calcsize(self.binary_format))
- data = struct.unpack(self.binary_format,temp)
- self.meshcount = data[0]
- class G3DModelHeaderv4: #Read Modelheader: Number of Meshes and Meshtype (must be 0)
- binary_format = "<HB"
- def __init__(self, fileID):
- temp = fileID.read(struct.calcsize(self.binary_format))
- data = struct.unpack(self.binary_format,temp)
- self.meshcount = data[0]
- self.mtype = data[1]
- class G3DMeshHeaderv3: #Read Meshheader
- binary_format = "<7I64c"
- def __init__(self,fileID):
- temp = fileID.read(struct.calcsize(self.binary_format))
- data = struct.unpack(self.binary_format,temp)
- self.framecount = data[0] #Framecount = Number of Animationsteps
- self.normalframecount= data[1] #Number of Normal Frames actualli equal to Framecount
- self.texturecoordframecount= data[2]#Number of Frames of Texturecoordinates seems everytime to be 1
- self.colorframecount= data[3] #Number of Frames of Colors seems everytime to be 1
- self.vertexcount= data[4] #Number of Vertices in each Frame
- self.indexcount= data[5] #Number of Indices in Mesh (Triangles = Indexcount/3)
- self.properties= data[6] #Property flags
- if self.properties & 1: #PropertyBit is Mesh Textured ?
- self.hastexture = False
- self.texturefilename = None
- else:
- self.texturefilename = "".join([str(x, "ascii") for x in data[7:-1] if x[0]< 127 ])
- self.hastexture = True
- if self.properties & 2: #PropertyBit is Mesh TwoSided ?
- self.istwosided = True
- else:
- self.istwosided = False
- if self.properties & 4: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
- self.customalpha = True
- else:
- self.customalpha = False
- class G3DMeshHeaderv4: #Read Meshheader
- binary_format = "<64c3I8f2I"
- texname_format = "<64c"
- def __init__(self,fileID):
- temp = fileID.read(struct.calcsize(self.binary_format))
- data = struct.unpack(self.binary_format,temp)
- self.meshname = "".join([str(x, "ascii") for x in data[0:64] if x[0] < 127 ]) #Name of Mesh every Char is a String on his own
- self.framecount = data[64] #Framecount = Number of Animationsteps
- self.vertexcount = data[65] #Number of Vertices in each Frame
- self.indexcount = data[66] #Number of Indices in Mesh (Triangles = Indexcount/3)
- self.diffusecolor = data[67:70] #RGB diffuse color
- self.specularcolor = data[70:73] #RGB specular color (unused)
- self.specularpower = data[73] #Specular power (unused)
- self.opacity = data[74] #Opacity
- self.properties= data[75] #Property flags
- self.textures = data[76] #Texture flag
- if self.properties & 1: #PropertyBit is Mesh TwoSided ?
- self.istwosided = True
- else:
- self.istwosided = False
- if self.properties & 2: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
- self.customalpha = True
- else:
- self.customalpha = False
- if self.textures & 1: #PropertyBit is Mesh Textured ?
- temp = fileID.read(struct.calcsize(self.texname_format))
- data = struct.unpack(self.texname_format,temp)
- self.texturefilename = "".join([str(x, "ascii") for x in data[0:-1] if x[0] < 127 ])
- self.hastexture = True
- else:
- self.hastexture = False
- self.texturefilename = None
- class G3DMeshdataV3: #Calculate and read the Mesh Datapack
- def __init__(self,fileID,header):
- #Calculation of the Meshdatasize to load because its variable
- #Animationframes * Vertices per Animation * 3 (Each Point are 3 Float X Y Z Coordinates)
- vertex_format = "<%if" % int(header.framecount * header.vertexcount * 3)
- #The same for Normals
- normals_format = "<%if" % int(header.normalframecount * header.vertexcount * 3)
- #Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
- texturecoords_format = "<%if" % int(header.texturecoordframecount * header.vertexcount * 2)
- #Colors in format RGBA
- colors_format = "<%if" % int(header.colorframecount * 4)
- #Indices
- indices_format = "<%iI" % int(header.indexcount)
- #Load the Meshdata as calculated above
- self.vertices = struct.unpack(vertex_format,fileID.read(struct.calcsize(vertex_format)))
- self.normals = struct.unpack(normals_format,fileID.read(struct.calcsize(normals_format)))
- self.texturecoords = struct.unpack(texturecoords_format,fileID.read(struct.calcsize(texturecoords_format)))
- self.colors = struct.unpack(colors_format,fileID.read(struct.calcsize(colors_format)))
- self.indices = struct.unpack(indices_format ,fileID.read(struct.calcsize(indices_format)))
- class G3DMeshdataV4: #Calculate and read the Mesh Datapack
- def __init__(self,fileID,header):
- #Calculation of the Meshdatasize to load because its variable
- #Animationframes * Points (Vertex) per Animation * 3 (Each Point are 3 Float X Y Z Coordinates)
- vertex_format = "<%if" % int(header.framecount * header.vertexcount * 3)
- #The same for Normals
- normals_format = "<%if" % int(header.framecount * header.vertexcount * 3)
- #Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
- texturecoords_format = "<%if" % int(header.vertexcount * 2)
- #Indices
- indices_format = "<%iI" % int(header.indexcount)
- #Load the Meshdata as calculated above
- self.vertices = struct.unpack(vertex_format,fileID.read(struct.calcsize(vertex_format)))
- self.normals = struct.unpack(normals_format,fileID.read(struct.calcsize(normals_format)))
- if header.hastexture:
- self.texturecoords = struct.unpack(texturecoords_format,fileID.read(struct.calcsize(texturecoords_format)))
- self.indices = struct.unpack(indices_format ,fileID.read(struct.calcsize(indices_format)))
- def createMesh(filename,header,data): #Create a Mesh inside Blender
- mesh = bpy.data.meshes.new(header.meshname) #New Mesh
- meshobj = bpy.data.objects.new(header.meshname+'Object', mesh) #New Object for the new Mesh
- scene = bpy.context.scene
- scene.objects.link(meshobj)
- scene.update()
- uvcoords = []
- image = None
- if header.hastexture: #Load Texture when assigned
- texturefile = dirname(filename)+os.sep+header.texturefilename
- image = bpy.data.images.load(texturefile) #load_image(texturefile, dirname(filename))
- for x in range(0,len(data.texturecoords),2): #Prepare the UV
- uvcoords.append([data.texturecoords[x],data.texturecoords[x+1]])
- vertsCO = []
- vertsNormal = []
- for x in range(0,header.vertexcount*3,3): #Get the Vertices and Normals into empty Mesh
- vertsCO.extend([(data.vertices[x],data.vertices[x+1],data.vertices[x+2])])
- vertsNormal.extend([(data.normals[x],data.normals[x+1],data.normals[x+2])])
- #vertsCO.extend([(data.vertices[x+(header.framecount-1)*header.vertexcount*3],data.vertices[x+(header.framecount-1)*header.vertexcount*3+1],data.vertices[x+(header.framecount-1)*header.vertexcount*3+2])])
- #vertsNormal.extend([(data.normals[x+(header.framecount-1)*header.vertexcount*3],data.normals[x+(header.framecount-1)*header.vertexcount*3+1],data.normals[x+(header.framecount-1)*header.vertexcount*3+2])])
- mesh.vertices.add(len(vertsCO))
- mesh.vertices.foreach_set("co", unpack_list(vertsCO))
- mesh.vertices.foreach_set("normal", unpack_list(vertsNormal))
- faces = []
- faceuv = []
- for i in range(0,len(data.indices),3): #Build Faces into Mesh
- faces.extend([data.indices[i], data.indices[i+1], data.indices[i+2], 0])
- if header.hastexture:
- uv = []
- u0 = uvcoords[data.indices[i]][0]
- v0 = uvcoords[data.indices[i]][1]
- uv.append([u0,v0])
- u1 = uvcoords[data.indices[i+1]][0]
- v1 = uvcoords[data.indices[i+1]][1]
- uv.append([u1,v1])
- u2 = uvcoords[data.indices[i+2]][0]
- v2 = uvcoords[data.indices[i+2]][1]
- uv.append([u2,v2])
- faceuv.append([uv,0,0,0])
- else:
- uv.append([0,0])
- uv.append([0,0])
- uv.append([0,0])
- faceuv.append([uv,0,0,0])
- mesh.faces.add(len(faces)//4)
- mesh.faces.foreach_set("vertices_raw", faces)
- mesh.faces.foreach_set("use_smooth", [True] * len(mesh.faces))
- mesh.update_tag()
- mesh.update() #Update changes to Mesh
- #===================================================================================================
- #Material Setup
- #===================================================================================================
- if header.hastexture:
- materialname = "pskmat"
- materials = []
- texture = bpy.data.textures.new(name=header.texturefilename,type='IMAGE')
- texture.image = image
- matdata = bpy.data.materials.new(materialname + '1')
- slot = matdata.texture_slots.add()
- slot.texture = texture
- slot.texture_coords = 'UV'
- if header.isv4:
- matdata.diffuse_color = (header.diffusecolor[0], header.diffusecolor[1],header.diffusecolor[2])
- matdata.alpha = header.opacity
- matdata.specular_color = (header.specularcolor[0], header.specularcolor[1],header.specularcolor[2])
- materials.append(matdata)
- for material in materials:
- #add material to the mesh list of materials
- mesh.materials.append(material)
- countm = 0
- psktexname="psk" + str(countm)
- mesh.uv_textures.new(name=psktexname)
- for countm in range(len(mesh.uv_textures)):
- mesh.update()
- uvtex = mesh.uv_textures[countm] #add one uv texture
- mesh.update()
- if (len(faceuv) > 0):
- counttex = 0
- countm = 0
- for countm in range(len(mesh.uv_textures)):
- mesh.update()
- psktexname="psk" + str(countm)
- uvtex = mesh.uv_textures[countm] #add one uv texture
- mesh.update()
- for i, face in enumerate(mesh.faces):
- blender_tface = uvtex.data[i] #face
- mfaceuv = faceuv[i]
- #face.material_index = faceuv[i][1]
- if countm == faceuv[i][1]:
- face.material_index = faceuv[i][1]
- blender_tface.uv1 = mfaceuv[0][0] #uv = (0,0)
- blender_tface.uv2 = mfaceuv[0][1] #uv = (0,0)
- blender_tface.uv3 = mfaceuv[0][2] #uv = (0,0)
- blender_tface.image = image
- blender_tface.use_image = True
- blender_tface.blend_type = 'ALPHA'
- blender_tface.use_twoside = True
- else:
- blender_tface.uv1 = [0,0]
- blender_tface.uv2 = [0,0]
- blender_tface.uv3 = [0,0]
- mesh.update()
- imported.append(meshobj) #Add to Imported Objects
- for x in range(header.framecount): #Put in Vertex Positions for Keyanimation
- print("Frame"+str(x))
- for i in range(0,header.vertexcount*3,3):
- print(str(i//3)+':\t'+str(data.vertices[x*header.vertexcount*3 + i])+'\t'+
- str(data.vertices[x*header.vertexcount*3 + i +1])+'\t'+
- str(data.vertices[x*header.vertexcount*3 + i +2]))
- mesh.vertices[i//3].co[0]= data.vertices[x*header.vertexcount*3 + i]
- mesh.vertices[i//3].co[1]= data.vertices[x*header.vertexcount*3 + i +1]
- mesh.vertices[i//3].co[2]= data.vertices[x*header.vertexcount*3 + i +2]
- meshobj.keyframe_insert("location",-1, frame=(x+1))
- mesh.update()
- return
- ###########################################################################
- # Import
- ###########################################################################
- def G3DLoader(filepath): #Main Import Routine
- global imported, sceneID
- print ("\nNow Importing File: " + filepath)
- fileID = open(filepath,"rb")
- header = G3DHeader(fileID)
- print ("\nHeader ID : " + header.id)
- print ("Version : " + str(header.version))
- if header.id != "G3D":
- print ("This is Not a G3D Model File")
- fileID.close
- return
- if header.version not in (3, 4):
- print ("The Version of this G3D File is not Supported")
- fileID.close
- return
- #in_editmode = Blender.Window.EditMode() #Must leave Editmode when active
- #if in_editmode: Blender.Window.EditMode(0)
- sceneID = bpy.context.scene #Get active Scene
- #scenecontext=sceneID.getRenderingContext() #To Access the Start/Endframe its so hidden i searched till i got angry :-)
- basename=os.path.basename(filepath).split('.')[0] #Generate the Base Filename without Path + extension
- imported = []
- maxframe=0
- if header.version == 3:
- modelheader = G3DModelHeaderv3(fileID)
- print ("Number of Meshes : " + str(modelheader.meshcount))
- for x in range(modelheader.meshcount):
- meshheader = G3DMeshHeaderv3(fileID)
- meshheader.isv4 = False
- print ("\nMesh Number : " + str(x+1))
- print ("framecount : " + str(meshheader.framecount))
- print ("normalframecount : " + str(meshheader.normalframecount))
- print ("texturecoordframecount: " + str(meshheader.texturecoordframecount))
- print ("colorframecount : " + str(meshheader.colorframecount))
- print ("pointcount : " + str(meshheader.vertexcount))
- print ("indexcount : " + str(meshheader.indexcount))
- print ("texturename : " + str(meshheader.texturefilename))
- print ("hastexture : " + str(meshheader.hastexture))
- print ("istwosided : " + str(meshheader.istwosided))
- print ("customalpha : " + str(meshheader.customalpha))
- meshheader.meshname = basename+str(x+1) #Generate Meshname because V3 has none
- if meshheader.framecount > maxframe: maxframe = meshheader.framecount #Evaluate the maximal animationsteps
- meshdata = G3DMeshdataV3(fileID,meshheader)
- createMesh(filepath,meshheader,meshdata)
- fileID.close
- bpy.context.scene.frame_start=1
- bpy.context.scene.frame_end=maxframe
- bpy.context.scene.frame_current=1
- anchor = bpy.data.objects.new('Empty', None)
- anchor.select = True
- bpy.context.scene.objects.link(anchor)
- for ob in imported:
- ob.parent = anchor
- bpy.context.scene.update()
- return
- if header.version == 4:
- modelheader = G3DModelHeaderv4(fileID)
- print ("Number of Meshes : " + str(modelheader.meshcount))
- for x in range(modelheader.meshcount):
- meshheader = G3DMeshHeaderv4(fileID)
- meshheader.isv4 = True
- print ("\nMesh Number : " + str(x+1))
- print ("meshname : " + str(meshheader.meshname))
- print ("framecount : " + str(meshheader.framecount))
- print ("vertexcount : " + str(meshheader.vertexcount))
- print ("indexcount : " + str(meshheader.indexcount))
- print ("diffusecolor : %1.6f %1.6f %1.6f" %meshheader.diffusecolor)
- print ("specularcolor : %1.6f %1.6f %1.6f" %meshheader.specularcolor)
- print ("specularpower : %1.6f" %meshheader.specularpower)
- print ("opacity : %1.6f" %meshheader.opacity)
- print ("properties : " + str(meshheader.properties))
- print ("textures : " + str(meshheader.textures))
- print ("texturename : " + str(meshheader.texturefilename))
- if len(meshheader.meshname) ==0: #When no Meshname in File Generate one
- meshheader.meshname = basename+str(x+1)
- if meshheader.framecount > maxframe: maxframe = meshheader.framecount #Evaluate the maximal animationsteps
- meshdata = G3DMeshdataV4(fileID,meshheader)
- createMesh(filepath,meshheader,meshdata)
- fileID.close
- bpy.context.scene.frame_start=1
- bpy.context.scene.frame_end=maxframe
- bpy.context.scene.frame_current=1
- anchor = bpy.data.objects.new('Empty', None)
- anchor.select = True
- bpy.context.scene.objects.link(anchor)
- for ob in imported:
- ob.parent = anchor
- bpy.context.scene.update()
- print ("Created a empty Object as 'Grip' where all imported Objects are parented to")
- print ("To move the complete Meshes only select this empty Object and move it")
- print ("All Done, have a good Day :-)\n\n")
- return
- #---=== Register ===
- class ImportG3D(bpy.types.Operator, ImportHelper):
- '''Load a G3D file'''
- bl_idname = "importg3d.g3d"
- bl_label = "Import G3D"
- filename_ext = ".g3d"
- filter_glob = StringProperty(default="*.g3d", options={'HIDDEN'})
- def execute(self, context):
- try:
- G3DLoader(**self.as_keywords(ignore=("filter_glob",)))
- except:
- import traceback
- traceback.print_exc()
- return {'CANCELLED'}
- return {'FINISHED'}
- def menu_func_import(self, context):
- self.layout.operator(ImportG3D.bl_idname, text="Glest 3D File (.g3d)")
- def register():
- bpy.utils.register_module(__name__)
- bpy.types.INFO_MT_file_import.append(menu_func_import)
- def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.INFO_MT_file_import.remove(menu_func_import)
- if __name__ == '__main__':
- register()
- # main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement