# -*- coding: utf-8 -*- ########################################################################### # Glest Model / Texture / UV / Animation Importer and Exporter # for the Game Glest that u can find http://www.glest.org # copyright 2005 By Andreas Becker (seltsamuel@yahoo.de) # # 2011/05/25: v0.1 alpha1 # modified by William Zheng for Blender 2.57(loveheaven_zhengwei@hotmail.com) # # corrected by MrPostiga for Blender 2.58 # # extended by Yggdrasil # # Started Date: 07 June 2005 Put Public 20 June 2005 # Distributed under the GNU PUBLIC LICENSE #""" #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{ # mpfCustomColor= 1, # mpfTwoSided= 2, # mpfNoSelect= 4 #}; #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 #Code: #enum MeshTexture{ # diffuse = 1, # specular = 2, # normal = 4 #}; #================================ #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. #================================ #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 Import/Export", "description": "Import/Export .g3d file (Glest 3D)", "author": "various, see head of script", "version": (0, 11, 1), "blender": (2, 65, 0), "location": "File > Import-Export", "warning": "always keep .blend files", "wiki_url": "http://glest.wikia.com/wiki/G3D_support", "tracker_url": "https://forum.megaglest.org/index.php?topic=6596", "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 bmesh import sys, struct, string, types from types import * import os from os import path from os.path import dirname, abspath import subprocess from mathutils import Matrix from math import radians ########################################################################### # 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 = "> 3 while tex: tex &= tex - 1 # set rightmost 1-bit to 0 # discard texture name, as we don't know what to do with it fileID.seek(struct.calcsize(self.texname_format)) print("warning: ignored texture in undefined texture slot") 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))) #Create a Mesh inside Blender def createMesh(filename, header, data, toblender, operator): 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 = [] img_diffuse = None img_specular = None img_normal = None if header.hastexture: #Load Texture when assigned try: texturefile = dirname(abspath(filename)) + os.sep + header.diffusetexture img_diffuse = bpy.data.images.load(texturefile) for x in range(0,len(data.texturecoords),2): #Prepare the UV uvcoords.append([data.texturecoords[x],data.texturecoords[x+1]]) if header.isv4: if header.speculartexture: texturefile = dirname(abspath(filename)) + os.sep + header.speculartexture img_specular = bpy.data.images.load(texturefile) if header.normaltexture: texturefile = dirname(abspath(filename)) + os.sep + header.normaltexture img_normal = bpy.data.images.load(texturefile) except: import traceback traceback.print_exc() header.hastexture = False operator.report({'WARNING'}, "Couldn't load texture. See console for details.") 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 = [] uv.append([0,0]) uv.append([0,0]) uv.append([0,0]) faceuv.append([uv,0,0,0]) mesh.tessfaces.add(len(faces)//4) mesh.tessfaces.foreach_set("vertices_raw", faces) mesh.tessfaces.foreach_set("use_smooth", [True] * len(mesh.tessfaces)) mesh.g3d_customColor = header.customalpha mesh.show_double_sided = header.istwosided if header.isv4: mesh.g3d_noSelect = header.noselect mesh.g3d_glow = header.glow else: mesh.g3d_noSelect = False mesh.glow = False mesh.g3d_fullyOpaque = False #=================================================================================================== #Material Setup #=================================================================================================== def addtexslot(matdata,index,name,img): texture = bpy.data.textures.new(name=name,type='IMAGE') texture.image = img slot = matdata.texture_slots.create(index) slot.texture = texture slot.texture_coords = 'UV' if header.hastexture: materialname = "pskmat" materials = [] matdata = bpy.data.materials.new(materialname + '1') addtexslot(matdata, 0, 'diffusetexture', img_diffuse) if img_specular: addtexslot(matdata, 1, 'speculartexture', img_specular) if img_normal: addtexslot(matdata, 2, 'normaltexture', img_normal) 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.tessface_uv_textures.new(name=psktexname) if (len(faceuv) > 0): for countm in range(len(mesh.tessface_uv_textures)): uvtex = mesh.tessface_uv_textures[countm] #add one uv texture for i, face in enumerate(mesh.tessfaces): blender_tface = uvtex.data[i] #face mfaceuv = faceuv[i] 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 = img_diffuse else: blender_tface.uv1 = [0,0] blender_tface.uv2 = [0,0] blender_tface.uv3 = [0,0] imported.append(meshobj) #Add to Imported Objects sk = meshobj.shape_key_add() for x in range(1,header.framecount): #Put in Vertex Positions for Keyanimation sk = meshobj.shape_key_add() for i in range(0,header.vertexcount*3,3): sk.data[i//3].co[0]= data.vertices[x*header.vertexcount*3 + i] sk.data[i//3].co[1]= data.vertices[x*header.vertexcount*3 + i +1] sk.data[i//3].co[2]= data.vertices[x*header.vertexcount*3 + i +2] # activate one shapekey per frame for i in range(1,header.framecount): shape = mesh.shape_keys.key_blocks[i] shape.value = 0.0 shape.keyframe_insert("value", frame=i) shape.value = 1.0 shape.keyframe_insert("value", frame=(i+1)) shape.value = 0.0 shape.keyframe_insert("value", frame=(i+2)) meshobj.active_shape_key_index = 0 if toblender: # rotate from glest to blender orientation #mesh.transform( Matrix( ((1,0,0,0),(0,0,-1,0),(0,1,0,0),(0,0,0,1)) ) ) # doesn't work, maybe because of shape keys # use object transformation instead meshobj.rotation_euler = (radians(90), 0, 0) # update polygon structures from tessfaces mesh.update() mesh.update_tag() # remove duplicates bm = bmesh.new() bm.from_mesh(mesh) bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.0001) bm.to_mesh(mesh) bm.free() return ########################################################################### # Import ########################################################################### def G3DLoader(filepath, toblender, operator): #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 ("ERROR: This is Not a G3D Model File") operator.report({'ERROR'}, "This is Not a G3D Model File") fileID.close return if header.version not in (3, 4): print ("ERROR: The Version of this G3D File is not Supported") operator.report({'ERROR'}, "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.diffusetexture)) 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, toblender, operator) 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.diffusetexture)) 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, toblender, operator) 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 def G3DSaver(filepath, context, toglest, operator): print ("\nNow Exporting File: " + filepath) objs = context.selected_objects if len(objs) == 0: objs = bpy.data.objects #get real meshcount as len(bpy.data.meshes) holds also old meshes meshCount = 0 for obj in objs: if obj.type == 'MESH': meshCount += 1 if obj.mode != 'OBJECT': # we want to be in object mode print("ERROR: mesh not in object mode") operator.report({'ERROR'}, "mesh not in object mode") return -1 if meshCount == 0: print("ERROR: no meshes found") operator.report({'ERROR'}, "no meshes found") return -1 fileID = open(filepath,"wb") # G3DHeader v4 fileID.write(struct.pack("<3cB", b'G', b'3', b'D', 4)) # G3DModelHeaderv4 fileID.write(struct.pack(" 0: # we have a texture, hopefully material = mesh.materials[0] slot = material.texture_slots[0] # only look for other textures when we have diffuse if slot and slot.texture.type=='IMAGE' and len(mesh.uv_textures)>0: diffuseColor = material.diffuse_color specularColor = material.specular_color opacity = material.alpha textures = 1 texnames = [] texnames.append(bpy.path.basename(slot.texture.image.filepath)) # specular and normal for i in range(1, 3): slot = material.texture_slots[i] if slot and slot.texture.type=='IMAGE': texnames.append(bpy.path.basename(slot.texture.image.filepath)) textures |= 1 << i else: print("WARNING: first texture slot in first material isn't of type IMAGE or it's not unwrapped, texture ignored") operator.report({'WARNING'}, "first texture slot in first material isn't of type IMAGE or it's not unwrapped, texture ignored") #continue without texture meshname = mesh.name frameCount = context.scene.frame_end - context.scene.frame_start +1 realFaceCount = 0 # real face count (triangles) indices=[] # list of indices newverts=[] # list of vertex indices which need to be duplicated uvlist = [] # list of texcoords mesh.update(calc_tessface=True) # tesselate n-polygons to triangles & quads if textures: uvtex = mesh.tessface_uv_textures[0] uvlist[:] = [[0]*2 for i in range(len(mesh.vertices))] # blender allows to have multiple texcoords per vertex, # in g3d format every vertex can only have one texcoord # -> duplicate vertex # the dictionary/map vdict collects all the stuff # index to "unique" vertices from blender # -> tuple( list of texcoords, list of indices to the duplicated vertex ) vdict = dict() nextIndex = len(mesh.vertices) for face in mesh.tessfaces: # when a vertex is duplicated it gets a new index, so the # triple of indices describing the face is different too faceindices = [] realFaceCount += 1 uvdata = uvtex.data[face.index] for i in range(3): #closure, got rid of copy&paste, still looking weird def getTexCoords(): nonlocal nextIndex, vdict, uvlist, newverts vindex = face.vertices[i] if vindex not in vdict: # new vertex -> add it vdict[vindex] = [uvdata.uv[i]], [vindex] uvlist[vindex] = uvdata.uv[i] # that's a (s,t)-pair else: found = False idx = 0 for ele in vdict[vindex][0]: if uvdata.uv[i][0] == ele[0] and uvdata.uv[i][1] == ele[1]: found = True break idx += 1 if found: # same vertex and texcoord before # it could be a different index now, the index of a new # duplicated vertex #vindex = vdict[vindex][1][ vdict[vindex][0].index(uvdata.uv[i]) ] vindex = vdict[vindex][1][idx] else: # same vertex as before but with different texcoord -> duplicate vdict[vindex][0].append(uvdata.uv[i]) vdict[vindex][1].append(nextIndex) # duplicate vertex because it takes part in different faces # with different texcoords newverts.append(vindex) uvlist.append(uvdata.uv[i]) # new index for the duplicated vertex vindex = nextIndex nextIndex += 1 faceindices.append(vindex) getTexCoords() indices.extend(faceindices) if len(face.vertices) == 4: faceindices = [] realFaceCount += 1 for i in [0,2,3]: getTexCoords() indices.extend(faceindices) else: for face in mesh.tessfaces: realFaceCount += 1 indices.extend(face.vertices[0:3]) if len(face.vertices) == 4: realFaceCount += 1 # new face because quad got split indices.append(face.vertices[0]) indices.append(face.vertices[2]) indices.append(face.vertices[3]) # abort when no triangles as it crashs g3dviewer if realFaceCount == 0: print("ERROR: no triangles found") operator.report({'ERROR'}, "no triangles found") fileID.close() return -1 indexCount = realFaceCount * 3 vertexCount = len(mesh.vertices) + len(newverts) specularPower = 9.999999 # unused, same as old exporter properties = 0 if mesh.g3d_customColor: properties |= 1 if mesh.show_double_sided: properties |= 2 if mesh.g3d_noSelect: properties |= 4 if mesh.g3d_glow: properties |= 8 #MeshData vertices = [] normals = [] fcurrent = context.scene.frame_current for i in range(context.scene.frame_start, context.scene.frame_end+1): context.scene.frame_set(i) #FIXME: not sure what's better: PREVIEW or RENDER settings m = obj.to_mesh(context.scene, True, 'RENDER') m.transform(obj.matrix_world) # apply object-mode transformations if toglest: # rotate from blender to glest orientation m.transform( Matrix( ((1,0,0,0),(0,0,1,0),(0,-1,0,0),(0,0,0,1)) ) ) # transform normals too m.calc_normals() for vertex in m.vertices: vertices.extend(vertex.co) normals.extend(vertex.normal) # duplicate vertices and corresponding normals, for every frame for nv in newverts: vertices.extend(m.vertices[nv].co) normals.extend(m.vertices[nv].normal) context.scene.frame_set(fcurrent) if mesh.g3d_fullyOpaque: opacity = 1.0 # MeshHeader fileID.write(struct.pack("<64s3I8f2I", bytes(meshname, "ascii"), frameCount, vertexCount, indexCount, diffuseColor[0], diffuseColor[1], diffuseColor[2], specularColor[0], specularColor[1], specularColor[2], specularPower, opacity, properties, textures )) #Texture names if textures: # only when we have textures for i in range(len(texnames)): fileID.write(struct.pack("<64s", bytes(texnames[i], "ascii"))) # see G3DMeshdataV4 vertex_format = "<%if" % int(frameCount * vertexCount * 3) normals_format = "<%if" % int(frameCount * vertexCount * 3) texturecoords_format = "<%if" % int(vertexCount * 2) indices_format = "<%iI" % int(indexCount) fileID.write(struct.pack(vertex_format, *vertices)) fileID.write(struct.pack(normals_format, *normals)) # texcoords if textures: # only when we have textures texcoords = [] for uv in uvlist: texcoords.extend(uv) fileID.write(struct.pack(texturecoords_format, *texcoords)) fileID.write(struct.pack(indices_format, *indices)) fileID.close() return 0 #---=== Register === class G3DPanel(bpy.types.Panel): #bl_idname = "OBJECT_PT_G3DPanel" bl_label = "G3D properties" bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "data" @classmethod def poll(cls, context): return (context.object is not None and context.object.type == 'MESH') def draw(self, context): self.layout.prop(context.object.data, "g3d_customColor") self.layout.prop(context.object.data, "show_double_sided", text="double sided") self.layout.prop(context.object.data, "g3d_noSelect") self.layout.prop(context.object.data, "g3d_fullyOpaque") self.layout.prop(context.object.data, "g3d_glow") 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'}) toblender = bpy.props.BoolProperty( name="rotate to Blender orientation", description="Rotate meshes from Glest to Blender orientation", default=True) def execute(self, context): try: G3DLoader(self.filepath, self.toblender, self) except: import traceback traceback.print_exc() return {'CANCELLED'} return {'FINISHED'} class ExportG3D(bpy.types.Operator, ExportHelper): '''Save a G3D file''' bl_idname = "exportg3d.g3d" bl_label = "Export G3D" filename_ext = ".g3d" filter_glob = StringProperty(default="*.g3d", options={'HIDDEN'}) #export options showg3d = bpy.props.BoolProperty( name="show G3D afterwards", description=("Run g3dviewer to show G3D after export. " "g3dviewer needs to be in the scripts directory, " "otherwise the associated program of .g3d is run."), default=False) toglest = bpy.props.BoolProperty( name="rotate to glest orientation", description="Rotate meshes from Blender to Glest orientation", default=True) def execute(self, context): try: res = G3DSaver(self.filepath, context, self.toglest, self) if res==0 and self.showg3d: print("opening g3dviewer with " + self.filepath) scriptsdir = bpy.utils.script_path_user() dname = os.path.dirname(self.filepath) found = False for f in os.listdir(scriptsdir): if "g3dviewer" in f: f = os.path.join(scriptsdir, f) if os.path.isfile(f) and os.access(f, os.X_OK): cmd = [f, self.filepath] print(cmd) subprocess.Popen(cmd, cwd=dname) found = True # try default associated program if not found: if os.name == 'posix': # xdg-open is only a shell script which delegates the job to a # desktop specific program, e.g. if DE=kde than kde-open # needs DE environment variable set, otherwise it just throws it # at the browser, which is not very helpful print("running xdg-open "+self.filepath) subprocess.Popen(['xdg-open', self.filepath], cwd=dname) elif os.name == 'mac': subprocess.Popen(['open', self.filepath], cwd=dname) elif os.name == 'nt': #os.startfile(self.filepath) # no way to change dir subprocess.Popen(['cmd', '/C', 'start', self.filepath], cwd=dname) 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 menu_func_export(self, context): self.layout.operator(ExportG3D.bl_idname, text="Glest 3D File (.g3d)") def register(): # custom mesh properties bpy.types.Mesh.g3d_customColor = bpy.props.BoolProperty( name="team color", description="replace alpha channel of texture with team color") bpy.types.Mesh.g3d_noSelect = bpy.props.BoolProperty( name="non-selectable", description="click on mesh doesn't select unit") bpy.types.Mesh.g3d_fullyOpaque = bpy.props.BoolProperty( name="fully opaque", description="sets opacity to 1.0, ignoring what's set in materials") bpy.types.Mesh.g3d_glow = bpy.props.BoolProperty( name="glow", description="let objects glow like particles") bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_import.append(menu_func_import) bpy.types.INFO_MT_file_export.append(menu_func_export) def unregister(): bpy.utils.unregister_module(__name__) bpy.types.INFO_MT_file_import.remove(menu_func_import) bpy.types.INFO_MT_file_export.remove(menu_func_export) if __name__ == '__main__': register() # main() #for obj in bpy.data.objects: # if obj.type == 'MESH': # obj.select = True # bpy.ops.object.delete() #G3DLoader("import.g3d", True, None) #for obj in bpy.context.selected_objects: # obj.select = False # deselect everything, so we get it all #G3DSaver("test.g3d", bpy.context)